diff options
Diffstat (limited to 'compiler')
58 files changed, 1128 insertions, 564 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index ed06423b260..85adf0f3716 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -175,6 +175,13 @@ pub(crate) fn codegen_const_value<'tcx>( fx.module.declare_data_in_func(data_id, &mut fx.bcx.func); fx.bcx.ins().global_value(fx.pointer_type, local_data_id) } + GlobalAlloc::TypeId { .. } => { + return CValue::const_val( + fx, + layout, + ScalarInt::try_from_target_usize(offset.bytes(), fx.tcx).unwrap(), + ); + } GlobalAlloc::Static(def_id) => { assert!(fx.tcx.is_static(def_id)); let data_id = data_id_for_static( @@ -360,6 +367,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant GlobalAlloc::Memory(alloc) => alloc, GlobalAlloc::Function { .. } | GlobalAlloc::Static(_) + | GlobalAlloc::TypeId { .. } | GlobalAlloc::VTable(..) => { unreachable!() } @@ -471,6 +479,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant .principal() .map(|principal| tcx.instantiate_bound_regions_with_erased(principal)), ), + GlobalAlloc::TypeId { .. } => { + // Nothing to do, the bytes/offset of this pointer have already been written together with all other bytes, + // so we just need to drop this provenance. + continue; + } GlobalAlloc::Static(def_id) => { if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 32713eb56c6..28848ca6184 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,7 +1,6 @@ use gccjit::{LValue, RValue, ToRValue, Type}; -use rustc_abi as abi; -use rustc_abi::HasDataLayout; use rustc_abi::Primitive::Pointer; +use rustc_abi::{self as abi, HasDataLayout}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; @@ -282,6 +281,13 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { let init = self.const_data_from_alloc(alloc); self.static_addr_of(init, alloc.inner().align, None) } + GlobalAlloc::TypeId { .. } => { + let val = self.const_usize(offset.bytes()); + // This is still a variable of pointer type, even though we only use the provenance + // of that pointer in CTFE and Miri. But to make LLVM's type system happy, + // we need an int-to-ptr cast here (it doesn't matter at all which provenance that picks). + return self.context.new_cast(None, val, ty); + } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); self.get_static(def_id).get_address(None) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 92f38565eef..b9b5c776d86 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -3,9 +3,8 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; -use rustc_abi as abi; -use rustc_abi::HasDataLayout; use rustc_abi::Primitive::Pointer; +use rustc_abi::{self as abi, HasDataLayout as _}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; @@ -284,7 +283,8 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_bitcast(llval, llty) }; } else { - let init = const_alloc_to_llvm(self, alloc, /*static*/ false); + let init = + const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), @@ -316,15 +316,19 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }), ))) .unwrap_memory(); - let init = const_alloc_to_llvm(self, alloc, /*static*/ false); - let value = self.static_addr_of_impl(init, alloc.inner().align, None); - value + let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + self.static_addr_of_impl(init, alloc.inner().align, None) } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); assert!(!self.tcx.is_thread_local_static(def_id)); self.get_static(def_id) } + GlobalAlloc::TypeId { .. } => { + // Drop the provenance, the offset contains the bytes of the hash + let llval = self.const_usize(offset.bytes()); + return unsafe { llvm::LLVMConstIntToPtr(llval, llty) }; + } }; let base_addr_space = global_alloc.address_space(self); let llval = unsafe { @@ -346,7 +350,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { } fn const_data_from_alloc(&self, alloc: ConstAllocation<'_>) -> Self::Value { - const_alloc_to_llvm(self, alloc, /*static*/ false) + const_alloc_to_llvm(self, alloc.inner(), /*static*/ false) } fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 28f5282c6b0..21524fd2eb8 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -27,10 +27,9 @@ use crate::{base, debuginfo}; pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, - alloc: ConstAllocation<'_>, + alloc: &Allocation, is_static: bool, ) -> &'ll Value { - let alloc = alloc.inner(); // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be // producing empty LLVM allocations as they're just adding noise to binaries and forcing less @@ -141,7 +140,7 @@ fn codegen_static_initializer<'ll, 'tcx>( def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((const_alloc_to_llvm(cx, alloc, /*static*/ true), alloc)) + Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc)) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 343cb0eeca9..b46773396fc 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1379,7 +1379,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { } } - let features = sess.opts.unstable_opts.linker_features; + let features = sess.opts.cg.linker_features; // linker and linker flavor specified via command line have precedence over what the target // specification specifies @@ -3327,35 +3327,6 @@ fn add_lld_args( // this, `wasm-component-ld`, which is overridden if this option is passed. if !sess.target.is_like_wasm { cmd.cc_arg("-fuse-ld=lld"); - - // On ELF platforms like at least x64 linux, GNU ld and LLD have opposite defaults on some - // section garbage-collection features. For example, the somewhat popular `linkme` crate and - // its dependents rely in practice on this difference: when using lld, they need `-z - // nostart-stop-gc` to prevent encapsulation symbols and sections from being - // garbage-collected. - // - // More information about all this can be found in: - // - https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order - // - https://lld.llvm.org/ELF/start-stop-gc - // - // So when using lld, we restore, for now, the traditional behavior to help migration, but - // will remove it in the future. - // Since this only disables an optimization, it shouldn't create issues, but is in theory - // slightly suboptimal. However, it: - // - doesn't have any visible impact on our benchmarks - // - reduces the need to disable lld for the crates that depend on this - // - // Note that lld can detect some cases where this difference is relied on, and emits a - // dedicated error to add this link arg. We could make use of this error to emit an FCW. As - // of writing this, we don't do it, because lld is already enabled by default on nightly - // without this mitigation: no working project would see the FCW, so we do this to help - // stabilization. - // - // FIXME: emit an FCW if linking fails due its absence, and then remove this link-arg in the - // future. - if sess.target.llvm_target == "x86_64-unknown-linux-gnu" { - cmd.link_arg("-znostart-stop-gc"); - } } if !flavor.is_gnu() { diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 99f35b79208..6d6465dd798 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -171,8 +171,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer if let Some(local) = place.as_local() { self.define(local, DefLocation::Assignment(location)); if self.locals[local] != LocalKind::Memory { - let decl_span = self.fx.mir.local_decls[local].source_info.span; - if !self.fx.rvalue_creates_operand(rvalue, decl_span) { + if !self.fx.rvalue_creates_operand(rvalue) { self.locals[local] = LocalKind::Memory; } } diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 2896dfd5463..bc83e41b278 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -186,7 +186,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { offset: Size, ) -> Self { let alloc_align = alloc.inner().align; - assert!(alloc_align >= layout.align.abi); + assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi); let read_scalar = |start, size, s: abi::Scalar, ty| { match alloc.0.read_scalar( @@ -565,118 +565,167 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } } } +} - /// Creates an incomplete operand containing the [`abi::Scalar`]s expected based - /// on the `layout` passed. This is for use with [`OperandRef::insert_field`] - /// later to set the necessary immediate(s), one-by-one converting all the `Right` to `Left`. - /// - /// Returns `None` for `layout`s which cannot be built this way. - pub(crate) fn builder( - layout: TyAndLayout<'tcx>, - ) -> Option<OperandRef<'tcx, Either<V, abi::Scalar>>> { - // Uninhabited types are weird, because for example `Result<!, !>` - // shows up as `FieldsShape::Primitive` and we need to be able to write - // a field into `(u32, !)`. We'll do that in an `alloca` instead. - if layout.uninhabited { - return None; - } +/// Each of these variants starts out as `Either::Right` when it's uninitialized, +/// then setting the field changes that to `Either::Left` with the backend value. +#[derive(Debug, Copy, Clone)] +enum OperandValueBuilder<V> { + ZeroSized, + Immediate(Either<V, abi::Scalar>), + Pair(Either<V, abi::Scalar>, Either<V, abi::Scalar>), + /// `repr(simd)` types need special handling because they each have a non-empty + /// array field (which uses [`OperandValue::Ref`]) despite the SIMD type itself + /// using [`OperandValue::Immediate`] which for any other kind of type would + /// mean that its one non-ZST field would also be [`OperandValue::Immediate`]. + Vector(Either<V, ()>), +} + +/// Allows building up an `OperandRef` by setting fields one at a time. +#[derive(Debug, Copy, Clone)] +pub(super) struct OperandRefBuilder<'tcx, V> { + val: OperandValueBuilder<V>, + layout: TyAndLayout<'tcx>, +} +impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { + /// Creates an uninitialized builder for an instance of the `layout`. + /// + /// ICEs for [`BackendRepr::Memory`] types (other than ZSTs), which should + /// be built up inside a [`PlaceRef`] instead as they need an allocated place + /// into which to write the values of the fields. + pub(super) fn new(layout: TyAndLayout<'tcx>) -> Self { let val = match layout.backend_repr { - BackendRepr::Memory { .. } if layout.is_zst() => OperandValue::ZeroSized, - BackendRepr::Scalar(s) => OperandValue::Immediate(Either::Right(s)), - BackendRepr::ScalarPair(a, b) => OperandValue::Pair(Either::Right(a), Either::Right(b)), - BackendRepr::Memory { .. } | BackendRepr::SimdVector { .. } => return None, + BackendRepr::Memory { .. } if layout.is_zst() => OperandValueBuilder::ZeroSized, + BackendRepr::Scalar(s) => OperandValueBuilder::Immediate(Either::Right(s)), + BackendRepr::ScalarPair(a, b) => { + OperandValueBuilder::Pair(Either::Right(a), Either::Right(b)) + } + BackendRepr::SimdVector { .. } => OperandValueBuilder::Vector(Either::Right(())), + BackendRepr::Memory { .. } => { + bug!("Cannot use non-ZST Memory-ABI type in operand builder: {layout:?}"); + } }; - Some(OperandRef { val, layout }) + OperandRefBuilder { val, layout } } -} -impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Either<V, abi::Scalar>> { - pub(crate) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>( + pub(super) fn insert_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>( &mut self, bx: &mut Bx, - v: VariantIdx, - f: FieldIdx, - operand: OperandRef<'tcx, V>, + variant: VariantIdx, + field: FieldIdx, + field_operand: OperandRef<'tcx, V>, ) { - let (expect_zst, is_zero_offset) = if let abi::FieldsShape::Primitive = self.layout.fields { + if let OperandValue::ZeroSized = field_operand.val { + // A ZST never adds any state, so just ignore it. + // This special-casing is worth it because of things like + // `Result<!, !>` where `Ok(never)` is legal to write, + // but the type shows as FieldShape::Primitive so we can't + // actually look at the layout for the field being set. + return; + } + + let is_zero_offset = if let abi::FieldsShape::Primitive = self.layout.fields { // The other branch looking at field layouts ICEs for primitives, // so we need to handle them separately. - // Multiple fields is possible for cases such as aggregating - // a thin pointer, where the second field is the unit. + // Because we handled ZSTs above (like the metadata in a thin pointer), + // the only possibility is that we're setting the one-and-only field. assert!(!self.layout.is_zst()); - assert_eq!(v, FIRST_VARIANT); - let first_field = f == FieldIdx::ZERO; - (!first_field, first_field) + assert_eq!(variant, FIRST_VARIANT); + assert_eq!(field, FieldIdx::ZERO); + true } else { - let variant_layout = self.layout.for_variant(bx.cx(), v); - let field_layout = variant_layout.field(bx.cx(), f.as_usize()); - let field_offset = variant_layout.fields.offset(f.as_usize()); - (field_layout.is_zst(), field_offset == Size::ZERO) + let variant_layout = self.layout.for_variant(bx.cx(), variant); + let field_offset = variant_layout.fields.offset(field.as_usize()); + field_offset == Size::ZERO }; let mut update = |tgt: &mut Either<V, abi::Scalar>, src, from_scalar| { let to_scalar = tgt.unwrap_right(); + // We transmute here (rather than just `from_immediate`) because in + // `Result<usize, *const ()>` the field of the `Ok` is an integer, + // but the corresponding scalar in the enum is a pointer. let imm = transmute_scalar(bx, src, from_scalar, to_scalar); *tgt = Either::Left(imm); }; - match (operand.val, operand.layout.backend_repr) { - (OperandValue::ZeroSized, _) if expect_zst => {} + match (field_operand.val, field_operand.layout.backend_repr) { + (OperandValue::ZeroSized, _) => unreachable!("Handled above"), (OperandValue::Immediate(v), BackendRepr::Scalar(from_scalar)) => match &mut self.val { - OperandValue::Immediate(val @ Either::Right(_)) if is_zero_offset => { + OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => { update(val, v, from_scalar); } - OperandValue::Pair(fst @ Either::Right(_), _) if is_zero_offset => { + OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => { update(fst, v, from_scalar); } - OperandValue::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { + OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { update(snd, v, from_scalar); } - _ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"), + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } + }, + (OperandValue::Immediate(v), BackendRepr::SimdVector { .. }) => match &mut self.val { + OperandValueBuilder::Vector(val @ Either::Right(())) if is_zero_offset => { + *val = Either::Left(v); + } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } }, (OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => { match &mut self.val { - OperandValue::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { + OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { update(fst, a, from_sa); update(snd, b, from_sb); } - _ => bug!("Tried to insert {operand:?} into {v:?}.{f:?} of {self:?}"), + _ => bug!( + "Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}" + ), } } - _ => bug!("Unsupported operand {operand:?} inserting into {v:?}.{f:?} of {self:?}"), + (OperandValue::Ref(place), BackendRepr::Memory { .. }) => match &mut self.val { + OperandValueBuilder::Vector(val @ Either::Right(())) => { + let ibty = bx.cx().immediate_backend_type(self.layout); + let simd = bx.load_from_place(ibty, place); + *val = Either::Left(simd); + } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } + }, + _ => bug!("Operand cannot be used with `insert_field`: {field_operand:?}"), } } /// Insert the immediate value `imm` for field `f` in the *type itself*, /// rather than into one of the variants. /// - /// Most things want [`OperandRef::insert_field`] instead, but this one is + /// Most things want [`Self::insert_field`] instead, but this one is /// necessary for writing things like enum tags that aren't in any variant. pub(super) fn insert_imm(&mut self, f: FieldIdx, imm: V) { let field_offset = self.layout.fields.offset(f.as_usize()); let is_zero_offset = field_offset == Size::ZERO; match &mut self.val { - OperandValue::Immediate(val @ Either::Right(_)) if is_zero_offset => { + OperandValueBuilder::Immediate(val @ Either::Right(_)) if is_zero_offset => { *val = Either::Left(imm); } - OperandValue::Pair(fst @ Either::Right(_), _) if is_zero_offset => { + OperandValueBuilder::Pair(fst @ Either::Right(_), _) if is_zero_offset => { *fst = Either::Left(imm); } - OperandValue::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { + OperandValueBuilder::Pair(_, snd @ Either::Right(_)) if !is_zero_offset => { *snd = Either::Left(imm); } _ => bug!("Tried to insert {imm:?} into field {f:?} of {self:?}"), } } - /// After having set all necessary fields, this converts the - /// `OperandValue<Either<V, _>>` (as obtained from [`OperandRef::builder`]) - /// to the normal `OperandValue<V>`. + /// After having set all necessary fields, this converts the builder back + /// to the normal `OperandRef`. /// /// ICEs if any required fields were not set. - pub fn build(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> { - let OperandRef { val, layout } = *self; + pub(super) fn build(&self, cx: &impl CodegenMethods<'tcx, Value = V>) -> OperandRef<'tcx, V> { + let OperandRefBuilder { val, layout } = *self; // For something like `Option::<u32>::None`, it's expected that the // payload scalar will not actually have been set, so this converts @@ -692,10 +741,22 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, Either<V, abi::Scalar>> { }; let val = match val { - OperandValue::ZeroSized => OperandValue::ZeroSized, - OperandValue::Immediate(v) => OperandValue::Immediate(unwrap(v)), - OperandValue::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)), - OperandValue::Ref(_) => bug!(), + OperandValueBuilder::ZeroSized => OperandValue::ZeroSized, + OperandValueBuilder::Immediate(v) => OperandValue::Immediate(unwrap(v)), + OperandValueBuilder::Pair(a, b) => OperandValue::Pair(unwrap(a), unwrap(b)), + OperandValueBuilder::Vector(v) => match v { + Either::Left(v) => OperandValue::Immediate(v), + Either::Right(()) + if let BackendRepr::SimdVector { element, .. } = layout.backend_repr + && element.is_uninit_valid() => + { + let bty = cx.immediate_backend_type(layout); + OperandValue::Immediate(cx.const_undef(bty)) + } + Either::Right(()) => { + bug!("OperandRef::build called while fields are missing {self:?}") + } + }, }; OperandRef { val, layout } } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 4af4e1e6a61..cbbb0196890 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -4,10 +4,9 @@ use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_session::config::OptLevel; -use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; -use super::operand::{OperandRef, OperandValue}; +use super::operand::{OperandRef, OperandRefBuilder, OperandValue}; use super::place::{PlaceRef, codegen_tag_value}; use super::{FunctionCx, LocalRef}; use crate::common::{IntPredicate, TypeKind}; @@ -181,7 +180,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => { - assert!(self.rvalue_creates_operand(rvalue, DUMMY_SP)); + assert!(self.rvalue_creates_operand(rvalue)); let temp = self.codegen_rvalue_operand(bx, rvalue); temp.val.store(bx, dest); } @@ -354,10 +353,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx: &mut Bx, rvalue: &mir::Rvalue<'tcx>, ) -> OperandRef<'tcx, Bx::Value> { - assert!( - self.rvalue_creates_operand(rvalue, DUMMY_SP), - "cannot codegen {rvalue:?} to operand", - ); + assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {rvalue:?} to operand",); match *rvalue { mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { @@ -668,9 +664,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // `rvalue_creates_operand` has arranged that we only get here if // we can build the aggregate immediate from the field immediates. - let Some(mut builder) = OperandRef::builder(layout) else { - bug!("Cannot use type in operand builder: {layout:?}") - }; + let mut builder = OperandRefBuilder::new(layout); for (field_idx, field) in fields.iter_enumerated() { let op = self.codegen_operand(bx, field); let fi = active_field_index.unwrap_or(field_idx); @@ -980,7 +974,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// will not actually take the operand path because the result type is such /// that it always gets an `alloca`, but where it's not worth re-checking the /// layout in this code when the right thing will happen anyway. - pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool { + pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { match *rvalue { mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => { let operand_ty = operand.ty(self.mir, self.cx.tcx()); @@ -1025,18 +1019,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::NullaryOp(..) | mir::Rvalue::ThreadLocalRef(_) | mir::Rvalue::Use(..) | + mir::Rvalue::Aggregate(..) | // (*) mir::Rvalue::WrapUnsafeBinder(..) => // (*) true, // Arrays are always aggregates, so it's not worth checking anything here. // (If it's really `[(); N]` or `[T; 0]` and we use the place path, fine.) mir::Rvalue::Repeat(..) => false, - mir::Rvalue::Aggregate(..) => { - let ty = rvalue.ty(self.mir, self.cx.tcx()); - let ty = self.monomorphize(ty); - let layout = self.cx.spanned_layout_of(ty, span); - OperandRef::<Bx::Value>::builder(layout).is_some() - } - } + } // (*) this is only true if the type is suitable } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 22a1894ee72..b767ca9a3c2 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -78,6 +78,8 @@ const_eval_dealloc_kind_mismatch = const_eval_deref_function_pointer = accessing {$allocation} which contains a function +const_eval_deref_typeid_pointer = + accessing {$allocation} which contains a `TypeId` const_eval_deref_vtable_pointer = accessing {$allocation} which contains a vtable const_eval_division_by_zero = diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 9133a5fc8ef..49cd7138748 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -475,6 +475,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { WriteToReadOnly(_) => const_eval_write_to_read_only, DerefFunctionPointer(_) => const_eval_deref_function_pointer, DerefVTablePointer(_) => const_eval_deref_vtable_pointer, + DerefTypeIdPointer(_) => const_eval_deref_typeid_pointer, InvalidBool(_) => const_eval_invalid_bool, InvalidChar(_) => const_eval_invalid_char, InvalidTag(_) => const_eval_invalid_tag, @@ -588,7 +589,10 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("has", has.bytes()); diag.arg("msg", format!("{msg:?}")); } - WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => { + WriteToReadOnly(alloc) + | DerefFunctionPointer(alloc) + | DerefVTablePointer(alloc) + | DerefTypeIdPointer(alloc) => { diag.arg("allocation", alloc); } InvalidBool(b) => { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 068d6369f87..41fc8d47cd3 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -96,7 +96,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// This inherent method takes priority over the trait method with the same name in LayoutOf, /// and allows wrapping the actual [LayoutOf::layout_of] with a tracing span. /// See [LayoutOf::layout_of] for the original documentation. - #[inline] + #[inline(always)] pub fn layout_of( &self, ty: Ty<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 378ed6d0e10..1eba1f2f03c 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -4,7 +4,7 @@ use std::assert_matches::assert_matches; -use rustc_abi::Size; +use rustc_abi::{FieldIdx, Size}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; @@ -28,8 +28,35 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ConstAll let alloc = Allocation::from_bytes_byte_aligned_immutable(path.into_bytes(), ()); tcx.mk_const_alloc(alloc) } - impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { + /// Generates a value of `TypeId` for `ty` in-place. + pub(crate) fn write_type_id( + &mut self, + ty: Ty<'tcx>, + dest: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> { + let tcx = self.tcx; + let type_id_hash = tcx.type_id_hash(ty).as_u128(); + let op = self.const_val_to_op( + ConstValue::Scalar(Scalar::from_u128(type_id_hash)), + tcx.types.u128, + None, + )?; + self.copy_op_allow_transmute(&op, dest)?; + + // Give the first pointer-size bytes provenance that knows about the type id. + // Here we rely on `TypeId` being a newtype around an array of pointers, so we + // first project to its only field and then the first array element. + let alloc_id = tcx.reserve_and_set_type_id_alloc(ty); + let first = self.project_field(dest, FieldIdx::ZERO)?; + let first = self.project_index(&first, 0)?; + let offset = self.read_scalar(&first)?.to_target_usize(&tcx)?; + let ptr = Pointer::new(alloc_id.into(), Size::from_bytes(offset)); + let ptr = self.global_root_pointer(ptr)?; + let val = Scalar::from_pointer(ptr, &tcx); + self.write_scalar(val, &first) + } + /// Returns `true` if emulation happened. /// Here we implement the intrinsics that are common to all Miri instances; individual machines can add their own /// intrinsic handling. @@ -63,9 +90,48 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { sym::type_id => { let tp_ty = instance.args.type_at(0); ensure_monomorphic_enough(tcx, tp_ty)?; - let val = ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128()); - let val = self.const_val_to_op(val, dest.layout.ty, Some(dest.layout))?; - self.copy_op(&val, dest)?; + self.write_type_id(tp_ty, dest)?; + } + sym::type_id_eq => { + // Both operands are `TypeId`, which is a newtype around an array of pointers. + // Project until we have the array elements. + let a_fields = self.project_field(&args[0], FieldIdx::ZERO)?; + let b_fields = self.project_field(&args[1], FieldIdx::ZERO)?; + + let mut a_fields = self.project_array_fields(&a_fields)?; + let mut b_fields = self.project_array_fields(&b_fields)?; + + let (_idx, a) = a_fields + .next(self)? + .expect("we know the layout of TypeId has at least 2 array elements"); + let a = self.deref_pointer(&a)?; + let (a, offset_a) = self.get_ptr_type_id(a.ptr())?; + + let (_idx, b) = b_fields + .next(self)? + .expect("we know the layout of TypeId has at least 2 array elements"); + let b = self.deref_pointer(&b)?; + let (b, offset_b) = self.get_ptr_type_id(b.ptr())?; + + let provenance_matches = a == b; + + let mut eq_id = offset_a == offset_b; + + while let Some((_, a)) = a_fields.next(self)? { + let (_, b) = b_fields.next(self)?.unwrap(); + + let a = self.read_target_usize(&a)?; + let b = self.read_target_usize(&b)?; + eq_id &= a == b; + } + + if !eq_id && provenance_matches { + throw_ub_format!( + "type_id_eq: one of the TypeId arguments is invalid, the hash does not match the type it represents" + ) + } + + self.write_scalar(Scalar::from_bool(provenance_matches), dest)?; } sym::variant_count => { let tp_ty = instance.args.type_at(0); diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 844c19fea2d..d150ed69250 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -18,8 +18,8 @@ use rustc_target::callconv::FnAbi; use super::{ AllocBytes, AllocId, AllocKind, AllocRange, Allocation, CTFE_ALLOC_SALT, ConstAllocation, - CtfeProvenance, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, MemoryKind, - Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup, + CtfeProvenance, EnteredTraceSpan, FnArg, Frame, ImmTy, InterpCx, InterpResult, MPlaceTy, + MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, RangeSet, interp_ok, throw_unsup, }; /// Data returned by [`Machine::after_stack_pop`], and consumed by @@ -147,12 +147,6 @@ pub trait Machine<'tcx>: Sized { /// already been checked before. const ALL_CONSTS_ARE_PRECHECKED: bool = true; - /// Determines whether rustc_const_eval functions that make use of the [Machine] should make - /// tracing calls (to the `tracing` library). By default this is `false`, meaning the tracing - /// calls will supposedly be optimized out. This flag is set to `true` inside Miri, to allow - /// tracing the interpretation steps, among other things. - const TRACING_ENABLED: bool = false; - /// Whether memory accesses should be alignment-checked. fn enforce_alignment(ecx: &InterpCx<'tcx, Self>) -> bool; @@ -634,6 +628,17 @@ pub trait Machine<'tcx>: Sized { /// Compute the value passed to the constructors of the `AllocBytes` type for /// abstract machine allocations. fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams; + + /// Allows enabling/disabling tracing calls from within `rustc_const_eval` at compile time, by + /// delegating the entering of [tracing::Span]s to implementors of the [Machine] trait. The + /// default implementation corresponds to tracing being disabled, meaning the tracing calls will + /// supposedly be optimized out completely. To enable tracing, override this trait method and + /// return `span.entered()`. Also see [crate::enter_trace_span]. + #[must_use] + #[inline(always)] + fn enter_trace_span(_span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan { + () + } } /// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 524023b8104..bb301600135 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -15,9 +15,9 @@ use std::{fmt, ptr}; use rustc_abi::{Align, HasDataLayout, Size}; use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_middle::bug; use rustc_middle::mir::display_allocation; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::{bug, throw_ub_format}; use tracing::{debug, instrument, trace}; use super::{ @@ -346,6 +346,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { kind = "vtable", ) } + Some(GlobalAlloc::TypeId { .. }) => { + err_ub_custom!( + fluent::const_eval_invalid_dealloc, + alloc_id = alloc_id, + kind = "typeid", + ) + } Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => { err_ub_custom!( fluent::const_eval_invalid_dealloc, @@ -615,6 +622,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)), Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)), + Some(GlobalAlloc::TypeId { .. }) => throw_ub!(DerefTypeIdPointer(id)), None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccess)), Some(GlobalAlloc::Static(def_id)) => { assert!(self.tcx.is_static(def_id)); @@ -896,7 +904,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env); let mutbl = global_alloc.mutability(*self.tcx, self.typing_env); let kind = match global_alloc { - GlobalAlloc::Static { .. } | GlobalAlloc::Memory { .. } => AllocKind::LiveData, + GlobalAlloc::TypeId { .. } + | GlobalAlloc::Static { .. } + | GlobalAlloc::Memory { .. } => AllocKind::LiveData, GlobalAlloc::Function { .. } => bug!("We already checked function pointers above"), GlobalAlloc::VTable { .. } => AllocKind::VTable, }; @@ -936,6 +946,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + /// Takes a pointer that is the first chunk of a `TypeId` and return the type that its + /// provenance refers to, as well as the segment of the hash that this pointer covers. + pub fn get_ptr_type_id( + &self, + ptr: Pointer<Option<M::Provenance>>, + ) -> InterpResult<'tcx, (Ty<'tcx>, Size)> { + let (alloc_id, offset, _meta) = self.ptr_get_alloc_id(ptr, 0)?; + let GlobalAlloc::TypeId { ty } = self.tcx.global_alloc(alloc_id) else { + throw_ub_format!("type_id_eq: `TypeId` provenance is not a type id") + }; + interp_ok((ty, offset)) + } + pub fn get_ptr_fn( &self, ptr: Pointer<Option<M::Provenance>>, @@ -1197,6 +1220,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> { Some(GlobalAlloc::VTable(ty, dyn_ty)) => { write!(fmt, " (vtable: impl {dyn_ty} for {ty})")?; } + Some(GlobalAlloc::TypeId { ty }) => { + write!(fmt, " (typeid for {ty})")?; + } Some(GlobalAlloc::Static(did)) => { write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?; } diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 8303f891f98..2fc372dd019 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -37,6 +37,7 @@ pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; use self::place::{MemPlace, Place}; pub use self::projection::{OffsetMode, Projectable}; pub use self::stack::{Frame, FrameInfo, LocalState, ReturnContinuation, StackPopInfo}; +pub use self::util::EnteredTraceSpan; pub(crate) use self::util::create_static_alloc; pub use self::validity::{CtfeValidationMode, RangeSet, RefTracking}; pub use self::visitor::ValueVisitor; diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 306697d4ec9..f72c4418081 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -296,7 +296,11 @@ where base: &'a P, ) -> InterpResult<'tcx, ArrayIterator<'a, 'tcx, M::Provenance, P>> { let abi::FieldsShape::Array { stride, .. } = base.layout().fields else { - span_bug!(self.cur_span(), "project_array_fields: expected an array layout"); + span_bug!( + self.cur_span(), + "project_array_fields: expected an array layout, got {:#?}", + base.layout() + ); }; let len = base.len(self)?; let field_layout = base.layout().field(self, 0); diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 99add01f95c..72650d545c3 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -46,21 +46,20 @@ pub(crate) fn create_static_alloc<'tcx>( interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout)) } -/// This struct is needed to enforce `#[must_use]` on [tracing::span::EnteredSpan] -/// while wrapping them in an `Option`. -#[must_use] -pub enum MaybeEnteredSpan { - Some(tracing::span::EnteredSpan), - None, -} +/// A marker trait returned by [crate::interpret::Machine::enter_trace_span], identifying either a +/// real [tracing::span::EnteredSpan] in case tracing is enabled, or the dummy type `()` when +/// tracing is disabled. +pub trait EnteredTraceSpan {} +impl EnteredTraceSpan for () {} +impl EnteredTraceSpan for tracing::span::EnteredSpan {} +/// Shortand for calling [crate::interpret::Machine::enter_trace_span] on a [tracing::info_span]. +/// This is supposed to be compiled out when [crate::interpret::Machine::enter_trace_span] has the +/// default implementation (i.e. when it does not actually enter the span but instead returns `()`). +/// Note: the result of this macro **must be used** because the span is exited when it's dropped. #[macro_export] macro_rules! enter_trace_span { ($machine:ident, $($tt:tt)*) => { - if $machine::TRACING_ENABLED { - $crate::interpret::util::MaybeEnteredSpan::Some(tracing::info_span!($($tt)*).entered()) - } else { - $crate::interpret::util::MaybeEnteredSpan::None - } + $machine::enter_trace_span(|| tracing::info_span!($($tt)*)) } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index fc4d13af8c4..fc44490c96d 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -571,40 +571,42 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { let alloc_actual_mutbl = global_alloc.mutability(*self.ecx.tcx, self.ecx.typing_env); - if let GlobalAlloc::Static(did) = global_alloc { - let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { - bug!() - }; - // Special handling for pointers to statics (irrespective of their type). - assert!(!self.ecx.tcx.is_thread_local_static(did)); - assert!(self.ecx.tcx.is_static(did)); - // Mode-specific checks - match ctfe_mode { - CtfeValidationMode::Static { .. } - | CtfeValidationMode::Promoted { .. } => { - // We skip recursively checking other statics. These statics must be sound by - // themselves, and the only way to get broken statics here is by using - // unsafe code. - // The reasons we don't check other statics is twofold. For one, in all - // sound cases, the static was already validated on its own, and second, we - // trigger cycle errors if we try to compute the value of the other static - // and that static refers back to us (potentially through a promoted). - // This could miss some UB, but that's fine. - // We still walk nested allocations, as they are fundamentally part of this validation run. - // This means we will also recurse into nested statics of *other* - // statics, even though we do not recurse into other statics directly. - // That's somewhat inconsistent but harmless. - skip_recursive_check = !nested; - } - CtfeValidationMode::Const { .. } => { - // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd - // just get errors trying to read the value. - if alloc_actual_mutbl.is_mut() || self.ecx.tcx.is_foreign_item(did) - { - skip_recursive_check = true; + match global_alloc { + GlobalAlloc::Static(did) => { + let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { + bug!() + }; + assert!(!self.ecx.tcx.is_thread_local_static(did)); + assert!(self.ecx.tcx.is_static(did)); + match ctfe_mode { + CtfeValidationMode::Static { .. } + | CtfeValidationMode::Promoted { .. } => { + // We skip recursively checking other statics. These statics must be sound by + // themselves, and the only way to get broken statics here is by using + // unsafe code. + // The reasons we don't check other statics is twofold. For one, in all + // sound cases, the static was already validated on its own, and second, we + // trigger cycle errors if we try to compute the value of the other static + // and that static refers back to us (potentially through a promoted). + // This could miss some UB, but that's fine. + // We still walk nested allocations, as they are fundamentally part of this validation run. + // This means we will also recurse into nested statics of *other* + // statics, even though we do not recurse into other statics directly. + // That's somewhat inconsistent but harmless. + skip_recursive_check = !nested; + } + CtfeValidationMode::Const { .. } => { + // If this is mutable memory or an `extern static`, there's no point in checking it -- we'd + // just get errors trying to read the value. + if alloc_actual_mutbl.is_mut() + || self.ecx.tcx.is_foreign_item(did) + { + skip_recursive_check = true; + } } } } + _ => (), } // If this allocation has size zero, there is no actual mutability here. diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 2f398cea926..510f37f37e2 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -3526,7 +3526,7 @@ pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { // All the chars that differ in capitalization are confusable (above): let confusable = iter::zip(found.chars(), suggested.chars()) .filter(|(f, s)| f != s) - .all(|(f, s)| (ascii_confusables.contains(&f) || ascii_confusables.contains(&s))); + .all(|(f, s)| ascii_confusables.contains(&f) || ascii_confusables.contains(&s)); confusable && found.to_lowercase() == suggested.to_lowercase() // FIXME: We sometimes suggest the same thing we already have, which is a // bug, but be defensive against that here. diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 3fc0fa06191..89d6e62834d 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -133,6 +133,32 @@ expand_module_multiple_candidates = expand_must_repeat_once = this must repeat at least once +expand_mve_extra_tokens = + unexpected trailing tokens + .label = for this metavariable expression + .range = the `{$name}` metavariable expression takes between {$min_or_exact_args} and {$max_args} arguments + .exact = the `{$name}` metavariable expression takes {$min_or_exact_args -> + [zero] no arguments + [one] a single argument + *[other] {$min_or_exact_args} arguments + } + .suggestion = try removing {$extra_count -> + [one] this token + *[other] these tokens + } + +expand_mve_missing_paren = + expected `(` + .label = for this this metavariable expression + .unexpected = unexpected token + .note = metavariable expressions use function-like parentheses syntax + .suggestion = try adding parentheses + +expand_mve_unrecognized_expr = + unrecognized metavariable expression + .label = not a valid metavariable expression + .note = valid metavariable expressions are {$valid_expr_list} + expand_mve_unrecognized_var = variable `{$key}` is not recognized in meta-variable expression diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index fdbc65aff68..3ac5d213053 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -496,6 +496,50 @@ pub(crate) use metavar_exprs::*; mod metavar_exprs { use super::*; + #[derive(Diagnostic, Default)] + #[diag(expand_mve_extra_tokens)] + pub(crate) struct MveExtraTokens { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub span: Span, + #[label] + pub ident_span: Span, + pub extra_count: usize, + + // The rest is only used for specific diagnostics and can be default if neither + // `note` is `Some`. + #[note(expand_exact)] + pub exact_args_note: Option<()>, + #[note(expand_range)] + pub range_args_note: Option<()>, + pub min_or_exact_args: usize, + pub max_args: usize, + pub name: String, + } + + #[derive(Diagnostic)] + #[note] + #[diag(expand_mve_missing_paren)] + pub(crate) struct MveMissingParen { + #[primary_span] + #[label] + pub ident_span: Span, + #[label(expand_unexpected)] + pub unexpected_span: Option<Span>, + #[suggestion(code = "( /* ... */ )", applicability = "has-placeholders")] + pub insert_span: Option<Span>, + } + + #[derive(Diagnostic)] + #[note] + #[diag(expand_mve_unrecognized_expr)] + pub(crate) struct MveUnrecognizedExpr { + #[primary_span] + #[label] + pub span: Span, + pub valid_expr_list: &'static str, + } + #[derive(Diagnostic)] #[diag(expand_mve_unrecognized_var)] pub(crate) struct MveUnrecognizedVar { diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index ffd3548019a..d2b275ad20a 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -7,6 +7,8 @@ use rustc_macros::{Decodable, Encodable}; use rustc_session::parse::ParseSess; use rustc_span::{Ident, Span, Symbol}; +use crate::errors; + pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; @@ -40,11 +42,32 @@ impl MetaVarExpr { ) -> PResult<'psess, MetaVarExpr> { let mut iter = input.iter(); let ident = parse_ident(&mut iter, psess, outer_span)?; - let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = iter.next() else { - let msg = "meta-variable expression parameter must be wrapped in parentheses"; - return Err(psess.dcx().struct_span_err(ident.span, msg)); + let next = iter.next(); + let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = next else { + // No `()`; wrong or no delimiters. Point at a problematic span or a place to + // add parens if it makes sense. + let (unexpected_span, insert_span) = match next { + Some(TokenTree::Delimited(..)) => (None, None), + Some(tt) => (Some(tt.span()), None), + None => (None, Some(ident.span.shrink_to_hi())), + }; + let err = + errors::MveMissingParen { ident_span: ident.span, unexpected_span, insert_span }; + return Err(psess.dcx().create_err(err)); }; - check_trailing_token(&mut iter, psess)?; + + // Ensure there are no trailing tokens in the braces, e.g. `${foo() extra}` + if iter.peek().is_some() { + let span = iter_span(&iter).expect("checked is_some above"); + let err = errors::MveExtraTokens { + span, + ident_span: ident.span, + extra_count: iter.count(), + ..Default::default() + }; + return Err(psess.dcx().create_err(err)); + } + let mut iter = args.iter(); let rslt = match ident.as_str() { "concat" => parse_concat(&mut iter, psess, outer_span, ident.span)?, @@ -56,18 +79,14 @@ impl MetaVarExpr { "index" => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?), "len" => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?), _ => { - let err_msg = "unrecognized meta-variable expression"; - let mut err = psess.dcx().struct_span_err(ident.span, err_msg); - err.span_suggestion( - ident.span, - "supported expressions are count, ignore, index and len", - "", - Applicability::MachineApplicable, - ); - return Err(err); + let err = errors::MveUnrecognizedExpr { + span: ident.span, + valid_expr_list: "`count`, `ignore`, `index`, `len`, and `concat`", + }; + return Err(psess.dcx().create_err(err)); } }; - check_trailing_token(&mut iter, psess)?; + check_trailing_tokens(&mut iter, psess, ident)?; Ok(rslt) } @@ -87,20 +106,51 @@ impl MetaVarExpr { } } -// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` -fn check_trailing_token<'psess>( +/// Checks if there are any remaining tokens (for example, `${ignore($valid, extra)}`) and create +/// a diag with the correct arg count if so. +fn check_trailing_tokens<'psess>( iter: &mut TokenStreamIter<'_>, psess: &'psess ParseSess, + ident: Ident, ) -> PResult<'psess, ()> { - if let Some(tt) = iter.next() { - let mut diag = psess - .dcx() - .struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt))); - diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens"); - Err(diag) - } else { - Ok(()) + if iter.peek().is_none() { + // All tokens consumed, as expected + return Ok(()); } + + // `None` for max indicates the arg count must be exact, `Some` indicates a range is accepted. + let (min_or_exact_args, max_args) = match ident.as_str() { + "concat" => panic!("concat takes unlimited tokens but didn't eat them all"), + "ignore" => (1, None), + // 1 or 2 args + "count" => (1, Some(2)), + // 0 or 1 arg + "index" => (0, Some(1)), + "len" => (0, Some(1)), + other => unreachable!("unknown MVEs should be rejected earlier (got `{other}`)"), + }; + + let err = errors::MveExtraTokens { + span: iter_span(iter).expect("checked is_none above"), + ident_span: ident.span, + extra_count: iter.count(), + + exact_args_note: if max_args.is_some() { None } else { Some(()) }, + range_args_note: if max_args.is_some() { Some(()) } else { None }, + min_or_exact_args, + max_args: max_args.unwrap_or_default(), + name: ident.to_string(), + }; + Err(psess.dcx().create_err(err)) +} + +/// Returns a span encompassing all tokens in the iterator if there is at least one item. +fn iter_span(iter: &TokenStreamIter<'_>) -> Option<Span> { + let mut iter = iter.clone(); // cloning is cheap + let first_sp = iter.next()?.span(); + let last_sp = iter.last().map(TokenTree::span).unwrap_or(first_sp); + let span = first_sp.with_hi(last_sp.hi()); + Some(span) } /// Indicates what is placed in a `concat` parameter. For example, literals diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c11db63ba11..6347f1bfa71 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -274,6 +274,8 @@ language_item_table! { PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; + TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None; + // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and // various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays. // diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index cebf7d1b532..e3532ade32f 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -93,6 +93,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::three_way_compare | sym::discriminant_value | sym::type_id + | sym::type_id_eq | sym::select_unpredictable | sym::cold_path | sym::ptr_guaranteed_cmp @@ -220,7 +221,13 @@ pub(crate) fn check_intrinsic_type( sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), - sym::type_id => (1, 0, vec![], tcx.types.u128), + sym::type_id => { + (1, 0, vec![], tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()) + } + sym::type_id_eq => { + let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity(); + (0, 0, vec![type_id, type_id], tcx.types.bool) + } sym::offset => (2, 0, vec![param(0), param(1)], param(0)), sym::arith_offset => ( 1, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index d3942a1c816..a9eb1739f7f 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,7 +1,7 @@ use std::iter; use rustc_ast::util::{classify, parser}; -use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind}; +use rustc_ast::{self as ast, ExprKind, FnRetTy, HasAttrs as _, StmtKind}; use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{MultiSpan, pluralize}; @@ -599,6 +599,7 @@ enum UnusedDelimsCtx { AnonConst, MatchArmExpr, IndexExpr, + ClosureBody, } impl From<UnusedDelimsCtx> for &'static str { @@ -620,6 +621,7 @@ impl From<UnusedDelimsCtx> for &'static str { UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression", UnusedDelimsCtx::MatchArmExpr => "match arm expression", UnusedDelimsCtx::IndexExpr => "index expression", + UnusedDelimsCtx::ClosureBody => "closure body", } } } @@ -919,6 +921,11 @@ trait UnusedDelimLint { let (args_to_check, ctx) = match *call_or_other { Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg), MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg), + Closure(ref closure) + if matches!(closure.fn_decl.output, FnRetTy::Default(_)) => + { + (&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody) + } // actual catch-all arm _ => { return; @@ -1508,6 +1515,7 @@ impl UnusedDelimLint for UnusedBraces { && (ctx != UnusedDelimsCtx::AnonConst || (matches!(expr.kind, ast::ExprKind::Lit(_)) && !expr.span.from_expansion())) + && ctx != UnusedDelimsCtx::ClosureBody && !cx.sess().source_map().is_multiline(value.span) && value.attrs.is_empty() && !value.span.from_expansion() diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b37548b281c..15f0bad226d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4343,11 +4343,12 @@ declare_lint! { /// /// [future-incompatible]: ../index.md#future-incompatible-lints pub AMBIGUOUS_GLOB_IMPORTS, - Warn, + Deny, "detects certain glob imports that require reporting an ambiguity error", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::FutureReleaseError, reference: "issue #114095 <https://github.com/rust-lang/rust/issues/114095>", + report_in_deps: true, }; } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 8acb8fa9f80..71e0c943fbb 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -392,6 +392,8 @@ pub enum UndefinedBehaviorInfo<'tcx> { DerefFunctionPointer(AllocId), /// Trying to access the data behind a vtable pointer. DerefVTablePointer(AllocId), + /// Trying to access the actual type id. + DerefTypeIdPointer(AllocId), /// Using a non-boolean `u8` as bool. InvalidBool(u8), /// Using a non-character `u32` as character. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 0b2645013ba..bed99a4ff2a 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -103,6 +103,7 @@ enum AllocDiscriminant { Fn, VTable, Static, + Type, } pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( @@ -127,6 +128,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<'tcx>>( ty.encode(encoder); poly_trait_ref.encode(encoder); } + GlobalAlloc::TypeId { ty } => { + trace!("encoding {alloc_id:?} with {ty:#?}"); + AllocDiscriminant::Type.encode(encoder); + ty.encode(encoder); + } GlobalAlloc::Static(did) => { assert!(!tcx.is_thread_local_static(did)); // References to statics doesn't need to know about their allocations, @@ -228,6 +234,12 @@ impl<'s> AllocDecodingSession<'s> { trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref, CTFE_ALLOC_SALT) } + AllocDiscriminant::Type => { + trace!("creating typeid alloc ID"); + let ty = Decodable::decode(decoder); + trace!("decoded typid: {ty:?}"); + decoder.interner().reserve_and_set_type_id_alloc(ty) + } AllocDiscriminant::Static => { trace!("creating extern static alloc ID"); let did = <DefId as Decodable<D>>::decode(decoder); @@ -258,6 +270,9 @@ pub enum GlobalAlloc<'tcx> { Static(DefId), /// The alloc ID points to memory. Memory(ConstAllocation<'tcx>), + /// The first pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id + /// is split into two segments, on 32 bit systems there are 4 segments, and so on. + TypeId { ty: Ty<'tcx> }, } impl<'tcx> GlobalAlloc<'tcx> { @@ -296,9 +311,10 @@ impl<'tcx> GlobalAlloc<'tcx> { pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace { match self { GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space, - GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => { - AddressSpace::ZERO - } + GlobalAlloc::TypeId { .. } + | GlobalAlloc::Static(..) + | GlobalAlloc::Memory(..) + | GlobalAlloc::VTable(..) => AddressSpace::ZERO, } } @@ -334,7 +350,7 @@ impl<'tcx> GlobalAlloc<'tcx> { } } GlobalAlloc::Memory(alloc) => alloc.inner().mutability, - GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { + GlobalAlloc::TypeId { .. } | GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => { // These are immutable. Mutability::Not } @@ -380,8 +396,10 @@ impl<'tcx> GlobalAlloc<'tcx> { GlobalAlloc::Function { .. } => (Size::ZERO, Align::ONE), GlobalAlloc::VTable(..) => { // No data to be accessed here. But vtables are pointer-aligned. - return (Size::ZERO, tcx.data_layout.pointer_align().abi); + (Size::ZERO, tcx.data_layout.pointer_align().abi) } + // Fake allocation, there's nothing to access here + GlobalAlloc::TypeId { .. } => (Size::ZERO, Align::ONE), } } } @@ -487,6 +505,11 @@ impl<'tcx> TyCtxt<'tcx> { self.reserve_and_set_dedup(GlobalAlloc::VTable(ty, dyn_ty), salt) } + /// Generates an [AllocId] for a [core::any::TypeId]. Will get deduplicated. + pub fn reserve_and_set_type_id_alloc(self, ty: Ty<'tcx>) -> AllocId { + self.reserve_and_set_dedup(GlobalAlloc::TypeId { ty }, 0) + } + /// Interns the `Allocation` and return a new `AllocId`, even if there's already an identical /// `Allocation` with a different `AllocId`. /// Statics with identical content will still point to the same `Allocation`, i.e., diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e9f3fb6ac8d..8e403dfddae 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1621,6 +1621,7 @@ pub fn write_allocations<'tcx>( Some(GlobalAlloc::VTable(ty, dyn_ty)) => { write!(w, " (vtable: impl {dyn_ty} for {ty})")? } + Some(GlobalAlloc::TypeId { ty }) => write!(w, " (typeid for {ty})")?, Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => { write!(w, " (static: {}", tcx.def_path_str(did))?; if body.phase <= MirPhase::Runtime(RuntimePhase::PostCleanup) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 4e078847815..7c48a4b0885 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1773,6 +1773,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } Some(GlobalAlloc::Function { .. }) => p!("<function>"), Some(GlobalAlloc::VTable(..)) => p!("<vtable>"), + Some(GlobalAlloc::TypeId { .. }) => p!("<typeid>"), None => p!("<dangling pointer>"), } return Ok(()); diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 5a5456aedc2..12a56d7c5ea 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -927,6 +927,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrut_span: rustc_span::Span::default(), refutable: true, known_valid_scrutinee: true, + internal_state: Default::default(), }; let valtree = match self.eval_unevaluated_mir_constant_to_valtree(constant) { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index b7b160c738d..a49bfc1b8f4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -406,6 +406,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { scrut_span, refutable, known_valid_scrutinee, + internal_state: Default::default(), } } diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index 496a342cf4c..c9c7fddae5a 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -1,5 +1,6 @@ use rustc_abi::VariantIdx; use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind}; +use smallvec::SmallVec; use tracing::debug; use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex}; @@ -155,15 +156,28 @@ where } } -/// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a -/// `Downcast` to a variant besides the `active_variant`. -/// -/// NOTE: If there are no move paths corresponding to an inactive variant, -/// `handle_inactive_variant` will not be called for that variant. +/// Indicates which variants are inactive at a `SwitchInt` edge by listing their `VariantIdx`s or +/// specifying the single active variant's `VariantIdx`. +pub(crate) enum InactiveVariants { + Inactives(SmallVec<[VariantIdx; 4]>), + Active(VariantIdx), +} + +impl InactiveVariants { + fn contains(&self, variant_idx: VariantIdx) -> bool { + match self { + InactiveVariants::Inactives(inactives) => inactives.contains(&variant_idx), + InactiveVariants::Active(active) => variant_idx != *active, + } + } +} + +/// Calls `handle_inactive_variant` for each child move path of `enum_place` corresponding to an +/// inactive variant at a particular `SwitchInt` edge. pub(crate) fn on_all_inactive_variants<'tcx>( move_data: &MoveData<'tcx>, enum_place: mir::Place<'tcx>, - active_variant: VariantIdx, + inactive_variants: &InactiveVariants, mut handle_inactive_variant: impl FnMut(MovePathIndex), ) { let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else { @@ -182,7 +196,7 @@ pub(crate) fn on_all_inactive_variants<'tcx>( unreachable!(); }; - if variant_idx != active_variant { + if inactive_variants.contains(variant_idx) { on_all_children_bits(move_data, variant_mpi, |mpi| handle_inactive_variant(mpi)); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index e955e38ad10..cb647476db8 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -112,12 +112,13 @@ impl Direction for Backward { propagate(pred, &tmp); } - mir::TerminatorKind::SwitchInt { targets: _, ref discr } => { + mir::TerminatorKind::SwitchInt { ref targets, ref discr } => { if let Some(mut data) = analysis.get_switch_int_data(block, discr) { let mut tmp = analysis.bottom_value(body); for &value in &body.basic_blocks.switch_sources()[&(block, pred)] { tmp.clone_from(exit_state); - analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value); + analysis + .apply_switch_int_edge_effect(&mut data, &mut tmp, value, targets); propagate(pred, &tmp); } } else { @@ -290,20 +291,20 @@ impl Direction for Forward { for (value, target) in targets.iter() { tmp.clone_from(exit_state); let value = SwitchTargetValue::Normal(value); - analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value); + analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value, targets); propagate(target, &tmp); } // Once we get to the final, "otherwise" branch, there is no need to preserve // `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save // a clone of the dataflow state. - let otherwise = targets.otherwise(); analysis.apply_switch_int_edge_effect( &mut data, exit_state, SwitchTargetValue::Otherwise, + targets, ); - propagate(otherwise, exit_state); + propagate(targets.otherwise(), exit_state); } else { for target in targets.all_targets() { propagate(*target, exit_state); diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 9cadec100b5..b6a56036019 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -224,6 +224,7 @@ pub trait Analysis<'tcx> { _data: &mut Self::SwitchIntData, _state: &mut Self::Domain, _value: SwitchTargetValue, + _targets: &mir::SwitchTargets, ) { unreachable!(); } diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 18165b0b9bd..085757f0fb6 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -9,9 +9,10 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::util::Discr; use rustc_middle::ty::{self, TyCtxt}; +use smallvec::SmallVec; use tracing::{debug, instrument}; -use crate::drop_flag_effects::DropFlagState; +use crate::drop_flag_effects::{DropFlagState, InactiveVariants}; use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::{ Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry, @@ -26,6 +27,12 @@ pub struct MaybePlacesSwitchIntData<'tcx> { } impl<'tcx> MaybePlacesSwitchIntData<'tcx> { + /// Creates a `SmallVec` mapping each target in `targets` to its `VariantIdx`. + fn variants(&mut self, targets: &mir::SwitchTargets) -> SmallVec<[VariantIdx; 4]> { + self.index = 0; + targets.all_values().iter().map(|value| self.next_discr(value.get())).collect() + } + // The discriminant order in the `SwitchInt` targets should match the order yielded by // `AdtDef::discriminants`. We rely on this to match each discriminant in the targets to its // corresponding variant in linear time. @@ -131,12 +138,26 @@ pub struct MaybeInitializedPlaces<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>, + exclude_inactive_in_otherwise: bool, skip_unreachable_unwind: bool, } impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self { - MaybeInitializedPlaces { tcx, body, move_data, skip_unreachable_unwind: false } + MaybeInitializedPlaces { + tcx, + body, + move_data, + exclude_inactive_in_otherwise: false, + skip_unreachable_unwind: false, + } + } + + /// Ensures definitely inactive variants are excluded from the set of initialized places for + /// blocks reached through an `otherwise` edge. + pub fn exclude_inactive_in_otherwise(mut self) -> Self { + self.exclude_inactive_in_otherwise = true; + self } pub fn skipping_unreachable_unwind(mut self) -> Self { @@ -208,6 +229,7 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> { move_data: &'a MoveData<'tcx>, mark_inactive_variants_as_uninit: bool, + include_inactive_in_otherwise: bool, skip_unreachable_unwind: DenseBitSet<mir::BasicBlock>, } @@ -218,6 +240,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { body, move_data, mark_inactive_variants_as_uninit: false, + include_inactive_in_otherwise: false, skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()), } } @@ -232,6 +255,13 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { self } + /// Ensures definitely inactive variants are included in the set of uninitialized places for + /// blocks reached through an `otherwise` edge. + pub fn include_inactive_in_otherwise(mut self) -> Self { + self.include_inactive_in_otherwise = true; + self + } + pub fn skipping_unreachable_unwind( mut self, unreachable_unwind: DenseBitSet<mir::BasicBlock>, @@ -431,17 +461,24 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, + targets: &mir::SwitchTargets, ) { - if let SwitchTargetValue::Normal(value) = value { - // Kill all move paths that correspond to variants we know to be inactive along this - // particular outgoing edge of a `SwitchInt`. - drop_flag_effects::on_all_inactive_variants( - self.move_data, - data.enum_place, - data.next_discr(value), - |mpi| state.kill(mpi), - ); - } + let inactive_variants = match value { + SwitchTargetValue::Normal(value) => InactiveVariants::Active(data.next_discr(value)), + SwitchTargetValue::Otherwise if self.exclude_inactive_in_otherwise => { + InactiveVariants::Inactives(data.variants(targets)) + } + _ => return, + }; + + // Kill all move paths that correspond to variants we know to be inactive along this + // particular outgoing edge of a `SwitchInt`. + drop_flag_effects::on_all_inactive_variants( + self.move_data, + data.enum_place, + &inactive_variants, + |mpi| state.kill(mpi), + ); } } @@ -544,17 +581,24 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { data: &mut Self::SwitchIntData, state: &mut Self::Domain, value: SwitchTargetValue, + targets: &mir::SwitchTargets, ) { - if let SwitchTargetValue::Normal(value) = value { - // Mark all move paths that correspond to variants other than this one as maybe - // uninitialized (in reality, they are *definitely* uninitialized). - drop_flag_effects::on_all_inactive_variants( - self.move_data, - data.enum_place, - data.next_discr(value), - |mpi| state.gen_(mpi), - ); - } + let inactive_variants = match value { + SwitchTargetValue::Normal(value) => InactiveVariants::Active(data.next_discr(value)), + SwitchTargetValue::Otherwise if self.include_inactive_in_otherwise => { + InactiveVariants::Inactives(data.variants(targets)) + } + _ => return, + }; + + // Mark all move paths that correspond to variants other than this one as maybe + // uninitialized (in reality, they are *definitely* uninitialized). + drop_flag_effects::on_all_inactive_variants( + self.move_data, + data.enum_place, + &inactive_variants, + |mpi| state.gen_(mpi), + ); } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 42c8cb0b906..b4fa2be1d00 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -62,12 +62,14 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { let env = MoveDataTypingEnv { move_data, typing_env }; let mut inits = MaybeInitializedPlaces::new(tcx, body, &env.move_data) + .exclude_inactive_in_otherwise() .skipping_unreachable_unwind() .iterate_to_fixpoint(tcx, body, Some("elaborate_drops")) .into_results_cursor(body); let dead_unwinds = compute_dead_unwinds(body, &mut inits); let uninits = MaybeUninitializedPlaces::new(tcx, body, &env.move_data) + .include_inactive_in_otherwise() .mark_inactive_variants_as_uninit() .skipping_unreachable_unwind(dead_unwinds) .iterate_to_fixpoint(tcx, body, Some("elaborate_drops")) diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index 9044a88295c..25d6fa1bab9 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -22,6 +22,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, typing_env)); let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) + .exclude_inactive_in_otherwise() .iterate_to_fixpoint(tcx, body, Some("remove_uninit_drops")) .into_results_cursor(body); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 131064f9832..91c8e64ce9a 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1219,6 +1219,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt )); collect_alloc(tcx, alloc_id, output) } + GlobalAlloc::TypeId { .. } => {} } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9ed7124a11c..d6cc98d505c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2206,7 +2206,7 @@ impl<'a> Parser<'a> { if self.look_ahead(1, |t| *t == token::Bang) && self.look_ahead(2, |t| t.is_ident()) { return IsMacroRulesItem::Yes { has_bang: true }; - } else if self.look_ahead(1, |t| (t.is_ident())) { + } else if self.look_ahead(1, |t| t.is_ident()) { // macro_rules foo self.dcx().emit_err(errors::MacroRulesMissingBang { span: macro_rules_span, diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 7e15267a953..b49e8118fe3 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -325,6 +325,7 @@ impl<'tcx> ReachableContext<'tcx> { self.visit(args); } } + GlobalAlloc::TypeId { ty, .. } => self.visit(ty), GlobalAlloc::Memory(alloc) => self.propagate_from_alloc(alloc), } } diff --git a/compiler/rustc_pattern_analysis/src/checks.rs b/compiler/rustc_pattern_analysis/src/checks.rs new file mode 100644 index 00000000000..88ccaa1e0e5 --- /dev/null +++ b/compiler/rustc_pattern_analysis/src/checks.rs @@ -0,0 +1,50 @@ +//! Contains checks that must be run to validate matches before performing usefulness analysis. + +use crate::constructor::Constructor::*; +use crate::pat_column::PatternColumn; +use crate::{MatchArm, PatCx}; + +/// Validate that deref patterns and normal constructors aren't used to match on the same place. +pub(crate) fn detect_mixed_deref_pat_ctors<'p, Cx: PatCx>( + cx: &Cx, + arms: &[MatchArm<'p, Cx>], +) -> Result<(), Cx::Error> { + let pat_column = PatternColumn::new(arms); + detect_mixed_deref_pat_ctors_inner(cx, &pat_column) +} + +fn detect_mixed_deref_pat_ctors_inner<'p, Cx: PatCx>( + cx: &Cx, + column: &PatternColumn<'p, Cx>, +) -> Result<(), Cx::Error> { + let Some(ty) = column.head_ty() else { + return Ok(()); + }; + + // Check for a mix of deref patterns and normal constructors. + let mut deref_pat = None; + let mut normal_pat = None; + for pat in column.iter() { + match pat.ctor() { + // The analysis can handle mixing deref patterns with wildcards and opaque patterns. + Wildcard | Opaque(_) => {} + DerefPattern(_) => deref_pat = Some(pat), + // Nothing else can be compared to deref patterns in `Constructor::is_covered_by`. + _ => normal_pat = Some(pat), + } + } + if let Some(deref_pat) = deref_pat + && let Some(normal_pat) = normal_pat + { + return Err(cx.report_mixed_deref_pat_ctors(deref_pat, normal_pat)); + } + + // Specialize and recurse into the patterns' fields. + let set = column.analyze_ctors(cx, &ty)?; + for ctor in set.present { + for specialized_column in column.specialize(cx, &ty, &ctor).iter() { + detect_mixed_deref_pat_ctors_inner(cx, specialized_column)?; + } + } + Ok(()) +} diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 2b85d7b26ce..66df35f9ee4 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -8,6 +8,7 @@ #![allow(unused_crate_dependencies)] // tidy-alphabetical-end +pub(crate) mod checks; pub mod constructor; #[cfg(feature = "rustc")] pub mod errors; @@ -107,6 +108,20 @@ pub trait PatCx: Sized + fmt::Debug { _gapped_with: &[&DeconstructedPat<Self>], ) { } + + /// Check if we may need to perform additional deref-pattern-specific validation. + fn match_may_contain_deref_pats(&self) -> bool { + true + } + + /// The current implementation of deref patterns requires that they can't match on the same + /// place as a normal constructor. Since this isn't caught by type-checking, we check it in the + /// `PatCx` before running the analysis. This reports an error if the check fails. + fn report_mixed_deref_pat_ctors( + &self, + deref_pat: &DeconstructedPat<Self>, + normal_pat: &DeconstructedPat<Self>, + ) -> Self::Error; } /// The arm of a match expression. diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index e53cebc59ba..ee72b676b38 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -1,3 +1,4 @@ +use std::cell::Cell; use std::fmt; use std::iter::once; @@ -99,6 +100,16 @@ pub struct RustcPatCtxt<'p, 'tcx: 'p> { /// Whether the data at the scrutinee is known to be valid. This is false if the scrutinee comes /// from a union field, a pointer deref, or a reference deref (pending opsem decisions). pub known_valid_scrutinee: bool, + pub internal_state: RustcPatCtxtState, +} + +/// Private fields of [`RustcPatCtxt`], separated out to permit record initialization syntax. +#[derive(Clone, Default)] +pub struct RustcPatCtxtState { + /// Has a deref pattern been lowered? This is initialized to `false` and is updated by + /// [`RustcPatCtxt::lower_pat`] in order to avoid performing deref-pattern-specific validation + /// for everything containing patterns. + has_lowered_deref_pat: Cell<bool>, } impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> { @@ -474,6 +485,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { fields = vec![self.lower_pat(subpattern).at_index(0)]; arity = 1; ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty)); + self.internal_state.has_lowered_deref_pat.set(true); } PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { match ty.kind() { @@ -1027,6 +1039,25 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { ); } } + + fn match_may_contain_deref_pats(&self) -> bool { + self.internal_state.has_lowered_deref_pat.get() + } + + fn report_mixed_deref_pat_ctors( + &self, + deref_pat: &crate::pat::DeconstructedPat<Self>, + normal_pat: &crate::pat::DeconstructedPat<Self>, + ) -> Self::Error { + let deref_pattern_label = deref_pat.data().span; + let normal_constructor_label = normal_pat.data().span; + self.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors { + spans: vec![deref_pattern_label, normal_constructor_label], + smart_pointer_ty: deref_pat.ty().inner(), + deref_pattern_label, + normal_constructor_label, + }) + } } /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. @@ -1055,13 +1086,6 @@ pub fn analyze_match<'p, 'tcx>( ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); - // The analysis doesn't support deref patterns mixed with normal constructors; error if present. - // FIXME(deref_patterns): This only needs to run when a deref pattern was found during lowering. - if tycx.tcx.features().deref_patterns() { - let pat_column = PatternColumn::new(arms); - detect_mixed_deref_pat_ctors(tycx, &pat_column)?; - } - let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee); let report = compute_match_usefulness( tycx, @@ -1080,48 +1104,3 @@ pub fn analyze_match<'p, 'tcx>( Ok(report) } - -// FIXME(deref_patterns): Currently it's the responsibility of the frontend (rustc or rust-analyzer) -// to ensure that deref patterns don't appear in the same column as normal constructors. Deref -// patterns aren't currently implemented in rust-analyzer, but should they be, the columnwise check -// here could be made generic and shared between frontends. -fn detect_mixed_deref_pat_ctors<'p, 'tcx>( - cx: &RustcPatCtxt<'p, 'tcx>, - column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>, -) -> Result<(), ErrorGuaranteed> { - let Some(&ty) = column.head_ty() else { - return Ok(()); - }; - - // Check for a mix of deref patterns and normal constructors. - let mut normal_ctor_span = None; - let mut deref_pat_span = None; - for pat in column.iter() { - match pat.ctor() { - // The analysis can handle mixing deref patterns with wildcards and opaque patterns. - Wildcard | Opaque(_) => {} - DerefPattern(_) => deref_pat_span = Some(pat.data().span), - // Nothing else can be compared to deref patterns in `Constructor::is_covered_by`. - _ => normal_ctor_span = Some(pat.data().span), - } - } - if let Some(normal_constructor_label) = normal_ctor_span - && let Some(deref_pattern_label) = deref_pat_span - { - return Err(cx.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors { - spans: vec![deref_pattern_label, normal_constructor_label], - smart_pointer_ty: ty.inner(), - deref_pattern_label, - normal_constructor_label, - })); - } - - // Specialize and recurse into the patterns' fields. - let set = column.analyze_ctors(cx, &ty)?; - for ctor in set.present { - for specialized_column in column.specialize(cx, &ty, &ctor).iter() { - detect_mixed_deref_pat_ctors(cx, specialized_column)?; - } - } - Ok(()) -} diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index c348cd508f9..b1c646e9884 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -720,7 +720,7 @@ use tracing::{debug, instrument}; use self::PlaceValidity::*; use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; -use crate::{MatchArm, PatCx, PrivateUninhabitedField}; +use crate::{MatchArm, PatCx, PrivateUninhabitedField, checks}; #[cfg(not(feature = "rustc"))] pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R { f() @@ -1836,6 +1836,11 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( scrut_validity: PlaceValidity, complexity_limit: usize, ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> { + // The analysis doesn't support deref patterns mixed with normal constructors; error if present. + if tycx.match_may_contain_deref_pats() { + checks::detect_mixed_deref_pat_ctors(tycx, arms)?; + } + let mut cx = UsefulnessCtxt { tycx, branch_usefulness: FxHashMap::default(), diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 8980b644f59..0b939ef7816 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -1,6 +1,7 @@ use rustc_pattern_analysis::constructor::{ Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, RangeEnd, VariantVisibility, }; +use rustc_pattern_analysis::pat::DeconstructedPat; use rustc_pattern_analysis::usefulness::{PlaceValidity, UsefulnessReport}; use rustc_pattern_analysis::{MatchArm, PatCx, PrivateUninhabitedField}; @@ -184,6 +185,14 @@ impl PatCx for Cx { fn complexity_exceeded(&self) -> Result<(), Self::Error> { Err(()) } + + fn report_mixed_deref_pat_ctors( + &self, + _deref_pat: &DeconstructedPat<Self>, + _normal_pat: &DeconstructedPat<Self>, + ) -> Self::Error { + panic!("`rustc_pattern_analysis::tests` currently doesn't test deref pattern errors") + } } /// Construct a single pattern; see `pats!()`. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index fa04c8bc604..8114021510e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -328,8 +328,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id); let mod_prefix = - mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr()))); - + mod_prefix.map_or_else(String::new, |res| format!("{} ", res.descr())); (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None) }; @@ -1183,15 +1182,23 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { _ => "`self` value is a keyword only available in methods with a `self` parameter", }, ); + + // using `let self` is wrong even if we're not in an associated method or if we're in a macro expansion. + // So, we should return early if we're in a pattern, see issue #143134. + if matches!(source, PathSource::Pat) { + return true; + } + let is_assoc_fn = self.self_type_is_available(); let self_from_macro = "a `self` parameter, but a macro invocation can only \ access identifiers it receives from parameters"; - if let Some((fn_kind, span)) = &self.diag_metadata.current_function { + if let Some((fn_kind, fn_span)) = &self.diag_metadata.current_function { // The current function has a `self` parameter, but we were unable to resolve // a reference to `self`. This can only happen if the `self` identifier we - // are resolving came from a different hygiene context. + // are resolving came from a different hygiene context or a variable binding. + // But variable binding error is returned early above. if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) { - err.span_label(*span, format!("this function has {self_from_macro}")); + err.span_label(*fn_span, format!("this function has {self_from_macro}")); } else { let doesnt = if is_assoc_fn { let (span, sugg) = fn_kind @@ -1204,7 +1211,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // This avoids placing the suggestion into the visibility specifier. let span = fn_kind .ident() - .map_or(*span, |ident| span.with_lo(ident.span.hi())); + .map_or(*fn_span, |ident| fn_span.with_lo(ident.span.hi())); ( self.r .tcx diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4627c2978fc..a91e2140fd4 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -370,12 +370,34 @@ impl LinkSelfContained { } /// To help checking CLI usage while some of the values are unstable: returns whether one of the - /// components was set individually. This would also require the `-Zunstable-options` flag, to - /// be allowed. - fn are_unstable_variants_set(&self) -> bool { - let any_component_set = - !self.enabled_components.is_empty() || !self.disabled_components.is_empty(); - self.explicitly_set.is_none() && any_component_set + /// unstable components was set individually, for the given `TargetTuple`. This would also + /// require the `-Zunstable-options` flag, to be allowed. + fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { + if self.explicitly_set.is_some() { + return Ok(()); + } + + // `-C link-self-contained=-linker` is only stable on x64 linux. + let has_minus_linker = self.disabled_components.is_linker_enabled(); + if has_minus_linker && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { + return Err(format!( + "`-C link-self-contained=-linker` is unstable on the `{target_tuple}` \ + target. The `-Z unstable-options` flag must also be passed to use it on this target", + )); + } + + // Any `+linker` or other component used is unstable, and that's an error. + let unstable_enabled = self.enabled_components; + let unstable_disabled = self.disabled_components - LinkSelfContainedComponents::LINKER; + if !unstable_enabled.union(unstable_disabled).is_empty() { + return Err(String::from( + "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off`/`-linker` \ + are stable, the `-Z unstable-options` flag must also be passed to use \ + the unstable values", + )); + } + + Ok(()) } /// Returns whether the self-contained linker component was enabled on the CLI, using the @@ -402,7 +424,7 @@ impl LinkSelfContained { } } -/// The different values that `-Z linker-features` can take on the CLI: a list of individually +/// The different values that `-C linker-features` can take on the CLI: a list of individually /// enabled or disabled features used during linking. /// /// There is no need to enable or disable them in bulk. Each feature is fine-grained, and can be @@ -442,6 +464,39 @@ impl LinkerFeaturesCli { _ => None, } } + + /// When *not* using `-Z unstable-options` on the CLI, ensure only stable linker features are + /// used, for the given `TargetTuple`. Returns `Ok` if no unstable variants are used. + /// The caller should ensure that e.g. `nightly_options::is_unstable_enabled()` + /// returns false. + pub(crate) fn check_unstable_variants(&self, target_tuple: &TargetTuple) -> Result<(), String> { + // `-C linker-features=-lld` is only stable on x64 linux. + let has_minus_lld = self.disabled.is_lld_enabled(); + if has_minus_lld && target_tuple.tuple() != "x86_64-unknown-linux-gnu" { + return Err(format!( + "`-C linker-features=-lld` is unstable on the `{target_tuple}` \ + target. The `-Z unstable-options` flag must also be passed to use it on this target", + )); + } + + // Any `+lld` or non-lld feature used is unstable, and that's an error. + let unstable_enabled = self.enabled; + let unstable_disabled = self.disabled - LinkerFeatures::LLD; + if !unstable_enabled.union(unstable_disabled).is_empty() { + let unstable_features: Vec<_> = unstable_enabled + .iter() + .map(|f| format!("+{}", f.as_str().unwrap())) + .chain(unstable_disabled.iter().map(|f| format!("-{}", f.as_str().unwrap()))) + .collect(); + return Err(format!( + "`-C linker-features={}` is unstable, and also requires the \ + `-Z unstable-options` flag to be used", + unstable_features.join(","), + )); + } + + Ok(()) + } } /// Used with `-Z assert-incr-state`. @@ -2638,26 +2693,21 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } } - if !nightly_options::is_unstable_enabled(matches) - && cg.force_frame_pointers == FramePointer::NonLeaf - { + let unstable_options_enabled = nightly_options::is_unstable_enabled(matches); + if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf { early_dcx.early_fatal( "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \ and a nightly compiler", ) } - // For testing purposes, until we have more feedback about these options: ensure `-Z - // unstable-options` is required when using the unstable `-C link-self-contained` and `-C - // linker-flavor` options. - if !nightly_options::is_unstable_enabled(matches) { - let uses_unstable_self_contained_option = - cg.link_self_contained.are_unstable_variants_set(); - if uses_unstable_self_contained_option { - early_dcx.early_fatal( - "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \ - the `-Z unstable-options` flag must also be passed to use the unstable values", - ); + let target_triple = parse_target_triple(early_dcx, matches); + + // Ensure `-Z unstable-options` is required when using the unstable `-C link-self-contained` and + // `-C linker-flavor` options. + if !unstable_options_enabled { + if let Err(error) = cg.link_self_contained.check_unstable_variants(&target_triple) { + early_dcx.early_fatal(error); } if let Some(flavor) = cg.linker_flavor { @@ -2697,7 +2747,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let cg = cg; - let target_triple = parse_target_triple(early_dcx, matches); let opt_level = parse_opt_level(early_dcx, matches, &cg); // The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able // to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`) @@ -2706,6 +2755,12 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let debuginfo = select_debuginfo(matches, &cg); let debuginfo_compression = unstable_opts.debuginfo_compression; + if !unstable_options_enabled { + if let Err(error) = cg.linker_features.check_unstable_variants(&target_triple) { + early_dcx.early_fatal(error); + } + } + let crate_name = matches.opt_str("crate-name"); let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); // Parse any `-l` flags, which link to native libraries. diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index ecd82c0cc01..626262c8442 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2015,6 +2015,8 @@ options! { on a C toolchain or linker installed in the system"), linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED], "system linker to link outputs with"), + linker_features: LinkerFeaturesCli = (LinkerFeaturesCli::default(), parse_linker_features, [UNTRACKED], + "a comma-separated list of linker features to enable (+) or disable (-): `lld`"), linker_flavor: Option<LinkerFlavorCli> = (None, parse_linker_flavor, [UNTRACKED], "linker flavor"), linker_plugin_lto: LinkerPluginLto = (LinkerPluginLto::Disabled, @@ -2307,8 +2309,6 @@ options! { "link native libraries in the linker invocation (default: yes)"), link_only: bool = (false, parse_bool, [TRACKED], "link the `.rlink` file generated by `-Z no-link` (default: no)"), - linker_features: LinkerFeaturesCli = (LinkerFeaturesCli::default(), parse_linker_features, [UNTRACKED], - "a comma-separated list of linker features to enable (+) or disable (-): `lld`"), lint_llvm_ir: bool = (false, parse_bool, [TRACKED], "lint LLVM IR (default: no)"), lint_mir: bool = (false, parse_bool, [UNTRACKED], diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 09f01d8704e..4df91cc3429 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2194,6 +2194,7 @@ symbols! { type_changing_struct_update, type_const, type_id, + type_id_eq, type_ir, type_ir_infer_ctxt_like, type_ir_inherent, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 5346b206a17..4bc0d88a910 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -725,7 +725,7 @@ impl ToJson for LinkSelfContainedComponents { } bitflags::bitflags! { - /// The `-Z linker-features` components that can individually be enabled or disabled. + /// The `-C linker-features` components that can individually be enabled or disabled. /// /// They are feature flags intended to be a more flexible mechanism than linker flavors, and /// also to prevent a combinatorial explosion of flavors whenever a new linker feature is @@ -756,7 +756,7 @@ bitflags::bitflags! { rustc_data_structures::external_bitflags_debug! { LinkerFeatures } impl LinkerFeatures { - /// Parses a single `-Z linker-features` well-known feature, not a set of flags. + /// Parses a single `-C linker-features` well-known feature, not a set of flags. pub fn from_str(s: &str) -> Option<LinkerFeatures> { Some(match s { "cc" => LinkerFeatures::CC, @@ -765,6 +765,17 @@ impl LinkerFeatures { }) } + /// Return the linker feature name, as would be passed on the CLI. + /// + /// Returns `None` if the bitflags aren't a singular component (but a mix of multiple flags). + pub fn as_str(self) -> Option<&'static str> { + Some(match self { + LinkerFeatures::CC => "cc", + LinkerFeatures::LLD => "lld", + _ => return None, + }) + } + /// Returns whether the `lld` linker feature is enabled. pub fn is_lld_enabled(self) -> bool { self.contains(LinkerFeatures::LLD) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 39f115ce0cd..a8ba1baf6b9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -4,7 +4,7 @@ use rustc_errors::{Applicability, Diag, E0283, E0284, E0790, MultiSpan, struct_s use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::intravisit::Visitor as _; use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; @@ -128,19 +128,26 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>( }, ); - let predicates = - tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx); - for (pred, span) in elaborate(tcx, predicates.into_iter()) { - let kind = pred.kind(); - if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder() - && param_env_candidate_may_apply(kind.rebind(trait_pred)) - { - if kind.rebind(trait_pred.trait_ref) - == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) + // If our `body_id` has been set (and isn't just from a dummy obligation cause), + // then try to look for a param-env clause that would apply. The way we compute + // this is somewhat manual, since we need the spans, so we elaborate this directly + // from `predicates_of` rather than actually looking at the param-env which + // otherwise would be more appropriate. + let body_id = obligation.cause.body_id; + if body_id != CRATE_DEF_ID { + let predicates = tcx.predicates_of(body_id.to_def_id()).instantiate_identity(tcx); + for (pred, span) in elaborate(tcx, predicates.into_iter()) { + let kind = pred.kind(); + if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder() + && param_env_candidate_may_apply(kind.rebind(trait_pred)) { - ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id()))) - } else { - ambiguities.push(CandidateSource::ParamEnv(span)) + if kind.rebind(trait_pred.trait_ref) + == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) + { + ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id()))) + } else { + ambiguities.push(CandidateSource::ParamEnv(span)) + } } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index e801ec358fa..3e64573aa03 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1581,12 +1581,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { 'outer: loop { while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind { count += 1; - let span = if expr.span.eq_ctxt(borrowed.span) { - expr.span.until(borrowed.span) - } else { - expr.span.with_hi(expr.span.lo() + BytePos(1)) - }; + let span = + if let Some(borrowed_span) = borrowed.span.find_ancestor_inside(expr.span) { + expr.span.until(borrowed_span) + } else { + break 'outer; + }; + // Double check that the span we extracted actually corresponds to a borrow, + // rather than some macro garbage. match self.tcx.sess.source_map().span_to_snippet(span) { Ok(snippet) if snippet.starts_with("&") => {} _ => break 'outer, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 9af85f5e3f0..cc188a280aa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -19,7 +19,7 @@ use rustc_middle::{bug, span_bug}; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; -use super::{BuiltinImplConditions, SelectionCandidateSet, SelectionContext, TraitObligationStack}; +use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; use crate::traits::util; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { @@ -75,8 +75,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_candidates_from_impls(obligation, &mut candidates); // For other types, we'll use the builtin rules. - let copy_conditions = self.copy_clone_conditions(obligation); - self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); + self.assemble_builtin_copy_clone_candidate( + obligation.predicate.self_ty().skip_binder(), + &mut candidates, + ); } Some(LangItem::DiscriminantKind) => { // `DiscriminantKind` is automatically implemented for every type. @@ -88,14 +90,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } Some(LangItem::Sized) => { self.assemble_builtin_sized_candidate( - obligation, + obligation.predicate.self_ty().skip_binder(), &mut candidates, SizedTraitKind::Sized, ); } Some(LangItem::MetaSized) => { self.assemble_builtin_sized_candidate( - obligation, + obligation.predicate.self_ty().skip_binder(), &mut candidates, SizedTraitKind::MetaSized, ); @@ -357,14 +359,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let self_ty = obligation.self_ty().skip_binder(); - // gen constructs get lowered to a special kind of coroutine that - // should directly `impl FusedIterator`. - if let ty::Coroutine(did, ..) = self_ty.kind() - && self.tcx().coroutine_is_gen(*did) - { - debug!(?self_ty, ?obligation, "assemble_fused_iterator_candidates",); - + if self.coroutine_is_gen(obligation.self_ty().skip_binder()) { candidates.vec.push(BuiltinCandidate); } } @@ -1113,41 +1108,164 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself. + /// Assembles `Copy` and `Clone` candidates for built-in types with no libcore-defined + /// `Copy` or `Clone` impls. #[instrument(level = "debug", skip(self, candidates))] - fn assemble_builtin_sized_candidate( + fn assemble_builtin_copy_clone_candidate( &mut self, - obligation: &PolyTraitObligation<'tcx>, + self_ty: Ty<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, - sizedness: SizedTraitKind, ) { - match self.sizedness_conditions(obligation, sizedness) { - BuiltinImplConditions::Where(_nested) => { - candidates.vec.push(SizedCandidate); + match *self_ty.kind() { + // These impls are built-in because we cannot express sufficiently + // generic impls in libcore. + ty::FnDef(..) + | ty::FnPtr(..) + | ty::Error(_) + | ty::Tuple(..) + | ty::CoroutineWitness(..) + | ty::Pat(..) => { + candidates.vec.push(BuiltinCandidate); + } + + // Implementations provided in libcore. + ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => {} + + // FIXME(unsafe_binder): Should we conditionally + // (i.e. universally) implement copy/clone? + ty::UnsafeBinder(_) => {} + + // Not `Sized`, which is a supertrait of `Copy`/`Clone`. + ty::Dynamic(..) | ty::Str | ty::Slice(..) | ty::Foreign(..) => {} + + // Not `Copy` or `Clone` by design. + ty::Ref(_, _, hir::Mutability::Mut) => {} + + ty::Coroutine(coroutine_def_id, args) => { + match self.tcx().coroutine_movability(coroutine_def_id) { + hir::Movability::Static => {} + hir::Movability::Movable => { + if self.tcx().features().coroutine_clone() { + let resolved_upvars = + self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); + let resolved_witness = + self.infcx.shallow_resolve(args.as_coroutine().witness()); + if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() { + // Not yet resolved. + candidates.ambiguous = true; + } else { + candidates.vec.push(BuiltinCandidate); + } + } + } + } } - BuiltinImplConditions::None => {} - BuiltinImplConditions::Ambiguous => { + + ty::Closure(_, args) => { + let resolved_upvars = + self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); + if resolved_upvars.is_ty_var() { + // Not yet resolved. + candidates.ambiguous = true; + } else { + candidates.vec.push(BuiltinCandidate); + } + } + + ty::CoroutineClosure(_, args) => { + let resolved_upvars = + self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); + if resolved_upvars.is_ty_var() { + // Not yet resolved. + candidates.ambiguous = true; + } else { + candidates.vec.push(BuiltinCandidate); + } + } + + // Fallback to whatever user-defined impls or param-env clauses exist in this case. + ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {} + + ty::Infer(ty::TyVar(_)) => { candidates.ambiguous = true; } + + // Only appears when assembling higher-ranked `for<T> T: Clone`. + ty::Bound(..) => {} + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } } } - /// Assembles the trait which are built-in to the language itself: - /// e.g. `Copy` and `Clone`. + /// Assembles the `Sized` and `MetaSized` traits which are built-in to the language itself. #[instrument(level = "debug", skip(self, candidates))] - fn assemble_builtin_bound_candidates( + fn assemble_builtin_sized_candidate( &mut self, - conditions: BuiltinImplConditions<'tcx>, + self_ty: Ty<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, + sizedness: SizedTraitKind, ) { - match conditions { - BuiltinImplConditions::Where(_) => { - candidates.vec.push(BuiltinCandidate); + match *self_ty.kind() { + // Always sized. + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Never + | ty::Error(_) => { + candidates.vec.push(SizedCandidate); + } + + // Conditionally `Sized`. + ty::Tuple(..) | ty::Pat(..) | ty::Adt(..) | ty::UnsafeBinder(_) => { + candidates.vec.push(SizedCandidate); } - BuiltinImplConditions::None => {} - BuiltinImplConditions::Ambiguous => { + + // `MetaSized` but not `Sized`. + ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { + SizedTraitKind::Sized => {} + SizedTraitKind::MetaSized => { + candidates.vec.push(SizedCandidate); + } + }, + + // Not `MetaSized` or `Sized`. + ty::Foreign(..) => {} + + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => {} + + ty::Infer(ty::TyVar(_)) => { candidates.ambiguous = true; } + + // Only appears when assembling higher-ranked `for<T> T: Sized`. + ty::Bound(..) => {} + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 545531f927a..ee8cef20279 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -21,7 +21,7 @@ use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; -use super::{BuiltinImplConditions, PredicateObligations, SelectionContext}; +use super::{PredicateObligations, SelectionContext}; use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type}; use crate::traits::{ @@ -257,16 +257,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, "confirm_builtin_candidate"); let tcx = self.tcx(); let trait_def = obligation.predicate.def_id(); - let conditions = match tcx.as_lang_item(trait_def) { - Some(LangItem::Sized) => self.sizedness_conditions(obligation, SizedTraitKind::Sized), + let self_ty = self.infcx.shallow_resolve( + self.infcx.enter_forall_and_leak_universe(obligation.predicate.self_ty()), + ); + let types = match tcx.as_lang_item(trait_def) { + Some(LangItem::Sized) => self.sizedness_conditions(self_ty, SizedTraitKind::Sized), Some(LangItem::MetaSized) => { - self.sizedness_conditions(obligation, SizedTraitKind::MetaSized) + self.sizedness_conditions(self_ty, SizedTraitKind::MetaSized) } Some(LangItem::PointeeSized) => { bug!("`PointeeSized` is removing during lowering"); } - Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(obligation), - Some(LangItem::FusedIterator) => self.fused_iterator_conditions(obligation), + Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(self_ty), + Some(LangItem::FusedIterator) => { + if self.coroutine_is_gen(self_ty) { + ty::Binder::dummy(vec![]) + } else { + unreachable!("tried to assemble `FusedIterator` for non-gen coroutine"); + } + } Some( LangItem::Destruct | LangItem::DiscriminantKind @@ -274,12 +283,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | LangItem::PointeeTrait | LangItem::Tuple | LangItem::Unpin, - ) => BuiltinImplConditions::Where(ty::Binder::dummy(vec![])), + ) => ty::Binder::dummy(vec![]), other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"), }; - let BuiltinImplConditions::Where(types) = conditions else { - bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation); - }; let types = self.infcx.enter_forall_and_leak_universe(types); let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived); @@ -403,6 +409,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty)); + let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty); let types = self.constituent_types_for_ty(self_ty)?; let types = self.infcx.enter_forall_and_leak_universe(types); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c9930c69b32..2e65750db25 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -188,18 +188,6 @@ struct EvaluatedCandidate<'tcx> { evaluation: EvaluationResult, } -/// When does the builtin impl for `T: Trait` apply? -#[derive(Debug)] -enum BuiltinImplConditions<'tcx> { - /// The impl is conditional on `T1, T2, ...: Trait`. - Where(ty::Binder<'tcx, Vec<Ty<'tcx>>>), - /// There is no built-in impl. There may be some other - /// candidate (a where-clause or user-defined impl). - None, - /// It is unknown whether there is an impl. - Ambiguous, -} - impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { @@ -2104,14 +2092,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { impl<'tcx> SelectionContext<'_, 'tcx> { fn sizedness_conditions( &mut self, - obligation: &PolyTraitObligation<'tcx>, + self_ty: Ty<'tcx>, sizedness: SizedTraitKind, - ) -> BuiltinImplConditions<'tcx> { - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - + ) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> { match self_ty.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) @@ -2129,59 +2112,44 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Never - | ty::Error(_) => { - // safe for everything - Where(ty::Binder::dummy(Vec::new())) - } + | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Str | ty::Slice(_) | ty::Dynamic(..) => match sizedness { - SizedTraitKind::Sized => None, - SizedTraitKind::MetaSized => Where(ty::Binder::dummy(Vec::new())), + SizedTraitKind::Sized => unreachable!("tried to assemble `Sized` for unsized type"), + SizedTraitKind::MetaSized => ty::Binder::dummy(vec![]), }, - ty::Foreign(..) => None, + ty::Foreign(..) => unreachable!("tried to assemble `Sized` for unsized type"), - ty::Tuple(tys) => Where( - obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])), - ), + ty::Tuple(tys) => { + ty::Binder::dummy(tys.last().map_or_else(Vec::new, |&last| vec![last])) + } - ty::Pat(ty, _) => Where(obligation.predicate.rebind(vec![*ty])), + ty::Pat(ty, _) => ty::Binder::dummy(vec![*ty]), ty::Adt(def, args) => { if let Some(crit) = def.sizedness_constraint(self.tcx(), sizedness) { - // (*) binder moved here - Where(obligation.predicate.rebind(vec![crit.instantiate(self.tcx(), args)])) + ty::Binder::dummy(vec![crit.instantiate(self.tcx(), args)]) } else { - Where(ty::Binder::dummy(Vec::new())) + ty::Binder::dummy(vec![]) } } - // FIXME(unsafe_binders): This binder needs to be squashed - ty::UnsafeBinder(binder_ty) => Where(binder_ty.map_bound(|ty| vec![ty])), - - ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None, - ty::Infer(ty::TyVar(_)) => Ambiguous, - - // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. - ty::Bound(..) => None, + ty::UnsafeBinder(binder_ty) => binder_ty.map_bound(|ty| vec![ty]), - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => { + bug!("asked to assemble `Sized` of unexpected type: {:?}", self_ty); } } } - fn copy_clone_conditions( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - // NOTE: binder moved to (*) - let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); - - use self::BuiltinImplConditions::{Ambiguous, None, Where}; - + fn copy_clone_conditions(&mut self, self_ty: Ty<'tcx>) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> { match *self_ty.kind() { - ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => ty::Binder::dummy(vec![]), ty::Uint(_) | ty::Int(_) @@ -2193,127 +2161,78 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Never | ty::Ref(_, _, hir::Mutability::Not) | ty::Array(..) => { - // Implementations provided in libcore - None + unreachable!("tried to assemble `Sized` for type with libcore-provided impl") } // FIXME(unsafe_binder): Should we conditionally // (i.e. universally) implement copy/clone? - ty::UnsafeBinder(_) => None, - - ty::Dynamic(..) - | ty::Str - | ty::Slice(..) - | ty::Foreign(..) - | ty::Ref(_, _, hir::Mutability::Mut) => None, + ty::UnsafeBinder(_) => unreachable!("tried to assemble `Sized` for unsafe binder"), ty::Tuple(tys) => { // (*) binder moved here - Where(obligation.predicate.rebind(tys.iter().collect())) + ty::Binder::dummy(tys.iter().collect()) } ty::Pat(ty, _) => { // (*) binder moved here - Where(obligation.predicate.rebind(vec![ty])) + ty::Binder::dummy(vec![ty]) } ty::Coroutine(coroutine_def_id, args) => { match self.tcx().coroutine_movability(coroutine_def_id) { - hir::Movability::Static => None, + hir::Movability::Static => { + unreachable!("tried to assemble `Sized` for static coroutine") + } hir::Movability::Movable => { if self.tcx().features().coroutine_clone() { - let resolved_upvars = - self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); - let resolved_witness = - self.infcx.shallow_resolve(args.as_coroutine().witness()); - if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() { - // Not yet resolved. - Ambiguous - } else { - let all = args - .as_coroutine() + ty::Binder::dummy( + args.as_coroutine() .upvar_tys() .iter() .chain([args.as_coroutine().witness()]) - .collect::<Vec<_>>(); - Where(obligation.predicate.rebind(all)) - } + .collect::<Vec<_>>(), + ) } else { - None + unreachable!( + "tried to assemble `Sized` for coroutine without enabled feature" + ) } } } } - ty::CoroutineWitness(def_id, args) => { - let hidden_types = rebind_coroutine_witness_types( - self.infcx.tcx, - def_id, - args, - obligation.predicate.bound_vars(), - ); - Where(hidden_types) - } + ty::CoroutineWitness(def_id, args) => self + .infcx + .tcx + .coroutine_hidden_types(def_id) + .instantiate(self.infcx.tcx, args) + .map_bound(|witness| witness.types.to_vec()), - ty::Closure(_, args) => { - // (*) binder moved here - let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); - if let ty::Infer(ty::TyVar(_)) = ty.kind() { - // Not yet resolved. - Ambiguous - } else { - Where(obligation.predicate.rebind(args.as_closure().upvar_tys().to_vec())) - } - } + ty::Closure(_, args) => ty::Binder::dummy(args.as_closure().upvar_tys().to_vec()), ty::CoroutineClosure(_, args) => { - // (*) binder moved here - let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); - if let ty::Infer(ty::TyVar(_)) = ty.kind() { - // Not yet resolved. - Ambiguous - } else { - Where( - obligation - .predicate - .rebind(args.as_coroutine_closure().upvar_tys().to_vec()), - ) - } + ty::Binder::dummy(args.as_coroutine_closure().upvar_tys().to_vec()) } - ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => { - // Fallback to whatever user-defined impls exist in this case. - None - } - - ty::Infer(ty::TyVar(_)) => { - // Unbound type variable. Might or might not have - // applicable impls and so forth, depending on what - // those type variables wind up being bound to. - Ambiguous - } - - // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. - ty::Bound(..) => None, - - ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + ty::Foreign(..) + | ty::Str + | ty::Slice(_) + | ty::Dynamic(..) + | ty::Adt(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Ref(_, _, ty::Mutability::Mut) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); } } } - fn fused_iterator_conditions( - &mut self, - obligation: &PolyTraitObligation<'tcx>, - ) -> BuiltinImplConditions<'tcx> { - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - if let ty::Coroutine(did, ..) = *self_ty.kind() - && self.tcx().coroutine_is_gen(did) - { - BuiltinImplConditions::Where(ty::Binder::dummy(Vec::new())) - } else { - BuiltinImplConditions::None - } + fn coroutine_is_gen(&mut self, self_ty: Ty<'tcx>) -> bool { + matches!(*self_ty.kind(), ty::Coroutine(did, ..) + if self.tcx().coroutine_is_gen(did)) } /// For default impls, we need to break apart a type into its @@ -2330,9 +2249,9 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn constituent_types_for_ty( &self, - t: ty::Binder<'tcx, Ty<'tcx>>, + t: Ty<'tcx>, ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> { - Ok(match *t.skip_binder().kind() { + Ok(match *t.kind() { ty::Uint(_) | ty::Int(_) | ty::Bool @@ -2349,8 +2268,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // `assemble_candidates_from_auto_impls`. ty::Foreign(..) => ty::Binder::dummy(Vec::new()), - // FIXME(unsafe_binders): Squash the double binder for now, I guess. - ty::UnsafeBinder(_) => return Err(SelectionError::Unimplemented), + ty::UnsafeBinder(ty) => ty.map_bound(|ty| vec![ty]), // Treat this like `struct str([u8]);` ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]), @@ -2364,40 +2282,47 @@ impl<'tcx> SelectionContext<'_, 'tcx> { bug!("asked to assemble constituent types of unexpected type: {:?}", t); } - ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => t.rebind(vec![element_ty]), + ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => { + ty::Binder::dummy(vec![element_ty]) + } - ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => t.rebind(vec![ty]), + ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => ty::Binder::dummy(vec![ty]), ty::Tuple(tys) => { // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet - t.rebind(tys.iter().collect()) + ty::Binder::dummy(tys.iter().collect()) } ty::Closure(_, args) => { let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); - t.rebind(vec![ty]) + ty::Binder::dummy(vec![ty]) } ty::CoroutineClosure(_, args) => { let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); - t.rebind(vec![ty]) + ty::Binder::dummy(vec![ty]) } ty::Coroutine(_, args) => { let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); let witness = args.as_coroutine().witness(); - t.rebind([ty].into_iter().chain(iter::once(witness)).collect()) + ty::Binder::dummy([ty].into_iter().chain(iter::once(witness)).collect()) } - ty::CoroutineWitness(def_id, args) => { - rebind_coroutine_witness_types(self.infcx.tcx, def_id, args, t.bound_vars()) - } + ty::CoroutineWitness(def_id, args) => self + .infcx + .tcx + .coroutine_hidden_types(def_id) + .instantiate(self.infcx.tcx, args) + .map_bound(|witness| witness.types.to_vec()), // For `PhantomData<T>`, we pass `T`. - ty::Adt(def, args) if def.is_phantom_data() => t.rebind(args.types().collect()), + ty::Adt(def, args) if def.is_phantom_data() => { + ty::Binder::dummy(args.types().collect()) + } ty::Adt(def, args) => { - t.rebind(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect()) + ty::Binder::dummy(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect()) } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { @@ -2408,7 +2333,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // which enforces a DAG between the functions requiring // the auto trait bounds in question. match self.tcx().type_of_opaque(def_id) { - Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]), + Ok(ty) => ty::Binder::dummy(vec![ty.instantiate(self.tcx(), args)]), Err(_) => { return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id)); } @@ -2880,23 +2805,6 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } -fn rebind_coroutine_witness_types<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - args: ty::GenericArgsRef<'tcx>, - bound_vars: &'tcx ty::List<ty::BoundVariableKind>, -) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> { - let bound_coroutine_types = tcx.coroutine_hidden_types(def_id).skip_binder(); - let shifted_coroutine_types = - tcx.shift_bound_var_indices(bound_vars.len(), bound_coroutine_types.skip_binder()); - ty::Binder::bind_with_vars( - ty::EarlyBinder::bind(shifted_coroutine_types.types.to_vec()).instantiate(tcx, args), - tcx.mk_bound_variable_kinds_from_iter( - bound_vars.iter().chain(bound_coroutine_types.bound_vars()), - ), - ) -} - impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { TraitObligationStackList::with(self) diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs index 0d45e59885c..9a94551f3ec 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/stable_mir/src/mir/alloc.rs @@ -23,6 +23,9 @@ pub enum GlobalAlloc { Static(StaticDef), /// The alloc ID points to memory. Memory(Allocation), + /// The first pointer-sized segment of a type id. On 64 bit systems, the 128 bit type id + /// is split into two segments, on 32 bit systems there are 4 segments, and so on. + TypeId { ty: Ty }, } impl From<AllocId> for GlobalAlloc { diff --git a/compiler/stable_mir/src/unstable/convert/stable/mir.rs b/compiler/stable_mir/src/unstable/convert/stable/mir.rs index f6f4706e40b..ad39fc37600 100644 --- a/compiler/stable_mir/src/unstable/convert/stable/mir.rs +++ b/compiler/stable_mir/src/unstable/convert/stable/mir.rs @@ -864,6 +864,9 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> { mir::interpret::GlobalAlloc::Memory(alloc) => { GlobalAlloc::Memory(alloc.stable(tables, cx)) } + mir::interpret::GlobalAlloc::TypeId { ty } => { + GlobalAlloc::TypeId { ty: ty.stable(tables, cx) } + } } } } |
