Auto merge of #136030 - matthiaskrgr:rollup-cbue0ng, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #135415 (Add `File already exists` error doc to `hard_link` function) - #135581 (Separate Builder methods from tcx) - #135728 (document order of items in iterator from drain) - #135749 (Do not assume const params are printed after type params) - #135829 (Rustc dev guide subtree update) - #135938 (Add memory layout documentation to generic NonZero<T>) - #135949 (Use short type string in E0308 secondary span label) - #135976 (Don't drop types with no drop glue when building drops for tailcalls) - #135998 ([rustdoc] Fix indent of trait items on mobile) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
814ebca293
67 changed files with 706 additions and 345 deletions
|
|
@ -444,7 +444,6 @@ impl WriteBackendMethods for GccCodegenBackend {
|
|||
}
|
||||
fn autodiff(
|
||||
_cgcx: &CodegenContext<Self>,
|
||||
_tcx: TyCtxt<'_>,
|
||||
_module: &ModuleCodegen<Self::Module>,
|
||||
_diff_fncs: Vec<AutoDiffItem>,
|
||||
_config: &ModuleConfig,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::borrow::Cow;
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::ops::Deref;
|
||||
use std::{iter, ptr};
|
||||
|
||||
|
|
@ -31,20 +31,22 @@ use tracing::{debug, instrument};
|
|||
use crate::abi::FnAbiLlvmExt;
|
||||
use crate::attributes;
|
||||
use crate::common::Funclet;
|
||||
use crate::context::CodegenCx;
|
||||
use crate::context::{CodegenCx, SimpleCx};
|
||||
use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True};
|
||||
use crate::type_::Type;
|
||||
use crate::type_of::LayoutLlvmExt;
|
||||
use crate::value::Value;
|
||||
|
||||
// All Builders must have an llfn associated with them
|
||||
#[must_use]
|
||||
pub(crate) struct Builder<'a, 'll, 'tcx> {
|
||||
pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow<SimpleCx<'ll>>> {
|
||||
pub llbuilder: &'ll mut llvm::Builder<'ll>,
|
||||
pub cx: &'a CodegenCx<'ll, 'tcx>,
|
||||
pub cx: &'a CX,
|
||||
}
|
||||
|
||||
impl Drop for Builder<'_, '_, '_> {
|
||||
pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SimpleCx<'ll>>;
|
||||
pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, CodegenCx<'ll, 'tcx>>;
|
||||
|
||||
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> Drop for GenericBuilder<'a, 'll, CX> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
llvm::LLVMDisposeBuilder(&mut *(self.llbuilder as *mut _));
|
||||
|
|
@ -52,6 +54,112 @@ impl Drop for Builder<'_, '_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'll> SBuilder<'a, 'll> {
|
||||
fn call(
|
||||
&mut self,
|
||||
llty: &'ll Type,
|
||||
llfn: &'ll Value,
|
||||
args: &[&'ll Value],
|
||||
funclet: Option<&Funclet<'ll>>,
|
||||
) -> &'ll Value {
|
||||
debug!("call {:?} with args ({:?})", llfn, args);
|
||||
|
||||
let args = self.check_call("call", llty, llfn, args);
|
||||
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
|
||||
let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
|
||||
if let Some(funclet_bundle) = funclet_bundle {
|
||||
bundles.push(funclet_bundle);
|
||||
}
|
||||
|
||||
let call = unsafe {
|
||||
llvm::LLVMBuildCallWithOperandBundles(
|
||||
self.llbuilder,
|
||||
llty,
|
||||
llfn,
|
||||
args.as_ptr() as *const &llvm::Value,
|
||||
args.len() as c_uint,
|
||||
bundles.as_ptr(),
|
||||
bundles.len() as c_uint,
|
||||
c"".as_ptr(),
|
||||
)
|
||||
};
|
||||
call
|
||||
}
|
||||
|
||||
fn with_scx(scx: &'a SimpleCx<'ll>) -> Self {
|
||||
// Create a fresh builder from the simple context.
|
||||
let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.llcx) };
|
||||
SBuilder { llbuilder, cx: scx }
|
||||
}
|
||||
}
|
||||
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
|
||||
pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) }
|
||||
}
|
||||
|
||||
fn ret_void(&mut self) {
|
||||
unsafe {
|
||||
llvm::LLVMBuildRetVoid(self.llbuilder);
|
||||
}
|
||||
}
|
||||
|
||||
fn ret(&mut self, v: &'ll Value) {
|
||||
unsafe {
|
||||
llvm::LLVMBuildRet(self.llbuilder, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, 'll> SBuilder<'a, 'll> {
|
||||
fn build(cx: &'a SimpleCx<'ll>, llbb: &'ll BasicBlock) -> SBuilder<'a, 'll> {
|
||||
let bx = SBuilder::with_scx(cx);
|
||||
unsafe {
|
||||
llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb);
|
||||
}
|
||||
bx
|
||||
}
|
||||
|
||||
fn check_call<'b>(
|
||||
&mut self,
|
||||
typ: &str,
|
||||
fn_ty: &'ll Type,
|
||||
llfn: &'ll Value,
|
||||
args: &'b [&'ll Value],
|
||||
) -> Cow<'b, [&'ll Value]> {
|
||||
assert!(
|
||||
self.cx.type_kind(fn_ty) == TypeKind::Function,
|
||||
"builder::{typ} not passed a function, but {fn_ty:?}"
|
||||
);
|
||||
|
||||
let param_tys = self.cx.func_params_types(fn_ty);
|
||||
|
||||
let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.cx.val_ty(v)))
|
||||
.all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);
|
||||
|
||||
if all_args_match {
|
||||
return Cow::Borrowed(args);
|
||||
}
|
||||
|
||||
let casted_args: Vec<_> = iter::zip(param_tys, args)
|
||||
.enumerate()
|
||||
.map(|(i, (expected_ty, &actual_val))| {
|
||||
let actual_ty = self.cx.val_ty(actual_val);
|
||||
if expected_ty != actual_ty {
|
||||
debug!(
|
||||
"type mismatch in function call of {:?}. \
|
||||
Expected {:?} for param {}, got {:?}; injecting bitcast",
|
||||
llfn, expected_ty, i, actual_ty
|
||||
);
|
||||
self.bitcast(actual_val, expected_ty)
|
||||
} else {
|
||||
actual_val
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Cow::Owned(casted_args)
|
||||
}
|
||||
}
|
||||
|
||||
/// Empty string, to be used where LLVM expects an instruction name, indicating
|
||||
/// that the instruction is to be left unnamed (i.e. numbered, in textual IR).
|
||||
// FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer.
|
||||
|
|
@ -1222,6 +1330,14 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> {
|
|||
}
|
||||
|
||||
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Builder<'a, 'll, 'tcx> {
|
||||
let bx = Builder::with_cx(cx);
|
||||
unsafe {
|
||||
llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb);
|
||||
}
|
||||
bx
|
||||
}
|
||||
|
||||
fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self {
|
||||
// Create a fresh builder from the crate context.
|
||||
let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) };
|
||||
|
|
@ -1231,13 +1347,16 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
pub(crate) fn llfn(&self) -> &'ll Value {
|
||||
unsafe { llvm::LLVMGetBasicBlockParent(self.llbb()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
|
||||
fn position_at_start(&mut self, llbb: &'ll BasicBlock) {
|
||||
unsafe {
|
||||
llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
fn align_metadata(&mut self, load: &'ll Value, align: Align) {
|
||||
unsafe {
|
||||
let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))];
|
||||
|
|
@ -1259,7 +1378,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
self.set_metadata(inst, llvm::MD_unpredictable, md);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
|
||||
pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
|
||||
unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) }
|
||||
}
|
||||
|
|
@ -1360,7 +1480,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) };
|
||||
ret.expect("LLVM does not have support for catchret")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
fn check_call<'b>(
|
||||
&mut self,
|
||||
typ: &str,
|
||||
|
|
@ -1401,11 +1523,13 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
|
||||
Cow::Owned(casted_args)
|
||||
}
|
||||
|
||||
}
|
||||
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
|
||||
pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value {
|
||||
unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) }
|
||||
}
|
||||
|
||||
}
|
||||
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value {
|
||||
let (ty, f) = self.cx.get_intrinsic(intrinsic);
|
||||
self.call(ty, None, None, f, args, None, None)
|
||||
|
|
@ -1423,7 +1547,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
|
||||
self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]);
|
||||
}
|
||||
|
||||
}
|
||||
impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> {
|
||||
pub(crate) fn phi(
|
||||
&mut self,
|
||||
ty: &'ll Type,
|
||||
|
|
@ -1443,7 +1568,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
let src_ty = self.cx.val_ty(val);
|
||||
let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
|
||||
|
|
|
|||
|
|
@ -3,20 +3,19 @@ use std::ptr;
|
|||
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode};
|
||||
use rustc_codegen_ssa::ModuleCodegen;
|
||||
use rustc_codegen_ssa::back::write::ModuleConfig;
|
||||
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods};
|
||||
use rustc_errors::FatalError;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::Lto;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::back::write::{llvm_err, llvm_optimize};
|
||||
use crate::builder::Builder;
|
||||
use crate::declare::declare_raw_fn;
|
||||
use crate::builder::SBuilder;
|
||||
use crate::context::SimpleCx;
|
||||
use crate::declare::declare_simple_fn;
|
||||
use crate::errors::LlvmError;
|
||||
use crate::llvm::AttributePlace::Function;
|
||||
use crate::llvm::{Metadata, True};
|
||||
use crate::value::Value;
|
||||
use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, context, llvm};
|
||||
use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm};
|
||||
|
||||
fn get_params(fnc: &Value) -> Vec<&Value> {
|
||||
unsafe {
|
||||
|
|
@ -38,8 +37,8 @@ fn get_params(fnc: &Value) -> Vec<&Value> {
|
|||
/// [^1]: <https://enzyme.mit.edu/getting_started/CallingConvention/>
|
||||
// FIXME(ZuseZ4): `outer_fn` should include upstream safety checks to
|
||||
// cover some assumptions of enzyme/autodiff, which could lead to UB otherwise.
|
||||
fn generate_enzyme_call<'ll, 'tcx>(
|
||||
cx: &context::CodegenCx<'ll, 'tcx>,
|
||||
fn generate_enzyme_call<'ll>(
|
||||
cx: &SimpleCx<'ll>,
|
||||
fn_to_diff: &'ll Value,
|
||||
outer_fn: &'ll Value,
|
||||
attrs: AutoDiffAttrs,
|
||||
|
|
@ -112,7 +111,7 @@ fn generate_enzyme_call<'ll, 'tcx>(
|
|||
//FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and
|
||||
// think a bit more about what should go here.
|
||||
let cc = llvm::LLVMGetFunctionCallConv(outer_fn);
|
||||
let ad_fn = declare_raw_fn(
|
||||
let ad_fn = declare_simple_fn(
|
||||
cx,
|
||||
&ad_name,
|
||||
llvm::CallConv::try_from(cc).expect("invalid callconv"),
|
||||
|
|
@ -132,7 +131,7 @@ fn generate_enzyme_call<'ll, 'tcx>(
|
|||
llvm::LLVMRustEraseInstFromParent(br);
|
||||
|
||||
let last_inst = llvm::LLVMRustGetLastInstruction(entry).unwrap();
|
||||
let mut builder = Builder::build(cx, entry);
|
||||
let mut builder = SBuilder::build(cx, entry);
|
||||
|
||||
let num_args = llvm::LLVMCountParams(&fn_to_diff);
|
||||
let mut args = Vec::with_capacity(num_args as usize + 1);
|
||||
|
|
@ -236,7 +235,7 @@ fn generate_enzyme_call<'ll, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None);
|
||||
let call = builder.call(enzyme_ty, ad_fn, &args, None);
|
||||
|
||||
// This part is a bit iffy. LLVM requires that a call to an inlineable function has some
|
||||
// metadata attachted to it, but we just created this code oota. Given that the
|
||||
|
|
@ -274,10 +273,9 @@ fn generate_enzyme_call<'ll, 'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn differentiate<'ll, 'tcx>(
|
||||
pub(crate) fn differentiate<'ll>(
|
||||
module: &'ll ModuleCodegen<ModuleLlvm>,
|
||||
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
diff_items: Vec<AutoDiffItem>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<(), FatalError> {
|
||||
|
|
@ -286,8 +284,7 @@ pub(crate) fn differentiate<'ll, 'tcx>(
|
|||
}
|
||||
|
||||
let diag_handler = cgcx.create_dcx();
|
||||
let (_, cgus) = tcx.collect_and_partition_mono_items(());
|
||||
let cx = context::CodegenCx::new(tcx, &cgus.first().unwrap(), &module.module_llvm);
|
||||
let cx = SimpleCx { llmod: module.module_llvm.llmod(), llcx: module.module_llvm.llcx };
|
||||
|
||||
// Before dumping the module, we want all the TypeTrees to become part of the module.
|
||||
for item in diff_items.iter() {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::ffi::{CStr, c_char, c_uint};
|
||||
use std::ops::Deref;
|
||||
use std::str;
|
||||
|
||||
use rustc_abi::{HasDataLayout, TargetDataLayout, VariantIdx};
|
||||
use rustc_codegen_ssa::back::versioned_llvm_target;
|
||||
use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
|
||||
use rustc_codegen_ssa::common::TypeKind;
|
||||
use rustc_codegen_ssa::errors as ssa_errors;
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
|
||||
|
|
@ -30,23 +32,46 @@ use smallvec::SmallVec;
|
|||
|
||||
use crate::back::write::to_llvm_code_model;
|
||||
use crate::callee::get_fn;
|
||||
use crate::common::AsCCharPtr;
|
||||
use crate::common::{self, AsCCharPtr};
|
||||
use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
|
||||
use crate::llvm::{Metadata, MetadataType};
|
||||
use crate::type_::Type;
|
||||
use crate::value::Value;
|
||||
use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util};
|
||||
|
||||
/// `TyCtxt` (and related cache datastructures) can't be move between threads.
|
||||
/// However, there are various cx related functions which we want to be available to the builder and
|
||||
/// other compiler pieces. Here we define a small subset which has enough information and can be
|
||||
/// moved around more freely.
|
||||
pub(crate) struct SimpleCx<'ll> {
|
||||
pub llmod: &'ll llvm::Module,
|
||||
pub llcx: &'ll llvm::Context,
|
||||
}
|
||||
|
||||
impl<'ll> Borrow<SimpleCx<'ll>> for CodegenCx<'ll, '_> {
|
||||
fn borrow(&self) -> &SimpleCx<'ll> {
|
||||
&self.scx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> {
|
||||
type Target = SimpleCx<'ll>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.scx
|
||||
}
|
||||
}
|
||||
|
||||
/// There is one `CodegenCx` per codegen unit. Each one has its own LLVM
|
||||
/// `llvm::Context` so that several codegen units may be processed in parallel.
|
||||
/// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`.
|
||||
pub(crate) struct CodegenCx<'ll, 'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
pub scx: SimpleCx<'ll>,
|
||||
pub use_dll_storage_attrs: bool,
|
||||
pub tls_model: llvm::ThreadLocalMode,
|
||||
|
||||
pub llmod: &'ll llvm::Module,
|
||||
pub llcx: &'ll llvm::Context,
|
||||
pub codegen_unit: &'tcx CodegenUnit<'tcx>,
|
||||
|
||||
/// Cache instances of monomorphic and polymorphic items
|
||||
|
|
@ -553,10 +578,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
|
||||
CodegenCx {
|
||||
tcx,
|
||||
scx: SimpleCx { llcx, llmod },
|
||||
use_dll_storage_attrs,
|
||||
tls_model,
|
||||
llmod,
|
||||
llcx,
|
||||
codegen_unit,
|
||||
instances: Default::default(),
|
||||
vtables: Default::default(),
|
||||
|
|
@ -600,6 +624,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
llvm::set_section(g, c"llvm.metadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'ll> SimpleCx<'ll> {
|
||||
pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type {
|
||||
common::val_ty(v)
|
||||
}
|
||||
|
||||
pub(crate) fn get_metadata_value(&self, metadata: &'ll Metadata) -> &'ll Value {
|
||||
unsafe { llvm::LLVMMetadataAsValue(self.llcx, metadata) }
|
||||
|
|
@ -625,6 +654,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
llvm::LLVMMDStringInContext2(self.llcx, name.as_ptr() as *const c_char, name.len())
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn type_kind(&self, ty: &'ll Type) -> TypeKind {
|
||||
unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
|
|
@ -1178,6 +1211,20 @@ impl CodegenCx<'_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
// This is a duplication of the set_metadata function above. However, so far it's the only one
|
||||
// shared between both contexts, so it doesn't seem worth it to make the Cx generic like we did it
|
||||
// for the Builder.
|
||||
impl SimpleCx<'_> {
|
||||
#[allow(unused)]
|
||||
/// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`.
|
||||
pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) {
|
||||
unsafe {
|
||||
let node = llvm::LLVMMetadataAsValue(&self.llcx, md);
|
||||
llvm::LLVMSetMetadata(val, kind_id as c_uint, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDataLayout for CodegenCx<'_, '_> {
|
||||
#[inline]
|
||||
fn data_layout(&self) -> &TargetDataLayout {
|
||||
|
|
|
|||
|
|
@ -21,26 +21,26 @@ use tracing::debug;
|
|||
|
||||
use crate::abi::{FnAbi, FnAbiLlvmExt};
|
||||
use crate::common::AsCCharPtr;
|
||||
use crate::context::CodegenCx;
|
||||
use crate::context::{CodegenCx, SimpleCx};
|
||||
use crate::llvm::AttributePlace::Function;
|
||||
use crate::llvm::Visibility;
|
||||
use crate::type_::Type;
|
||||
use crate::value::Value;
|
||||
use crate::{attributes, llvm};
|
||||
|
||||
/// Declare a function.
|
||||
/// Declare a function with a SimpleCx.
|
||||
///
|
||||
/// If there’s a value with the same name already declared, the function will
|
||||
/// update the declaration and return existing Value instead.
|
||||
pub(crate) fn declare_raw_fn<'ll>(
|
||||
cx: &CodegenCx<'ll, '_>,
|
||||
pub(crate) fn declare_simple_fn<'ll>(
|
||||
cx: &SimpleCx<'ll>,
|
||||
name: &str,
|
||||
callconv: llvm::CallConv,
|
||||
unnamed: llvm::UnnamedAddr,
|
||||
visibility: llvm::Visibility,
|
||||
ty: &'ll Type,
|
||||
) -> &'ll Value {
|
||||
debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty);
|
||||
debug!("declare_simple_fn(name={:?}, ty={:?})", name, ty);
|
||||
let llfn = unsafe {
|
||||
llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_c_char_ptr(), name.len(), ty)
|
||||
};
|
||||
|
|
@ -49,6 +49,24 @@ pub(crate) fn declare_raw_fn<'ll>(
|
|||
llvm::SetUnnamedAddress(llfn, unnamed);
|
||||
llvm::set_visibility(llfn, visibility);
|
||||
|
||||
llfn
|
||||
}
|
||||
|
||||
/// Declare a function.
|
||||
///
|
||||
/// If there’s a value with the same name already declared, the function will
|
||||
/// update the declaration and return existing Value instead.
|
||||
pub(crate) fn declare_raw_fn<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
name: &str,
|
||||
callconv: llvm::CallConv,
|
||||
unnamed: llvm::UnnamedAddr,
|
||||
visibility: llvm::Visibility,
|
||||
ty: &'ll Type,
|
||||
) -> &'ll Value {
|
||||
debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty);
|
||||
let llfn = declare_simple_fn(cx, name, callconv, unnamed, visibility, ty);
|
||||
|
||||
let mut attrs = SmallVec::<[_; 4]>::new();
|
||||
|
||||
if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.disable_redzone) {
|
||||
|
|
|
|||
|
|
@ -1081,11 +1081,11 @@ fn codegen_emcc_try<'ll>(
|
|||
|
||||
// Helper function to give a Block to a closure to codegen a shim function.
|
||||
// This is currently primarily used for the `try` intrinsic functions above.
|
||||
fn gen_fn<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
fn gen_fn<'a, 'll, 'tcx>(
|
||||
cx: &'a CodegenCx<'ll, 'tcx>,
|
||||
name: &str,
|
||||
rust_fn_sig: ty::PolyFnSig<'tcx>,
|
||||
codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>),
|
||||
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
|
||||
) -> (&'ll Type, &'ll Value) {
|
||||
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
|
||||
let llty = fn_abi.llvm_type(cx);
|
||||
|
|
@ -1104,9 +1104,9 @@ fn gen_fn<'ll, 'tcx>(
|
|||
// catch exceptions.
|
||||
//
|
||||
// This function is only generated once and is then cached.
|
||||
fn get_rust_try_fn<'ll, 'tcx>(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>),
|
||||
fn get_rust_try_fn<'a, 'll, 'tcx>(
|
||||
cx: &'a CodegenCx<'ll, 'tcx>,
|
||||
codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>),
|
||||
) -> (&'ll Type, &'ll Value) {
|
||||
if let Some(llfn) = cx.rust_try_fn.get() {
|
||||
return llfn;
|
||||
|
|
|
|||
|
|
@ -237,7 +237,6 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
/// Generate autodiff rules
|
||||
fn autodiff(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
tcx: TyCtxt<'_>,
|
||||
module: &ModuleCodegen<Self::Module>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
config: &ModuleConfig,
|
||||
|
|
@ -246,7 +245,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
let dcx = cgcx.create_dcx();
|
||||
return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO));
|
||||
}
|
||||
builder::autodiff::differentiate(module, cgcx, tcx, diff_fncs, config)
|
||||
builder::autodiff::differentiate(module, cgcx, diff_fncs, config)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Ty};
|
|||
use rustc_target::callconv::{CastTarget, FnAbi, Reg};
|
||||
|
||||
use crate::abi::{FnAbiLlvmExt, LlvmType};
|
||||
use crate::context::CodegenCx;
|
||||
use crate::context::{CodegenCx, SimpleCx};
|
||||
pub(crate) use crate::llvm::Type;
|
||||
use crate::llvm::{Bool, False, Metadata, True};
|
||||
use crate::type_of::LayoutLlvmExt;
|
||||
|
|
@ -35,7 +35,8 @@ impl fmt::Debug for Type {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'ll> CodegenCx<'ll, '_> {
|
||||
impl<'ll> CodegenCx<'ll, '_> {}
|
||||
impl<'ll> SimpleCx<'ll> {
|
||||
pub(crate) fn type_named_struct(&self, name: &str) -> &'ll Type {
|
||||
let name = SmallCStr::new(name);
|
||||
unsafe { llvm::LLVMStructCreateNamed(self.llcx, name.as_ptr()) }
|
||||
|
|
@ -44,11 +45,9 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
pub(crate) fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) {
|
||||
unsafe { llvm::LLVMStructSetBody(ty, els.as_ptr(), els.len() as c_uint, packed as Bool) }
|
||||
}
|
||||
|
||||
pub(crate) fn type_void(&self) -> &'ll Type {
|
||||
unsafe { llvm::LLVMVoidTypeInContext(self.llcx) }
|
||||
}
|
||||
|
||||
pub(crate) fn type_token(&self) -> &'ll Type {
|
||||
unsafe { llvm::LLVMTokenTypeInContext(self.llcx) }
|
||||
}
|
||||
|
|
@ -75,7 +74,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
args
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
||||
pub(crate) fn type_bool(&self) -> &'ll Type {
|
||||
self.type_i8()
|
||||
}
|
||||
|
|
@ -120,7 +120,8 @@ impl<'ll> CodegenCx<'ll, '_> {
|
|||
assert_eq!(size % unit_size, 0);
|
||||
self.type_array(self.type_from_integer(unit), size / unit_size)
|
||||
}
|
||||
|
||||
}
|
||||
impl<'ll> SimpleCx<'ll> {
|
||||
pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type {
|
||||
unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use std::sync::Arc;
|
|||
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_errors::FatalError;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use super::write::CodegenContext;
|
||||
use crate::ModuleCodegen;
|
||||
|
|
@ -89,13 +88,12 @@ impl<B: WriteBackendMethods> LtoModuleCodegen<B> {
|
|||
pub unsafe fn autodiff(
|
||||
self,
|
||||
cgcx: &CodegenContext<B>,
|
||||
tcx: TyCtxt<'_>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
config: &ModuleConfig,
|
||||
) -> Result<LtoModuleCodegen<B>, FatalError> {
|
||||
match &self {
|
||||
LtoModuleCodegen::Fat(module) => {
|
||||
B::autodiff(cgcx, tcx, &module, diff_fncs, config)?;
|
||||
B::autodiff(cgcx, &module, diff_fncs, config)?;
|
||||
}
|
||||
_ => panic!("autodiff called with non-fat LTO module"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use rustc_ast::expand::autodiff_attrs::AutoDiffItem;
|
||||
use rustc_errors::{DiagCtxtHandle, FatalError};
|
||||
use rustc_middle::dep_graph::WorkProduct;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
|
||||
use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig};
|
||||
|
|
@ -65,7 +64,6 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
|
|||
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer);
|
||||
fn autodiff(
|
||||
cgcx: &CodegenContext<Self>,
|
||||
tcx: TyCtxt<'_>,
|
||||
module: &ModuleCodegen<Self::Module>,
|
||||
diff_fncs: Vec<AutoDiffItem>,
|
||||
config: &ModuleConfig,
|
||||
|
|
|
|||
|
|
@ -785,6 +785,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let local =
|
||||
place.as_local().unwrap_or_else(|| bug!("projection in tail call args"));
|
||||
|
||||
if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(DropData { source_info, local, kind: DropKind::Value })
|
||||
}
|
||||
Operand::Constant(_) => None,
|
||||
|
|
@ -795,6 +799,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope,
|
||||
DUMMY_SP,
|
||||
);
|
||||
let typing_env = self.typing_env();
|
||||
let unwind_drops = &mut self.scopes.unwind_drops;
|
||||
|
||||
// the innermost scope contains only the destructors for the tail call arguments
|
||||
|
|
@ -805,6 +810,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let source_info = drop_data.source_info;
|
||||
let local = drop_data.local;
|
||||
|
||||
if !self.local_decls[local].ty.needs_drop(self.tcx, typing_env) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match drop_data.kind {
|
||||
DropKind::Value => {
|
||||
// `unwind_to` should drop the value that we're about to
|
||||
|
|
|
|||
|
|
@ -435,6 +435,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
|
||||
terr: TypeError<'tcx>,
|
||||
param_env: Option<ParamEnv<'tcx>>,
|
||||
path: &mut Option<PathBuf>,
|
||||
) {
|
||||
match *cause.code() {
|
||||
ObligationCauseCode::Pattern {
|
||||
|
|
@ -457,6 +458,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
format!("this is an iterator with items of type `{}`", args.type_at(0)),
|
||||
);
|
||||
} else {
|
||||
let expected_ty = self.tcx.short_ty_string(expected_ty, path);
|
||||
err.span_label(span, format!("this expression has type `{expected_ty}`"));
|
||||
}
|
||||
}
|
||||
|
|
@ -717,53 +719,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
value: &mut DiagStyledString,
|
||||
other_value: &mut DiagStyledString,
|
||||
name: String,
|
||||
sub: ty::GenericArgsRef<'tcx>,
|
||||
args: &[ty::GenericArg<'tcx>],
|
||||
pos: usize,
|
||||
other_ty: Ty<'tcx>,
|
||||
) {
|
||||
// `value` and `other_value` hold two incomplete type representation for display.
|
||||
// `name` is the path of both types being compared. `sub`
|
||||
value.push_highlighted(name);
|
||||
let len = sub.len();
|
||||
if len > 0 {
|
||||
value.push_highlighted("<");
|
||||
|
||||
if args.is_empty() {
|
||||
return;
|
||||
}
|
||||
value.push_highlighted("<");
|
||||
|
||||
// Output the lifetimes for the first type
|
||||
let lifetimes = sub
|
||||
.regions()
|
||||
.map(|lifetime| {
|
||||
let s = lifetime.to_string();
|
||||
if s.is_empty() { "'_".to_string() } else { s }
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
if !lifetimes.is_empty() {
|
||||
if sub.regions().count() < len {
|
||||
value.push_normal(lifetimes + ", ");
|
||||
} else {
|
||||
value.push_normal(lifetimes);
|
||||
}
|
||||
}
|
||||
|
||||
// Highlight all the type arguments that aren't at `pos` and compare the type argument at
|
||||
// `pos` and `other_ty`.
|
||||
for (i, type_arg) in sub.types().enumerate() {
|
||||
if i == pos {
|
||||
let values = self.cmp(type_arg, other_ty);
|
||||
value.0.extend((values.0).0);
|
||||
other_value.0.extend((values.1).0);
|
||||
} else {
|
||||
value.push_highlighted(type_arg.to_string());
|
||||
}
|
||||
|
||||
if len > 0 && i != len - 1 {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
value.push_normal(", ");
|
||||
}
|
||||
|
||||
match arg.unpack() {
|
||||
ty::GenericArgKind::Lifetime(lt) => {
|
||||
let s = lt.to_string();
|
||||
value.push_normal(if s.is_empty() { "'_" } else { &s });
|
||||
}
|
||||
ty::GenericArgKind::Const(ct) => {
|
||||
value.push_normal(ct.to_string());
|
||||
}
|
||||
// Highlight all the type arguments that aren't at `pos` and compare
|
||||
// the type argument at `pos` and `other_ty`.
|
||||
ty::GenericArgKind::Type(type_arg) => {
|
||||
if i == pos {
|
||||
let values = self.cmp(type_arg, other_ty);
|
||||
value.0.extend((values.0).0);
|
||||
other_value.0.extend((values.1).0);
|
||||
} else {
|
||||
value.push_highlighted(type_arg.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len > 0 {
|
||||
value.push_highlighted(">");
|
||||
}
|
||||
|
||||
value.push_highlighted(">");
|
||||
}
|
||||
|
||||
/// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`,
|
||||
|
|
@ -791,27 +787,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
t1_out: &mut DiagStyledString,
|
||||
t2_out: &mut DiagStyledString,
|
||||
path: String,
|
||||
sub: &'tcx [ty::GenericArg<'tcx>],
|
||||
args: &'tcx [ty::GenericArg<'tcx>],
|
||||
other_path: String,
|
||||
other_ty: Ty<'tcx>,
|
||||
) -> Option<()> {
|
||||
// FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods,
|
||||
// ideally that shouldn't be necessary.
|
||||
let sub = self.tcx.mk_args(sub);
|
||||
for (i, ta) in sub.types().enumerate() {
|
||||
if ta == other_ty {
|
||||
self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty);
|
||||
return Some(());
|
||||
}
|
||||
if let ty::Adt(def, _) = ta.kind() {
|
||||
let path_ = self.tcx.def_path_str(def.did());
|
||||
if path_ == other_path {
|
||||
self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty);
|
||||
return Some(());
|
||||
) -> bool {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if let Some(ta) = arg.as_type() {
|
||||
if ta == other_ty {
|
||||
self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);
|
||||
return true;
|
||||
}
|
||||
if let ty::Adt(def, _) = ta.kind() {
|
||||
let path_ = self.tcx.def_path_str(def.did());
|
||||
if path_ == other_path {
|
||||
self.highlight_outer(t1_out, t2_out, path, args, i, other_ty);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
false
|
||||
}
|
||||
|
||||
/// Adds a `,` to the type representation only if it is appropriate.
|
||||
|
|
@ -819,10 +814,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
&self,
|
||||
value: &mut DiagStyledString,
|
||||
other_value: &mut DiagStyledString,
|
||||
len: usize,
|
||||
pos: usize,
|
||||
) {
|
||||
if len > 0 && pos != len - 1 {
|
||||
if pos > 0 {
|
||||
value.push_normal(", ");
|
||||
other_value.push_normal(", ");
|
||||
}
|
||||
|
|
@ -899,10 +893,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
let len2 = sig2.inputs().len();
|
||||
if len1 == len2 {
|
||||
for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() {
|
||||
self.push_comma(&mut values.0, &mut values.1, i);
|
||||
let (x1, x2) = self.cmp(*l, *r);
|
||||
(values.0).0.extend(x1.0);
|
||||
(values.1).0.extend(x2.0);
|
||||
self.push_comma(&mut values.0, &mut values.1, len1, i);
|
||||
}
|
||||
} else {
|
||||
for (i, l) in sig1.inputs().iter().enumerate() {
|
||||
|
|
@ -1150,14 +1144,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
let len1 = sub_no_defaults_1.len();
|
||||
let len2 = sub_no_defaults_2.len();
|
||||
let common_len = cmp::min(len1, len2);
|
||||
let remainder1: Vec<_> = sub1.types().skip(common_len).collect();
|
||||
let remainder2: Vec<_> = sub2.types().skip(common_len).collect();
|
||||
let remainder1 = &sub1[common_len..];
|
||||
let remainder2 = &sub2[common_len..];
|
||||
let common_default_params =
|
||||
iter::zip(remainder1.iter().rev(), remainder2.iter().rev())
|
||||
.filter(|(a, b)| a == b)
|
||||
.count();
|
||||
let len = sub1.len() - common_default_params;
|
||||
let consts_offset = len - sub1.consts().count();
|
||||
|
||||
// Only draw `<...>` if there are lifetime/type arguments.
|
||||
if len > 0 {
|
||||
|
|
@ -1169,70 +1162,68 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
let s = lifetime.to_string();
|
||||
if s.is_empty() { "'_".to_string() } else { s }
|
||||
}
|
||||
// At one point we'd like to elide all lifetimes here, they are irrelevant for
|
||||
// all diagnostics that use this output
|
||||
//
|
||||
// Foo<'x, '_, Bar>
|
||||
// Foo<'y, '_, Qux>
|
||||
// ^^ ^^ --- type arguments are not elided
|
||||
// | |
|
||||
// | elided as they were the same
|
||||
// not elided, they were different, but irrelevant
|
||||
//
|
||||
// For bound lifetimes, keep the names of the lifetimes,
|
||||
// even if they are the same so that it's clear what's happening
|
||||
// if we have something like
|
||||
//
|
||||
// for<'r, 's> fn(Inv<'r>, Inv<'s>)
|
||||
// for<'r> fn(Inv<'r>, Inv<'r>)
|
||||
let lifetimes = sub1.regions().zip(sub2.regions());
|
||||
for (i, lifetimes) in lifetimes.enumerate() {
|
||||
let l1 = lifetime_display(lifetimes.0);
|
||||
let l2 = lifetime_display(lifetimes.1);
|
||||
if lifetimes.0 != lifetimes.1 {
|
||||
values.0.push_highlighted(l1);
|
||||
values.1.push_highlighted(l2);
|
||||
} else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose {
|
||||
values.0.push_normal(l1);
|
||||
values.1.push_normal(l2);
|
||||
} else {
|
||||
values.0.push_normal("'_");
|
||||
values.1.push_normal("'_");
|
||||
}
|
||||
self.push_comma(&mut values.0, &mut values.1, len, i);
|
||||
}
|
||||
|
||||
// We're comparing two types with the same path, so we compare the type
|
||||
// arguments for both. If they are the same, do not highlight and elide from the
|
||||
// output.
|
||||
// Foo<_, Bar>
|
||||
// Foo<_, Qux>
|
||||
// ^ elided type as this type argument was the same in both sides
|
||||
let type_arguments = sub1.types().zip(sub2.types());
|
||||
let regions_len = sub1.regions().count();
|
||||
let num_display_types = consts_offset - regions_len;
|
||||
for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() {
|
||||
let i = i + regions_len;
|
||||
if ta1 == ta2 && !self.tcx.sess.opts.verbose {
|
||||
values.0.push_normal("_");
|
||||
values.1.push_normal("_");
|
||||
} else {
|
||||
recurse(ta1, ta2, &mut values);
|
||||
}
|
||||
self.push_comma(&mut values.0, &mut values.1, len, i);
|
||||
}
|
||||
for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) {
|
||||
self.push_comma(&mut values.0, &mut values.1, i);
|
||||
match arg1.unpack() {
|
||||
// At one point we'd like to elide all lifetimes here, they are
|
||||
// irrelevant for all diagnostics that use this output.
|
||||
//
|
||||
// Foo<'x, '_, Bar>
|
||||
// Foo<'y, '_, Qux>
|
||||
// ^^ ^^ --- type arguments are not elided
|
||||
// | |
|
||||
// | elided as they were the same
|
||||
// not elided, they were different, but irrelevant
|
||||
//
|
||||
// For bound lifetimes, keep the names of the lifetimes,
|
||||
// even if they are the same so that it's clear what's happening
|
||||
// if we have something like
|
||||
//
|
||||
// for<'r, 's> fn(Inv<'r>, Inv<'s>)
|
||||
// for<'r> fn(Inv<'r>, Inv<'r>)
|
||||
ty::GenericArgKind::Lifetime(l1) => {
|
||||
let l1_str = lifetime_display(l1);
|
||||
let l2 = arg2.expect_region();
|
||||
let l2_str = lifetime_display(l2);
|
||||
if l1 != l2 {
|
||||
values.0.push_highlighted(l1_str);
|
||||
values.1.push_highlighted(l2_str);
|
||||
} else if l1.is_bound() || self.tcx.sess.opts.verbose {
|
||||
values.0.push_normal(l1_str);
|
||||
values.1.push_normal(l2_str);
|
||||
} else {
|
||||
values.0.push_normal("'_");
|
||||
values.1.push_normal("'_");
|
||||
}
|
||||
}
|
||||
ty::GenericArgKind::Type(ta1) => {
|
||||
let ta2 = arg2.expect_ty();
|
||||
if ta1 == ta2 && !self.tcx.sess.opts.verbose {
|
||||
values.0.push_normal("_");
|
||||
values.1.push_normal("_");
|
||||
} else {
|
||||
recurse(ta1, ta2, &mut values);
|
||||
}
|
||||
}
|
||||
// We're comparing two types with the same path, so we compare the type
|
||||
// arguments for both. If they are the same, do not highlight and elide
|
||||
// from the output.
|
||||
// Foo<_, Bar>
|
||||
// Foo<_, Qux>
|
||||
// ^ elided type as this type argument was the same in both sides
|
||||
|
||||
// Do the same for const arguments, if they are equal, do not highlight and
|
||||
// elide them from the output.
|
||||
let const_arguments = sub1.consts().zip(sub2.consts());
|
||||
for (i, (ca1, ca2)) in const_arguments.enumerate() {
|
||||
let i = i + consts_offset;
|
||||
maybe_highlight(ca1, ca2, &mut values, self.tcx);
|
||||
self.push_comma(&mut values.0, &mut values.1, len, i);
|
||||
// Do the same for const arguments, if they are equal, do not highlight and
|
||||
// elide them from the output.
|
||||
ty::GenericArgKind::Const(ca1) => {
|
||||
let ca2 = arg2.expect_const();
|
||||
maybe_highlight(ca1, ca2, &mut values, self.tcx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the type argument bracket.
|
||||
// Only draw `<...>` if there are lifetime/type arguments.
|
||||
// Only draw `<...>` if there are arguments.
|
||||
if len > 0 {
|
||||
values.0.push_normal(">");
|
||||
values.1.push_normal(">");
|
||||
|
|
@ -1244,17 +1235,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
// Foo<Bar<Qux>
|
||||
// ------- this type argument is exactly the same as the other type
|
||||
// Bar<Qux>
|
||||
if self
|
||||
.cmp_type_arg(
|
||||
&mut values.0,
|
||||
&mut values.1,
|
||||
path1.clone(),
|
||||
sub_no_defaults_1,
|
||||
path2.clone(),
|
||||
t2,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
if self.cmp_type_arg(
|
||||
&mut values.0,
|
||||
&mut values.1,
|
||||
path1.clone(),
|
||||
sub_no_defaults_1,
|
||||
path2.clone(),
|
||||
t2,
|
||||
) {
|
||||
return values;
|
||||
}
|
||||
// Check for case:
|
||||
|
|
@ -1262,17 +1250,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
// Bar<Qux>
|
||||
// Foo<Bar<Qux>>
|
||||
// ------- this type argument is exactly the same as the other type
|
||||
if self
|
||||
.cmp_type_arg(
|
||||
&mut values.1,
|
||||
&mut values.0,
|
||||
path2,
|
||||
sub_no_defaults_2,
|
||||
path1,
|
||||
t1,
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
if self.cmp_type_arg(
|
||||
&mut values.1,
|
||||
&mut values.0,
|
||||
path2,
|
||||
sub_no_defaults_2,
|
||||
path1,
|
||||
t1,
|
||||
) {
|
||||
return values;
|
||||
}
|
||||
|
||||
|
|
@ -1343,8 +1328,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("("));
|
||||
let len = args1.len();
|
||||
for (i, (left, right)) in args1.iter().zip(args2).enumerate() {
|
||||
self.push_comma(&mut values.0, &mut values.1, i);
|
||||
recurse(left, right, &mut values);
|
||||
self.push_comma(&mut values.0, &mut values.1, len, i);
|
||||
}
|
||||
if len == 1 {
|
||||
// Keep the output for single element tuples as `(ty,)`.
|
||||
|
|
@ -1611,7 +1596,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some((expected, found, path)) = expected_found {
|
||||
let mut path = None;
|
||||
if let Some((expected, found, p)) = expected_found {
|
||||
path = p;
|
||||
let (expected_label, found_label, exp_found) = match exp_found {
|
||||
Mismatch::Variable(ef) => (
|
||||
ef.expected.prefix_string(self.tcx),
|
||||
|
|
@ -1792,13 +1779,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
&sort_string(values.expected),
|
||||
&sort_string(values.found),
|
||||
);
|
||||
if let Some(path) = path {
|
||||
diag.note(format!(
|
||||
"the full type name has been written to '{}'",
|
||||
path.display(),
|
||||
));
|
||||
diag.note("consider using `--verbose` to print the full type name to the console");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1894,7 +1874,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
|
||||
// It reads better to have the error origin as the final
|
||||
// thing.
|
||||
self.note_error_origin(diag, cause, exp_found, terr, param_env);
|
||||
self.note_error_origin(diag, cause, exp_found, terr, param_env, &mut path);
|
||||
if let Some(path) = path {
|
||||
diag.note(format!("the full type name has been written to '{}'", path.display()));
|
||||
diag.note("consider using `--verbose` to print the full type name to the console");
|
||||
}
|
||||
|
||||
debug!(?diag);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2571,9 +2571,11 @@ impl<T, A: Allocator> Vec<T, A> {
|
|||
self.len += count;
|
||||
}
|
||||
|
||||
/// Removes the specified range from the vector in bulk, returning all
|
||||
/// removed elements as an iterator. If the iterator is dropped before
|
||||
/// being fully consumed, it drops the remaining removed elements.
|
||||
/// Removes the subslice indicated by the given range from the vector,
|
||||
/// returning a double-ended iterator over the removed subslice.
|
||||
///
|
||||
/// If the iterator is dropped before being fully consumed,
|
||||
/// it drops the remaining removed elements.
|
||||
///
|
||||
/// The returned iterator keeps a mutable borrow on the vector to optimize
|
||||
/// its implementation.
|
||||
|
|
|
|||
|
|
@ -90,6 +90,26 @@ impl_zeroable_primitive!(
|
|||
///
|
||||
/// assert_eq!(size_of::<Option<NonZero<u32>>>(), size_of::<u32>());
|
||||
/// ```
|
||||
///
|
||||
/// # Layout
|
||||
///
|
||||
/// `NonZero<T>` is guaranteed to have the same layout and bit validity as `T`
|
||||
/// with the exception that the all-zero bit pattern is invalid.
|
||||
/// `Option<NonZero<T>>` is guaranteed to be compatible with `T`, including in
|
||||
/// FFI.
|
||||
///
|
||||
/// Thanks to the [null pointer optimization], `NonZero<T>` and
|
||||
/// `Option<NonZero<T>>` are guaranteed to have the same size and alignment:
|
||||
///
|
||||
/// ```
|
||||
/// # use std::mem::{size_of, align_of};
|
||||
/// use std::num::NonZero;
|
||||
///
|
||||
/// assert_eq!(size_of::<NonZero<u32>>(), size_of::<Option<NonZero<u32>>>());
|
||||
/// assert_eq!(align_of::<NonZero<u32>>(), align_of::<Option<NonZero<u32>>>());
|
||||
/// ```
|
||||
///
|
||||
/// [null pointer optimization]: crate::option#representation
|
||||
#[stable(feature = "generic_nonzero", since = "1.79.0")]
|
||||
#[repr(transparent)]
|
||||
#[rustc_nonnull_optimization_guaranteed]
|
||||
|
|
|
|||
|
|
@ -2529,6 +2529,7 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
|
|||
/// limited to just these cases:
|
||||
///
|
||||
/// * The `original` path is not a file or doesn't exist.
|
||||
/// * The 'link' path already exists.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
|||
19
src/doc/rustc-dev-guide/.github/workflows/ci.yml
vendored
19
src/doc/rustc-dev-guide/.github/workflows/ci.yml
vendored
|
|
@ -35,12 +35,13 @@ jobs:
|
|||
~/.cargo/bin
|
||||
key: ${{ runner.os }}-${{ env.MDBOOK_VERSION }}--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ env.MDBOOK_TOC_VERSION }}--${{ env.MDBOOK_MERMAID_VERSION }}
|
||||
|
||||
- name: Cache linkcheck
|
||||
uses: actions/cache@v4
|
||||
- name: Restore cached Linkcheck
|
||||
if: github.event_name == 'schedule'
|
||||
id: cache-linkcheck-restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: |
|
||||
~/book/linkcheck
|
||||
key: ${{ runner.os }}-${{ hashFiles('./book/linkcheck') }}
|
||||
path: book/linkcheck/cache.json
|
||||
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}
|
||||
|
||||
- name: Install latest nightly Rust toolchain
|
||||
if: steps.mdbook-cache.outputs.cache-hit != 'true'
|
||||
|
|
@ -59,6 +60,14 @@ jobs:
|
|||
- name: Check build
|
||||
run: ENABLE_LINKCHECK=1 mdbook build
|
||||
|
||||
- name: Save cached Linkcheck
|
||||
id: cache-linkcheck-save
|
||||
if: ${{ !cancelled() && github.event_name == 'schedule' }}
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: book/linkcheck/cache.json
|
||||
key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }}
|
||||
|
||||
- name: Deploy to gh-pages
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
|
|
|
|||
85
src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
vendored
Normal file
85
src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
name: rustc-pull
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Run at 04:00 UTC every Monday
|
||||
- cron: '0 4 * * 1'
|
||||
|
||||
jobs:
|
||||
pull:
|
||||
if: github.repository == 'rust-lang/rustc-dev-guide'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
pr_url: ${{ steps.update-pr.outputs.pr_url }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# We need the full history for josh to work
|
||||
fetch-depth: '0'
|
||||
- name: Install stable Rust toolchain
|
||||
run: rustup update stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "josh-sync"
|
||||
# Cache the josh directory with checked out rustc
|
||||
cache-directories: "/home/runner/.cache/rustc-dev-guide-josh"
|
||||
- name: Install josh
|
||||
run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
|
||||
- name: Setup bot git name and email
|
||||
run: |
|
||||
git config --global user.name 'The rustc-dev-guide Cronjob Bot'
|
||||
git config --global user.email 'github-actions@github.com'
|
||||
- name: Perform rustc-pull
|
||||
run: cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull
|
||||
- name: Push changes to a branch
|
||||
run: |
|
||||
# Update a sticky branch that is used only for rustc pulls
|
||||
BRANCH="rustc-pull"
|
||||
git switch -c $BRANCH
|
||||
git push -u origin $BRANCH --force
|
||||
- name: Create pull request
|
||||
id: update-pr
|
||||
run: |
|
||||
# Check if an open pull request for an rustc pull update already exists
|
||||
# If it does, the previous push has just updated it
|
||||
# If not, we create it now
|
||||
RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title`
|
||||
if [[ "$RESULT" -eq 0 ]]; then
|
||||
echo "Creating new pull request"
|
||||
PR_URL=gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.'
|
||||
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
|
||||
else
|
||||
PR_URL=gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title
|
||||
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
send-zulip-message:
|
||||
needs: [pull]
|
||||
if: ${{ !cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Compute message
|
||||
id: message
|
||||
run: |
|
||||
if [ "${{ needs.pull.result }}" == "failure" ];
|
||||
then
|
||||
WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
echo "message=Rustc pull sync failed. Check out the [workflow URL]($WORKFLOW_URL)." >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "message=Rustc pull sync succeeded. Check out the [PR](${{ needs.pull.outputs.pr_url }})." >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Send a Zulip message about updated PR
|
||||
uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5
|
||||
with:
|
||||
api-key: ${{ secrets.ZULIP_API_TOKEN }}
|
||||
email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com"
|
||||
organization-url: "https://rust-lang.zulipchat.com"
|
||||
to: 196385
|
||||
type: "stream"
|
||||
topic: "Subtree sync automation"
|
||||
content: ${{ steps.message.outputs.message }}
|
||||
2
src/doc/rustc-dev-guide/.gitignore
vendored
2
src/doc/rustc-dev-guide/.gitignore
vendored
|
|
@ -4,3 +4,5 @@ ci/date-check/target/
|
|||
|
||||
# Generated by check-in.sh
|
||||
pulls.json
|
||||
|
||||
josh-sync/target
|
||||
|
|
|
|||
|
|
@ -74,6 +74,13 @@ including the `<!-- toc -->` marker at the place where you want the TOC.
|
|||
|
||||
This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the following commands to synchronize the subtree in both directions.
|
||||
|
||||
You'll need to install `josh-proxy` locally via
|
||||
|
||||
```
|
||||
cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04
|
||||
```
|
||||
Older versions of `josh-proxy` may not round trip commits losslessly so it is important to install this exact version.
|
||||
|
||||
### Pull changes from `rust-lang/rust` into this repository
|
||||
1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide`
|
||||
2) Run the pull command
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ exclude = [
|
|||
# 500 is returned for HEAD request
|
||||
"code\\.visualstudio\\.com/docs/editor/tasks",
|
||||
]
|
||||
cache-timeout = 86400
|
||||
# The scheduled CI runs every day and so we need to reuse a part of the cache
|
||||
# in order to face "Server returned 429 Too Many Requests" errors for github.com.
|
||||
cache-timeout = 90000
|
||||
warning-policy = "error"
|
||||
|
||||
[output.html.redirect]
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ impl GitSync {
|
|||
let josh_url =
|
||||
format!("http://localhost:{JOSH_PORT}/{UPSTREAM_REPO}.git@{commit}{JOSH_FILTER}.git");
|
||||
|
||||
let previous_base_commit = sh.read_file("rust-version")?.trim().to_string();
|
||||
if previous_base_commit == commit {
|
||||
return Err(anyhow::anyhow!("No changes since last pull"));
|
||||
}
|
||||
|
||||
// Update rust-version file. As a separate commit, since making it part of
|
||||
// the merge has confused the heck out of josh in the past.
|
||||
// We pass `--no-verify` to avoid running git hooks.
|
||||
|
|
@ -76,12 +81,22 @@ impl GitSync {
|
|||
};
|
||||
let num_roots_before = num_roots()?;
|
||||
|
||||
let sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout;
|
||||
|
||||
// Merge the fetched commit.
|
||||
const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc";
|
||||
cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}")
|
||||
.run()
|
||||
.context("FAILED to merge new commits, something went wrong")?;
|
||||
|
||||
let current_sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout;
|
||||
if current_sha == sha {
|
||||
cmd!(sh, "git reset --hard HEAD^")
|
||||
.run()
|
||||
.expect("FAILED to clean up after creating the preparation commit");
|
||||
return Err(anyhow::anyhow!("No merge was performed, nothing to pull. Rolled back the preparation commit."));
|
||||
}
|
||||
|
||||
// Check that the number of roots did not increase.
|
||||
if num_roots()? != num_roots_before {
|
||||
bail!("Josh created a new root commit. This is probably not the history you want.");
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
dcfa38fe234de9304169afc6638e81d0dd222c06
|
||||
ecda83b30f0f68cf5692855dddc0bc38ee8863fc
|
||||
|
|
|
|||
|
|
@ -14,17 +14,16 @@ Item | Kind | Short description | Chapter |
|
|||
`Diag` | struct | A struct for a compiler diagnostic, such as an error or lint | [Emitting Diagnostics] | [compiler/rustc_errors/src/diagnostic.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html)
|
||||
`DocContext` | struct | A state container used by rustdoc when crawling through a crate to gather its documentation | [Rustdoc] | [src/librustdoc/core.rs](https://github.com/rust-lang/rust/blob/master/src/librustdoc/core.rs)
|
||||
`HirId` | struct | One of four types of HIR node identifiers | [Identifiers in the HIR] | [compiler/rustc_hir/src/hir_id.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir_id/struct.HirId.html)
|
||||
`Lexer` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [compiler/rustc_parse/src/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html)
|
||||
`NodeId` | struct | One of four types of HIR node identifiers. Being phased out | [Identifiers in the HIR] | [compiler/rustc_ast/src/ast.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/node_id/struct.NodeId.html)
|
||||
`P` | struct | An owned immutable smart pointer. By contrast, `&T` is not owned, and `Box<T>` is not immutable. | None | [compiler/rustc_ast/src/ptr.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ptr/struct.P.html)
|
||||
`ParamEnv` | struct | Information about generic parameters or `Self`, useful for working with associated or generic items | [Parameter Environment] | [compiler/rustc_middle/src/ty/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html)
|
||||
`ParseSess` | struct | This struct contains information about a parsing session | [The parser] | [compiler/rustc_session/src/parse/parse.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html)
|
||||
`Query` | struct | Represents the result of query to the `Compiler` interface and allows stealing, borrowing, and returning the results of compiler passes. | [The Rustc Driver and Interface] | [compiler/rustc_interface/src/queries.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/queries/struct.Query.html)
|
||||
`Rib` | struct | Represents a single scope of names | [Name resolution] | [compiler/rustc_resolve/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.Rib.html)
|
||||
`Session` | struct | The data associated with a compilation session | [The parser], [The Rustc Driver and Interface] | [compiler/rustc_session/src/session.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html)
|
||||
`SourceFile` | struct | Part of the `SourceMap`. Maps AST nodes to their source code for a single source file. Was previously called FileMap | [The parser] | [compiler/rustc_span/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.SourceFile.html)
|
||||
`SourceMap` | struct | Maps AST nodes to their source code. It is composed of `SourceFile`s. Was previously called CodeMap | [The parser] | [compiler/rustc_span/src/source_map.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html)
|
||||
`Span` | struct | A location in the user's source code, used for error reporting primarily | [Emitting Diagnostics] | [compiler/rustc_span/src/span_encoding.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html)
|
||||
`StringReader` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [compiler/rustc_parse/src/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html)
|
||||
`rustc_ast::token_stream::TokenStream` | struct | An abstract sequence of tokens, organized into `TokenTree`s | [The parser], [Macro expansion] | [compiler/rustc_ast/src/tokenstream.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/struct.TokenStream.html)
|
||||
`TraitDef` | struct | This struct contains a trait's definition with type information | [The `ty` modules] | [compiler/rustc_middle/src/ty/trait_def.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait_def/struct.TraitDef.html)
|
||||
`TraitRef` | struct | The combination of a trait and its input types (e.g. `P0: Trait<P1...Pn>`) | [Trait Solving: Goals and Clauses] | [compiler/rustc_middle/src/ty/sty.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html)
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ Term | Meaning
|
|||
<span id="rib">rib</span> | A data structure in the name resolver that keeps track of a single scope for names. ([see more](../name-resolution.md))
|
||||
<span id="rpit">RPIT</span> | A return-position `impl Trait`. ([see the reference](https://doc.rust-lang.org/reference/types/impl-trait.html#abstract-return-types)).
|
||||
<span id="rpitit">RPITIT</span> | A return-position `impl Trait` in trait. Unlike RPIT, this is desugared to a generic associated type (GAT). Introduced in [RFC 3425](https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html). ([see more](../return-position-impl-trait-in-trait.md))
|
||||
<span id="scrutinee">scrutinee</div> | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee.
|
||||
<span id="scrutinee">scrutinee</span> | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee.
|
||||
<span id="sess">`sess`</span> | The compiler _session_, which stores global data used throughout compilation
|
||||
<span id="side-tables">side tables</span> | Because the [AST](#ast) and HIR are immutable once created, we often carry extra information about them in the form of hashtables, indexed by the id of a particular node.
|
||||
<span id="sigil">sigil</span> | Like a keyword but composed entirely of non-alphanumeric tokens. For example, `&` is a sigil for references.
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ heavily on other parts of the crate. The separation of the code must not affect
|
|||
the logic of the code nor its performance.
|
||||
|
||||
For these reasons, the separation process involves two transformations that
|
||||
have to be done at the same time for the resulting code to compile :
|
||||
have to be done at the same time for the resulting code to compile:
|
||||
|
||||
1. replace all the LLVM-specific types by generics inside function signatures
|
||||
and structure definitions;
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ It starts by calling `fn try_promote_type_test_subject`. This function takes the
|
|||
|
||||
We then promote the `lower_bound` into the context of the caller. If the lower bound is equal to a placeholder, we replace it with `'static`
|
||||
|
||||
We then look at all universal regions `uv` which are required to outlive `lower_bound`, i.e. for which borrow checking adding region constraints. For each of these we then emit a `ClosureOutlivesRequirement` for non-local universal regions which are known to outlive `uv`.
|
||||
We then look at all universal regions `uv` which are required to be outlived by `lower_bound`, i.e. for which borrow checking added region constraints. For each of these we then emit a `ClosureOutlivesRequirement` for all non-local universal regions which are known to outlive `uv`.
|
||||
|
||||
As we've already built the region graph of the closure at this point and emitted errors if that one is inconsistent, we are also able to assume that the outlive constraints `uv: lower_bound` hold.
|
||||
As we've already built the region graph of the closure at this point and separately check that it is consistent, we are also able to assume the outlive constraints `uv: lower_bound` here.
|
||||
|
||||
So if we have a type-outlives bounds we can't prove, e.g. `T: 'local_infer`, we use the region graph to go to universal variables `'a` with `'a: local_infer`. In case `'a` are local, we then use the assumed outlived constraints to go to non-local ones.
|
||||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ that we use for unstable features:
|
|||
Ideally, breaking changes should have landed on the **stable branch** of the
|
||||
compiler before they are finalized.
|
||||
|
||||
<a id="guide">
|
||||
<a id="guide"></a>
|
||||
|
||||
### Removing a lint
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ The other option is to step through the code using lldb or gdb.
|
|||
|
||||
1. `rust-lldb build/host/stage1/bin/rustc test.rs`
|
||||
2. In lldb:
|
||||
1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file`
|
||||
1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file
|
||||
2. `r` // Run the program until it hits the breakpoint
|
||||
|
||||
Let's start with [`upvar.rs`][upvar]. This file has something called
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ Prominent examples are:
|
|||
* need to be known to check for overlapping patterns
|
||||
|
||||
Additionally constant evaluation can be used to reduce the workload or binary
|
||||
size at runtime by precomputing complex operations at compiletime and only
|
||||
size at runtime by precomputing complex operations at compile time and only
|
||||
storing the result.
|
||||
|
||||
All uses of constant evaluation can either be categorized as "influencing the type system"
|
||||
|
|
|
|||
|
|
@ -422,68 +422,8 @@ Just a few things to keep in mind:
|
|||
|
||||
## Issue triage
|
||||
|
||||
Sometimes, an issue will stay open, even though the bug has been fixed.
|
||||
And sometimes, the original bug may go stale because something has changed in the meantime.
|
||||
Please see <https://forge.rust-lang.org/release/issue-triaging.html>.
|
||||
|
||||
It can be helpful to go through older bug reports and make sure that they are still valid.
|
||||
Load up an older issue, double check that it's still true,
|
||||
and leave a comment letting us know if it is or is not.
|
||||
The [least recently updated sort][lru] is good for finding issues like this.
|
||||
|
||||
[Thanks to `@rustbot`][rustbot], anyone can help triage issues by adding
|
||||
appropriate labels to issues that haven't been triaged yet:
|
||||
|
||||
[lru]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc
|
||||
[rustbot]: ./rustbot.md
|
||||
|
||||
<style>
|
||||
.label-color {
|
||||
border-radius:0.5em;
|
||||
}
|
||||
table td:nth-child(2) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
| Labels | Color | Description |
|
||||
|--------|-------|-------------|
|
||||
| [A-] | <span class="label-color" style="background-color:#f7e101;"> </span> Yellow | The **area** of the project an issue relates to. |
|
||||
| [B-] | <span class="label-color" style="background-color:#d304cb;"> </span> Magenta | Issues which are **blockers**. |
|
||||
| [beta-] | <span class="label-color" style="background-color:#1e76d9;"> </span> Dark Blue | Tracks changes which need to be [backported to beta][beta-backport] |
|
||||
| [C-] | <span class="label-color" style="background-color:#f5f1fd;"> </span> Light Purple | The **category** of an issue. |
|
||||
| [D-] | <span class="label-color" style="background-color:#c9f7a3;"> </span> Mossy Green | Issues for **diagnostics**. |
|
||||
| [E-] | <span class="label-color" style="background-color:#02e10c;"> </span> Green | The **experience** level necessary to fix an issue. |
|
||||
| [F-] | <span class="label-color" style="background-color:#f9c0cc;"> </span> Peach | Issues for **nightly features**. |
|
||||
| [I-] | <span class="label-color" style="background-color:#e10c02;"> </span> Red | The **importance** of the issue. |
|
||||
| [I-\*-nominated] | <span class="label-color" style="background-color:#e10c02;"> </span> Red | The issue has been nominated for discussion at the next meeting of the corresponding team. |
|
||||
| [I-prioritize] | <span class="label-color" style="background-color:#e10c02;"> </span> Red | The issue has been nominated for prioritization by the team tagged with a **T**-prefixed label. |
|
||||
| [L-] | <span class="label-color" style="background-color:#64E9CF;"> </span> Teal | The relevant **lint**. |
|
||||
| [metabug] | <span class="label-color" style="background-color:#5319e7;"> </span> Purple | Bugs that collect other bugs. |
|
||||
| [O-] | <span class="label-color" style="background-color:#6e6ec0;"> </span> Purple Grey | The **operating system** or platform that the issue is specific to. |
|
||||
| [P-] | <span class="label-color" style="background-color:#eb6420;"> </span> Orange | The issue **priority**. These labels can be assigned by anyone that understand the issue and is able to prioritize it, and remove the [I-prioritize] label. |
|
||||
| [regression-] | <span class="label-color" style="background-color:#e4008a;"> </span> Pink | Tracks regressions from a stable release. |
|
||||
| [relnotes] | <span class="label-color" style="background-color:#fad8c7;"> </span> Light Orange | Changes that should be documented in the release notes of the next release. |
|
||||
| [S-] | <span class="label-color" style="background-color:#d3dddd;"> </span> Gray | Tracks the **status** of pull requests. |
|
||||
| [S-tracking-] | <span class="label-color" style="background-color:#4682b4;"> </span> Steel Blue | Tracks the **status** of [tracking issues]. |
|
||||
| [stable-] | <span class="label-color" style="background-color:#00229c;"> </span> Dark Blue | Tracks changes which need to be [backported to stable][stable-backport] in anticipation of a point release. |
|
||||
| [T-] | <span class="label-color" style="background-color:#bfd4f2;"> </span> Blue | Denotes which **team** the issue belongs to. |
|
||||
| [WG-] | <span class="label-color" style="background-color:#c2e0c6;"> </span> Green | Denotes which **working group** the issue belongs to. |
|
||||
|
||||
|
||||
[A-]: https://github.com/rust-lang/rust/labels?q=A
|
||||
[B-]: https://github.com/rust-lang/rust/labels?q=B
|
||||
[C-]: https://github.com/rust-lang/rust/labels?q=C
|
||||
[D-]: https://github.com/rust-lang/rust/labels?q=D
|
||||
[E-]: https://github.com/rust-lang/rust/labels?q=E
|
||||
[F-]: https://github.com/rust-lang/rust/labels?q=F
|
||||
[I-]: https://github.com/rust-lang/rust/labels?q=I
|
||||
[L-]: https://github.com/rust-lang/rust/labels?q=L
|
||||
[O-]: https://github.com/rust-lang/rust/labels?q=O
|
||||
[P-]: https://github.com/rust-lang/rust/labels?q=P
|
||||
[S-]: https://github.com/rust-lang/rust/labels?q=S
|
||||
[T-]: https://github.com/rust-lang/rust/labels?q=T
|
||||
[WG-]: https://github.com/rust-lang/rust/labels?q=WG
|
||||
[stable-]: https://github.com/rust-lang/rust/labels?q=stable
|
||||
[beta-]: https://github.com/rust-lang/rust/labels?q=beta
|
||||
[I-\*-nominated]: https://github.com/rust-lang/rust/labels?q=nominated
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ Here are a few examples:
|
|||
their crate, making this a hard error would make refactoring and development
|
||||
very painful.
|
||||
- [future-incompatible lints]:
|
||||
these are silencable lints.
|
||||
these are silenceable lints.
|
||||
It was decided that making them fixed errors would cause too much breakage,
|
||||
so warnings are instead emitted,
|
||||
and will eventually be turned into fixed (hard) errors.
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ The following external projects are managed using some form of a `subtree`:
|
|||
|
||||
* [clippy](https://github.com/rust-lang/rust-clippy)
|
||||
* [miri](https://github.com/rust-lang/miri)
|
||||
* [portable-simd](https://github.com/rust-lang/portable-simd)
|
||||
* [rustfmt](https://github.com/rust-lang/rustfmt)
|
||||
* [rust-analyzer](https://github.com/rust-lang/rust-analyzer)
|
||||
* [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift)
|
||||
* [rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide)
|
||||
|
||||
In contrast to `submodule` dependencies
|
||||
(see below for those), the `subtree` dependencies are just regular files and directories which can
|
||||
|
|
@ -33,13 +35,15 @@ implement a new tool feature or test, that should happen in one collective rustc
|
|||
|
||||
* Using `git subtree`
|
||||
* `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy))
|
||||
* `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh))
|
||||
* `rustfmt`
|
||||
* `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7))
|
||||
* Using the [josh] tool
|
||||
* `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo))
|
||||
* `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147))
|
||||
* `rustc-dev-guide` ([sync guide](https://github.com/rust-lang/rustc-dev-guide#synchronizing-josh-subtree-with-rustc))
|
||||
|
||||
The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a subtree from `git subtree` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg).
|
||||
The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a repository dependency from `git subtree` or `git submodule` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg).
|
||||
|
||||
Below you can find a guide on how to perform push and pull synchronization with the main rustc repo using `git subtree`, although these instructions might differ repo from repo.
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ stability.
|
|||
## Edition parsing
|
||||
|
||||
For the most part, the lexer is edition-agnostic.
|
||||
Within [`StringReader`], tokens can be modified based on edition-specific behavior.
|
||||
Within [`Lexer`], tokens can be modified based on edition-specific behavior.
|
||||
For example, C-String literals like `c"foo"` are split into multiple tokens in editions before 2021.
|
||||
This is also where things like reserved prefixes are handled for the 2021 edition.
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ For example, the deprecated `start...end` pattern syntax emits the
|
|||
[`ellipsis_inclusive_range_patterns`] lint on editions before 2021, and in 2021 is an hard error via
|
||||
the `emit_err` method.
|
||||
|
||||
[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html
|
||||
[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html
|
||||
[`ParseSess::edition`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html#structfield.edition
|
||||
[`ellipsis_inclusive_range_patterns`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#ellipsis-inclusive-range-patterns
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ If that fails, we reveal the hidden type of the opaque type,
|
|||
but only to prove this specific trait bound, not in general.
|
||||
Revealing is done by invoking the `type_of` query on the `DefId` of the opaque type.
|
||||
The query will internally request the hidden types from the defining function(s)
|
||||
and return that (see [the section on `type_of`](#Within-the-type_of-query) for more details).
|
||||
and return that (see [the section on `type_of`](#within-the-type_of-query) for more details).
|
||||
|
||||
#### Flowchart of type checking steps
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ Unicode character encoding.
|
|||
|
||||
The token stream passes through a higher-level lexer located in
|
||||
[`rustc_parse`] to prepare for the next stage of the compile process. The
|
||||
[`StringReader`] `struct` is used at this stage to perform a set of validations
|
||||
[`Lexer`] `struct` is used at this stage to perform a set of validations
|
||||
and turn strings into interned symbols (_interning_ is discussed later).
|
||||
[String interning] is a way of storing only one immutable
|
||||
copy of each distinct string value.
|
||||
|
|
@ -153,7 +153,7 @@ the final binary.
|
|||
[`rustc_parse::parser::Parser`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/struct.Parser.html
|
||||
[`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html
|
||||
[`simplify_try`]: https://github.com/rust-lang/rust/pull/66282
|
||||
[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html
|
||||
[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html
|
||||
[`Ty<'tcx>`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
|
||||
[borrow checking]: borrow_check.md
|
||||
[codegen]: backend/codegen.md
|
||||
|
|
|
|||
|
|
@ -98,8 +98,8 @@ TODO: write this :3
|
|||
[req-depth-ck]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_middle/src/traits/solve/cache.rs#L76-L86
|
||||
[update-depth]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_trait_selection/src/solve/search_graph.rs#L308
|
||||
[rem-depth]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_middle/src/traits/solve/cache.rs#L124
|
||||
[^1]: This is overly restrictive: if all nested goal return the overflow response with some
|
||||
availabledepth `n`, then their result should be the same for any depths smaller than `n`.
|
||||
[^1]: This is overly restrictive: if all nested goals return the overflow response with some
|
||||
available depth `n`, then their result should be the same for any depths smaller than `n`.
|
||||
We can implement this optimization in the future.
|
||||
|
||||
[chapter on coinduction]: ./coinduction.md
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@ well-formed after normalizing said aliases. We rely on this as
|
|||
otherwise we would have to re-check for well-formedness for these
|
||||
types.
|
||||
|
||||
This is unfortunately broken for `<fndef as FnOnce<..>>::Output` due to implied bounds,
|
||||
resulting in [#114936].
|
||||
|
||||
### Structural equality modulo regions implies semantic equality ✅
|
||||
|
||||
If you have a some type and equate it to itself after replacing any regions with unique
|
||||
|
|
@ -33,7 +30,7 @@ inference variables in both the lhs and rhs, the now potentially structurally di
|
|||
types should still be equal to each other.
|
||||
|
||||
Needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck.
|
||||
If this does invariant is broken MIR typeck ends up failing with an ICE.
|
||||
If this invariant is broken MIR typeck ends up failing with an ICE.
|
||||
|
||||
### Applying inference results from a goal does not change its result ❌
|
||||
|
||||
|
|
@ -91,7 +88,7 @@ it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/
|
|||
|
||||
If a trait goal holds with an empty environment, there should be a unique `impl`,
|
||||
either user-defined or builtin, which is used to prove that goal. This is
|
||||
necessary to select a unique method. It
|
||||
necessary to select a unique method.
|
||||
|
||||
We do however break this invariant in few cases, some of which are due to bugs,
|
||||
some by design:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ a separate "probe", to not leak inference constraints to the other candidates.
|
|||
We then try to merge the assembled candidates via `EvalCtxt::merge_candidates`.
|
||||
|
||||
|
||||
## Important concepts and design pattern
|
||||
## Important concepts and design patterns
|
||||
|
||||
### `EvalCtxt::add_goal`
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ eagerly instantiates `'a` with a placeholder and then recursively proves
|
|||
Some goals can be proven in multiple ways. In these cases we try each option in
|
||||
a separate "probe" and then attempt to merge the resulting responses by using
|
||||
`EvalCtxt::try_merge_responses`. If merging the responses fails, we use
|
||||
`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try
|
||||
`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try to
|
||||
incompletely prefer some choices over others in case `EvalCtxt::try_merge_responses`
|
||||
fails.
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ Our CI is primarily executed on [GitHub Actions], with a single workflow defined
|
|||
in [`.github/workflows/ci.yml`], which contains a bunch of steps that are
|
||||
unified for all CI jobs that we execute. When a commit is pushed to a
|
||||
corresponding branch or a PR, the workflow executes the
|
||||
[`ci.py`] script, which dynamically generates the specific CI
|
||||
[`src/ci/github-actions/ci.py`] script, which dynamically generates the specific CI
|
||||
jobs that should be executed. This script uses the [`jobs.yml`] file as an
|
||||
input, which contains a declarative configuration of all our CI jobs.
|
||||
|
||||
|
|
@ -409,10 +409,25 @@ To learn more about the dashboard, see the [Datadog CI docs].
|
|||
[Datadog CI docs]: https://docs.datadoghq.com/continuous_integration/
|
||||
[public dashboard]: https://p.datadoghq.com/sb/3a172e20-e9e1-11ed-80e3-da7ad0900002-b5f7bb7e08b664a06b08527da85f7e30
|
||||
|
||||
## Determining the CI configuration
|
||||
|
||||
If you want to determine which `config.toml` settings are used in CI for a
|
||||
particular job, it is probably easiest to just look at the build log. To do
|
||||
this:
|
||||
|
||||
1. Go to
|
||||
<https://github.com/rust-lang-ci/rust/actions?query=branch%3Aauto+is%3Asuccess>
|
||||
to find the most recently successful build, and click on it.
|
||||
2. Choose the job you are interested in on the left-hand side.
|
||||
3. Click on the gear icon and choose "View raw logs"
|
||||
4. Search for the string "Configure the build"
|
||||
5. All of the build settings are listed below that starting with the
|
||||
`configure:` prefix.
|
||||
|
||||
[GitHub Actions]: https://github.com/rust-lang/rust/actions
|
||||
[`jobs.yml`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/jobs.yml
|
||||
[`.github/workflows/ci.yml`]: https://github.com/rust-lang/rust/blob/master/.github/workflows/ci.yml
|
||||
[`ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py
|
||||
[`src/ci/github-actions/ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py
|
||||
[rust-lang-ci]: https://github.com/rust-lang-ci/rust/actions
|
||||
[bors]: https://github.com/bors
|
||||
[homu]: https://github.com/rust-lang/homu
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ Consider writing the test as a proper incremental test instead.
|
|||
|
||||
| Directive | Explanation | Supported test suites | Possible values |
|
||||
|-------------|--------------------------------------------------------------|------------------------------------------|---------------------------|
|
||||
| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `js-doc-test`, `rustdoc-json` | Any valid `rustdoc` flags |
|
||||
| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags |
|
||||
|
||||
<!--
|
||||
**FIXME(rustdoc)**: what does `check-test-line-numbers-match` do?
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ the token stream, and then execute the parser to get a [`Crate`] (the root AST
|
|||
node).
|
||||
|
||||
To minimize the amount of copying that is done,
|
||||
both [`StringReader`] and [`Parser`] have lifetimes which bind them to the parent [`ParseSess`].
|
||||
both [`Lexer`] and [`Parser`] have lifetimes which bind them to the parent [`ParseSess`].
|
||||
This contains all the information needed while parsing, as well as the [`SourceMap`] itself.
|
||||
|
||||
Note that while parsing, we may encounter macro definitions or invocations.
|
||||
|
|
@ -67,7 +67,7 @@ Code for lexical analysis is split between two crates:
|
|||
constituting tokens. Although it is popular to implement lexers as generated
|
||||
finite state machines, the lexer in [`rustc_lexer`] is hand-written.
|
||||
|
||||
- [`StringReader`] integrates [`rustc_lexer`] with data structures specific to
|
||||
- [`Lexer`] integrates [`rustc_lexer`] with data structures specific to
|
||||
`rustc`. Specifically, it adds `Span` information to tokens returned by
|
||||
[`rustc_lexer`] and interns identifiers.
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ Code for lexical analysis is split between two crates:
|
|||
[`ParseSess`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html
|
||||
[`rustc_lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lexer/index.html
|
||||
[`SourceMap`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html
|
||||
[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html
|
||||
[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html
|
||||
[ast module]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
|
||||
[ast]: ./ast-validation.md
|
||||
[parser]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/index.html
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ requirements of impls and functions as explicit predicates.
|
|||
|
||||
These bounds are not added to the `ParamEnv` of the affected item itself. For lexical
|
||||
region resolution they are added using [`fn OutlivesEnvironment::with_bounds`].
|
||||
Similarly,during MIR borrowck we add them using
|
||||
Similarly, during MIR borrowck we add them using
|
||||
[`fn UniversalRegionRelationsBuilder::add_implied_bounds`].
|
||||
|
||||
[We add implied bounds for the function signature and impl header in MIR borrowck][mir].
|
||||
|
|
@ -81,4 +81,4 @@ This results in multiple unsoundnesses:
|
|||
|
||||
[#25860]: https://github.com/rust-lang/rust/issues/25860
|
||||
[#84591]: https://github.com/rust-lang/rust/issues/84591
|
||||
[#100051]: https://github.com/rust-lang/rust/issues/100051
|
||||
[#100051]: https://github.com/rust-lang/rust/issues/100051
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Usages of these parameters is represented by the `RegionKind::Bound` (or `TyKind
|
|||
- A [`BoundVar`] which specifies which of the parameters the `Binder` introduces we are referring to.
|
||||
- We also sometimes store some extra information for diagnostics reasons via the [`BoundTyKind`]/[`BoundRegionKind`] but this is not important for type equality or more generally the semantics of `Ty`. (omitted from the above example)
|
||||
|
||||
In debug output (and also informally when talking to eachother) we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. The above example would instead be written as `Binder(fn(&'^0_0), &[BoundVariableKind::Region])`. Sometimes when the `DebruijnIndex` is `0` we just omit it and would write `^0`.
|
||||
In debug output (and also informally when talking to each other) we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. The above example would instead be written as `Binder(fn(&'^0_0), &[BoundVariableKind::Region])`. Sometimes when the `DebruijnIndex` is `0` we just omit it and would write `^0`.
|
||||
|
||||
Another concrete example, this time a mixture of `for<'a>` in a where clause and a type:
|
||||
```
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ fn bar(foo: Foo<u32, f32>) {
|
|||
In the compiler the `instantiate` call for this is done in [`FieldDef::ty`] ([src][field_def_ty_src]), at some point during type checking `bar` we will wind up calling `FieldDef::ty(x, &[u32, f32])` in order to obtain the type of `foo.x`.
|
||||
|
||||
**Note on indices:** It is a bug if the index of a `Param` does not match what the `EarlyBinder` binds. For
|
||||
example, if the index is out of bounds or the index index of a lifetime corresponds to a type parameter.
|
||||
example, if the index is out of bounds or the index of a lifetime corresponds to a type parameter.
|
||||
These sorts of errors are caught earlier in the compiler during name resolution where we disallow references
|
||||
to generics parameters introduced by items that should not be nameable by the inner item.
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ Concretely given the `ty::Generics` for the item the parameter is defined on, if
|
|||
|
||||
The index fully defines the `Ty` and is the only part of `TyKind::Param` that matters for reasoning about the code we are compiling.
|
||||
|
||||
Generally we do not care what the name is and only use the index is included for diagnostics and debug logs as otherwise it would be
|
||||
Generally we do not care what the name is and only use the index. The name is included for diagnostics and debug logs as otherwise it would be
|
||||
incredibly difficult to understand the output, i.e. `Vec<Param(0)>: Sized` vs `Vec<T>: Sized`. In debug output, parameter types are
|
||||
often printed out as `{name}/#{index}`, for example in the function `foo` if we were to debug print `Vec<T>` it would be written as `Vec<T/#0>`.
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ The rules against shadowing make this difficult but those language rules could c
|
|||
|
||||
### Lifetime parameters
|
||||
|
||||
In contrast to `Ty`/`Const`'s `Param` singular `Param` variant, lifetimes have two variants for representing region parameters: [`RegionKind::EarlyParam`] and [`RegionKind::LateParam`]. The reason for this is due to function's distinguishing between [early and late bound parameters](../early-late-bound-params/early-late-bound-summary.md) which is discussed in an earlier chapter (see link).
|
||||
In contrast to `Ty`/`Const`'s `Param` singular `Param` variant, lifetimes have two variants for representing region parameters: [`RegionKind::EarlyParam`] and [`RegionKind::LateParam`]. The reason for this is due to function's distinguishing between [early and late bound parameters][ch_early_late_bound] which is discussed in an earlier chapter (see link).
|
||||
|
||||
`RegionKind::EarlyParam` is structured identically to `Ty/Const`'s `Param` variant, it is simply a `u32` index and a `Symbol`. For lifetime parameters defined on non-function items we always use `ReEarlyParam`. For functions we use `ReEarlyParam` for any early bound parameters and `ReLateParam` for any late bound parameters. Note that just like `Ty` and `Const` params we often debug format them as `'SYMBOL/#INDEX`, see for example:
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ fn foo<'a, 'b, T: 'a>(one: T, two: &'a &'b u32) -> &'b u32 {
|
|||
|
||||
`RegionKind::LateParam` will be discussed more in the chapter on [instantiating binders][ch_instantiating_binders].
|
||||
|
||||
[ch_early_late_bound]: ../early-late-bound-params/early-late-bound-summary.md
|
||||
[ch_early_late_bound]: ../early_late_parameters.md
|
||||
[ch_binders]: ./binders.md
|
||||
[ch_instantiating_binders]: ./instantiating_binders.md
|
||||
[`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
|
||||
|
|
|
|||
|
|
@ -6,3 +6,6 @@ allow-unauthenticated = [
|
|||
"waiting-on-author",
|
||||
"blocked",
|
||||
]
|
||||
|
||||
# Automatically close and reopen PRs made by bots to run CI on them
|
||||
[bot-pull-requests]
|
||||
|
|
|
|||
|
|
@ -2166,7 +2166,8 @@ details.toggle > summary:not(.hideme)::before {
|
|||
top: 4px;
|
||||
}
|
||||
|
||||
.impl-items > details.toggle > summary:not(.hideme)::before {
|
||||
.impl-items > details.toggle > summary:not(.hideme)::before,
|
||||
#main-content > .methods > details.toggle > summary:not(.hideme)::before {
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
}
|
||||
|
|
@ -2176,7 +2177,9 @@ details.toggle > summary:not(.hideme)::before {
|
|||
.impl-items > *:not(.item-info),
|
||||
/* We also indent the first top doc comment the same to still keep an indent on the
|
||||
doc block while aligning it with the impl block items. */
|
||||
.implementors-toggle > .docblock {
|
||||
.implementors-toggle > .docblock,
|
||||
/* We indent trait items as well. */
|
||||
#main-content > .methods > :not(.item-info) {
|
||||
margin-left: var(--impl-items-indent);
|
||||
}
|
||||
|
||||
|
|
@ -2508,7 +2511,8 @@ in src-script.js and main.js
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.impl-items > details.toggle > summary:not(.hideme)::before {
|
||||
.impl-items > details.toggle > summary:not(.hideme)::before,
|
||||
#main-content > .methods > details.toggle > summary:not(.hideme)::before {
|
||||
left: -20px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ LL | let local = 0;
|
|||
help: ALLOC was deallocated here:
|
||||
--> tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
|
|
||||
LL | }
|
||||
| ^
|
||||
LL | become g(ptr)
|
||||
| ^^^^^^^^^^^^^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC
|
||||
note: inside `main`
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
//@ known-bug: #128097
|
||||
#![feature(explicit_tail_calls)]
|
||||
fn f(x: &mut ()) {
|
||||
let _y: String;
|
||||
become f(x);
|
||||
}
|
||||
|
|
@ -66,7 +66,6 @@
|
|||
bb6: {
|
||||
+ _8 = const false;
|
||||
StorageDead(_4);
|
||||
StorageDead(_3);
|
||||
drop(_2) -> [return: bb7, unwind: bb12];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@
|
|||
bb6: {
|
||||
+ _8 = const false;
|
||||
StorageDead(_4);
|
||||
StorageDead(_3);
|
||||
- drop(_2) -> [return: bb7, unwind continue];
|
||||
+ drop(_2) -> [return: bb7, unwind: bb12];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ fn f() -> () {
|
|||
|
||||
bb6: {
|
||||
StorageDead(_4);
|
||||
StorageDead(_3);
|
||||
drop(_2) -> [return: bb7, unwind: bb17];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ fn f() -> () {
|
|||
|
||||
bb6: {
|
||||
StorageDead(_4);
|
||||
StorageDead(_3);
|
||||
drop(_2) -> [return: bb7, unwind: bb17];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@
|
|||
bb8: {
|
||||
+ _12 = const false;
|
||||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
drop(_4) -> [return: bb9, unwind: bb16];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@
|
|||
bb8: {
|
||||
+ _12 = const false;
|
||||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
drop(_4) -> [return: bb9, unwind: bb16];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ fn f_with_arg(_1: String, _2: String) -> () {
|
|||
|
||||
bb8: {
|
||||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
drop(_4) -> [return: bb9, unwind: bb23];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ fn f_with_arg(_1: String, _2: String) -> () {
|
|||
|
||||
bb8: {
|
||||
StorageDead(_6);
|
||||
StorageDead(_5);
|
||||
drop(_4) -> [return: bb9, unwind: bb23];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ call-function: ("check-colors", {
|
|||
set-window-size: (851, 600)
|
||||
|
||||
// Check the size and count in tabs
|
||||
assert-text: ("#search-tabs > button:nth-child(1) > .count", " (26) ")
|
||||
assert-text: ("#search-tabs > button:nth-child(1) > .count", " (27) ")
|
||||
assert-text: ("#search-tabs > button:nth-child(2) > .count", " (7) ")
|
||||
assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0) ")
|
||||
store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth})
|
||||
|
|
|
|||
|
|
@ -691,3 +691,25 @@ impl ImplDoc {
|
|||
impl ImplDoc {
|
||||
pub fn bar5() {}
|
||||
}
|
||||
|
||||
pub trait ItemsTrait {
|
||||
/// You want doc, here is doc!
|
||||
///
|
||||
/// blablala
|
||||
type F;
|
||||
|
||||
/// You want doc, here is doc!
|
||||
///
|
||||
/// blablala
|
||||
const X: u32;
|
||||
|
||||
/// You want doc, here is doc!
|
||||
///
|
||||
/// blablala
|
||||
fn foo() {}
|
||||
|
||||
/// You want doc, here is doc!
|
||||
///
|
||||
/// blablala
|
||||
fn bar();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,3 +31,29 @@ assert-attribute: (".top-doc", {"open": ""})
|
|||
// To ensure that the toggle isn't over the text, we check that the toggle isn't clicked.
|
||||
click: (3, 270)
|
||||
assert-attribute: (".top-doc", {"open": ""})
|
||||
|
||||
// Same check on trait items.
|
||||
fail-on-request-error: false // To prevent downloads errors on "trait.impl/test_docs/trait.ItemsTrait.js"
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/trait.ItemsTrait.html"
|
||||
|
||||
define-function: (
|
||||
"check-trait-item",
|
||||
[nth, text],
|
||||
block {
|
||||
store-value: (selector, ".methods:nth-of-type(" + |nth| + ") > details summary")
|
||||
assert-text: (|selector| + " h4", |text|)
|
||||
assert-position: (
|
||||
|selector| + "::before",
|
||||
{"x": 6},
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
// Assert the position of the toggle on an associated const.
|
||||
call-function: ("check-trait-item", {"nth": 2, "text": "const X: u32"})
|
||||
// Assert the position of the toggle on an associated type.
|
||||
call-function: ("check-trait-item", {"nth": 3, "text": "type F"})
|
||||
// Assert the position of the toggle on an associated required method.
|
||||
call-function: ("check-trait-item", {"nth": 4, "text": "fn bar()"})
|
||||
// Assert the position of the toggle on an associated provided method.
|
||||
call-function: ("check-trait-item", {"nth": 5, "text": "fn foo()"})
|
||||
|
|
|
|||
17
tests/ui/diagnostic-width/secondary-label-with-long-type.rs
Normal file
17
tests/ui/diagnostic-width/secondary-label-with-long-type.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
//@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes
|
||||
//@ normalize-stderr: "long-type-\d+" -> "long-type-hash"
|
||||
type A = (i32, i32, i32, i32);
|
||||
type B = (A, A, A, A);
|
||||
type C = (B, B, B, B);
|
||||
type D = (C, C, C, C);
|
||||
|
||||
fn foo(x: D) {
|
||||
let () = x; //~ ERROR mismatched types
|
||||
//~^ NOTE this expression has type `((...,
|
||||
//~| NOTE expected `((...,
|
||||
//~| NOTE expected tuple
|
||||
//~| NOTE the full type name has been written to
|
||||
//~| NOTE consider using `--verbose` to print the full type name to the console
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/secondary-label-with-long-type.rs:9:9
|
||||
|
|
||||
LL | let () = x;
|
||||
| ^^ - this expression has type `((..., ..., ..., ...), ..., ..., ...)`
|
||||
| |
|
||||
| expected `((..., ..., ..., ...), ..., ..., ...)`, found `()`
|
||||
|
|
||||
= note: expected tuple `((..., ..., ..., ...), ..., ..., ...)`
|
||||
found unit type `()`
|
||||
= note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/secondary-label-with-long-type/secondary-label-with-long-type.long-type-hash.txt'
|
||||
= note: consider using `--verbose` to print the full type name to the console
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
@ -4,10 +4,9 @@ error[E0597]: `local` does not live long enough
|
|||
LL | let local = Type;
|
||||
| ----- binding `local` declared here
|
||||
LL | become takes_borrow(&local);
|
||||
| ^^^^^^ borrowed value does not live long enough
|
||||
LL |
|
||||
LL | }
|
||||
| - `local` dropped here while still borrowed
|
||||
| ^^^^^^- `local` dropped here while still borrowed
|
||||
| |
|
||||
| borrowed value does not live long enough
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
|||
14
tests/ui/explicit-tail-calls/two-phase.rs
Normal file
14
tests/ui/explicit-tail-calls/two-phase.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// regression test for <https://github.com/rust-lang/rust/issues/112788>.
|
||||
// this test used to ICE because we tried to run drop glue of `x`
|
||||
// if dropping `_y` (happening at the `become` site) panicked and caused an unwind.
|
||||
//
|
||||
//@ check-pass
|
||||
#![expect(incomplete_features)]
|
||||
#![feature(explicit_tail_calls)]
|
||||
|
||||
fn f(x: &mut ()) {
|
||||
let _y = String::new();
|
||||
become f(x);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -53,7 +53,7 @@ LL | assert_eq!(smart_ptr.a::<&Foo>(), 2);
|
|||
| ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>`
|
||||
|
|
||||
= note: expected reference `&Foo`
|
||||
found struct `SmartPtr<'_, Foo, >`
|
||||
found struct `SmartPtr<'_, Foo>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/arbitrary-self-from-method-substs-with-receiver.rs:62:16
|
||||
|
|
@ -62,7 +62,7 @@ LL | assert_eq!(smart_ptr.b::<&Foo>(), 1);
|
|||
| ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>`
|
||||
|
|
||||
= note: expected reference `&Foo`
|
||||
found struct `SmartPtr<'_, Foo, >`
|
||||
found struct `SmartPtr<'_, Foo>`
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ LL | smart_ptr.get::<&Foo>();
|
|||
| ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>`
|
||||
|
|
||||
= note: expected reference `&Foo`
|
||||
found struct `SmartPtr<'_, Foo, >`
|
||||
found struct `SmartPtr<'_, Foo>`
|
||||
|
||||
error[E0271]: type mismatch resolving `<Silly as FindReceiver>::Receiver == Foo`
|
||||
--> $DIR/arbitrary-self-from-method-substs.rs:92:9
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ LL | pub fn new(thing: T) -> GenericStruct<1, T> {
|
|||
LL | Self { thing }
|
||||
| ^^^^^^^^^^^^^^ expected `1`, found `0`
|
||||
|
|
||||
= note: expected struct `GenericStruct<_, 1>`
|
||||
found struct `GenericStruct<_, 0>`
|
||||
= note: expected struct `GenericStruct<1, _>`
|
||||
found struct `GenericStruct<0, _>`
|
||||
help: use the type name directly
|
||||
|
|
||||
LL | GenericStruct::<1, T> { thing }
|
||||
|
|
@ -25,8 +25,8 @@ LL | pub fn new(thing: T) -> GenericStruct2<1, T> {
|
|||
LL | Self { 0: thing }
|
||||
| ^^^^^^^^^^^^^^^^^ expected `1`, found `0`
|
||||
|
|
||||
= note: expected struct `GenericStruct2<_, 1>`
|
||||
found struct `GenericStruct2<_, 0>`
|
||||
= note: expected struct `GenericStruct2<1, _>`
|
||||
found struct `GenericStruct2<0, _>`
|
||||
help: use the type name directly
|
||||
|
|
||||
LL | GenericStruct2::<1, T> { 0: thing }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue