diff options
Diffstat (limited to 'compiler')
452 files changed, 9191 insertions, 6872 deletions
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index e3214d1ab9c..9ef8fa75062 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -30,6 +30,7 @@ features = ['unprefixed_malloc_on_supported_platforms'] check_only = ['rustc_driver_impl/check_only'] jemalloc = ['dep:tikv-jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] +llvm_enzyme = ['rustc_driver_impl/llvm_enzyme'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] # tidy-alphabetical-end diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 8cf140dbaad..66c8056d0c2 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -42,22 +42,22 @@ impl Reg { let dl = cx.data_layout(); match self.kind { RegKind::Integer => match self.size.bits() { - 1 => dl.i1_align.abi, - 2..=8 => dl.i8_align.abi, - 9..=16 => dl.i16_align.abi, - 17..=32 => dl.i32_align.abi, - 33..=64 => dl.i64_align.abi, - 65..=128 => dl.i128_align.abi, + 1 => dl.i1_align, + 2..=8 => dl.i8_align, + 9..=16 => dl.i16_align, + 17..=32 => dl.i32_align, + 33..=64 => dl.i64_align, + 65..=128 => dl.i128_align, _ => panic!("unsupported integer: {self:?}"), }, RegKind::Float => match self.size.bits() { - 16 => dl.f16_align.abi, - 32 => dl.f32_align.abi, - 64 => dl.f64_align.abi, - 128 => dl.f128_align.abi, + 16 => dl.f16_align, + 32 => dl.f32_align, + 64 => dl.f64_align, + 128 => dl.f128_align, _ => panic!("unsupported float: {self:?}"), }, - RegKind::Vector => dl.llvmlike_vector_align(self.size).abi, + RegKind::Vector => dl.llvmlike_vector_align(self.size), } } } diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 5004d0c8022..14356813b7b 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -174,11 +174,11 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { // Non-power-of-two vectors have padding up to the next power-of-two. // If we're a packed repr, remove the padding while keeping the alignment as close // to a vector as possible. - (BackendRepr::Memory { sized: true }, AbiAlign { abi: Align::max_aligned_factor(size) }) + (BackendRepr::Memory { sized: true }, Align::max_aligned_factor(size)) } else { (BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size)) }; - let size = size.align_to(align.abi); + let size = size.align_to(align); Ok(LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, @@ -190,7 +190,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { largest_niche: elt.largest_niche, uninhabited: false, size, - align, + align: AbiAlign::new(align), max_repr_align: None, unadjusted_abi_align: elt.align.abi, randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)), @@ -388,7 +388,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { return Err(LayoutCalculatorError::UnexpectedUnsized(*field)); } - align = align.max(field.align); + align = align.max(field.align.abi); max_repr_align = max_repr_align.max(field.max_repr_align); size = cmp::max(size, field.size); @@ -423,13 +423,13 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { } if let Some(pack) = repr.pack { - align = align.min(AbiAlign::new(pack)); + align = align.min(pack); } // The unadjusted ABI alignment does not include repr(align), but does include repr(pack). // See documentation on `LayoutData::unadjusted_abi_align`. - let unadjusted_abi_align = align.abi; + let unadjusted_abi_align = align; if let Some(repr_align) = repr.align { - align = align.max(AbiAlign::new(repr_align)); + align = align.max(repr_align); } // `align` must not be modified after this, or `unadjusted_abi_align` could be inaccurate. let align = align; @@ -441,14 +441,12 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { Ok(Some((repr, _))) => match repr { // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) - if repr.scalar_align(dl).unwrap() != align.abi => + if repr.scalar_align(dl).unwrap() != align => { BackendRepr::Memory { sized: true } } // Vectors require at least element alignment, else disable the opt - BackendRepr::SimdVector { element, count: _ } - if element.align(dl).abi > align.abi => - { + BackendRepr::SimdVector { element, count: _ } if element.align(dl).abi > align => { BackendRepr::Memory { sized: true } } // the alignment tests passed and we can use this @@ -474,8 +472,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { backend_repr, largest_niche: None, uninhabited: false, - align, - size: size.align_to(align.abi), + align: AbiAlign::new(align), + size: size.align_to(align), max_repr_align, unadjusted_abi_align, randomization_seed: combined_seed, @@ -611,7 +609,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let mut align = dl.aggregate_align; let mut max_repr_align = repr.align; - let mut unadjusted_abi_align = align.abi; + let mut unadjusted_abi_align = align; let mut variant_layouts = variants .iter_enumerated() @@ -619,7 +617,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let mut st = self.univariant(v, repr, StructKind::AlwaysSized).ok()?; st.variants = Variants::Single { index: j }; - align = align.max(st.align); + align = align.max(st.align.abi); max_repr_align = max_repr_align.max(st.max_repr_align); unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align); @@ -646,7 +644,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let (niche_start, niche_scalar) = niche.reserve(dl, count)?; let niche_offset = niche.offset; let niche_size = niche.value.size(dl); - let size = variant_layouts[largest_variant_index].size.align_to(align.abi); + let size = variant_layouts[largest_variant_index].size.align_to(align); let all_variants_fit = variant_layouts.iter_enumerated_mut().all(|(i, layout)| { if i == largest_variant_index { @@ -699,7 +697,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { .iter_enumerated() .all(|(i, layout)| i == largest_variant_index || layout.size == Size::ZERO); let same_size = size == variant_layouts[largest_variant_index].size; - let same_align = align == variant_layouts[largest_variant_index].align; + let same_align = align == variant_layouts[largest_variant_index].align.abi; let uninhabited = variant_layouts.iter().all(|v| v.is_uninhabited()); let abi = if same_size && same_align && others_zst { @@ -746,7 +744,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { largest_niche, uninhabited, size, - align, + align: AbiAlign::new(align), max_repr_align, unadjusted_abi_align, randomization_seed: combined_seed, @@ -818,7 +816,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let mut align = dl.aggregate_align; let mut max_repr_align = repr.align; - let mut unadjusted_abi_align = align.abi; + let mut unadjusted_abi_align = align; let mut size = Size::ZERO; @@ -860,7 +858,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { } } size = cmp::max(size, st.size); - align = align.max(st.align); + align = align.max(st.align.abi); max_repr_align = max_repr_align.max(st.max_repr_align); unadjusted_abi_align = unadjusted_abi_align.max(st.unadjusted_abi_align); Ok(st) @@ -868,7 +866,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { .collect::<Result<IndexVec<VariantIdx, _>, _>>()?; // Align the maximum variant size to the largest alignment. - size = size.align_to(align.abi); + size = size.align_to(align); // FIXME(oli-obk): deduplicate and harden these checks if size.bytes() >= dl.obj_size_bound() { @@ -1042,7 +1040,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { }; if pair_offsets[FieldIdx::new(0)] == Size::ZERO && pair_offsets[FieldIdx::new(1)] == *offset - && align == pair.align + && align == pair.align.abi && size == pair.size { // We can use `ScalarPair` only when it matches our @@ -1066,7 +1064,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { // Also need to bump up the size and alignment, so that the entire value fits // in here. variant.size = cmp::max(variant.size, size); - variant.align.abi = cmp::max(variant.align.abi, align.abi); + variant.align.abi = cmp::max(variant.align.abi, align); } } } @@ -1092,7 +1090,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { largest_niche, uninhabited, backend_repr: abi, - align, + align: AbiAlign::new(align), size, max_repr_align, unadjusted_abi_align, @@ -1169,7 +1167,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { // To allow unsizing `&Foo<Type>` -> `&Foo<dyn Trait>`, the layout of the struct must // not depend on the layout of the tail. let max_field_align = - fields_excluding_tail.iter().map(|f| f.align.abi.bytes()).max().unwrap_or(1); + fields_excluding_tail.iter().map(|f| f.align.bytes()).max().unwrap_or(1); let largest_niche_size = fields_excluding_tail .iter() .filter_map(|f| f.largest_niche) @@ -1189,7 +1187,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { } else { // Returns `log2(effective-align)`. The calculation assumes that size is an // integer multiple of align, except for ZSTs. - let align = layout.align.abi.bytes(); + let align = layout.align.bytes(); let size = layout.size.bytes(); let niche_size = layout.largest_niche.map(|n| n.available(dl)).unwrap_or(0); // Group [u8; 4] with align-4 or [u8; 6] with align-2 fields. @@ -1288,7 +1286,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { if let StructKind::Prefixed(prefix_size, prefix_align) = kind { let prefix_align = if let Some(pack) = pack { prefix_align.min(pack) } else { prefix_align }; - align = align.max(AbiAlign::new(prefix_align)); + align = align.max(prefix_align); offset = prefix_size.align_to(prefix_align); } for &i in &inverse_memory_index { @@ -1312,7 +1310,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { field.align }; offset = offset.align_to(field_align.abi); - align = align.max(field_align); + align = align.max(field_align.abi); max_repr_align = max_repr_align.max(field.max_repr_align); debug!("univariant offset: {:?} field: {:#?}", offset, field); @@ -1339,9 +1337,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { // The unadjusted ABI alignment does not include repr(align), but does include repr(pack). // See documentation on `LayoutData::unadjusted_abi_align`. - let unadjusted_abi_align = align.abi; + let unadjusted_abi_align = align; if let Some(repr_align) = repr.align { - align = align.max(AbiAlign::new(repr_align)); + align = align.max(repr_align); } // `align` must not be modified after this point, or `unadjusted_abi_align` could be inaccurate. let align = align; @@ -1360,7 +1358,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { debug_assert!(inverse_memory_index.iter().copied().eq(fields.indices())); inverse_memory_index.into_iter().map(|it| it.index() as u32).collect() }; - let size = min_size.align_to(align.abi); + let size = min_size.align_to(align); // FIXME(oli-obk): deduplicate and harden these checks if size.bytes() >= dl.obj_size_bound() { return Err(LayoutCalculatorError::SizeOverflow); @@ -1383,8 +1381,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { layout_of_single_non_zst_field = Some(field); // Field fills the struct and it has a scalar or scalar pair ABI. - if offsets[i].bytes() == 0 && align.abi == field.align.abi && size == field.size - { + if offsets[i].bytes() == 0 && align == field.align.abi && size == field.size { match field.backend_repr { // For plain scalars, or vectors of them, we can't unpack // newtypes for `#[repr(C)]`, as that affects C ABIs. @@ -1428,7 +1425,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { }; if offsets[i] == pair_offsets[FieldIdx::new(0)] && offsets[j] == pair_offsets[FieldIdx::new(1)] - && align == pair.align + && align == pair.align.abi && size == pair.size { // We can use `ScalarPair` only when it matches our @@ -1450,7 +1447,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { Some(l) => l.unadjusted_abi_align, None => { // `repr(transparent)` with all ZST fields. - align.abi + align } } } else { @@ -1465,7 +1462,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { backend_repr: abi, largest_niche, uninhabited, - align, + align: AbiAlign::new(align), size, max_repr_align, unadjusted_abi_align, @@ -1488,7 +1485,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { for i in layout.fields.index_by_increasing_offset() { let offset = layout.fields.offset(i); let f = &fields[FieldIdx::new(i)]; - write!(s, "[o{}a{}s{}", offset.bytes(), f.align.abi.bytes(), f.size.bytes()).unwrap(); + write!(s, "[o{}a{}s{}", offset.bytes(), f.align.bytes(), f.size.bytes()).unwrap(); if let Some(n) = f.largest_niche { write!( s, diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs index 0d0706defc2..b3807c87273 100644 --- a/compiler/rustc_abi/src/layout/simple.rs +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -4,7 +4,8 @@ use rustc_hashes::Hash64; use rustc_index::{Idx, IndexVec}; use crate::{ - BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, Variants, + AbiAlign, BackendRepr, FieldsShape, HasDataLayout, LayoutData, Niche, Primitive, Scalar, Size, + Variants, }; /// "Simple" layout constructors that cannot fail. @@ -20,10 +21,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { backend_repr: BackendRepr::Memory { sized }, largest_niche: None, uninhabited: false, - align: dl.i8_align, + align: AbiAlign::new(dl.i8_align), size: Size::ZERO, max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, + unadjusted_abi_align: dl.i8_align, randomization_seed: Hash64::new(0), } } @@ -37,10 +38,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { backend_repr: BackendRepr::Memory { sized: true }, largest_niche: None, uninhabited: true, - align: dl.i8_align, + align: AbiAlign::new(dl.i8_align), size: Size::ZERO, max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, + unadjusted_abi_align: dl.i8_align, randomization_seed: Hash64::ZERO, } } @@ -89,10 +90,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { pub fn scalar_pair<C: HasDataLayout>(cx: &C, a: Scalar, b: Scalar) -> Self { let dl = cx.data_layout(); - let b_align = b.align(dl); - let align = a.align(dl).max(b_align).max(dl.aggregate_align); - let b_offset = a.size(dl).align_to(b_align.abi); - let size = (b_offset + b.size(dl)).align_to(align.abi); + let b_align = b.align(dl).abi; + let align = a.align(dl).abi.max(b_align).max(dl.aggregate_align); + let b_offset = a.size(dl).align_to(b_align); + let size = (b_offset + b.size(dl)).align_to(align); // HACK(nox): We iter on `b` and then `a` because `max_by_key` // returns the last maximum. @@ -112,10 +113,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { backend_repr: BackendRepr::ScalarPair(a, b), largest_niche, uninhabited: false, - align, + align: AbiAlign::new(align), size, max_repr_align: None, - unadjusted_abi_align: align.abi, + unadjusted_abi_align: align, randomization_seed: Hash64::new(combined_seed), } } @@ -138,10 +139,10 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { backend_repr: BackendRepr::Memory { sized: true }, largest_niche: None, uninhabited: true, - align: dl.i8_align, + align: AbiAlign::new(dl.i8_align), size: Size::ZERO, max_repr_align: None, - unadjusted_abi_align: dl.i8_align.abi, + unadjusted_abi_align: dl.i8_align, randomization_seed: Hash64::ZERO, } } diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 369874521e5..de44c8755a0 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -229,7 +229,7 @@ pub struct PointerSpec { /// The size of the bitwise representation of the pointer. pointer_size: Size, /// The alignment of pointers for this address space - pointer_align: AbiAlign, + pointer_align: Align, /// The size of the value a pointer can be offset by in this address space. pointer_offset: Size, /// Pointers into this address space contain extra metadata @@ -242,20 +242,20 @@ pub struct PointerSpec { #[derive(Debug, PartialEq, Eq)] pub struct TargetDataLayout { pub endian: Endian, - pub i1_align: AbiAlign, - pub i8_align: AbiAlign, - pub i16_align: AbiAlign, - pub i32_align: AbiAlign, - pub i64_align: AbiAlign, - pub i128_align: AbiAlign, - pub f16_align: AbiAlign, - pub f32_align: AbiAlign, - pub f64_align: AbiAlign, - pub f128_align: AbiAlign, - pub aggregate_align: AbiAlign, + pub i1_align: Align, + pub i8_align: Align, + pub i16_align: Align, + pub i32_align: Align, + pub i64_align: Align, + pub i128_align: Align, + pub f16_align: Align, + pub f32_align: Align, + pub f64_align: Align, + pub f128_align: Align, + pub aggregate_align: Align, /// Alignments for vector types. - pub vector_align: Vec<(Size, AbiAlign)>, + pub vector_align: Vec<(Size, Align)>, pub default_address_space: AddressSpace, pub default_address_space_pointer_spec: PointerSpec, @@ -282,25 +282,25 @@ impl Default for TargetDataLayout { let align = |bits| Align::from_bits(bits).unwrap(); TargetDataLayout { endian: Endian::Big, - i1_align: AbiAlign::new(align(8)), - i8_align: AbiAlign::new(align(8)), - i16_align: AbiAlign::new(align(16)), - i32_align: AbiAlign::new(align(32)), - i64_align: AbiAlign::new(align(32)), - i128_align: AbiAlign::new(align(32)), - f16_align: AbiAlign::new(align(16)), - f32_align: AbiAlign::new(align(32)), - f64_align: AbiAlign::new(align(64)), - f128_align: AbiAlign::new(align(128)), - aggregate_align: AbiAlign { abi: align(8) }, + i1_align: align(8), + i8_align: align(8), + i16_align: align(16), + i32_align: align(32), + i64_align: align(32), + i128_align: align(32), + f16_align: align(16), + f32_align: align(32), + f64_align: align(64), + f128_align: align(128), + aggregate_align: align(8), vector_align: vec![ - (Size::from_bits(64), AbiAlign::new(align(64))), - (Size::from_bits(128), AbiAlign::new(align(128))), + (Size::from_bits(64), align(64)), + (Size::from_bits(128), align(128)), ], default_address_space: AddressSpace::ZERO, default_address_space_pointer_spec: PointerSpec { pointer_size: Size::from_bits(64), - pointer_align: AbiAlign::new(align(64)), + pointer_align: align(64), pointer_offset: Size::from_bits(64), _is_fat: false, }, @@ -360,7 +360,7 @@ impl TargetDataLayout { .map_err(|err| TargetDataLayoutErrors::InvalidAlignment { cause, err }) }; let abi = parse_bits(s, "alignment", cause)?; - Ok(AbiAlign::new(align_from_bits(abi)?)) + Ok(align_from_bits(abi)?) }; // Parse an alignment sequence, possibly in the form `<align>[:<preferred_alignment>]`, @@ -596,7 +596,7 @@ impl TargetDataLayout { /// psABI-mandated alignment for a vector type, if any #[inline] - fn cabi_vector_align(&self, vec_size: Size) -> Option<AbiAlign> { + fn cabi_vector_align(&self, vec_size: Size) -> Option<Align> { self.vector_align .iter() .find(|(size, _align)| *size == vec_size) @@ -605,10 +605,9 @@ impl TargetDataLayout { /// an alignment resembling the one LLVM would pick for a vector #[inline] - pub fn llvmlike_vector_align(&self, vec_size: Size) -> AbiAlign { - self.cabi_vector_align(vec_size).unwrap_or(AbiAlign::new( - Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap(), - )) + pub fn llvmlike_vector_align(&self, vec_size: Size) -> Align { + self.cabi_vector_align(vec_size) + .unwrap_or(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap()) } /// Get the pointer size in the default data address space. @@ -654,21 +653,19 @@ impl TargetDataLayout { /// Get the pointer alignment in the default data address space. #[inline] pub fn pointer_align(&self) -> AbiAlign { - self.default_address_space_pointer_spec.pointer_align + AbiAlign::new(self.default_address_space_pointer_spec.pointer_align) } /// Get the pointer alignment in a specific address space. #[inline] pub fn pointer_align_in(&self, c: AddressSpace) -> AbiAlign { - if c == self.default_address_space { - return self.default_address_space_pointer_spec.pointer_align; - } - - if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) { + AbiAlign::new(if c == self.default_address_space { + self.default_address_space_pointer_spec.pointer_align + } else if let Some(e) = self.address_space_info.iter().find(|(a, _)| a == &c) { e.1.pointer_align } else { panic!("Use of unknown address space {c:?}"); - } + }) } } @@ -1185,13 +1182,13 @@ impl Integer { use Integer::*; let dl = cx.data_layout(); - match self { + AbiAlign::new(match self { I8 => dl.i8_align, I16 => dl.i16_align, I32 => dl.i32_align, I64 => dl.i64_align, I128 => dl.i128_align, - } + }) } /// Returns the largest signed value that can be represented by this Integer. @@ -1311,12 +1308,12 @@ impl Float { use Float::*; let dl = cx.data_layout(); - match self { + AbiAlign::new(match self { F16 => dl.f16_align, F32 => dl.f32_align, F64 => dl.f64_align, F128 => dl.f128_align, - } + }) } } @@ -2159,7 +2156,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> { /// Returns `true` if the type is sized and a 1-ZST (meaning it has size 0 and alignment 1). pub fn is_1zst(&self) -> bool { - self.is_sized() && self.size.bytes() == 0 && self.align.abi.bytes() == 1 + self.is_sized() && self.size.bytes() == 0 && self.align.bytes() == 1 } /// Returns `true` if the type is a ZST and not unsized. diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3e8fddd9954..082d5e88ac7 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -114,8 +114,7 @@ impl PartialEq<Symbol> for Path { impl PartialEq<&[Symbol]> for Path { #[inline] fn eq(&self, names: &&[Symbol]) -> bool { - self.segments.len() == names.len() - && self.segments.iter().zip(names.iter()).all(|(s1, s2)| s1 == s2) + self.segments.iter().eq(*names) } } diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index 33451f99748..90f15753e99 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -6,6 +6,7 @@ use std::fmt::{self, Display, Formatter}; use std::str::FromStr; +use crate::expand::typetree::TypeTree; use crate::expand::{Decodable, Encodable, HashStable_Generic}; use crate::{Ty, TyKind}; @@ -84,6 +85,8 @@ pub struct AutoDiffItem { /// The name of the function being generated pub target: String, pub attrs: AutoDiffAttrs, + pub inputs: Vec<TypeTree>, + pub output: TypeTree, } #[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] @@ -275,14 +278,22 @@ impl AutoDiffAttrs { !matches!(self.mode, DiffMode::Error | DiffMode::Source) } - pub fn into_item(self, source: String, target: String) -> AutoDiffItem { - AutoDiffItem { source, target, attrs: self } + pub fn into_item( + self, + source: String, + target: String, + inputs: Vec<TypeTree>, + output: TypeTree, + ) -> AutoDiffItem { + AutoDiffItem { source, target, inputs, output, attrs: self } } } impl fmt::Display for AutoDiffItem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Differentiating {} -> {}", self.source, self.target)?; - write!(f, " with attributes: {:?}", self.attrs) + write!(f, " with attributes: {:?}", self.attrs)?; + write!(f, " with inputs: {:?}", self.inputs)?; + write!(f, " with output: {:?}", self.output) } } diff --git a/compiler/rustc_ast/src/expand/typetree.rs b/compiler/rustc_ast/src/expand/typetree.rs index 9a2dd2e85e0..e7b4f3aff41 100644 --- a/compiler/rustc_ast/src/expand/typetree.rs +++ b/compiler/rustc_ast/src/expand/typetree.rs @@ -31,6 +31,7 @@ pub enum Kind { Half, Float, Double, + F128, Unknown, } diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index f1951049b47..5fe218776e5 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -15,6 +15,7 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(if_let_guard)] +#![feature(iter_order_by)] #![feature(macro_metavar_expr)] #![feature(rustdoc_internals)] #![recursion_limit = "256"] diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index a5d8fbfac61..4111182c3b7 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -48,9 +48,7 @@ impl TokenTree { match (self, other) { (TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind, (TokenTree::Delimited(.., delim, tts), TokenTree::Delimited(.., delim2, tts2)) => { - delim == delim2 - && tts.len() == tts2.len() - && tts.iter().zip(tts2.iter()).all(|(a, b)| a.eq_unspanned(b)) + delim == delim2 && tts.iter().eq_by(tts2.iter(), |a, b| a.eq_unspanned(b)) } _ => false, } diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index ec9d26eb33f..2871430030c 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -487,26 +487,6 @@ fn expand_format_args<'hir>( // Generate: // [] (vec![], ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(&[])))) - } else if argmap.len() == 1 && arguments.len() == 1 { - // Only one argument, so we don't need to make the `args` tuple. - // - // Generate: - // super let args = [<core::fmt::Argument>::new_display(&arg)]; - let args = ctx.arena.alloc_from_iter(argmap.iter().map( - |(&(arg_index, ty), &placeholder_span)| { - let arg = &arguments[arg_index]; - let placeholder_span = - placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt()); - let arg = ctx.lower_expr(&arg.expr); - let ref_arg = ctx.arena.alloc(ctx.expr_ref(arg.span.with_ctxt(macsp.ctxt()), arg)); - make_argument(ctx, placeholder_span, ref_arg, ty) - }, - )); - let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args))); - let args_ident = Ident::new(sym::args, macsp); - let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident); - let let_statement = ctx.stmt_super_let_pat(macsp, args_pat, Some(args)); - (vec![let_statement], ctx.arena.alloc(ctx.expr_ident_mut(macsp, args_ident, args_hir_id))) } else { // Generate: // super let args = (&arg0, &arg1, &…); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index bb559bd8921..53351f91c46 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -5,7 +5,9 @@ use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin, Target, find_attr}; +use rustc_hir::{ + self as hir, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target, find_attr, +}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; @@ -1117,20 +1119,31 @@ impl<'hir> LoweringContext<'_, 'hir> { } }; + let span = self.lower_span(i.span); let item = hir::ImplItem { owner_id: hir_id.expect_owner(), ident: self.lower_ident(ident), generics, + impl_kind: if is_in_trait_impl { + ImplItemImplKind::Trait { + defaultness, + trait_item_def_id: self + .resolver + .get_partial_res(i.id) + .and_then(|r| r.expect_full_res().opt_def_id()) + .ok_or_else(|| { + self.dcx().span_delayed_bug( + span, + "could not resolve trait item being implemented", + ) + }), + } + } else { + ImplItemImplKind::Inherent { vis_span: self.lower_span(i.vis.span) } + }, kind, - vis_span: self.lower_span(i.vis.span), - span: self.lower_span(i.span), - defaultness, + span, has_delayed_lints: !self.delayed_lints.is_empty(), - trait_item_def_id: self - .resolver - .get_partial_res(i.id) - .map(|r| r.expect_full_res().opt_def_id()) - .unwrap_or(None), }; self.arena.alloc(item) } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 8dcf3e3aa38..5e10c5a77d9 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -64,8 +64,6 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect -ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list - ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions .label = `extern "{$abi}"` because of this .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list @@ -216,6 +214,10 @@ ast_passes_match_arm_with_no_body = .suggestion = add a body after the pattern ast_passes_missing_unsafe_on_extern = extern blocks must be unsafe + .suggestion = needs `unsafe` before the extern keyword + +ast_passes_missing_unsafe_on_extern_lint = extern blocks should be unsafe + .suggestion = needs `unsafe` before the extern keyword ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name .help = consider using the `#[path]` attribute to specify filesystem path diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a6ef89b553d..f773b02058e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -696,7 +696,7 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, - FnCtxt::Free => match sig.header.ext { + FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext { Extern::Implicit(_) => { if !matches!(sig.header.safety, Safety::Unsafe(_)) { self.dcx().emit_err(errors::CVariadicMustBeUnsafe { @@ -726,11 +726,6 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(err); } }, - FnCtxt::Assoc(_) => { - // For now, C variable argument lists are unsupported in associated functions. - let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; - self.dcx().emit_err(err); - } } } @@ -1131,7 +1126,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { MISSING_UNSAFE_ON_EXTERN, item.id, item.span, - BuiltinLintDiag::MissingUnsafeOnExtern { + errors::MissingUnsafeOnExternLint { suggestion: item.span.shrink_to_lo(), }, ); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ae805042c54..fd75e999d13 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -319,13 +319,6 @@ pub(crate) struct ExternItemAscii { } #[derive(Diagnostic)] -#[diag(ast_passes_c_variadic_associated_function)] -pub(crate) struct CVariadicAssociatedFunction { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(ast_passes_c_variadic_no_extern)] #[help] pub(crate) struct CVariadicNoExtern { @@ -523,6 +516,13 @@ pub(crate) struct MissingUnsafeOnExtern { pub span: Span, } +#[derive(LintDiagnostic)] +#[diag(ast_passes_missing_unsafe_on_extern_lint)] +pub(crate) struct MissingUnsafeOnExternLint { + #[suggestion(code = "unsafe ", applicability = "machine-applicable")] + pub suggestion: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_fieldless_union)] pub(crate) struct FieldlessUnion { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 9ab5b0b3547..608ccfefeb6 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -183,7 +183,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_doc!( "experimental" { cfg => doc_cfg - cfg_hide => doc_cfg_hide + auto_cfg => doc_cfg masked => doc_masked notable_trait => doc_notable_trait } diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 839a5d23c3b..6c5346e8355 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -8,7 +8,15 @@ attr_parsing_deprecated_item_suggestion = attr_parsing_empty_attribute = unused attribute - .suggestion = remove this attribute + .suggestion = {$valid_without_list -> + [true] remove these parentheses + *[other] remove this attribute + } + .note = {$valid_without_list -> + [true] using `{$attr_path}` with an empty list is equivalent to not using a list at all + *[other] using `{$attr_path}` with an empty list has no effect + } + attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} .help = `#[{$name}]` can {$only}be applied to {$applied} @@ -122,6 +130,14 @@ attr_parsing_null_on_export = `export_name` may not contain null characters attr_parsing_null_on_link_section = `link_section` may not contain null characters +attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters + +attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters + +attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal + +attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal + attr_parsing_repr_ident = meta item in `repr` must be an identifier diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index d5d51f2e79a..262b8213977 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -2,7 +2,10 @@ use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy}; use rustc_session::parse::feature_err; use super::prelude::*; -use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport}; +use crate::session_diagnostics::{ + NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, + ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, +}; pub(crate) struct OptimizeParser; @@ -150,6 +153,70 @@ impl<S: Stage> SingleAttributeParser<S> for ExportNameParser { } } +pub(crate) struct ObjcClassParser; + +impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(classname) = nv.value_as_str() else { + // `#[rustc_objc_class = ...]` is expected to be used as an implementatioin detail + // inside a standard library macro, but `cx.expected_string_literal` exposes too much. + // Use a custom error message instead. + cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span }); + return None; + }; + if classname.as_str().contains('\0') { + // `#[rustc_objc_class = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnObjcClass { span: nv.value_span }); + return None; + } + Some(AttributeKind::ObjcClass { classname, span: cx.attr_span }) + } +} + +pub(crate) struct ObjcSelectorParser; + +impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(methname) = nv.value_as_str() else { + // `#[rustc_objc_selector = ...]` is expected to be used as an implementatioin detail + // inside a standard library macro, but `cx.expected_string_literal` exposes too much. + // Use a custom error message instead. + cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span }); + return None; + }; + if methname.as_str().contains('\0') { + // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnObjcSelector { span: nv.value_span }); + return None; + } + Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span }) + } +} + #[derive(Default)] pub(crate) struct NakedParser { span: Option<Span>, diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 0b2c05482bf..20ec0fd0c7b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,40 +1,4 @@ -use std::num::IntErrorKind; - -use rustc_hir::limit::Limit; - use super::prelude::*; -use crate::session_diagnostics::LimitInvalid; - -impl<S: Stage> AcceptContext<'_, '_, S> { - fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> { - let Some(limit) = nv.value_as_str() else { - self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); - return None; - }; - - let error_str = match limit.as_str().parse() { - Ok(i) => return Some(Limit::new(i)), - Err(e) => match e.kind() { - IntErrorKind::PosOverflow => "`limit` is too large", - IntErrorKind::Empty => "`limit` must be a non-negative integer", - IntErrorKind::InvalidDigit => "not a valid integer", - IntErrorKind::NegOverflow => { - panic!( - "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead" - ) - } - IntErrorKind::Zero => { - panic!("zero is a valid `limit` so should have returned Ok() when parsing") - } - kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), - }, - }; - - self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str }); - - None - } -} pub(crate) struct CrateNameParser; @@ -43,11 +7,7 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { let ArgParser::NameValue(n) = args else { @@ -76,11 +36,7 @@ impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { let ArgParser::NameValue(nv) = args else { @@ -103,11 +59,7 @@ impl<S: Stage> SingleAttributeParser<S> for MoveSizeLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { let ArgParser::NameValue(nv) = args else { @@ -130,11 +82,7 @@ impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { let ArgParser::NameValue(nv) = args else { @@ -157,11 +105,7 @@ impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { let ArgParser::NameValue(nv) = args else { @@ -182,11 +126,8 @@ pub(crate) struct NoCoreParser; impl<S: Stage> NoArgsAttributeParser<S> for NoCoreParser { const PATH: &[Symbol] = &[sym::no_core]; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; - const TYPE: AttributeType = AttributeType::CrateLevel; } pub(crate) struct NoStdParser; @@ -194,9 +135,15 @@ pub(crate) struct NoStdParser; impl<S: Stage> NoArgsAttributeParser<S> for NoStdParser { const PATH: &[Symbol] = &[sym::no_std]; const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; - const TYPE: AttributeType = AttributeType::CrateLevel; +} + +pub(crate) struct RustcCoherenceIsCoreParser; + +impl<S: Stage> NoArgsAttributeParser<S> for RustcCoherenceIsCoreParser { + const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore; } diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs new file mode 100644 index 00000000000..56ff10be426 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -0,0 +1,60 @@ +use rustc_hir::attrs::{DebugVisualizer, DebuggerVisualizerType}; + +use super::prelude::*; + +pub(crate) struct DebuggerViualizerParser; + +impl<S: Stage> CombineAttributeParser<S> for DebuggerViualizerParser { + const PATH: &[Symbol] = &[sym::debugger_visualizer]; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Mod), Allow(Target::Crate)]); + const TEMPLATE: AttributeTemplate = template!( + List: &[r#"natvis_file = "...", gdb_script_file = "...""#], + "https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute" + ); + + type Item = DebugVisualizer; + const CONVERT: ConvertFn<Self::Item> = |v, _| AttributeKind::DebuggerVisualizer(v); + + fn extend<'c>( + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) -> impl IntoIterator<Item = Self::Item> + 'c { + let Some(l) = args.list() else { + cx.expected_list(args.span().unwrap_or(cx.attr_span)); + return None; + }; + let Some(single) = l.single() else { + cx.expected_single_argument(l.span); + return None; + }; + let Some(mi) = single.meta_item() else { + cx.expected_name_value(single.span(), None); + return None; + }; + let path = mi.path().word_sym(); + let visualizer_type = match path { + Some(sym::natvis_file) => DebuggerVisualizerType::Natvis, + Some(sym::gdb_script_file) => DebuggerVisualizerType::GdbPrettyPrinter, + _ => { + cx.expected_specific_argument( + mi.path().span(), + &[sym::natvis_file, sym::gdb_script_file], + ); + return None; + } + }; + + let Some(path) = mi.args().name_value() else { + cx.expected_name_value(single.span(), path); + return None; + }; + + let Some(path) = path.value_as_str() else { + cx.expected_string_literal(path.value_span, Some(path.value_as_lit())); + return None; + }; + + Some(DebugVisualizer { span: mi.span(), visualizer_type, path }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 180130c7be4..849141c34f7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,3 +1,4 @@ +use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; @@ -133,3 +134,65 @@ impl<S: Stage> NoArgsAttributeParser<S> for AllowInternalUnsafeParser { ]); const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span); } + +pub(crate) struct MacroExportParser; + +impl<S: Stage> SingleAttributeParser<S> for MacroExportParser { + const PATH: &[Symbol] = &[sym::macro_export]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(Word, List: &["local_inner_macros"]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + Allow(Target::MacroDef), + Error(Target::WherePredicate), + Error(Target::Crate), + ]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let suggestions = || { + <Self as SingleAttributeParser<S>>::TEMPLATE + .suggestions(AttrStyle::Inner, "macro_export") + }; + let local_inner_macros = match args { + ArgParser::NoArgs => false, + ArgParser::List(list) => { + let Some(l) = list.single() else { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + }; + match l.meta_item().and_then(|i| i.path().word_sym()) { + Some(sym::local_inner_macros) => true, + _ => { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + } + } + } + ArgParser::NameValue(_) => { + let span = cx.attr_span; + let suggestions = suggestions(); + cx.emit_err(IllFormedAttributeInputLint { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span, + }); + return None; + } + }; + Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 043bc925eac..8dbf4c0ef32 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -12,15 +12,11 @@ //! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the //! contents of attributes, if an attribute appear multiple times in a list //! -//! By default, attributes are allowed anywhere. When adding an attribute that should only be used -//! at the crate root, consider setting the `TYPE` in the parser trait to -//! [`AttributeType::CrateLevel`](rustc_feature::AttributeType::CrateLevel). -//! //! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed. use std::marker::PhantomData; -use rustc_feature::{AttributeTemplate, AttributeType, template}; +use rustc_feature::{AttributeTemplate, template}; use rustc_hir::attrs::AttributeKind; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; @@ -40,6 +36,7 @@ pub(crate) mod cfg_old; pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod crate_level; +pub(crate) mod debugger; pub(crate) mod deprecation; pub(crate) mod dummy; pub(crate) mod inline; @@ -89,11 +86,8 @@ pub(crate) trait AttributeParser<S: Stage>: Default + 'static { /// /// If an attribute has this symbol, the `accept` function will be called on it. const ATTRIBUTES: AcceptMapping<Self, S>; - const ALLOWED_TARGETS: AllowedTargets; - const TYPE: AttributeType = AttributeType::Normal; - /// The parser has gotten a chance to accept the attributes on an item, /// here it can produce an attribute. /// @@ -135,8 +129,6 @@ pub(crate) trait SingleAttributeParser<S: Stage>: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; - const TYPE: AttributeType = AttributeType::Normal; - /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind>; } @@ -183,8 +175,6 @@ impl<T: SingleAttributeParser<S>, S: Stage> AttributeParser<S> for Single<T, S> )]; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; - const TYPE: AttributeType = T::TYPE; - fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { Some(self.1?.0) } @@ -269,7 +259,6 @@ pub(crate) trait NoArgsAttributeParser<S: Stage>: 'static { const PATH: &[Symbol]; const ON_DUPLICATE: OnDuplicate<S>; const ALLOWED_TARGETS: AllowedTargets; - const TYPE: AttributeType = AttributeType::Normal; /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; @@ -289,7 +278,6 @@ impl<T: NoArgsAttributeParser<S>, S: Stage> SingleAttributeParser<S> for Without const ON_DUPLICATE: OnDuplicate<S> = T::ON_DUPLICATE; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; const TEMPLATE: AttributeTemplate = template!(Word); - const TYPE: AttributeType = T::TYPE; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { if let Err(span) = args.no_args() { @@ -323,8 +311,6 @@ pub(crate) trait CombineAttributeParser<S: Stage>: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; - const TYPE: AttributeType = AttributeType::Normal; - /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, @@ -360,7 +346,6 @@ impl<T: CombineAttributeParser<S>, S: Stage> AttributeParser<S> for Combine<T, S group.items.extend(T::extend(cx, args)) })]; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; - const TYPE: AttributeType = T::TYPE; fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { if let Some(first_span) = self.first_span { diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index 8f040ffb9d4..980366b5c37 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -1,6 +1,6 @@ // data structures #[doc(hidden)] -pub(super) use rustc_feature::{AttributeTemplate, AttributeType, template}; +pub(super) use rustc_feature::{AttributeTemplate, template}; #[doc(hidden)] pub(super) use rustc_hir::attrs::AttributeKind; #[doc(hidden)] diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index a995549fc7c..cf2f5c6c790 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -49,3 +49,21 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser { Some(AttributeKind::RustcObjectLifetimeDefault) } } + +pub(crate) struct RustcSimdMonomorphizeLaneLimitParser; + +impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser { + const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> { + let ArgParser::NameValue(nv) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index fbd9a480fbb..ced3bcad229 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -1,7 +1,5 @@ use std::mem; -use rustc_feature::AttributeType; - use super::prelude::*; use crate::attributes::{ AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, @@ -151,15 +149,6 @@ impl<S: Stage> NoArgsAttributeParser<S> for AllowIncoherentImplParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl; } -pub(crate) struct CoherenceIsCoreParser; -impl<S: Stage> NoArgsAttributeParser<S> for CoherenceIsCoreParser { - const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; - const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); - const TYPE: AttributeType = AttributeType::CrateLevel; - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore; -} - pub(crate) struct FundamentalParser; impl<S: Stage> NoArgsAttributeParser<S> for FundamentalParser { const PATH: &[Symbol] = &[sym::fundamental]; diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 77e8c32e59d..62b72798e96 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -1,11 +1,15 @@ +use std::num::IntErrorKind; + use rustc_ast::LitKind; use rustc_ast::attr::AttributeExt; use rustc_feature::is_builtin_attr_name; use rustc_hir::RustcVersion; +use rustc_hir::limit::Limit; use rustc_span::{Symbol, sym}; use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; +use crate::parser::{ArgParser, NameValueParser}; +use crate::session_diagnostics::LimitInvalid; /// Parse a rustc version number written inside string literal in an attribute, /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are @@ -85,3 +89,34 @@ pub(crate) fn parse_single_integer<S: Stage>( }; Some(num.0) } + +impl<S: Stage> AcceptContext<'_, '_, S> { + pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> { + let Some(limit) = nv.value_as_str() else { + self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + let error_str = match limit.as_str().parse() { + Ok(i) => return Some(Limit::new(i)), + Err(e) => match e.kind() { + IntErrorKind::PosOverflow => "`limit` is too large", + IntErrorKind::Empty => "`limit` must be a non-negative integer", + IntErrorKind::InvalidDigit => "not a valid integer", + IntErrorKind::NegOverflow => { + panic!( + "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead" + ) + } + IntErrorKind::Zero => { + panic!("zero is a valid `limit` so should have returned Ok() when parsing") + } + kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), + }, + }; + + self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str }); + + None + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b3ab1d3edd6..e8bb4caa416 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -6,7 +6,7 @@ use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId}; use rustc_errors::{Diag, Diagnostic, Level}; -use rustc_feature::{AttributeTemplate, AttributeType}; +use rustc_feature::AttributeTemplate; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; @@ -20,14 +20,15 @@ use crate::attributes::allow_unstable::{ use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, - NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser, - UsedParser, + NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser, + TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser, - RecursionLimitParser, TypeLengthLimitParser, + RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser, }; +use crate::attributes::debugger::DebuggerViualizerParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -40,7 +41,7 @@ use crate::attributes::lint_helpers::{ }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::macro_attrs::{ - AllowInternalUnsafeParser, MacroEscapeParser, MacroUseParser, + AllowInternalUnsafeParser, MacroEscapeParser, MacroExportParser, MacroUseParser, }; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; @@ -53,7 +54,7 @@ use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, - RustcObjectLifetimeDefaultParser, + RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -61,10 +62,10 @@ use crate::attributes::stability::{ }; use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser}; use crate::attributes::traits::{ - AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser, - DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, - ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, - TypeConstParser, UnsafeSpecializationMarkerParser, + AllowIncoherentImplParser, CoinductiveParser, ConstTraitParser, DenyExplicitImplParser, + DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser, + PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, + UnsafeSpecializationMarkerParser, }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; @@ -83,7 +84,6 @@ pub(super) struct GroupTypeInnerAccept<S: Stage> { pub(super) template: AttributeTemplate, pub(super) accept_fn: AcceptFn<S>, pub(super) allowed_targets: AllowedTargets, - pub(super) attribute_type: AttributeType, } type AcceptFn<S> = @@ -133,7 +133,6 @@ macro_rules! attribute_parsers { }) }), allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, - attribute_type: <$names as crate::attributes::AttributeParser<$stage>>::TYPE, }); } @@ -165,6 +164,7 @@ attribute_parsers!( // tidy-alphabetical-start Combine<AllowConstFnUnstableParser>, Combine<AllowInternalUnstableParser>, + Combine<DebuggerViualizerParser>, Combine<ForceTargetFeatureParser>, Combine<LinkParser>, Combine<ReprParser>, @@ -185,8 +185,11 @@ attribute_parsers!( Single<LinkOrdinalParser>, Single<LinkSectionParser>, Single<LinkageParser>, + Single<MacroExportParser>, Single<MoveSizeLimitParser>, Single<MustUseParser>, + Single<ObjcClassParser>, + Single<ObjcSelectorParser>, Single<OptimizeParser>, Single<PathAttributeParser>, Single<PatternComplexityLimitParser>, @@ -197,6 +200,7 @@ attribute_parsers!( Single<RustcLayoutScalarValidRangeEnd>, Single<RustcLayoutScalarValidRangeStart>, Single<RustcObjectLifetimeDefaultParser>, + Single<RustcSimdMonomorphizeLaneLimitParser>, Single<SanitizeParser>, Single<ShouldPanicParser>, Single<SkipDuringMethodDispatchParser>, @@ -206,7 +210,6 @@ attribute_parsers!( Single<WithoutArgs<AllowInternalUnsafeParser>>, Single<WithoutArgs<AsPtrParser>>, Single<WithoutArgs<AutomaticallyDerivedParser>>, - Single<WithoutArgs<CoherenceIsCoreParser>>, Single<WithoutArgs<CoinductiveParser>>, Single<WithoutArgs<ColdParser>>, Single<WithoutArgs<ConstContinueParser>>, @@ -234,6 +237,7 @@ attribute_parsers!( Single<WithoutArgs<ProcMacroAttributeParser>>, Single<WithoutArgs<ProcMacroParser>>, Single<WithoutArgs<PubTransparentParser>>, + Single<WithoutArgs<RustcCoherenceIsCoreParser>>, Single<WithoutArgs<SpecializationTraitParser>>, Single<WithoutArgs<StdInternalSymbolParser>>, Single<WithoutArgs<TrackCallerParser>>, @@ -593,7 +597,12 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { - self.emit_lint(AttributeLintKind::EmptyAttribute { first_span: span }, span); + let attr_path = self.attr_path.clone(); + let valid_without_list = self.template.word; + self.emit_lint( + AttributeLintKind::EmptyAttribute { first_span: span, attr_path, valid_without_list }, + span, + ); } } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 0fe3c209421..8f2de4af14e 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -272,7 +272,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { (accept.accept_fn)(&mut cx, args); if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { - Self::check_type(accept.attribute_type, target, &mut cx); Self::check_target(&accept.allowed_targets, target, &mut cx); } } diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index b1a971eec32..3a2a3704669 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -31,12 +31,30 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<L::Id>, lint_emi }, ); } - AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint( - rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - *id, - *first_span, - session_diagnostics::EmptyAttributeList { attr_span: *first_span }, - ), + AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter + .emit_node_span_lint( + rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS, + *id, + *span, + session_diagnostics::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + }, + ), + AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => { + lint_emitter.emit_node_span_lint( + rustc_session::lint::builtin::UNUSED_ATTRIBUTES, + *id, + *first_span, + session_diagnostics::EmptyAttributeList { + attr_span: *first_span, + attr_path: attr_path.clone(), + valid_without_list: *valid_without_list, + }, + ) + } AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter .emit_node_span_lint( // This check is here because `deprecated` had its own lint group and removing this would be a breaking change diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 4f903594225..3f4f5679015 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -49,7 +49,7 @@ impl<'a> PathParser<'a> { } pub fn segments_is(&self, segments: &[Symbol]) -> bool { - self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b) + self.segments().map(|segment| &segment.name).eq(segments) } pub fn word(&self) -> Option<Ident> { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 32ea9005a97..1194ac5872c 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -460,6 +460,34 @@ pub(crate) struct NullOnLinkSection { } #[derive(Diagnostic)] +#[diag(attr_parsing_null_on_objc_class)] +pub(crate) struct NullOnObjcClass { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_null_on_objc_selector)] +pub(crate) struct NullOnObjcSelector { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_objc_class_expected_string_literal)] +pub(crate) struct ObjcClassExpectedStringLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_objc_selector_expected_string_literal)] +pub(crate) struct ObjcSelectorExpectedStringLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(attr_parsing_stability_outside_std, code = E0734)] pub(crate) struct StabilityOutsideStd { #[primary_span] @@ -475,9 +503,12 @@ pub(crate) struct EmptyConfusables { #[derive(LintDiagnostic)] #[diag(attr_parsing_empty_attribute)] +#[note] pub(crate) struct EmptyAttributeList { #[suggestion(code = "", applicability = "machine-applicable")] pub attr_span: Span, + pub attr_path: AttrPath, + pub valid_without_list: bool, } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index edc496b460c..c52253699b5 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; -use rustc_feature::{AttributeType, Features}; +use rustc_feature::Features; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{MethodKind, Target}; @@ -14,6 +14,11 @@ use crate::session_diagnostics::InvalidTarget; pub(crate) enum AllowedTargets { AllowList(&'static [Policy]), AllowListWarnRest(&'static [Policy]), + /// Special, and not the same as `AllowList(&[Allow(Target::Crate)])`. + /// For crate-level attributes we emit a specific set of lints to warn + /// people about accidentally not using them on the crate. + /// Only use this for attributes that are *exclusively* valid at the crate level. + CrateLevel, } pub(crate) enum AllowedResult { @@ -43,6 +48,7 @@ impl AllowedTargets { AllowedResult::Warn } } + AllowedTargets::CrateLevel => AllowedResult::Allowed, } } @@ -50,6 +56,7 @@ impl AllowedTargets { match self { AllowedTargets::AllowList(list) => list, AllowedTargets::AllowListWarnRest(list) => list, + AllowedTargets::CrateLevel => ALL_TARGETS, } .iter() .filter_map(|target| match target { @@ -74,6 +81,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { + Self::check_type(matches!(allowed_targets, AllowedTargets::CrateLevel), target, cx); + match allowed_targets.is_allowed(target) { AllowedResult::Allowed => {} AllowedResult::Warn => { @@ -109,7 +118,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } pub(crate) fn check_type( - attribute_type: AttributeType, + crate_level: bool, target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { @@ -119,7 +128,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } - if attribute_type != AttributeType::CrateLevel { + if !crate_level { return; } diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index c9be5575da5..7c9011505d6 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -426,7 +426,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { } pub(crate) fn path_does_not_live_long_enough(&self, span: Span, path: &str) -> Diag<'infcx> { - struct_span_code_err!(self.dcx(), span, E0597, "{} does not live long enough", path,) + struct_span_code_err!(self.dcx(), span, E0597, "{} does not live long enough", path) } pub(crate) fn cannot_return_reference_to_local( @@ -480,7 +480,7 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, 'infcx, 'tcx> { } pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'infcx> { - struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed",) + struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed") } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7e20a5133e0..efb622e2155 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2992,6 +2992,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.buffer_error(err); } + #[tracing::instrument(level = "debug", skip(self, explanation))] fn report_local_value_does_not_live_long_enough( &self, location: Location, @@ -3001,13 +3002,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { borrow_spans: UseSpans<'tcx>, explanation: BorrowExplanation<'tcx>, ) -> Diag<'infcx> { - debug!( - "report_local_value_does_not_live_long_enough(\ - {:?}, {:?}, {:?}, {:?}, {:?}\ - )", - location, name, borrow, drop_span, borrow_spans - ); - let borrow_span = borrow_spans.var_or_use_path_span(); if let BorrowExplanation::MustBeValidFor { category, diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index fda96dde826..638d89f5bcb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -416,6 +416,26 @@ impl<'tcx> BorrowExplanation<'tcx> { { self.add_object_lifetime_default_note(tcx, err, unsize_ty); } + + let mut preds = path + .iter() + .filter_map(|constraint| match constraint.category { + ConstraintCategory::Predicate(pred) if !pred.is_dummy() => Some(pred), + _ => None, + }) + .collect::<Vec<Span>>(); + preds.sort(); + preds.dedup(); + if !preds.is_empty() { + let s = if preds.len() == 1 { "" } else { "s" }; + err.span_note( + preds, + format!( + "requirement{s} that the value outlives `{region_name}` introduced here" + ), + ); + } + self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name); } _ => {} @@ -438,7 +458,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let elaborated_args = std::iter::zip(*args, &generics.own_params).map(|(arg, param)| { - if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) { + if let Some(ty::Dynamic(obj, _)) = arg.as_type().map(Ty::kind) { let default = tcx.object_lifetime_default(param.def_id); let re_static = tcx.lifetimes.re_static; @@ -464,7 +484,7 @@ impl<'tcx> BorrowExplanation<'tcx> { has_dyn = true; - Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into() + Ty::new_dynamic(tcx, obj, implied_region).into() } else { arg } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index ea264c8064a..6d69040c711 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -716,7 +716,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return (false, false, None); }; - let implemented_trait_item = self.infcx.tcx.associated_item(my_def).trait_item_def_id; + let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def); ( true, diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 94379cdebf7..6be90994015 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -166,13 +166,9 @@ impl RegionTracker { } } - /// Determine if the tracked universes of the two SCCs are compatible. - pub(crate) fn universe_compatible_with(&self, other: Self) -> bool { - // HACK: We first check whether we can name the highest existential universe - // of `other`. This only exists to avoid errors in case that scc already - // depends on a placeholder it cannot name itself. - self.max_nameable_universe().can_name(other.max_nameable_universe()) - || other.reachable_placeholders.can_be_named_by(self.max_nameable_universe()) + /// Determine if we can name all the placeholders in `other`. + pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool { + other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0) } /// If this SCC reaches a placeholder it can't name, return it. diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5d2dda8b0e7..268cb47fd12 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -119,7 +119,7 @@ pub fn provide(providers: &mut Providers) { fn mir_borrowck( tcx: TyCtxt<'_>, def: LocalDefId, -) -> Result<&ConcreteOpaqueTypes<'_>, ErrorGuaranteed> { +) -> Result<&DefinitionSiteHiddenTypes<'_>, ErrorGuaranteed> { assert!(!tcx.is_typeck_child(def.to_def_id())); let (input_body, _) = tcx.mir_promoted(def); debug!("run query mir_borrowck: {}", tcx.def_path_str(def)); @@ -130,7 +130,7 @@ fn mir_borrowck( Err(guar) } else if input_body.should_skip() { debug!("Skipping borrowck because of injected body"); - let opaque_types = ConcreteOpaqueTypes(Default::default()); + let opaque_types = DefinitionSiteHiddenTypes(Default::default()); Ok(tcx.arena.alloc(opaque_types)) } else { let mut root_cx = BorrowCheckRootCtxt::new(tcx, def, None); @@ -1557,9 +1557,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { + &Rvalue::Discriminant(place) => { let af = match *rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), Rvalue::Discriminant(..) => None, _ => unreachable!(), }; @@ -1904,7 +1903,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) @@ -1950,7 +1949,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 99dd0b2dd46..c2ad6fcb4b7 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -306,16 +306,11 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, op); } - &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { - let af = match rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), - Rvalue::Discriminant(..) => None, - _ => unreachable!(), - }; + &Rvalue::Discriminant(place) => { self.access_place( location, place, - (Shallow(af), Read(ReadKind::Copy)), + (Shallow(None), Read(ReadKind::Copy)), LocalMutationIsAllowed::No, ); } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f57456949bb..e98c60e6338 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -571,11 +571,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - /// Returns `true` if all the elements in the value of `scc_b` are nameable + /// Returns `true` if all the placeholders in the value of `scc_b` are nameable /// in `scc_a`. Used during constraint propagation, and only once /// the value of `scc_b` has been computed. - fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool { - self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b]) + fn can_name_all_placeholders( + &self, + scc_a: ConstraintSccIndex, + scc_b: ConstraintSccIndex, + ) -> bool { + self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b]) } /// Once regions have been propagated, this method is used to see @@ -964,16 +968,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { return true; } + let fr_static = self.universal_regions().fr_static; + // If we are checking that `'sup: 'sub`, and `'sub` contains // some placeholder that `'sup` cannot name, then this is only // true if `'sup` outlives static. - if !self.universe_compatible(sub_region_scc, sup_region_scc) { + // + // Avoid infinite recursion if `sub_region` is already `'static` + if sub_region != fr_static + && !self.can_name_all_placeholders(sup_region_scc, sub_region_scc) + { debug!( "sub universe `{sub_region_scc:?}` is not nameable \ by super `{sup_region_scc:?}`, promoting to static", ); - return self.eval_outlives(sup_region, self.universal_regions().fr_static); + return self.eval_outlives(sup_region, fr_static); } // Both the `sub_region` and `sup_region` consist of the union @@ -1372,10 +1382,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// The constraints we get from equating the hidden type of each use of an opaque - /// with its final concrete type may end up getting preferred over other, potentially + /// with its final hidden type may end up getting preferred over other, potentially /// longer constraint paths. /// - /// Given that we compute the final concrete type by relying on this existing constraint + /// Given that we compute the final hidden type by relying on this existing constraint /// path, this can easily end up hiding the actual reason for why we require these regions /// to be equal. /// @@ -1726,9 +1736,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { // `BoringNoLocation` constraints can point to user-written code, but are less // specific, and are not used for relations that would make sense to blame. ConstraintCategory::BoringNoLocation => 6, - // Do not blame internal constraints. - ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 7, - ConstraintCategory::Internal => 8, + // Do not blame internal constraints if we can avoid it. Never blame + // the `'region: 'static` constraints introduced by placeholder outlives. + ConstraintCategory::Internal => 7, + ConstraintCategory::OutlivesUnnameablePlaceholder(_) => 8, }; debug!("constraint {constraint:?} category: {category:?}, interest: {interest:?}"); diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 0af636aa734..8d89f3e0d87 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, OpaqueTypeStorageEntries}; use rustc_infer::traits::ObligationCause; use rustc_macros::extension; -use rustc_middle::mir::{Body, ConcreteOpaqueTypes, ConstraintCategory}; +use rustc_middle::mir::{Body, ConstraintCategory, DefinitionSiteHiddenTypes}; use rustc_middle::ty::{ self, DefiningScopeKind, EarlyBinder, FallibleTypeFolder, GenericArg, GenericArgsRef, OpaqueHiddenType, OpaqueTypeKey, Region, RegionVid, Ty, TyCtxt, TypeFoldable, @@ -129,9 +129,9 @@ fn nll_var_to_universal_region<'tcx>( /// Collect all defining uses of opaque types inside of this typeck root. This /// expects the hidden type to be mapped to the definition parameters of the opaque /// and errors if we end up with distinct hidden types. -fn add_concrete_opaque_type<'tcx>( +fn add_hidden_type<'tcx>( tcx: TyCtxt<'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, def_id: LocalDefId, hidden_ty: OpaqueHiddenType<'tcx>, ) { @@ -139,7 +139,7 @@ fn add_concrete_opaque_type<'tcx>( // back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to // `(X, Y)` and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we // only know that once we convert the generic parameters to those of the opaque type. - if let Some(prev) = concrete_opaque_types.0.get_mut(&def_id) { + if let Some(prev) = hidden_types.0.get_mut(&def_id) { if prev.ty != hidden_ty.ty { let guar = hidden_ty.ty.error_reported().err().unwrap_or_else(|| { let (Ok(e) | Err(e)) = prev.build_mismatch_error(&hidden_ty, tcx).map(|d| d.emit()); @@ -151,15 +151,15 @@ fn add_concrete_opaque_type<'tcx>( // FIXME(oli-obk): collect multiple spans for better diagnostics down the road. prev.span = prev.span.substitute_dummy(hidden_ty.span); } else { - concrete_opaque_types.0.insert(def_id, hidden_ty); + hidden_types.0.insert(def_id, hidden_ty); } } -fn get_concrete_opaque_type<'tcx>( - concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>, +fn get_hidden_type<'tcx>( + hidden_types: &DefinitionSiteHiddenTypes<'tcx>, def_id: LocalDefId, ) -> Option<EarlyBinder<'tcx, OpaqueHiddenType<'tcx>>> { - concrete_opaque_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) + hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) } #[derive(Debug)] @@ -173,22 +173,22 @@ struct DefiningUse<'tcx> { } /// This computes the actual hidden types of the opaque types and maps them to their -/// definition sites. Outside of registering the computed concrete types this function +/// definition sites. Outside of registering the computed hidden types this function /// does not mutate the current borrowck state. /// /// While it may fail to infer the hidden type and return errors, we always apply -/// the computed concrete hidden type to all opaque type uses to check whether they +/// the computed hidden type to all opaque type uses to check whether they /// are correct. This is necessary to support non-defining uses of opaques in their /// defining scope. /// /// It also means that this whole function is not really soundness critical as we /// recheck all uses of the opaques regardless. -pub(crate) fn compute_concrete_opaque_types<'tcx>( +pub(crate) fn compute_definition_site_hidden_types<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>, constraints: &MirTypeckRegionConstraints<'tcx>, location_map: Rc<DenseLocationMap>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) -> Vec<DeferredOpaqueTypeError<'tcx>> { let mut errors = Vec::new(); @@ -201,8 +201,7 @@ pub(crate) fn compute_concrete_opaque_types<'tcx>( // We start by checking each use of an opaque type during type check and // check whether the generic arguments of the opaque type are fully // universal, if so, it's a defining use. - let defining_uses = - collect_defining_uses(&mut rcx, concrete_opaque_types, opaque_types, &mut errors); + let defining_uses = collect_defining_uses(&mut rcx, hidden_types, opaque_types, &mut errors); // We now compute and apply member constraints for all regions in the hidden // types of each defining use. This mutates the region values of the `rcx` which @@ -210,11 +209,11 @@ pub(crate) fn compute_concrete_opaque_types<'tcx>( apply_member_constraints(&mut rcx, &defining_uses); // After applying member constraints, we now check whether all member regions ended - // up equal to one of their choice regions and compute the actual concrete type of + // up equal to one of their choice regions and compute the actual hidden type of // the opaque type definition. This is stored in the `root_cx`. - compute_concrete_types_from_defining_uses( + compute_definition_site_hidden_types_from_defining_uses( &rcx, - concrete_opaque_types, + hidden_types, &defining_uses, &mut errors, ); @@ -224,7 +223,7 @@ pub(crate) fn compute_concrete_opaque_types<'tcx>( #[instrument(level = "debug", skip_all, ret)] fn collect_defining_uses<'tcx>( rcx: &mut RegionCtxt<'_, 'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>, ) -> Vec<DefiningUse<'tcx>> { @@ -244,9 +243,9 @@ fn collect_defining_uses<'tcx>( // with `TypingMode::Borrowck`. if infcx.tcx.use_typing_mode_borrowck() { match err { - NonDefiningUseReason::Tainted(guar) => add_concrete_opaque_type( + NonDefiningUseReason::Tainted(guar) => add_hidden_type( infcx.tcx, - concrete_opaque_types, + hidden_types, opaque_type_key.def_id, OpaqueHiddenType::new_error(infcx.tcx, guar), ), @@ -277,9 +276,9 @@ fn collect_defining_uses<'tcx>( defining_uses } -fn compute_concrete_types_from_defining_uses<'tcx>( +fn compute_definition_site_hidden_types_from_defining_uses<'tcx>( rcx: &RegionCtxt<'_, 'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, defining_uses: &[DefiningUse<'tcx>], errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>, ) { @@ -358,9 +357,9 @@ fn compute_concrete_types_from_defining_uses<'tcx>( }, )); } - add_concrete_opaque_type( + add_hidden_type( tcx, - concrete_opaque_types, + hidden_types, opaque_type_key.def_id, OpaqueHiddenType { span: hidden_type.span, ty }, ); @@ -489,20 +488,20 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for ToArgRegionsFolder<'_, 'tcx> { /// /// It does this by equating the hidden type of each use with the instantiated final /// hidden type of the opaque. -pub(crate) fn apply_computed_concrete_opaque_types<'tcx>( +pub(crate) fn apply_definition_site_hidden_types<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, universal_regions: &UniversalRegions<'tcx>, region_bound_pairs: &RegionBoundPairs<'tcx>, known_type_outlives_obligations: &[ty::PolyTypeOutlivesPredicate<'tcx>], constraints: &mut MirTypeckRegionConstraints<'tcx>, - concrete_opaque_types: &mut ConcreteOpaqueTypes<'tcx>, + hidden_types: &mut DefinitionSiteHiddenTypes<'tcx>, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) -> Vec<DeferredOpaqueTypeError<'tcx>> { let tcx = infcx.tcx; let mut errors = Vec::new(); for &(key, hidden_type) in opaque_types { - let Some(expected) = get_concrete_opaque_type(concrete_opaque_types, key.def_id) else { + let Some(expected) = get_hidden_type(hidden_types, key.def_id) else { if !tcx.use_typing_mode_borrowck() { if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() && alias_ty.def_id == key.def_id.to_def_id() @@ -521,12 +520,7 @@ pub(crate) fn apply_computed_concrete_opaque_types<'tcx>( hidden_type.span, "non-defining use in the defining scope with no defining uses", ); - add_concrete_opaque_type( - tcx, - concrete_opaque_types, - key.def_id, - OpaqueHiddenType::new_error(tcx, guar), - ); + add_hidden_type(tcx, hidden_types, key.def_id, OpaqueHiddenType::new_error(tcx, guar)); continue; }; @@ -566,18 +560,13 @@ pub(crate) fn apply_computed_concrete_opaque_types<'tcx>( "equating opaque types", ), ) { - add_concrete_opaque_type( - tcx, - concrete_opaque_types, - key.def_id, - OpaqueHiddenType::new_error(tcx, guar), - ); + add_hidden_type(tcx, hidden_types, key.def_id, OpaqueHiddenType::new_error(tcx, guar)); } } errors } -/// In theory `apply_concrete_opaque_types` could introduce new uses of opaque types. +/// In theory `apply_definition_site_hidden_types` could introduce new uses of opaque types. /// We do not check these new uses so this could be unsound. /// /// We detect any new uses and simply delay a bug if they occur. If this results in @@ -682,13 +671,6 @@ impl<'tcx> InferCtxt<'tcx> { /// /// (*) C1 and C2 were introduced in the comments on /// `register_member_constraints`. Read that comment for more context. - /// - /// # Parameters - /// - /// - `def_id`, the `impl Trait` type - /// - `args`, the args used to instantiate this opaque type - /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of - /// `opaque_defn.concrete_ty` #[instrument(level = "debug", skip(self))] fn infer_opaque_definition_from_instantiation( &self, diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs index cd4e9683f2d..21c11e12873 100644 --- a/compiler/rustc_borrowck/src/root_cx.rs +++ b/compiler/rustc_borrowck/src/root_cx.rs @@ -12,12 +12,12 @@ use smallvec::SmallVec; use crate::consumers::BorrowckConsumer; use crate::nll::compute_closure_requirements_modulo_opaques; use crate::region_infer::opaque_types::{ - apply_computed_concrete_opaque_types, clone_and_resolve_opaque_types, - compute_concrete_opaque_types, detect_opaque_types_added_while_handling_opaque_types, + apply_definition_site_hidden_types, clone_and_resolve_opaque_types, + compute_definition_site_hidden_types, detect_opaque_types_added_while_handling_opaque_types, }; use crate::type_check::{Locations, constraint_conversion}; use crate::{ - ClosureRegionRequirements, CollectRegionConstraintsResult, ConcreteOpaqueTypes, + ClosureRegionRequirements, CollectRegionConstraintsResult, DefinitionSiteHiddenTypes, PropagatedBorrowCheckResults, borrowck_check_region_constraints, borrowck_collect_region_constraints, }; @@ -27,7 +27,7 @@ use crate::{ pub(super) struct BorrowCheckRootCtxt<'tcx> { pub tcx: TyCtxt<'tcx>, root_def_id: LocalDefId, - concrete_opaque_types: ConcreteOpaqueTypes<'tcx>, + hidden_types: DefinitionSiteHiddenTypes<'tcx>, /// The region constraints computed by [borrowck_collect_region_constraints]. This uses /// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before /// their parents. @@ -49,7 +49,7 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { BorrowCheckRootCtxt { tcx, root_def_id, - concrete_opaque_types: Default::default(), + hidden_types: Default::default(), collect_region_constraints_results: Default::default(), propagated_borrowck_results: Default::default(), tainted_by_errors: None, @@ -72,11 +72,11 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { &self.propagated_borrowck_results[&nested_body_def_id].used_mut_upvars } - pub(super) fn finalize(self) -> Result<&'tcx ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { + pub(super) fn finalize(self) -> Result<&'tcx DefinitionSiteHiddenTypes<'tcx>, ErrorGuaranteed> { if let Some(guar) = self.tainted_by_errors { Err(guar) } else { - Ok(self.tcx.arena.alloc(self.concrete_opaque_types)) + Ok(self.tcx.arena.alloc(self.hidden_types)) } } @@ -88,12 +88,12 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { &input.universal_region_relations, &mut input.constraints, ); - input.deferred_opaque_type_errors = compute_concrete_opaque_types( + input.deferred_opaque_type_errors = compute_definition_site_hidden_types( &input.infcx, &input.universal_region_relations, &input.constraints, Rc::clone(&input.location_map), - &mut self.concrete_opaque_types, + &mut self.hidden_types, &opaque_types, ); per_body_info.push((num_entries, opaque_types)); @@ -103,14 +103,14 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> { self.collect_region_constraints_results.values_mut().zip(per_body_info) { if input.deferred_opaque_type_errors.is_empty() { - input.deferred_opaque_type_errors = apply_computed_concrete_opaque_types( + input.deferred_opaque_type_errors = apply_definition_site_hidden_types( &input.infcx, &input.body_owned, &input.universal_region_relations.universal_regions, &input.region_bound_pairs, &input.known_type_outlives_obligations, &mut input.constraints, - &mut self.concrete_opaque_types, + &mut self.hidden_types, &opaque_types, ); } diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 2627ed899a9..aece0bda346 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -230,8 +230,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { location: impl NormalizeLocation, ) -> Ty<'tcx> { let tcx = self.tcx(); + let body = self.body; + + let cause = ObligationCause::misc( + location.to_locations().span(body), + body.source.def_id().expect_local(), + ); + if self.infcx.next_trait_solver() { - let body = self.body; let param_env = self.infcx.param_env; // FIXME: Make this into a real type op? self.fully_perform_op( @@ -241,10 +247,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { |ocx| { let structurally_normalize = |ty| { ocx.structurally_normalize_ty( - &ObligationCause::misc( - location.to_locations().span(body), - body.source.def_id().expect_local(), - ), + &cause, param_env, ty, ) @@ -253,6 +256,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tail = tcx.struct_tail_raw( ty, + &cause, structurally_normalize, || {}, ); @@ -265,7 +269,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } else { let mut normalize = |ty| self.normalize(ty, location); - let tail = tcx.struct_tail_raw(ty, &mut normalize, || {}); + let tail = tcx.struct_tail_raw(ty, &cause, &mut normalize, || {}); normalize(tail) } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c5447fe1e9..606d3d95d9e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -505,6 +505,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let mut constraints = Default::default(); let mut liveness_constraints = LivenessValues::without_specific_points(Rc::new(DenseLocationMap::new(promoted_body))); + let mut deferred_closure_requirements = Default::default(); // Don't try to add borrow_region facts for the promoted MIR as they refer // to the wrong locations. @@ -512,6 +513,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { mem::swap(this.polonius_facts, polonius_facts); mem::swap(&mut this.constraints.outlives_constraints, &mut constraints); mem::swap(&mut this.constraints.liveness_constraints, &mut liveness_constraints); + mem::swap(this.deferred_closure_requirements, &mut deferred_closure_requirements); }; swap_constraints(self); @@ -536,6 +538,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } self.constraints.outlives_constraints.push(constraint) } + + // If there are nested bodies in promoteds, we also need to update their + // location to something in the actual body, not the promoted. + // + // We don't update the constraint categories of the resulting constraints + // as returns in nested bodies are a proper return, even if that nested body + // is in a promoted. + for (closure_def_id, args, _locations) in deferred_closure_requirements { + self.deferred_closure_requirements.push((closure_def_id, args, locations)); + } + // If the region is live at least one location in the promoted MIR, // then add a liveness constraint to the main MIR for this region // at the location provided as an argument to this method @@ -1487,9 +1500,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { unsize_to: None, }, ); - } else if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) = + } else if let ty::Dynamic(src_tty, _src_lt) = *self.struct_tail(src.ty, location).kind() - && let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = + && let ty::Dynamic(dst_tty, dst_lt) = *self.struct_tail(dst.ty, location).kind() && src_tty.principal().is_some() && dst_tty.principal().is_some() @@ -1511,7 +1524,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME: Once we disallow casting `*const dyn Trait + 'short` // to `*const dyn Trait + 'long`, then this can just be `src_lt`. dst_lt, - ty::Dyn, ); let dst_obj = Ty::new_dynamic( tcx, @@ -1519,7 +1531,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { &dst_tty.without_auto_traits().collect::<Vec<_>>(), ), dst_lt, - ty::Dyn, ); debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj); @@ -1631,7 +1642,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::BinaryOp(..) | Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Len(..) | Rvalue::Discriminant(..) | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} } @@ -2201,7 +2211,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::Repeat(..) | Rvalue::Ref(..) | Rvalue::RawPtr(..) - | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::BinaryOp(..) diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index e56b9e641a1..ce9a3ce3f24 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -33,3 +33,8 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end + +[features] +# tidy-alphabetical-start +llvm_enzyme = [] +# tidy-alphabetical-end diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 358c0d3db46..7c1a5f44e16 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -64,6 +64,11 @@ builtin_macros_autodiff_ty_activity = {$act} can not be used for this type builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}` builtin_macros_autodiff_width = autodiff width must fit u32, but is {$width} + +builtin_macros_avoid_att_syntax = avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + +builtin_macros_avoid_intel_syntax = avoid using `.intel_syntax`, Intel syntax is the default + builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s .label = not applicable here .label2 = not a `struct`, `enum` or `union` @@ -138,6 +143,8 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values .suggestion = remove the value +builtin_macros_duplicate_macro_attribute = duplicated attribute + builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time @@ -231,6 +238,8 @@ builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struc builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field +builtin_macros_incomplete_include = include macro expected single expression in source + builtin_macros_multiple_default_attrs = multiple `#[default]` attributes .note = only one `#[default]` attribute is needed .label = `#[default]` used here @@ -294,3 +303,5 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal .label = not a trait .str_lit = try using `#[derive({$sym})]` .other = for example, write `#[derive(Debug)]` for `Debug` + +builtin_macros_unnameable_test_items = cannot test inner items diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 86b8e1ff8db..ae62b5ea2a0 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,4 +1,3 @@ -use lint::BuiltinLintDiag; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; @@ -352,7 +351,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".intel_syntax"), ecx.current_expansion.lint_node_id, - BuiltinLintDiag::AvoidUsingIntelSyntax, + errors::AvoidIntelSyntax, ); } if template_str.contains(".att_syntax") { @@ -360,7 +359,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".att_syntax"), ecx.current_expansion.lint_node_id, - BuiltinLintDiag::AvoidUsingAttSyntax, + errors::AvoidAttSyntax, ); } } diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 48d0795af5e..ddc59bfe141 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -209,7 +209,8 @@ mod llvm_enzyme { mut item: Annotatable, mode: DiffMode, ) -> Vec<Annotatable> { - if cfg!(not(llvm_enzyme)) { + // FIXME(bjorn3) maybe have the backend directly tell if autodiff is supported? + if cfg!(not(feature = "llvm_enzyme")) { ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span }); return vec![item]; } @@ -376,8 +377,7 @@ mod llvm_enzyme { (ast::AttrKind::Normal(a), ast::AttrKind::Normal(b)) => { let a = &a.item.path; let b = &b.item.path; - a.segments.len() == b.segments.len() - && a.segments.iter().zip(b.segments.iter()).all(|(a, b)| a.ident == b.ident) + a.segments.iter().eq_by(&b.segments, |a, b| a.ident == b.ident) } _ => false, } diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index dd8f0e46a0e..63342880b09 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -51,43 +51,4 @@ pub(crate) fn expand_deriving_const_param_ty( }; trait_def.expand(cx, mitem, item, push); - - let trait_def = TraitDef { - span, - path: path_std!(marker::UnsizedConstParamTy), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: false, - additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], - supports_unions: false, - methods: Vec::new(), - associated_types: Vec::new(), - is_const, - is_staged_api_crate: cx.ecfg.features.staged_api(), - }; - - trait_def.expand(cx, mitem, item, push); -} - -pub(crate) fn expand_deriving_unsized_const_param_ty( - cx: &ExtCtxt<'_>, - span: Span, - mitem: &MetaItem, - item: &Annotatable, - push: &mut dyn FnMut(Annotatable), - is_const: bool, -) { - let trait_def = TraitDef { - span, - path: path_std!(marker::UnsizedConstParamTy), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: false, - additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], - supports_unions: false, - methods: Vec::new(), - associated_types: Vec::new(), - is_const, - is_staged_api_crate: cx.ecfg.features.staged_api(), - }; - - trait_def.expand(cx, mitem, item, push); } diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 75db5d77783..5b378de8bbd 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -356,21 +356,14 @@ fn contains_maybe_sized_bound(bounds: &[GenericBound]) -> bool { bounds.iter().any(is_maybe_sized_bound) } -fn path_segment_is_exact_match(path_segments: &[ast::PathSegment], syms: &[Symbol]) -> bool { - path_segments.iter().zip(syms).all(|(segment, &symbol)| segment.ident.name == symbol) -} - fn is_sized_marker(path: &ast::Path) -> bool { const CORE_UNSIZE: [Symbol; 3] = [sym::core, sym::marker, sym::Sized]; const STD_UNSIZE: [Symbol; 3] = [sym::std, sym::marker, sym::Sized]; - if path.segments.len() == 4 && path.is_global() { - path_segment_is_exact_match(&path.segments[1..], &CORE_UNSIZE) - || path_segment_is_exact_match(&path.segments[1..], &STD_UNSIZE) - } else if path.segments.len() == 3 { - path_segment_is_exact_match(&path.segments, &CORE_UNSIZE) - || path_segment_is_exact_match(&path.segments, &STD_UNSIZE) + let segments = || path.segments.iter().map(|segment| segment.ident.name); + if path.is_global() { + segments().skip(1).eq(CORE_UNSIZE) || segments().skip(1).eq(STD_UNSIZE) } else { - *path == sym::Sized + segments().eq(CORE_UNSIZE) || segments().eq(STD_UNSIZE) || *path == sym::Sized } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 54e8f750337..0993fdc5be4 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -3,9 +3,29 @@ use rustc_errors::{ Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans, Subdiagnostic, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; +#[derive(LintDiagnostic)] +#[diag(builtin_macros_avoid_intel_syntax)] +pub(crate) struct AvoidIntelSyntax; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_avoid_att_syntax)] +pub(crate) struct AvoidAttSyntax; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_incomplete_include)] +pub(crate) struct IncompleteInclude; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_unnameable_test_items)] +pub(crate) struct UnnameableTestItems; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_duplicate_macro_attribute)] +pub(crate) struct DuplicateMacroAttribute; + #[derive(Diagnostic)] #[diag(builtin_macros_requires_cfg_pattern)] pub(crate) struct RequiresCfgPattern { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index d70888205a5..bffc0407e81 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -768,7 +768,7 @@ fn report_missing_placeholders( if !found_foreign && invalid_refs.is_empty() { // Show example if user didn't use any format specifiers - let show_example = used.iter().all(|used| !used); + let show_example = !used.contains(&true); if !show_example { if unused.len() > 1 { diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index f14b1920722..96bece2a368 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -85,7 +85,7 @@ impl AllocFnFactory<'_, '_> { body, define_opaque: None, })); - let item = self.cx.item(self.span, self.attrs(), kind); + let item = self.cx.item(self.span, self.attrs(method), kind); self.cx.stmt_item(self.ty_span, item) } @@ -100,8 +100,18 @@ impl AllocFnFactory<'_, '_> { self.cx.expr_call(self.ty_span, method, args) } - fn attrs(&self) -> AttrVec { - thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)] + fn attrs(&self, method: &AllocatorMethod) -> AttrVec { + let alloc_attr = match method.name { + sym::alloc => sym::rustc_allocator, + sym::dealloc => sym::rustc_deallocator, + sym::realloc => sym::rustc_reallocator, + sym::alloc_zeroed => sym::rustc_allocator_zeroed, + _ => unreachable!("Unknown allocator method!"), + }; + thin_vec![ + self.cx.attr_word(sym::rustc_std_internal_symbol, self.span), + self.cx.attr_word(alloc_attr, self.span) + ] } fn arg_ty(&self, input: &AllocatorMethodInput, args: &mut ThinVec<Param>) -> Box<Expr> { diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 1bcea95fbb7..57cf62ea612 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -11,6 +11,7 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] +#![feature(iter_order_by)] #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(rustdoc_internals)] @@ -129,7 +130,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Clone: clone::expand_deriving_clone, Copy: bounds::expand_deriving_copy, ConstParamTy: bounds::expand_deriving_const_param_ty, - UnsizedConstParamTy: bounds::expand_deriving_unsized_const_param_ty, Debug: debug::expand_deriving_debug, Default: default::expand_deriving_default, Eq: eq::expand_deriving_eq, diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 11b868f81a9..16adaab15c5 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -12,7 +12,6 @@ use rustc_expand::base::{ DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path, }; use rustc_expand::module::DirOwnership; -use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::lexer::StripTokens; use rustc_parse::parser::ForceCollect; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; @@ -159,7 +158,7 @@ pub(crate) fn expand_include<'cx>( INCOMPLETE_INCLUDE, p.token.span, self.node_id, - BuiltinLintDiag::IncompleteInclude, + errors::IncompleteInclude, ); } Some(expr) diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index a9d91f77560..82c59d5a3a2 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -11,7 +11,6 @@ use rustc_errors::DiagCtxtHandle; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; -use rustc_lint_defs::BuiltinLintDiag; use rustc_session::Session; use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; @@ -64,8 +63,8 @@ pub fn inject( if sess.is_test_crate() { let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) { - (PanicStrategy::Abort, true) => PanicStrategy::Abort, - (PanicStrategy::Abort, false) => { + (PanicStrategy::Abort | PanicStrategy::ImmediateAbort, true) => panic_strategy, + (PanicStrategy::Abort | PanicStrategy::ImmediateAbort, false) => { if panic_strategy == platform_panic_strategy { // Silently allow compiling with panic=abort on these platforms, // but with old behavior (abort if a test fails). @@ -165,7 +164,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { UNNAMEABLE_TEST_ITEMS, attr.span, i.id, - BuiltinLintDiag::UnnameableTestItems, + errors::UnnameableTestItems, ); } } @@ -288,10 +287,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> Box<ast::Item> { let ecx = &cx.ext_cx; let test_ident = Ident::new(sym::test, sp); - let runner_name = match cx.panic_strategy { - PanicStrategy::Unwind => "test_main_static", - PanicStrategy::Abort => "test_main_static_abort", - }; + let runner_name = + if cx.panic_strategy.unwinds() { "test_main_static" } else { "test_main_static_abort" }; // test::test_main_static(...) let mut test_runner = cx.test_runner.clone().unwrap_or_else(|| { diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 3a4585d5be9..e26f31dce67 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -5,7 +5,6 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt}; use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; -use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; use rustc_parse::{exp, parser}; use rustc_session::errors::report_lit_error; @@ -49,7 +48,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, DUPLICATE_MACRO_ATTRIBUTES, attr.span, ecx.current_expansion.lint_node_id, - BuiltinLintDiag::DuplicateMacroAttribute, + errors::DuplicateMacroAttribute, ); } } diff --git a/compiler/rustc_codegen_cranelift/src/abi/comments.rs b/compiler/rustc_codegen_cranelift/src/abi/comments.rs index c74efeb59f3..d1b2b9a502a 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/comments.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/comments.rs @@ -89,7 +89,7 @@ pub(super) fn add_local_place_comments<'tcx>( format!("{:?}", local), format!("{:?}", ty), size.bytes(), - align.abi.bytes(), + align.bytes(), if extra.is_empty() { "" } else { " " }, extra, )); diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 7d0731c77bd..29ee46194de 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -715,7 +715,7 @@ pub(crate) fn codegen_drop<'tcx>( fx.bcx.ins().jump(ret_block, &[]); } else { match ty.kind() { - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { // IN THIS ARM, WE HAVE: // ty = *mut (dyn Trait) // which is: exists<T> ( *mut T, Vtable<T: Trait> ) diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 2031842062d..7a909a740b0 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -233,7 +233,7 @@ pub(super) fn from_casted_value<'tcx>( // It may also be smaller for example when the type is a wrapper around an integer with a // larger alignment than the integer. std::cmp::max(abi_param_size, layout_size), - u32::try_from(layout.align.abi.bytes()).unwrap(), + u32::try_from(layout.align.bytes()).unwrap(), ); let mut block_params_iter = block_params.iter().copied(); for (offset, _) in abi_params { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 3a28dd7e73c..2cc5b82ddd3 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -834,12 +834,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: fx.bcx.ins().nop(); } } - Rvalue::Len(place) => { - let place = codegen_place(fx, place); - let usize_layout = fx.layout_of(fx.tcx.types.usize); - let len = codegen_array_len(fx, place); - lval.write_cvalue(fx, CValue::by_val(len, usize_layout)); - } Rvalue::ShallowInitBox(ref operand, content_ty) => { let content_ty = fx.monomorphize(content_ty); let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty)); @@ -852,7 +846,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: let layout = fx.layout_of(fx.monomorphize(ty)); let val = match null_op { NullOp::SizeOf => layout.size.bytes(), - NullOp::AlignOf => layout.align.abi.bytes(), + NullOp::AlignOf => layout.align.bytes(), NullOp::OffsetOf(fields) => fx .tcx .offset_of_subfield( diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 2fbe5c02802..81b1814605a 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -439,7 +439,10 @@ pub(crate) struct FullyMonomorphizedLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); impl<'tcx> LayoutOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.0.sess.dcx().span_fatal(span, err.to_string()) } else { self.0 @@ -458,7 +461,9 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> { span: Span, fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { - if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err { + if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) = + err + { self.0.sess.dcx().emit_fatal(Spanned { span, node: err }) } else { match fn_abi_request { diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs index 286e02b986b..4c438742f3d 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -304,7 +304,7 @@ impl DebugContext { entry.set(gimli::DW_AT_decl_file, AttributeValue::FileIndex(Some(file_id))); entry.set(gimli::DW_AT_decl_line, AttributeValue::Udata(line)); - entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(static_layout.align.abi.bytes())); + entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(static_layout.align.bytes())); let mut expr = Expression::new(); expr.op_addr(address_for_data(data_id)); diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs index 25b922c8be4..0d49f32373c 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -166,7 +166,7 @@ impl DebugContext { let tuple_entry = self.dwarf.unit.get_mut(tuple_type_id); tuple_entry.set(gimli::DW_AT_name, AttributeValue::StringRef(self.dwarf.strings.add(name))); tuple_entry.set(gimli::DW_AT_byte_size, AttributeValue::Udata(layout.size.bytes())); - tuple_entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(layout.align.abi.bytes())); + tuple_entry.set(gimli::DW_AT_alignment, AttributeValue::Udata(layout.align.bytes())); for (i, (ty, dw_ty)) in components.into_iter().enumerate() { let member_id = self.dwarf.unit.add(tuple_type_id, gimli::DW_TAG_member); @@ -178,9 +178,7 @@ impl DebugContext { member_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); member_entry.set( gimli::DW_AT_alignment, - AttributeValue::Udata( - FullyMonomorphizedLayoutCx(tcx).layout_of(ty).align.abi.bytes(), - ), + AttributeValue::Udata(FullyMonomorphizedLayoutCx(tcx).layout_of(ty).align.bytes()), ); member_entry.set( gimli::DW_AT_data_member_location, diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 203b443269f..1306c6aa517 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -42,7 +42,10 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> { impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.tcx.sess.dcx().span_fatal(span, err.to_string()) } else { self.tcx diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 8e34436fb5e..5fd7c4d4f41 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -165,6 +165,10 @@ impl CodegenBackend for CraneliftCodegenBackend { "" } + fn name(&self) -> &'static str { + "cranelift" + } + fn init(&self, sess: &Session) { use rustc_session::config::{InstrumentCoverage, Lto}; match sess.lto() { diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 2aee0b2e974..d994f3e32ec 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -30,9 +30,7 @@ pub(crate) fn unsized_info<'tcx>( fx.pointer_type, len.try_to_target_usize(fx.tcx).expect("expected monomorphic const in codegen") as i64, ), - (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) - if src_dyn_kind == target_dyn_kind => - { + (&ty::Dynamic(data_a, _), &ty::Dynamic(data_b, _)) => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); let b_principal_def_id = data_b.principal_def_id(); @@ -169,7 +167,7 @@ pub(crate) fn size_and_align_of<'tcx>( if layout.is_sized() { return ( fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64), - fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64), + fx.bcx.ins().iconst(fx.pointer_type, layout.align.bytes() as i64), ); } @@ -188,7 +186,7 @@ pub(crate) fn size_and_align_of<'tcx>( // times the unit size. ( fx.bcx.ins().imul_imm(info.unwrap(), unit.size.bytes() as i64), - fx.bcx.ins().iconst(fx.pointer_type, unit.align.abi.bytes() as i64), + fx.bcx.ins().iconst(fx.pointer_type, unit.align.bytes() as i64), ) } ty::Foreign(_) => { @@ -226,7 +224,7 @@ pub(crate) fn size_and_align_of<'tcx>( let unsized_offset_unadjusted = layout.fields.offset(i).bytes(); let unsized_offset_unadjusted = fx.bcx.ins().iconst(fx.pointer_type, unsized_offset_unadjusted as i64); - let sized_align = layout.align.abi.bytes(); + let sized_align = layout.align.bytes(); let sized_align = fx.bcx.ins().iconst(fx.pointer_type, sized_align as i64); // Recurse to get the size of the dynamically sized field (must be diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 9d73f200afe..04e10cf1708 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -383,7 +383,7 @@ impl<'tcx> CPlace<'tcx> { let stack_slot = fx.create_stack_slot( u32::try_from(layout.size.bytes()).unwrap(), - u32::try_from(layout.align.abi.bytes()).unwrap(), + u32::try_from(layout.align.bytes()).unwrap(), ); CPlace { inner: CPlaceInner::Addr(stack_slot, None), layout } } @@ -641,8 +641,8 @@ impl<'tcx> CPlace<'tcx> { let size = dst_layout.size.bytes(); // `emit_small_memory_copy` uses `u8` for alignments, just use the maximum // alignment that fits in a `u8` if the actual alignment is larger. - let src_align = src_layout.align.abi.bytes().try_into().unwrap_or(128); - let dst_align = dst_layout.align.abi.bytes().try_into().unwrap_or(128); + let src_align = src_layout.align.bytes().try_into().unwrap_or(128); + let dst_align = dst_layout.align.bytes().try_into().unwrap_or(128); fx.bcx.emit_small_memory_copy( fx.target_config, to_addr, @@ -909,8 +909,7 @@ pub(crate) fn assert_assignable<'tcx>( ); // fn(&T) -> for<'l> fn(&'l T) is allowed } - (&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => { - // FIXME(dyn-star): Do the right thing with DynKinds + (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { for (from, to) in from_traits.iter().zip(to_traits) { let from = fx.tcx.normalize_erasing_late_bound_regions(fx.typing_env(), from); let to = fx.tcx.normalize_erasing_late_bound_regions(fx.typing_env(), to); diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 17e2e028b16..a14881c502c 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -698,8 +698,12 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + InlineAsmRegClass::PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -777,8 +781,12 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => { cx.type_vector(cx.type_i32(), 4) } - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + InlineAsmRegClass::PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index e9d72e457a0..e8672f49580 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -15,9 +15,9 @@ use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::DebugInfo; use rustc_span::Symbol; +use rustc_target::spec::RelocModel; #[cfg(feature = "master")] use rustc_target::spec::SymbolVisibility; -use rustc_target::spec::{PanicStrategy, RelocModel}; use crate::builder::Builder; use crate::context::CodegenCx; @@ -101,7 +101,7 @@ pub fn compile_codegen_unit( // Instantiate monomorphizations without filling out definitions yet... let context = new_context(tcx); - if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + if tcx.sess.panic_strategy().unwinds() { context.add_command_line_option("-fexceptions"); context.add_driver_option("-fexceptions"); } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f7a7a3f8c7e..5657620879c 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1383,6 +1383,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { _src_align: Align, size: RValue<'gcc>, flags: MemFlags, + _tt: Option<rustc_ast::expand::typetree::FncTree>, // Autodiff TypeTrees are LLVM-only, ignored in GCC backend ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported"); let size = self.intcast(size, self.type_size_t(), false); diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 665cf22ddba..c9ae96777de 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -147,7 +147,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let layout = tcx .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(rust_type)) .unwrap(); - let align = layout.align.abi.bytes(); + let align = layout.align.bytes(); // For types with size 1, the alignment can be 1 and only 1 // So, we can skip the call to ``get_aligned`. // In the future, we can add a GCC API to query the type align, @@ -186,9 +186,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { (i128_type, u128_type) } else { /*let layout = tcx.layout_of(ParamEnv::reveal_all().and(tcx.types.i128)).unwrap(); - let i128_align = layout.align.abi.bytes(); + let i128_align = layout.align.bytes(); let layout = tcx.layout_of(ParamEnv::reveal_all().and(tcx.types.u128)).unwrap(); - let u128_align = layout.align.abi.bytes();*/ + let u128_align = layout.align.bytes();*/ // TODO(antoyo): re-enable the alignment when libgccjit fixed the issue in // gcc_jit_context_new_array_constructor (it should not use reinterpret_cast). @@ -529,7 +529,10 @@ impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::InvalidSimd { .. } + | LayoutError::ReferencesError(_) = err + { self.tcx.dcx().emit_fatal(respan(span, err.into_diagnostic())) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) @@ -545,7 +548,9 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { span: Span, fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { - if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err { + if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) = + err + { self.tcx.dcx().emit_fatal(respan(span, err)) } else { match fn_abi_request { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index eb0a5336a1f..99a4f9b9f7e 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -29,7 +29,6 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::{ArgAbi, PassMode}; -use rustc_target::spec::PanicStrategy; #[cfg(feature = "master")] use crate::abi::FnAbiGccExt; @@ -730,7 +729,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { if self.is_sized_indirect() { OperandValue::Ref(PlaceValue::new_sized(val, self.layout.align.abi)).store(bx, dst) } else if self.is_unsized_indirect() { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + bug!("unsized `ArgAbi` cannot be stored"); } else if let PassMode::Cast { ref cast, .. } = self.mode { // FIXME(eddyb): Figure out when the simpler Store is safe, clang // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. @@ -771,6 +770,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { scratch_align, bx.const_usize(self.layout.size.bytes()), MemFlags::empty(), + None, ); bx.lifetime_end(scratch, scratch_size); @@ -797,12 +797,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { meta_attrs: Some(_), .. } => { - let place_val = PlaceValue { - llval: next(), - llextra: Some(next()), - align: self.layout.align.abi, - }; - OperandValue::Ref(place_val).store(bx, dst); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) | PassMode::Indirect { meta_attrs: None, .. } @@ -1339,7 +1334,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( _catch_func: RValue<'gcc>, dest: PlaceRef<'tcx, RValue<'gcc>>, ) { - if bx.sess().panic_strategy() == PanicStrategy::Abort { + if !bx.sess().panic_strategy().unwinds() { bx.call(bx.type_void(), None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index f76f933cad4..ec7eab8489a 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -184,6 +184,10 @@ impl CodegenBackend for GccCodegenBackend { crate::DEFAULT_LOCALE_RESOURCE } + fn name(&self) -> &'static str { + "gcc" + } + fn init(&self, _sess: &Session) { #[cfg(feature = "master")] { diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 2d11628250c..67bd1e59bb0 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -46,5 +46,6 @@ tracing = "0.1" [features] # tidy-alphabetical-start check_only = ["rustc_llvm/check_only"] +llvm_enzyme = [] # tidy-alphabetical-end diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index ac7583f5666..1703cab942b 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -215,9 +215,9 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); OperandValue::Ref(PlaceValue::new_sized(val, align)).store(bx, dst); } - // Unsized indirect qrguments + // Unsized indirect arguments cannot be stored PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Cast { cast, pad_i32: _ } => { // The ABI mandates that the value is passed as a different struct representation. @@ -246,6 +246,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { scratch_align, bx.const_usize(copy_bytes), MemFlags::empty(), + None, ); bx.lifetime_end(llscratch, scratch_size); } @@ -272,12 +273,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { - let place_val = PlaceValue { - llval: next(), - llextra: Some(next()), - align: self.layout.align.abi, - }; - OperandValue::Ref(place_val).store(bx, dst); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } @@ -543,7 +539,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { // If the declaration has an associated instance, compute extra attributes based on that. if let Some(instance) = instance { - llfn_attrs_from_instance(cx, llfn, instance); + llfn_attrs_from_instance( + cx, + cx.tcx, + llfn, + &cx.tcx.codegen_instance_attrs(instance.def), + Some(instance), + ); } } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index df3e49279d9..896d6755c75 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -5,15 +5,17 @@ use rustc_ast::expand::allocator::{ }; use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; use rustc_middle::bug; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, OomStrategy}; +use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; -use smallvec::SmallVec; +use crate::attributes::llfn_attrs_from_instance; use crate::builder::SBuilder; use crate::declare::declare_simple_fn; use crate::llvm::{self, FALSE, TRUE, Type, Value}; -use crate::{SimpleCx, attributes, debuginfo, llvm_util}; +use crate::{SimpleCx, attributes, debuginfo}; pub(crate) unsafe fn codegen( tcx: TyCtxt<'_>, @@ -58,7 +60,26 @@ pub(crate) unsafe fn codegen( let from_name = mangle_internal_symbol(tcx, &global_fn_name(method.name)); let to_name = mangle_internal_symbol(tcx, &default_fn_name(method.name)); - create_wrapper_function(tcx, &cx, &from_name, Some(&to_name), &args, output, false); + let alloc_attr_flag = match method.name { + sym::alloc => CodegenFnAttrFlags::ALLOCATOR, + sym::dealloc => CodegenFnAttrFlags::DEALLOCATOR, + sym::realloc => CodegenFnAttrFlags::REALLOCATOR, + sym::alloc_zeroed => CodegenFnAttrFlags::ALLOCATOR_ZEROED, + _ => unreachable!("Unknown allocator method!"), + }; + + let mut attrs = CodegenFnAttrs::new(); + attrs.flags |= alloc_attr_flag; + create_wrapper_function( + tcx, + &cx, + &from_name, + Some(&to_name), + &args, + output, + false, + &attrs, + ); } } @@ -71,6 +92,7 @@ pub(crate) unsafe fn codegen( &[usize, usize], // size, align None, true, + &CodegenFnAttrs::new(), ); unsafe { @@ -92,6 +114,7 @@ pub(crate) unsafe fn codegen( &[], None, false, + &CodegenFnAttrs::new(), ); } @@ -138,6 +161,7 @@ fn create_wrapper_function( args: &[&Type], output: Option<&Type>, no_return: bool, + attrs: &CodegenFnAttrs, ) { let ty = cx.type_func(args, output.unwrap_or_else(|| cx.type_void())); let llfn = declare_simple_fn( @@ -149,18 +173,7 @@ fn create_wrapper_function( ty, ); - let mut attrs = SmallVec::<[_; 2]>::new(); - - let target_cpu = llvm_util::target_cpu(tcx.sess); - let target_cpu_attr = llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu); - - let tune_cpu_attr = llvm_util::tune_cpu(tcx.sess) - .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu)); - - attrs.push(target_cpu_attr); - attrs.extend(tune_cpu_attr); - - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &attrs); + llfn_attrs_from_instance(cx, tcx, llfn, attrs, None); let no_return = if no_return { // -> ! DIFlagNoReturn @@ -171,12 +184,6 @@ fn create_wrapper_function( None }; - if tcx.sess.must_emit_unwind_tables() { - let uwtable = - attributes::uwtable_attr(cx.llcx, tcx.sess.opts.unstable_opts.use_sync_unwind); - attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]); - } - let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, c"entry".as_ptr()) }; let mut bx = SBuilder::build(&cx, llbb); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 38c1d3b53e8..cc09fa5b69b 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -240,6 +240,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { constraints.extend_from_slice(&[ + "~{fflags}".to_string(), "~{vtype}".to_string(), "~{vl}".to_string(), "~{vxsat}".to_string(), @@ -339,8 +340,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { attrs.push(llvm::AttributeKind::WillReturn.create_attr(self.cx.llcx)); } else if options.contains(InlineAsmOptions::NOMEM) { attrs.push(llvm::MemoryEffects::InaccessibleMemOnly.create_attr(self.cx.llcx)); - } else { - // LLVM doesn't have an attribute to represent ReadOnly + SideEffect + } else if options.contains(InlineAsmOptions::READONLY) { + attrs.push(llvm::MemoryEffects::ReadOnlyNotPure.create_attr(self.cx.llcx)); } attributes::apply_to_callsite(result, llvm::AttributePlace::Function, &{ attrs }); @@ -661,7 +662,12 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", PowerPC(PowerPCInlineAsmRegClass::freg) => "f", PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { + PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => "r", @@ -829,7 +835,12 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), PowerPC(PowerPCInlineAsmRegClass::vreg) => cx.type_vector(cx.type_i32(), 4), - PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { + PowerPC( + PowerPCInlineAsmRegClass::cr + | PowerPCInlineAsmRegClass::ctr + | PowerPCInlineAsmRegClass::lr + | PowerPCInlineAsmRegClass::xer, + ) => { unreachable!("clobber-only") } RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 573c51a9539..8070ea0b3e9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -1,20 +1,21 @@ //! Set and unset common attributes on LLVM values. -use rustc_codegen_ssa::traits::*; use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry}; +use rustc_middle::middle::codegen_fn_attrs::{ + CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, +}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector}; use smallvec::SmallVec; -use crate::context::CodegenCx; +use crate::context::SimpleCx; use crate::errors::SanitizerMemtagRequiresMte; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, AllocKindFlags, Attribute, AttributeKind, AttributePlace, MemoryEffects}; use crate::value::Value; -use crate::{attributes, llvm_util}; +use crate::{Session, attributes, llvm_util}; pub(crate) fn apply_to_llfn(llfn: &Value, idx: AttributePlace, attrs: &[&Attribute]) { if !attrs.is_empty() { @@ -30,18 +31,19 @@ pub(crate) fn apply_to_callsite(callsite: &Value, idx: AttributePlace, attrs: &[ /// Get LLVM attribute for the provided inline heuristic. pub(crate) fn inline_attr<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, ) -> Option<&'ll Attribute> { // `optnone` requires `noinline` - let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id()); + let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, - (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint, + (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, (inline, _) => inline, }; - if !cx.tcx.sess.opts.unstable_opts.inline_llvm { + if !tcx.sess.opts.unstable_opts.inline_llvm { // disable LLVM inlining return Some(AttributeKind::NoInline.create_attr(cx.llcx)); } @@ -51,7 +53,7 @@ pub(crate) fn inline_attr<'ll, 'tcx>( Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)) } InlineAttr::Never => { - if cx.sess().target.arch != "amdgpu" { + if tcx.sess.target.arch != "amdgpu" { Some(AttributeKind::NoInline.create_attr(cx.llcx)) } else { None @@ -63,12 +65,13 @@ pub(crate) fn inline_attr<'ll, 'tcx>( #[inline] fn patchable_function_entry_attrs<'ll>( - cx: &CodegenCx<'ll, '_>, + cx: &SimpleCx<'ll>, + sess: &Session, attr: Option<PatchableFunctionEntry>, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); let patchable_spec = attr.unwrap_or_else(|| { - PatchableFunctionEntry::from_config(cx.tcx.sess.opts.unstable_opts.patchable_function_entry) + PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry) }); let entry = patchable_spec.entry(); let prefix = patchable_spec.prefix(); @@ -91,12 +94,13 @@ fn patchable_function_entry_attrs<'ll>( /// Get LLVM sanitize attributes. #[inline] -pub(crate) fn sanitize_attrs<'ll>( - cx: &CodegenCx<'ll, '_>, +pub(crate) fn sanitize_attrs<'ll, 'tcx>( + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, no_sanitize: SanitizerSet, ) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - let enabled = cx.tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; + let enabled = tcx.sess.opts.unstable_opts.sanitizer - no_sanitize; if enabled.contains(SanitizerSet::ADDRESS) || enabled.contains(SanitizerSet::KERNELADDRESS) { attrs.push(llvm::AttributeKind::SanitizeAddress.create_attr(cx.llcx)); } @@ -114,11 +118,11 @@ pub(crate) fn sanitize_attrs<'ll>( } if enabled.contains(SanitizerSet::MEMTAG) { // Check to make sure the mte target feature is actually enabled. - let features = cx.tcx.global_backend_features(()); + let features = tcx.global_backend_features(()); let mte_feature = features.iter().map(|s| &s[..]).rfind(|n| ["+mte", "-mte"].contains(&&n[..])); if let None | Some("-mte") = mte_feature { - cx.tcx.dcx().emit_err(SanitizerMemtagRequiresMte); + tcx.dcx().emit_err(SanitizerMemtagRequiresMte); } attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx)); @@ -139,9 +143,12 @@ pub(crate) fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option<bool>) llvm::CreateUWTableAttr(llcx, async_unwind) } -pub(crate) fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - let mut fp = cx.sess().target.frame_pointer; - let opts = &cx.sess().opts; +pub(crate) fn frame_pointer_type_attr<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, +) -> Option<&'ll Attribute> { + let mut fp = sess.target.frame_pointer; + let opts = &sess.opts; // "mcount" function relies on stack pointer. // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>. if opts.unstable_opts.instrument_mcount { @@ -156,8 +163,8 @@ pub(crate) fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&' Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value)) } -fn function_return_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - let function_return_attr = match cx.sess().opts.unstable_opts.function_return { +fn function_return_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + let function_return_attr = match sess.opts.unstable_opts.function_return { FunctionReturn::Keep => return None, FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern, }; @@ -167,17 +174,20 @@ fn function_return_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> /// Tell LLVM what instrument function to insert. #[inline] -fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> { +fn instrument_function_attr<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, +) -> SmallVec<[&'ll Attribute; 4]> { let mut attrs = SmallVec::new(); - if cx.sess().opts.unstable_opts.instrument_mcount { + if sess.opts.unstable_opts.instrument_mcount { // Similar to `clang -pg` behavior. Handled by the // `post-inline-ee-instrument` LLVM pass. // The function name varies on platforms. // See test/CodeGen/mcount.c in clang. - let mcount_name = match &cx.sess().target.llvm_mcount_intrinsic { + let mcount_name = match &sess.target.llvm_mcount_intrinsic { Some(llvm_mcount_intrinsic) => llvm_mcount_intrinsic.as_ref(), - None => cx.sess().target.mcount.as_ref(), + None => sess.target.mcount.as_ref(), }; attrs.push(llvm::CreateAttrStringValue( @@ -186,7 +196,7 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attr mcount_name, )); } - if let Some(options) = &cx.sess().opts.unstable_opts.instrument_xray { + if let Some(options) = &sess.opts.unstable_opts.instrument_xray { // XRay instrumentation is similar to __cyg_profile_func_{enter,exit}. // Function prologue and epilogue are instrumented with NOP sleds, // a runtime library later replaces them with detours into tracing code. @@ -217,20 +227,20 @@ fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attr attrs } -fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - if !cx.sess().opts.unstable_opts.no_jump_tables { +fn nojumptables_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + if !sess.opts.unstable_opts.no_jump_tables { return None; } Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true")) } -fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { +fn probestack_attr<'ll, 'tcx>(cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>) -> Option<&'ll Attribute> { // Currently stack probes seem somewhat incompatible with the address // sanitizer and thread sanitizer. With asan we're already protected from // stack overflow anyway so we don't really need stack probes regardless. - if cx - .sess() + if tcx + .sess .opts .unstable_opts .sanitizer @@ -240,22 +250,22 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { } // probestack doesn't play nice either with `-C profile-generate`. - if cx.sess().opts.cg.profile_generate.enabled() { + if tcx.sess.opts.cg.profile_generate.enabled() { return None; } - let attr_value = match cx.sess().target.stack_probes { + let attr_value = match tcx.sess.target.stack_probes { StackProbeType::None => return None, // Request LLVM to generate the probes inline. If the given LLVM version does not support // this, no probe is generated at all (even if the attribute is specified). StackProbeType::Inline => "inline-asm", // Flag our internal `__rust_probestack` function as the stack probe symbol. // This is defined in the `compiler-builtins` crate for each architecture. - StackProbeType::Call => &mangle_internal_symbol(cx.tcx, "__rust_probestack"), + StackProbeType::Call => &mangle_internal_symbol(tcx, "__rust_probestack"), // Pick from the two above based on the LLVM version. StackProbeType::InlineOrCall { min_llvm_version_for_inline } => { if llvm_util::get_version() < min_llvm_version_for_inline { - &mangle_internal_symbol(cx.tcx, "__rust_probestack") + &mangle_internal_symbol(tcx, "__rust_probestack") } else { "inline-asm" } @@ -264,8 +274,8 @@ fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { Some(llvm::CreateAttrStringValue(cx.llcx, "probe-stack", attr_value)) } -fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - let sspattr = match cx.sess().stack_protector() { +fn stackprotector_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + let sspattr = match sess.stack_protector() { StackProtector::None => return None, StackProtector::All => AttributeKind::StackProtectReq, StackProtector::Strong => AttributeKind::StackProtectStrong, @@ -275,33 +285,34 @@ fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { Some(sspattr.create_attr(cx.llcx)) } -fn backchain_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - if cx.sess().target.arch != "s390x" { +fn backchain_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + if sess.target.arch != "s390x" { return None; } - let requested_features = cx.sess().opts.cg.target_feature.split(','); + let requested_features = sess.opts.cg.target_feature.split(','); let found_positive = requested_features.clone().any(|r| r == "+backchain"); if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None } } -pub(crate) fn target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Attribute { - let target_cpu = llvm_util::target_cpu(cx.tcx.sess); +pub(crate) fn target_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> &'ll Attribute { + let target_cpu = llvm_util::target_cpu(sess); llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu) } -pub(crate) fn tune_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { - llvm_util::tune_cpu(cx.tcx.sess) +pub(crate) fn tune_cpu_attr<'ll>(cx: &SimpleCx<'ll>, sess: &Session) -> Option<&'ll Attribute> { + llvm_util::tune_cpu(sess) .map(|tune_cpu| llvm::CreateAttrStringValue(cx.llcx, "tune-cpu", tune_cpu)) } /// Get the `target-features` LLVM attribute. -pub(crate) fn target_features_attr<'ll>( - cx: &CodegenCx<'ll, '_>, +pub(crate) fn target_features_attr<'ll, 'tcx>( + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, function_features: Vec<String>, ) -> Option<&'ll Attribute> { - let global_features = cx.tcx.global_backend_features(()).iter().map(String::as_str); + let global_features = tcx.global_backend_features(()).iter().map(String::as_str); let function_features = function_features.iter().map(String::as_str); let target_features = global_features.chain(function_features).intersperse(",").collect::<String>(); @@ -311,22 +322,22 @@ pub(crate) fn target_features_attr<'ll>( /// Get the `NonLazyBind` LLVM attribute, /// if the codegen options allow skipping the PLT. -pub(crate) fn non_lazy_bind_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { +pub(crate) fn non_lazy_bind_attr<'ll>( + cx: &SimpleCx<'ll>, + sess: &Session, +) -> Option<&'ll Attribute> { // Don't generate calls through PLT if it's not necessary - if !cx.sess().needs_plt() { - Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) - } else { - None - } + if !sess.needs_plt() { Some(AttributeKind::NonLazyBind.create_attr(cx.llcx)) } else { None } } /// Get the default optimizations attrs for a function. #[inline] pub(crate) fn default_optimisation_attrs<'ll>( - cx: &CodegenCx<'ll, '_>, + cx: &SimpleCx<'ll>, + sess: &Session, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); - match cx.sess().opts.optimize { + match sess.opts.optimize { OptLevel::Size => { attrs.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); } @@ -347,17 +358,18 @@ fn create_alloc_family_attr(llcx: &llvm::Context) -> &llvm::Attribute { /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, + cx: &SimpleCx<'ll>, + tcx: TyCtxt<'tcx>, llfn: &'ll Value, - instance: ty::Instance<'tcx>, + codegen_fn_attrs: &CodegenFnAttrs, + instance: Option<ty::Instance<'tcx>>, ) { - let codegen_fn_attrs = cx.tcx.codegen_instance_attrs(instance.def); - + let sess = tcx.sess; let mut to_add = SmallVec::<[_; 16]>::new(); match codegen_fn_attrs.optimize { OptimizeAttr::Default => { - to_add.extend(default_optimisation_attrs(cx)); + to_add.extend(default_optimisation_attrs(cx, sess)); } OptimizeAttr::DoNotOptimize => { to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx)); @@ -369,21 +381,21 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } - if cx.sess().must_emit_unwind_tables() { - to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind)); + if sess.must_emit_unwind_tables() { + to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind)); } - if cx.sess().opts.unstable_opts.profile_sample_use.is_some() { + if sess.opts.unstable_opts.profile_sample_use.is_some() { to_add.push(llvm::CreateAttrString(cx.llcx, "use-sample-profile")); } // FIXME: none of these functions interact with source level attributes. - to_add.extend(frame_pointer_type_attr(cx)); - to_add.extend(function_return_attr(cx)); - to_add.extend(instrument_function_attr(cx)); - to_add.extend(nojumptables_attr(cx)); - to_add.extend(probestack_attr(cx)); - to_add.extend(stackprotector_attr(cx)); + to_add.extend(frame_pointer_type_attr(cx, sess)); + to_add.extend(function_return_attr(cx, sess)); + to_add.extend(instrument_function_attr(cx, sess)); + to_add.extend(nojumptables_attr(cx, sess)); + to_add.extend(probestack_attr(cx, tcx)); + to_add.extend(stackprotector_attr(cx, sess)); if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_BUILTINS) { to_add.push(llvm::CreateAttrString(cx.llcx, "no-builtins")); @@ -404,16 +416,19 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // not used. } else { // Do not set sanitizer attributes for naked functions. - to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); + to_add.extend(sanitize_attrs(cx, tcx, codegen_fn_attrs.no_sanitize)); // For non-naked functions, set branch protection attributes on aarch64. - if let Some(BranchProtection { bti, pac_ret }) = - cx.sess().opts.unstable_opts.branch_protection + if let Some(BranchProtection { bti, pac_ret, gcs }) = + sess.opts.unstable_opts.branch_protection { - assert!(cx.sess().target.arch == "aarch64"); + assert!(sess.target.arch == "aarch64"); if bti { to_add.push(llvm::CreateAttrString(cx.llcx, "branch-target-enforcement")); } + if gcs { + to_add.push(llvm::CreateAttrString(cx.llcx, "guarded-control-stack")); + } if let Some(PacRet { leaf, pc, key }) = pac_ret { if pc { to_add.push(llvm::CreateAttrString(cx.llcx, "branch-protection-pauth-lr")); @@ -435,14 +450,15 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR_ZEROED) { to_add.push(create_alloc_family_attr(cx.llcx)); - if let Some(zv) = - cx.tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant) + if let Some(instance) = instance + && let Some(zv) = + tcx.get_attr(instance.def_id(), rustc_span::sym::rustc_allocator_zeroed_variant) && let Some(name) = zv.value_str() { to_add.push(llvm::CreateAttrStringValue( cx.llcx, "alloc-variant-zeroed", - &mangle_internal_symbol(cx.tcx, name.as_str()), + &mangle_internal_symbol(tcx, name.as_str()), )); } // apply to argument place instead of function @@ -487,18 +503,22 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( if let Some(align) = codegen_fn_attrs.alignment { llvm::set_alignment(llfn, align); } - if let Some(backchain) = backchain_attr(cx) { + if let Some(backchain) = backchain_attr(cx, sess) { to_add.push(backchain); } - to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry)); + to_add.extend(patchable_function_entry_attrs( + cx, + sess, + codegen_fn_attrs.patchable_function_entry, + )); // Always annotate functions with the target-cpu they are compiled for. // Without this, ThinLTO won't inline Rust functions into Clang generated // functions (because Clang annotates functions this way too). - to_add.push(target_cpu_attr(cx)); + to_add.push(target_cpu_attr(cx, sess)); // tune-cpu is only conveyed through the attribute for our purpose. // The target doesn't care; the subtarget reads our attribute. - to_add.extend(tune_cpu_attr(cx)); + to_add.extend(tune_cpu_attr(cx, sess)); let function_features = codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::<Vec<&str>>(); @@ -506,7 +526,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( // Apply function attributes as per usual if there are no user defined // target features otherwise this will get applied at the callsite. if function_features.is_empty() { - if let Some(inline_attr) = inline_attr(cx, instance) { + if let Some(instance) = instance + && let Some(inline_attr) = inline_attr(cx, tcx, instance) + { to_add.push(inline_attr); } } @@ -514,7 +536,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones - .flat_map(|feat| llvm_util::to_llvm_features(cx.tcx.sess, feat)) + .flat_map(|feat| llvm_util::to_llvm_features(sess, feat)) // Convert LLVMFeatures & dependencies to +<feats>s .flat_map(|feat| feat.into_iter().map(|f| format!("+{f}"))) .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { @@ -523,20 +545,22 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( })) .collect::<Vec<String>>(); - if cx.tcx.sess.target.is_like_wasm { + if sess.target.is_like_wasm { // If this function is an import from the environment but the wasm // import has a specific module/name, apply them here. - if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) { + if let Some(instance) = instance + && let Some(module) = wasm_import_module(tcx, instance.def_id()) + { to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-module", module)); let name = - codegen_fn_attrs.symbol_name.unwrap_or_else(|| cx.tcx.item_name(instance.def_id())); + codegen_fn_attrs.symbol_name.unwrap_or_else(|| tcx.item_name(instance.def_id())); let name = name.as_str(); to_add.push(llvm::CreateAttrStringValue(cx.llcx, "wasm-import-name", name)); } } - to_add.extend(target_features_attr(cx, function_features)); + to_add.extend(target_features_attr(cx, tcx, function_features)); attributes::apply_to_llfn(llfn, Function, &to_add); } diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs new file mode 100644 index 00000000000..b14713969b3 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs @@ -0,0 +1,37 @@ +#[cfg(test)] +mod tests; + +/// Joins command-line arguments into a single space-separated string, quoting +/// and escaping individual arguments as necessary. +/// +/// The result is intended to be informational, for embedding in debug metadata, +/// and might not be properly quoted/escaped for actual command-line use. +pub(crate) fn quote_command_line_args(args: &[String]) -> String { + // Start with a decent-sized buffer, since rustc invocations tend to be long. + let mut buf = String::with_capacity(128); + + for arg in args { + if !buf.is_empty() { + buf.push(' '); + } + + print_arg_quoted(&mut buf, arg); + } + + buf +} + +/// Equivalent to LLVM's `sys::printArg` with quoting always enabled +/// (see llvm/lib/Support/Program.cpp). +fn print_arg_quoted(buf: &mut String, arg: &str) { + buf.reserve(arg.len() + 2); + + buf.push('"'); + for ch in arg.chars() { + if matches!(ch, '"' | '\\' | '$') { + buf.push('\\'); + } + buf.push(ch); + } + buf.push('"'); +} diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs new file mode 100644 index 00000000000..69641fed3bc --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs @@ -0,0 +1,25 @@ +#[test] +fn quote_command_line_args() { + use super::quote_command_line_args; + + struct Case<'a> { + args: &'a [&'a str], + expected: &'a str, + } + + let cases = &[ + Case { args: &[], expected: "" }, + Case { args: &["--hello", "world"], expected: r#""--hello" "world""# }, + Case { args: &["--hello world"], expected: r#""--hello world""# }, + Case { + args: &["plain", "$dollar", "spa ce", r"back\slash", r#""quote""#, "plain"], + expected: r#""plain" "\$dollar" "spa ce" "back\\slash" "\"quote\"" "plain""#, + }, + ]; + + for &Case { args, expected } in cases { + let args = args.iter().copied().map(str::to_owned).collect::<Vec<_>>(); + let actual = quote_command_line_args(&args); + assert_eq!(actual, expected, "args {args:?}"); + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index f571716d9dd..5ac3a87c158 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -563,6 +563,8 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff]) { config::AutoDiff::Enable => {} // We handle this below config::AutoDiff::NoPostopt => {} + // Disables TypeTree generation + config::AutoDiff::NoTT => {} } } // This helps with handling enums for now. @@ -617,7 +619,7 @@ pub(crate) fn run_pass_manager( crate::builder::gpu_offload::handle_gpu_code(cgcx, &cx); } - if cfg!(llvm_enzyme) && enable_ad && !thin { + if cfg!(feature = "llvm_enzyme") && enable_ad && !thin { let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index 6cb89f80ab8..fe3883e8c73 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod archive; +mod command_line_args; pub(crate) mod lto; pub(crate) mod owned_target_machine; mod profiling; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 6d8178320fe..903a882916e 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -1,4 +1,3 @@ -use std::assert_matches::assert_matches; use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::NonNull; @@ -39,13 +38,10 @@ impl OwnedTargetMachine { output_obj_file: &CStr, debug_info_compression: &CStr, use_emulated_tls: bool, - args_cstr_buff: &[u8], + argv0: &str, + command_line_args: &str, use_wasm_eh: bool, ) -> Result<Self, LlvmError<'static>> { - // The argument list is passed as the concatenation of one or more C strings. - // This implies that there must be a last byte, and it must be 0. - assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator"); - // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data let tm_ptr = unsafe { llvm::LLVMRustCreateTargetMachine( @@ -70,8 +66,10 @@ impl OwnedTargetMachine { output_obj_file.as_ptr(), debug_info_compression.as_ptr(), use_emulated_tls, - args_cstr_buff.as_ptr(), - args_cstr_buff.len(), + argv0.as_ptr(), + argv0.len(), + command_line_args.as_ptr(), + command_line_args.len(), use_wasm_eh, ) }; @@ -97,8 +95,6 @@ impl Drop for OwnedTargetMachine { // SAFETY: constructing ensures we have a valid pointer created by // llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no // double free or use after free. - unsafe { - llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr()); - } + unsafe { llvm::LLVMDisposeTargetMachine(self.tm_unique) }; } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 423f0da4878..1950b8288d1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -31,6 +31,7 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; +use crate::back::command_line_args::quote_command_line_args; use crate::back::lto::ThinBuffer; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -203,6 +204,9 @@ pub(crate) fn target_machine_factory( optlvl: config::OptLevel, target_features: &[String], ) -> TargetMachineFactoryFn<LlvmCodegenBackend> { + // Self-profile timer for creating a _factory_. + let _prof_timer = sess.prof.generic_activity("target_machine_factory"); + let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); @@ -249,23 +253,18 @@ pub(crate) fn target_machine_factory( let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated); - // copy the exe path, followed by path all into one buffer - // null terminating them so we can use them as null terminated strings - let args_cstr_buff = { - let mut args_cstr_buff: Vec<u8> = Vec::new(); - let exe_path = std::env::current_exe().unwrap_or_default(); - let exe_path_str = exe_path.into_os_string().into_string().unwrap_or_default(); - - args_cstr_buff.extend_from_slice(exe_path_str.as_bytes()); - args_cstr_buff.push(0); - - for arg in sess.expanded_args.iter() { - args_cstr_buff.extend_from_slice(arg.as_bytes()); - args_cstr_buff.push(0); - } - - args_cstr_buff - }; + // Command-line information to be included in the target machine. + // This seems to only be used for embedding in PDB debuginfo files. + // FIXME(Zalathar): Maybe skip this for non-PDB targets? + let argv0 = std::env::current_exe() + .unwrap_or_default() + .into_os_string() + .into_string() + .unwrap_or_default(); + let command_line_args = quote_command_line_args(&sess.expanded_args); + // Self-profile counter for the number of bytes produced by command-line quoting. + // Values are summed, so the summary result is cumulative across all TM factories. + sess.prof.artifact_size("quoted_command_line_args", "-", command_line_args.len() as u64); let debuginfo_compression = sess.opts.debuginfo_compression.to_string(); match sess.opts.debuginfo_compression { @@ -288,7 +287,11 @@ pub(crate) fn target_machine_factory( let use_wasm_eh = wants_wasm_eh(sess); + let prof = SelfProfilerRef::clone(&sess.prof); Arc::new(move |config: TargetMachineFactoryConfig| { + // Self-profile timer for invoking a factory to create a target machine. + let _prof_timer = prof.generic_activity("target_machine_factory_inner"); + let path_to_cstring_helper = |path: Option<PathBuf>| -> CString { let path = path.unwrap_or_default(); let path = path_mapping @@ -323,7 +326,8 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, - &args_cstr_buff, + &argv0, + &command_line_args, use_wasm_eh, ) }) @@ -574,7 +578,8 @@ pub(crate) unsafe fn llvm_optimize( // FIXME(ZuseZ4): In a future update we could figure out how to only optimize individual functions getting // differentiated. - let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); + let consider_ad = + cfg!(feature = "llvm_enzyme") && config.autodiff.contains(&config::AutoDiff::Enable); let run_enzyme = autodiff_stage == AutodiffStage::DuringAD; let print_before_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModBefore); let print_after_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModAfter); @@ -740,7 +745,8 @@ pub(crate) fn optimize( // If we know that we will later run AD, then we disable vectorization and loop unrolling. // Otherwise we pretend AD is already done and run the normal opt pipeline (=PostAD). - let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); + let consider_ad = + cfg!(feature = "llvm_enzyme") && config.autodiff.contains(&config::AutoDiff::Enable); let autodiff_stage = if consider_ad { AutodiffStage::PreAD } else { AutodiffStage::PostAD }; // The embedded bitcode is used to run LTO/ThinLTO. // The bitcode obtained during the `codegen` phase is no longer suitable for performing LTO. diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 9cc5d8dbc21..6d12b511e9c 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -105,22 +105,40 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx, cx.codegen_unit) { - let attrs = attributes::sanitize_attrs(&cx, SanitizerSet::empty()); + let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerSet::empty()); attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } + // Define Objective-C module info and module flags. Note, the module info will + // also be added to the `llvm.compiler.used` variable, created later. + // + // These are only necessary when we need the linker to do its Objective-C-specific + // magic. We could theoretically do it unconditionally, but at a slight cost to linker + // performance in the common case where it's unnecessary. + if !cx.objc_classrefs.borrow().is_empty() || !cx.objc_selrefs.borrow().is_empty() { + if cx.objc_abi_version() == 1 { + cx.define_objc_module_info(); + } + cx.add_objc_module_flags(); + } + // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.compiler.used` variable, created next. if cx.sess().instrument_coverage() { cx.coverageinfo_finalize(); } - // Create the llvm.used and llvm.compiler.used variables. + // Create the llvm.used variable. if !cx.used_statics.is_empty() { cx.create_used_variable_impl(c"llvm.used", &cx.used_statics); } - if !cx.compiler_used_statics.is_empty() { - cx.create_used_variable_impl(c"llvm.compiler.used", &cx.compiler_used_statics); + + // Create the llvm.compiler.used variable. + { + let compiler_used_statics = cx.compiler_used_statics.borrow(); + if !compiler_used_statics.is_empty() { + cx.create_used_variable_impl(c"llvm.compiler.used", &compiler_used_statics); + } } // Run replace-all-uses-with for statics that need it. This must diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7d0691366e6..5271d0b4bb8 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2,6 +2,7 @@ use std::borrow::{Borrow, Cow}; use std::ops::Deref; use std::{iter, ptr}; +use rustc_ast::expand::typetree::FncTree; pub(crate) mod autodiff; pub(crate) mod gpu_offload; @@ -1091,16 +1092,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ty: Ty<'tcx>, lhs: Self::Value, rhs: Self::Value, - ) -> Option<Self::Value> { - // FIXME: See comment on the definition of `three_way_compare`. - if crate::llvm_util::get_version() < (20, 0, 0) { - return None; - } - + ) -> Self::Value { let size = ty.primitive_size(self.tcx); let name = if ty.is_signed() { "llvm.scmp" } else { "llvm.ucmp" }; - Some(self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs])) + self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs]) } /* Miscellaneous instructions */ @@ -1112,11 +1108,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { src_align: Align, size: &'ll Value, flags: MemFlags, + tt: Option<FncTree>, ) { assert!(!flags.contains(MemFlags::NONTEMPORAL), "non-temporal memcpy not supported"); let size = self.intcast(size, self.type_isize(), false); let is_volatile = flags.contains(MemFlags::VOLATILE); - unsafe { + let memcpy = unsafe { llvm::LLVMRustBuildMemCpy( self.llbuilder, dst, @@ -1125,7 +1122,16 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { src_align.bytes() as c_uint, size, is_volatile, - ); + ) + }; + + // TypeTree metadata for memcpy is especially important: when Enzyme encounters + // a memcpy during autodiff, it needs to know the structure of the data being + // copied to properly track derivatives. For example, copying an array of floats + // vs. copying a struct with mixed types requires different derivative handling. + // The TypeTree tells Enzyme exactly what memory layout to expect. + if let Some(tt) = tt { + crate::typetree::add_tt(self.cx().llmod, self.cx().llcx, memcpy, tt); } } @@ -1438,7 +1444,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // If there is an inline attribute and a target feature that matches // we will add the attribute to the callsite otherwise we'll omit // this and not add the attribute to prevent soundness issues. - && let Some(inlining_rule) = attributes::inline_attr(&self.cx, instance) + && let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, instance) && self.cx.tcx.is_target_feature_call_safe( &fn_call_attrs.target_features, &fn_defn_attrs.target_features, diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 6ddf53cdc87..c3485f56391 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -1,10 +1,12 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; +use rustc_ast::expand::typetree::FncTree; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; -use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; +use rustc_middle::ty::{Instance, PseudoCanonicalInput, TyCtxt, TypingEnv}; use rustc_middle::{bug, ty}; +use rustc_target::callconv::PassMode; use tracing::debug; use crate::builder::{Builder, PlaceRef, UNNAMED}; @@ -16,9 +18,12 @@ use crate::value::Value; pub(crate) fn adjust_activity_to_abi<'tcx>( tcx: TyCtxt<'tcx>, - fn_ty: Ty<'tcx>, + instance: Instance<'tcx>, + typing_env: TypingEnv<'tcx>, da: &mut Vec<DiffActivity>, ) { + let fn_ty = instance.ty(tcx, typing_env); + if !matches!(fn_ty.kind(), ty::FnDef(..)) { bug!("expected fn def for autodiff, got {:?}", fn_ty); } @@ -27,8 +32,16 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( // All we do is decide how to handle the arguments. let sig = fn_ty.fn_sig(tcx).skip_binder(); + // FIXME(Sa4dUs): pass proper varargs once we have support for differentiating variadic functions + let Ok(fn_abi) = + tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty()))) + else { + bug!("failed to get fn_abi of instance with empty varargs"); + }; + let mut new_activities = vec![]; let mut new_positions = vec![]; + let mut del_activities = 0; for (i, ty) in sig.inputs().iter().enumerate() { if let Some(inner_ty) = ty.builtin_deref(true) { if inner_ty.is_slice() { @@ -80,6 +93,34 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( continue; } } + + let pci = PseudoCanonicalInput { typing_env: TypingEnv::fully_monomorphized(), value: *ty }; + + let layout = match tcx.layout_of(pci) { + Ok(layout) => layout.layout, + Err(_) => { + bug!("failed to compute layout for type {:?}", ty); + } + }; + + let pass_mode = &fn_abi.args[i].mode; + + // For ZST, just ignore and don't add its activity, as this arg won't be present + // in the LLVM passed to Enzyme. + // Some targets pass ZST indirectly in the C ABI, in that case, handle it as a normal arg + // FIXME(Sa4dUs): Enforce ZST corresponding diff activity be `Const` + if *pass_mode == PassMode::Ignore { + del_activities += 1; + da.remove(i); + } + + // If the argument is lowered as a `ScalarPair`, we need to duplicate its activity. + // Otherwise, the number of activities won't match the number of LLVM arguments and + // this will lead to errors when verifying the Enzyme call. + if let rustc_abi::BackendRepr::ScalarPair(_, _) = layout.backend_repr() { + new_activities.push(da[i].clone()); + new_positions.push(i + 1 - del_activities); + } } // now add the extra activities coming from slices // Reverse order to not invalidate the indices @@ -254,6 +295,7 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( fn_args: &[&'ll Value], attrs: AutoDiffAttrs, dest: PlaceRef<'tcx, &'ll Value>, + fnc_tree: FncTree, ) { // We have to pick the name depending on whether we want forward or reverse mode autodiff. let mut ad_name: String = match attrs.mode { @@ -330,6 +372,10 @@ pub(crate) fn generate_enzyme_call<'ll, 'tcx>( fn_args, ); + if !fnc_tree.args.is_empty() || !fnc_tree.ret.0.is_empty() { + crate::typetree::add_tt(cx.llmod, cx.llcx, fn_to_diff, fnc_tree); + } + let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); builder.store_to_place(call, dest.val); diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 11b79a7fe68..aa2df46329f 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -108,6 +108,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> { bytes_in_context(self.llcx(), bytes) } + pub(crate) fn null_terminate_const_bytes(&self, bytes: &[u8]) -> &'ll Value { + null_terminate_bytes_in_context(self.llcx(), bytes) + } + pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value { unsafe { let idx = c_uint::try_from(idx).expect("LLVMGetAggregateElement index overflow"); @@ -381,6 +385,16 @@ pub(crate) fn bytes_in_context<'ll>(llcx: &'ll llvm::Context, bytes: &[u8]) -> & } } +pub(crate) fn null_terminate_bytes_in_context<'ll>( + llcx: &'ll llvm::Context, + bytes: &[u8], +) -> &'ll Value { + unsafe { + let ptr = bytes.as_ptr() as *const c_char; + llvm::LLVMConstStringInContext2(llcx, ptr, bytes.len(), FALSE) + } +} + pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { let len = c_uint::try_from(elts.len()).expect("LLVMConstStructInContext elements len overflow"); unsafe { llvm::LLVMConstNamedStruct(ty, elts.as_ptr(), len) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index dc9bb743560..40375ef6510 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -16,6 +16,7 @@ use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; +use rustc_span::Symbol; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; @@ -331,6 +332,10 @@ impl<'ll> CodegenCx<'ll, '_> { } g + } else if let Some(classname) = fn_attrs.objc_class { + self.get_objc_classref(classname) + } else if let Some(methname) = fn_attrs.objc_selector { + self.get_objc_selref(methname) } else { check_and_apply_linkage(self, fn_attrs, llty, sym, def_id) }; @@ -543,8 +548,225 @@ impl<'ll> CodegenCx<'ll, '_> { /// Add a global value to a list to be stored in the `llvm.compiler.used` variable, /// an array of ptr. - pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) { - self.compiler_used_statics.push(global); + pub(crate) fn add_compiler_used_global(&self, global: &'ll Value) { + self.compiler_used_statics.borrow_mut().push(global); + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `CGObjCCommonMac::CreateCStringLiteral`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134 + fn define_objc_classname(&self, classname: &str) -> &'ll Value { + assert_eq!(self.objc_abi_version(), 1); + + let llval = self.null_terminate_const_bytes(classname.as_bytes()); + let llty = self.val_ty(llval); + let sym = self.generate_local_symbol_name("OBJC_CLASS_NAME_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.i8_align); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__TEXT,__cstring,cstring_literals"); + llvm::LLVMSetGlobalConstant(g, llvm::TRUE); + llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); + self.add_compiler_used_global(g); + + g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `ObjCNonFragileABITypesHelper`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L6052 + fn get_objc_class_t(&self) -> &'ll Type { + if let Some(class_t) = self.objc_class_t.get() { + return class_t; + } + + assert_eq!(self.objc_abi_version(), 2); + + // struct _class_t { + // struct _class_t* isa; + // struct _class_t* const superclass; + // void* cache; + // IMP* vtable; + // struct class_ro_t* ro; + // } + + let class_t = self.type_named_struct("struct._class_t"); + let els = [self.type_ptr(); 5]; + let packed = false; + self.set_struct_body(class_t, &els, packed); + + self.objc_class_t.set(Some(class_t)); + class_t + } + + // We do our best here to match what Clang does when compiling Objective-C natively. We + // deduplicate references within a CGU, but we need a reference definition in each referencing + // CGU. All attempts at using external references to a single reference definition result in + // linker errors. + fn get_objc_classref(&self, classname: Symbol) -> &'ll Value { + let mut classrefs = self.objc_classrefs.borrow_mut(); + if let Some(classref) = classrefs.get(&classname).copied() { + return classref; + } + + let g = match self.objc_abi_version() { + 1 => { + // See Clang's `CGObjCMac::EmitClassRefFromId`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5205 + let llval = self.define_objc_classname(classname.as_str()); + let llty = self.type_ptr(); + let sym = self.generate_local_symbol_name("OBJC_CLASS_REFERENCES_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__OBJC,__cls_refs,literal_pointers,no_dead_strip"); + self.add_compiler_used_global(g); + g + } + 2 => { + // See Clang's `CGObjCNonFragileABIMac::EmitClassRefFromId`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7423 + let llval = { + let extern_sym = format!("OBJC_CLASS_$_{}", classname.as_str()); + let extern_llty = self.get_objc_class_t(); + self.declare_global(&extern_sym, extern_llty) + }; + let llty = self.type_ptr(); + let sym = self.generate_local_symbol_name("OBJC_CLASSLIST_REFERENCES_$_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + llvm::set_section(g, c"__DATA,__objc_classrefs,regular,no_dead_strip"); + self.add_compiler_used_global(g); + g + } + _ => unreachable!(), + }; + + classrefs.insert(classname, g); + g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. We + // deduplicate references within a CGU, but we need a reference definition in each referencing + // CGU. All attempts at using external references to a single reference definition result in + // linker errors. + // + // Newer versions of Apple Clang generate calls to `@"objc_msgSend$methname"` selector stub + // functions. We don't currently do that. The code we generate is closer to what Apple Clang + // generates with the `-fno-objc-msgsend-selector-stubs` option. + fn get_objc_selref(&self, methname: Symbol) -> &'ll Value { + let mut selrefs = self.objc_selrefs.borrow_mut(); + if let Some(selref) = selrefs.get(&methname).copied() { + return selref; + } + + let abi_version = self.objc_abi_version(); + + // See Clang's `CGObjCCommonMac::CreateCStringLiteral`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134 + let methname_llval = self.null_terminate_const_bytes(methname.as_str().as_bytes()); + let methname_llty = self.val_ty(methname_llval); + let methname_sym = self.generate_local_symbol_name("OBJC_METH_VAR_NAME_"); + let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", methname_sym); + }); + set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align); + llvm::set_initializer(methname_g, methname_llval); + llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage); + llvm::set_section( + methname_g, + match abi_version { + 1 => c"__TEXT,__cstring,cstring_literals", + 2 => c"__TEXT,__objc_methname,cstring_literals", + _ => unreachable!(), + }, + ); + llvm::LLVMSetGlobalConstant(methname_g, llvm::TRUE); + llvm::LLVMSetUnnamedAddress(methname_g, llvm::UnnamedAddr::Global); + self.add_compiler_used_global(methname_g); + + // See Clang's `CGObjCMac::EmitSelectorAddr`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5243 + // And Clang's `CGObjCNonFragileABIMac::EmitSelectorAddr`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7586 + let selref_llval = methname_g; + let selref_llty = self.type_ptr(); + let selref_sym = self.generate_local_symbol_name("OBJC_SELECTOR_REFERENCES_"); + let selref_g = self.define_global(&selref_sym, selref_llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", selref_sym); + }); + set_global_alignment(self, selref_g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(selref_g, selref_llval); + llvm::set_externally_initialized(selref_g, true); + llvm::set_linkage( + selref_g, + match abi_version { + 1 => llvm::Linkage::PrivateLinkage, + 2 => llvm::Linkage::InternalLinkage, + _ => unreachable!(), + }, + ); + llvm::set_section( + selref_g, + match abi_version { + 1 => c"__OBJC,__message_refs,literal_pointers,no_dead_strip", + 2 => c"__DATA,__objc_selrefs,literal_pointers,no_dead_strip", + _ => unreachable!(), + }, + ); + self.add_compiler_used_global(selref_g); + + selrefs.insert(methname, selref_g); + selref_g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `ObjCTypesHelper`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5936 + // And Clang's `CGObjCMac::EmitModuleInfo`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5151 + pub(crate) fn define_objc_module_info(&mut self) { + assert_eq!(self.objc_abi_version(), 1); + + // struct _objc_module { + // long version; // Hardcoded to 7 in Clang. + // long size; // sizeof(struct _objc_module) + // char* name; // Hardcoded to classname "" in Clang. + // struct _objc_symtab* symtab; // Null without class or category definitions. + // } + + let llty = self.type_named_struct("struct._objc_module"); + let i32_llty = self.type_i32(); + let ptr_llty = self.type_ptr(); + let packed = false; + self.set_struct_body(llty, &[i32_llty, i32_llty, ptr_llty, ptr_llty], packed); + + let version = self.const_uint(i32_llty, 7); + let size = self.const_uint(i32_llty, 16); + let name = self.define_objc_classname(""); + let symtab = self.const_null(ptr_llty); + let llval = crate::common::named_struct(llty, &[version, size, name, symtab]); + + let sym = "OBJC_MODULES"; + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__OBJC,__module_info,regular,no_dead_strip"); + + self.add_compiler_used_global(g); } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a69fa54a54a..aa5c17269fb 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -26,7 +26,7 @@ use rustc_session::config::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet, }; use rustc_span::source_map::Spanned; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel}; use smallvec::SmallVec; @@ -119,7 +119,7 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// Statics that will be placed in the llvm.compiler.used variable /// See <https://llvm.org/docs/LangRef.html#the-llvm-compiler-used-global-variable> for details - pub compiler_used_statics: Vec<&'ll Value>, + pub compiler_used_statics: RefCell<Vec<&'ll Value>>, /// Mapping of non-scalar types to llvm types. pub type_lowering: RefCell<FxHashMap<(Ty<'tcx>, Option<VariantIdx>), &'ll Type>>, @@ -146,6 +146,15 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// `global_asm!` needs to be able to find this new global so that it can /// compute the correct mangled symbol name to insert into the asm. pub renamed_statics: RefCell<FxHashMap<DefId, &'ll Value>>, + + /// Cached Objective-C class type + pub objc_class_t: Cell<Option<&'ll Type>>, + + /// Cache of Objective-C class references + pub objc_classrefs: RefCell<FxHashMap<Symbol, &'ll Value>>, + + /// Cache of Objective-C selector references + pub objc_selrefs: RefCell<FxHashMap<Symbol, &'ll Value>>, } fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { @@ -172,35 +181,6 @@ pub(crate) unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (20, 0, 0) { - if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") { - // LLVM 20 defines three additional address spaces for alternate - // pointer kinds used in Windows. - // See https://github.com/llvm/llvm-project/pull/111879 - target_data_layout = - target_data_layout.replace("-p270:32:32-p271:32:32-p272:64:64", ""); - } - if sess.target.arch.starts_with("sparc") { - // LLVM 20 updates the sparc layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/106951 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("mips64") { - // LLVM 20 updates the mips64 layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/112084 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("powerpc64") { - // LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/118004 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("wasm32") || sess.target.arch.starts_with("wasm64") { - // LLVM 20 updates the wasm(32|64) layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/119204 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - } if llvm_version < (21, 0, 0) { if sess.target.arch == "nvptx64" { // LLVM 21 updated the default layout on nvptx: https://github.com/llvm/llvm-project/pull/124961 @@ -390,7 +370,8 @@ pub(crate) unsafe fn create_module<'ll>( ); } - if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection { + if let Some(BranchProtection { bti, pac_ret, gcs }) = sess.opts.unstable_opts.branch_protection + { if sess.target.arch == "aarch64" { llvm::add_module_flag_u32( llmod, @@ -423,6 +404,12 @@ pub(crate) unsafe fn create_module<'ll>( "sign-return-address-with-bkey", u32::from(pac_opts.key == PAuthKey::B), ); + llvm::add_module_flag_u32( + llmod, + llvm::ModuleFlagMergeBehavior::Min, + "guarded-control-stack", + gcs.into(), + ); } else { bug!( "branch-protection used on non-AArch64 target; \ @@ -644,7 +631,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), used_statics: Vec::new(), - compiler_used_statics: Vec::new(), + compiler_used_statics: Default::default(), type_lowering: Default::default(), scalar_lltypes: Default::default(), coverage_cx, @@ -655,6 +642,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { intrinsics: Default::default(), local_gen_sym_counter: Cell::new(0), renamed_statics: Default::default(), + objc_class_t: Cell::new(None), + objc_classrefs: Default::default(), + objc_selrefs: Default::default(), }, PhantomData, ) @@ -679,6 +669,69 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::set_linkage(g, llvm::Linkage::AppendingLinkage); llvm::set_section(g, c"llvm.metadata"); } + + /// The Objective-C ABI that is used. + /// + /// This corresponds to the `-fobjc-abi-version=` flag in Clang / GCC. + pub(crate) fn objc_abi_version(&self) -> u32 { + assert!(self.tcx.sess.target.is_like_darwin); + if self.tcx.sess.target.arch == "x86" && self.tcx.sess.target.os == "macos" { + // 32-bit x86 macOS uses ABI version 1 (a.k.a. the "fragile ABI"). + 1 + } else { + // All other Darwin-like targets we support use ABI version 2 + // (a.k.a the "non-fragile ABI"). + 2 + } + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `CGObjCCommonMac::EmitImageInfo`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085 + pub(crate) fn add_objc_module_flags(&self) { + let abi_version = self.objc_abi_version(); + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Version", + abi_version, + ); + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Image Info Version", + 0, + ); + + llvm::add_module_flag_str( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Image Info Section", + match abi_version { + 1 => "__OBJC,__image_info,regular", + 2 => "__DATA,__objc_imageinfo,regular,no_dead_strip", + _ => unreachable!(), + }, + ); + + if self.tcx.sess.target.env == "sim" { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Is Simulated", + 1 << 5, + ); + } + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Class Properties", + 1 << 6, + ); + } } impl<'ll> SimpleCx<'ll> { pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { @@ -823,7 +876,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } else { let fty = self.type_variadic_func(&[], self.type_i32()); let llfn = self.declare_cfn(name, llvm::UnnamedAddr::Global, fty); - let target_cpu = attributes::target_cpu_attr(self); + let target_cpu = attributes::target_cpu_attr(self, self.sess()); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[target_cpu]); llfn } @@ -838,15 +891,15 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn set_frame_pointer_type(&self, llfn: &'ll Value) { - if let Some(attr) = attributes::frame_pointer_type_attr(self) { + if let Some(attr) = attributes::frame_pointer_type_attr(self, self.sess()) { attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[attr]); } } fn apply_target_cpu_attr(&self, llfn: &'ll Value) { let mut attrs = SmallVec::<[_; 2]>::new(); - attrs.push(attributes::target_cpu_attr(self)); - attrs.extend(attributes::tune_cpu_attr(self)); + attrs.push(attributes::target_cpu_attr(self, self.sess())); + attrs.extend(attributes::tune_cpu_attr(self, self.sess())); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &attrs); } @@ -865,7 +918,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { attributes::apply_to_llfn( llfn, llvm::AttributePlace::Function, - attributes::target_features_attr(self, vec![]).as_slice(), + attributes::target_features_attr(self, self.tcx, vec![]).as_slice(), ); Some(llfn) } else { @@ -991,7 +1044,10 @@ impl<'tcx, 'll> HasTypingEnv<'tcx> for CodegenCx<'ll, 'tcx> { impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { + if let LayoutError::SizeOverflow(_) + | LayoutError::ReferencesError(_) + | LayoutError::InvalidSimd { .. } = err + { self.tcx.dcx().emit_fatal(Spanned { span, node: err.into_diagnostic() }) } else { self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err }) @@ -1008,7 +1064,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { match err { - FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => { + FnAbiError::Layout( + LayoutError::SizeOverflow(_) + | LayoutError::Cycle(_) + | LayoutError::InvalidSimd { .. }, + ) => { self.tcx.dcx().emit_fatal(Spanned { span, node: err }); } _ => match fn_abi_request { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 7a6dc008c7b..3081badb821 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -2,9 +2,9 @@ use rustc_codegen_ssa::base::collect_debugger_visualizers_transitive; use rustc_codegen_ssa::traits::*; +use rustc_hir::attrs::DebuggerVisualizerType; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; -use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerType; use rustc_session::config::{CrateType, DebugInfo}; use crate::builder::Builder; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dc3a84b6a15..1e4ace4ca92 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -32,9 +32,7 @@ use self::type_map::{DINodeCreationResult, Stub, UniqueTypeId}; use super::CodegenUnitDebugContext; use super::namespace::mangled_name_of_instance; use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name}; -use super::utils::{ - DIB, create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, -}; +use super::utils::{DIB, debug_context, get_namespace_for_item, is_node_local_to_unit}; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::dwarf_const; use crate::debuginfo::metadata::type_map::build_type_with_children; @@ -119,17 +117,17 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( .try_to_target_usize(cx.tcx) .expect("expected monomorphic const in codegen") as c_longlong; - let subrange = - unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; + let subrange = unsafe { llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; + let subscripts = &[subrange]; - let subscripts = create_DIArray(DIB(cx), &[subrange]); let di_node = unsafe { - llvm::LLVMRustDIBuilderCreateArrayType( + llvm::LLVMDIBuilderCreateArrayType( DIB(cx), size.bits(), align.bits() as u32, element_type_di_node, - subscripts, + subscripts.as_ptr(), + subscripts.len() as c_uint, ) }; @@ -175,17 +173,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( "ptr_type={ptr_type}, pointee_type={pointee_type}", ); - let di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_di_node, - pointer_size.bits(), - pointer_align.abi.bits() as u32, - 0, // Ignore DWARF address space. - ptr_type_debuginfo_name.as_c_char_ptr(), - ptr_type_debuginfo_name.len(), - ) - }; + let di_node = create_pointer_type( + cx, + pointee_type_di_node, + pointer_size, + pointer_align.abi, + &ptr_type_debuginfo_name, + ); DINodeCreationResult { di_node, already_stored_in_typemap: false } } @@ -233,17 +227,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( // The data pointer type is a regular, thin pointer, regardless of whether this // is a slice or a trait object. - let data_ptr_type_di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_di_node, - addr_field.size.bits(), - addr_field.align.abi.bits() as u32, - 0, // Ignore DWARF address space. - std::ptr::null(), - 0, - ) - }; + let data_ptr_type_di_node = create_pointer_type( + cx, + pointee_type_di_node, + addr_field.size, + addr_field.align.abi, + "", + ); smallvec![ build_field_di_node( @@ -318,7 +308,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id); - let fn_di_node = create_subroutine_type(cx, create_DIArray(DIB(cx), &signature_di_nodes[..])); + let fn_di_node = create_subroutine_type(cx, &signature_di_nodes[..]); // This is actually a function pointer, so wrap it in pointer DI. let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false); @@ -329,26 +319,44 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( } _ => unreachable!(), }; - let di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - fn_di_node, - size.bits(), - align.bits() as u32, - 0, // Ignore DWARF address space. - name.as_c_char_ptr(), - name.len(), - ) - }; + let di_node = create_pointer_type(cx, fn_di_node, size, align, &name); DINodeCreationResult::new(di_node, false) } pub(super) fn create_subroutine_type<'ll>( cx: &CodegenCx<'ll, '_>, - signature: &'ll DICompositeType, + signature: &[Option<&'ll llvm::Metadata>], ) -> &'ll DICompositeType { - unsafe { llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), signature) } + unsafe { + llvm::LLVMDIBuilderCreateSubroutineType( + DIB(cx), + None, // ("File" is ignored and has no effect) + signature.as_ptr(), + signature.len() as c_uint, + DIFlags::FlagZero, // (default value) + ) + } +} + +fn create_pointer_type<'ll>( + cx: &CodegenCx<'ll, '_>, + pointee_ty: &'ll llvm::Metadata, + size: Size, + align: Align, + name: &str, +) -> &'ll llvm::Metadata { + unsafe { + llvm::LLVMDIBuilderCreatePointerType( + DIB(cx), + pointee_ty, + size.bits(), + align.bits() as u32, + 0, // Ignore DWARF address space. + name.as_ptr(), + name.len(), + ) + } } /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs @@ -469,8 +477,8 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id), ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id), ty::Adt(def, ..) => match def.adt_kind() { - AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id), - AdtKind::Union => build_union_type_di_node(cx, unique_type_id), + AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id, span), + AdtKind::Union => build_union_type_di_node(cx, unique_type_id, span), AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span), }, ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id), @@ -813,14 +821,15 @@ fn build_basic_type_di_node<'ll, 'tcx>( }; let typedef_di_node = unsafe { - llvm::LLVMRustDIBuilderCreateTypedef( + llvm::LLVMDIBuilderCreateTypedef( DIB(cx), ty_di_node, - typedef_name.as_c_char_ptr(), + typedef_name.as_ptr(), typedef_name.len(), unknown_file_metadata(cx), - 0, - None, + 0, // (no line number) + None, // (no scope) + 0u32, // (no alignment specified) ) }; @@ -834,12 +843,13 @@ fn create_basic_type<'ll, 'tcx>( encoding: u32, ) -> &'ll DIBasicType { unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( + llvm::LLVMDIBuilderCreateBasicType( DIB(cx), - name.as_c_char_ptr(), + name.as_ptr(), name.len(), size.bits(), encoding, + DIFlags::FlagZero, ) } } @@ -1025,15 +1035,15 @@ fn create_member_type<'ll, 'tcx>( type_di_node: &'ll DIType, ) -> &'ll DIType { unsafe { - llvm::LLVMRustDIBuilderCreateMemberType( + llvm::LLVMDIBuilderCreateMemberType( DIB(cx), owner, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, layout.size.bits(), - layout.align.abi.bits() as u32, + layout.align.bits() as u32, offset.bits(), flags, type_di_node, @@ -1066,6 +1076,7 @@ fn visibility_di_flags<'ll, 'tcx>( fn build_struct_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let struct_type = unique_type_id.expect_ty(); let ty::Adt(adt_def, _) = struct_type.kind() else { @@ -1073,7 +1084,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( }; assert!(adt_def.is_struct()); let containing_scope = get_namespace_for_item(cx, adt_def.did()); - let struct_type_and_layout = cx.layout_of(struct_type); + let struct_type_and_layout = cx.spanned_layout_of(struct_type, span); let variant_def = adt_def.non_enum_variant(); let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { Some(file_metadata_from_def_id(cx, Some(adt_def.did()))) @@ -1266,6 +1277,7 @@ fn build_closure_env_di_node<'ll, 'tcx>( fn build_union_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let union_type = unique_type_id.expect_ty(); let (union_def_id, variant_def) = match union_type.kind() { @@ -1273,7 +1285,7 @@ fn build_union_type_di_node<'ll, 'tcx>( _ => bug!("build_union_type_di_node on a non-ADT"), }; let containing_scope = get_namespace_for_item(cx, union_def_id); - let union_ty_and_layout = cx.layout_of(union_type); + let union_ty_and_layout = cx.spanned_layout_of(union_type, span); let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false); let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers { Some(file_metadata_from_def_id(cx, Some(union_def_id))) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index a5c80895741..4ecc3086e1b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty}; use smallvec::smallvec; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::debuginfo::dwarf_const::DW_TAG_const_type; use crate::debuginfo::metadata::enums::DiscrResult; use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; @@ -378,20 +378,17 @@ fn build_single_variant_union_fields<'ll, 'tcx>( variant_struct_type_wrapper_di_node, None, ), - unsafe { - llvm::LLVMRustDIBuilderCreateStaticMemberType( - DIB(cx), - enum_type_di_node, - TAG_FIELD_NAME.as_c_char_ptr(), - TAG_FIELD_NAME.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - variant_names_type_di_node, - visibility_flags, - Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), - tag_base_type_align.bits() as u32, - ) - } + create_static_member_type( + cx, + enum_type_di_node, + TAG_FIELD_NAME, + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + variant_names_type_di_node, + visibility_flags, + Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), + tag_base_type_align, + ), ] } @@ -570,27 +567,28 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( let build_assoc_const = |name: &str, type_di_node_: &'ll DIType, value: u64, - align: Align| unsafe { + align: Align| + -> &'ll llvm::Metadata { // FIXME: Currently we force all DISCR_* values to be u64's as LLDB seems to have // problems inspecting other value types. Since DISCR_* is typically only going to be // directly inspected via the debugger visualizer - which compares it to the `tag` value // (whose type is not modified at all) it shouldn't cause any real problems. let (t_di, align) = if name == ASSOC_CONST_DISCR_NAME { - (type_di_node_, align.bits() as u32) + (type_di_node_, align) } else { let ty_u64 = Ty::new_uint(cx.tcx, ty::UintTy::U64); - (type_di_node(cx, ty_u64), Align::EIGHT.bits() as u32) + (type_di_node(cx, ty_u64), Align::EIGHT) }; // must wrap type in a `const` modifier for LLDB to be able to inspect the value of the member - let field_type = - llvm::LLVMRustDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di); + let field_type = unsafe { + llvm::LLVMDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di) + }; - llvm::LLVMRustDIBuilderCreateStaticMemberType( - DIB(cx), + create_static_member_type( + cx, wrapper_struct_type_di_node, - name.as_c_char_ptr(), - name.len(), + name, unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, field_type, @@ -975,3 +973,30 @@ fn variant_struct_wrapper_type_name(variant_index: VariantIdx) -> Cow<'static, s .map(|&s| Cow::from(s)) .unwrap_or_else(|| format!("Variant{}", variant_index.as_usize()).into()) } + +fn create_static_member_type<'ll>( + cx: &CodegenCx<'ll, '_>, + scope: &'ll llvm::Metadata, + name: &str, + file: &'ll llvm::Metadata, + line_number: c_uint, + ty: &'ll llvm::Metadata, + flags: DIFlags, + value: Option<&'ll llvm::Value>, + align: Align, +) -> &'ll llvm::Metadata { + unsafe { + llvm::LLVMDIBuilderCreateStaticMemberType( + DIB(cx), + scope, + name.as_ptr(), + name.len(), + file, + line_number, + ty, + flags, + value, + align.bits() as c_uint, + ) + } +} diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 62d38d463ab..1ae6e6e5eec 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -289,7 +289,7 @@ fn build_enum_variant_part_di_node<'ll, 'tcx>( file_metadata, line_number, enum_type_and_layout.size.bits(), - enum_type_and_layout.align.abi.bits() as u32, + enum_type_and_layout.align.bits() as u32, DIFlags::FlagZero, tag_member_di_node, create_DIArray(DIB(cx), &[]), @@ -449,7 +449,7 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>( file_di_node, line_number, enum_type_and_layout.size.bits(), - enum_type_and_layout.align.abi.bits() as u32, + enum_type_and_layout.align.bits() as u32, Size::ZERO.bits(), discr, DIFlags::FlagZero, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 18a783a348a..37200fdc41a 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; +use libc::c_uint; use rustc_abi::{Align, Size, VariantIdx}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -9,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::ty::{self, ExistentialTraitRef, Ty, TyCtxt}; use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata}; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::debuginfo::utils::{DIB, create_DIArray, debug_context}; use crate::llvm::debuginfo::{DIFlags, DIScope, DIType}; use crate::llvm::{self}; @@ -191,7 +192,7 @@ pub(super) fn stub<'ll, 'tcx>( containing_scope: Option<&'ll DIScope>, flags: DIFlags, ) -> StubInfo<'ll, 'tcx> { - let empty_array = create_DIArray(DIB(cx), &[]); + let no_elements: &[Option<&llvm::Metadata>] = &[]; let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); let (file_metadata, line_number) = if let Some(def_location) = def_location { @@ -207,10 +208,10 @@ pub(super) fn stub<'ll, 'tcx>( _ => None, }; unsafe { - llvm::LLVMRustDIBuilderCreateStructType( + llvm::LLVMDIBuilderCreateStructType( DIB(cx), containing_scope, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, @@ -218,28 +219,30 @@ pub(super) fn stub<'ll, 'tcx>( align.bits() as u32, flags, None, - empty_array, - 0, + no_elements.as_ptr(), + no_elements.len() as c_uint, + 0u32, // (Objective-C runtime version; default is 0) vtable_holder, - unique_type_id_str.as_c_char_ptr(), + unique_type_id_str.as_ptr(), unique_type_id_str.len(), ) } } Stub::Union => unsafe { - llvm::LLVMRustDIBuilderCreateUnionType( + llvm::LLVMDIBuilderCreateUnionType( DIB(cx), containing_scope, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, size.bits(), align.bits() as u32, flags, - Some(empty_array), - 0, - unique_type_id_str.as_c_char_ptr(), + no_elements.as_ptr(), + no_elements.len() as c_uint, + 0u32, // (Objective-C runtime version; default is 0) + unique_type_id_str.as_ptr(), unique_type_id_str.len(), ) }, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 79334f7f9fe..af64e4ebed0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -52,15 +52,6 @@ mod utils; use self::create_scope_map::compute_mir_scopes; pub(crate) use self::metadata::build_global_var_di_node; -// FIXME(Zalathar): These `DW_TAG_*` constants are fake values that were -// removed from LLVM in 2015, and are only used by our own `RustWrapper.cpp` -// to decide which C++ API to call. Instead, we should just have two separate -// FFI functions and choose the correct one on the Rust side. -#[allow(non_upper_case_globals)] -const DW_TAG_auto_variable: c_uint = 0x100; -#[allow(non_upper_case_globals)] -const DW_TAG_arg_variable: c_uint = 0x101; - /// A context object for maintaining all state needed by the debuginfo module. pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { llmod: &'ll llvm::Module, @@ -174,35 +165,38 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> { if direct_offset.bytes() > 0 { addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(direct_offset.bytes() as u64); + addr_ops.push(direct_offset.bytes()); } for &offset in indirect_offsets { addr_ops.push(DW_OP_deref); if offset.bytes() > 0 { addr_ops.push(DW_OP_plus_uconst); - addr_ops.push(offset.bytes() as u64); + addr_ops.push(offset.bytes()); } } if let Some(fragment) = fragment { // `DW_OP_LLVM_fragment` takes as arguments the fragment's // offset and size, both of them in bits. addr_ops.push(DW_OP_LLVM_fragment); - addr_ops.push(fragment.start.bits() as u64); - addr_ops.push((fragment.end - fragment.start).bits() as u64); + addr_ops.push(fragment.start.bits()); + addr_ops.push((fragment.end - fragment.start).bits()); } + let di_builder = DIB(self.cx()); + let addr_expr = unsafe { + llvm::LLVMDIBuilderCreateExpression(di_builder, addr_ops.as_ptr(), addr_ops.len()) + }; unsafe { // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. - llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(self.cx()), + llvm::LLVMDIBuilderInsertDeclareRecordAtEnd( + di_builder, variable_alloca, dbg_var, - addr_ops.as_ptr(), - addr_ops.len() as c_uint, + addr_expr, dbg_loc, self.llbb(), - ); - } + ) + }; } fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { @@ -349,7 +343,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let file_metadata = file_metadata(self, &loc.file); let function_type_metadata = - create_subroutine_type(self, get_function_signature(self, fn_abi)); + create_subroutine_type(self, &get_function_signature(self, fn_abi)); let mut name = String::with_capacity(64); type_names::push_item_name(tcx, def_id, false, &mut name); @@ -441,9 +435,9 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_function_signature<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - ) -> &'ll DIArray { + ) -> Vec<Option<&'ll llvm::Metadata>> { if cx.sess().opts.debuginfo != DebugInfo::Full { - return create_DIArray(DIB(cx), &[]); + return vec![]; } let mut signature = Vec::with_capacity(fn_abi.args.len() + 1); @@ -484,7 +478,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { .extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty)))); } - create_DIArray(DIB(cx), &signature[..]) + signature } fn get_template_parameters<'ll, 'tcx>( @@ -630,28 +624,39 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let type_metadata = spanned_type_di_node(self, variable_type, span); - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable => (0, DW_TAG_auto_variable), - }; let align = self.align_of(variable_type); let name = variable_name.as_str(); - unsafe { - llvm::LLVMRustDIBuilderCreateVariable( - DIB(self), - dwarf_tag, - scope_metadata, - name.as_c_char_ptr(), - name.len(), - file_metadata, - loc.line, - type_metadata, - true, - DIFlags::FlagZero, - argument_index, - align.bits() as u32, - ) + + match variable_kind { + ArgumentVariable(arg_index) => unsafe { + llvm::LLVMDIBuilderCreateParameterVariable( + DIB(self), + scope_metadata, + name.as_ptr(), + name.len(), + arg_index as c_uint, + file_metadata, + loc.line, + type_metadata, + llvm::Bool::TRUE, // (preserve descriptor during optimizations) + DIFlags::FlagZero, + ) + }, + LocalVariable => unsafe { + llvm::LLVMDIBuilderCreateAutoVariable( + DIB(self), + scope_metadata, + name.as_ptr(), + name.len(), + file_metadata, + loc.line, + type_metadata, + llvm::Bool::TRUE, // (preserve descriptor during optimizations) + DIFlags::FlagZero, + align.bits() as u32, + ) + }, } } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index cc1d504b430..7e1e49310f6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -28,7 +28,7 @@ pub(crate) fn create_DIArray<'ll>( builder: &DIBuilder<'ll>, arr: &[Option<&'ll DIDescriptor>], ) -> &'ll DIArray { - unsafe { llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) } + unsafe { llvm::LLVMDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len()) } } #[inline] diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 960a895a203..36cdb498839 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -76,7 +76,7 @@ pub(crate) fn declare_raw_fn<'ll, 'tcx>( attrs.push(llvm::AttributeKind::NoRedZone.create_attr(cx.llcx)); } - attrs.extend(attributes::non_lazy_bind_attr(cx)); + attrs.extend(attributes::non_lazy_bind_attr(cx, cx.tcx.sess)); attributes::apply_to_llfn(llfn, Function, &attrs); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 85f71f331a4..467655b0bfc 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -18,7 +18,6 @@ use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -298,7 +297,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let align = if name == sym::unaligned_volatile_load { 1 } else { - result.layout.align.abi.bytes() as u32 + result.layout.align.bytes() as u32 }; unsafe { llvm::LLVMSetAlignment(load, align); @@ -674,7 +673,7 @@ fn catch_unwind_intrinsic<'ll, 'tcx>( catch_func: &'ll Value, dest: PlaceRef<'tcx, &'ll Value>, ) { - if bx.sess().panic_strategy() == PanicStrategy::Abort { + if !bx.sess().panic_strategy().unwinds() { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.call(try_func_ty, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; @@ -1048,7 +1047,7 @@ fn codegen_emcc_try<'ll, 'tcx>( // create an alloca and pass a pointer to that. let ptr_size = bx.tcx().data_layout.pointer_size(); let ptr_align = bx.tcx().data_layout.pointer_align().abi; - let i8_align = bx.tcx().data_layout.i8_align.abi; + let i8_align = bx.tcx().data_layout.i8_align; // Required in order for there to be no padding between the fields. assert!(i8_align <= ptr_align); let catch_data = bx.alloca(2 * ptr_size, ptr_align); @@ -1208,10 +1207,14 @@ fn codegen_autodiff<'ll, 'tcx>( adjust_activity_to_abi( tcx, - fn_source.ty(tcx, TypingEnv::fully_monomorphized()), + fn_source, + TypingEnv::fully_monomorphized(), &mut diff_attrs.input_activity, ); + let fnc_tree = + rustc_middle::ty::fnc_typetrees(tcx, fn_source.ty(tcx, TypingEnv::fully_monomorphized())); + // Build body generate_enzyme_call( bx, @@ -1222,6 +1225,7 @@ fn codegen_autodiff<'ll, 'tcx>( &val_arr, diff_attrs.clone(), result, + fnc_tree, ); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6fb23d09843..2405a25c702 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -45,6 +45,7 @@ use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; +use rustc_target::spec::{RelocModel, TlsModel}; mod abi; mod allocator; @@ -67,6 +68,7 @@ mod llvm_util; mod mono_item; mod type_; mod type_of; +mod typetree; mod va_arg; mod value; @@ -230,6 +232,10 @@ impl CodegenBackend for LlvmCodegenBackend { crate::DEFAULT_LOCALE_RESOURCE } + fn name(&self) -> &'static str { + "llvm" + } + fn init(&self, sess: &Session) { llvm_util::init(sess); // Make sure llvm is inited } @@ -244,16 +250,7 @@ impl CodegenBackend for LlvmCodegenBackend { match req.kind { PrintKind::RelocationModels => { writeln!(out, "Available relocation models:").unwrap(); - for name in &[ - "static", - "pic", - "pie", - "dynamic-no-pic", - "ropi", - "rwpi", - "ropi-rwpi", - "default", - ] { + for name in RelocModel::ALL.iter().map(RelocModel::desc).chain(["default"]) { writeln!(out, " {name}").unwrap(); } writeln!(out).unwrap(); @@ -267,9 +264,7 @@ impl CodegenBackend for LlvmCodegenBackend { } PrintKind::TlsModels => { writeln!(out, "Available TLS models:").unwrap(); - for name in - &["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"] - { + for name in TlsModel::ALL.iter().map(TlsModel::desc) { writeln!(out, " {name}").unwrap(); } writeln!(out).unwrap(); @@ -359,7 +354,14 @@ impl CodegenBackend for LlvmCodegenBackend { // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. - link_binary(sess, &LlvmArchiveBuilderBuilder, codegen_results, metadata, outputs); + link_binary( + sess, + &LlvmArchiveBuilderBuilder, + codegen_results, + metadata, + outputs, + self.name(), + ); } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 56d756e52cc..e63043b2122 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -3,9 +3,36 @@ use libc::{c_char, c_uint}; use super::MetadataKindId; -use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; +use super::ffi::{AttributeKind, BasicBlock, Context, Metadata, Module, Type, Value}; use crate::llvm::{Bool, Builder}; +// TypeTree types +pub(crate) type CTypeTreeRef = *mut EnzymeTypeTree; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub(crate) struct EnzymeTypeTree { + _unused: [u8; 0], +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub(crate) enum CConcreteType { + DT_Anything = 0, + DT_Integer = 1, + DT_Pointer = 2, + DT_Half = 3, + DT_Float = 4, + DT_Double = 5, + DT_Unknown = 6, + DT_FP128 = 9, +} + +pub(crate) struct TypeTree { + pub(crate) inner: CTypeTreeRef, +} + #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme @@ -59,19 +86,49 @@ pub(crate) enum LLVMRustVerifierFailureAction { LLVMReturnStatusAction = 2, } -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) use self::Enzyme_AD::*; -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) mod Enzyme_AD { use std::ffi::{CString, c_char}; use libc::c_void; + use super::{CConcreteType, CTypeTreeRef, Context}; + unsafe extern "C" { pub(crate) fn EnzymeSetCLBool(arg1: *mut ::std::os::raw::c_void, arg2: u8); pub(crate) fn EnzymeSetCLString(arg1: *mut ::std::os::raw::c_void, arg2: *const c_char); } + + // TypeTree functions + unsafe extern "C" { + pub(crate) fn EnzymeNewTypeTree() -> CTypeTreeRef; + pub(crate) fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef; + pub(crate) fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef; + pub(crate) fn EnzymeFreeTypeTree(CTT: CTypeTreeRef); + pub(crate) fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool; + pub(crate) fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64); + pub(crate) fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef); + pub(crate) fn EnzymeTypeTreeShiftIndiciesEq( + arg1: CTypeTreeRef, + data_layout: *const c_char, + offset: i64, + max_size: i64, + add_offset: u64, + ); + pub(crate) fn EnzymeTypeTreeInsertEq( + CTT: CTypeTreeRef, + indices: *const i64, + len: usize, + ct: CConcreteType, + ctx: &Context, + ); + pub(crate) fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char; + pub(crate) fn EnzymeTypeTreeToStringFree(arg1: *const c_char); + } + unsafe extern "C" { static mut EnzymePrintPerf: c_void; static mut EnzymePrintActivity: c_void; @@ -134,13 +191,74 @@ pub(crate) mod Enzyme_AD { } } -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) use self::Fallback_AD::*; -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) mod Fallback_AD { #![allow(unused_variables)] + use libc::c_char; + + use super::{CConcreteType, CTypeTreeRef, Context}; + + // TypeTree function fallbacks + pub(crate) unsafe fn EnzymeNewTypeTree() -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeFreeTypeTree(CTT: CTypeTreeRef) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeShiftIndiciesEq( + arg1: CTypeTreeRef, + data_layout: *const c_char, + offset: i64, + max_size: i64, + add_offset: u64, + ) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeInsertEq( + CTT: CTypeTreeRef, + indices: *const i64, + len: usize, + ct: CConcreteType, + ctx: &Context, + ) { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char { + unimplemented!() + } + + pub(crate) unsafe fn EnzymeTypeTreeToStringFree(arg1: *const c_char) { + unimplemented!() + } + pub(crate) fn set_inline(val: bool) { unimplemented!() } @@ -169,3 +287,89 @@ pub(crate) mod Fallback_AD { unimplemented!() } } + +impl TypeTree { + pub(crate) fn new() -> TypeTree { + let inner = unsafe { EnzymeNewTypeTree() }; + TypeTree { inner } + } + + pub(crate) fn from_type(t: CConcreteType, ctx: &Context) -> TypeTree { + let inner = unsafe { EnzymeNewTypeTreeCT(t, ctx) }; + TypeTree { inner } + } + + pub(crate) fn merge(self, other: Self) -> Self { + unsafe { + EnzymeMergeTypeTree(self.inner, other.inner); + } + drop(other); + self + } + + #[must_use] + pub(crate) fn shift( + self, + layout: &str, + offset: isize, + max_size: isize, + add_offset: usize, + ) -> Self { + let layout = std::ffi::CString::new(layout).unwrap(); + + unsafe { + EnzymeTypeTreeShiftIndiciesEq( + self.inner, + layout.as_ptr(), + offset as i64, + max_size as i64, + add_offset as u64, + ); + } + + self + } + + pub(crate) fn insert(&mut self, indices: &[i64], ct: CConcreteType, ctx: &Context) { + unsafe { + EnzymeTypeTreeInsertEq(self.inner, indices.as_ptr(), indices.len(), ct, ctx); + } + } +} + +impl Clone for TypeTree { + fn clone(&self) -> Self { + let inner = unsafe { EnzymeNewTypeTreeTR(self.inner) }; + TypeTree { inner } + } +} + +impl std::fmt::Display for TypeTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ptr = unsafe { EnzymeTypeTreeToString(self.inner) }; + let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; + match cstr.to_str() { + Ok(x) => write!(f, "{}", x)?, + Err(err) => write!(f, "could not parse: {}", err)?, + } + + // delete C string pointer + unsafe { + EnzymeTypeTreeToStringFree(ptr); + } + + Ok(()) + } +} + +impl std::fmt::Debug for TypeTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + <Self as std::fmt::Display>::fmt(self, f) + } +} + +impl Drop for TypeTree { + fn drop(&mut self) { + unsafe { EnzymeFreeTypeTree(self.inner) } + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 0679f55ab7f..afd2991a09c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -24,9 +24,9 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ - DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, - DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, + DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, + DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, + DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -710,6 +710,7 @@ pub(crate) enum MemoryEffects { None, ReadOnly, InaccessibleMemOnly, + ReadOnlyNotPure, } /// LLVMOpcode @@ -806,6 +807,8 @@ unsafe extern "C" { pub(crate) type Metadata; pub(crate) type BasicBlock; pub(crate) type Comdat; + /// `&'ll DbgRecord` represents `LLVMDbgRecordRef`. + pub(crate) type DbgRecord; } #[repr(C)] pub(crate) struct Builder<'a>(InvariantOpaque<'a>); @@ -890,7 +893,6 @@ pub(crate) mod debuginfo { pub(crate) type DIVariable = DIDescriptor; pub(crate) type DIGlobalVariableExpression = DIDescriptor; pub(crate) type DIArray = DIDescriptor; - pub(crate) type DISubrange = DIDescriptor; pub(crate) type DIEnumerator = DIDescriptor; pub(crate) type DITemplateTypeParameter = DIDescriptor; @@ -1052,6 +1054,8 @@ unsafe extern "C" { SLen: c_uint, ) -> MetadataKindId; + pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull<TargetMachine>); + // Create modules. pub(crate) fn LLVMModuleCreateWithNameInContext( ModuleID: *const c_char, @@ -1237,6 +1241,7 @@ unsafe extern "C" { pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind); + pub(crate) safe fn LLVMSetExternallyInitialized(GlobalVar: &Value, IsExtInit: Bool); // Operations on attributes pub(crate) fn LLVMCreateStringAttribute( @@ -1870,6 +1875,177 @@ unsafe extern "C" { Scope: &'ll Metadata, InlinedAt: Option<&'ll Metadata>, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateSubroutineType<'ll>( + Builder: &DIBuilder<'ll>, + File: Option<&'ll Metadata>, // (ignored and has no effect) + ParameterTypes: *const Option<&'ll Metadata>, + NumParameterTypes: c_uint, + Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateUnionType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: Option<&'ll Metadata>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + SizeInBits: u64, + AlignInBits: u32, + Flags: DIFlags, + Elements: *const Option<&'ll Metadata>, + NumElements: c_uint, + RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0) + UniqueId: *const c_uchar, // See "PTR_LEN_STR". + UniqueIdLen: size_t, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateArrayType<'ll>( + Builder: &DIBuilder<'ll>, + Size: u64, + Align: u32, + Ty: &'ll Metadata, + Subscripts: *const &'ll Metadata, + NumSubscripts: c_uint, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateBasicType<'ll>( + Builder: &DIBuilder<'ll>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + SizeInBits: u64, + Encoding: c_uint, // (`LLVMDWARFTypeEncoding`) + Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreatePointerType<'ll>( + Builder: &DIBuilder<'ll>, + PointeeTy: &'ll Metadata, + SizeInBits: u64, + AlignInBits: u32, + AddressSpace: c_uint, // (optional DWARF address space; default is 0) + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateStructType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: Option<&'ll Metadata>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + SizeInBits: u64, + AlignInBits: u32, + Flags: DIFlags, + DerivedFrom: Option<&'ll Metadata>, + Elements: *const Option<&'ll Metadata>, + NumElements: c_uint, + RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0) + VTableHolder: Option<&'ll Metadata>, + UniqueId: *const c_uchar, // See "PTR_LEN_STR". + UniqueIdLen: size_t, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateMemberType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + SizeInBits: u64, + AlignInBits: u32, + OffsetInBits: u64, + Flags: DIFlags, + Ty: &'ll Metadata, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateStaticMemberType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + Type: &'ll Metadata, + Flags: DIFlags, + ConstantVal: Option<&'ll Value>, + AlignInBits: u32, + ) -> &'ll Metadata; + + /// Creates a "qualified type" in the C/C++ sense, by adding modifiers + /// like `const` or `volatile`. + pub(crate) fn LLVMDIBuilderCreateQualifiedType<'ll>( + Builder: &DIBuilder<'ll>, + Tag: c_uint, // (DWARF tag, e.g. `DW_TAG_const_type`) + Type: &'ll Metadata, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateTypedef<'ll>( + Builder: &DIBuilder<'ll>, + Type: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + Scope: Option<&'ll Metadata>, + AlignInBits: u32, // (optional; default is 0) + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderGetOrCreateSubrange<'ll>( + Builder: &DIBuilder<'ll>, + LowerBound: i64, + Count: i64, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderGetOrCreateArray<'ll>( + Builder: &DIBuilder<'ll>, + Data: *const Option<&'ll Metadata>, + NumElements: size_t, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateExpression<'ll>( + Builder: &DIBuilder<'ll>, + Addr: *const u64, + Length: size_t, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderInsertDeclareRecordAtEnd<'ll>( + Builder: &DIBuilder<'ll>, + Storage: &'ll Value, + VarInfo: &'ll Metadata, + Expr: &'ll Metadata, + DebugLoc: &'ll Metadata, + Block: &'ll BasicBlock, + ) -> &'ll DbgRecord; + + pub(crate) fn LLVMDIBuilderCreateAutoVariable<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + Ty: &'ll Metadata, + AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations." + Flags: DIFlags, + AlignInBits: u32, + ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateParameterVariable<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ArgNo: c_uint, + File: &'ll Metadata, + LineNo: c_uint, + Ty: &'ll Metadata, + AlwaysPreserve: llvm::Bool, // "If true, this descriptor will survive optimizations." + Flags: DIFlags, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2172,11 +2348,6 @@ unsafe extern "C" { SourceLen: size_t, ) -> &'a DIFile; - pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( - Builder: &DIBuilder<'a>, - ParameterTypes: &'a DIArray, - ) -> &'a DICompositeType; - pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, @@ -2210,66 +2381,6 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( - Builder: &DIBuilder<'a>, - Name: *const c_char, - NameLen: size_t, - SizeInBits: u64, - Encoding: c_uint, - ) -> &'a DIBasicType; - - pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( - Builder: &DIBuilder<'a>, - Type: &'a DIBasicType, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Scope: Option<&'a DIScope>, - ) -> &'a DIDerivedType; - - pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( - Builder: &DIBuilder<'a>, - PointeeTy: &'a DIType, - SizeInBits: u64, - AlignInBits: u32, - AddressSpace: c_uint, - Name: *const c_char, - NameLen: size_t, - ) -> &'a DIDerivedType; - - pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIDescriptor>, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNumber: c_uint, - SizeInBits: u64, - AlignInBits: u32, - Flags: DIFlags, - DerivedFrom: Option<&'a DIType>, - Elements: &'a DIArray, - RunTimeLang: c_uint, - VTableHolder: Option<&'a DIType>, - UniqueId: *const c_char, - UniqueIdLen: size_t, - ) -> &'a DICompositeType; - - pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - SizeInBits: u64, - AlignInBits: u32, - OffsetInBits: u64, - Flags: DIFlags, - Ty: &'a DIType, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, @@ -2285,25 +2396,6 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Ty: &'a DIType, - Flags: DIFlags, - val: Option<&'a Value>, - AlignInBits: u32, - ) -> &'a DIDerivedType; - - pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( - Builder: &DIBuilder<'a>, - Tag: c_uint, - Type: &'a DIType, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( Builder: &DIBuilder<'a>, Context: Option<&'a DIScope>, @@ -2320,51 +2412,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIGlobalVariableExpression; - pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( - Builder: &DIBuilder<'a>, - Tag: c_uint, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Ty: &'a DIType, - AlwaysPreserve: bool, - Flags: DIFlags, - ArgNo: c_uint, - AlignInBits: u32, - ) -> &'a DIVariable; - - pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( - Builder: &DIBuilder<'a>, - Size: u64, - AlignInBits: u32, - Ty: &'a DIType, - Subscripts: &'a DIArray, - ) -> &'a DIType; - - pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( - Builder: &DIBuilder<'a>, - Lo: i64, - Count: i64, - ) -> &'a DISubrange; - - pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( - Builder: &DIBuilder<'a>, - Ptr: *const Option<&'a DIDescriptor>, - Count: c_uint, - ) -> &'a DIArray; - - pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( - Builder: &DIBuilder<'a>, - Val: &'a Value, - VarInfo: &'a DIVariable, - AddrOps: *const u64, - AddrOpsCount: c_uint, - DL: &'a DILocation, - InsertAtEnd: &'a BasicBlock, - ); - pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( Builder: &DIBuilder<'a>, Name: *const c_char, @@ -2388,22 +2435,6 @@ unsafe extern "C" { IsScoped: bool, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIScope>, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNumber: c_uint, - SizeInBits: u64, - AlignInBits: u32, - Flags: DIFlags, - Elements: Option<&'a DIArray>, - RunTimeLang: c_uint, - UniqueId: *const c_char, - UniqueIdLen: size_t, - ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, @@ -2480,12 +2511,13 @@ unsafe extern "C" { OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, UseEmulatedTls: bool, - ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR". - ArgsCstrBuffLen: usize, + Argv0: *const c_uchar, // See "PTR_LEN_STR". + Argv0Len: size_t, + CommandLineArgs: *const c_uchar, // See "PTR_LEN_STR". + CommandLineArgsLen: size_t, UseWasmEH: bool, ) -> *mut TargetMachine; - pub(crate) fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine); pub(crate) fn LLVMRustAddLibraryInfo<'a>( PM: &PassManager<'a>, M: &'a Module, diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index d6974e22c85..1115d82fa85 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -258,6 +258,10 @@ pub(crate) fn set_alignment(llglobal: &Value, align: Align) { } } +pub(crate) fn set_externally_initialized(llglobal: &Value, is_ext_init: bool) { + LLVMSetExternallyInitialized(llglobal, is_ext_init.to_llvm_bool()); +} + /// Get the `name`d comdat from `llmod` and assign it to `llglobal`. /// /// Inserts the comdat into `llmod` if it does not exist. diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index d927ffd78c2..3b920168e06 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -106,7 +106,7 @@ unsafe fn configure_llvm(sess: &Session) { if sess.target.os == "emscripten" && !sess.opts.unstable_opts.emscripten_wasm_eh - && sess.panic_strategy() == PanicStrategy::Unwind + && sess.panic_strategy().unwinds() { add("-enable-emscripten-cxx-exceptions", false); } @@ -217,27 +217,16 @@ impl<'a> IntoIterator for LLVMFeature<'a> { /// Rust can also be build with an external precompiled version of LLVM which might lead to failures /// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics. pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> { - let arch = if sess.target.arch == "x86_64" { - "x86" - } else if sess.target.arch == "arm64ec" { - "aarch64" - } else if sess.target.arch == "sparc64" { - "sparc" - } else if sess.target.arch == "powerpc64" { - "powerpc" - } else { - &*sess.target.arch + let raw_arch = &*sess.target.arch; + let arch = match raw_arch { + "x86_64" => "x86", + "arm64ec" => "aarch64", + "sparc64" => "sparc", + "powerpc64" => "powerpc", + _ => raw_arch, }; + let (major, _, _) = get_version(); match (arch, s) { - ("x86", "sse4.2") => Some(LLVMFeature::with_dependencies( - "sse4.2", - smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], - )), - ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), - ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), - ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), - ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), - ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), ("aarch64", "rcpc2") => Some(LLVMFeature::new("rcpc-immo")), ("aarch64", "dpb") => Some(LLVMFeature::new("ccpp")), ("aarch64", "dpb2") => Some(LLVMFeature::new("ccdp")), @@ -246,9 +235,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea ("aarch64", "pmuv3") => Some(LLVMFeature::new("perfmon")), ("aarch64", "paca") => Some(LLVMFeature::new("pauth")), ("aarch64", "pacg") => Some(LLVMFeature::new("pauth")), - // Before LLVM 20 those two features were packaged together as b16b16 - ("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), - ("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), ("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")), // Rust ties fp and neon together. ("aarch64", "neon") => Some(LLVMFeature::with_dependencies( @@ -262,57 +248,26 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea // Filter out features that are not supported by the current LLVM version ("aarch64", "fpmr") => None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), - // NVPTX targets added in LLVM 20 - ("nvptx64", "sm_100") if get_version().0 < 20 => None, - ("nvptx64", "sm_100a") if get_version().0 < 20 => None, - ("nvptx64", "sm_101") if get_version().0 < 20 => None, - ("nvptx64", "sm_101a") if get_version().0 < 20 => None, - ("nvptx64", "sm_120") if get_version().0 < 20 => None, - ("nvptx64", "sm_120a") if get_version().0 < 20 => None, - ("nvptx64", "ptx86") if get_version().0 < 20 => None, - ("nvptx64", "ptx87") if get_version().0 < 20 => None, // Filter out features that are not supported by the current LLVM version - ("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq") - if get_version().0 < 20 => - { - None - } - ("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None, - // Filter out features that are not supported by the current LLVM version - ("riscv32" | "riscv64", "zacas" | "rva23u64" | "supm") if get_version().0 < 20 => None, - ( - "s390x", - "message-security-assist-extension12" - | "concurrent-functions" - | "miscellaneous-extensions-4" - | "vector-enhancements-3" - | "vector-packed-decimal-enhancement-3", - ) if get_version().0 < 20 => None, + ("loongarch32" | "loongarch64", "32s") if major < 21 => None, + ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), + ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), + ("x86", "sse4.2") => Some(LLVMFeature::with_dependencies( + "sse4.2", + smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], + )), + ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), + ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), + ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), + ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), + ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], )), - // Support for `wide-arithmetic` will first land in LLVM 20 as part of - // llvm/llvm-project#111598 - ("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None, - ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), - // In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used". - // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28 - // Before LLVM 19, there was no `v8plus` feature and `v9` means "SPARC-V9 instruction available". - // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26 - ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")), - ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), - // These new `amx` variants and `movrs` were introduced in LLVM20 - ("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose") - if get_version().0 < 20 => - { - None - } - ("x86", "movrs") if get_version().0 < 20 => None, ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")), - ("x86", "avx10.2") if get_version().0 < 20 => None, - ("x86", "avx10.2") if get_version().0 >= 20 => Some(LLVMFeature::new("avx10.2-512")), + ("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")), ("x86", "apxf") => Some(LLVMFeature::with_dependencies( "egpr", smallvec![ @@ -716,17 +671,7 @@ pub(crate) fn global_llvm_features( }; // Features implied by an implicit or explicit `--target`. - features.extend( - sess.target - .features - .split(',') - .filter(|v| !v.is_empty()) - // Drop +v8plus feature introduced in LLVM 20. - // (Hard-coded target features do not go through `to_llvm_feature` since they already - // are LLVM feature names, hence we need a special case here.) - .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0)) - .map(String::from), - ); + features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from)); if wants_wasm_eh(sess) && sess.panic_strategy() == PanicStrategy::Unwind { features.push("+exception-handling".into()); diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs new file mode 100644 index 00000000000..7e263503700 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -0,0 +1,122 @@ +use rustc_ast::expand::typetree::FncTree; +#[cfg(feature = "llvm_enzyme")] +use { + crate::attributes, + rustc_ast::expand::typetree::TypeTree as RustTypeTree, + std::ffi::{CString, c_char, c_uint}, +}; + +use crate::llvm::{self, Value}; + +#[cfg(feature = "llvm_enzyme")] +fn to_enzyme_typetree( + rust_typetree: RustTypeTree, + _data_layout: &str, + llcx: &llvm::Context, +) -> llvm::TypeTree { + let mut enzyme_tt = llvm::TypeTree::new(); + process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx); + enzyme_tt +} +#[cfg(feature = "llvm_enzyme")] +fn process_typetree_recursive( + enzyme_tt: &mut llvm::TypeTree, + rust_typetree: &RustTypeTree, + parent_indices: &[i64], + llcx: &llvm::Context, +) { + for rust_type in &rust_typetree.0 { + let concrete_type = match rust_type.kind { + rustc_ast::expand::typetree::Kind::Anything => llvm::CConcreteType::DT_Anything, + rustc_ast::expand::typetree::Kind::Integer => llvm::CConcreteType::DT_Integer, + rustc_ast::expand::typetree::Kind::Pointer => llvm::CConcreteType::DT_Pointer, + rustc_ast::expand::typetree::Kind::Half => llvm::CConcreteType::DT_Half, + rustc_ast::expand::typetree::Kind::Float => llvm::CConcreteType::DT_Float, + rustc_ast::expand::typetree::Kind::Double => llvm::CConcreteType::DT_Double, + rustc_ast::expand::typetree::Kind::F128 => llvm::CConcreteType::DT_FP128, + rustc_ast::expand::typetree::Kind::Unknown => llvm::CConcreteType::DT_Unknown, + }; + + let mut indices = parent_indices.to_vec(); + if !parent_indices.is_empty() { + indices.push(rust_type.offset as i64); + } else if rust_type.offset == -1 { + indices.push(-1); + } else { + indices.push(rust_type.offset as i64); + } + + enzyme_tt.insert(&indices, concrete_type, llcx); + + if rust_type.kind == rustc_ast::expand::typetree::Kind::Pointer + && !rust_type.child.0.is_empty() + { + process_typetree_recursive(enzyme_tt, &rust_type.child, &indices, llcx); + } + } +} + +#[cfg(feature = "llvm_enzyme")] +pub(crate) fn add_tt<'ll>( + llmod: &'ll llvm::Module, + llcx: &'ll llvm::Context, + fn_def: &'ll Value, + tt: FncTree, +) { + let inputs = tt.args; + let ret_tt: RustTypeTree = tt.ret; + + let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) }; + let llvm_data_layout = + std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes()) + .expect("got a non-UTF8 data-layout from LLVM"); + + let attr_name = "enzyme_type"; + let c_attr_name = CString::new(attr_name).unwrap(); + + for (i, input) in inputs.iter().enumerate() { + unsafe { + let enzyme_tt = to_enzyme_typetree(input.clone(), llvm_data_layout, llcx); + let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); + let c_str = std::ffi::CStr::from_ptr(c_str); + + let attr = llvm::LLVMCreateStringAttribute( + llcx, + c_attr_name.as_ptr(), + c_attr_name.as_bytes().len() as c_uint, + c_str.as_ptr(), + c_str.to_bytes().len() as c_uint, + ); + + attributes::apply_to_llfn(fn_def, llvm::AttributePlace::Argument(i as u32), &[attr]); + llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + } + } + + unsafe { + let enzyme_tt = to_enzyme_typetree(ret_tt, llvm_data_layout, llcx); + let c_str = llvm::EnzymeTypeTreeToString(enzyme_tt.inner); + let c_str = std::ffi::CStr::from_ptr(c_str); + + let ret_attr = llvm::LLVMCreateStringAttribute( + llcx, + c_attr_name.as_ptr(), + c_attr_name.as_bytes().len() as c_uint, + c_str.as_ptr(), + c_str.to_bytes().len() as c_uint, + ); + + attributes::apply_to_llfn(fn_def, llvm::AttributePlace::ReturnValue, &[ret_attr]); + llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + } +} + +#[cfg(not(feature = "llvm_enzyme"))] +pub(crate) fn add_tt<'ll>( + _llmod: &'ll llvm::Module, + _llcx: &'ll llvm::Context, + _fn_def: &'ll Value, + _tt: FncTree, +) { + unimplemented!() +} diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index ab08125217f..234366e491c 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -193,7 +193,7 @@ fn emit_aapcs_va_arg<'ll, 'tcx>( // the offset again. bx.switch_to_block(maybe_reg); - if gr_type && layout.align.abi.bytes() > 8 { + if gr_type && layout.align.bytes() > 8 { reg_off_v = bx.add(reg_off_v, bx.const_i32(15)); reg_off_v = bx.and(reg_off_v, bx.const_i32(-16)); } @@ -291,7 +291,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) // fpr }; - let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align.abi); + let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align); // "Align" the register count when the type is passed as `i64`. if is_i64 || (is_f64 && is_soft_float_abi) { @@ -329,7 +329,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( // Increase the used-register count. let reg_incr = if is_i64 || (is_f64 && is_soft_float_abi) { 2 } else { 1 }; let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr)); - bx.store(new_num_regs, num_regs_addr, dl.i8_align.abi); + bx.store(new_num_regs, num_regs_addr, dl.i8_align); bx.br(end); @@ -339,7 +339,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( let mem_addr = { bx.switch_to_block(in_mem); - bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align.abi); + bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align); // Everything in the overflow area is rounded up to a size of at least 4. let overflow_area_align = Align::from_bytes(4).unwrap(); @@ -738,6 +738,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( src_align, bx.const_u32(layout.layout.size().bytes() as u32), MemFlags::empty(), + None, ); tmp } else { @@ -760,7 +761,7 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( // byte boundary if alignment needed by type exceeds 8 byte boundary. // It isn't stated explicitly in the standard, but in practice we use // alignment greater than 16 where necessary. - if layout.layout.align.abi.bytes() > 8 { + if layout.layout.align.bytes() > 8 { unreachable!("all instances of VaArgSafe have an alignment <= 8"); } @@ -813,7 +814,7 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( let va_ndx_offset = va_reg_offset + 4; let offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_ndx_offset)); - let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align.abi); + let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align); let offset = round_up_to_alignment(bx, offset, layout.align.abi); let slot_size = layout.size.align_to(Align::from_bytes(4).unwrap()).bytes() as i32; diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 5462163f4ff..9c5a3d839ce 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,9 +8,7 @@ edition = "2024" ar_archive_writer = "0.5" bitflags = "2.4.1" bstr = "1.11.3" -# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_windows_rc`. -cc = "=1.2.16" +find-msvc-tools = "0.1.2" itertools = "0.12" pathdiff = "0.2.0" regex = "1.4" diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index dd688b8b345..91c3806df4c 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -8,8 +8,6 @@ codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} -codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto - codegen_ssa_bare_instruction_set = `#[instruction_set]` requires an argument codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty @@ -31,7 +29,7 @@ codegen_ssa_cpu_required = target requires explicitly specifying a cpu with `-C codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} codegen_ssa_dlltool_fail_import_library = - Dlltool could not create import library with {$dlltool_path} {$dlltool_args}: + dlltool could not create import library with {$dlltool_path} {$dlltool_args}: {$stdout} {$stderr} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 48b01ea2df1..db2f2dd65b0 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -9,7 +9,7 @@ use std::path::{Path, PathBuf}; use std::process::{Output, Stdio}; use std::{env, fmt, fs, io, mem, str}; -use cc::windows_registry; +use find_msvc_tools; use itertools::Itertools; use regex::Regex; use rustc_arena::TypedArena; @@ -47,8 +47,8 @@ use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, - LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, - SanitizerSet, SplitDebuginfo, + LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, RelocModel, RelroLevel, SanitizerSet, + SplitDebuginfo, }; use tracing::{debug, info, warn}; @@ -79,6 +79,7 @@ pub fn link_binary( codegen_results: CodegenResults, metadata: EncodedMetadata, outputs: &OutputFilenames, + codegen_backend: &'static str, ) { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); @@ -154,6 +155,7 @@ pub fn link_binary( &codegen_results, &metadata, path.as_ref(), + codegen_backend, ); } } @@ -680,6 +682,7 @@ fn link_natively( codegen_results: &CodegenResults, metadata: &EncodedMetadata, tmpdir: &Path, + codegen_backend: &'static str, ) { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); @@ -705,6 +708,7 @@ fn link_natively( codegen_results, metadata, self_contained_components, + codegen_backend, ); linker::disable_localization(&mut cmd); @@ -877,9 +881,9 @@ fn link_natively( // All Microsoft `link.exe` linking ror codes are // four digit numbers in the range 1000 to 9999 inclusive if is_msvc_link_exe && (code < 1000 || code > 9999) { - let is_vs_installed = windows_registry::find_vs_version().is_ok(); + let is_vs_installed = find_msvc_tools::find_vs_version().is_ok(); let has_linker = - windows_registry::find_tool(&sess.target.arch, "link.exe").is_some(); + find_msvc_tools::find_tool(&sess.target.arch, "link.exe").is_some(); sess.dcx().emit_note(errors::LinkExeUnexpectedError); @@ -2208,6 +2212,7 @@ fn linker_with_args( codegen_results: &CodegenResults, metadata: &EncodedMetadata, self_contained_components: LinkSelfContainedComponents, + codegen_backend: &'static str, ) -> Command { let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( @@ -2216,6 +2221,7 @@ fn linker_with_args( flavor, self_contained_components.are_any_components_enabled(), &codegen_results.crate_info.target_cpu, + codegen_backend, ); let link_output_kind = link_output_kind(sess, crate_type); @@ -2512,10 +2518,10 @@ fn add_order_independent_options( if sess.target.os == "emscripten" { cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh { "-fwasm-exceptions" - } else if sess.panic_strategy() == PanicStrategy::Abort { - "-sDISABLE_EXCEPTION_CATCHING=1" - } else { + } else if sess.panic_strategy().unwinds() { "-sDISABLE_EXCEPTION_CATCHING=0" + } else { + "-sDISABLE_EXCEPTION_CATCHING=1" }); } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index a2efd420a32..e644a43f883 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -4,7 +4,7 @@ use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::{env, io, iter, mem, str}; -use cc::windows_registry; +use find_msvc_tools; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::{ find_native_static_library, try_find_native_dynamic_library, try_find_native_static_library, @@ -52,8 +52,9 @@ pub(crate) fn get_linker<'a>( flavor: LinkerFlavor, self_contained: bool, target_cpu: &'a str, + codegen_backend: &'static str, ) -> Box<dyn Linker + 'a> { - let msvc_tool = windows_registry::find_tool(&sess.target.arch, "link.exe"); + let msvc_tool = find_msvc_tools::find_tool(&sess.target.arch, "link.exe"); // If our linker looks like a batch script on Windows then to execute this // we'll need to spawn `cmd` explicitly. This is primarily done to handle @@ -117,7 +118,6 @@ pub(crate) fn get_linker<'a>( if sess.target.is_like_msvc && let Some(ref tool) = msvc_tool { - cmd.args(tool.args()); for (k, v) in tool.env() { if k == "PATH" { new_path.extend(env::split_paths(v)); @@ -155,6 +155,7 @@ pub(crate) fn get_linker<'a>( is_ld: cc == Cc::No, is_gnu: flavor.is_gnu(), uses_lld: flavor.uses_lld(), + codegen_backend, }) as Box<dyn Linker>, LinkerFlavor::Msvc(..) => Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>, LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>, @@ -368,6 +369,7 @@ struct GccLinker<'a> { is_ld: bool, is_gnu: bool, uses_lld: bool, + codegen_backend: &'static str, } impl<'a> GccLinker<'a> { @@ -424,9 +426,15 @@ impl<'a> GccLinker<'a> { if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use { self.link_arg(&format!("-plugin-opt=sample-profile={}", path.display())); }; + let prefix = if self.codegen_backend == "gcc" { + // The GCC linker plugin requires a leading dash. + "-" + } else { + "" + }; self.link_args(&[ - &format!("-plugin-opt={opt_level}"), - &format!("-plugin-opt=mcpu={}", self.target_cpu), + &format!("-plugin-opt={prefix}{opt_level}"), + &format!("-plugin-opt={prefix}mcpu={}", self.target_cpu), ]); } @@ -845,6 +853,11 @@ impl<'a> Linker for GccLinker<'a> { self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); } self.link_arg("--dynamic-list").link_arg(path); + } else if self.sess.target.is_like_wasm { + self.link_arg("--no-export-dynamic"); + for (sym, _) in symbols { + self.link_arg("--export").link_arg(sym); + } } else { // Write an LD version script let res: io::Result<()> = try { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 45b028aa8ef..422b06350e1 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -11,12 +11,12 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; -use rustc_hir::attrs::OptimizeAttr; +use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; @@ -168,9 +168,7 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (&ty::Array(_, len), &ty::Slice(_)) => cx.const_usize( len.try_to_target_usize(cx.tcx()).expect("expected monomorphic const in codegen"), ), - (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) - if src_dyn_kind == target_dyn_kind => - { + (&ty::Dynamic(data_a, _), &ty::Dynamic(data_b, _)) => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); let b_principal_def_id = data_b.principal_def_id(); @@ -208,7 +206,7 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( old_info } } - (_, ty::Dynamic(data, _, _)) => meth::get_vtable( + (_, ty::Dynamic(data, _)) => meth::get_vtable( cx, source, data.principal() diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 008340e614d..32ae810ecc8 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -296,6 +296,12 @@ fn process_builtin_attrs( AttributeKind::Sanitize { span, .. } => { interesting_spans.sanitize = Some(*span); } + AttributeKind::ObjcClass { classname, .. } => { + codegen_fn_attrs.objc_class = Some(*classname); + } + AttributeKind::ObjcSelector { methname, .. } => { + codegen_fn_attrs.objc_selector = Some(*methname); + } _ => {} } } @@ -562,15 +568,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs } -/// If the provided DefId is a method in a trait impl, return the DefId of the method prototype. -fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { - let impl_item = tcx.opt_associated_item(def_id)?; - match impl_item.container { - ty::AssocItemContainer::Impl => impl_item.trait_item_def_id, - _ => None, - } -} - fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { // Backtrack to the crate root. let mut disabled = match tcx.opt_local_parent(did) { @@ -600,14 +597,15 @@ fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let Some(trait_item) = opt_trait_item(tcx, def_id) else { return false }; - tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER) + tcx.trait_item_of(def_id).is_some_and(|id| { + tcx.codegen_fn_attrs(id).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER) + }) } /// If the provided DefId is a method in a trait impl, return the value of the `#[align]` /// attribute on the method prototype (if any). fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Align> { - tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment + tcx.codegen_fn_attrs(tcx.trait_item_of(def_id)?).alignment } /// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)] diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index fb5a8205140..d5c30c5c7a6 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -38,10 +38,6 @@ pub(crate) struct CguNotRecorded<'a> { } #[derive(Diagnostic)] -#[diag(codegen_ssa_autodiff_without_lto)] -pub struct AutodiffWithoutLto; - -#[derive(Diagnostic)] #[diag(codegen_ssa_unknown_reuse_kind)] pub(crate) struct UnknownReuseKind { #[primary_span] diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 34ad35a729b..2fa466b5001 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -78,7 +78,7 @@ fn dyn_trait_in_self<'tcx>( ) -> Option<ty::ExistentialTraitRef<'tcx>> { for arg in ty.peel_refs().walk() { if let GenericArgKind::Type(ty) = arg.kind() - && let ty::Dynamic(data, _, _) = ty.kind() + && let ty::Dynamic(data, _) = ty.kind() { // FIXME(arbitrary_self_types): This is likely broken for receivers which // have a "non-self" trait objects as a generic argument. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6492ef73956..b2dc4fe32b0 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -614,7 +614,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (maybe_null, drop_fn, fn_abi, drop_instance) = match ty.kind() { // FIXME(eddyb) perhaps move some of this logic into // `Instance::resolve_drop_in_place`? - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { // IN THIS ARM, WE HAVE: // ty = *mut (dyn Trait) // which is: exists<T> ( *mut T, Vtable<T: Trait> ) @@ -1626,6 +1626,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { align, bx.const_usize(copy_bytes), MemFlags::empty(), + None, ); // ...and then load it with the ABI type. llval = load_cast(bx, cast, llscratch, scratch_align); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 3c667b8e882..befa00c6861 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -30,7 +30,7 @@ fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if allow_overlap { bx.memmove(dst, align, src, align, size, flags); } else { - bx.memcpy(dst, align, src, align, size, flags); + bx.memcpy(dst, align, src, align, size, flags, None); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 31784cabf4a..e7239ebfecf 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -228,6 +228,9 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + // emit a label starting with `func_end` for `cargo asm` and other tooling that might + // pattern match on assembly generated by LLVM. + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { @@ -246,6 +249,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); @@ -263,6 +267,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); @@ -287,6 +292,7 @@ fn prefix_and_suffix<'tcx>( writeln!(end).unwrap(); // .size is ignored for function symbols, so we can skip it writeln!(end, "end_function").unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); } BinaryFormat::Xcoff => { // the LLVM XCOFFAsmParser is extremely incomplete and does not implement many of the diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f6f2e3f2a3a..0a4b0f8d494 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,5 +1,5 @@ use itertools::Itertools as _; -use rustc_abi::{self as abi, FIRST_VARIANT}; +use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -7,9 +7,9 @@ use rustc_middle::{bug, mir, span_bug}; use rustc_session::config::OptLevel; use tracing::{debug, instrument}; +use super::FunctionCx; use super::operand::{OperandRef, OperandRefBuilder, OperandValue}; use super::place::{PlaceRef, PlaceValue, codegen_tag_value}; -use super::{FunctionCx, LocalRef}; use crate::common::{IntPredicate, TypeKind}; use crate::traits::*; use crate::{MemFlags, base}; @@ -25,6 +25,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match *rvalue { mir::Rvalue::Use(ref operand) => { let cg_operand = self.codegen_operand(bx, operand); + // Crucially, we do *not* use `OperandValue::Ref` for types with + // `BackendRepr::Scalar | BackendRepr::ScalarPair`. This ensures we match the MIR + // semantics regarding when assignment operators allow overlap of LHS and RHS. + if matches!( + cg_operand.layout.backend_repr, + BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..), + ) { + debug_assert!(!matches!(cg_operand.val, OperandValue::Ref(..))); + } // FIXME: consider not copying constants through stack. (Fixable by codegen'ing // constants into `OperandValue::Ref`; why don’t we do that yet if we don’t?) cg_operand.val.store(bx, dest); @@ -501,14 +510,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_place_to_pointer(bx, place, mk_ptr) } - mir::Rvalue::Len(place) => { - let size = self.evaluate_array_len(bx, place); - OperandRef { - val: OperandValue::Immediate(size), - layout: bx.cx().layout_of(bx.tcx().types.usize), - } - } - mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs)) if let Some(op) = op_with_overflow.overflowing_to_wrapping() => { @@ -616,7 +617,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::NullOp::AlignOf => { assert!(bx.cx().type_is_sized(ty)); - let val = layout.align.abi.bytes(); + let val = layout.align.bytes(); bx.cx().const_usize(val) } mir::NullOp::OffsetOf(fields) => { @@ -740,21 +741,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Value { - // ZST are passed as operands and require special handling - // because codegen_place() panics if Local is operand. - if let Some(index) = place.as_local() - && let LocalRef::Operand(op) = self.locals[index] - && let ty::Array(_, n) = op.layout.ty.kind() - { - let n = n.try_to_target_usize(bx.tcx()).expect("expected monomorphic const in codegen"); - return bx.cx().const_usize(n); - } - // use common size calculation for non zero-sized types - let cg_value = self.codegen_place(bx, place.as_ref()); - cg_value.len(bx.cx()) - } - /// Codegen an `Rvalue::RawPtr` or `Rvalue::Ref` fn codegen_place_to_pointer( &mut self, @@ -892,36 +878,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } mir::BinOp::Cmp => { - use std::cmp::Ordering; assert!(!is_float); - if let Some(value) = bx.three_way_compare(lhs_ty, lhs, rhs) { - return value; - } - let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); - if bx.cx().tcx().sess.opts.optimize == OptLevel::No { - // FIXME: This actually generates tighter assembly, and is a classic trick - // <https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign> - // However, as of 2023-11 it optimizes worse in things like derived - // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it - // better (see <https://github.com/llvm/llvm-project/issues/73417>), it'll - // be worth trying it in optimized builds as well. - let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs); - let gtext = bx.zext(is_gt, bx.type_i8()); - let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); - let ltext = bx.zext(is_lt, bx.type_i8()); - bx.unchecked_ssub(gtext, ltext) - } else { - // These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`, - // from <https://github.com/rust-lang/rust/pull/63767>. - let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); - let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs); - let ge = bx.select( - is_ne, - bx.cx().const_i8(Ordering::Greater as i8), - bx.cx().const_i8(Ordering::Equal as i8), - ); - bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) - } + bx.three_way_compare(lhs_ty, lhs, rhs) } mir::BinOp::AddWithOverflow | mir::BinOp::SubWithOverflow diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index f164e0f9123..0a50d7f18db 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -90,7 +90,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let align = pointee_layout.align; let dst = dst_val.immediate(); let src = src_val.immediate(); - bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty()); + bx.memcpy(dst, align, src, align, bytes, crate::MemFlags::empty(), None); } mir::StatementKind::FakeRead(..) | mir::StatementKind::Retag { .. } diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 577012151e4..e1bd8014d7a 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -21,7 +21,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( trace!("size_and_align_of_dst(ty={}, info={:?}): layout: {:?}", t, info, layout); if layout.is_sized() { let size = bx.const_usize(layout.size.bytes()); - let align = bx.const_usize(layout.align.abi.bytes()); + let align = bx.const_usize(layout.align.bytes()); return (size, align); } match t.kind() { @@ -49,7 +49,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // All slice sizes must fit into `isize`, so this multiplication cannot // wrap -- neither signed nor unsigned. bx.unchecked_sumul(info.unwrap(), bx.const_usize(unit.size.bytes())), - bx.const_usize(unit.align.abi.bytes()), + bx.const_usize(unit.align.bytes()), ) } ty::Foreign(_) => { @@ -82,7 +82,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // This function does not return so we can now return whatever we want. let size = bx.const_usize(layout.size.bytes()); - let align = bx.const_usize(layout.align.abi.bytes()); + let align = bx.const_usize(layout.align.bytes()); (size, align) } ty::Adt(..) | ty::Tuple(..) => { @@ -94,7 +94,7 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let i = layout.fields.count() - 1; let unsized_offset_unadjusted = layout.fields.offset(i).bytes(); - let sized_align = layout.align.abi.bytes(); + let sized_align = layout.align.bytes(); debug!( "DST {} offset of dyn field: {}, statically sized align: {}", t, unsized_offset_unadjusted, sized_align diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 29ec7eb1da3..2400160075e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -41,6 +41,8 @@ pub trait CodegenBackend { /// Called before `init` so that all other functions are able to emit translatable diagnostics. fn locale_resource(&self) -> &'static str; + fn name(&self) -> &'static str; + fn init(&self, _sess: &Session) {} fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {} @@ -96,7 +98,14 @@ pub trait CodegenBackend { metadata: EncodedMetadata, outputs: &OutputFilenames, ) { - link_binary(sess, &ArArchiveBuilderBuilder, codegen_results, metadata, outputs); + link_binary( + sess, + &ArArchiveBuilderBuilder, + codegen_results, + metadata, + outputs, + self.name(), + ); } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index f417d1a7bf7..60296e36e0c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::{AtomicOrdering, Instance, Ty}; use rustc_session::config::OptLevel; @@ -405,15 +406,41 @@ pub trait BuilderMethods<'a, 'tcx>: fn fcmp(&mut self, op: RealPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; /// Returns `-1` if `lhs < rhs`, `0` if `lhs == rhs`, and `1` if `lhs > rhs`. - // FIXME: Move the default implementation from `codegen_scalar_binop` into this method and - // remove the `Option` return once LLVM 20 is the minimum version. fn three_way_compare( &mut self, - _ty: Ty<'tcx>, - _lhs: Self::Value, - _rhs: Self::Value, - ) -> Option<Self::Value> { - None + ty: Ty<'tcx>, + lhs: Self::Value, + rhs: Self::Value, + ) -> Self::Value { + // FIXME: This implementation was designed around LLVM's ability to optimize, but `cg_llvm` + // overrides this to just use `@llvm.scmp`/`ucmp` since LLVM 20. This default impl should be + // reevaluated with respect to the remaining backends like cg_gcc, whether they might use + // specialized implementations as well, or continue to use a generic implementation here. + use std::cmp::Ordering; + let pred = |op| crate::base::bin_op_to_icmp_predicate(op, ty.is_signed()); + if self.cx().sess().opts.optimize == OptLevel::No { + // This actually generates tighter assembly, and is a classic trick: + // <https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign>. + // However, as of 2023-11 it optimized worse in LLVM in things like derived + // `PartialOrd`, so we were only using it in debug. Since LLVM now uses its own + // intrinsics, it may be be worth trying it in optimized builds for other backends. + let is_gt = self.icmp(pred(mir::BinOp::Gt), lhs, rhs); + let gtext = self.zext(is_gt, self.type_i8()); + let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let ltext = self.zext(is_lt, self.type_i8()); + self.unchecked_ssub(gtext, ltext) + } else { + // These operations were better optimized by LLVM, before `@llvm.scmp`/`ucmp` in 20. + // See <https://github.com/rust-lang/rust/pull/63767>. + let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let is_ne = self.icmp(pred(mir::BinOp::Ne), lhs, rhs); + let ge = self.select( + is_ne, + self.cx().const_i8(Ordering::Greater as i8), + self.cx().const_i8(Ordering::Equal as i8), + ); + self.select(is_lt, self.cx().const_i8(Ordering::Less as i8), ge) + } } fn memcpy( @@ -424,6 +451,7 @@ pub trait BuilderMethods<'a, 'tcx>: src_align: Align, size: Self::Value, flags: MemFlags, + tt: Option<rustc_ast::expand::typetree::FncTree>, ); fn memmove( &mut self, @@ -480,7 +508,7 @@ pub trait BuilderMethods<'a, 'tcx>: temp.val.store_with_flags(self, dst.with_type(layout), flags); } else if !layout.is_zst() { let bytes = self.const_usize(layout.size.bytes()); - self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags); + self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, flags, None); } } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 700d7c26752..010ffa60c7a 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -476,14 +476,20 @@ const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wid const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!` -const_eval_validation_null_box = {$front_matter}: encountered a null box +const_eval_validation_null_box = {$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null + } box const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer -const_eval_validation_null_ref = {$front_matter}: encountered a null reference -const_eval_validation_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range} +const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe -> + [true] maybe-null + *[false] null + } reference +const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range} const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers const_eval_validation_pointer_as_int = {$front_matter}: encountered a pointer, but {$expected} -const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range} +const_eval_validation_ptr_out_of_range = {$front_matter}: encountered a pointer with unknown absolute address, but expected something that is definitely {$in_range} const_eval_validation_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty} const_eval_validation_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes}) const_eval_validation_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes}) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ca707b50d50..3397bd9a68e 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -573,8 +573,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Use(_) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) - | Rvalue::Discriminant(..) - | Rvalue::Len(_) => {} + | Rvalue::Discriminant(..) => {} Rvalue::Aggregate(kind, ..) => { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index faf41f1658b..34d1fdd8c86 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -232,9 +232,7 @@ where Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) } - Rvalue::Discriminant(place) | Rvalue::Len(place) => { - in_place::<Q, _>(cx, in_local, place.as_ref()) - } + Rvalue::Discriminant(place) => in_place::<Q, _>(cx, in_local, place.as_ref()), Rvalue::CopyForDeref(place) => in_place::<Q, _>(cx, in_local, place.as_ref()), diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index d98e5027e4d..664dda8701a 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -197,7 +197,6 @@ where | mir::Rvalue::CopyForDeref(..) | mir::Rvalue::ThreadLocalRef(..) | mir::Rvalue::Repeat(..) - | mir::Rvalue::Len(..) | mir::Rvalue::BinaryOp(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::UnaryOp(..) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 37c6c4a61d8..7c41258ebfe 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,6 +1,7 @@ use rustc_abi::{BackendRepr, FieldIdx, VariantIdx}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; @@ -196,6 +197,7 @@ fn reconstruct_place_meta<'tcx>( // Traverse the type, and update `last_valtree` as we go. let tail = tcx.struct_tail_raw( layout.ty, + &ObligationCause::dummy(), |ty| ty, || { let branches = last_valtree.unwrap_branch(); diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 2d412ee5ec2..d352a638424 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -668,7 +668,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { MutableRefInConst => const_eval_validation_mutable_ref_in_const, NullFnPtr => const_eval_validation_null_fn_ptr, NeverVal => const_eval_validation_never_val, - NullablePtrOutOfRange { .. } => const_eval_validation_nullable_ptr_out_of_range, + NonnullPtrMaybeNull { .. } => const_eval_validation_nonnull_ptr_out_of_range, PtrOutOfRange { .. } => const_eval_validation_ptr_out_of_range, OutOfRange { .. } => const_eval_validation_out_of_range, UnsafeCellInImmutable => const_eval_validation_unsafe_cell, @@ -696,8 +696,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { } UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box, - NullPtr { ptr_kind: PointerKind::Box } => const_eval_validation_null_box, - NullPtr { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_null_ref, + NullPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_null_box, + NullPtr { ptr_kind: PointerKind::Ref(_), .. } => const_eval_validation_null_ref, DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => { const_eval_validation_dangling_box_no_provenance } @@ -804,9 +804,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { | InvalidFnPtr { value } => { err.arg("value", value); } - NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => { - add_range_arg(range, max_value, err) - } + PtrOutOfRange { range, max_value } => add_range_arg(range, max_value, err), OutOfRange { range, max_value, value } => { err.arg("value", value); add_range_arg(range, max_value, err); @@ -822,10 +820,13 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { err.arg("vtable_dyn_type", vtable_dyn_type.to_string()); err.arg("expected_dyn_type", expected_dyn_type.to_string()); } - NullPtr { .. } - | MutableRefToImmutable + NullPtr { maybe, .. } => { + err.arg("maybe", maybe); + } + MutableRefToImmutable | MutableRefInConst | NullFnPtr + | NonnullPtrMaybeNull | NeverVal | UnsafeCellInImmutable | InvalidMetaSliceTooLarge { .. } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 4cb88d44e1b..23e4a2921ea 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -658,7 +658,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let val = self.read_immediate(&receiver)?; break self.ref_to_mplace(&val)?; } - ty::Dynamic(.., ty::Dyn) => break receiver.assert_mem_place(), // no immediate unsized values + ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values _ => { // Not there yet, search for the only non-ZST field. // (The rules for `DispatchFromDyn` ensure there's exactly one such field.) @@ -675,7 +675,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // (For that reason we also cannot use `unpack_dyn_trait`.) let receiver_tail = self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.typing_env); - let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { + let ty::Dynamic(receiver_trait, _) = receiver_tail.kind() else { span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail) }; assert!(receiver_place.layout.is_unsized()); @@ -822,7 +822,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // instead we do the virtual call stuff ourselves. It's easier here than in `eval_fn_call` // since we can just get a place of the underlying type and use `mplace_to_ref`. let place = match place.layout.ty.kind() { - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { // Dropping a trait object. Need to find actual drop fn. self.unpack_dyn_trait(&place, data)? } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index e3afeda5b7c..0075740e031 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -386,7 +386,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); self.write_immediate(val, dest) } - (ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => { + (ty::Dynamic(data_a, _), ty::Dynamic(data_b, _)) => { let val = self.read_immediate(src)?; // MIR building generates odd NOP casts, prevent them from causing unexpected trouble. // See <https://github.com/rust-lang/rust/issues/128880>. @@ -436,7 +436,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let new_vptr = self.get_vtable_ptr(ty, data_b)?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } - (_, &ty::Dynamic(data, _, ty::Dyn)) => { + (_, &ty::Dynamic(data, _)) => { // Initial cast from sized to dyn trait let vtable = self.get_vtable_ptr(src_pointee_ty, data)?; let ptr = self.read_pointer(src)?; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 91ed71ac3e5..0e4a98f0941 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -469,7 +469,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } interp_ok(Some((full_size, full_align))) } - ty::Dynamic(expected_trait, _, ty::Dyn) => { + ty::Dynamic(expected_trait, _) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). interp_ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 5e3d0a15d8b..785978b4d71 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -181,7 +181,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) @@ -636,6 +636,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { dest, rustc_apfloat::Round::NearestTiesToEven, )?, + sym::fmaf16 => self.fma_intrinsic::<Half>(args, dest)?, + sym::fmaf32 => self.fma_intrinsic::<Single>(args, dest)?, + sym::fmaf64 => self.fma_intrinsic::<Double>(args, dest)?, + sym::fmaf128 => self.fma_intrinsic::<Quad>(args, dest)?, + sym::fmuladdf16 => self.float_muladd_intrinsic::<Half>(args, dest)?, + sym::fmuladdf32 => self.float_muladd_intrinsic::<Single>(args, dest)?, + sym::fmuladdf64 => self.float_muladd_intrinsic::<Double>(args, dest)?, + sym::fmuladdf128 => self.float_muladd_intrinsic::<Quad>(args, dest)?, // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), @@ -1035,4 +1043,42 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(res, dest)?; interp_ok(()) } + + fn fma_intrinsic<F>( + &mut self, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>, + { + let a: F = self.read_scalar(&args[0])?.to_float()?; + let b: F = self.read_scalar(&args[1])?.to_float()?; + let c: F = self.read_scalar(&args[2])?.to_float()?; + + let res = a.mul_add(b, c).value; + let res = self.adjust_nan(res, &[a, b, c]); + self.write_scalar(res, dest)?; + interp_ok(()) + } + + fn float_muladd_intrinsic<F>( + &mut self, + args: &[OpTy<'tcx, M::Provenance>], + dest: &PlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ()> + where + F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>, + { + let a: F = self.read_scalar(&args[0])?.to_float()?; + let b: F = self.read_scalar(&args[1])?.to_float()?; + let c: F = self.read_scalar(&args[2])?.to_float()?; + + let fuse = M::float_fuse_mul_add(self); + + let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; + let res = self.adjust_nan(res, &[a, b, c]); + self.write_scalar(res, dest)?; + interp_ok(()) + } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index e22629993fb..1725635e0b4 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -289,6 +289,9 @@ pub trait Machine<'tcx>: Sized { a } + /// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations. + fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool; + /// Called before a basic block terminator is executed. #[inline] fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> { @@ -673,6 +676,11 @@ pub macro compile_time_machine(<$tcx: lifetime>) { } #[inline(always)] + fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool { + true + } + + #[inline(always)] fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> { // We can't look at `tcx.sess` here as that can differ across crates, which can lead to // unsound differences in evaluating the same constant at different instantiation sites. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index ebcdb9461d0..323e1cefd58 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1504,7 +1504,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // This will also error if copying partial provenance is not supported. let provenance = src_alloc .provenance() - .prepare_copy(src_range, dest_offset, num_copies, self) + .prepare_copy(src_range, self) .map_err(|e| e.to_interp_error(src_alloc_id))?; // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); @@ -1590,7 +1590,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { num_copies, ); // copy the provenance to the destination - dest_alloc.provenance_apply_copy(provenance); + dest_alloc.provenance_apply_copy(provenance, alloc_range(dest_offset, size), num_copies); interp_ok(()) } diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 74f8a0a7b09..f0819423aa0 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -528,7 +528,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !layout.is_sized() { span_bug!(self.cur_span(), "unsized type for `NullaryOp::AlignOf`"); } - let val = layout.align.abi.bytes(); + let val = layout.align.bytes(); ImmTy::from_uint(val, usize_layout()) } OffsetOf(fields) => { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index a86fdf80f60..cd34892f029 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -858,7 +858,7 @@ where /// Also, if you use this you are responsible for validating that things get copied at the /// right type. #[instrument(skip(self), level = "trace")] - fn copy_op_no_validate( + pub(super) fn copy_op_no_validate( &mut self, src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 7cabfd96121..1c1c59da9d8 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -509,7 +509,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Never | ty::Error(_) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) | ty::Foreign(..) => false, ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 23d362de308..923e00ad4cf 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -17,7 +17,7 @@ use tracing::{info, instrument, trace}; use super::{ FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, - Projectable, Scalar, interp_ok, throw_ub, throw_unsup_format, + Projectable, interp_ok, throw_ub, throw_unsup_format, }; use crate::interpret::EnteredTraceSpan; use crate::{enter_trace_span, util}; @@ -225,12 +225,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_repeat(operand, &dest)?; } - Len(place) => { - let src = self.eval_place(place)?; - let len = src.len(self)?; - self.write_scalar(Scalar::from_target_usize(len, self), &dest)?; - } - Ref(_, borrow_kind, place) => { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; @@ -310,7 +304,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { operands: &IndexSlice<FieldIdx, mir::Operand<'tcx>>, dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - self.write_uninit(dest)?; // make sure all the padding ends up as uninit let (variant_index, variant_dest, active_field_index) = match *kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { let variant_dest = self.project_downcast(dest, variant_index)?; @@ -346,9 +339,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let field_index = active_field_index.unwrap_or(field_index); let field_dest = self.project_field(&variant_dest, field_index)?; let op = self.eval_operand(operand, Some(field_dest.layout))?; - self.copy_op(&op, &field_dest)?; + // We validate manually below so we don't have to do it here. + self.copy_op_no_validate(&op, &field_dest, /*allow_transmute*/ false)?; } - self.write_discriminant(variant_index, dest) + self.write_discriminant(variant_index, dest)?; + // Validate that the entire thing is valid, and reset padding that might be in between the + // fields. + if M::enforce_validity(self, dest.layout()) { + self.validate_operand( + dest, + M::enforce_validity_recursively(self, dest.layout()), + /*reset_provenance_and_padding*/ true, + )?; + } + interp_ok(()) } /// Repeats `operand` into the destination. `dest` must have array type, and that type diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 870f9a396ae..d982ed96167 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -109,7 +109,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { assert!( - matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _)), "`unpack_dyn_trait` only makes sense on `dyn*` types" ); let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 02e3d90f4af..5f088fe37e8 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -449,7 +449,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.typing_env); match tail.kind() { - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; // Make sure it is a genuine vtable pointer for the right trait. try_validation!( @@ -511,7 +511,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message ), self.path, - Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind }, + Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false }, Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance { ptr_kind, // FIXME this says "null pointer" when null but we need translate @@ -538,8 +538,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ); // Make sure this is non-null. We checked dereferenceability above, but if `size` is zero // that does not imply non-null. - if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? { - throw_validation_failure!(self.path, NullPtr { ptr_kind }) + let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx); + if self.ecx.scalar_may_be_null(scalar)? { + let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..)); + throw_validation_failure!(self.path, NullPtr { ptr_kind, maybe }) } // Do not allow references to uninhabited types. if place.layout.is_uninhabited() { @@ -757,6 +759,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { } else { // Otherwise (for standalone Miri), we have to still check it to be non-null. if self.ecx.scalar_may_be_null(scalar)? { + let maybe = + !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..)); + // This can't be a "maybe-null" pointer since the check for this being + // a fn ptr at all already ensures that the pointer is inbounds. + assert!(!maybe); throw_validation_failure!(self.path, NullFnPtr); } } @@ -819,10 +826,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { if start == 1 && end == max_value { // Only null is the niche. So make sure the ptr is NOT null. if self.ecx.scalar_may_be_null(scalar)? { - throw_validation_failure!( - self.path, - NullablePtrOutOfRange { range: valid_range, max_value } - ) + throw_validation_failure!(self.path, NonnullPtrMaybeNull) } else { return interp_ok(()); } diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 82c50fac6c0..b5de10c7dcd 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -90,7 +90,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // Special treatment for special types, where the (static) layout is not sufficient. match *ty.kind() { // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { // Dyn types. This is unsized, and the actual dynamic type of the data is given by the // vtable stored in the place metadata. // unsized values are never immediate, so we can assert_mem_place diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 8ace560d85d..9c0ec4e51a0 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,7 +1,6 @@ // tidy-alphabetical-start #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] -#![cfg_attr(bootstrap, feature(strict_overflow_ops))] #![doc(rust_logo)] #![feature(array_try_map)] #![feature(assert_matches)] diff --git a/compiler/rustc_const_eval/src/util/alignment.rs b/compiler/rustc_const_eval/src/util/alignment.rs index 9507b24f603..9aafc7efd8a 100644 --- a/compiler/rustc_const_eval/src/util/alignment.rs +++ b/compiler/rustc_const_eval/src/util/alignment.rs @@ -37,7 +37,7 @@ where debug!( "is_disaligned({:?}) - align = {}, packed = {}; not disaligned", place, - layout.align.abi.bytes(), + layout.align.bytes(), pack.bytes() ); false diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs index 5249b32eca4..4e7c8310007 100644 --- a/compiler/rustc_const_eval/src/util/caller_location.rs +++ b/compiler/rustc_const_eval/src/util/caller_location.rs @@ -61,7 +61,7 @@ pub(crate) fn const_caller_location_provider( trace!("const_caller_location: {}:{}:{}", file, line, col); let mut ecx = mk_eval_cx_to_read_const_val( tcx, - rustc_span::DUMMY_SP, // FIXME: use a proper span here? + rustc_span::DUMMY_SP, // This interpreter cannot fail, so the span is irrelevant. ty::TypingEnv::fully_monomorphized(), CanAccessMutGlobal::No, ); diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index b1f29598750..1dea7e4252d 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -129,7 +129,7 @@ fn check_validity_requirement_lax<'tcx>( if let Some(pointee) = this.ty.builtin_deref(false) { let pointee = cx.layout_of(pointee)?; // We need to ensure that the LLVM attributes `aligned` and `dereferenceable(size)` are satisfied. - if pointee.align.abi.bytes() > 1 { + if pointee.align.bytes() > 1 { // 0x01-filling is not aligned. return Ok(false); } diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 5bcf96abd8c..db651811551 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -41,7 +41,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::FnPtr(..) | ty::Never | ty::Tuple(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::UnsafeBinder(_) => self.pretty_print_type(ty), // Placeholders (all printed as `_` to uniformize them). diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 17da3ea83c8..e4e86bcc41a 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -34,6 +34,7 @@ #![feature(sized_hierarchy)] #![feature(test)] #![feature(thread_id_value)] +#![feature(trusted_len)] #![feature(type_alias_impl_trait)] #![feature(unwrap_infallible)] // tidy-alphabetical-end diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index c002d47815b..15e3e6ea4c3 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -1,6 +1,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt::Debug; +use std::iter::TrustedLen; use std::mem; use std::ops::{Bound, Index, IndexMut, RangeBounds}; @@ -215,36 +216,40 @@ impl<K: Ord, V> SortedMap<K, V> { /// It is up to the caller to make sure that the elements are sorted by key /// and that there are no duplicates. #[inline] - pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) { - if elements.is_empty() { + pub fn insert_presorted( + &mut self, + // We require `TrustedLen` to ensure that the `splice` below is actually efficient. + mut elements: impl Iterator<Item = (K, V)> + DoubleEndedIterator + TrustedLen, + ) { + let Some(first) = elements.next() else { return; - } - - debug_assert!(elements.array_windows().all(|[fst, snd]| fst.0 < snd.0)); + }; - let start_index = self.lookup_index_for(&elements[0].0); + let start_index = self.lookup_index_for(&first.0); let elements = match start_index { Ok(index) => { - let mut elements = elements.into_iter(); - self.data[index] = elements.next().unwrap(); - elements + self.data[index] = first; // overwrite first element + elements.chain(None) // insert the rest below } Err(index) => { - if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 { + let last = elements.next_back(); + if index == self.data.len() + || last.as_ref().is_none_or(|l| l.0 < self.data[index].0) + { // We can copy the whole range without having to mix with // existing elements. - self.data.splice(index..index, elements); + self.data + .splice(index..index, std::iter::once(first).chain(elements).chain(last)); return; } - let mut elements = elements.into_iter(); - self.data.insert(index, elements.next().unwrap()); - elements + self.data.insert(index, first); + elements.chain(last) // insert the rest below } }; - // Insert the rest + // Insert the rest. This is super inefficicent since each insertion copies the entire tail. for (k, v) in elements { self.insert(k, v); } diff --git a/compiler/rustc_data_structures/src/sorted_map/tests.rs b/compiler/rustc_data_structures/src/sorted_map/tests.rs index ea4d2f1feac..17d0d3cb170 100644 --- a/compiler/rustc_data_structures/src/sorted_map/tests.rs +++ b/compiler/rustc_data_structures/src/sorted_map/tests.rs @@ -171,7 +171,7 @@ fn test_insert_presorted_non_overlapping() { map.insert(2, 0); map.insert(8, 0); - map.insert_presorted(vec![(3, 0), (7, 0)]); + map.insert_presorted(vec![(3, 0), (7, 0)].into_iter()); let expected = vec![2, 3, 7, 8]; assert_eq!(keys(map), expected); @@ -183,7 +183,7 @@ fn test_insert_presorted_first_elem_equal() { map.insert(2, 2); map.insert(8, 8); - map.insert_presorted(vec![(2, 0), (7, 7)]); + map.insert_presorted(vec![(2, 0), (7, 7)].into_iter()); let expected = vec![(2, 0), (7, 7), (8, 8)]; assert_eq!(elements(map), expected); @@ -195,7 +195,7 @@ fn test_insert_presorted_last_elem_equal() { map.insert(2, 2); map.insert(8, 8); - map.insert_presorted(vec![(3, 3), (8, 0)]); + map.insert_presorted(vec![(3, 3), (8, 0)].into_iter()); let expected = vec![(2, 2), (3, 3), (8, 0)]; assert_eq!(elements(map), expected); @@ -207,7 +207,7 @@ fn test_insert_presorted_shuffle() { map.insert(2, 2); map.insert(7, 7); - map.insert_presorted(vec![(1, 1), (3, 3), (8, 8)]); + map.insert_presorted(vec![(1, 1), (3, 3), (8, 8)].into_iter()); let expected = vec![(1, 1), (2, 2), (3, 3), (7, 7), (8, 8)]; assert_eq!(elements(map), expected); @@ -219,7 +219,7 @@ fn test_insert_presorted_at_end() { map.insert(1, 1); map.insert(2, 2); - map.insert_presorted(vec![(3, 3), (8, 8)]); + map.insert_presorted(vec![(3, 3), (8, 8)].into_iter()); let expected = vec![(1, 1), (2, 2), (3, 3), (8, 8)]; assert_eq!(elements(map), expected); diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index ae1dbd2cf51..46efa50cff3 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -74,6 +74,7 @@ ctrlc = "3.4.4" # tidy-alphabetical-start check_only = ['rustc_interface/check_only'] llvm = ['rustc_interface/llvm'] +llvm_enzyme = ['rustc_interface/llvm_enzyme'] max_level_info = ['rustc_log/max_level_info'] rustc_randomized_layouts = [ 'rustc_index/rustc_randomized_layouts', diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 4f875cf99ec..8dab520cf36 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -206,7 +206,7 @@ impl Callbacks for TimePassesCallbacks { // time because it will mess up the --print output. See #64339. // self.time_passes = (config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes) - .then(|| config.opts.unstable_opts.time_passes_format); + .then_some(config.opts.unstable_opts.time_passes_format); config.opts.trimmed_def_paths = true; } } @@ -439,8 +439,9 @@ fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option<Inpu "when UNSTABLE_RUSTDOC_TEST_PATH is set \ UNSTABLE_RUSTDOC_TEST_LINE also needs to be set", ); - let line = isize::from_str_radix(&line, 10) - .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number"); + let line = line + .parse::<isize>() + .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be a number"); FileName::doc_test_source_code(PathBuf::from(path), line) } Err(_) => FileName::anon_source_code(&input), @@ -474,8 +475,7 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col let mut text = String::new(); // Slice off the leading newline and print. for line in description.lines() { - let indent_level = - line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len()); + let indent_level = line.find(|c: char| !c.is_whitespace()).unwrap_or(line.len()); let dedented_line = &line[indent_level..]; if dedented_line.starts_with("```") { is_in_code_block = !is_in_code_block; @@ -547,7 +547,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { // The pager failed. Try to print pretty output to stdout. if let Some((bufwtr, mdbuf)) = &pretty_data - && bufwtr.print(&mdbuf).is_ok() + && bufwtr.print(mdbuf).is_ok() { return; } @@ -598,8 +598,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { match sess.io.input { - Input::File(ref ifile) => { - let path = &(*ifile); + Input::File(ref path) => { let mut v = Vec::new(); locator::list_file_metadata( &sess.target, @@ -833,7 +832,7 @@ fn print_crate_info( SupportedCrateTypes => { let supported_crate_types = CRATE_TYPES .iter() - .filter(|(_, crate_type)| !invalid_output_for_target(&sess, *crate_type)) + .filter(|(_, crate_type)| !invalid_output_for_target(sess, *crate_type)) .filter(|(_, crate_type)| *crate_type != CrateType::Sdylib) .map(|(crate_type_sym, _)| *crate_type_sym) .collect::<BTreeSet<_>>(); @@ -1434,7 +1433,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&DiagCtxt)) eprintln!(); if let Some(ice_path) = ice_path() - && let Ok(mut out) = File::options().create(true).append(true).open(&ice_path) + && let Ok(mut out) = File::options().create(true).append(true).open(ice_path) { // The current implementation always returns `Some`. let location = info.location().unwrap(); @@ -1510,7 +1509,7 @@ fn report_ice( let file = if let Some(path) = ice_path() { // Create the ICE dump target file. - match crate::fs::File::options().create(true).append(true).open(&path) { + match crate::fs::File::options().create(true).append(true).open(path) { Ok(mut file) => { dcx.emit_note(session_diagnostics::IcePath { path: path.clone() }); if FIRST_PANIC.swap(false, Ordering::SeqCst) { diff --git a/compiler/rustc_driver_impl/src/print.rs b/compiler/rustc_driver_impl/src/print.rs index 70de55320f7..3f107eb7a61 100644 --- a/compiler/rustc_driver_impl/src/print.rs +++ b/compiler/rustc_driver_impl/src/print.rs @@ -14,7 +14,7 @@ macro_rules! safe_println { } pub(crate) fn print(args: fmt::Arguments<'_>) { - if let Err(_) = io::stdout().write_fmt(args) { + if io::stdout().write_fmt(args).is_err() { rustc_errors::FatalError.raise(); } } diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 61ba716d082..47c00bff5c9 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -3,6 +3,8 @@ expand_attributes_on_expressions_experimental = .help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//` .help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !` +expand_cfg_attr_no_attributes = `#[cfg_attr]` does not expand to any attributes + expand_collapse_debuginfo_illegal = illegal value for attribute #[collapse_debuginfo(no|external|yes)] @@ -78,6 +80,10 @@ expand_macro_body_stability = .label = invalid body stability attribute .label2 = body stability attribute affects this macro +expand_macro_call_unused_doc_comment = unused doc comment + .label = rustdoc does not generate documentation for macro invocations + .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion + expand_macro_const_stability = macros cannot have const stability attributes .label = invalid const stability attribute @@ -89,6 +95,13 @@ expand_malformed_feature_attribute = malformed `feature` attribute input .expected = expected just one word +expand_metavar_still_repeating = variable `{$ident}` is still repeating at this depth + .label = expected repetition + +expand_metavariable_wrong_operator = meta-variable repeats with different Kleene operator + .binder_label = expected repetition + .occurrence_label = conflicting repetition + expand_meta_var_dif_seq_matchers = {$msg} expand_missing_fragment_specifier = missing fragment specifier @@ -147,6 +160,9 @@ expand_mve_unrecognized_var = expand_non_inline_modules_in_proc_macro_input_are_unstable = non-inline modules in proc macro input are unstable +expand_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + .suggestion = use pat_param to preserve semantics + expand_proc_macro_back_compat = using an old version of `{$crate_name}` .note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives @@ -176,11 +192,18 @@ expand_resolve_relative_path = expand_trace_macro = trace_macro +expand_trailing_semi_macro = trailing semicolon in macro used in expression position + .note1 = macro invocations at the end of a block are treated as expressions + .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` + +expand_unknown_macro_variable = unknown macro variable `{$name}` + +expand_unused_builtin_attribute = unused attribute `{$attr_name}` + .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` + .suggestion = remove the attribute + expand_unsupported_key_value = key-value macro attributes are not supported -expand_var_still_repeating = - variable `{$ident}` is still repeating at this depth - expand_wrong_fragment_kind = non-{$kind} macro in {$kind} position: {$name} diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 88f88f30a8c..33b712e3aed 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -324,16 +324,16 @@ pub trait BangProcMacro { impl<F> BangProcMacro for F where - F: Fn(TokenStream) -> TokenStream, + F: Fn(&mut ExtCtxt<'_>, Span, TokenStream) -> Result<TokenStream, ErrorGuaranteed>, { fn expand<'cx>( &self, - _ecx: &'cx mut ExtCtxt<'_>, - _span: Span, + ecx: &'cx mut ExtCtxt<'_>, + span: Span, ts: TokenStream, ) -> Result<TokenStream, ErrorGuaranteed> { // FIXME setup implicit context in TLS before calling self. - Ok(self(ts)) + self(ecx, span, ts) } } @@ -942,9 +942,9 @@ impl SyntaxExtension { .unwrap_or_default(); let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe(_)); - let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) - .and_then(|macro_export| macro_export.meta_item_list()) - .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); + let local_inner_macros = + *find_attr!(attrs, AttributeKind::MacroExport {local_inner_macros: l, ..} => l) + .unwrap_or(&false); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); @@ -999,17 +999,14 @@ impl SyntaxExtension { /// A dummy bang macro `foo!()`. pub fn dummy_bang(edition: Edition) -> SyntaxExtension { - fn expander<'cx>( - cx: &'cx mut ExtCtxt<'_>, + fn expand( + ecx: &mut ExtCtxt<'_>, span: Span, - _: TokenStream, - ) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(DummyResult::any( - span, - cx.dcx().span_delayed_bug(span, "expanded a dummy bang macro"), - )) + _ts: TokenStream, + ) -> Result<TokenStream, ErrorGuaranteed> { + Err(ecx.dcx().span_delayed_bug(span, "expanded a dummy bang macro")) } - SyntaxExtension::default(SyntaxExtensionKind::LegacyBang(Arc::new(expander)), edition) + SyntaxExtension::default(SyntaxExtensionKind::Bang(Arc::new(expand)), edition) } /// A dummy derive macro `#[derive(Foo)]`. diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 15419ab7423..2925e337071 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -21,7 +21,6 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features, REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; -use rustc_lint_defs::BuiltinLintDiag; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -315,7 +314,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, cfg_attr.span, ast::CRATE_NODE_ID, - BuiltinLintDiag::CfgAttrNoAttributes, + crate::errors::CfgAttrNoAttributes, ); } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 6eb5cd65846..c37c2d88d9c 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -3,9 +3,13 @@ use std::borrow::Cow; use rustc_ast::ast; use rustc_errors::codes::*; use rustc_hir::limit::Limit; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; +#[derive(LintDiagnostic)] +#[diag(expand_cfg_attr_no_attributes)] +pub(crate) struct CfgAttrNoAttributes; + #[derive(Diagnostic)] #[diag(expand_expr_repeat_no_syntax_vars)] pub(crate) struct NoSyntaxVarsExprRepeat { @@ -28,13 +32,30 @@ pub(crate) struct CountRepetitionMisplaced { } #[derive(Diagnostic)] -#[diag(expand_var_still_repeating)] -pub(crate) struct VarStillRepeating { +#[diag(expand_metavar_still_repeating)] +pub(crate) struct MacroVarStillRepeating { #[primary_span] pub span: Span, pub ident: MacroRulesNormalizedIdent, } +#[derive(LintDiagnostic)] +#[diag(expand_metavar_still_repeating)] +pub(crate) struct MetaVarStillRepeatingLint { + #[label] + pub label: Span, + pub ident: MacroRulesNormalizedIdent, +} + +#[derive(LintDiagnostic)] +#[diag(expand_metavariable_wrong_operator)] +pub(crate) struct MetaVariableWrongOperator { + #[label(expand_binder_label)] + pub binder: Span, + #[label(expand_occurrence_label)] + pub occurrence: Span, +} + #[derive(Diagnostic)] #[diag(expand_meta_var_dif_seq_matchers)] pub(crate) struct MetaVarsDifSeqMatchers { @@ -43,6 +64,12 @@ pub(crate) struct MetaVarsDifSeqMatchers { pub msg: String, } +#[derive(LintDiagnostic)] +#[diag(expand_unknown_macro_variable)] +pub(crate) struct UnknownMacroVariable { + pub name: MacroRulesNormalizedIdent, +} + #[derive(Diagnostic)] #[diag(expand_resolve_relative_path)] pub(crate) struct ResolveRelativePath { @@ -345,6 +372,15 @@ pub(crate) struct DuplicateMatcherBinding { pub prev: Span, } +#[derive(LintDiagnostic)] +#[diag(expand_duplicate_matcher_binding)] +pub(crate) struct DuplicateMatcherBindingLint { + #[label] + pub span: Span, + #[label(expand_label2)] + pub prev: Span, +} + #[derive(Diagnostic)] #[diag(expand_missing_fragment_specifier)] #[note] @@ -501,3 +537,39 @@ pub(crate) struct MacroArgsBadDelimSugg { #[suggestion_part(code = ")")] pub close: Span, } + +#[derive(LintDiagnostic)] +#[diag(expand_macro_call_unused_doc_comment)] +#[help] +pub(crate) struct MacroCallUnusedDocComment { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(expand_or_patterns_back_compat)] +pub(crate) struct OrPatternsBackCompat { + #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] + pub span: Span, + pub suggestion: String, +} + +#[derive(LintDiagnostic)] +#[diag(expand_trailing_semi_macro)] +pub(crate) struct TrailingMacro { + #[note(expand_note1)] + #[note(expand_note2)] + pub is_trailing: bool, + pub name: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(expand_unused_builtin_attribute)] +pub(crate) struct UnusedBuiltinAttribute { + #[note] + pub invoc_span: Span, + pub attr_name: Symbol, + pub macro_name: String, + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + pub attr_span: Span, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 38e057d2776..172bc3d1d9f 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -25,7 +25,6 @@ use rustc_parse::parser::{ token_descr, }; use rustc_session::Session; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; use rustc_span::hygiene::SyntaxContext; @@ -972,7 +971,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }); } }, - SyntaxExtensionKind::LegacyBang(..) => { + SyntaxExtensionKind::Bang(..) => { let msg = "expanded a dummy glob delegation"; let guar = self.cx.dcx().span_delayed_bug(span, msg); return ExpandResult::Ready(fragment_kind.dummy(span, guar)); @@ -2183,7 +2182,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { UNUSED_DOC_COMMENTS, current_span, self.cx.current_expansion.lint_node_id, - BuiltinLintDiag::UnusedDocComment(attr.span), + crate::errors::MacroCallUnusedDocComment { span: attr.span }, ); } else if rustc_attr_parsing::is_builtin_attr(attr) && !AttributeParser::<Early>::is_parsed_attribute(&attr.path()) @@ -2196,7 +2195,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { UNUSED_ATTRIBUTES, attr.span, self.cx.current_expansion.lint_node_id, - BuiltinLintDiag::UnusedBuiltinAttribute { + crate::errors::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), invoc_span: call.path.span, diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index faeae1f494e..0eae44a05e7 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -108,8 +108,7 @@ use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::{DUMMY_NODE_ID, NodeId}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::MultiSpan; -use rustc_lint_defs::BuiltinLintDiag; +use rustc_errors::DecorateDiagCompat; use rustc_session::lint::builtin::META_VARIABLE_MISUSE; use rustc_session::parse::ParseSess; use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw}; @@ -211,8 +210,7 @@ pub(super) fn check_meta_variables( guar.map_or(Ok(()), Err) } -/// Checks `lhs` as part of the LHS of a macro definition, extends `binders` with new binders, and -/// sets `valid` to false in case of errors. +/// Checks `lhs` as part of the LHS of a macro definition. /// /// Arguments: /// - `psess` is used to emit diagnostics and lints @@ -245,9 +243,12 @@ fn check_binders( // There are 3 possibilities: if let Some(prev_info) = binders.get(&name) { // 1. The meta-variable is already bound in the current LHS: This is an error. - let mut span = MultiSpan::from_span(span); - span.push_span_label(prev_info.span, "previous declaration"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::DuplicateMatcherBinding); + buffer_lint( + psess, + span, + node_id, + errors::DuplicateMatcherBindingLint { span, prev: prev_info.span }, + ); } else if get_binder_info(macros, binders, name).is_none() { // 2. The meta-variable is free: This is a binder. binders.insert(name, BinderInfo { span, ops: ops.into() }); @@ -304,8 +305,7 @@ fn get_binder_info<'a>( binders.get(&name).or_else(|| macros.find_map(|state| state.binders.get(&name))) } -/// Checks `rhs` as part of the RHS of a macro definition and sets `valid` to false in case of -/// errors. +/// Checks `rhs` as part of the RHS of a macro definition. /// /// Arguments: /// - `psess` is used to emit diagnostics and lints @@ -370,7 +370,7 @@ enum NestedMacroState { } /// Checks `tts` as part of the RHS of a macro definition, tries to recognize nested macro -/// definitions, and sets `valid` to false in case of errors. +/// definitions. /// /// Arguments: /// - `psess` is used to emit diagnostics and lints @@ -489,8 +489,7 @@ fn check_nested_occurrences( } } -/// Checks the body of nested macro, returns where the check stopped, and sets `valid` to false in -/// case of errors. +/// Checks the body of nested macro, returns where the check stopped. /// /// The token trees are checked as long as they look like a list of (LHS) => {RHS} token trees. This /// check is a best-effort to detect a macro definition. It returns the position in `tts` where we @@ -579,7 +578,7 @@ fn check_ops_is_prefix( return; } } - buffer_lint(psess, span.into(), node_id, BuiltinLintDiag::UnknownMacroVariable(name)); + buffer_lint(psess, span, node_id, errors::UnknownMacroVariable { name }); } /// Returns whether `binder_ops` is a prefix of `occurrence_ops`. @@ -604,29 +603,42 @@ fn ops_is_prefix( psess: &ParseSess, node_id: NodeId, span: Span, - name: MacroRulesNormalizedIdent, + ident: MacroRulesNormalizedIdent, binder_ops: &[KleeneToken], occurrence_ops: &[KleeneToken], ) { for (i, binder) in binder_ops.iter().enumerate() { if i >= occurrence_ops.len() { - let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableStillRepeating(name)); + buffer_lint( + psess, + span, + node_id, + errors::MetaVarStillRepeatingLint { label: binder.span, ident }, + ); return; } let occurrence = &occurrence_ops[i]; if occurrence.op != binder.op { - let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition"); - span.push_span_label(occurrence.span, "conflicting repetition"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableWrongOperator); + buffer_lint( + psess, + span, + node_id, + errors::MetaVariableWrongOperator { + binder: binder.span, + occurrence: occurrence.span, + }, + ); return; } } } -fn buffer_lint(psess: &ParseSess, span: MultiSpan, node_id: NodeId, diag: BuiltinLintDiag) { +fn buffer_lint( + psess: &ParseSess, + span: Span, + node_id: NodeId, + diag: impl Into<DecorateDiagCompat>, +) { // Macros loaded from other crates have dummy node ids. if node_id != DUMMY_NODE_ID { psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, diag); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8b43f852b26..d4504ba720e 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -17,7 +17,6 @@ use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::MacroKinds; use rustc_hir::find_attr; -use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, }; @@ -34,8 +33,8 @@ use super::diagnostics::{FailedMacro, failed_to_match_macro}; use super::macro_parser::{NamedMatches, NamedParseResult}; use super::{SequenceRepetition, diagnostics}; use crate::base::{ - AttrProcMacro, DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult, - SyntaxExtension, SyntaxExtensionKind, TTMacroExpander, + AttrProcMacro, BangProcMacro, DummyResult, ExpandResult, ExtCtxt, MacResult, + MacroExpanderResult, SyntaxExtension, SyntaxExtensionKind, TTMacroExpander, }; use crate::errors; use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment}; @@ -90,7 +89,7 @@ impl<'a> ParserAnyMacro<'a> { SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, parser.token.span, lint_node_id, - BuiltinLintDiag::TrailingMacro(is_trailing_mac, macro_ident), + errors::TrailingMacro { is_trailing: is_trailing_mac, name: macro_ident }, ); } parser.bump(); @@ -268,16 +267,16 @@ impl AttrProcMacro for MacroRulesMacroExpander { } } -struct DummyExpander(ErrorGuaranteed); +struct DummyBang(ErrorGuaranteed); -impl TTMacroExpander for DummyExpander { +impl BangProcMacro for DummyBang { fn expand<'cx>( &self, _: &'cx mut ExtCtxt<'_>, - span: Span, + _: Span, _: TokenStream, - ) -> ExpandResult<Box<dyn MacResult + 'cx>, ()> { - ExpandResult::Ready(DummyResult::any(span, self.0)) + ) -> Result<TokenStream, ErrorGuaranteed> { + Err(self.0) } } @@ -665,7 +664,7 @@ pub fn compile_declarative_macro( SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local) }; let dummy_syn_ext = - |guar| (mk_syn_ext(SyntaxExtensionKind::LegacyBang(Arc::new(DummyExpander(guar)))), 0); + |guar| (mk_syn_ext(SyntaxExtensionKind::Bang(Arc::new(DummyBang(guar)))), 0); let macro_rules = macro_def.macro_rules; let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) }; @@ -703,7 +702,7 @@ pub fn compile_declarative_macro( kinds |= MacroKinds::DERIVE; let derive_keyword_span = p.prev_token.span; if !features.macro_derive() { - feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable") + feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable") .emit(); } if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") { @@ -895,12 +894,12 @@ fn check_redundant_vis_repetition( seq: &SequenceRepetition, span: &DelimSpan, ) { - let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne; - let is_vis = seq.tts.first().map_or(false, |tt| { - matches!(tt, mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. }) - }); - - if is_vis && is_zero_or_one { + if seq.kleene.op == KleeneOp::ZeroOrOne + && matches!( + seq.tts.first(), + Some(mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. }) + ) + { err.note("a `vis` fragment can already be empty"); err.multipart_suggestion( "remove the `$(` and `)?`", @@ -1425,7 +1424,7 @@ fn check_matcher_core<'tt>( RUST_2021_INCOMPATIBLE_OR_PATTERNS, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::OrPatternsBackCompat(span, suggestion), + errors::OrPatternsBackCompat { span, suggestion }, ); } match is_in_follow(next_token, kind) { diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index d2b275ad20a..70d796cda11 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -5,7 +5,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, PResult}; use rustc_macros::{Decodable, Encodable}; use rustc_session::parse::ParseSess; -use rustc_span::{Ident, Span, Symbol}; +use rustc_span::{Ident, Span, Symbol, sym}; use crate::errors; @@ -69,15 +69,15 @@ impl MetaVarExpr { } let mut iter = args.iter(); - let rslt = match ident.as_str() { - "concat" => parse_concat(&mut iter, psess, outer_span, ident.span)?, - "count" => parse_count(&mut iter, psess, ident.span)?, - "ignore" => { + let rslt = match ident.name { + sym::concat => parse_concat(&mut iter, psess, outer_span, ident.span)?, + sym::count => parse_count(&mut iter, psess, ident.span)?, + sym::ignore => { eat_dollar(&mut iter, psess, ident.span)?; MetaVarExpr::Ignore(parse_ident(&mut iter, psess, ident.span)?) } - "index" => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?), - "len" => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?), + sym::index => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?), + sym::len => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?), _ => { let err = errors::MveUnrecognizedExpr { span: ident.span, @@ -119,14 +119,13 @@ fn check_trailing_tokens<'psess>( } // `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), + let (min_or_exact_args, max_args) = match ident.name { + sym::concat => panic!("concat takes unlimited tokens but didn't eat them all"), + sym::ignore => (1, None), // 1 or 2 args - "count" => (1, Some(2)), + sym::count => (1, Some(2)), // 0 or 1 arg - "index" => (0, Some(1)), - "len" => (0, Some(1)), + sym::index | sym::len => (0, Some(1)), other => unreachable!("unknown MVEs should be rejected earlier (got `{other}`)"), }; diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index ed8aa71d59d..dddd62a4945 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -17,8 +17,8 @@ use rustc_span::{ use smallvec::{SmallVec, smallvec}; use crate::errors::{ - CountRepetitionMisplaced, MetaVarsDifSeqMatchers, MustRepeatOnce, MveUnrecognizedVar, - NoSyntaxVarsExprRepeat, VarStillRepeating, + CountRepetitionMisplaced, MacroVarStillRepeating, MetaVarsDifSeqMatchers, MustRepeatOnce, + MveUnrecognizedVar, NoSyntaxVarsExprRepeat, }; use crate::mbe::macro_parser::NamedMatch; use crate::mbe::macro_parser::NamedMatch::*; @@ -375,6 +375,19 @@ fn transcribe_metavar<'tx>( return Ok(()); }; + let MatchedSingle(pnr) = cur_matched else { + // We were unable to descend far enough. This is an error. + return Err(dcx.create_err(MacroVarStillRepeating { span: sp, ident })); + }; + + transcribe_pnr(tscx, sp, pnr) +} + +fn transcribe_pnr<'tx>( + tscx: &mut TranscrCtx<'tx, '_>, + mut sp: Span, + pnr: &ParseNtResult, +) -> PResult<'tx, ()> { // We wrap the tokens in invisible delimiters, unless they are already wrapped // in invisible delimiters with the same `MetaVarKind`. Because some proc // macros can't handle multiple layers of invisible delimiters of the same @@ -404,33 +417,33 @@ fn transcribe_metavar<'tx>( ) }; - let tt = match cur_matched { - MatchedSingle(ParseNtResult::Tt(tt)) => { + let tt = match pnr { + ParseNtResult::Tt(tt) => { // `tt`s are emitted into the output stream directly as "raw tokens", // without wrapping them into groups. Other variables are emitted into // the output stream as groups with `Delimiter::Invisible` to maintain // parsing priorities. maybe_use_metavar_location(tscx.psess, &tscx.stack, sp, tt, &mut tscx.marker) } - MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => { + ParseNtResult::Ident(ident, is_raw) => { tscx.marker.mark_span(&mut sp); with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtIdent(*ident, *is_raw); TokenTree::token_alone(kind, sp) } - MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => { + ParseNtResult::Lifetime(ident, is_raw) => { tscx.marker.mark_span(&mut sp); with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtLifetime(*ident, *is_raw); TokenTree::token_alone(kind, sp) } - MatchedSingle(ParseNtResult::Item(item)) => { + ParseNtResult::Item(item) => { mk_delimited(item.span, MetaVarKind::Item, TokenStream::from_ast(item)) } - MatchedSingle(ParseNtResult::Block(block)) => { + ParseNtResult::Block(block) => { mk_delimited(block.span, MetaVarKind::Block, TokenStream::from_ast(block)) } - MatchedSingle(ParseNtResult::Stmt(stmt)) => { + ParseNtResult::Stmt(stmt) => { let stream = if let StmtKind::Empty = stmt.kind { // FIXME: Properly collect tokens for empty statements. TokenStream::token_alone(token::Semi, stmt.span) @@ -439,10 +452,10 @@ fn transcribe_metavar<'tx>( }; mk_delimited(stmt.span, MetaVarKind::Stmt, stream) } - MatchedSingle(ParseNtResult::Pat(pat, pat_kind)) => { + ParseNtResult::Pat(pat, pat_kind) => { mk_delimited(pat.span, MetaVarKind::Pat(*pat_kind), TokenStream::from_ast(pat)) } - MatchedSingle(ParseNtResult::Expr(expr, kind)) => { + ParseNtResult::Expr(expr, kind) => { let (can_begin_literal_maybe_minus, can_begin_string_literal) = match &expr.kind { ExprKind::Lit(_) => (true, true), ExprKind::Unary(UnOp::Neg, e) if matches!(&e.kind, ExprKind::Lit(_)) => { @@ -460,14 +473,14 @@ fn transcribe_metavar<'tx>( TokenStream::from_ast(expr), ) } - MatchedSingle(ParseNtResult::Literal(lit)) => { + ParseNtResult::Literal(lit) => { mk_delimited(lit.span, MetaVarKind::Literal, TokenStream::from_ast(lit)) } - MatchedSingle(ParseNtResult::Ty(ty)) => { + ParseNtResult::Ty(ty) => { let is_path = matches!(&ty.kind, TyKind::Path(None, _path)); mk_delimited(ty.span, MetaVarKind::Ty { is_path }, TokenStream::from_ast(ty)) } - MatchedSingle(ParseNtResult::Meta(attr_item)) => { + ParseNtResult::Meta(attr_item) => { let has_meta_form = attr_item.meta_kind().is_some(); mk_delimited( attr_item.span(), @@ -475,16 +488,12 @@ fn transcribe_metavar<'tx>( TokenStream::from_ast(attr_item), ) } - MatchedSingle(ParseNtResult::Path(path)) => { + ParseNtResult::Path(path) => { mk_delimited(path.span, MetaVarKind::Path, TokenStream::from_ast(path)) } - MatchedSingle(ParseNtResult::Vis(vis)) => { + ParseNtResult::Vis(vis) => { mk_delimited(vis.span, MetaVarKind::Vis, TokenStream::from_ast(vis)) } - MatchedSeq(..) => { - // We were unable to descend far enough. This is an error. - return Err(dcx.create_err(VarStillRepeating { span: sp, ident })); - } }; tscx.result.push(tt); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 6af4cfb0e56..364a1202b05 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -205,7 +205,7 @@ declare_features! ( (accepted, extended_key_value_attributes, "1.54.0", Some(78835)), /// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions /// for functions with varargs. - (accepted, extended_varargs_abi_support, "CURRENT_RUSTC_VERSION", Some(100189)), + (accepted, extended_varargs_abi_support, "1.91.0", Some(100189)), /// Allows resolving absolute paths as paths from other crates. (accepted, extern_absolute_paths, "1.30.0", Some(44660)), /// Allows `extern crate foo as bar;`. This puts `bar` into extern prelude. @@ -400,7 +400,7 @@ declare_features! ( /// Allows use of `&foo[a..b]` as a slicing syntax. (accepted, slicing_syntax, "1.0.0", None), /// Allows use of `sse4a` target feature. - (accepted, sse4a_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (accepted, sse4a_target_feature, "1.91.0", Some(44839)), /// Allows elision of `'static` lifetimes in `static`s and `const`s. (accepted, static_in_const, "1.17.0", Some(35897)), /// Allows the definition recursive static items. @@ -414,7 +414,7 @@ declare_features! ( /// Allows the use of `#[target_feature]` on safe functions. (accepted, target_feature_11, "1.86.0", Some(69098)), /// Allows use of `tbm` target feature. - (accepted, tbm_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (accepted, tbm_target_feature, "1.91.0", Some(44839)), /// Allows `fn main()` with return types which implements `Termination` (RFC 1937). (accepted, termination_trait, "1.26.0", Some(43301)), /// Allows `#[test]` functions where the return type implements `Termination` (RFC 1937). diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 129ab7eccb5..220388ffe43 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1057,6 +1057,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, ), + rustc_attr!( + rustc_objc_class, Normal, template!(NameValueStr: "ClassName"), ErrorPreceding, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_objc_selector, Normal, template!(NameValueStr: "methodName"), ErrorPreceding, + EncodeCrossCrate::No, + ), // ========================================================================== // Internal attributes, Macro related: @@ -1204,6 +1212,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ niche optimizations in the standard library", ), rustc_attr!( + rustc_simd_monomorphize_lane_limit, Normal, template!(NameValueStr: "N"), ErrorFollowing, + EncodeCrossCrate::Yes, + "the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \ + for better error messages", + ), + rustc_attr!( rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document \ diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index e37fc6b7bfc..539d67e0b6b 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -101,6 +101,10 @@ declare_features! ( Some("never properly implemented; requires significant design work"), 127655), /// Allows deriving traits as per `SmartPointer` specification (removed, derive_smart_pointer, "1.84.0", Some(123430), Some("replaced by `CoercePointee`"), 131284), + /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. + (removed, doc_auto_cfg, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907), + /// Allows `#[doc(cfg_hide(...))]`. + (removed, doc_cfg_hide, "CURRENT_RUSTC_VERSION", Some(43781), Some("merged into `doc_cfg`"), 138907), /// Allows using `#[doc(keyword = "...")]`. (removed, doc_keyword, "1.58.0", Some(51315), Some("merged into `#![feature(rustdoc_internals)]`"), 90420), @@ -192,7 +196,7 @@ declare_features! ( (removed, no_debug, "1.43.0", Some(29721), Some("removed due to lack of demand"), 69667), // Allows the use of `no_sanitize` attribute. /// The feature was renamed to `sanitize` and the attribute to `#[sanitize(xyz = "on|off")]` - (removed, no_sanitize, "CURRENT_RUSTC_VERSION", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), + (removed, no_sanitize, "1.91.0", Some(39699), Some(r#"renamed to sanitize(xyz = "on|off")"#), 142681), /// Note: this feature was previously recorded in a separate /// `STABLE_REMOVED` list because it, uniquely, was once stable but was /// then removed. But there was no utility storing it separately, so now @@ -203,7 +207,7 @@ declare_features! ( (removed, object_safe_for_dispatch, "1.83.0", Some(43561), Some("renamed to `dyn_compatible_for_dispatch`"), 131511), /// Allows using `#[omit_gdb_pretty_printer_section]`. - (removed, omit_gdb_pretty_printer_section, "CURRENT_RUSTC_VERSION", None, None, 144738), + (removed, omit_gdb_pretty_printer_section, "1.91.0", None, None, 144738), /// Allows using `#[on_unimplemented(..)]` on traits. /// (Moved to `rustc_attrs`.) (removed, on_unimplemented, "1.40.0", None, None, 65794), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 93e5588146e..e63f29a9570 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -327,7 +327,7 @@ declare_features! ( (unstable, m68k_target_feature, "1.85.0", Some(134328)), (unstable, mips_target_feature, "1.27.0", Some(44839)), (unstable, movrs_target_feature, "1.88.0", Some(137976)), - (unstable, nvptx_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), + (unstable, nvptx_target_feature, "1.91.0", Some(44839)), (unstable, powerpc_target_feature, "1.27.0", Some(44839)), (unstable, prfchw_target_feature, "1.78.0", Some(44839)), (unstable, riscv_target_feature, "1.45.0", Some(44839)), @@ -471,17 +471,13 @@ declare_features! ( /// Allows deref patterns. (incomplete, deref_patterns, "1.79.0", Some(87121)), /// Allows deriving the From trait on single-field structs. - (unstable, derive_from, "CURRENT_RUSTC_VERSION", Some(144889)), - /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. - (unstable, doc_auto_cfg, "1.58.0", Some(43781)), + (unstable, derive_from, "1.91.0", Some(144889)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), - /// Allows `#[doc(cfg_hide(...))]`. - (unstable, doc_cfg_hide, "1.57.0", Some(43781)), /// Allows `#[doc(masked)]`. (unstable, doc_masked, "1.21.0", Some(44027)), /// Allows features to allow target_feature to better interact with traits. - (incomplete, effective_target_features, "CURRENT_RUSTC_VERSION", Some(143352)), + (incomplete, effective_target_features, "1.91.0", Some(143352)), /// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }` (incomplete, ergonomic_clones, "1.87.0", Some(132290)), /// Allows exhaustive pattern matching on types that contain uninhabited types. @@ -554,9 +550,9 @@ declare_features! ( /// Allows fused `loop`/`match` for direct intraprocedural jumps. (incomplete, loop_match, "1.90.0", Some(132306)), /// Allow `macro_rules!` attribute rules - (unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)), + (unstable, macro_attr, "1.91.0", Some(143547)), /// Allow `macro_rules!` derive rules - (unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)), + (unstable, macro_derive, "1.91.0", Some(143549)), /// Give access to additional metadata about declarative macro meta-variables. (unstable, macro_metavar_expr, "1.61.0", Some(83527)), /// Provides a way to concatenate identifiers using metavariable expressions. @@ -613,7 +609,7 @@ declare_features! ( (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows the use of raw-dylibs on ELF platforms (incomplete, raw_dylib_elf, "1.87.0", Some(135694)), - (unstable, reborrow, "CURRENT_RUSTC_VERSION", Some(145612)), + (unstable, reborrow, "1.91.0", Some(145612)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024. (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)), /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant @@ -627,13 +623,13 @@ declare_features! ( /// Allows `extern "rust-cold"`. (unstable, rust_cold_cc, "1.63.0", Some(97544)), /// Allows the use of the `sanitize` attribute. - (unstable, sanitize, "CURRENT_RUSTC_VERSION", Some(39699)), + (unstable, sanitize, "1.91.0", Some(39699)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). (incomplete, specialization, "1.7.0", Some(31844)), /// Allows using `#[rustc_align_static(...)]` on static items. - (unstable, static_align, "CURRENT_RUSTC_VERSION", Some(146177)), + (unstable, static_align, "1.91.0", Some(146177)), /// Allows attributes on expressions and non-item statements. (unstable, stmt_expr_attributes, "1.6.0", Some(15701)), /// Allows lints part of the strict provenance effort. @@ -645,7 +641,7 @@ declare_features! ( /// Allows subtrait items to shadow supertrait items. (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), /// Allows the use of target_feature when a function is marked inline(always). - (unstable, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574)), + (unstable, target_feature_inline_always, "1.91.0", Some(145574)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ea11a99efbc..ddcbaeaad88 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -363,6 +363,20 @@ pub struct LinkEntry { pub import_name_type: Option<(PeImportNameType, Span)>, } +#[derive(HashStable_Generic, PrintAttribute)] +#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] +pub enum DebuggerVisualizerType { + Natvis, + GdbPrettyPrinter, +} + +#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic, PrintAttribute)] +pub struct DebugVisualizer { + pub span: Span, + pub visualizer_type: DebuggerVisualizerType, + pub path: Symbol, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -444,9 +458,6 @@ pub enum AttributeKind { span: Span, }, - /// Represents `#[rustc_coherence_is_core]`. - CoherenceIsCore, - /// Represents `#[rustc_coinductive]`. Coinductive(Span), @@ -488,7 +499,10 @@ pub enum AttributeKind { /// Represents `#[custom_mir]`. CustomMir(Option<(MirDialect, Span)>, Option<(MirPhase, Span)>, Span), - ///Represents `#[rustc_deny_explicit_impl]`. + /// Represents `#[debugger_visualizer]`. + DebuggerVisualizer(ThinVec<DebugVisualizer>), + + /// Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), /// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute). @@ -554,6 +568,9 @@ pub enum AttributeKind { /// Represents `#[macro_escape]`. MacroEscape(Span), + /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path). + MacroExport { span: Span, local_inner_macros: bool }, + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), @@ -594,6 +611,12 @@ pub enum AttributeKind { /// Represents `#[non_exhaustive]` NonExhaustive(Span), + /// Represents `#[rustc_objc_class]` + ObjcClass { classname: Symbol, span: Span }, + + /// Represents `#[rustc_objc_selector]` + ObjcSelector { methname: Symbol, span: Span }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), @@ -633,6 +656,9 @@ pub enum AttributeKind { /// Represents `#[rustc_builtin_macro]`. RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span }, + /// Represents `#[rustc_coherence_is_core]` + RustcCoherenceIsCore(Span), + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box<u128>, Span), @@ -642,6 +668,9 @@ pub enum AttributeKind { /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, + /// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`. + RustcSimdMonomorphizeLaneLimit(Limit), + /// Represents `#[sanitize]` /// /// the on set and off set are distjoint since there's a third option: unset. diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 55521c15854..1611b865c77 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -26,7 +26,6 @@ impl AttributeKind { AsPtr(..) => Yes, AutomaticallyDerived(..) => Yes, BodyStability { .. } => No, - CoherenceIsCore => No, Coinductive(..) => No, Cold(..) => No, Confusables { .. } => Yes, @@ -38,6 +37,7 @@ impl AttributeKind { Coverage(..) => No, CrateName { .. } => No, CustomMir(_, _, _) => Yes, + DebuggerVisualizer(..) => No, DenyExplicitImpl(..) => No, Deprecation { .. } => Yes, DoNotImplementViaObject(..) => No, @@ -57,6 +57,7 @@ impl AttributeKind { Linkage(..) => No, LoopMatch(..) => No, MacroEscape(..) => No, + MacroExport { .. } => Yes, MacroTransparency(..) => Yes, MacroUse { .. } => No, Marker(..) => No, @@ -69,6 +70,8 @@ impl AttributeKind { NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc + ObjcClass { .. } => No, + ObjcSelector { .. } => No, Optimize(..) => No, ParenSugar(..) => No, PassByValue(..) => Yes, @@ -82,9 +85,11 @@ impl AttributeKind { RecursionLimit { .. } => No, Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, + RustcCoherenceIsCore(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, + RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate Sanitize { .. } => No, ShouldPanic { .. } => No, SkipDuringMethodDispatch { .. } => No, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 75551fe4c19..bc1c47e95c3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1298,10 +1298,7 @@ impl AttributeExt for Attribute { #[inline] fn path_matches(&self, name: &[Symbol]) -> bool { match &self { - Attribute::Unparsed(n) => { - n.path.segments.len() == name.len() - && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n) - } + Attribute::Unparsed(n) => n.path.segments.iter().map(|ident| &ident.name).eq(name), _ => false, } } @@ -3220,12 +3217,21 @@ pub struct ImplItem<'hir> { pub owner_id: OwnerId, pub generics: &'hir Generics<'hir>, pub kind: ImplItemKind<'hir>, - pub defaultness: Defaultness, + pub impl_kind: ImplItemImplKind, pub span: Span, - pub vis_span: Span, pub has_delayed_lints: bool, - /// When we are in a trait impl, link to the trait-item's id. - pub trait_item_def_id: Option<DefId>, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum ImplItemImplKind { + Inherent { + vis_span: Span, + }, + Trait { + defaultness: Defaultness, + /// Item in the trait that this item implements + trait_item_def_id: Result<DefId, ErrorGuaranteed>, + }, } impl<'hir> ImplItem<'hir> { @@ -3239,6 +3245,13 @@ impl<'hir> ImplItem<'hir> { ImplItemId { owner_id: self.owner_id } } + pub fn vis_span(&self) -> Option<Span> { + match self.impl_kind { + ImplItemImplKind::Trait { .. } => None, + ImplItemImplKind::Inherent { vis_span, .. } => Some(vis_span), + } + } + expect_methods_self_kind! { expect_const, (&'hir Ty<'hir>, BodyId), ImplItemKind::Const(ty, body), (ty, *body); expect_fn, (&FnSig<'hir>, BodyId), ImplItemKind::Fn(ty, body), (ty, *body); @@ -4985,7 +4998,7 @@ mod size_asserts { static_assert_size!(GenericBound<'_>, 64); static_assert_size!(Generics<'_>, 56); static_assert_size!(Impl<'_>, 40); - static_assert_size!(ImplItem<'_>, 96); + static_assert_size!(ImplItem<'_>, 88); static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); static_assert_size!(ItemKind<'_>, 64); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 25a7ae239f3..eb682f32111 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1257,18 +1257,21 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( owner_id: _, ident, ref generics, + ref impl_kind, ref kind, - ref defaultness, span: _, - vis_span: _, has_delayed_lints: _, - trait_item_def_id: _, } = *impl_item; try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_defaultness(defaultness)); try_visit!(visitor.visit_id(impl_item.hir_id())); + match impl_kind { + ImplItemImplKind::Inherent { vis_span: _ } => {} + ImplItemImplKind::Trait { defaultness, trait_item_def_id: _ } => { + try_visit!(visitor.visit_defaultness(defaultness)); + } + } match *kind { ImplItemKind::Const(ref ty, body) => { try_visit!(visitor.visit_ty_unambig(ty)); diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 67d2f15d414..311cf8f995c 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -370,7 +370,6 @@ language_item_table! { CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0); ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); - UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; @@ -441,6 +440,7 @@ language_item_table! { // Reborrowing related lang-items Reborrow, sym::reborrow, reborrow, Target::Trait, GenericRequirement::Exact(0); + CoerceShared, sym::coerce_shared, coerce_shared, Target::Trait, GenericRequirement::Exact(0); } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 0b24052b453..c9de6f6b5d5 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -31,9 +31,35 @@ pub struct AttributeLint<Id> { #[derive(Clone, Debug, HashStable_Generic)] pub enum AttributeLintKind { - UnusedDuplicate { this: Span, other: Span, warning: bool }, - IllFormedAttributeInput { suggestions: Vec<String> }, - EmptyAttribute { first_span: Span }, - InvalidTarget { name: AttrPath, target: Target, applied: Vec<String>, only: &'static str }, - InvalidStyle { name: AttrPath, is_used_as_inner: bool, target: Target, target_span: Span }, + /// Copy of `IllFormedAttributeInput` + /// specifically for the `invalid_macro_export_arguments` lint until that is removed, + /// see <https://github.com/rust-lang/rust/pull/143857#issuecomment-3079175663> + InvalidMacroExportArguments { + suggestions: Vec<String>, + }, + UnusedDuplicate { + this: Span, + other: Span, + warning: bool, + }, + IllFormedAttributeInput { + suggestions: Vec<String>, + }, + EmptyAttribute { + first_span: Span, + attr_path: AttrPath, + valid_without_list: bool, + }, + InvalidTarget { + name: AttrPath, + target: Target, + applied: Vec<String>, + only: &'static str, + }, + InvalidStyle { + name: AttrPath, + is_used_as_inner: bool, + target: Target, + target_span: Span, + }, } diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index e8237471e1b..88bd3339e4e 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -68,7 +68,14 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { return None; } - if self.state.cur_ty.is_ty_var() { + // We want to support method and function calls for `impl Deref<Target = ..>`. + // + // To do so we don't eagerly bail if the current type is the hidden type of an + // opaque type and instead return `None` in `fn overloaded_deref_ty` if the + // opaque does not have a `Deref` item-bound. + if let &ty::Infer(ty::TyVar(vid)) = self.state.cur_ty.kind() + && !self.infcx.has_opaques_with_sub_unified_hidden_type(vid) + { return None; } @@ -160,7 +167,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.param_env, ty::Binder::dummy(trait_ref), ); - if !self.infcx.next_trait_solver() && !self.infcx.predicate_may_hold(&obligation) { + // We detect whether the self type implements `Deref` before trying to + // structurally normalize. We use `predicate_may_hold_opaque_types_jank` + // to support not-yet-defined opaque types. It will succeed for `impl Deref` + // but fail for `impl OtherTrait`. + if !self.infcx.predicate_may_hold_opaque_types_jank(&obligation) { debug!("overloaded_deref_ty: cannot match obligation"); return None; } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 08b344638dd..e1e6860e430 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -219,7 +219,7 @@ fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting // `async-std` (and `pub async fn` in general). - // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! + // Since rustdoc doesn't care about the hidden type behind `impl Trait`, just don't look at it! // See https://github.com/rust-lang/rust/issues/75100 if tcx.sess.opts.actually_rustdoc { return; @@ -252,7 +252,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>( Ok(()) } -/// Check that the concrete type behind `impl Trait` actually implements `Trait`. +/// Check that the hidden type behind `impl Trait` actually implements `Trait`. /// /// This is mostly checked at the places that specify the opaque type, but we /// check those cases in the `param_env` of that function, which may have @@ -1009,8 +1009,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), res = res.and(check_associated_item(tcx, def_id)); let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocItemContainer::Impl => {} - ty::AssocItemContainer::Trait => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => {} + ty::AssocContainer::Trait => { res = res.and(check_trait_item(tcx, def_id)); } } @@ -1026,8 +1026,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), res = res.and(check_associated_item(tcx, def_id)); let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocItemContainer::Impl => {} - ty::AssocItemContainer::Trait => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => {} + ty::AssocContainer::Trait => { res = res.and(check_trait_item(tcx, def_id)); } } @@ -1043,8 +1043,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), let assoc_item = tcx.associated_item(def_id); let has_type = match assoc_item.container { - ty::AssocItemContainer::Impl => true, - ty::AssocItemContainer::Trait => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, + ty::AssocContainer::Trait => { tcx.ensure_ok().explicit_item_bounds(def_id); tcx.ensure_ok().explicit_item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { @@ -1177,12 +1177,9 @@ fn check_impl_items_against_trait<'tcx>( for &impl_item in impl_item_refs { let ty_impl_item = tcx.associated_item(impl_item); - let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { - tcx.associated_item(trait_item_id) - } else { - // Checked in `associated_item`. - tcx.dcx().span_delayed_bug(tcx.def_span(impl_item), "missing associated item in trait"); - continue; + let ty_trait_item = match ty_impl_item.expect_trait_impl() { + Ok(trait_item_id) => tcx.associated_item(trait_item_id), + Err(ErrorGuaranteed { .. }) => continue, }; let res = tcx.ensure_ok().compare_impl_item(impl_item.expect_local()); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index e4827256193..946c4936bb6 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -37,7 +37,7 @@ pub(super) fn compare_impl_item( impl_item_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { let impl_item = tcx.associated_item(impl_item_def_id); - let trait_item = tcx.associated_item(impl_item.trait_item_def_id.unwrap()); + let trait_item = tcx.associated_item(impl_item.expect_trait_impl()?); let impl_trait_ref = tcx.impl_trait_ref(impl_item.container_id(tcx)).unwrap().instantiate_identity(); debug!(?impl_trait_ref); @@ -298,14 +298,9 @@ fn compare_method_predicate_entailment<'tcx>( // compatible with that of the trait method. We do this by // checking that `impl_fty <: trait_fty`. // - // FIXME. Unfortunately, this doesn't quite work right now because - // associated type normalization is not integrated into subtype - // checks. For the comparison to be valid, we need to - // normalize the associated types in the impl/trait methods - // first. However, because function types bind regions, just - // calling `FnCtxt::normalize` would have no effect on - // any associated types appearing in the fn arguments or return - // type. + // FIXME: We manually instantiate the trait method here as we need + // to manually compute its implied bounds. Otherwise this could just + // be `ocx.sub(impl_sig, trait_sig)`. let mut wf_tys = FxIndexSet::default(); @@ -446,7 +441,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( impl_m_def_id: LocalDefId, ) -> Result<&'tcx DefIdMap<ty::EarlyBinder<'tcx, Ty<'tcx>>>, ErrorGuaranteed> { let impl_m = tcx.associated_item(impl_m_def_id.to_def_id()); - let trait_m = tcx.associated_item(impl_m.trait_item_def_id.unwrap()); + let trait_m = tcx.associated_item(impl_m.expect_trait_impl()?); let impl_trait_ref = tcx.impl_trait_ref(tcx.parent(impl_m_def_id.to_def_id())).unwrap().instantiate_identity(); // First, check a few of the same things as `compare_impl_method`, @@ -1449,8 +1444,10 @@ fn compare_self_type<'tcx>( let self_string = |method: ty::AssocItem| { let untransformed_self_ty = match method.container { - ty::AssocItemContainer::Impl => impl_trait_ref.self_ty(), - ty::AssocItemContainer::Trait => tcx.types.self_param, + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { + impl_trait_ref.self_ty() + } + ty::AssocContainer::Trait => tcx.types.self_param, }; let self_arg_ty = tcx.fn_sig(method.def_id).instantiate_identity().input(0); let (infcx, param_env) = tcx @@ -2458,8 +2455,12 @@ fn param_env_with_gat_bounds<'tcx>( for impl_ty in impl_tys_to_install { let trait_ty = match impl_ty.container { - ty::AssocItemContainer::Trait => impl_ty, - ty::AssocItemContainer::Impl => tcx.associated_item(impl_ty.trait_item_def_id.unwrap()), + ty::AssocContainer::InherentImpl => bug!(), + ty::AssocContainer::Trait => impl_ty, + ty::AssocContainer::TraitImpl(Err(_)) => continue, + ty::AssocContainer::TraitImpl(Ok(trait_item_def_id)) => { + tcx.associated_item(trait_item_def_id) + } }; let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index aa2d27ab809..bc3448be582 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -64,83 +64,159 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi // it's usually worth updating that intrinsic's documentation // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. - sym::abort + + // tidy-alphabetical-start + | sym::abort + | sym::add_with_overflow + | sym::aggregate_raw_ptr + | sym::align_of | sym::assert_inhabited - | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid + | sym::assert_zero_valid + | sym::autodiff + | sym::bitreverse + | sym::black_box | sym::box_new | sym::breakpoint - | sym::size_of - | sym::align_of - | sym::needs_drop + | sym::bswap | sym::caller_location - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow | sym::carrying_mul_add - | sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::saturating_add - | sym::saturating_sub - | sym::rotate_left - | sym::rotate_right - | sym::ctpop + | sym::ceilf16 + | sym::ceilf32 + | sym::ceilf64 + | sym::ceilf128 + | sym::cold_path + | sym::const_eval_select + | sym::contract_check_ensures + | sym::contract_check_requires + | sym::contract_checks + | sym::copysignf16 + | sym::copysignf32 + | sym::copysignf64 + | sym::copysignf128 + | sym::cosf16 + | sym::cosf32 + | sym::cosf64 + | sym::cosf128 | sym::ctlz + | sym::ctpop | sym::cttz - | sym::bswap - | sym::bitreverse - | sym::three_way_compare | sym::discriminant_value - | sym::type_id - | sym::type_id_eq - | sym::select_unpredictable - | sym::cold_path - | sym::ptr_guaranteed_cmp - | sym::minnumf16 - | sym::minnumf32 - | sym::minnumf64 - | sym::minnumf128 - | sym::minimumf16 - | sym::minimumf32 - | sym::minimumf64 - | sym::minimumf128 - | sym::maxnumf16 - | sym::maxnumf32 - | sym::maxnumf64 - | sym::maxnumf128 + | sym::exp2f16 + | sym::exp2f32 + | sym::exp2f64 + | sym::exp2f128 + | sym::expf16 + | sym::expf32 + | sym::expf64 + | sym::expf128 + | sym::fabsf16 + | sym::fabsf32 + | sym::fabsf64 + | sym::fabsf128 + | sym::fadd_algebraic + | sym::fdiv_algebraic + | sym::floorf16 + | sym::floorf32 + | sym::floorf64 + | sym::floorf128 + | sym::fmaf16 + | sym::fmaf32 + | sym::fmaf64 + | sym::fmaf128 + | sym::fmul_algebraic + | sym::fmuladdf16 + | sym::fmuladdf32 + | sym::fmuladdf64 + | sym::fmuladdf128 + | sym::forget + | sym::frem_algebraic + | sym::fsub_algebraic + | sym::is_val_statically_known + | sym::log2f16 + | sym::log2f32 + | sym::log2f64 + | sym::log2f128 + | sym::log10f16 + | sym::log10f32 + | sym::log10f64 + | sym::log10f128 + | sym::logf16 + | sym::logf32 + | sym::logf64 + | sym::logf128 | sym::maximumf16 | sym::maximumf32 | sym::maximumf64 | sym::maximumf128 - | sym::rustc_peek - | sym::type_name - | sym::forget - | sym::black_box - | sym::variant_count - | sym::is_val_statically_known + | sym::maxnumf16 + | sym::maxnumf32 + | sym::maxnumf64 + | sym::maxnumf128 + | sym::minimumf16 + | sym::minimumf32 + | sym::minimumf64 + | sym::minimumf128 + | sym::minnumf16 + | sym::minnumf32 + | sym::minnumf64 + | sym::minnumf128 + | sym::mul_with_overflow + | sym::needs_drop + | sym::powf16 + | sym::powf32 + | sym::powf64 + | sym::powf128 + | sym::powif16 + | sym::powif32 + | sym::powif64 + | sym::powif128 + | sym::prefetch_read_data + | sym::prefetch_read_instruction + | sym::prefetch_write_data + | sym::prefetch_write_instruction + | sym::ptr_guaranteed_cmp | sym::ptr_mask - | sym::aggregate_raw_ptr | sym::ptr_metadata - | sym::ub_checks - | sym::contract_checks - | sym::contract_check_requires - | sym::contract_check_ensures - | sym::fadd_algebraic - | sym::fsub_algebraic - | sym::fmul_algebraic - | sym::fdiv_algebraic - | sym::frem_algebraic + | sym::rotate_left + | sym::rotate_right | sym::round_ties_even_f16 | sym::round_ties_even_f32 | sym::round_ties_even_f64 | sym::round_ties_even_f128 - | sym::autodiff - | sym::prefetch_read_data - | sym::prefetch_write_data - | sym::prefetch_read_instruction - | sym::prefetch_write_instruction - | sym::const_eval_select => hir::Safety::Safe, + | sym::roundf16 + | sym::roundf32 + | sym::roundf64 + | sym::roundf128 + | sym::rustc_peek + | sym::saturating_add + | sym::saturating_sub + | sym::select_unpredictable + | sym::sinf16 + | sym::sinf32 + | sym::sinf64 + | sym::sinf128 + | sym::size_of + | sym::sqrtf16 + | sym::sqrtf32 + | sym::sqrtf64 + | sym::sqrtf128 + | sym::sub_with_overflow + | sym::three_way_compare + | sym::truncf16 + | sym::truncf32 + | sym::truncf64 + | sym::truncf128 + | sym::type_id + | sym::type_id_eq + | sym::type_name + | sym::ub_checks + | sym::variant_count + | sym::wrapping_add + | sym::wrapping_mul + | sym::wrapping_sub + // tidy-alphabetical-end + => hir::Safety::Safe, _ => hir::Safety::Unsafe, }; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 63d0f400aef..e70d5505aae 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -70,6 +70,7 @@ pub mod intrinsic; mod region; pub mod wfcheck; +use std::borrow::Cow; use std::num::NonZero; pub use check::{check_abi, check_custom_abi}; @@ -86,7 +87,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_types_for_signature; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypingMode, + self, GenericArgs, GenericArgsRef, OutlivesPredicate, Region, Ty, TyCtxt, TypingMode, }; use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; @@ -335,6 +336,7 @@ fn bounds_from_generic_predicates<'tcx>( assoc: ty::AssocItem, ) -> (String, String) { let mut types: FxIndexMap<Ty<'tcx>, Vec<DefId>> = FxIndexMap::default(); + let mut regions: FxIndexMap<Region<'tcx>, Vec<Region<'tcx>>> = FxIndexMap::default(); let mut projections = vec![]; for (predicate, _) in predicates { debug!("predicate {:?}", predicate); @@ -351,20 +353,23 @@ fn bounds_from_generic_predicates<'tcx>( ty::ClauseKind::Projection(projection_pred) => { projections.push(bound_predicate.rebind(projection_pred)); } + ty::ClauseKind::RegionOutlives(OutlivesPredicate(a, b)) => { + regions.entry(a).or_default().push(b); + } _ => {} } } let mut where_clauses = vec![]; let generics = tcx.generics_of(assoc.def_id); - let types_str = generics + let params = generics .own_params .iter() - .filter(|p| matches!(p.kind, GenericParamDefKind::Type { synthetic: false, .. })) - .map(|p| { - // we just checked that it's a type, so the unwrap can't fail - let ty = tcx.mk_param_from_def(p).as_type().unwrap(); - if let Some(bounds) = types.get(&ty) { + .filter(|p| !p.kind.is_synthetic()) + .map(|p| match tcx.mk_param_from_def(p).kind() { + ty::GenericArgKind::Type(ty) => { + let bounds = + types.get(&ty).map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(Vec::new())); let mut bounds_str = vec![]; for bound in bounds.iter().copied() { let mut projections_str = vec![]; @@ -377,7 +382,11 @@ fn bounds_from_generic_predicates<'tcx>( projections_str.push(format!("{} = {}", name, p.term)); } } - let bound_def_path = tcx.def_path_str(bound); + let bound_def_path = if tcx.is_lang_item(bound, LangItem::MetaSized) { + String::from("?Sized") + } else { + tcx.def_path_str(bound) + }; if projections_str.is_empty() { where_clauses.push(format!("{}: {}", ty, bound_def_path)); } else { @@ -393,8 +402,21 @@ fn bounds_from_generic_predicates<'tcx>( } else { format!("{}: {}", ty, bounds_str.join(" + ")) } - } else { - ty.to_string() + } + ty::GenericArgKind::Const(ct) => { + format!("const {ct}: {}", tcx.type_of(p.def_id).skip_binder()) + } + ty::GenericArgKind::Lifetime(region) => { + if let Some(v) = regions.get(®ion) + && !v.is_empty() + { + format!( + "{region}: {}", + v.into_iter().map(Region::to_string).collect::<Vec<_>>().join(" + ") + ) + } else { + region.to_string() + } } }) .collect::<Vec<_>>(); @@ -409,7 +431,7 @@ fn bounds_from_generic_predicates<'tcx>( } let generics = - if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) }; + if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(", ")) }; let where_clauses = if where_clauses.is_empty() { "".to_string() diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 2ba7ed46f92..43e6f5fe104 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -467,8 +467,12 @@ fn resolve_local<'tcx>( // A, but the inner rvalues `a()` and `b()` have an extended lifetime // due to rule C. - if let_kind == LetKind::Super { - if let Some(scope) = visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) { + let extend_initializer = match let_kind { + LetKind::Regular => true, + LetKind::Super + if let Some(scope) = + visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) => + { // This expression was lifetime-extended by a parent let binding. E.g. // // let a = { @@ -481,7 +485,10 @@ fn resolve_local<'tcx>( // Processing of `let a` will have already decided to extend the lifetime of this // `super let` to its own var_scope. We use that scope. visitor.cx.var_parent = scope; - } else { + // Extend temporaries to live in the same scope as the parent `let`'s bindings. + true + } + LetKind::Super => { // This `super let` is not subject to lifetime extension from a parent let binding. E.g. // // identity({ super let x = temp(); &x }).method(); @@ -493,10 +500,17 @@ fn resolve_local<'tcx>( if let Some(inner_scope) = visitor.cx.var_parent { (visitor.cx.var_parent, _) = visitor.scope_tree.default_temporary_scope(inner_scope) } + // Don't lifetime-extend child `super let`s or block tail expressions' temporaries in + // the initializer when this `super let` is not itself extended by a parent `let` + // (#145784). Block tail expressions are temporary drop scopes in Editions 2024 and + // later, their temps shouldn't outlive the block in e.g. `f(pin!({ &temp() }))`. + false } - } + }; - if let Some(expr) = init { + if let Some(expr) = init + && extend_initializer + { record_rvalue_scope_if_borrow_expr(visitor, expr, visitor.cx.var_parent); if let Some(pat) = pat { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 22a9446fd4c..0a555c7f6e9 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -819,17 +819,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er let span = tcx.def_span(param.def_id); let def_id = param.def_id.expect_local(); - if tcx.features().unsized_const_params() { - enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| { - wfcx.register_bound( - ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)), - wfcx.param_env, - ty, - tcx.require_lang_item(LangItem::UnsizedConstParamTy, span), - ); - Ok(()) - }) - } else if tcx.features().adt_const_params() { + if tcx.features().adt_const_params() { enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| { wfcx.register_bound( ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)), @@ -880,7 +870,6 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er tcx, tcx.param_env(param.def_id), ty, - LangItem::ConstParamTy, cause, ) { // Can never implement `ConstParamTy`, don't suggest anything. @@ -944,12 +933,11 @@ pub(crate) fn check_associated_item( // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. - tcx.ensure_ok() - .coherent_trait(tcx.parent(item.trait_item_def_id.unwrap_or(item_id.into())))?; + tcx.ensure_ok().coherent_trait(tcx.parent(item.trait_item_or_self()?))?; let self_ty = match item.container { - ty::AssocItemContainer::Trait => tcx.types.self_param, - ty::AssocItemContainer::Impl => { + ty::AssocContainer::Trait => tcx.types.self_param, + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { tcx.type_of(item.container_id(tcx)).instantiate_identity() } }; @@ -978,7 +966,7 @@ pub(crate) fn check_associated_item( check_method_receiver(wfcx, hir_sig, item, self_ty) } ty::AssocKind::Type { .. } => { - if let ty::AssocItemContainer::Trait = item.container { + if let ty::AssocContainer::Trait = item.container { check_associated_type_bounds(wfcx, item, span) } if item.defaultness(tcx).has_value() { diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 32b175611ce..0b9a01d6042 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -1,7 +1,6 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. -use std::assert_matches::assert_matches; use std::collections::BTreeMap; use rustc_data_structures::fx::FxHashSet; @@ -40,10 +39,7 @@ pub(super) fn check_trait<'tcx>( checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; checker.check(lang_items.const_param_ty_trait(), |checker| { - visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy) - })?; - checker.check(lang_items.unsized_const_param_ty_trait(), |checker| { - visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy) + visit_implementation_of_const_param_ty(checker) })?; checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; checker @@ -138,12 +134,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } -fn visit_implementation_of_const_param_ty( - checker: &Checker<'_>, - kind: LangItem, -) -> Result<(), ErrorGuaranteed> { - assert_matches!(kind, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy); - +fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let header = checker.impl_header; let impl_did = checker.impl_def_id; @@ -157,7 +148,7 @@ fn visit_implementation_of_const_param_ty( } let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); - match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, kind, cause) { + match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b72e743f95b..02baaec3713 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1140,7 +1140,7 @@ fn recover_infer_ret_ty<'tcx>( // recursive function definition to leak out into the fn sig. let mut recovered_ret_ty = None; if let Some(suggestable_ret_ty) = ret_ty.make_suggestable(tcx, false, None) { - diag.span_suggestion( + diag.span_suggestion_verbose( infer_ret_ty.span, "replace with the correct return type", suggestable_ret_ty, @@ -1152,7 +1152,7 @@ fn recover_infer_ret_ty<'tcx>( tcx.param_env(def_id), ret_ty, ) { - diag.span_suggestion( + diag.span_suggestion_verbose( infer_ret_ty.span, "replace with an appropriate return type", sugg, diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index c3f965d8456..44cc2dec1cb 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -152,7 +152,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { ); continue; }; - let ty::Dynamic(data, _, _) = *ty.kind() else { + let ty::Dynamic(data, _) = *ty.kind() else { tcx.dcx() .span_err(attr.span(), "`rustc_dump_vtable` to type alias of dyn type"); continue; diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 126ffabd448..dd3590f9ac5 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -111,9 +111,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } Some(ImplTraitInTraitData::Impl { fn_def_id }) => { - let assoc_item = tcx.associated_item(def_id); - let trait_assoc_predicates = - tcx.explicit_predicates_of(assoc_item.trait_item_def_id.unwrap()); + let trait_item_def_id = tcx.trait_item_of(def_id).unwrap(); + let trait_assoc_predicates = tcx.explicit_predicates_of(trait_item_def_id); let impl_assoc_identity_args = ty::GenericArgs::identity_for_item(tcx, def_id); let impl_def_id = tcx.parent(fn_def_id); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 62125c99d80..8cbf17162e3 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -125,8 +125,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ Some(ty::ImplTraitInTraitData::Impl { fn_def_id }) => { match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) { Ok(map) => { - let assoc_item = tcx.associated_item(def_id); - return map[&assoc_item.trait_item_def_id.unwrap()]; + let trait_item_def_id = tcx.trait_item_of(def_id).unwrap(); + return map[&trait_item_def_id]; } Err(_) => { return ty::EarlyBinder::bind(Ty::new_error_with_message( @@ -198,7 +198,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ } } ImplItemKind::Type(ty) => { - if tcx.impl_trait_ref(tcx.hir_get_parent_item(hir_id)).is_none() { + if let ImplItemImplKind::Inherent { .. } = item.impl_kind { check_feature_inherent_assoc_ty(tcx, item.span); } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index b6d898886ac..a02990fe4ab 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -177,7 +177,7 @@ impl<'tcx> TaitConstraintLocator<'tcx> { let tables = tcx.typeck(item_def_id); if let Some(guar) = tables.tainted_by_errors { self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)); - } else if let Some(&hidden_type) = tables.concrete_opaque_types.get(&self.def_id) { + } else if let Some(&hidden_type) = tables.hidden_types.get(&self.def_id) { self.insert_found(hidden_type); } else { self.non_defining_use_in_defining_scope(item_def_id); @@ -185,8 +185,8 @@ impl<'tcx> TaitConstraintLocator<'tcx> { } DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(item_def_id) { Err(guar) => self.insert_found(ty::OpaqueHiddenType::new_error(tcx, guar)), - Ok(concrete_opaque_types) => { - if let Some(&hidden_type) = concrete_opaque_types.0.get(&self.def_id) { + Ok(hidden_types) => { + if let Some(&hidden_type) = hidden_types.0.get(&self.def_id) { debug!(?hidden_type, "found constraint"); self.insert_found(hidden_type); } else if let Err(guar) = tcx @@ -247,7 +247,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( let tables = tcx.typeck(owner_def_id); if let Some(guar) = tables.tainted_by_errors { Ty::new_error(tcx, guar) - } else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) { + } else if let Some(hidden_ty) = tables.hidden_types.get(&def_id) { hidden_ty.ty } else { assert!(!tcx.next_trait_solver_globally()); @@ -261,8 +261,8 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>( } } DefiningScopeKind::MirBorrowck => match tcx.mir_borrowck(owner_def_id) { - Ok(concrete_opaque_types) => { - if let Some(hidden_ty) = concrete_opaque_types.0.get(&def_id) { + Ok(hidden_types) => { + if let Some(hidden_ty) = hidden_types.0.get(&def_id) { hidden_ty.ty } else { let hir_ty = tcx.type_of_opaque_hir_typeck(def_id).instantiate_identity(); diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 366b3943a05..2a633810cd7 100644 --- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeV use rustc_span::Span; use tracing::debug; -#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub(crate) struct Parameter(pub u32); impl From<ty::ParamTy> for Parameter { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5088c63702e..0a41659ec66 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -85,6 +85,12 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; }; + // An `extern "cmse-nonsecure-entry"` function cannot be c-variadic. We run + // into https://github.com/rust-lang/rust/issues/132142 if we don't explicitly bail. + if decl.c_variadic { + return; + } + match is_valid_cmse_inputs(tcx, fn_sig) { Ok(Ok(())) => {} Ok(Err(index)) => { @@ -128,11 +134,12 @@ fn is_valid_cmse_inputs<'tcx>( // this type is only used for layout computation, which does not rely on regions let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + let fn_sig = tcx.erase_and_anonymize_regions(fn_sig); for (index, ty) in fn_sig.inputs().iter().enumerate() { let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?; - let align = layout.layout.align().abi.bytes(); + let align = layout.layout.align().bytes(); let size = layout.layout.size().bytes(); accum += size; @@ -213,6 +220,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError } Unknown(..) | SizeOverflow(..) + | InvalidSimd { .. } | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index 76bb59e3f09..c248cd7fec2 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -1,16 +1,22 @@ +use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; -use rustc_errors::struct_span_code_err; +use rustc_errors::{ + Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err, +}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; +use rustc_hir::def_id::DefId; +use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS}; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ - self, BottomUpFolder, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, + self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; +use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::traits; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -28,11 +34,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_id: hir::HirId, hir_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, - representation: DynKind, + syntax: TraitObjectSyntax, ) -> Ty<'tcx> { let tcx = self.tcx(); let dummy_self = tcx.types.trait_object_dummy_self; + match syntax { + TraitObjectSyntax::Dyn => {} + TraitObjectSyntax::None => { + match self.prohibit_or_lint_bare_trait_object_ty(span, hir_id, hir_bounds) { + // Don't continue with type analysis if the `dyn` keyword is missing. + // It generates confusing errors, especially if the user meant to use + // another keyword like `impl`. + Some(guar) => return Ty::new_error(tcx, guar), + None => {} + } + } + } + let mut user_written_bounds = Vec::new(); let mut potential_assoc_types = Vec::new(); for poly_trait_ref in hir_bounds.iter() { @@ -47,10 +66,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - let ast_bounds: Vec<_> = - hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect(); - - self.add_default_traits(&mut user_written_bounds, dummy_self, &ast_bounds, None, span); + self.add_default_traits( + &mut user_written_bounds, + dummy_self, + &hir_bounds + .iter() + .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) + .collect::<Vec<_>>(), + None, + span, + ); let (elaborated_trait_bounds, elaborated_projection_bounds) = traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); @@ -431,7 +456,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; debug!(?region_bound); - Ty::new_dynamic(tcx, existential_predicates, region_bound, representation) + Ty::new_dynamic(tcx, existential_predicates, region_bound) } /// Check that elaborating the principal of a trait ref doesn't lead to projections @@ -483,6 +508,521 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ); } + + /// Prohibit or lint against *bare* trait object types depending on the edition. + /// + /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. + /// In edition 2021 and onward we emit a hard error for them. + fn prohibit_or_lint_bare_trait_object_ty( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + ) -> Option<ErrorGuaranteed> { + let tcx = self.tcx(); + let [poly_trait_ref, ..] = hir_bounds else { return None }; + + let in_path = match tcx.parent_hir_node(hir_id) { + hir::Node::Ty(hir::Ty { + kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) if qself.hir_id == hir_id => true, + _ => false, + }; + let needs_bracket = in_path + && !tcx + .sess + .source_map() + .span_to_prev_source(span) + .ok() + .is_some_and(|s| s.trim_end().ends_with('<')); + + let is_global = poly_trait_ref.trait_ref.path.is_global(); + + let mut sugg = vec![( + span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, + ), + )]; + + if is_global || needs_bracket { + sugg.push(( + span.shrink_to_hi(), + format!( + "{}{}", + if is_global { ")" } else { "" }, + if needs_bracket { ">" } else { "" }, + ), + )); + } + + if span.edition().at_least_rust_2021() { + let mut diag = rustc_errors::struct_span_code_err!( + self.dcx(), + span, + E0782, + "{}", + "expected a type, found a trait" + ); + if span.can_be_used_for_suggestions() + && poly_trait_ref.trait_ref.trait_def_id().is_some() + && !self.maybe_suggest_impl_trait(span, hir_id, hir_bounds, &mut diag) + && !self.maybe_suggest_dyn_trait(hir_id, sugg, &mut diag) + { + self.maybe_suggest_add_generic_impl_trait(span, hir_id, &mut diag); + } + // Check if the impl trait that we are considering is an impl of a local trait. + self.maybe_suggest_blanket_trait_impl(span, hir_id, &mut diag); + self.maybe_suggest_assoc_ty_bound(hir_id, &mut diag); + self.maybe_suggest_typoed_method( + hir_id, + poly_trait_ref.trait_ref.trait_def_id(), + &mut diag, + ); + // In case there is an associated type with the same name + // Add the suggestion to this error + if let Some(mut sugg) = + self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) + && let Suggestions::Enabled(ref mut s1) = diag.suggestions + && let Suggestions::Enabled(ref mut s2) = sugg.suggestions + { + s1.append(s2); + sugg.cancel(); + } + Some(diag.emit()) + } else { + tcx.node_span_lint(BARE_TRAIT_OBJECTS, hir_id, span, |lint| { + lint.primary_message("trait objects without an explicit `dyn` are deprecated"); + if span.can_be_used_for_suggestions() { + lint.multipart_suggestion_verbose( + "if this is a dyn-compatible trait, use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + } + self.maybe_suggest_blanket_trait_impl(span, hir_id, lint); + }); + None + } + } + + /// For a struct or enum with an invalid bare trait object field, suggest turning + /// it into a generic type bound. + fn maybe_suggest_add_generic_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + let parent_hir_id = tcx.parent_hir_id(hir_id); + let parent_item = tcx.hir_get_parent_item(hir_id).def_id; + + let generics = match tcx.hir_node_by_def_id(parent_item) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, generics, variant), + .. + }) => { + if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { + return false; + } + generics + } + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { + if !def + .variants + .iter() + .flat_map(|variant| variant.data.fields().iter()) + .any(|field| field.hir_id == parent_hir_id) + { + return false; + } + generics + } + _ => return false, + }; + + let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + + let param = "TUV" + .chars() + .map(|c| c.to_string()) + .chain((0..).map(|i| format!("P{i}"))) + .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) + .expect("we definitely can find at least one param name to generate"); + let mut sugg = vec![(span, param.to_string())]; + if let Some(insertion_span) = generics.span_for_param_suggestion() { + sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); + } else { + sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); + } + diag.multipart_suggestion_verbose( + "you might be missing a type parameter", + sugg, + Applicability::MachineApplicable, + ); + true + } + + /// Make sure that we are in the condition to suggest the blanket implementation. + fn maybe_suggest_blanket_trait_impl<G: EmissionGuarantee>( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_, G>, + ) { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), + .. + }) = tcx.hir_node_by_def_id(parent_id) + && hir_id == impl_self_ty.hir_id + { + let Some(of_trait) = of_trait else { + diag.span_suggestion_verbose( + impl_self_ty.span.shrink_to_hi(), + "you might have intended to implement this trait for a given type", + format!(" for /* Type */"), + Applicability::HasPlaceholders, + ); + return; + }; + if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { + return; + } + let of_trait_span = of_trait.trait_ref.path.span; + // make sure that we are not calling unwrap to abort during the compilation + let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { + return; + }; + + let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(span) else { + return; + }; + let sugg = self.add_generic_param_suggestion(generics, span, &impl_trait_name); + diag.multipart_suggestion( + format!( + "alternatively use a blanket implementation to implement `{of_trait_name}` for \ + all types that also implement `{impl_trait_name}`" + ), + sugg, + Applicability::MaybeIncorrect, + ); + } + } + + /// Try our best to approximate when adding `dyn` would be helpful for a bare + /// trait object. + /// + /// Right now, this is if the type is either directly nested in another ty, + /// or if it's in the tail field within a struct. This approximates what the + /// user would've gotten on edition 2015, except for the case where we have + /// an *obvious* knock-on `Sized` error. + fn maybe_suggest_dyn_trait( + &self, + hir_id: hir::HirId, + sugg: Vec<(Span, String)>, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + // Look at the direct HIR parent, since we care about the relationship between + // the type and the thing that directly encloses it. + match tcx.parent_hir_node(hir_id) { + // These are all generally ok. Namely, when a trait object is nested + // into another expression or ty, it's either very certain that they + // missed the ty (e.g. `&Trait`) or it's not really possible to tell + // what their intention is, so let's not give confusing suggestions and + // just mention `dyn`. The user can make up their mind what to do here. + hir::Node::Ty(_) + | hir::Node::Expr(_) + | hir::Node::PatExpr(_) + | hir::Node::PathSegment(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Item(_) + | hir::Node::WherePredicate(_) => {} + + hir::Node::Field(field) => { + // Enums can't have unsized fields, fields can only have an unsized tail field. + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, _, variant), .. + }) = tcx.parent_hir_node(field.hir_id) + && variant + .fields() + .last() + .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) + { + // Ok + } else { + return false; + } + } + _ => return false, + } + + // FIXME: Only emit this suggestion if the trait is dyn-compatible. + diag.multipart_suggestion_verbose( + "you can add the `dyn` keyword if you want a trait object", + sugg, + Applicability::MachineApplicable, + ); + true + } + + fn add_generic_param_suggestion( + &self, + generics: &hir::Generics<'_>, + self_ty_span: Span, + impl_trait_name: &str, + ) -> Vec<(Span, String)> { + // check if the trait has generics, to make a correct suggestion + let param_name = generics.params.next_type_param_name(None); + + let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { + (span, format!(", {param_name}: {impl_trait_name}")) + } else { + (generics.span, format!("<{param_name}: {impl_trait_name}>")) + }; + vec![(self_ty_span, param_name), add_generic_sugg] + } + + /// Make sure that we are in the condition to suggest `impl Trait`. + fn maybe_suggest_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>` + // and suggest `Trait0<Ty = impl Trait1>`. + // Functions are found in three different contexts. + // 1. Independent functions + // 2. Functions inside trait blocks + // 3. Functions inside impl blocks + let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, generics, .. }, .. + }) => (sig, generics), + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + _ => return false, + }; + let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + let impl_sugg = vec![(span.shrink_to_lo(), "impl ".to_string())]; + // Check if trait object is safe for suggesting dynamic dispatch. + let is_dyn_compatible = hir_bounds.iter().all(|bound| match bound.trait_ref.path.res { + Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), + _ => false, + }); + + let borrowed = matches!( + tcx.parent_hir_node(hir_id), + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) + ); + + // Suggestions for function return type. + if let hir::FnRetTy::Return(ty) = sig.decl.output + && ty.peel_refs().hir_id == hir_id + { + let pre = if !is_dyn_compatible { + format!("`{trait_name}` is dyn-incompatible, ") + } else { + String::new() + }; + let msg = format!( + "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ + single underlying type", + ); + + diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); + + // Suggest `Box<dyn Trait>` for return type + if is_dyn_compatible { + // If the return type is `&Trait`, we don't want + // the ampersand to be displayed in the `Box<dyn Trait>` + // suggestion. + let suggestion = if borrowed { + vec![(ty.span, format!("Box<dyn {trait_name}>"))] + } else { + vec![ + (ty.span.shrink_to_lo(), "Box<dyn ".to_string()), + (ty.span.shrink_to_hi(), ">".to_string()), + ] + }; + + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + suggestion, + Applicability::MachineApplicable, + ); + } + return true; + } + + // Suggestions for function parameters. + for ty in sig.decl.inputs { + if ty.peel_refs().hir_id != hir_id { + continue; + } + let sugg = self.add_generic_param_suggestion(generics, span, &trait_name); + diag.multipart_suggestion_verbose( + format!("use a new generic type parameter, constrained by `{trait_name}`"), + sugg, + Applicability::MachineApplicable, + ); + diag.multipart_suggestion_verbose( + "you can also use an opaque type, but users won't be able to specify the type \ + parameter when calling the `fn`, having to rely exclusively on type inference", + impl_sugg, + Applicability::MachineApplicable, + ); + if !is_dyn_compatible { + diag.note(format!( + "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" + )); + } else { + // No ampersand in suggestion if it's borrowed already + let (dyn_str, paren_dyn_str) = + if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; + + let sugg = if let [_, _, ..] = hir_bounds { + // There is more than one trait bound, we need surrounding parentheses. + vec![ + (span.shrink_to_lo(), paren_dyn_str.to_string()), + (span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(span.shrink_to_lo(), dyn_str.to_string())] + }; + diag.multipart_suggestion_verbose( + format!( + "alternatively, use a trait object to accept any type that implements \ + `{trait_name}`, accessing its methods at runtime using dynamic dispatch", + ), + sugg, + Applicability::MachineApplicable, + ); + } + return true; + } + false + } + + fn maybe_suggest_assoc_ty_bound(&self, hir_id: hir::HirId, diag: &mut Diag<'_>) { + let mut parents = self.tcx().hir_parent_iter(hir_id); + + if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() + && let Some(obj_ty) = constraint.ty() + && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() + { + if let Some((_, hir::Node::Ty(ty))) = parents.next() + && let hir::TyKind::TraitObject(..) = ty.kind + { + // Assoc ty bounds aren't permitted inside trait object types. + return; + } + + if trait_ref + .path + .segments + .iter() + .find_map(|seg| { + seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) + }) + .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) + { + // Only consider angle-bracketed args (where we have a `=` to replace with `:`). + return; + } + + let lo = if constraint.gen_args.span_ext.is_dummy() { + constraint.ident.span + } else { + constraint.gen_args.span_ext + }; + let hi = obj_ty.span; + + if !lo.eq_ctxt(hi) { + return; + } + + diag.span_suggestion_verbose( + lo.between(hi), + "you might have meant to write a bound here", + ": ", + Applicability::MaybeIncorrect, + ); + } + } + + fn maybe_suggest_typoed_method( + &self, + hir_id: hir::HirId, + trait_def_id: Option<DefId>, + diag: &mut Diag<'_>, + ) { + let tcx = self.tcx(); + let Some(trait_def_id) = trait_def_id else { + return; + }; + let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), + .. + }) = tcx.parent_hir_node(hir_id) + else { + return; + }; + if path_ty.hir_id != hir_id { + return; + } + let names: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|assoc| assoc.namespace() == hir::def::Namespace::ValueNS) + .map(|cand| cand.name()) + .collect(); + if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { + diag.span_suggestion_verbose( + segment.ident.span, + format!( + "you may have misspelled this associated item, causing `{}` \ + to be interpreted as a type rather than a trait", + tcx.item_name(trait_def_id), + ), + typo, + Applicability::MaybeIncorrect, + ); + } + } } fn replace_dummy_self_with_error<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 0cf9cb7193f..16505174464 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -859,7 +859,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs deleted file mode 100644 index 56998b5b53c..00000000000 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ /dev/null @@ -1,533 +0,0 @@ -use rustc_ast::TraitObjectSyntax; -use rustc_errors::codes::*; -use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_lint_defs::Applicability; -use rustc_lint_defs::builtin::BARE_TRAIT_OBJECTS; -use rustc_span::Span; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; - -use super::HirTyLowerer; - -impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - /// Prohibit or lint against *bare* trait object types depending on the edition. - /// - /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. - /// In edition 2021 and onward we emit a hard error for them. - pub(super) fn prohibit_or_lint_bare_trait_object_ty( - &self, - self_ty: &hir::Ty<'_>, - ) -> Option<ErrorGuaranteed> { - let tcx = self.tcx(); - - let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = - self_ty.kind - && let TraitObjectSyntax::None = tagged_ptr.tag() - { - poly_trait_ref - } else { - return None; - }; - - let in_path = match tcx.parent_hir_node(self_ty.hir_id) { - hir::Node::Ty(hir::Ty { - kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::PatExpr(hir::PatExpr { - kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) if qself.hir_id == self_ty.hir_id => true, - _ => false, - }; - let needs_bracket = in_path - && !tcx - .sess - .source_map() - .span_to_prev_source(self_ty.span) - .ok() - .is_some_and(|s| s.trim_end().ends_with('<')); - - let is_global = poly_trait_ref.trait_ref.path.is_global(); - - let mut sugg = vec![( - self_ty.span.shrink_to_lo(), - format!( - "{}dyn {}", - if needs_bracket { "<" } else { "" }, - if is_global { "(" } else { "" }, - ), - )]; - - if is_global || needs_bracket { - sugg.push(( - self_ty.span.shrink_to_hi(), - format!( - "{}{}", - if is_global { ")" } else { "" }, - if needs_bracket { ">" } else { "" }, - ), - )); - } - - if self_ty.span.edition().at_least_rust_2021() { - let mut diag = rustc_errors::struct_span_code_err!( - self.dcx(), - self_ty.span, - E0782, - "{}", - "expected a type, found a trait" - ); - if self_ty.span.can_be_used_for_suggestions() - && poly_trait_ref.trait_ref.trait_def_id().is_some() - && !self.maybe_suggest_impl_trait(self_ty, &mut diag) - && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag) - { - self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag); - } - // Check if the impl trait that we are considering is an impl of a local trait. - self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); - self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); - self.maybe_suggest_typoed_method( - self_ty, - poly_trait_ref.trait_ref.trait_def_id(), - &mut diag, - ); - // In case there is an associated type with the same name - // Add the suggestion to this error - if let Some(mut sugg) = - self.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) - && let Suggestions::Enabled(ref mut s1) = diag.suggestions - && let Suggestions::Enabled(ref mut s2) = sugg.suggestions - { - s1.append(s2); - sugg.cancel(); - } - Some(diag.emit()) - } else { - tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { - lint.primary_message("trait objects without an explicit `dyn` are deprecated"); - if self_ty.span.can_be_used_for_suggestions() { - lint.multipart_suggestion_verbose( - "if this is a dyn-compatible trait, use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - } - self.maybe_suggest_blanket_trait_impl(self_ty, lint); - }); - None - } - } - - /// For a struct or enum with an invalid bare trait object field, suggest turning - /// it into a generic type bound. - fn maybe_suggest_add_generic_impl_trait( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id); - let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - - let generics = match tcx.hir_node_by_def_id(parent_item) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, generics, variant), - .. - }) => { - if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { - return false; - } - generics - } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { - if !def - .variants - .iter() - .flat_map(|variant| variant.data.fields().iter()) - .any(|field| field.hir_id == parent_hir_id) - { - return false; - } - generics - } - _ => return false, - }; - - let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - - let param = "TUV" - .chars() - .map(|c| c.to_string()) - .chain((0..).map(|i| format!("P{i}"))) - .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) - .expect("we definitely can find at least one param name to generate"); - let mut sugg = vec![(self_ty.span, param.to_string())]; - if let Some(insertion_span) = generics.span_for_param_suggestion() { - sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); - } else { - sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); - } - diag.multipart_suggestion_verbose( - "you might be missing a type parameter", - sugg, - Applicability::MachineApplicable, - ); - true - } - /// Make sure that we are in the condition to suggest the blanket implementation. - fn maybe_suggest_blanket_trait_impl<G: EmissionGuarantee>( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_, G>, - ) { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), - .. - }) = tcx.hir_node_by_def_id(parent_id) - && self_ty.hir_id == impl_self_ty.hir_id - { - let Some(of_trait) = of_trait else { - diag.span_suggestion_verbose( - impl_self_ty.span.shrink_to_hi(), - "you might have intended to implement this trait for a given type", - format!(" for /* Type */"), - Applicability::HasPlaceholders, - ); - return; - }; - if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { - return; - } - let of_trait_span = of_trait.trait_ref.path.span; - // make sure that we are not calling unwrap to abort during the compilation - let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { - return; - }; - - let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span) - else { - return; - }; - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); - diag.multipart_suggestion( - format!( - "alternatively use a blanket implementation to implement `{of_trait_name}` for \ - all types that also implement `{impl_trait_name}`" - ), - sugg, - Applicability::MaybeIncorrect, - ); - } - } - - /// Try our best to approximate when adding `dyn` would be helpful for a bare - /// trait object. - /// - /// Right now, this is if the type is either directly nested in another ty, - /// or if it's in the tail field within a struct. This approximates what the - /// user would've gotten on edition 2015, except for the case where we have - /// an *obvious* knock-on `Sized` error. - fn maybe_suggest_dyn_trait( - &self, - self_ty: &hir::Ty<'_>, - sugg: Vec<(Span, String)>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - // Look at the direct HIR parent, since we care about the relationship between - // the type and the thing that directly encloses it. - match tcx.parent_hir_node(self_ty.hir_id) { - // These are all generally ok. Namely, when a trait object is nested - // into another expression or ty, it's either very certain that they - // missed the ty (e.g. `&Trait`) or it's not really possible to tell - // what their intention is, so let's not give confusing suggestions and - // just mention `dyn`. The user can make up their mind what to do here. - hir::Node::Ty(_) - | hir::Node::Expr(_) - | hir::Node::PatExpr(_) - | hir::Node::PathSegment(_) - | hir::Node::AssocItemConstraint(_) - | hir::Node::TraitRef(_) - | hir::Node::Item(_) - | hir::Node::WherePredicate(_) => {} - - hir::Node::Field(field) => { - // Enums can't have unsized fields, fields can only have an unsized tail field. - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, _, variant), .. - }) = tcx.parent_hir_node(field.hir_id) - && variant - .fields() - .last() - .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) - { - // Ok - } else { - return false; - } - } - _ => return false, - } - - // FIXME: Only emit this suggestion if the trait is dyn-compatible. - diag.multipart_suggestion_verbose( - "you can add the `dyn` keyword if you want a trait object", - sugg, - Applicability::MachineApplicable, - ); - true - } - - fn add_generic_param_suggestion( - &self, - generics: &hir::Generics<'_>, - self_ty_span: Span, - impl_trait_name: &str, - ) -> Vec<(Span, String)> { - // check if the trait has generics, to make a correct suggestion - let param_name = generics.params.next_type_param_name(None); - - let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { - (span, format!(", {param_name}: {impl_trait_name}")) - } else { - (generics.span, format!("<{param_name}: {impl_trait_name}>")) - }; - vec![(self_ty_span, param_name), add_generic_sugg] - } - - /// Make sure that we are in the condition to suggest `impl Trait`. - fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>` - // and suggest `Trait0<Ty = impl Trait1>`. - // Functions are found in three different contexts. - // 1. Independent functions - // 2. Functions inside trait blocks - // 3. Functions inside impl blocks - let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn { sig, generics, .. }, .. - }) => (sig, generics), - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - _ => return false, - }; - let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; - // Check if trait object is safe for suggesting dynamic dispatch. - let is_dyn_compatible = match self_ty.kind { - hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { - Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), - _ => false, - }) - } - _ => false, - }; - - let borrowed = matches!( - tcx.parent_hir_node(self_ty.hir_id), - hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) - ); - - // Suggestions for function return type. - if let hir::FnRetTy::Return(ty) = sig.decl.output - && ty.peel_refs().hir_id == self_ty.hir_id - { - let pre = if !is_dyn_compatible { - format!("`{trait_name}` is dyn-incompatible, ") - } else { - String::new() - }; - let msg = format!( - "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ - single underlying type", - ); - - diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); - - // Suggest `Box<dyn Trait>` for return type - if is_dyn_compatible { - // If the return type is `&Trait`, we don't want - // the ampersand to be displayed in the `Box<dyn Trait>` - // suggestion. - let suggestion = if borrowed { - vec![(ty.span, format!("Box<dyn {trait_name}>"))] - } else { - vec![ - (ty.span.shrink_to_lo(), "Box<dyn ".to_string()), - (ty.span.shrink_to_hi(), ">".to_string()), - ] - }; - - diag.multipart_suggestion_verbose( - "alternatively, you can return an owned trait object", - suggestion, - Applicability::MachineApplicable, - ); - } - return true; - } - - // Suggestions for function parameters. - for ty in sig.decl.inputs { - if ty.peel_refs().hir_id != self_ty.hir_id { - continue; - } - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); - diag.multipart_suggestion_verbose( - format!("use a new generic type parameter, constrained by `{trait_name}`"), - sugg, - Applicability::MachineApplicable, - ); - diag.multipart_suggestion_verbose( - "you can also use an opaque type, but users won't be able to specify the type \ - parameter when calling the `fn`, having to rely exclusively on type inference", - impl_sugg, - Applicability::MachineApplicable, - ); - if !is_dyn_compatible { - diag.note(format!( - "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" - )); - } else { - // No ampersand in suggestion if it's borrowed already - let (dyn_str, paren_dyn_str) = - if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { - // There is more than one trait bound, we need surrounding parentheses. - vec![ - (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), - (self_ty.span.shrink_to_hi(), ")".to_string()), - ] - } else { - vec![(self_ty.span.shrink_to_lo(), dyn_str.to_string())] - }; - diag.multipart_suggestion_verbose( - format!( - "alternatively, use a trait object to accept any type that implements \ - `{trait_name}`, accessing its methods at runtime using dynamic dispatch", - ), - sugg, - Applicability::MachineApplicable, - ); - } - return true; - } - false - } - - fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) { - let mut parents = self.tcx().hir_parent_iter(self_ty.hir_id); - - if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() - && let Some(obj_ty) = constraint.ty() - && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() - { - if let Some((_, hir::Node::Ty(ty))) = parents.next() - && let hir::TyKind::TraitObject(..) = ty.kind - { - // Assoc ty bounds aren't permitted inside trait object types. - return; - } - - if trait_ref - .path - .segments - .iter() - .find_map(|seg| { - seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) - }) - .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) - { - // Only consider angle-bracketed args (where we have a `=` to replace with `:`). - return; - } - - let lo = if constraint.gen_args.span_ext.is_dummy() { - constraint.ident.span - } else { - constraint.gen_args.span_ext - }; - let hi = obj_ty.span; - - if !lo.eq_ctxt(hi) { - return; - } - - diag.span_suggestion_verbose( - lo.between(hi), - "you might have meant to write a bound here", - ": ", - Applicability::MaybeIncorrect, - ); - } - } - - fn maybe_suggest_typoed_method( - &self, - self_ty: &hir::Ty<'_>, - trait_def_id: Option<DefId>, - diag: &mut Diag<'_>, - ) { - let tcx = self.tcx(); - let Some(trait_def_id) = trait_def_id else { - return; - }; - let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), - .. - }) = tcx.parent_hir_node(self_ty.hir_id) - else { - return; - }; - if path_ty.hir_id != self_ty.hir_id { - return; - } - let names: Vec<_> = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter(|assoc| assoc.namespace() == Namespace::ValueNS) - .map(|cand| cand.name()) - .collect(); - if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { - diag.span_suggestion_verbose( - segment.ident.span, - format!( - "you may have misspelled this associated item, causing `{}` \ - to be interpreted as a type rather than a trait", - tcx.item_name(trait_def_id), - ), - typo, - Applicability::MaybeIncorrect, - ); - } - } -} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index c5e079fe89a..9b198d04454 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -15,15 +15,13 @@ mod bounds; mod cmse; -mod dyn_compatibility; +mod dyn_trait; pub mod errors; pub mod generics; -mod lint; use std::assert_matches::assert_matches; use std::slice; -use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -2428,19 +2426,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ), hir::TyKind::TraitObject(bounds, tagged_ptr) => { let lifetime = tagged_ptr.pointer(); - let repr = tagged_ptr.tag(); - - if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { - // Don't continue with type analysis if the `dyn` keyword is missing - // It generates confusing errors, especially if the user meant to use another - // keyword like `impl` - Ty::new_error(tcx, guar) - } else { - let repr = match repr { - TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, - }; - self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr) - } + let syntax = tagged_ptr.tag(); + self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, syntax) } // If we encounter a fully qualified path with RTN generics, then it must have // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index b38639ed8c6..13c744ab461 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -275,7 +275,7 @@ fn check_duplicate_params<'tcx>( span: Span, ) -> Result<(), ErrorGuaranteed> { let mut base_params = cgp::parameters_for(tcx, parent_args, true); - base_params.sort_by_key(|param| param.0); + base_params.sort_unstable(); if let (_, [duplicate, ..]) = base_params.partition_dedup() { let param = impl1_args[duplicate.0 as usize]; return Err(tcx diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 960ec7f66ab..be841675821 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -281,7 +281,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_args(current, data.def_id, data.args, variance); } - ty::Dynamic(data, r, _) => { + ty::Dynamic(data, r) => { // The type `dyn Trait<T> +'a` is covariant w/r/t `'a`: self.add_constraints_from_region(current, r, variance); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index c6a4d78dcc8..f59fcab4666 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -25,6 +25,7 @@ use tracing::{debug, instrument}; use super::method::MethodCallee; use super::method::probe::ProbeScope; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; +use crate::method::TreatNotYetDefinedOpaques; use crate::{errors, fluent_generated}; /// Checks that it is legal to call methods of the trait corresponding @@ -78,7 +79,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.check_expr(callee_expr), }; - let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty); + let expr_ty = self.try_structurally_resolve_type(call_expr.span, original_callee_ty); let mut autoderef = self.autoderef(callee_expr.span, expr_ty); let mut result = None; @@ -200,7 +201,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], autoderef: &Autoderef<'a, 'tcx>, ) -> Option<CallStep<'tcx>> { - let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty()); + let adjusted_ty = + self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty()); // If the callee is a function pointer or a closure, then we're all set. match *adjusted_ty.kind() { @@ -297,6 +299,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } + ty::Infer(ty::TyVar(vid)) => { + // If we end up with an inference variable which is not the hidden type of + // an opaque, emit an error. + if !self.has_opaques_with_sub_unified_hidden_type(vid) { + self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty); + return None; + } + } + ty::Error(_) => { return None; } @@ -367,26 +378,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span))) }); + // We use `TreatNotYetDefinedOpaques::AsRigid` here so that if the `adjusted_ty` + // is `Box<impl FnOnce()>` we choose `FnOnce` instead of `Fn`. + // + // We try all the different call traits in order and choose the first + // one which may apply. So if we treat opaques as inference variables + // `Box<impl FnOnce()>: Fn` is considered ambiguous and chosen. if let Some(ok) = self.lookup_method_for_operator( self.misc(call_expr.span), method_name, trait_def_id, adjusted_ty, opt_input_type, + TreatNotYetDefinedOpaques::AsRigid, ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { // Check for &self vs &mut self in the method signature. Since this is either // the Fn or FnMut trait, it should be one of those. - let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else { + let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else { bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut") }; // For initial two-phase borrow // deployment, conservatively omit // overloaded function call ops. - let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No); + let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No); autoref = Some(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 27540fd1a43..40b21c45bc5 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)), + ty::Dynamic(tty, _) => Some(PointerKind::VTable(tty)), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { @@ -250,9 +250,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // cases now. We do a more thorough check at the end, once // inference is more completely known. match cast_ty.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { - Err(check.report_cast_to_unsized_type(fcx)) - } + ty::Dynamic(_, _) | ty::Slice(..) => Err(check.report_cast_to_unsized_type(fcx)), _ => Ok(check), } } @@ -900,7 +898,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { &src_tty.without_auto_traits().collect::<Vec<_>>(), ), tcx.lifetimes.re_erased, - ty::Dyn, ); let dst_obj = Ty::new_dynamic( tcx, @@ -908,7 +905,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { &dst_tty.without_auto_traits().collect::<Vec<_>>(), ), tcx.lifetimes.re_erased, - ty::Dyn, ); // `dyn Src = dyn Dst`, this checks for matching traits/generics/projections diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index b99f811db1a..ced2cf2b57b 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -558,7 +558,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index 6d95b6917e2..2fbea5b61cf 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -1,3 +1,4 @@ +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -74,8 +75,14 @@ impl<'a, 'tcx> Expectation<'tcx> { /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { + let span = match ty.kind() { + ty::Adt(adt_def, _) => fcx.tcx.def_span(adt_def.did()), + _ => fcx.tcx.def_span(fcx.body_id), + }; + let cause = ObligationCause::misc(span, fcx.body_id); + // FIXME: This is not right, even in the old solver... - match fcx.tcx.struct_tail_raw(ty, |ty| ty, || {}).kind() { + match fcx.tcx.struct_tail_raw(ty, &cause, |ty| ty, || {}).kind() { ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), _ => ExpectHasType(ty), } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7adbee7ff28..d1ce0afddf9 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1633,8 +1633,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, ) -> Ty<'tcx> { let rcvr_t = self.check_expr(rcvr); - // no need to check for bot/err -- callee does that - let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t); + let rcvr_t = self.try_structurally_resolve_type(rcvr.span, rcvr_t); match self.lookup_method(rcvr_t, segment, segment.ident.span, expr, rcvr, args) { Ok(method) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 0b3d50ff219..833ce433d56 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -424,6 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ty.references_error() { let tail = self.tcx.struct_tail_raw( ty, + &self.misc(span), |ty| { if self.next_trait_solver() { self.try_structurally_resolve_type(span, ty) @@ -1021,7 +1022,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let container_id = assoc_item.container_id(tcx); debug!(?def_id, ?container, ?container_id); match container { - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Trait => { if let Err(e) = callee::check_legal_trait_for_method_call( tcx, path_span, @@ -1033,7 +1034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(e); } } - ty::AssocItemContainer::Impl => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { if segments.len() == 1 { // `<T>::assoc` will end up here, and so // can `T::assoc`. If this came from an @@ -1469,24 +1470,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let ty = self.try_structurally_resolve_type(sp, ty); - if !ty.is_ty_var() { - ty - } else { - let e = self.tainted_by_errors().unwrap_or_else(|| { - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - sp, - ty.into(), - TypeAnnotationNeeded::E0282, - true, - ) - .emit() - }); - let err = Ty::new_error(self.tcx, e); - self.demand_suptype(sp, err, ty); - err - } + if !ty.is_ty_var() { ty } else { self.type_must_be_known_at_this_point(sp, ty) } + } + + #[cold] + pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + let guar = self.tainted_by_errors().unwrap_or_else(|| { + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + sp, + ty.into(), + TypeAnnotationNeeded::E0282, + true, + ) + .emit() + }); + let err = Ty::new_error(self.tcx, guar); + self.demand_suptype(sp, err, ty); + err } pub(crate) fn structurally_resolve_const( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 94b635c41b4..c8943d4634e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,3 +1,4 @@ +use std::ops::Deref; use std::{fmt, iter, mem}; use itertools::Itertools; @@ -7,7 +8,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, lis use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants}; use rustc_index::IndexVec; @@ -565,358 +566,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments: TupleArgumentsFlag, ) -> ErrorGuaranteed { // Next, let's construct the error - let (error_span, call_ident, full_call_span, call_name, is_method) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, - _, - ) => { - if let Res::Def(DefKind::Ctor(of, _), _) = - self.typeck_results.borrow().qpath_res(qpath, *hir_id) - { - let name = match of { - CtorOf::Struct => "struct", - CtorOf::Variant => "enum variant", - }; - (call_span, None, *span, name, false) - } else { - (call_span, None, *span, "function", false) - } - } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => { - (call_span, None, *span, "function", false) - } - hir::ExprKind::MethodCall(path_segment, _, _, span) => { - let ident_span = path_segment.ident.span; - let ident_span = if let Some(args) = path_segment.args { - ident_span.with_hi(args.span_ext.hi()) - } else { - ident_span - }; - (*span, Some(path_segment.ident), ident_span, "method", true) - } - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - - // Don't print if it has error types or is just plain `_` - fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { - tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) - } - - let tcx = self.tcx; - - // Get the argument span in the context of the call span so that - // suggestions and labels are (more) correct when an arg is a - // macro invocation. - let normalize_span = |span: Span| -> Span { - let normalized_span = span.find_ancestor_inside_same_ctxt(error_span).unwrap_or(span); - // Sometimes macros mess up the spans, so do not normalize the - // arg span to equal the error span, because that's less useful - // than pointing out the arg expr in the wrong context. - if normalized_span.source_equal(error_span) { span } else { normalized_span } - }; - - // Precompute the provided types and spans, since that's all we typically need for below - let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args - .iter() - .map(|expr| { - let ty = self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(*expr) - .unwrap_or_else(|| Ty::new_misc_error(tcx)); - (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) - }) - .collect(); - let callee_expr = match &call_expr.peel_blocks().kind { - hir::ExprKind::Call(callee, _) => Some(*callee), - hir::ExprKind::MethodCall(_, receiver, ..) => { - if let Some((DefKind::AssocFn, def_id)) = - self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) - && let Some(assoc) = tcx.opt_associated_item(def_id) - && assoc.is_method() - { - Some(*receiver) - } else { - None - } - } - _ => None, - }; - let callee_ty = callee_expr - .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); - - // Obtain another method on `Self` that have similar name. - let similar_assoc = |call_name: Ident| -> Option<(ty::AssocItem, ty::FnSig<'_>)> { - if let Some(callee_ty) = callee_ty - && let Ok(Some(assoc)) = self.probe_op( - call_name.span, - MethodCall, - Some(call_name), - None, - IsSuggestion(true), - callee_ty.peel_refs(), - callee_expr.unwrap().hir_id, - TraitsInScope, - |mut ctxt| ctxt.probe_for_similar_candidate(), - ) - && assoc.is_method() - { - let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id); - let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args); - - self.instantiate_binder_with_fresh_vars( - call_name.span, - BoundRegionConversionTime::FnCall, - fn_sig, - ); - } - None - }; - - let suggest_confusable = |err: &mut Diag<'_>| { - let Some(call_name) = call_ident else { - return; - }; - let Some(callee_ty) = callee_ty else { - return; - }; - let input_types: Vec<Ty<'_>> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); - // Check for other methods in the following order - // - methods marked as `rustc_confusables` with the provided arguments - // - methods with the same argument type/count and short levenshtein distance - // - methods marked as `rustc_confusables` (done) - // - methods with short levenshtein distance - - // Look for commonly confusable method names considering arguments. - if let Some(_name) = self.confusable_method_name( - err, - callee_ty.peel_refs(), - call_name, - Some(input_types.clone()), - ) { - return; - } - // Look for method names with short levenshtein distance, considering arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..] - .iter() - .zip(input_types.iter()) - .all(|(expected, found)| self.may_coerce(*expected, *found)) - && fn_sig.inputs()[1..].len() == input_types.len() - { - let assoc_name = assoc.name(); - err.span_suggestion_verbose( - call_name.span, - format!("you might have meant to use `{}`", assoc_name), - assoc_name, - Applicability::MaybeIncorrect, - ); - return; - } - // Look for commonly confusable method names disregarding arguments. - if let Some(_name) = - self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) - { - return; - } - // Look for similarly named methods with levenshtein distance with the right - // number of arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..].len() == input_types.len() - { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but the arguments don't match", - assoc.name(), - ), - ); - return; - } - // Fallthrough: look for similarly named methods with levenshtein distance. - if let Some((assoc, _)) = similar_assoc(call_name) { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but their argument count \ - doesn't match", - assoc.name(), - ), - ); - return; - } - }; - // A "softer" version of the `demand_compatible`, which checks types without persisting them, - // and treats error types differently - // This will allow us to "probe" for other argument orders that would likely have been correct - let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| { - if provided_idx.as_usize() == expected_idx.as_usize() { - return compatibility_diagonal[provided_idx].clone(); - } - - let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx]; - // If either is an error type, we defy the usual convention and consider them to *not* be - // coercible. This prevents our error message heuristic from trying to pass errors into - // every argument. - if (formal_input_ty, expected_input_ty).references_error() { - return Compatibility::Incompatible(None); - } - let (arg_ty, arg_span) = provided_arg_tys[provided_idx]; - - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - let can_coerce = self.may_coerce(arg_ty, coerced_ty); - if !can_coerce { - return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( - ty::error::ExpectedFound::new(coerced_ty, arg_ty), - ))); - } - - // Using probe here, since we don't want this subtyping to affect inference. - let subtyping_error = self.probe(|_| { - self.at(&self.misc(arg_span), self.param_env) - .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) - .err() - }); - - // Same as above: if either the coerce type or the checked type is an error type, - // consider them *not* compatible. - let references_error = (coerced_ty, arg_ty).references_error(); - match (references_error, subtyping_error) { - (false, None) => Compatibility::Compatible, - (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), - } - }; - - let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { - let mismatched_ty = if expected_ty == provided_ty { - // If expected == provided, then we must have failed to sup - // the formal type. Avoid printing out "expected Ty, found Ty" - // in that case. - formal_ty - } else { - expected_ty - }; - TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty) - }; - - // The algorithm here is inspired by levenshtein distance and longest common subsequence. - // We'll try to detect 4 different types of mistakes: - // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs - // - An input is missing, which isn't satisfied by *any* of the other arguments - // - Some number of arguments have been provided in the wrong order - // - A type is straight up invalid - - // First, let's find the errors - let (mut errors, matched_inputs) = - ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible) - .find_errors(); + let mut fn_call_diag_ctxt = FnCallDiagCtxt::new( + self, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); // First, check if we just need to wrap some arguments in a tuple. - if let Some((mismatch_idx, terr)) = - compatibility_diagonal.iter_enumerated().find_map(|(i, c)| { - if let Compatibility::Incompatible(Some(terr)) = c { - Some((i, *terr)) - } else { - None - } - }) - { - // Is the first bad expected argument a tuple? - // Do we have as many extra provided arguments as the tuple's length? - // If so, we might have just forgotten to wrap some args in a tuple. - if let Some(ty::Tuple(tys)) = - formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind()) - // If the tuple is unit, we're not actually wrapping any arguments. - && !tys.is_empty() - && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() - { - // Wrap up the N provided arguments starting at this position in a tuple. - let provided_args_to_tuple = &provided_arg_tys[mismatch_idx..]; - let (provided_args_to_tuple, provided_args_after_tuple) = - provided_args_to_tuple.split_at(tys.len()); - let provided_as_tuple = - Ty::new_tup_from_iter(tcx, provided_args_to_tuple.iter().map(|&(ty, _)| ty)); - - let mut satisfied = true; - // Check if the newly wrapped tuple + rest of the arguments are compatible. - for ((_, expected_ty), provided_ty) in std::iter::zip( - formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(), - [provided_as_tuple] - .into_iter() - .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)), - ) { - if !self.may_coerce(provided_ty, *expected_ty) { - satisfied = false; - break; - } - } + if let Some(err) = fn_call_diag_ctxt.check_wrap_args_in_tuple() { + return err; + } - // If they're compatible, suggest wrapping in an arg, and we're done! - // Take some care with spans, so we don't suggest wrapping a macro's - // innards in parenthesis, for example. - if satisfied - && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple - { - let mut err; - if tys.len() == 1 { - // A tuple wrap suggestion actually occurs within, - // so don't do anything special here. - err = self.err_ctxt().report_and_explain_type_error( - mk_trace( - lo, - formal_and_expected_inputs[mismatch_idx.to_expected_idx()], - provided_arg_tys[mismatch_idx].0, - ), - self.param_env, - terr, - ); - err.span_label( - full_call_span, - format!("arguments to this {call_name} are incorrect"), - ); - } else { - err = self.dcx().struct_span_err( - full_call_span, - format!( - "{call_name} takes {}{} but {} {} supplied", - if c_variadic { "at least " } else { "" }, - potentially_plural_count( - formal_and_expected_inputs.len(), - "argument" - ), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - ); - err.code(err_code.to_owned()); - err.multipart_suggestion_verbose( - "wrap these arguments in parentheses to construct a tuple", - vec![ - (lo.shrink_to_lo(), "(".to_string()), - (hi.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - }; - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - call_expr, - None, - Some(mismatch_idx.as_usize()), - &matched_inputs, - &formal_and_expected_inputs, - is_method, - tuple_arguments, - ); - suggest_confusable(&mut err); - return err.emit(); - } - } + if let Some(fallback_error) = fn_call_diag_ctxt.ensure_has_errors() { + return fallback_error; } // Okay, so here's where it gets complicated in regards to what errors @@ -926,608 +596,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 2) Valid but incorrect arguments // 3) Invalid arguments // - Currently I think this only comes up with `CyclicTy` - // + // We first need to go through, remove those from (3) and emit those // as their own error, particularly since they're error code and // message is special. From what I can tell, we *must* emit these // here (vs somewhere prior to this function) since the arguments // become invalid *because* of how they get used in the function. // It is what it is. - - if errors.is_empty() { - if cfg!(debug_assertions) { - span_bug!(error_span, "expected errors from argument matrix"); - } else { - let mut err = - self.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span }); - suggest_confusable(&mut err); - return err.emit(); - } + if let Some(err) = fn_call_diag_ctxt.filter_out_invalid_arguments() + && fn_call_diag_ctxt.errors.is_empty() + { + // We're done if we found errors, but we already emitted them. + return err; } - let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { - if let ty::Adt(adt, _) = ty.kind() - && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) - && let hir::ExprKind::Struct( - hir::QPath::LangItem(hir::LangItem::RangeFull, _), - [], - _, - ) = expr.kind - { - // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax - // from default field values, which is not supported on tuples. - let explanation = if self.tcx.features().default_field_values() { - "this is only supported on non-tuple struct literals" - } else if self.tcx.sess.is_nightly_build() { - "this is only supported on non-tuple struct literals when \ - `#![feature(default_field_values)]` is enabled" - } else { - "this is not supported" - }; - let msg = format!( - "you might have meant to use `..` to skip providing a value for \ - expected fields, but {explanation}; it is instead interpreted as a \ - `std::ops::RangeFull` literal", - ); - err.span_help(expr.span, msg); - } - }; + assert!(!fn_call_diag_ctxt.errors.is_empty()); - let mut reported = None; - errors.retain(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = - error - else { - return true; - }; - let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let trace = - mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); - if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { - let mut err = - self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *e); - suggest_confusable(&mut err); - reported = Some(err.emit()); - return false; - } - true - }); - - // We're done if we found errors, but we already emitted them. - if let Some(reported) = reported - && errors.is_empty() - { - return reported; + // Last special case: if there is only one "Incompatible" error, just emit that + if let Some(err) = fn_call_diag_ctxt.check_single_incompatible() { + return err; } - assert!(!errors.is_empty()); // Okay, now that we've emitted the special errors separately, we // are only left missing/extra/swapped and mismatched arguments, both // can be collated pretty easily if needed. - // Next special case: if there is only one "Incompatible" error, just emit that - if let &[ - Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), - ] = &errors[..] - { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_arg_span) = provided_arg_tys[provided_idx]; - let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); - let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); - self.emit_coerce_suggestions( - &mut err, - provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect")); - - self.label_generic_mismatches( - &mut err, - fn_def_id, - &matched_inputs, - &provided_arg_tys, - &formal_and_expected_inputs, - is_method, - ); - - if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind - && provided_idx.as_usize() == expected_idx.as_usize() - { - self.note_source_of_type_mismatch_constraint( - &mut err, - rcvr, - crate::demand::TypeMismatchSource::Arg { - call_expr, - incompatible_arg: provided_idx.as_usize(), - }, - ); - } - - self.suggest_ptr_null_mut( - expected_ty, - provided_ty, - provided_args[provided_idx], - &mut err, - ); - - self.suggest_deref_unwrap_or( - &mut err, - callee_ty, - call_ident, - expected_ty, - provided_ty, - provided_args[provided_idx], - is_method, - ); - - // Call out where the function is defined - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - call_expr, - Some(expected_ty), - Some(expected_idx.as_usize()), - &matched_inputs, - &formal_and_expected_inputs, - is_method, - tuple_arguments, - ); - suggest_confusable(&mut err); - detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); - return err.emit(); - } - // Special case, we found an extra argument is provided, which is very common in practice. // but there is a obviously better removing suggestion compared to the current one, // try to find the argument with Error type, if we removed it all the types will become good, // then we will replace the current suggestion. - if let [Error::Extra(provided_idx)] = &errors[..] { - let remove_idx_is_perfect = |idx: usize| -> bool { - let removed_arg_tys = provided_arg_tys - .iter() - .enumerate() - .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) }) - .collect::<IndexVec<ProvidedIdx, _>>(); - std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all( - |((expected_ty, _), (provided_ty, _))| { - !provided_ty.references_error() - && self.may_coerce(*provided_ty, *expected_ty) - }, - ) - }; + fn_call_diag_ctxt.maybe_optimize_extra_arg_suggestion(); - if !remove_idx_is_perfect(provided_idx.as_usize()) { - if let Some(i) = (0..provided_args.len()).find(|&i| remove_idx_is_perfect(i)) { - errors = vec![Error::Extra(ProvidedIdx::from_usize(i))]; - } - } - } + let mut err = fn_call_diag_ctxt.initial_final_diagnostic(); + fn_call_diag_ctxt.suggest_confusable(&mut err); - let mut err = if formal_and_expected_inputs.len() == provided_args.len() { - struct_span_code_err!( - self.dcx(), - full_call_span, - E0308, - "arguments to this {} are incorrect", - call_name, - ) - } else { - self.dcx() - .struct_span_err( - full_call_span, - format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(formal_and_expected_inputs.len(), "argument"), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - ) - .with_code(err_code.to_owned()) - }; + // As we encounter issues, keep track of what we want to provide for the suggestion. - suggest_confusable(&mut err); - // As we encounter issues, keep track of what we want to provide for the suggestion - let mut labels = vec![]; - // If there is a single error, we give a specific suggestion; otherwise, we change to - // "did you mean" with the suggested function call - enum SuggestionText { - None, - Provide(bool), - Remove(bool), - Swap, - Reorder, - DidYouMean, - } - let mut suggestion_text = SuggestionText::None; - - let ty_to_snippet = |ty: Ty<'tcx>, expected_idx: ExpectedIdx| { - if ty.is_unit() { - "()".to_string() - } else if ty.is_suggestable(tcx, false) { - format!("/* {ty} */") - } else if let Some(fn_def_id) = fn_def_id - && self.tcx.def_kind(fn_def_id).is_fn_like() - && let self_implicit = - matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize - && let Some(Some(arg)) = - self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) - && arg.name != kw::SelfLower - { - format!("/* {} */", arg.name) - } else { - "/* value */".to_string() - } - }; - - let mut errors = errors.into_iter().peekable(); - let mut only_extras_so_far = errors - .peek() - .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); - let mut prev_extra_idx = None; - let mut suggestions = vec![]; - while let Some(error) = errors.next() { - only_extras_so_far &= matches!(error, Error::Extra(_)); - - match error { - Error::Invalid(provided_idx, expected_idx, compatibility) => { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; - if let Compatibility::Incompatible(error) = compatibility { - let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); - if let Some(e) = error { - self.err_ctxt().note_type_err( - &mut err, - &trace.cause, - None, - Some(self.param_env.and(trace.values)), - e, - true, - None, - ); - } - } + let (mut suggestions, labels, suggestion_text) = + fn_call_diag_ctxt.labels_and_suggestion_text(&mut err); - self.emit_coerce_suggestions( - &mut err, - provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); - } - Error::Extra(arg_idx) => { - let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - // FIXME: not suggestable, use something else - format!(" of type `{provided_ty}`") - } else { - "".to_string() - }; - let idx = if provided_arg_tys.len() == 1 { - "".to_string() - } else { - format!(" #{}", arg_idx.as_usize() + 1) - }; - labels.push(( - provided_span, - format!("unexpected argument{idx}{provided_ty_name}"), - )); - let mut span = provided_span; - if span.can_be_used_for_suggestions() - && error_span.can_be_used_for_suggestions() - { - if arg_idx.index() > 0 - && let Some((_, prev)) = - provided_arg_tys.get(ProvidedIdx::from_usize(arg_idx.index() - 1)) - { - // Include previous comma - span = prev.shrink_to_hi().to(span); - } - - // Is last argument for deletion in a row starting from the 0-th argument? - // Then delete the next comma, so we are not left with `f(, ...)` - // - // fn f() {} - // - f(0, 1,) - // + f() - let trim_next_comma = match errors.peek() { - Some(Error::Extra(provided_idx)) - if only_extras_so_far - && provided_idx.index() > arg_idx.index() + 1 => - // If the next Error::Extra ("next") doesn't next to current ("current"), - // fn foo(_: (), _: u32) {} - // - foo("current", (), 1u32, "next") - // + foo((), 1u32) - // If the previous error is not a `Error::Extra`, then do not trim the next comma - // - foo((), "current", 42u32, "next") - // + foo((), 42u32) - { - prev_extra_idx.is_none_or(|prev_extra_idx| { - prev_extra_idx + 1 == arg_idx.index() - }) - } - // If no error left, we need to delete the next comma - None if only_extras_so_far => true, - // Not sure if other error type need to be handled as well - _ => false, - }; - - if trim_next_comma { - let next = provided_arg_tys - .get(arg_idx + 1) - .map(|&(_, sp)| sp) - .unwrap_or_else(|| { - // Try to move before `)`. Note that `)` here is not necessarily - // the latin right paren, it could be a Unicode-confusable that - // looks like a `)`, so we must not use `- BytePos(1)` - // manipulations here. - self.tcx().sess.source_map().end_point(call_expr.span) - }); - - // Include next comma - span = span.until(next); - } - - suggestions.push((span, String::new())); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Remove(false), - SuggestionText::Remove(_) => SuggestionText::Remove(true), - _ => SuggestionText::DidYouMean, - }; - prev_extra_idx = Some(arg_idx.index()) - } - detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]); - } - Error::Missing(expected_idx) => { - // If there are multiple missing arguments adjacent to each other, - // then we can provide a single error. - - let mut missing_idxs = vec![expected_idx]; - while let Some(e) = errors.next_if(|e| { - matches!(e, Error::Missing(next_expected_idx) - if *next_expected_idx == *missing_idxs.last().unwrap() + 1) - }) { - match e { - Error::Missing(expected_idx) => missing_idxs.push(expected_idx), - _ => unreachable!( - "control flow ensures that we should always get an `Error::Missing`" - ), - } - } - - // NOTE: Because we might be re-arranging arguments, might have extra - // arguments, etc. it's hard to *really* know where we should provide - // this error label, so as a heuristic, we point to the provided arg, or - // to the call if the missing inputs pass the provided args. - match &missing_idxs[..] { - &[expected_idx] => { - let (_, input_ty) = formal_and_expected_inputs[expected_idx]; - let span = if let Some((_, arg_span)) = - provided_arg_tys.get(expected_idx.to_provided_idx()) - { - *arg_span - } else { - args_span - }; - let rendered = if !has_error_or_infer([input_ty]) { - format!(" of type `{input_ty}`") - } else { - "".to_string() - }; - labels.push(( - span, - format!( - "argument #{}{rendered} is missing", - expected_idx.as_usize() + 1 - ), - )); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Provide(false), - SuggestionText::Provide(_) => SuggestionText::Provide(true), - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let span = if let (Some((_, first_span)), Some((_, second_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(second_idx.to_provided_idx()), - ) { - first_span.to(*second_span) - } else { - args_span - }; - let rendered = - if !has_error_or_infer([first_expected_ty, second_expected_ty]) { - format!( - " of type `{first_expected_ty}` and `{second_expected_ty}`" - ) - } else { - "".to_string() - }; - labels.push((span, format!("two arguments{rendered} are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx, third_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let (_, third_expected_ty) = formal_and_expected_inputs[third_idx]; - let span = if let (Some((_, first_span)), Some((_, third_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(third_idx.to_provided_idx()), - ) { - first_span.to(*third_span) - } else { - args_span - }; - let rendered = if !has_error_or_infer([ - first_expected_ty, - second_expected_ty, - third_expected_ty, - ]) { - format!( - " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`" - ) - } else { - "".to_string() - }; - labels.push((span, format!("three arguments{rendered} are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - missing_idxs => { - let first_idx = *missing_idxs.first().unwrap(); - let last_idx = *missing_idxs.last().unwrap(); - // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. - // It's hard to *really* know where we should provide this error label, so this is a - // decent heuristic - let span = if let (Some((_, first_span)), Some((_, last_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(last_idx.to_provided_idx()), - ) { - first_span.to(*last_span) - } else { - args_span - }; - labels.push((span, "multiple arguments are missing".to_string())); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - } - } - Error::Swap( - first_provided_idx, - second_provided_idx, - first_expected_idx, - second_expected_idx, - ) => { - let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx]; - let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx]; - let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { - format!(", found `{first_provided_ty}`") - } else { - String::new() - }; - labels.push(( - first_span, - format!("expected `{first_expected_ty}`{first_provided_ty_name}"), - )); - - let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx]; - let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { - format!(", found `{second_provided_ty}`") - } else { - String::new() - }; - labels.push(( - second_span, - format!("expected `{second_expected_ty}`{second_provided_ty_name}"), - )); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Swap, - _ => SuggestionText::DidYouMean, - }; - } - Error::Permutation(args) => { - for (dst_arg, dest_input) in args { - let (_, expected_ty) = formal_and_expected_inputs[dst_arg]; - let (provided_ty, provided_span) = provided_arg_tys[dest_input]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - format!(", found `{provided_ty}`") - } else { - String::new() - }; - labels.push(( - provided_span, - format!("expected `{expected_ty}`{provided_ty_name}"), - )); - } - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Reorder, - _ => SuggestionText::DidYouMean, - }; - } - } - } - - self.label_generic_mismatches( - &mut err, - fn_def_id, - &matched_inputs, - &provided_arg_tys, - &formal_and_expected_inputs, - is_method, - ); - - // Incorporate the argument changes in the removal suggestion. - // When a type is *missing*, and the rest are additional, we want to suggest these with a - // multipart suggestion, but in order to do so we need to figure out *where* the arg that - // was provided but had the wrong type should go, because when looking at `expected_idx` - // that is the position in the argument list in the definition, while `provided_idx` will - // not be present. So we have to look at what the *last* provided position was, and point - // one after to suggest the replacement. FIXME(estebank): This is hacky, and there's - // probably a better more involved change we can make to make this work. - // For example, if we have - // ``` - // fn foo(i32, &'static str) {} - // foo((), (), ()); - // ``` - // what should be suggested is - // ``` - // foo(/* i32 */, /* &str */); - // ``` - // which includes the replacement of the first two `()` for the correct type, and the - // removal of the last `()`. - let mut prev = -1; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - // We want to point not at the *current* argument expression index, but rather at the - // index position where it *should have been*, which is *after* the previous one. - if let Some(provided_idx) = provided_idx { - prev = provided_idx.index() as i64; - continue; - } - let idx = ProvidedIdx::from_usize((prev + 1) as usize); - if let Some((_, arg_span)) = provided_arg_tys.get(idx) { - prev += 1; - // There is a type that was *not* found anywhere, so it isn't a move, but a - // replacement and we look at what type it should have been. This will allow us - // To suggest a multipart suggestion when encountering `foo(1, "")` where the def - // was `fn foo(())`. - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx))); - } - } + fn_call_diag_ctxt.label_generic_mismatches(&mut err); + fn_call_diag_ctxt.append_arguments_changes(&mut suggestions); // If we have less than 5 things to say, it would be useful to call out exactly what's wrong if labels.len() <= 5 { @@ -1537,113 +646,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - self.label_fn_like( + fn_call_diag_ctxt.label_fn_like( &mut err, fn_def_id, - callee_ty, + fn_call_diag_ctxt.callee_ty, call_expr, None, None, - &matched_inputs, - &formal_and_expected_inputs, - is_method, + &fn_call_diag_ctxt.matched_inputs, + &fn_call_diag_ctxt.formal_and_expected_inputs, + fn_call_diag_ctxt.call_metadata.is_method, tuple_arguments, ); // And add a suggestion block for all of the parameters - let suggestion_text = match suggestion_text { - SuggestionText::None => None, - SuggestionText::Provide(plural) => { - Some(format!("provide the argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Remove(plural) => { - err.multipart_suggestion_verbose( - format!("remove the extra argument{}", if plural { "s" } else { "" }), - suggestions, - Applicability::HasPlaceholders, - ); - None - } - SuggestionText::Swap => Some("swap these arguments".to_string()), - SuggestionText::Reorder => Some("reorder these arguments".to_string()), - SuggestionText::DidYouMean => Some("did you mean".to_string()), - }; - if let Some(suggestion_text) = suggestion_text - && !full_call_span.in_external_macro(self.sess().source_map()) + if let Some(suggestion_message) = + FnCallDiagCtxt::format_suggestion_text(&mut err, suggestions, suggestion_text) + && !fn_call_diag_ctxt.call_is_in_macro() { - let source_map = self.sess().source_map(); - let suggestion_span = if let Some(args_span) = error_span.trim_start(full_call_span) { - // Span of the braces, e.g. `(a, b, c)`. - args_span - } else { - // The arg span of a function call that wasn't even given braces - // like what might happen with delegation reuse. - // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. - full_call_span.shrink_to_hi() - }; - - // Controls how the arguments should be listed in the suggestion. - enum ArgumentsFormatting { - SingleLine, - Multiline { fallback_indent: String, brace_indent: String }, - } - let arguments_formatting = { - let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a); - if let Some(brace_indent) = source_map.indentation_before(suggestion_span) - && let Some(first_idx) = provided_inputs.by_ref().next() - && let Some(last_idx) = provided_inputs.by_ref().next() - && let (_, first_span) = provided_arg_tys[first_idx] - && let (_, last_span) = provided_arg_tys[last_idx] - && source_map.is_multiline(first_span.to(last_span)) - && let Some(fallback_indent) = source_map.indentation_before(first_span) - { - ArgumentsFormatting::Multiline { fallback_indent, brace_indent } - } else { - ArgumentsFormatting::SingleLine - } - }; + let (suggestion_span, suggestion_code) = fn_call_diag_ctxt.suggestion_code(); - let mut suggestion = "(".to_owned(); - let mut needs_comma = false; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - if needs_comma { - suggestion += ","; - } - match &arguments_formatting { - ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", - ArgumentsFormatting::SingleLine => {} - ArgumentsFormatting::Multiline { .. } => suggestion += "\n", - } - needs_comma = true; - let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx - && let (_, provided_span) = provided_arg_tys[*provided_idx] - && let Ok(arg_text) = source_map.span_to_snippet(provided_span) - { - (Some(provided_span), arg_text) - } else { - // Propose a placeholder of the correct type - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - (None, ty_to_snippet(expected_ty, expected_idx)) - }; - if let ArgumentsFormatting::Multiline { fallback_indent, .. } = - &arguments_formatting - { - let indent = suggestion_span - .and_then(|span| source_map.indentation_before(span)) - .unwrap_or_else(|| fallback_indent.clone()); - suggestion += &indent; - } - suggestion += &suggestion_text; - } - if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { - suggestion += ",\n"; - suggestion += &brace_indent; - } - suggestion += ")"; err.span_suggestion_verbose( suggestion_span, - suggestion_text, - suggestion, + suggestion_message, + suggestion_code, Applicability::HasPlaceholders, ); } @@ -2354,7 +1380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We want it to always point to the trait item. // If we're pointing at an inherent function, we don't need to do anything, // so we fetch the parent and verify if it's a trait item. - && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) + && let Ok(maybe_trait_item_def_id) = assoc_item.trait_item_or_self() && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) @@ -2836,3 +1862,1331 @@ impl FnParam<'_> { D(*self, idx) } } + +struct FnCallDiagCtxt<'a, 'b, 'tcx> { + arg_matching_ctxt: ArgMatchingCtxt<'a, 'b, 'tcx>, + errors: Vec<Error<'tcx>>, + matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>>, +} + +impl<'a, 'b, 'tcx> Deref for FnCallDiagCtxt<'a, 'b, 'tcx> { + type Target = ArgMatchingCtxt<'a, 'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.arg_matching_ctxt + } +} + +// Controls how the arguments should be listed in the suggestion. +enum ArgumentsFormatting { + SingleLine, + Multiline { fallback_indent: String, brace_indent: String }, +} + +impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, + formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, + provided_args: IndexVec<ProvidedIdx, &'tcx Expr<'tcx>>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let arg_matching_ctxt = ArgMatchingCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + + // The algorithm here is inspired by levenshtein distance and longest common subsequence. + // We'll try to detect 4 different types of mistakes: + // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs + // - An input is missing, which isn't satisfied by *any* of the other arguments + // - Some number of arguments have been provided in the wrong order + // - A type is straight up invalid + let (errors, matched_inputs) = ArgMatrix::new( + arg_matching_ctxt.provided_args.len(), + arg_matching_ctxt.formal_and_expected_inputs.len(), + |provided, expected| arg_matching_ctxt.check_compatible(provided, expected), + ) + .find_errors(); + + FnCallDiagCtxt { arg_matching_ctxt, errors, matched_inputs } + } + + fn check_wrap_args_in_tuple(&self) -> Option<ErrorGuaranteed> { + if let Some((mismatch_idx, terr)) = self.first_incompatible_error() { + // Is the first bad expected argument a tuple? + // Do we have as many extra provided arguments as the tuple's length? + // If so, we might have just forgotten to wrap some args in a tuple. + if let Some(ty::Tuple(tys)) = + self.formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind()) + // If the tuple is unit, we're not actually wrapping any arguments. + && !tys.is_empty() + && self.provided_arg_tys.len() == self.formal_and_expected_inputs.len() - 1 + tys.len() + { + // Wrap up the N provided arguments starting at this position in a tuple. + let provided_args_to_tuple = &self.provided_arg_tys[mismatch_idx..]; + let (provided_args_to_tuple, provided_args_after_tuple) = + provided_args_to_tuple.split_at(tys.len()); + let provided_as_tuple = Ty::new_tup_from_iter( + self.tcx, + provided_args_to_tuple.iter().map(|&(ty, _)| ty), + ); + + let mut satisfied = true; + // Check if the newly wrapped tuple + rest of the arguments are compatible. + for ((_, expected_ty), provided_ty) in std::iter::zip( + self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(), + [provided_as_tuple] + .into_iter() + .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)), + ) { + if !self.may_coerce(provided_ty, *expected_ty) { + satisfied = false; + break; + } + } + + // If they're compatible, suggest wrapping in an arg, and we're done! + // Take some care with spans, so we don't suggest wrapping a macro's + // innards in parenthesis, for example. + if satisfied + && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple + { + let mut err; + if tys.len() == 1 { + // A tuple wrap suggestion actually occurs within, + // so don't do anything special here. + err = self.err_ctxt().report_and_explain_type_error( + self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace( + lo, + self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()], + self.provided_arg_tys[mismatch_idx].0, + ), + self.param_env, + terr, + ); + let call_name = self.call_metadata.call_name; + err.span_label( + self.call_metadata.full_call_span, + format!("arguments to this {call_name} are incorrect"), + ); + } else { + let call_name = self.call_metadata.call_name; + err = self.dcx().struct_span_err( + self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span, + format!( + "{call_name} takes {}{} but {} {} supplied", + if self.c_variadic { "at least " } else { "" }, + potentially_plural_count( + self.formal_and_expected_inputs.len(), + "argument" + ), + potentially_plural_count(self.provided_args.len(), "argument"), + pluralize!("was", self.provided_args.len()) + ), + ); + err.code(self.err_code.to_owned()); + err.multipart_suggestion_verbose( + "wrap these arguments in parentheses to construct a tuple", + vec![ + (lo.shrink_to_lo(), "(".to_string()), + (hi.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + }; + self.arg_matching_ctxt.args_ctxt.call_ctxt.fn_ctxt.label_fn_like( + &mut err, + self.fn_def_id, + self.callee_ty, + self.call_expr, + None, + Some(mismatch_idx.as_usize()), + &self.matched_inputs, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + self.tuple_arguments, + ); + self.suggest_confusable(&mut err); + Some(err.emit()) + } else { + None + } + } else { + None + } + } else { + None + } + } + + fn ensure_has_errors(&self) -> Option<ErrorGuaranteed> { + if self.errors.is_empty() { + if cfg!(debug_assertions) { + span_bug!(self.call_metadata.error_span, "expected errors from argument matrix"); + } else { + let mut err = self.dcx().create_err(errors::ArgMismatchIndeterminate { + span: self.call_metadata.error_span, + }); + self.arg_matching_ctxt.suggest_confusable(&mut err); + return Some(err.emit()); + } + } + + None + } + + fn detect_dotdot(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'tcx>) { + if let ty::Adt(adt, _) = ty.kind() + && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) + && let hir::ExprKind::Struct(hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], _) = + expr.kind + { + // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax + // from default field values, which is not supported on tuples. + let explanation = if self.tcx.features().default_field_values() { + "this is only supported on non-tuple struct literals" + } else if self.tcx.sess.is_nightly_build() { + "this is only supported on non-tuple struct literals when \ + `#![feature(default_field_values)]` is enabled" + } else { + "this is not supported" + }; + let msg = format!( + "you might have meant to use `..` to skip providing a value for \ + expected fields, but {explanation}; it is instead interpreted as a \ + `std::ops::RangeFull` literal", + ); + err.span_help(expr.span, msg); + } + } + + fn filter_out_invalid_arguments(&mut self) -> Option<ErrorGuaranteed> { + let mut reported = None; + + self.errors.retain(|error| { + let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = + error + else { + return true; + }; + let (provided_ty, provided_span) = + self.arg_matching_ctxt.provided_arg_tys[*provided_idx]; + let trace = self.arg_matching_ctxt.mk_trace( + provided_span, + self.arg_matching_ctxt.formal_and_expected_inputs[*expected_idx], + provided_ty, + ); + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { + let mut err = self.arg_matching_ctxt.err_ctxt().report_and_explain_type_error( + trace, + self.arg_matching_ctxt.param_env, + *e, + ); + self.arg_matching_ctxt.suggest_confusable(&mut err); + reported = Some(err.emit()); + return false; + } + true + }); + + reported + } + + fn check_single_incompatible(&self) -> Option<ErrorGuaranteed> { + if let &[ + Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), + ] = &self.errors[..] + { + let (formal_ty, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + let (provided_ty, provided_arg_span) = self.provided_arg_tys[provided_idx]; + let trace = self.mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); + let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); + self.emit_coerce_suggestions( + &mut err, + self.provided_args[provided_idx], + provided_ty, + Expectation::rvalue_hint(self.fn_ctxt, expected_ty) + .only_has_type(self.fn_ctxt) + .unwrap_or(formal_ty), + None, + None, + ); + let call_name = self.call_metadata.call_name; + err.span_label( + self.call_metadata.full_call_span, + format!("arguments to this {call_name} are incorrect"), + ); + + self.fn_ctxt.label_generic_mismatches( + &mut err, + self.fn_def_id, + &self.matched_inputs, + &self.provided_arg_tys, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + ); + + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = + self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind + && provided_idx.as_usize() == expected_idx.as_usize() + { + self.note_source_of_type_mismatch_constraint( + &mut err, + rcvr, + crate::demand::TypeMismatchSource::Arg { + call_expr: self.call_expr, + incompatible_arg: provided_idx.as_usize(), + }, + ); + } + + self.suggest_ptr_null_mut( + expected_ty, + provided_ty, + self.provided_args[provided_idx], + &mut err, + ); + + self.suggest_deref_unwrap_or( + &mut err, + self.callee_ty, + self.call_metadata.call_ident, + expected_ty, + provided_ty, + self.provided_args[provided_idx], + self.call_metadata.is_method, + ); + + // Call out where the function is defined + self.label_fn_like( + &mut err, + self.fn_def_id, + self.callee_ty, + self.call_expr, + Some(expected_ty), + Some(expected_idx.as_usize()), + &self.matched_inputs, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + self.tuple_arguments, + ); + self.arg_matching_ctxt.suggest_confusable(&mut err); + self.detect_dotdot(&mut err, provided_ty, self.provided_args[provided_idx]); + return Some(err.emit()); + } + + None + } + + fn maybe_optimize_extra_arg_suggestion(&mut self) { + if let [Error::Extra(provided_idx)] = &self.errors[..] { + if !self.remove_idx_is_perfect(provided_idx.as_usize()) { + if let Some(i) = (0..self.args_ctxt.call_ctxt.provided_args.len()) + .find(|&i| self.remove_idx_is_perfect(i)) + { + self.errors = vec![Error::Extra(ProvidedIdx::from_usize(i))]; + } + } + } + } + + fn initial_final_diagnostic(&self) -> Diag<'_> { + if self.formal_and_expected_inputs.len() == self.provided_args.len() { + struct_span_code_err!( + self.dcx(), + self.call_metadata.full_call_span, + E0308, + "arguments to this {} are incorrect", + self.call_metadata.call_name, + ) + } else { + self.arg_matching_ctxt + .dcx() + .struct_span_err( + self.call_metadata.full_call_span, + format!( + "this {} takes {}{} but {} {} supplied", + self.call_metadata.call_name, + if self.c_variadic { "at least " } else { "" }, + potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"), + potentially_plural_count(self.provided_args.len(), "argument"), + pluralize!("was", self.provided_args.len()) + ), + ) + .with_code(self.err_code.to_owned()) + } + } + + fn labels_and_suggestion_text( + &self, + err: &mut Diag<'_>, + ) -> (Vec<(Span, String)>, Vec<(Span, String)>, SuggestionText) { + // Don't print if it has error types or is just plain `_` + fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { + tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) + } + + let mut labels = Vec::new(); + let mut suggestion_text = SuggestionText::None; + + let mut errors = self.errors.iter().peekable(); + let mut only_extras_so_far = errors + .peek() + .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + let mut prev_extra_idx = None; + let mut suggestions = vec![]; + while let Some(error) = errors.next() { + only_extras_so_far &= matches!(error, Error::Extra(_)); + + match error { + Error::Invalid(provided_idx, expected_idx, compatibility) => { + let (formal_ty, expected_ty) = + self.arg_matching_ctxt.args_ctxt.call_ctxt.formal_and_expected_inputs + [*expected_idx]; + let (provided_ty, provided_span) = + self.arg_matching_ctxt.provided_arg_tys[*provided_idx]; + if let Compatibility::Incompatible(error) = compatibility { + let trace = self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace( + provided_span, + (formal_ty, expected_ty), + provided_ty, + ); + if let Some(e) = error { + self.err_ctxt().note_type_err( + err, + &trace.cause, + None, + Some(self.param_env.and(trace.values)), + *e, + true, + None, + ); + } + } + + self.emit_coerce_suggestions( + err, + self.provided_args[*provided_idx], + provided_ty, + Expectation::rvalue_hint(self.fn_ctxt, expected_ty) + .only_has_type(self.fn_ctxt) + .unwrap_or(formal_ty), + None, + None, + ); + self.detect_dotdot(err, provided_ty, self.provided_args[*provided_idx]); + } + Error::Extra(arg_idx) => { + let (provided_ty, provided_span) = self.provided_arg_tys[*arg_idx]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + // FIXME: not suggestable, use something else + format!(" of type `{provided_ty}`") + } else { + "".to_string() + }; + let idx = if self.provided_arg_tys.len() == 1 { + "".to_string() + } else { + format!(" #{}", arg_idx.as_usize() + 1) + }; + labels.push(( + provided_span, + format!("unexpected argument{idx}{provided_ty_name}"), + )); + let mut span = provided_span; + if span.can_be_used_for_suggestions() + && self.call_metadata.error_span.can_be_used_for_suggestions() + { + if arg_idx.index() > 0 + && let Some((_, prev)) = self + .provided_arg_tys + .get(ProvidedIdx::from_usize(arg_idx.index() - 1)) + { + // Include previous comma + span = prev.shrink_to_hi().to(span); + } + + // Is last argument for deletion in a row starting from the 0-th argument? + // Then delete the next comma, so we are not left with `f(, ...)` + // + // fn f() {} + // - f(0, 1,) + // + f() + let trim_next_comma = match errors.peek() { + Some(Error::Extra(provided_idx)) + if only_extras_so_far + && provided_idx.index() > arg_idx.index() + 1 => + // If the next Error::Extra ("next") doesn't next to current ("current"), + // fn foo(_: (), _: u32) {} + // - foo("current", (), 1u32, "next") + // + foo((), 1u32) + // If the previous error is not a `Error::Extra`, then do not trim the next comma + // - foo((), "current", 42u32, "next") + // + foo((), 42u32) + { + prev_extra_idx.is_none_or(|prev_extra_idx| { + prev_extra_idx + 1 == arg_idx.index() + }) + } + // If no error left, we need to delete the next comma + None if only_extras_so_far => true, + // Not sure if other error type need to be handled as well + _ => false, + }; + + if trim_next_comma { + let next = self + .provided_arg_tys + .get(*arg_idx + 1) + .map(|&(_, sp)| sp) + .unwrap_or_else(|| { + // Try to move before `)`. Note that `)` here is not necessarily + // the latin right paren, it could be a Unicode-confusable that + // looks like a `)`, so we must not use `- BytePos(1)` + // manipulations here. + self.arg_matching_ctxt + .tcx() + .sess + .source_map() + .end_point(self.call_expr.span) + }); + + // Include next comma + span = span.until(next); + } + + suggestions.push((span, String::new())); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Remove(false), + SuggestionText::Remove(_) => SuggestionText::Remove(true), + _ => SuggestionText::DidYouMean, + }; + prev_extra_idx = Some(arg_idx.index()) + } + self.detect_dotdot(err, provided_ty, self.provided_args[*arg_idx]); + } + Error::Missing(expected_idx) => { + // If there are multiple missing arguments adjacent to each other, + // then we can provide a single error. + + let mut missing_idxs = vec![*expected_idx]; + while let Some(e) = errors.next_if(|e| { + matches!(e, Error::Missing(next_expected_idx) + if *next_expected_idx == *missing_idxs.last().unwrap() + 1) + }) { + match e { + Error::Missing(expected_idx) => missing_idxs.push(*expected_idx), + _ => unreachable!( + "control flow ensures that we should always get an `Error::Missing`" + ), + } + } + + // NOTE: Because we might be re-arranging arguments, might have extra + // arguments, etc. it's hard to *really* know where we should provide + // this error label, so as a heuristic, we point to the provided arg, or + // to the call if the missing inputs pass the provided args. + match &missing_idxs[..] { + &[expected_idx] => { + let (_, input_ty) = self.formal_and_expected_inputs[expected_idx]; + let span = if let Some((_, arg_span)) = + self.provided_arg_tys.get(expected_idx.to_provided_idx()) + { + *arg_span + } else { + self.args_span + }; + let rendered = if !has_error_or_infer([input_ty]) { + format!(" of type `{input_ty}`") + } else { + "".to_string() + }; + labels.push(( + span, + format!( + "argument #{}{rendered} is missing", + expected_idx.as_usize() + 1 + ), + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Provide(false), + SuggestionText::Provide(_) => SuggestionText::Provide(true), + _ => SuggestionText::DidYouMean, + }; + } + &[first_idx, second_idx] => { + let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[second_idx]; + let span = if let (Some((_, first_span)), Some((_, second_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(second_idx.to_provided_idx()), + ) { + first_span.to(*second_span) + } else { + self.args_span + }; + let rendered = + if !has_error_or_infer([first_expected_ty, second_expected_ty]) { + format!( + " of type `{first_expected_ty}` and `{second_expected_ty}`" + ) + } else { + "".to_string() + }; + labels.push((span, format!("two arguments{rendered} are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + &[first_idx, second_idx, third_idx] => { + let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[second_idx]; + let (_, third_expected_ty) = self.formal_and_expected_inputs[third_idx]; + let span = if let (Some((_, first_span)), Some((_, third_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(third_idx.to_provided_idx()), + ) { + first_span.to(*third_span) + } else { + self.args_span + }; + let rendered = if !has_error_or_infer([ + first_expected_ty, + second_expected_ty, + third_expected_ty, + ]) { + format!( + " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`" + ) + } else { + "".to_string() + }; + labels.push((span, format!("three arguments{rendered} are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + missing_idxs => { + let first_idx = *missing_idxs.first().unwrap(); + let last_idx = *missing_idxs.last().unwrap(); + // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. + // It's hard to *really* know where we should provide this error label, so this is a + // decent heuristic + let span = if let (Some((_, first_span)), Some((_, last_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(last_idx.to_provided_idx()), + ) { + first_span.to(*last_span) + } else { + self.args_span + }; + labels.push((span, "multiple arguments are missing".to_string())); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + } + } + Error::Swap( + first_provided_idx, + second_provided_idx, + first_expected_idx, + second_expected_idx, + ) => { + let (first_provided_ty, first_span) = + self.provided_arg_tys[*first_provided_idx]; + let (_, first_expected_ty) = + self.formal_and_expected_inputs[*first_expected_idx]; + let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { + format!(", found `{first_provided_ty}`") + } else { + String::new() + }; + labels.push(( + first_span, + format!("expected `{first_expected_ty}`{first_provided_ty_name}"), + )); + + let (second_provided_ty, second_span) = + self.provided_arg_tys[*second_provided_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[*second_expected_idx]; + let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { + format!(", found `{second_provided_ty}`") + } else { + String::new() + }; + labels.push(( + second_span, + format!("expected `{second_expected_ty}`{second_provided_ty_name}"), + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Swap, + _ => SuggestionText::DidYouMean, + }; + } + Error::Permutation(args) => { + for (dst_arg, dest_input) in args { + let (_, expected_ty) = self.formal_and_expected_inputs[*dst_arg]; + let (provided_ty, provided_span) = self.provided_arg_tys[*dest_input]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + format!(", found `{provided_ty}`") + } else { + String::new() + }; + labels.push(( + provided_span, + format!("expected `{expected_ty}`{provided_ty_name}"), + )); + } + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Reorder, + _ => SuggestionText::DidYouMean, + }; + } + } + } + + (suggestions, labels, suggestion_text) + } + + fn label_generic_mismatches(&self, err: &mut Diag<'b>) { + self.fn_ctxt.label_generic_mismatches( + err, + self.fn_def_id, + &self.matched_inputs, + &self.provided_arg_tys, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + ); + } + + /// Incorporate the argument changes in the removal suggestion. + /// + /// When a type is *missing*, and the rest are additional, we want to suggest these with a + /// multipart suggestion, but in order to do so we need to figure out *where* the arg that + /// was provided but had the wrong type should go, because when looking at `expected_idx` + /// that is the position in the argument list in the definition, while `provided_idx` will + /// not be present. So we have to look at what the *last* provided position was, and point + /// one after to suggest the replacement. + fn append_arguments_changes(&self, suggestions: &mut Vec<(Span, String)>) { + // FIXME(estebank): This is hacky, and there's + // probably a better more involved change we can make to make this work. + // For example, if we have + // ``` + // fn foo(i32, &'static str) {} + // foo((), (), ()); + // ``` + // what should be suggested is + // ``` + // foo(/* i32 */, /* &str */); + // ``` + // which includes the replacement of the first two `()` for the correct type, and the + // removal of the last `()`. + + let mut prev = -1; + for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() { + // We want to point not at the *current* argument expression index, but rather at the + // index position where it *should have been*, which is *after* the previous one. + if let Some(provided_idx) = provided_idx { + prev = provided_idx.index() as i64; + continue; + } + let idx = ProvidedIdx::from_usize((prev + 1) as usize); + if let Some((_, arg_span)) = self.provided_arg_tys.get(idx) { + prev += 1; + // There is a type that was *not* found anywhere, so it isn't a move, but a + // replacement and we look at what type it should have been. This will allow us + // To suggest a multipart suggestion when encountering `foo(1, "")` where the def + // was `fn foo(())`. + let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + } + } + } + + fn format_suggestion_text( + err: &mut Diag<'_>, + suggestions: Vec<(Span, String)>, + suggestion_text: SuggestionText, + ) -> Option<String> { + let suggestion_text = match suggestion_text { + SuggestionText::None => None, + SuggestionText::Provide(plural) => { + Some(format!("provide the argument{}", if plural { "s" } else { "" })) + } + SuggestionText::Remove(plural) => { + err.multipart_suggestion_verbose( + format!("remove the extra argument{}", if plural { "s" } else { "" }), + suggestions, + Applicability::HasPlaceholders, + ); + None + } + SuggestionText::Swap => Some("swap these arguments".to_string()), + SuggestionText::Reorder => Some("reorder these arguments".to_string()), + SuggestionText::DidYouMean => Some("did you mean".to_string()), + }; + suggestion_text + } + + fn arguments_formatting(&self, suggestion_span: Span) -> ArgumentsFormatting { + let source_map = self.sess().source_map(); + let mut provided_inputs = self.matched_inputs.iter().filter_map(|a| *a); + if let Some(brace_indent) = source_map.indentation_before(suggestion_span) + && let Some(first_idx) = provided_inputs.by_ref().next() + && let Some(last_idx) = provided_inputs.by_ref().next() + && let (_, first_span) = self.provided_arg_tys[first_idx] + && let (_, last_span) = self.provided_arg_tys[last_idx] + && source_map.is_multiline(first_span.to(last_span)) + && let Some(fallback_indent) = source_map.indentation_before(first_span) + { + ArgumentsFormatting::Multiline { fallback_indent, brace_indent } + } else { + ArgumentsFormatting::SingleLine + } + } + + fn suggestion_code(&self) -> (Span, String) { + let source_map = self.sess().source_map(); + let suggestion_span = if let Some(args_span) = + self.call_metadata.error_span.trim_start(self.call_metadata.full_call_span) + { + // Span of the braces, e.g. `(a, b, c)`. + args_span + } else { + // The arg span of a function call that wasn't even given braces + // like what might happen with delegation reuse. + // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. + self.call_metadata.full_call_span.shrink_to_hi() + }; + + let arguments_formatting = self.arguments_formatting(suggestion_span); + + let mut suggestion = "(".to_owned(); + let mut needs_comma = false; + for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() { + if needs_comma { + suggestion += ","; + } + match &arguments_formatting { + ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", + ArgumentsFormatting::SingleLine => {} + ArgumentsFormatting::Multiline { .. } => suggestion += "\n", + } + needs_comma = true; + let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx + && let (_, provided_span) = self.provided_arg_tys[*provided_idx] + && let Ok(arg_text) = source_map.span_to_snippet(provided_span) + { + (Some(provided_span), arg_text) + } else { + // Propose a placeholder of the correct type + let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + (None, self.ty_to_snippet(expected_ty, expected_idx)) + }; + if let ArgumentsFormatting::Multiline { fallback_indent, .. } = &arguments_formatting { + let indent = suggestion_span + .and_then(|span| source_map.indentation_before(span)) + .unwrap_or_else(|| fallback_indent.clone()); + suggestion += &indent; + } + suggestion += &suggestion_text; + } + if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { + suggestion += ",\n"; + suggestion += &brace_indent; + } + suggestion += ")"; + + (suggestion_span, suggestion) + } +} + +struct ArgMatchingCtxt<'a, 'b, 'tcx> { + args_ctxt: ArgsCtxt<'a, 'b, 'tcx>, + provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)>, +} + +impl<'a, 'b, 'tcx> Deref for ArgMatchingCtxt<'a, 'b, 'tcx> { + type Target = ArgsCtxt<'a, 'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.args_ctxt + } +} + +impl<'a, 'b, 'tcx> ArgMatchingCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, + formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, + provided_args: IndexVec<ProvidedIdx, &'tcx Expr<'tcx>>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let args_ctxt = ArgsCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + let provided_arg_tys = args_ctxt.provided_arg_tys(); + + ArgMatchingCtxt { args_ctxt, provided_arg_tys } + } + + fn suggest_confusable(&self, err: &mut Diag<'_>) { + let Some(call_name) = self.call_metadata.call_ident else { + return; + }; + let Some(callee_ty) = self.callee_ty else { + return; + }; + let input_types: Vec<Ty<'_>> = self.provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); + + // Check for other methods in the following order + // - methods marked as `rustc_confusables` with the provided arguments + // - methods with the same argument type/count and short levenshtein distance + // - methods marked as `rustc_confusables` (done) + // - methods with short levenshtein distance + + // Look for commonly confusable method names considering arguments. + if let Some(_name) = self.confusable_method_name( + err, + callee_ty.peel_refs(), + call_name, + Some(input_types.clone()), + ) { + return; + } + // Look for method names with short levenshtein distance, considering arguments. + if let Some((assoc, fn_sig)) = self.similar_assoc(call_name) + && fn_sig.inputs()[1..] + .iter() + .eq_by(input_types, |expected, found| self.may_coerce(*expected, found)) + { + let assoc_name = assoc.name(); + err.span_suggestion_verbose( + call_name.span, + format!("you might have meant to use `{}`", assoc_name), + assoc_name, + Applicability::MaybeIncorrect, + ); + return; + } + } + + /// A "softer" version of the `demand_compatible`, which checks types without persisting them, + /// and treats error types differently + /// This will allow us to "probe" for other argument orders that would likely have been correct + fn check_compatible( + &self, + provided_idx: ProvidedIdx, + expected_idx: ExpectedIdx, + ) -> Compatibility<'tcx> { + if provided_idx.as_usize() == expected_idx.as_usize() { + return self.compatibility_diagonal[provided_idx].clone(); + } + + let (formal_input_ty, expected_input_ty) = self.formal_and_expected_inputs[expected_idx]; + // If either is an error type, we defy the usual convention and consider them to *not* be + // coercible. This prevents our error message heuristic from trying to pass errors into + // every argument. + if (formal_input_ty, expected_input_ty).references_error() { + return Compatibility::Incompatible(None); + } + + let (arg_ty, arg_span) = self.provided_arg_tys[provided_idx]; + + let expectation = Expectation::rvalue_hint(self.fn_ctxt, expected_input_ty); + let coerced_ty = expectation.only_has_type(self.fn_ctxt).unwrap_or(formal_input_ty); + let can_coerce = self.may_coerce(arg_ty, coerced_ty); + if !can_coerce { + return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( + ty::error::ExpectedFound::new(coerced_ty, arg_ty), + ))); + } + + // Using probe here, since we don't want this subtyping to affect inference. + let subtyping_error = self.probe(|_| { + self.at(&self.misc(arg_span), self.param_env) + .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) + .err() + }); + + // Same as above: if either the coerce type or the checked type is an error type, + // consider them *not* compatible. + let references_error = (coerced_ty, arg_ty).references_error(); + match (references_error, subtyping_error) { + (false, None) => Compatibility::Compatible, + (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), + } + } + + fn remove_idx_is_perfect(&self, idx: usize) -> bool { + let removed_arg_tys = self + .provided_arg_tys + .iter() + .enumerate() + .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) }) + .collect::<IndexVec<ProvidedIdx, _>>(); + std::iter::zip(self.formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all( + |((expected_ty, _), (provided_ty, _))| { + !provided_ty.references_error() && self.may_coerce(*provided_ty, *expected_ty) + }, + ) + } +} + +struct ArgsCtxt<'a, 'b, 'tcx> { + call_ctxt: CallCtxt<'a, 'b, 'tcx>, + call_metadata: CallMetadata, + args_span: Span, +} + +impl<'a, 'b, 'tcx> Deref for ArgsCtxt<'a, 'b, 'tcx> { + type Target = CallCtxt<'a, 'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.call_ctxt + } +} + +impl<'a, 'b, 'tcx> ArgsCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, + formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, + provided_args: IndexVec<ProvidedIdx, &'tcx Expr<'tcx>>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let call_ctxt: CallCtxt<'_, '_, '_> = CallCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + + let call_metadata = call_ctxt.call_metadata(); + let args_span = call_metadata + .error_span + .trim_start(call_metadata.full_call_span) + .unwrap_or(call_metadata.error_span); + + ArgsCtxt { args_span, call_metadata, call_ctxt } + } + + /// Get the argument span in the context of the call span so that + /// suggestions and labels are (more) correct when an arg is a + /// macro invocation. + fn normalize_span(&self, span: Span) -> Span { + let normalized_span = + span.find_ancestor_inside_same_ctxt(self.call_metadata.error_span).unwrap_or(span); + // Sometimes macros mess up the spans, so do not normalize the + // arg span to equal the error span, because that's less useful + // than pointing out the arg expr in the wrong context. + if normalized_span.source_equal(self.call_metadata.error_span) { + span + } else { + normalized_span + } + } + + /// Computes the provided types and spans. + fn provided_arg_tys(&self) -> IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> { + self.call_ctxt + .provided_args + .iter() + .map(|expr| { + let ty = self + .call_ctxt + .fn_ctxt + .typeck_results + .borrow() + .expr_ty_adjusted_opt(*expr) + .unwrap_or_else(|| Ty::new_misc_error(self.call_ctxt.fn_ctxt.tcx)); + ( + self.call_ctxt.fn_ctxt.resolve_vars_if_possible(ty), + self.normalize_span(expr.span), + ) + }) + .collect() + } + + // Obtain another method on `Self` that have similar name. + fn similar_assoc(&self, call_name: Ident) -> Option<(ty::AssocItem, ty::FnSig<'tcx>)> { + if let Some(callee_ty) = self.call_ctxt.callee_ty + && let Ok(Some(assoc)) = self.call_ctxt.fn_ctxt.probe_op( + call_name.span, + MethodCall, + Some(call_name), + None, + IsSuggestion(true), + callee_ty.peel_refs(), + self.call_ctxt.callee_expr.unwrap().hir_id, + TraitsInScope, + |mut ctxt| ctxt.probe_for_similar_candidate(), + ) + && assoc.is_method() + { + let args = + self.call_ctxt.fn_ctxt.infcx.fresh_args_for_item(call_name.span, assoc.def_id); + let fn_sig = self + .call_ctxt + .fn_ctxt + .tcx + .fn_sig(assoc.def_id) + .instantiate(self.call_ctxt.fn_ctxt.tcx, args); + + self.call_ctxt.fn_ctxt.instantiate_binder_with_fresh_vars( + call_name.span, + BoundRegionConversionTime::FnCall, + fn_sig, + ); + } + None + } + + fn call_is_in_macro(&self) -> bool { + self.call_metadata.full_call_span.in_external_macro(self.sess().source_map()) + } +} + +struct CallMetadata { + error_span: Span, + call_ident: Option<Ident>, + full_call_span: Span, + call_name: &'static str, + is_method: bool, +} + +struct CallCtxt<'a, 'b, 'tcx> { + fn_ctxt: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, + formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, + provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + callee_expr: Option<&'tcx Expr<'tcx>>, + callee_ty: Option<Ty<'tcx>>, +} + +impl<'a, 'b, 'tcx> Deref for CallCtxt<'a, 'b, 'tcx> { + type Target = &'a FnCtxt<'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.fn_ctxt + } +} + +impl<'a, 'b, 'tcx> CallCtxt<'a, 'b, 'tcx> { + fn new( + fn_ctxt: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, + formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, + provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option<DefId>, + call_span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> CallCtxt<'a, 'b, 'tcx> { + let callee_expr = match &call_expr.peel_blocks().kind { + hir::ExprKind::Call(callee, _) => Some(*callee), + hir::ExprKind::MethodCall(_, receiver, ..) => { + if let Some((DefKind::AssocFn, def_id)) = + fn_ctxt.typeck_results.borrow().type_dependent_def(call_expr.hir_id) + && let Some(assoc) = fn_ctxt.tcx.opt_associated_item(def_id) + && assoc.is_method() + { + Some(*receiver) + } else { + None + } + } + _ => None, + }; + + let callee_ty = callee_expr.and_then(|callee_expr| { + fn_ctxt.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr) + }); + + CallCtxt { + fn_ctxt, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + callee_expr, + callee_ty, + } + } + + fn call_metadata(&self) -> CallMetadata { + match &self.call_expr.kind { + hir::ExprKind::Call( + hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, + _, + ) => { + if let Res::Def(DefKind::Ctor(of, _), _) = + self.typeck_results.borrow().qpath_res(qpath, *hir_id) + { + let name = match of { + CtorOf::Struct => "struct", + CtorOf::Variant => "enum variant", + }; + CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: name, + is_method: false, + } + } else { + CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: "function", + is_method: false, + } + } + } + hir::ExprKind::Call(hir::Expr { span, .. }, _) => CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: "function", + is_method: false, + }, + hir::ExprKind::MethodCall(path_segment, _, _, span) => { + let ident_span = path_segment.ident.span; + let ident_span = if let Some(args) = path_segment.args { + ident_span.with_hi(args.span_ext.hi()) + } else { + ident_span + }; + CallMetadata { + error_span: *span, + call_ident: Some(path_segment.ident), + full_call_span: ident_span, + call_name: "method", + is_method: true, + } + } + k => span_bug!(self.call_span, "checking argument types on a non-call: `{:?}`", k), + } + } + + fn mk_trace( + &self, + span: Span, + (formal_ty, expected_ty): (Ty<'tcx>, Ty<'tcx>), + provided_ty: Ty<'tcx>, + ) -> TypeTrace<'tcx> { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty) + } + + fn ty_to_snippet(&self, ty: Ty<'tcx>, expected_idx: ExpectedIdx) -> String { + if ty.is_unit() { + "()".to_string() + } else if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else if let Some(fn_def_id) = self.fn_def_id + && self.tcx.def_kind(fn_def_id).is_fn_like() + && let self_implicit = + matches!(self.call_expr.kind, hir::ExprKind::MethodCall(..)) as usize + && let Some(Some(arg)) = + self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) + && arg.name != kw::SelfLower + { + format!("/* {} */", arg.name) + } else { + "/* value */".to_string() + } + } + + fn first_incompatible_error(&self) -> Option<(ProvidedIdx, TypeError<'tcx>)> { + self.compatibility_diagonal.iter_enumerated().find_map(|(i, c)| { + if let Compatibility::Incompatible(Some(terr)) = c { Some((i, *terr)) } else { None } + }) + } +} + +enum SuggestionText { + None, + Provide(bool), + Remove(bool), + Swap, + Reorder, + DidYouMean, +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d7ddbcc8b53..a5c6a7f34ef 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1882,7 +1882,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if segment.ident.name == sym::clone && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| { let assoc_item = self.tcx.associated_item(did); - assoc_item.container == ty::AssocItemContainer::Trait + assoc_item.container == ty::AssocContainer::Trait && assoc_item.container_id(self.tcx) == clone_trait_did }) // If that clone call hasn't already dereferenced the self type (i.e. don't give this @@ -3021,6 +3021,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let deref_kind = if checked_ty.is_box() { + // detect Box::new(..) + if let ExprKind::Call(box_new, [_]) = expr.kind + && let ExprKind::Path(qpath) = &box_new.kind + && let Res::Def(DefKind::AssocFn, fn_id) = + self.typeck_results.borrow().qpath_res(qpath, box_new.hir_id) + && let Some(impl_id) = self.tcx.inherent_impl_of_assoc(fn_id) + && self.tcx.type_of(impl_id).skip_binder().is_box() + && self.tcx.item_name(fn_id) == sym::new + { + let l_paren = self.tcx.sess.source_map().next_point(box_new.span); + let r_paren = self.tcx.sess.source_map().end_point(expr.span); + return Some(( + vec![ + (box_new.span.to(l_paren), String::new()), + (r_paren, String::new()), + ], + "consider removing the Box".to_string(), + Applicability::MachineApplicable, + false, + false, + )); + } "unboxing the value" } else if checked_ty.is_ref() { "dereferencing the borrow" diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 129de32fd4a..acc0481e457 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -5,6 +5,7 @@ #![feature(box_patterns)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(iter_order_by)] #![feature(never_type)] // tidy-alphabetical-end @@ -254,7 +255,7 @@ fn typeck_with_inspect<'tcx>( } fcx.select_obligations_where_possible(|_| {}); - if let None = fcx.infcx.tainted_by_errors() { + if fcx.infcx.tainted_by_errors().is_none() { fcx.report_ambiguity_errors(); } @@ -278,8 +279,7 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti { if let Some(item) = tcx.opt_associated_item(def_id.into()) && let ty::AssocKind::Const { .. } = item.kind - && let ty::AssocItemContainer::Impl = item.container - && let Some(trait_item_def_id) = item.trait_item_def_id + && let ty::AssocContainer::TraitImpl(Ok(trait_item_def_id)) = item.container { let impl_def_id = item.container_id(tcx); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); @@ -296,7 +296,7 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti } else if let Node::AnonConst(_) = node { let id = tcx.local_def_id_to_hir_id(def_id); match tcx.parent_hir_node(id) { - Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), span, .. }) + Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(anon_const), span, .. }) if anon_const.hir_id == id => { Some(fcx.next_ty_var(span)) diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index a23910a2006..b23e7ae8e77 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -172,7 +172,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty); - let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { + let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else { return Ty::new_error_with_message( self.tcx, DUMMY_SP, @@ -182,8 +182,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { assert_eq!(n, pick.autoderefs); let mut adjustments = self.adjust_steps(&autoderef); - let mut target = self.structurally_resolve_type(autoderef.span(), ty); - match pick.autoref_or_ptr_adjustment { Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { let region = self.next_region_var(RegionVariableOrigin::Autoref(self.span)); diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 652644ad78c..04f112e4a39 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -317,7 +317,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )?; Ok(pick) } +} + +/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`. +/// +/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while +/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary +/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque +/// types as rigid to support `impl Deref<Target = impl FnOnce()>` and +/// `Box<impl FnOnce()>`. +/// +/// We only want to treat opaque types as rigid if we need to eagerly choose +/// between multiple candidates. We otherwise treat them as ordinary inference +/// variable to avoid rejecting otherwise correct code. +#[derive(Debug)] +pub(super) enum TreatNotYetDefinedOpaques { + AsInfer, + AsRigid, +} +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `lookup_method_in_trait` is used for overloaded operators. /// It does a very narrow slice of what the normal probe/confirm path does. /// In particular, it doesn't really do any probing: it simply constructs @@ -331,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trait_def_id: DefId, self_ty: Ty<'tcx>, opt_rhs_ty: Option<Ty<'tcx>>, + treat_opaques: TreatNotYetDefinedOpaques, ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { // Construct a trait-reference `self_ty : Trait<input_tys>` let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind { @@ -360,7 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Now we want to know if this can be matched - if !self.predicate_may_hold(&obligation) { + let matches_trait = match treat_opaques { + TreatNotYetDefinedOpaques::AsInfer => self.predicate_may_hold(&obligation), + TreatNotYetDefinedOpaques::AsRigid => { + self.predicate_may_hold_opaque_types_jank(&obligation) + } + }; + + if !matches_trait { debug!("--> Cannot match obligation"); // Cannot be matched, no such method resolution is possible. return None; diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 52a8eff984b..12f80a197b1 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -13,13 +13,13 @@ use rustc_hir::def::DefKind; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; -use rustc_infer::traits::ObligationCauseCode; +use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query}; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; use rustc_middle::ty::{ - self, AssocItem, AssocItemContainer, GenericArgs, GenericArgsRef, GenericParamDefKind, - ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, Upcast, + self, AssocContainer, AssocItem, GenericArgs, GenericArgsRef, GenericParamDefKind, ParamEnvAnd, + Ty, TyCtxt, TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -30,7 +30,8 @@ use rustc_span::edit_distance::{ use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::query::CanonicalTyGoal; +use rustc_trait_selection::solve::Goal; +use rustc_trait_selection::traits::query::CanonicalMethodAutoderefStepsGoal; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::{ CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, @@ -389,10 +390,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result<R, MethodError<'tcx>>, { let mut orig_values = OriginalQueryValues::default(); - let query_input = self.canonicalize_query( - ParamEnvAnd { param_env: self.param_env, value: self_ty }, - &mut orig_values, - ); + let predefined_opaques_in_body = if self.next_trait_solver() { + self.tcx.mk_predefined_opaques_in_body_from_iter( + self.inner.borrow_mut().opaque_types().iter_opaque_types().map(|(k, v)| (k, v.ty)), + ) + } else { + ty::List::empty() + }; + let value = query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty }; + let query_input = self + .canonicalize_query(ParamEnvAnd { param_env: self.param_env, value }, &mut orig_values); let steps = match mode { Mode::MethodCall => self.tcx.method_autoderef_steps(query_input), @@ -403,13 +410,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // special handling for this "trivial case" is a good idea. let infcx = &self.infcx; - let (ParamEnvAnd { param_env: _, value: self_ty }, var_values) = + let (ParamEnvAnd { param_env: _, value }, var_values) = infcx.instantiate_canonical(span, &query_input.canonical); + let query::MethodAutoderefSteps { predefined_opaques_in_body: _, self_ty } = value; debug!(?self_ty, ?query_input, "probe_op: Mode::Path"); MethodAutoderefStepsResult { steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { self_ty: self .make_query_response_ignoring_pending_obligations(var_values, self_ty), + self_ty_is_opaque: false, autoderefs: 0, from_unsafe_deref: false, unsize: false, @@ -479,6 +488,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Infer(ty::TyVar(_)) => { let raw_ptr_call = bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types(); + // FIXME: Ideally we'd use the span of the self-expr here, + // not of the method path. let mut err = self.err_ctxt().emit_inference_failure_err( self.body_id, span, @@ -528,7 +539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ProbeScope::Single(def_id) => { let item = self.tcx.associated_item(def_id); // FIXME(fn_delegation): Delegation to inherent methods is not yet supported. - assert_eq!(item.container, AssocItemContainer::Trait); + assert_eq!(item.container, AssocContainer::Trait); let trait_def_id = self.tcx.parent(def_id); let trait_span = self.tcx.def_span(trait_def_id); @@ -553,12 +564,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn method_autoderef_steps<'tcx>( tcx: TyCtxt<'tcx>, - goal: CanonicalTyGoal<'tcx>, + goal: CanonicalMethodAutoderefStepsGoal<'tcx>, ) -> MethodAutoderefStepsResult<'tcx> { debug!("method_autoderef_steps({:?})", goal); let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); - let ParamEnvAnd { param_env, value: self_ty } = goal; + let ParamEnvAnd { + param_env, + value: query::MethodAutoderefSteps { predefined_opaques_in_body, self_ty }, + } = goal; + for (key, ty) in predefined_opaques_in_body { + let prev = + infcx.register_hidden_type_in_storage(key, ty::OpaqueHiddenType { span: DUMMY_SP, ty }); + // It may be possible that two entries in the opaque type storage end up + // with the same key after resolving contained inference variables. + // + // We could put them in the duplicate list but don't have to. The opaques we + // encounter here are already tracked in the caller, so there's no need to + // also store them here. We'd take them out when computing the query response + // and then discard them, as they're already present in the input. + // + // Ideally we'd drop duplicate opaque type definitions when computing + // the canonical input. This is more annoying to implement and may cause a + // perf regression, so we do it inside of the query for now. + if let Some(prev) = prev { + debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`"); + } + } + + // We accept not-yet-defined opaque types in the autoderef + // chain to support recursive calls. We do error if the final + // infer var is not an opaque. + let self_ty_is_opaque = |ty: Ty<'_>| { + if let &ty::Infer(ty::TyVar(vid)) = ty.kind() { + infcx.has_opaques_with_sub_unified_hidden_type(vid) + } else { + false + } + }; // If arbitrary self types is not enabled, we follow the chain of // `Deref<Target=T>`. If arbitrary self types is enabled, we instead @@ -593,6 +636,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( let step = CandidateStep { self_ty: infcx .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -613,6 +657,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( let step = CandidateStep { self_ty: infcx .make_query_response_ignoring_pending_obligations(inference_vars, ty), + self_ty_is_opaque: self_ty_is_opaque(ty), autoderefs: d, from_unsafe_deref: reached_raw_pointer, unsize: false, @@ -629,7 +674,11 @@ pub(crate) fn method_autoderef_steps<'tcx>( }; let final_ty = autoderef_via_deref.final_ty(); let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), }), @@ -640,6 +689,7 @@ pub(crate) fn method_autoderef_steps<'tcx>( inference_vars, Ty::new_slice(infcx.tcx, *elem_ty), ), + self_ty_is_opaque: false, autoderefs, // this could be from an unsafe deref if we had // a *mut/const [T; N] @@ -655,7 +705,8 @@ pub(crate) fn method_autoderef_steps<'tcx>( }; debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); - + // Need to empty the opaque types storage before it gets dropped. + let _ = infcx.take_opaque_types(); MethodAutoderefStepsResult { steps: tcx.arena.alloc_from_iter(steps), opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), @@ -1203,7 +1254,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { !step.self_ty.value.references_error() && !step.from_unsafe_deref }) .find_map(|step| { - let InferOk { value: self_ty, obligations: _ } = self + let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self .fcx .probe_instantiate_query_response( self.span, @@ -1214,7 +1265,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) }); - let by_value_pick = self.pick_by_value_method(step, self_ty, pick_diag_hints); + let by_value_pick = self.pick_by_value_method( + step, + self_ty, + &instantiate_self_ty_obligations, + pick_diag_hints, + ); // Check for shadowing of a by-reference method by a by-value method (see comments on check_for_shadowing) if let Some(by_value_pick) = by_value_pick { @@ -1225,6 +1281,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { by_value_pick, step, self_ty, + &instantiate_self_ty_obligations, mutbl, track_unstable_candidates, ) { @@ -1239,6 +1296,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let autoref_pick = self.pick_autorefd_method( step, self_ty, + &instantiate_self_ty_obligations, hir::Mutability::Not, pick_diag_hints, None, @@ -1252,6 +1310,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { autoref_pick, step, self_ty, + &instantiate_self_ty_obligations, hir::Mutability::Mut, track_unstable_candidates, ) { @@ -1288,12 +1347,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.pick_autorefd_method( step, self_ty, + &instantiate_self_ty_obligations, hir::Mutability::Mut, pick_diag_hints, None, ) - .or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints)) - .or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints)) + .or_else(|| { + self.pick_const_ptr_method( + step, + self_ty, + &instantiate_self_ty_obligations, + pick_diag_hints, + ) + }) + .or_else(|| { + self.pick_reborrow_pin_method( + step, + self_ty, + &instantiate_self_ty_obligations, + pick_diag_hints, + ) + }) }) } @@ -1317,6 +1391,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { possible_shadower: &Pick<'tcx>, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], mutbl: hir::Mutability, track_unstable_candidates: bool, ) -> Result<(), MethodError<'tcx>> { @@ -1381,6 +1456,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let potentially_shadowed_pick = self.pick_autorefd_method( step, self_ty, + instantiate_self_ty_obligations, mutbl, &mut pick_diag_hints, Some(&pick_constraints), @@ -1407,13 +1483,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { if step.unsize { return None; } - self.pick_method(self_ty, pick_diag_hints, None).map(|r| { + self.pick_method(self_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1450,6 +1527,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], mutbl: hir::Mutability, pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, pick_constraints: Option<&PickConstraintsForShadowed>, @@ -1466,7 +1544,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let region = tcx.lifetimes.re_erased; let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl); - self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| { + self.pick_method( + autoref_ty, + instantiate_self_ty_obligations, + pick_diag_hints, + pick_constraints, + ) + .map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = @@ -1482,6 +1566,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { if !self.tcx.features().pin_ergonomics() { @@ -1503,14 +1588,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let region = self.tcx.lifetimes.re_erased; let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not); - self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref_or_ptr_adjustment = - Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not)); - pick - }) - }) + self.pick_method(autopin_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map( + |r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref_or_ptr_adjustment = + Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not)); + pick + }) + }, + ) } /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a @@ -1520,6 +1607,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { // Don't convert an unsized reference to ptr @@ -1532,18 +1620,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty); - self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); - pick - }) - }) + self.pick_method(const_ptr_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map( + |r| { + r.map(|mut pick| { + pick.autoderefs = step.autoderefs; + pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); + pick + }) + }, + ) } fn pick_method( &self, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option<PickResult<'tcx>> { @@ -1553,8 +1644,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] { debug!("searching {} candidates", kind); - let res = - self.consider_candidates(self_ty, candidates, pick_diag_hints, pick_constraints); + let res = self.consider_candidates( + self_ty, + instantiate_self_ty_obligations, + candidates, + pick_diag_hints, + pick_constraints, + ); if let Some(pick) = res { return Some(pick); } @@ -1563,6 +1659,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if self.private_candidate.get().is_none() { if let Some(Ok(pick)) = self.consider_candidates( self_ty, + instantiate_self_ty_obligations, &self.private_candidates, &mut PickDiagHints { unstable_candidates: None, @@ -1579,6 +1676,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn consider_candidates( &self, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], candidates: &[Candidate<'tcx>], pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, pick_constraints: Option<&PickConstraintsForShadowed>, @@ -1595,6 +1693,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { probe, self.consider_probe( self_ty, + instantiate_self_ty_obligations, probe, &mut pick_diag_hints.unsatisfied_predicates, ), @@ -1659,7 +1758,7 @@ impl<'tcx> Pick<'tcx> { /// Do not use for type checking. pub(crate) fn differs_from(&self, other: &Self) -> bool { let Self { - item: AssocItem { def_id, kind: _, container: _, trait_item_def_id: _ }, + item: AssocItem { def_id, kind: _, container: _ }, kind: _, import_ids: _, autoderefs: _, @@ -1702,7 +1801,7 @@ impl<'tcx> Pick<'tcx> { tcx.def_path_str(self.item.def_id), )); } - (ty::AssocKind::Const { name }, ty::AssocItemContainer::Trait) => { + (ty::AssocKind::Const { name }, ty::AssocContainer::Trait) => { let def_id = self.item.container_id(tcx); lint.span_suggestion( span, @@ -1779,10 +1878,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - #[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)] + #[instrument(level = "debug", skip(self, possibly_unsatisfied_predicates), ret)] fn consider_probe( &self, self_ty: Ty<'tcx>, + instantiate_self_ty_obligations: &[PredicateObligation<'tcx>], probe: &Candidate<'tcx>, possibly_unsatisfied_predicates: &mut Vec<( ty::Predicate<'tcx>, @@ -1790,8 +1890,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { Option<ObligationCause<'tcx>>, )>, ) -> ProbeResult { - debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe); - self.probe(|snapshot| { let outer_universe = self.universe(); @@ -1799,6 +1897,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let cause = &self.misc(self.span); let ocx = ObligationCtxt::new_with_diagnostics(self); + // Subtle: we're not *really* instantiating the current self type while + // probing, but instead fully recompute the autoderef steps once we've got + // a final `Pick`. We can't nicely handle these obligations outside of a probe. + // + // We simply handle them for each candidate here for now. That's kinda scuffed + // and ideally we just put them into the `FnCtxt` right away. We need to consider + // them to deal with defining uses in `method_autoderef_steps`. + if self.next_trait_solver() { + ocx.register_obligations(instantiate_self_ty_obligations.iter().cloned()); + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + unreachable!("unexpected autoderef error {errors:?}"); + } + } + let mut trait_predicate = None; let (mut xform_self_ty, mut xform_ret_ty); @@ -2041,6 +2154,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + if self.infcx.next_trait_solver() { + if self.should_reject_candidate_due_to_opaque_treated_as_rigid(trait_predicate) { + result = ProbeResult::NoMatch; + } + } + // Previously, method probe used `evaluate_predicate` to determine if a predicate // was impossible to satisfy. This did a leak check, so we must also do a leak // check here to prevent backwards-incompatible ambiguity being introduced. See @@ -2054,6 +2173,80 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }) } + /// Trait candidates for not-yet-defined opaque types are a somewhat hacky. + /// + /// We want to only accept trait methods if they were hold even if the + /// opaque types were rigid. To handle this, we both check that for trait + /// candidates the goal were to hold even when treating opaques as rigid, + /// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank). + /// + /// We also check that all opaque types encountered as self types in the + /// autoderef chain don't get constrained when applying the candidate. + /// Importantly, this also handles calling methods taking `&self` on + /// `impl Trait` to reject the "by-self" candidate. + /// + /// This needs to happen at the end of `consider_probe` as we need to take + /// all the constraints from that into account. + #[instrument(level = "debug", skip(self), ret)] + fn should_reject_candidate_due_to_opaque_treated_as_rigid( + &self, + trait_predicate: Option<ty::Predicate<'tcx>>, + ) -> bool { + // This function is what hacky and doesn't perfectly do what we want it to. + // It's not soundness critical and we should be able to freely improve this + // in the future. + // + // Some concrete edge cases include the fact that `goal_may_hold_opaque_types_jank` + // also fails if there are any constraints opaques which are never used as a self + // type. We also allow where-bounds which are currently ambiguous but end up + // constraining an opaque later on. + + // Check whether the trait candidate would not be applicable if the + // opaque type were rigid. + if let Some(predicate) = trait_predicate { + let goal = Goal { param_env: self.param_env, predicate }; + if !self.infcx.goal_may_hold_opaque_types_jank(goal) { + return true; + } + } + + // Check whether any opaque types in the autoderef chain have been + // constrained. + for step in self.steps { + if step.self_ty_is_opaque { + debug!(?step.autoderefs, ?step.self_ty, "self_type_is_opaque"); + let constrained_opaque = self.probe(|_| { + // If we fail to instantiate the self type of this + // step, this part of the deref-chain is no longer + // reachable. In this case we don't care about opaque + // types there. + let Ok(ok) = self.fcx.probe_instantiate_query_response( + self.span, + self.orig_steps_var_values, + &step.self_ty, + ) else { + debug!("failed to instantiate self_ty"); + return false; + }; + let ocx = ObligationCtxt::new(self); + let self_ty = ocx.register_infer_ok_obligations(ok); + if !ocx.select_where_possible().is_empty() { + debug!("failed to prove instantiate self_ty obligations"); + return false; + } + + !self.resolve_vars_if_possible(self_ty).is_ty_var() + }); + if constrained_opaque { + debug!("opaque type has been constrained"); + return true; + } + } + } + + false + } + /// Sometimes we get in a situation where we have multiple probes that are all impls of the /// same trait, but we don't know which impl to use. In this case, since in all cases the /// external interface of the method can be determined from the trait, it's ok not to decide. diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c8f6c06b720..44602e62899 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1008,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { @@ -1914,9 +1914,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(ref args) = call_args && fn_sig.inputs()[1..] .iter() - .zip(args.into_iter()) - .all(|(expected, found)| self.may_coerce(*expected, *found)) - && fn_sig.inputs()[1..].len() == args.len() + .eq_by(args, |expected, found| self.may_coerce(*expected, *found)) { err.span_suggestion_verbose( item_name.span, @@ -2194,7 +2192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_associated_call_syntax( &self, err: &mut Diag<'_>, - static_candidates: &Vec<CandidateSource>, + static_candidates: &[CandidateSource], rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, item_name: Ident, @@ -2422,7 +2420,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span_included = match parent_expr.kind { hir::ExprKind::Struct(_, eps, _) => { - eps.len() > 0 && eps.last().is_some_and(|ep| ep.span.contains(span)) + eps.last().is_some_and(|ep| ep.span.contains(span)) } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. hir::ExprKind::Call(func, ..) => func.span.contains(span), @@ -2484,7 +2482,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { simplify_type(tcx, ty, TreatParams::InstantiateWithInfer) .and_then(|simp| { tcx.incoherent_impls(simp) - .into_iter() + .iter() .find_map(|&id| self.associated_value(id, item_name)) }) .is_some() @@ -2617,7 +2615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) && let ControlFlow::Break(Some(expr)) = - (LetVisitor { ident_name: seg1.ident.name }).visit_body(&body) + (LetVisitor { ident_name: seg1.ident.name }).visit_body(body) && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { let probe = self.lookup_probe_for_diagnostic( @@ -2960,14 +2958,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::<Vec<_>>() .into(); for pred in &local_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - local_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + local_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if local_spans.primary_span().is_some() { @@ -3006,14 +3001,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::<Vec<_>>() .into(); for pred in &foreign_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - foreign_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + foreign_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if foreign_spans.primary_span().is_some() { @@ -3595,7 +3587,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // would take care of them. && !skippable.contains(&Some(pick.item.container_id(self.tcx))) // Do not suggest pinning when the method is directly on `Pin`. - && pick.item.impl_container(self.tcx).map_or(true, |did| { + && pick.item.impl_container(self.tcx).is_none_or(|did| { match self.tcx.type_of(did).skip_binder().kind() { ty::Adt(def, _) => Some(def.did()) != self.tcx.lang_items().pin_type(), _ => true, @@ -3653,7 +3645,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![ ( rcvr.span.shrink_to_lo(), - format!("let mut pinned = std::pin::pin!("), + "let mut pinned = std::pin::pin!(".to_string(), ), ( rcvr.span.shrink_to_hi(), @@ -4128,7 +4120,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let trait_span = self.tcx.def_span(trait_def_id); let mut multi_span: MultiSpan = trait_span.into(); - multi_span.push_span_label(trait_span, format!("this is the trait that is needed")); + multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string()); let descr = self.tcx.associated_item(item_def_id).descr(); let rcvr_ty = rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string()); @@ -4146,7 +4138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } multi_span.push_span_label( self.tcx.def_span(def_id), - format!("this is the trait that was imported"), + "this is the trait that was imported".to_string(), ); } err.span_note(multi_span, msg); diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 11defc3aa03..a8e8582c51c 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -21,6 +21,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::FnCtxt; use super::method::MethodCallee; use crate::Expectation; +use crate::method::TreatNotYetDefinedOpaques; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a <op>= b` @@ -565,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty, lhs_expr, lhs_ty, - |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty), + is_compatible_after_call, ) { // Cool } @@ -974,8 +975,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let method = - self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty); + // We don't consider any other candidates if this lookup fails + // so we can freely treat opaque types as inference variables here + // to allow more code to compile. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + let method = self.lookup_method_for_operator( + cause.clone(), + opname, + trait_did, + lhs_ty, + opt_rhs_ty, + treat_opaques, + ); match method { Some(ok) => { let method = self.register_infer_ok_obligations(ok); @@ -1159,8 +1170,8 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> { } } -/// Returns `true` if this is a built-in arithmetic operation (e.g., u32 -/// + u32, i16x4 == i16x4) and false if these types would have to be +/// Returns `true` if this is a built-in arithmetic operation (e.g., +/// u32 + u32, i16x4 == i16x4) and false if these types would have to be /// overloaded to be legal. There are two reasons that we distinguish /// builtin operations from overloaded ones (vs trying to drive /// everything uniformly through the trait system and intrinsics or @@ -1180,7 +1191,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - match category.into() { + match category { BinOpCategory::Shortcircuit => true, BinOpCategory::Shift => { lhs.references_error() diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 97feac3d009..4c1fe69405e 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -15,7 +15,7 @@ use crate::FnCtxt; impl<'tcx> FnCtxt<'_, 'tcx> { /// This takes all the opaque type uses during HIR typeck. It first computes - /// the concrete hidden type by iterating over all defining uses. + /// the hidden type by iterating over all defining uses. /// /// A use during HIR typeck is defining if all non-lifetime arguments are /// unique generic parameters and the hidden type does not reference any @@ -35,8 +35,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } debug!(?opaque_types); - self.compute_concrete_opaque_types(&opaque_types); - self.apply_computed_concrete_opaque_types(&opaque_types); + self.compute_definition_site_hidden_types(&opaque_types); + self.apply_definition_site_hidden_types(&opaque_types); } } @@ -71,7 +71,7 @@ impl<'tcx> UsageKind<'tcx> { } impl<'tcx> FnCtxt<'_, 'tcx> { - fn compute_concrete_opaque_types( + fn compute_definition_site_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) { @@ -117,28 +117,32 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) } UsageKind::UnconstrainedHiddenType(hidden_type) => { - let infer_var = hidden_type - .ty - .walk() - .filter_map(ty::GenericArg::as_term) - .find(|term| term.is_infer()) - .unwrap_or_else(|| hidden_type.ty.into()); - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - hidden_type.span, - infer_var, - TypeAnnotationNeeded::E0282, - false, - ) - .emit() + if let Some(guar) = self.tainted_by_errors() { + guar + } else { + let infer_var = hidden_type + .ty + .walk() + .filter_map(ty::GenericArg::as_term) + .find(|term| term.is_infer()) + .unwrap_or_else(|| hidden_type.ty.into()); + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + hidden_type.span, + infer_var, + TypeAnnotationNeeded::E0282, + false, + ) + .emit() + } } UsageKind::HasDefiningUse => continue, }; self.typeck_results .borrow_mut() - .concrete_opaque_types + .hidden_types .insert(def_id, OpaqueHiddenType::new_error(tcx, guar)); self.set_tainted_by_errors(guar); } @@ -157,7 +161,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) { match err { NonDefiningUseReason::Tainted(guar) => { - self.typeck_results.borrow_mut().concrete_opaque_types.insert( + self.typeck_results.borrow_mut().hidden_types.insert( opaque_type_key.def_id, OpaqueHiddenType::new_error(self.tcx, guar), ); @@ -193,20 +197,19 @@ impl<'tcx> FnCtxt<'_, 'tcx> { let prev = self .typeck_results .borrow_mut() - .concrete_opaque_types + .hidden_types .insert(opaque_type_key.def_id, hidden_type); assert!(prev.is_none()); UsageKind::HasDefiningUse } - fn apply_computed_concrete_opaque_types( + fn apply_definition_site_hidden_types( &mut self, opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], ) { let tcx = self.tcx; for &(key, hidden_type) in opaque_types { - let expected = - *self.typeck_results.borrow_mut().concrete_opaque_types.get(&key.def_id).unwrap(); + let expected = *self.typeck_results.borrow_mut().hidden_types.get(&key.def_id).unwrap(); let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index f735c0a4160..46accb76a18 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -47,7 +47,7 @@ You can read more about trait objects in the Trait Objects section of the Refere https://doc.rust-lang.org/reference/types.html#trait-objects"; fn is_number(text: &str) -> bool { - text.chars().all(|c: char| c.is_digit(10)) + text.chars().all(|c: char| c.is_ascii_digit()) } /// Information about the expected type at the top level of type checking a pattern. @@ -262,8 +262,9 @@ enum InheritedRefMatchRule { /// pattern matches a given type: /// - If the underlying type is not a reference, a reference pattern may eat the inherited reference; /// - If the underlying type is a reference, a reference pattern matches if it can eat either one - /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the - /// underlying type is `&mut` or the inherited reference is `&mut`. + /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the + /// underlying type is `&mut` or the inherited reference is `&mut`. + /// /// If `false`, a reference pattern is only matched against the underlying type. /// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and /// `ref_pat_eat_one_layer_2024_structural` feature gates. @@ -1069,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if !self.tcx.features().mut_ref() { feature_err( - &self.tcx.sess, + self.tcx.sess, sym::mut_ref, pat.span.until(ident.span), "binding cannot be both mutable and by-reference", @@ -1487,31 +1488,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_def_id: Option<hir::def_id::DefId>, ident: Ident, ) -> bool { - match opt_def_id { - Some(def_id) => match self.tcx.hir_get_if_local(def_id) { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(_, _, _, body_id), - .. - })) => match self.tcx.hir_node(body_id.hir_id) { - hir::Node::Expr(expr) => { - if hir::is_range_literal(expr) { - let span = self.tcx.hir_span(body_id.hir_id); - if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { - e.span_suggestion_verbose( - ident.span, - "you may want to move the range into the match block", - snip, - Applicability::MachineApplicable, - ); - return true; - } - } - } - _ => (), - }, - _ => (), - }, - _ => (), + if let Some(def_id) = opt_def_id + && let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(_, _, _, body_id), + .. + })) = self.tcx.hir_get_if_local(def_id) + && let hir::Node::Expr(expr) = self.tcx.hir_node(body_id.hir_id) + && hir::is_range_literal(expr) + { + let span = self.tcx.hir_span(body_id.hir_id); + if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { + e.span_suggestion_verbose( + ident.span, + "you may want to move the range into the match block", + snip, + Applicability::MachineApplicable, + ); + return true; + } } false } @@ -1529,7 +1523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(span) = self.tcx.hir_res_span(pat_res) { e.span_label(span, format!("{} defined here", res.descr())); - if let [hir::PathSegment { ident, .. }] = &*segments { + if let [hir::PathSegment { ident, .. }] = segments { e.span_label( pat_span, format!( @@ -1557,17 +1551,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => (None, None), }; - let is_range = match type_def_id.and_then(|id| self.tcx.as_lang_item(id)) { + let is_range = matches!( + type_def_id.and_then(|id| self.tcx.as_lang_item(id)), Some( LangItem::Range - | LangItem::RangeFrom - | LangItem::RangeTo - | LangItem::RangeFull - | LangItem::RangeInclusiveStruct - | LangItem::RangeToInclusive, - ) => true, - _ => false, - }; + | LangItem::RangeFrom + | LangItem::RangeTo + | LangItem::RangeFull + | LangItem::RangeInclusiveStruct + | LangItem::RangeToInclusive, + ) + ); if is_range { if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { let msg = "constants only support matching by type, \ diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 1125e984080..a48db2cc855 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -12,7 +12,7 @@ use rustc_span::{Span, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; -use crate::method::MethodCallee; +use crate::method::{MethodCallee, TreatNotYetDefinedOpaques}; use crate::{FnCtxt, PlaceOp}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -210,7 +210,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty) + // FIXME(trait-system-refactor-initiative#231): we may want to treat + // opaque types as rigid here to support `impl Deref<Target = impl Index<usize>>`. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + imm_op, + imm_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } fn try_mutable_overloaded_place_op( @@ -230,7 +240,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty) + // We have to replace the operator with the mutable variant for the + // program to compile, so we don't really have a choice here and want + // to just try using `DerefMut` even if its not in the item bounds + // of the opaque. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + mut_op, + mut_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 06acff06a51..0a7b458f3fb 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Not all upvars are captured by ref, so use // `apply_capture_kind_on_capture_ty` to ensure that we // compute the right captured type. - return apply_capture_kind_on_capture_ty( + apply_capture_kind_on_capture_ty( self.tcx, upvar_ty, capture, @@ -430,7 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.tcx.lifetimes.re_erased }, - ); + ) }, ), ); @@ -529,11 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // process any deferred resolutions. let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); for deferred_call_resolution in deferred_call_resolutions { - deferred_call_resolution.resolve(&mut FnCtxt::new( - self, - self.param_env, - closure_def_id, - )); + deferred_call_resolution.resolve(&FnCtxt::new(self, self.param_env, closure_def_id)); } } @@ -1493,7 +1489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Notation: /// - Ty(place): Type of place /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` - /// respectively. + /// respectively. /// ```ignore (illustrative) /// (Ty(w), [ &[p, x], &[c] ]) /// // | @@ -2179,9 +2175,10 @@ fn restrict_precision_for_unsafe( (place, curr_mode) } -/// Truncate projections so that following rules are obeyed by the captured `place`: +/// Truncate projections so that the following rules are obeyed by the captured `place`: /// - No Index projections are captured, since arrays are captured completely. -/// - No unsafe block is required to capture `place` +/// - No unsafe block is required to capture `place`. +/// /// Returns the truncated place and updated capture mode. fn restrict_capture_precision( place: Place<'_>, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 90301d1b391..697029e55f7 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -220,7 +220,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // to use builtin indexing because the index type is known to be // usize-ish fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { - if let hir::ExprKind::Index(ref base, ref index, _) = e.kind { + if let hir::ExprKind::Index(base, index, _) = e.kind { // All valid indexing looks like this; might encounter non-valid indexes at this point. let base_ty = self.typeck_results.expr_ty_adjusted(base); if let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() { @@ -550,13 +550,12 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_opaque_types_next(&mut self) { let mut fcx_typeck_results = self.fcx.typeck_results.borrow_mut(); assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - for hidden_ty in fcx_typeck_results.concrete_opaque_types.values() { + for hidden_ty in fcx_typeck_results.hidden_types.values() { assert!(!hidden_ty.has_infer()); } - assert_eq!(self.typeck_results.concrete_opaque_types.len(), 0); - self.typeck_results.concrete_opaque_types = - mem::take(&mut fcx_typeck_results.concrete_opaque_types); + assert_eq!(self.typeck_results.hidden_types.len(), 0); + self.typeck_results.hidden_types = mem::take(&mut fcx_typeck_results.hidden_types); } #[instrument(skip(self), level = "debug")] @@ -583,12 +582,12 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } if let Err(err) = opaque_type_has_defining_use_args( - &self.fcx, + self.fcx, opaque_type_key, hidden_type.span, DefiningScopeKind::HirTypeck, ) { - self.typeck_results.concrete_opaque_types.insert( + self.typeck_results.hidden_types.insert( opaque_type_key.def_id, ty::OpaqueHiddenType::new_error(tcx, err.report(self.fcx)), ); @@ -600,16 +599,11 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { DefiningScopeKind::HirTypeck, ); - if let Some(prev) = self - .typeck_results - .concrete_opaque_types - .insert(opaque_type_key.def_id, hidden_type) + if let Some(prev) = + self.typeck_results.hidden_types.insert(opaque_type_key.def_id, hidden_type) { - let entry = &mut self - .typeck_results - .concrete_opaque_types - .get_mut(&opaque_type_key.def_id) - .unwrap(); + let entry = + &mut self.typeck_results.hidden_types.get_mut(&opaque_type_key.def_id).unwrap(); if prev.ty != hidden_type.ty { if let Some(guar) = self.typeck_results.tainted_by_errors { entry.ty = Ty::new_error(tcx, guar); @@ -628,7 +622,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let recursive_opaques: Vec<_> = self .typeck_results - .concrete_opaque_types + .hidden_types .iter() .filter(|&(&def_id, hidden_ty)| { hidden_ty @@ -636,7 +630,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { .visit_with(&mut HasRecursiveOpaque { def_id, seen: Default::default(), - opaques: &self.typeck_results.concrete_opaque_types, + opaques: &self.typeck_results.hidden_types, tcx, }) .is_break() @@ -651,7 +645,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { .with_code(E0720) .emit(); self.typeck_results - .concrete_opaque_types + .hidden_types .insert(def_id, OpaqueHiddenType { span, ty: Ty::new_error(tcx, guar) }); } } @@ -792,7 +786,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_potentially_region_dependent_goals(&mut self) { let obligations = self.fcx.take_hir_typeck_potentially_region_dependent_goals(); - if let None = self.fcx.tainted_by_errors() { + if self.fcx.tainted_by_errors().is_none() { for obligation in obligations { let (predicate, mut cause) = self.fcx.resolve_vars_if_possible((obligation.predicate, obligation.cause)); @@ -1003,8 +997,10 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> { } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - debug_assert!(!r.is_bound(), "Should not be resolving bound region."); - self.fcx.tcx.lifetimes.re_erased + match r.kind() { + ty::ReBound(..) => r, + _ => self.fcx.tcx.lifetimes.re_erased, + } } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 684bd34f909..f7649c5f5c5 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -645,7 +645,7 @@ impl<T: Idx> ChunkedBitSet<T> { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; @@ -708,7 +708,7 @@ impl<T: Idx> ChunkedBitSet<T> { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; diff --git a/compiler/rustc_index/src/idx.rs b/compiler/rustc_index/src/idx.rs index 9cd7134659c..2fb2008f9a3 100644 --- a/compiler/rustc_index/src/idx.rs +++ b/compiler/rustc_index/src/idx.rs @@ -130,12 +130,6 @@ impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeFrom<I> { impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeInclusive<I> { type Output = core::range::RangeInclusive<usize>; #[inline] - #[cfg(bootstrap)] - fn into_slice_idx(self) -> Self::Output { - core::range::RangeInclusive { start: self.start.index(), end: self.end.index() } - } - #[inline] - #[cfg(not(bootstrap))] fn into_slice_idx(self) -> Self::Output { core::range::RangeInclusive { start: self.start.index(), last: self.last.index() } } diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index cc680838e7e..9d647209c6f 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] +#![cfg_attr(bootstrap, feature(new_zeroed_alloc))] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))] #![cfg_attr(feature = "nightly", feature(new_range_api))] -#![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))] // tidy-alphabetical-end pub mod bit_set; diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index ad19cdef4e7..70e3d7dc9fe 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -28,7 +28,7 @@ use relate::lattice::{LatticeOp, LatticeOpKind}; use rustc_middle::bug; use rustc_middle::ty::relate::solver_relating::RelateExt as NextSolverRelate; -use rustc_middle::ty::{Const, ImplSubject, TypingMode}; +use rustc_middle::ty::{Const, TypingMode}; use super::*; use crate::infer::relate::type_relating::TypeRelating; @@ -304,23 +304,6 @@ impl<'a, 'tcx> At<'a, 'tcx> { } } -impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> { - fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { - match (a, b) { - (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { - ToTrace::to_trace(cause, trait_ref_a, trait_ref_b) - } - (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { - ToTrace::to_trace(cause, ty_a, ty_b) - } - (ImplSubject::Trait(_), ImplSubject::Inherent(_)) - | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { - bug!("can not trace TraitRef and Ty"); - } - } - } -} - impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { TypeTrace { diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 5d1b4be9e57..b3959113d5d 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -85,11 +85,29 @@ impl<'tcx> InferCtxt<'tcx> { where T: Debug + TypeFoldable<TyCtxt<'tcx>>, { + // While we ignore region constraints and pending obligations, + // we do return constrained opaque types to avoid unconstrained + // inference variables in the response. This is important as we want + // to check that opaques in deref steps stay unconstrained. + // + // This doesn't handle the more general case for non-opaques as + // ambiguous `Projection` obligations have same the issue. + let opaque_types = if self.next_trait_solver() { + self.inner + .borrow_mut() + .opaque_type_storage + .iter_opaque_types() + .map(|(k, v)| (k, v.ty)) + .collect() + } else { + vec![] + }; + self.canonicalize_response(QueryResponse { var_values: inference_vars, region_constraints: QueryRegionConstraints::default(), certainty: Certainty::Proven, // Ambiguities are OK! - opaque_types: vec![], + opaque_types, value: answer, }) } diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 14cc590720a..5ffa7304efa 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -302,6 +302,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { .map(|(k, h)| (k, h.ty)) .collect() } + fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec<ty::AliasTy<'tcx>> { + self.opaques_with_sub_unified_hidden_type(ty) + } fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 9d3886aff1c..c9fc124d3bf 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1004,6 +1004,60 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() } + pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool { + if !self.next_trait_solver() { + return false; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return true; + } + } + + false + }) + } + + /// Searches for an opaque type key whose hidden type is related to `ty_vid`. + /// + /// This only checks for a subtype relation, it does not require equality. + pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec<ty::AliasTy<'tcx>> { + // Avoid accidentally allowing more code to compile with the old solver. + if !self.next_trait_solver() { + return vec![]; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + // This is iffy, can't call `type_variables()` as we're already + // borrowing the `opaque_type_storage` here. + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner + .opaque_type_storage + .iter_opaque_types() + .filter_map(|(key, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return Some(ty::AliasTy::new_from_args( + self.tcx, + key.def_id.into(), + key.args, + )); + } + } + + None + }) + .collect() + } + #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool { debug_assert!(!self.next_trait_solver()); diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 9a66bd0574c..4a252719694 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -72,12 +72,27 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { self.register_predicate_obligation(infcx, obligation); } } - + /// Go over the list of pending obligations and try to evaluate them. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: leave the obligation in the list to be evaluated later + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Err. #[must_use] fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>; fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>; + /// Evaluate all pending obligations, return error if they can't be evaluated. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: remove the obligation from the list and return an error + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Ambiguous or Err. #[must_use] fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> { let errors = self.select_where_possible(infcx); diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 473ac5e0cea..f0836c47740 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -58,4 +58,5 @@ rustc_abi = { path = "../rustc_abi" } # tidy-alphabetical-start check_only = ['rustc_codegen_llvm?/check_only'] llvm = ['dep:rustc_codegen_llvm'] +llvm_enzyme = ['rustc_builtin_macros/llvm_enzyme', 'rustc_codegen_llvm/llvm_enzyme'] # tidy-alphabetical-end diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index d83b18cbc2d..1f5c5e74d97 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -30,6 +30,9 @@ interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag interface_input_file_would_be_overwritten = the input file "{$path}" would be overwritten by the generated executable +interface_invalid_crate_type_value = invalid `crate_type` value + .suggestion = did you mean + interface_mixed_bin_crate = cannot mix `bin` crate type with others @@ -44,7 +47,7 @@ interface_out_dir_error = failed to find or create the directory specified by `--out-dir` interface_proc_macro_crate_panic_abort = - building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic + building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic interface_temps_dir_error = failed to find or create the directory specified by `--temps-dir` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index d1082eaf617..727e09c7562 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -1,7 +1,7 @@ use std::io; use std::path::Path; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -108,3 +108,18 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } + +#[derive(LintDiagnostic)] +#[diag(interface_invalid_crate_type_value)] +pub(crate) struct UnknownCrateTypes { + #[subdiagnostic] + pub sugg: Option<UnknownCrateTypesSub>, +} + +#[derive(Subdiagnostic)] +#[suggestion(interface_suggestion, code = r#""{snippet}""#, applicability = "maybe-incorrect")] +pub(crate) struct UnknownCrateTypesSub { + #[primary_span] + pub span: Span, + pub snippet: Symbol, +} diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d39219bfd66..c1bba0b0197 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -42,7 +42,6 @@ use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, }; -use rustc_target::spec::PanicStrategy; use rustc_trait_selection::{solve, traits}; use tracing::{info, instrument}; @@ -192,7 +191,7 @@ fn configure_and_expand( unsafe { env::set_var( "PATH", - &env::join_paths( + env::join_paths( new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()), ) .unwrap(), @@ -282,7 +281,7 @@ fn configure_and_expand( feature_err(sess, sym::export_stable, DUMMY_SP, "`sdylib` crate type is unstable").emit(); } - if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort { + if is_proc_macro_crate && !sess.panic_strategy().unwinds() { sess.dcx().emit_warn(errors::ProcMacroCratePanicAbort); } @@ -446,7 +445,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { let prev_source = sess.psess.source_map().span_to_prev_source(first_span); let ferris_fix = prev_source .map_or(FerrisFix::SnakeCase, |source| { - let mut source_before_ferris = source.trim_end().split_whitespace().rev(); + let mut source_before_ferris = source.split_whitespace().rev(); match source_before_ferris.next() { Some("struct" | "trait" | "mod" | "union" | "type" | "enum") => { FerrisFix::PascalCase @@ -500,7 +499,7 @@ fn env_var_os<'tcx>(tcx: TyCtxt<'tcx>, key: &'tcx OsStr) -> Option<&'tcx OsStr> // properly change-tracked. tcx.sess.psess.env_depinfo.borrow_mut().insert(( Symbol::intern(&key.to_string_lossy()), - value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(&value)), + value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(value)), )); value_tcx @@ -824,7 +823,7 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) { let outputs = tcx.output_filenames(()); let output_paths = - generated_output_paths(tcx, &outputs, sess.io.output_file.is_some(), crate_name); + generated_output_paths(tcx, outputs, sess.io.output_file.is_some(), crate_name); // Ensure the source file isn't accidentally overwritten during compilation. if let Some(input_path) = sess.io.input.opt_path() { @@ -847,7 +846,7 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) { } } - write_out_deps(tcx, &outputs, &output_paths); + write_out_deps(tcx, outputs, &output_paths); let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1; @@ -1123,18 +1122,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { sess.time("layout_testing", || layout_test::test_layout(tcx)); sess.time("abi_testing", || abi_test::test_abi(tcx)); - - // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item - // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs - // in MIR optimizations that may only be reachable through codegen, or other codepaths - // that requires the optimized/ctfe MIR, coroutine bodies, or evaluating consts. - if tcx.sess.opts.unstable_opts.validate_mir { - sess.time("ensuring_final_MIR_is_computable", || { - tcx.par_hir_body_owners(|def_id| { - tcx.instance_mir(ty::InstanceKind::Item(def_id.into())); - }); - }); - } } /// Runs the type-checking, region checking and other miscellaneous analysis @@ -1200,6 +1187,20 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) { // we will fail to emit overlap diagnostics. Thus we invoke it here unconditionally. let _ = tcx.all_diagnostic_items(()); }); + + // If `-Zvalidate-mir` is set, we also want to compute the final MIR for each item + // (either its `mir_for_ctfe` or `optimized_mir`) since that helps uncover any bugs + // in MIR optimizations that may only be reachable through codegen, or other codepaths + // that requires the optimized/ctfe MIR, coroutine bodies, or evaluating consts. + // Nevertheless, wait after type checking is finished, as optimizing code that does not + // type-check is very prone to ICEs. + if tcx.sess.opts.unstable_opts.validate_mir { + sess.time("ensuring_final_MIR_is_computable", || { + tcx.par_hir_body_owners(|def_id| { + tcx.instance_mir(ty::InstanceKind::Item(def_id.into())); + }); + }); + } } /// Runs the codegen backend, after which the AST and analysis can @@ -1303,7 +1304,7 @@ pub(crate) fn parse_crate_name( let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = AttributeParser::parse_limited_should_emit( sess, - &attrs, + attrs, sym::crate_name, DUMMY_SP, rustc_ast::node_id::CRATE_NODE_ID, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 370e886c525..280214ab418 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -94,7 +94,7 @@ impl Linker { &rlink_file, &codegen_results, &self.metadata, - &*self.output_filenames, + &self.output_filenames, ) .unwrap_or_else(|error| { sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 7730bddc0f1..7e5186db4ea 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -765,14 +765,15 @@ fn test_unstable_options_tracking_hash() { tracked!(allow_features, Some(vec![String::from("lang_items")])); tracked!(always_encode_mir, true); tracked!(assume_incomplete_release, true); - tracked!(autodiff, vec![AutoDiff::Enable]); + tracked!(autodiff, vec![AutoDiff::Enable, AutoDiff::NoTT]); tracked!(binary_dep_depinfo, true); tracked!(box_noalias, false); tracked!( branch_protection, Some(BranchProtection { bti: true, - pac_ret: Some(PacRet { leaf: true, pc: true, key: PAuthKey::B }) + pac_ret: Some(PacRet { leaf: true, pc: true, key: PAuthKey::B }), + gcs: true, }) ); tracked!(codegen_backend, Some("abc".to_string())); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 04006f3e446..58ec72b5b45 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -13,9 +13,8 @@ use rustc_errors::LintBuffer; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; -use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; -use rustc_session::{EarlyDiagCtxt, Session, filesearch}; +use rustc_session::{EarlyDiagCtxt, Session, filesearch, lint}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; @@ -244,7 +243,7 @@ pub(crate) fn run_in_thread_pool_with_globals< let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs().ok().expect("failed to collect active queries in deadlock handler") + QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler") }); break_query_cycles(query_map, ®istry); }) @@ -387,8 +386,8 @@ fn get_codegen_sysroot( .collect::<Vec<_>>() .join("\n* "); let err = format!( - "failed to find a `codegen-backends` folder \ - in the sysroot candidates:\n* {candidates}" + "failed to find a `codegen-backends` folder in the sysroot candidates:\n\ + * {candidates}" ); early_dcx.early_fatal(err); }); @@ -397,10 +396,8 @@ fn get_codegen_sysroot( let d = sysroot.read_dir().unwrap_or_else(|e| { let err = format!( - "failed to load default codegen backend, couldn't \ - read `{}`: {}", + "failed to load default codegen backend, couldn't read `{}`: {e}", sysroot.display(), - e ); early_dcx.early_fatal(err); }); @@ -468,7 +465,10 @@ pub(crate) fn check_attr_crate_type( lint::builtin::UNKNOWN_CRATE_TYPES, ast::CRATE_NODE_ID, span, - BuiltinLintDiag::UnknownCrateTypes { span, candidate }, + errors::UnknownCrateTypes { + sugg: candidate + .map(|cand| errors::UnknownCrateTypesSub { span, snippet: cand }), + }, ); } } else { @@ -542,6 +542,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu stem, None, sess.io.temps_dir.clone(), + sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), ) @@ -559,7 +560,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu } Some(out_file.clone()) }; - if sess.io.output_dir != None { + if sess.io.output_dir.is_some() { sess.dcx().emit_warn(errors::IgnoringOutDir); } @@ -571,6 +572,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu out_filestem, ofile, sess.io.temps_dir.clone(), + sess.opts.unstable_opts.split_dwarf_out_dir.clone(), sess.opts.cg.extra_filename.clone(), sess.opts.output_types.clone(), ) diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index c29ab569f47..f6790f7ed1e 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -599,14 +599,16 @@ impl Cursor<'_> { if potential_closing.is_none() { // a less fortunate recovery if all else fails which finds any dashes preceded by whitespace // on a standalone line. Might be wrong. + let mut base_index = 0; while let Some(closing) = rest.find("---") { let preceding_chars_start = rest[..closing].rfind("\n").map_or(0, |i| i + 1); if rest[preceding_chars_start..closing].chars().all(is_horizontal_whitespace) { // candidate found - potential_closing = Some(closing); + potential_closing = Some(closing + base_index); break; } else { rest = &rest[closing + 3..]; + base_index += closing + 3; } } } diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 417e5a97069..5b98c9fafaa 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -40,12 +40,6 @@ lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` order lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` -lint_avoid_att_syntax = - avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - -lint_avoid_intel_syntax = - avoid using `.intel_syntax`, Intel syntax is the default - lint_bad_attribute_argument = bad attribute argument lint_bad_opt_access = {$msg} @@ -187,12 +181,6 @@ lint_builtin_unused_doc_comment = unused doc comment lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` -lint_byte_slice_in_packed_struct_with_derive = {$ty} slice in a packed struct that derives a built-in trait - .help = consider implementing the trait by hand, or remove the `packed` attribute - -lint_cfg_attr_no_attributes = - `#[cfg_attr]` does not expand to any attributes - lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_closure_returning_async_block = closure returning async block can be made into an async closure @@ -249,11 +237,6 @@ lint_dropping_copy_types = calls to `std::mem::drop` with a value that implement lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` -lint_duplicate_macro_attribute = - duplicated attribute - -lint_duplicate_matcher_binding = duplicate matcher binding - lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum @@ -409,9 +392,6 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive -lint_incomplete_include = - include macro expected single expression in source - lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` .help = start numbering with `2` instead @@ -427,9 +407,6 @@ lint_invalid_asm_label_named = avoid using named labels in inline assembly .note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro -lint_invalid_crate_type_value = invalid `crate_type` value - .suggestion = did you mean - # FIXME: we should ordinalize $valid_up_to when we add support for doing so lint_invalid_from_utf8_checked = calls to `{$method}` with an invalid literal always return an error .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes @@ -470,15 +447,9 @@ lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a point .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` -lint_legacy_derive_helpers = derive helper attribute is used before it is introduced - .label = the attribute is introduced here - lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead -lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths - .note = the macro is defined here - lint_macro_expr_fragment_specifier_2024_migration = the `expr` fragment specifier will accept more expressions in the 2024 edition .suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier @@ -486,10 +457,6 @@ lint_macro_is_private = macro `{$ident}` is private lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used -lint_macro_use_deprecated = - applying the `#[macro_use]` attribute to an `extern crate` item is deprecated - .help = remove it and import macros at use sites with a `use` item instead - lint_malformed_attribute = malformed lint attribute input lint_map_unit_fn = `Iterator::map` call that discard the iterator's values @@ -499,10 +466,6 @@ lint_map_unit_fn = `Iterator::map` call that discard the iterator's values .map_label = after this call to map, the resulting iterator is `impl Iterator<Item = ()>`, which means the only information carried by the iterator is the number of items .suggestion = you might have meant to use `Iterator::for_each` -lint_metavariable_still_repeating = variable `{$name}` is still repeating at this depth - -lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator - lint_mismatched_lifetime_syntaxes_eliding_while_named = eliding a lifetime that's named elsewhere is confusing @@ -548,9 +511,6 @@ lint_mismatched_lifetime_syntaxes_suggestion_mixed = lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths = use `'_` for type paths -lint_missing_unsafe_on_extern = extern blocks should be unsafe - .suggestion = needs `unsafe` before the extern keyword - lint_mixed_script_confusables = the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables .includes_note = the usage includes {$includes} @@ -674,9 +634,6 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass lint_opaque_hidden_inferred_bound_sugg = add this bound -lint_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro - .suggestion = use pat_param to preserve semantics - lint_out_of_scope_macro_calls = cannot find macro `{$path}` in the current scope when looking from {$location} .label = not found from {$location} .help = import `macro_rules` with `use` to make it callable above its definition @@ -719,9 +676,6 @@ lint_pattern_in_foreign = patterns aren't allowed in foreign function declaratio lint_private_extern_crate_reexport = extern crate `{$ident}` is private and cannot be re-exported .suggestion = consider making the `extern crate` item publicly accessible -lint_proc_macro_derive_resolution_fallback = cannot find {$ns_descr} `{$ident}` in this scope - .label = names from parent modules are not accessible without an explicit import - lint_query_instability = using `{$query}` can result in unstable query results .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale @@ -762,9 +716,6 @@ lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> *[false] this semicolon } -lint_reexport_private_dependency = - {$kind} `{$name}` from private dependency '{$krate}' is re-exported - lint_remove_mut_from_pattern = remove `mut` from the parameter lint_removed_lint = lint `{$name}` has been removed: {$reason} @@ -826,10 +777,6 @@ lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal lint_too_large_char_cast = value exceeds maximum `char` value .note = maximum valid `char` value is `0x10FFFF` -lint_trailing_semi_macro = trailing semicolon in macro used in expression position - .note1 = macro invocations at the end of a block are treated as expressions - .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` - lint_ty_qualified = usage of qualified `ty::{$ty}` .suggestion = try importing it and using it unqualified @@ -935,13 +882,9 @@ lint_unknown_lint = *[false] did you mean: `{$replace}` } -lint_unknown_macro_variable = unknown macro variable `{$name}` - lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root -lint_unnameable_test_items = cannot test inner items - lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments @@ -964,10 +907,6 @@ lint_untranslatable_diag = diagnostics should be created using translatable mess lint_unused_allocation = unnecessary allocation, use `&` instead lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead -lint_unused_builtin_attribute = unused attribute `{$attr_name}` - .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` - .suggestion = remove the attribute - lint_unused_closure = unused {$pre}{$count -> [one] closure @@ -993,14 +932,6 @@ lint_unused_def = unused {$pre}`{$def}`{$post} that must be used lint_unused_delim = unnecessary {$delim} around {$item} .suggestion = remove these {$delim} -lint_unused_doc_comment = unused doc comment - .label = rustdoc does not generate documentation for macro invocations - .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion - -lint_unused_extern_crate = unused extern crate - .label = unused - .suggestion = remove the unused `extern crate` - lint_unused_import_braces = braces around {$node} is unnecessary lint_unused_imports = {$num_snippets -> @@ -1014,15 +945,11 @@ lint_unused_imports = {$num_snippets -> } .help = if this is a test module, consider adding a `#[cfg(test)]` to the containing module -lint_unused_label = unused label - lint_unused_lifetime = lifetime parameter `{$ident}` never used .suggestion = elide the unused lifetime lint_unused_macro_definition = unused macro definition: `{$name}` -lint_unused_macro_use = unused `#[macro_use]` import - lint_unused_op = unused {$op} that must be used .label = the {$op} produces a value .suggestion = use `let _ = ...` to ignore the resulting value diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c3c0a34df71..75a0f89321b 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -29,12 +29,12 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{Body, FnDecl, PatKind, PredicateOrigin, find_attr}; +use rustc_hir::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef}; +use rustc_middle::ty::{self, AssocContainer, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef}; use rustc_session::lint::FutureIncompatibilityReason; // hardwired lints from rustc_lint_defs pub use rustc_session::lint::builtin::*; @@ -61,7 +61,6 @@ use crate::lints::{ BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, }; -use crate::nonstandard_style::{MethodLateContext, method_context}; use crate::{ EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, fluent_generated as fluent, @@ -469,14 +468,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { - let context = method_context(cx, impl_item.owner_id.def_id); + let container = cx.tcx.associated_item(impl_item.owner_id.def_id).container; - match context { + match container { // If the method is an impl for a trait, don't doc. - MethodLateContext::TraitImpl => return, - MethodLateContext::TraitAutoImpl => {} + AssocContainer::TraitImpl(_) => return, + AssocContainer::Trait => {} // If the method is an impl for an item with docs_hidden, don't doc. - MethodLateContext::PlainImpl => { + AssocContainer::InherentImpl => { let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()); let impl_ty = cx.tcx.type_of(parent).instantiate_identity(); let outerdef = match impl_ty.kind() { @@ -1321,9 +1320,8 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { - // Only lint inherent impl items. - if cx.tcx.associated_item(impl_item.owner_id).trait_item_def_id.is_none() { - self.perform_lint(cx, "item", impl_item.owner_id.def_id, impl_item.vis_span, false); + if let ImplItemImplKind::Inherent { vis_span } = impl_item.impl_kind { + self.perform_lint(cx, "item", impl_item.owner_id.def_id, vis_span, false); } } } diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index e1c51ff8299..703f757abd5 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -66,11 +66,11 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { && tcx.is_lang_item(did, LangItem::Deref) // the self type is `dyn t_principal` && let self_ty = tcx.type_of(item.owner_id).instantiate_identity() - && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind() + && let ty::Dynamic(data, _) = self_ty.kind() && let Some(self_principal) = data.principal() // `<T as Deref>::Target` is `dyn target_principal` && let Some(target) = cx.get_associated_type(self_ty, did, sym::Target) - && let ty::Dynamic(data, _, ty::Dyn) = target.kind() + && let ty::Dynamic(data, _) = target.kind() && let Some(target_principal) = data.principal() // `target_principal` is a supertrait of `t_principal` && let Some(supertrait_principal) = supertraits(tcx, self_principal.with_self_ty(tcx, self_ty)) diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7300490b838..9029ee04471 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -64,17 +64,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { - span: macro_span, - ns_descr, - ident, - } => lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns_descr, ident } - .decorate_lint(diag), - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { - lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def } - .decorate_lint(diag) - } - BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { lints::ElidedLifetimesInPaths { subdiag: elided_lifetime_in_path_suggestion( @@ -87,10 +76,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { - let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); - lints::UnknownCrateTypes { sugg }.decorate_lint(diag); - } BuiltinLintDiag::UnusedImports { remove_whole_use, num_to_remove, @@ -146,9 +131,6 @@ pub fn decorate_builtin_lint( stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } .decorate_lint(diag); } - BuiltinLintDiag::UnusedDocComment(attr_span) => { - lints::UnusedDocComment { span: attr_span }.decorate_lint(diag); - } BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; if is_foreign { @@ -158,12 +140,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { - lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); - } - BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { - lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); - } BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { lints::ReservedPrefix { label: label_span, @@ -183,18 +159,6 @@ pub fn decorate_builtin_lint( lints::ReservedMultihash { suggestion }.decorate_lint(diag); } } - BuiltinLintDiag::UnusedBuiltinAttribute { - attr_name, - macro_name, - invoc_span, - attr_span, - } => { - lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name, attr_span } - .decorate_lint(diag); - } - BuiltinLintDiag::TrailingMacro(is_trailing, name) => { - lints::TrailingMacro { is_trailing, name }.decorate_lint(diag); - } BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { lints::BreakWithLabelAndLoop { sub: lints::BreakWithLabelAndLoopSub { @@ -221,9 +185,6 @@ pub fn decorate_builtin_lint( }; lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); } - BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => { - lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag); - } BuiltinLintDiag::SingleUseLifetime { param_span, use_span: Some((use_span, elide)), @@ -253,7 +214,6 @@ pub fn decorate_builtin_lint( .decorate_lint(diag); } BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { - debug!(?deletion_span); lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag); } BuiltinLintDiag::NamedArgumentUsedPositionally { @@ -291,12 +251,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { - lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); - } - BuiltinLintDiag::UnusedExternCrate { span, removal_span } => { - lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag); - } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); let code = if vis_span.is_empty() { "use " } else { " use " }; @@ -335,9 +289,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => { - lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag); - } BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } @@ -377,15 +328,10 @@ pub fn decorate_builtin_lint( }); lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag); } - BuiltinLintDiag::MacroUseDeprecated => { - lints::MacroUseDeprecated.decorate_lint(diag); - } - BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag), BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => { lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() } .decorate_lint(diag); } - BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag), BuiltinLintDiag::MacroIsPrivate(ident) => { lints::MacroIsPrivate { ident }.decorate_lint(diag); } @@ -398,36 +344,6 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::UnstableFeature(msg) => { lints::UnstableFeature { msg }.decorate_lint(diag); } - BuiltinLintDiag::AvoidUsingIntelSyntax => { - lints::AvoidIntelSyntax.decorate_lint(diag); - } - BuiltinLintDiag::AvoidUsingAttSyntax => { - lints::AvoidAttSyntax.decorate_lint(diag); - } - BuiltinLintDiag::IncompleteInclude => { - lints::IncompleteInclude.decorate_lint(diag); - } - BuiltinLintDiag::UnnameableTestItems => { - lints::UnnameableTestItems.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMacroAttribute => { - lints::DuplicateMacroAttribute.decorate_lint(diag); - } - BuiltinLintDiag::CfgAttrNoAttributes => { - lints::CfgAttrNoAttributes.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableStillRepeating(name) => { - lints::MetaVariableStillRepeating { name }.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableWrongOperator => { - lints::MetaVariableWrongOperator.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMatcherBinding => { - lints::DuplicateMatcherBinding.decorate_lint(diag); - } - BuiltinLintDiag::UnknownMacroVariable(name) => { - lints::UnknownMacroVariable { name }.decorate_lint(diag); - } BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 56d65ed08f9..b7312484de5 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; use rustc_session::lint::AmbiguityErrorDiag; use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; @@ -2536,15 +2536,6 @@ pub(crate) mod unexpected_cfg_value { } #[derive(LintDiagnostic)] -#[diag(lint_macro_use_deprecated)] -#[help] -pub(crate) struct MacroUseDeprecated; - -#[derive(LintDiagnostic)] -#[diag(lint_unused_macro_use)] -pub(crate) struct UnusedMacroUse; - -#[derive(LintDiagnostic)] #[diag(lint_private_extern_crate_reexport, code = E0365)] pub(crate) struct PrivateExternCrateReexport { pub ident: Ident, @@ -2553,10 +2544,6 @@ pub(crate) struct PrivateExternCrateReexport { } #[derive(LintDiagnostic)] -#[diag(lint_unused_label)] -pub(crate) struct UnusedLabel; - -#[derive(LintDiagnostic)] #[diag(lint_macro_is_private)] pub(crate) struct MacroIsPrivate { pub ident: Ident, @@ -2586,50 +2573,6 @@ impl<'a> LintDiagnostic<'a, ()> for UnstableFeature { } #[derive(LintDiagnostic)] -#[diag(lint_avoid_intel_syntax)] -pub(crate) struct AvoidIntelSyntax; - -#[derive(LintDiagnostic)] -#[diag(lint_avoid_att_syntax)] -pub(crate) struct AvoidAttSyntax; - -#[derive(LintDiagnostic)] -#[diag(lint_incomplete_include)] -pub(crate) struct IncompleteInclude; - -#[derive(LintDiagnostic)] -#[diag(lint_unnameable_test_items)] -pub(crate) struct UnnameableTestItems; - -#[derive(LintDiagnostic)] -#[diag(lint_duplicate_macro_attribute)] -pub(crate) struct DuplicateMacroAttribute; - -#[derive(LintDiagnostic)] -#[diag(lint_cfg_attr_no_attributes)] -pub(crate) struct CfgAttrNoAttributes; - -#[derive(LintDiagnostic)] -#[diag(lint_metavariable_still_repeating)] -pub(crate) struct MetaVariableStillRepeating { - pub name: MacroRulesNormalizedIdent, -} - -#[derive(LintDiagnostic)] -#[diag(lint_metavariable_wrong_operator)] -pub(crate) struct MetaVariableWrongOperator; - -#[derive(LintDiagnostic)] -#[diag(lint_duplicate_matcher_binding)] -pub(crate) struct DuplicateMatcherBinding; - -#[derive(LintDiagnostic)] -#[diag(lint_unknown_macro_variable)] -pub(crate) struct UnknownMacroVariable { - pub name: MacroRulesNormalizedIdent, -} - -#[derive(LintDiagnostic)] #[diag(lint_unused_crate_dependency)] #[help] pub(crate) struct UnusedCrateDependency { @@ -2715,22 +2658,6 @@ pub(crate) struct AbsPathWithModuleSugg { } #[derive(LintDiagnostic)] -#[diag(lint_proc_macro_derive_resolution_fallback)] -pub(crate) struct ProcMacroDeriveResolutionFallback { - #[label] - pub span: Span, - pub ns_descr: &'static str, - pub ident: Ident, -} - -#[derive(LintDiagnostic)] -#[diag(lint_macro_expanded_macro_exports_accessed_by_absolute_paths)] -pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { - #[note] - pub definition: Span, -} - -#[derive(LintDiagnostic)] #[diag(lint_hidden_lifetime_parameters)] pub(crate) struct ElidedLifetimesInPaths { #[subdiagnostic] @@ -2738,21 +2665,6 @@ pub(crate) struct ElidedLifetimesInPaths { } #[derive(LintDiagnostic)] -#[diag(lint_invalid_crate_type_value)] -pub(crate) struct UnknownCrateTypes { - #[subdiagnostic] - pub sugg: Option<UnknownCrateTypesSub>, -} - -#[derive(Subdiagnostic)] -#[suggestion(lint_suggestion, code = r#""{candidate}""#, applicability = "maybe-incorrect")] -pub(crate) struct UnknownCrateTypesSub { - #[primary_span] - pub span: Span, - pub candidate: Symbol, -} - -#[derive(LintDiagnostic)] #[diag(lint_unused_imports)] pub(crate) struct UnusedImports { #[subdiagnostic] @@ -2810,14 +2722,6 @@ pub(crate) enum RedundantImportSub { } #[derive(LintDiagnostic)] -#[diag(lint_unused_doc_comment)] -#[help] -pub(crate) struct UnusedDocComment { - #[label] - pub span: Span, -} - -#[derive(LintDiagnostic)] pub(crate) enum PatternsInFnsWithoutBody { #[diag(lint_pattern_in_foreign)] Foreign { @@ -2841,21 +2745,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { } #[derive(LintDiagnostic)] -#[diag(lint_legacy_derive_helpers)] -pub(crate) struct LegacyDeriveHelpers { - #[label] - pub span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(lint_or_patterns_back_compat)] -pub(crate) struct OrPatternsBackCompat { - #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] - pub span: Span, - pub suggestion: String, -} - -#[derive(LintDiagnostic)] #[diag(lint_reserved_prefix)] pub(crate) struct ReservedPrefix { #[label] @@ -2876,27 +2765,6 @@ pub(crate) struct RawPrefix { } #[derive(LintDiagnostic)] -#[diag(lint_unused_builtin_attribute)] -pub(crate) struct UnusedBuiltinAttribute { - #[note] - pub invoc_span: Span, - pub attr_name: Symbol, - pub macro_name: String, - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub attr_span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(lint_trailing_semi_macro)] -pub(crate) struct TrailingMacro { - #[note(lint_note1)] - #[note(lint_note2)] - pub is_trailing: bool, - - pub name: Ident, -} - -#[derive(LintDiagnostic)] #[diag(lint_break_with_label_and_loop)] pub(crate) struct BreakWithLabelAndLoop { #[subdiagnostic] @@ -2939,13 +2807,6 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg { } #[derive(LintDiagnostic)] -#[diag(lint_missing_unsafe_on_extern)] -pub(crate) struct MissingUnsafeOnExtern { - #[suggestion(code = "unsafe ", applicability = "machine-applicable")] - pub suggestion: Span, -} - -#[derive(LintDiagnostic)] #[diag(lint_single_use_lifetime)] pub(crate) struct SingleUseLifetime { #[label(lint_label_param)] @@ -2993,23 +2854,6 @@ pub(crate) struct NamedArgumentUsedPositionally { } #[derive(LintDiagnostic)] -#[diag(lint_byte_slice_in_packed_struct_with_derive)] -#[help] -pub(crate) struct ByteSliceInPackedStructWithDerive { - // FIXME: make this translatable - pub ty: String, -} - -#[derive(LintDiagnostic)] -#[diag(lint_unused_extern_crate)] -pub(crate) struct UnusedExternCrate { - #[label] - pub span: Span, - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] - pub removal_span: Span, -} - -#[derive(LintDiagnostic)] #[diag(lint_extern_crate_not_idiomatic)] pub(crate) struct ExternCrateNotIdiomatic { #[suggestion(style = "verbose", code = "{code}", applicability = "machine-applicable")] @@ -3057,14 +2901,6 @@ pub(crate) struct HiddenGlobReexports { } #[derive(LintDiagnostic)] -#[diag(lint_reexport_private_dependency)] -pub(crate) struct ReexportPrivateDependency { - pub name: String, - pub kind: String, - pub krate: Symbol, -} - -#[derive(LintDiagnostic)] #[diag(lint_unnecessary_qualification)] pub(crate) struct UnusedQualifications { #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index dca22b986ff..b10be22dc38 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,11 +1,12 @@ use rustc_errors::MultiSpan; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; -use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; +use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind, find_attr}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::{ExpnKind, Span, kw, sym}; +use rustc_span::{ExpnKind, Span, kw}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -241,7 +242,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { ) } ItemKind::Macro(_, _macro, _kinds) - if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => + if find_attr!( + cx.tcx.get_all_attrs(item.owner_id.def_id), + AttributeKind::MacroExport { .. } + ) => { cx.emit_span_lint( NON_LOCAL_DEFINITIONS, diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 65075cfecfa..7f643a551bb 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; use rustc_middle::hir::nested_filter::All; -use rustc_middle::ty; +use rustc_middle::ty::AssocContainer; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; @@ -20,29 +20,6 @@ use crate::lints::{ }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -#[derive(PartialEq)] -pub(crate) enum MethodLateContext { - TraitAutoImpl, - TraitImpl, - PlainImpl, -} - -pub(crate) fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLateContext { - let item = cx.tcx.associated_item(id); - match item.container { - ty::AssocItemContainer::Trait => MethodLateContext::TraitAutoImpl, - ty::AssocItemContainer::Impl => match cx.tcx.impl_trait_ref(item.container_id(cx.tcx)) { - Some(_) => MethodLateContext::TraitImpl, - None => MethodLateContext::PlainImpl, - }, - } -} - -fn assoc_item_in_trait_impl(cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) -> bool { - let item = cx.tcx.associated_item(ii.owner_id); - item.trait_item_def_id.is_some() -} - declare_lint! { /// The `non_camel_case_types` lint detects types, variants, traits and /// type parameters that don't have camel case names. @@ -389,8 +366,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { id: LocalDefId, ) { match &fk { - FnKind::Method(ident, sig, ..) => match method_context(cx, id) { - MethodLateContext::PlainImpl => { + FnKind::Method(ident, sig, ..) => match cx.tcx.associated_item(id).container { + AssocContainer::InherentImpl => { if sig.header.abi != ExternAbi::Rust && find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..)) { @@ -398,10 +375,10 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } self.check_snake_case(cx, "method", ident); } - MethodLateContext::TraitAutoImpl => { + AssocContainer::Trait => { self.check_snake_case(cx, "trait method", ident); } - _ => (), + AssocContainer::TraitImpl(_) => {} }, FnKind::ItemFn(ident, _, header) => { // Skip foreign-ABI #[no_mangle] functions (Issue #31924) @@ -602,7 +579,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_impl_item(&mut self, cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) { if let hir::ImplItemKind::Const(..) = ii.kind - && !assoc_item_in_trait_impl(cx, ii) + && let hir::ImplItemImplKind::Inherent { .. } = ii.impl_kind { NonUpperCaseGlobals::check_upper_case(cx, "associated constant", None, &ii.ident); } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 88527fa2e6e..eaec0c9857d 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -320,7 +320,7 @@ fn lint_wide_pointer<'tcx>( }; (!ty.is_sized(cx.tcx, cx.typing_env())) - .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))) + .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _)))) }; // the left and right operands can have references, remove any explicit references diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 22d89d24612..874c4354029 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -312,7 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { }) .map(|inner| MustUsePath::Opaque(Box::new(inner))) } - ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { + ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { let def_id = trait_ref.def_id; @@ -843,6 +843,10 @@ trait UnusedDelimLint { && !snip.ends_with(' ') { " " + } else if let Ok(snip) = sm.span_to_prev_source(value_span) + && snip.ends_with(|c: char| c.is_alphanumeric()) + { + " " } else { "" }; @@ -852,6 +856,10 @@ trait UnusedDelimLint { && !snip.starts_with(' ') { " " + } else if let Ok(snip) = sm.span_to_prev_source(value_span) + && snip.starts_with(|c: char| c.is_alphanumeric()) + { + " " } else { "" }; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b79075ac09b..939f3d088b1 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2309,10 +2309,10 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![cfg_attr(not(bootstrap), feature(sanitize))] + /// #![feature(sanitize)] /// /// #[inline(always)] - /// #[cfg_attr(not(bootstrap), sanitize(address = "off"))] + /// #[sanitize(address = "off")] /// fn x() {} /// /// fn main() { @@ -4191,8 +4191,13 @@ declare_lint! { /// You can't have multiple arguments in a `#[macro_export(..)]`, or mention arguments other than `local_inner_macros`. /// pub INVALID_MACRO_EXPORT_ARGUMENTS, - Warn, + Deny, "\"invalid_parameter\" isn't a valid argument for `#[macro_export]`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #57571 <https://github.com/rust-lang/rust/issues/57571>", + report_in_deps: true, + }; } declare_lint! { @@ -4832,16 +4837,13 @@ declare_lint! { /// /// ### Example /// - #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] - #[cfg_attr(bootstrap, doc = "```rust")] + /// ```rust,compile_fail /// #![doc = in_root!()] /// /// macro_rules! in_root { () => { "" } } /// /// fn main() {} - #[cfg_attr(not(bootstrap), doc = "```")] - #[cfg_attr(bootstrap, doc = "```")] - // ^ Needed to avoid tidy warning about odd number of backticks + /// ``` /// /// {{produces}} /// diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 2e84233e5a5..40a818a3c9d 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -11,7 +11,7 @@ use rustc_hir_id::{HashStableContext, HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use serde::{Deserialize, Serialize}; pub use self::Level::*; @@ -620,17 +620,7 @@ pub enum DeprecatedSinceKind { #[derive(Debug)] pub enum BuiltinLintDiag { AbsPathWithModule(Span), - ProcMacroDeriveResolutionFallback { - span: Span, - ns_descr: &'static str, - ident: Ident, - }, - MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), - UnknownCrateTypes { - span: Span, - candidate: Option<Symbol>, - }, UnusedImports { remove_whole_use: bool, num_to_remove: usize, @@ -646,20 +636,11 @@ pub enum BuiltinLintDiag { path: String, since_kind: DeprecatedSinceKind, }, - UnusedDocComment(Span), - UnusedBuiltinAttribute { - attr_name: Symbol, - macro_name: String, - invoc_span: Span, - attr_span: Span, - }, PatternsInFnsWithoutBody { span: Span, ident: Ident, is_foreign: bool, }, - LegacyDeriveHelpers(Span), - OrPatternsBackCompat(Span, String), ReservedPrefix(Span, String), /// `'r#` in edition < 2021. RawPrefix(Span), @@ -668,15 +649,11 @@ pub enum BuiltinLintDiag { is_string: bool, suggestion: Span, }, - TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), - MissingUnsafeOnExtern { - suggestion: Span, - }, SingleUseLifetime { /// Span of the parameter which declares this lifetime. param_span: Span, @@ -701,14 +678,6 @@ pub enum BuiltinLintDiag { /// Indicates if the named argument is used as a width/precision for formatting is_formatting_arg: bool, }, - ByteSliceInPackedStructWithDerive { - // FIXME: enum of byte/string - ty: String, - }, - UnusedExternCrate { - span: Span, - removal_span: Span, - }, ExternCrateNotIdiomatic { vis_span: Span, ident_span: Span, @@ -736,11 +705,6 @@ pub enum BuiltinLintDiag { /// The local binding that shadows the glob reexport. private_item_span: Span, }, - ReexportPrivateDependency { - name: String, - kind: String, - krate: Symbol, - }, UnusedQualifications { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, @@ -763,27 +727,14 @@ pub enum BuiltinLintDiag { span: Span, typo_name: Option<Symbol>, }, - MacroUseDeprecated, - UnusedMacroUse, PrivateExternCrateReexport { source: Ident, extern_crate_span: Span, }, - UnusedLabel, MacroIsPrivate(Ident), UnusedMacroDefinition(Symbol), MacroRuleNeverUsed(usize, Symbol), UnstableFeature(DiagMessage), - AvoidUsingIntelSyntax, - AvoidUsingAttSyntax, - IncompleteInclude, - UnnameableTestItems, - DuplicateMacroAttribute, - CfgAttrNoAttributes, - MetaVariableStillRepeating(MacroRulesNormalizedIdent), - MetaVariableWrongOperator, - DuplicateMatcherBinding, - UnknownMacroVariable(MacroRulesNormalizedIdent), UnusedCrateDependency { extern_crate: Symbol, local_crate: Symbol, diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 0dfd1b13df5..ad93c745381 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,8 +10,7 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa` and `rustc_windows_rc`. +# `cc` updates often break things, so we pin it here. cc = "=1.2.16" # tidy-alphabetical-end diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 9d21d0d22e3..2cfda7a5fb4 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -16,7 +16,6 @@ const OPTIONAL_COMPONENTS: &[&str] = &[ "mips", "powerpc", "systemz", - "jsbackend", "webassembly", "msp430", "sparc", diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 3bb1533c2fe..2e9fd6754f1 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -79,122 +79,6 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) { timeTraceProfilerCleanup(); } -#ifdef LLVM_COMPONENT_X86 -#define SUBTARGET_X86 SUBTARGET(X86) -#else -#define SUBTARGET_X86 -#endif - -#ifdef LLVM_COMPONENT_ARM -#define SUBTARGET_ARM SUBTARGET(ARM) -#else -#define SUBTARGET_ARM -#endif - -#ifdef LLVM_COMPONENT_AARCH64 -#define SUBTARGET_AARCH64 SUBTARGET(AArch64) -#else -#define SUBTARGET_AARCH64 -#endif - -#ifdef LLVM_COMPONENT_AVR -#define SUBTARGET_AVR SUBTARGET(AVR) -#else -#define SUBTARGET_AVR -#endif - -#ifdef LLVM_COMPONENT_M68k -#define SUBTARGET_M68K SUBTARGET(M68k) -#else -#define SUBTARGET_M68K -#endif - -#ifdef LLVM_COMPONENT_CSKY -#define SUBTARGET_CSKY SUBTARGET(CSKY) -#else -#define SUBTARGET_CSKY -#endif - -#ifdef LLVM_COMPONENT_MIPS -#define SUBTARGET_MIPS SUBTARGET(Mips) -#else -#define SUBTARGET_MIPS -#endif - -#ifdef LLVM_COMPONENT_POWERPC -#define SUBTARGET_PPC SUBTARGET(PPC) -#else -#define SUBTARGET_PPC -#endif - -#ifdef LLVM_COMPONENT_SYSTEMZ -#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ) -#else -#define SUBTARGET_SYSTEMZ -#endif - -#ifdef LLVM_COMPONENT_MSP430 -#define SUBTARGET_MSP430 SUBTARGET(MSP430) -#else -#define SUBTARGET_MSP430 -#endif - -#ifdef LLVM_COMPONENT_RISCV -#define SUBTARGET_RISCV SUBTARGET(RISCV) -#else -#define SUBTARGET_RISCV -#endif - -#ifdef LLVM_COMPONENT_SPARC -#define SUBTARGET_SPARC SUBTARGET(Sparc) -#else -#define SUBTARGET_SPARC -#endif - -#ifdef LLVM_COMPONENT_XTENSA -#define SUBTARGET_XTENSA SUBTARGET(XTENSA) -#else -#define SUBTARGET_XTENSA -#endif - -#ifdef LLVM_COMPONENT_HEXAGON -#define SUBTARGET_HEXAGON SUBTARGET(Hexagon) -#else -#define SUBTARGET_HEXAGON -#endif - -#ifdef LLVM_COMPONENT_LOONGARCH -#define SUBTARGET_LOONGARCH SUBTARGET(LoongArch) -#else -#define SUBTARGET_LOONGARCH -#endif - -#define GEN_SUBTARGETS \ - SUBTARGET_X86 \ - SUBTARGET_ARM \ - SUBTARGET_AARCH64 \ - SUBTARGET_AVR \ - SUBTARGET_M68K \ - SUBTARGET_CSKY \ - SUBTARGET_MIPS \ - SUBTARGET_PPC \ - SUBTARGET_SYSTEMZ \ - SUBTARGET_MSP430 \ - SUBTARGET_SPARC \ - SUBTARGET_HEXAGON \ - SUBTARGET_XTENSA \ - SUBTARGET_RISCV \ - SUBTARGET_LOONGARCH - -#define SUBTARGET(x) \ - namespace llvm { \ - extern const SubtargetFeatureKV x##FeatureKV[]; \ - extern const SubtargetFeatureKV x##SubTypeKV[]; \ - } - -GEN_SUBTARGETS -#undef SUBTARGET - // This struct and various functions are sort of a hack right now, but the // problem is that we've got in-memory LLVM modules after we generate and // optimize all codegen-units for one compilation in rustc. To be compatible @@ -340,14 +224,6 @@ static FloatABI::ABIType fromRust(LLVMRustFloatABI RustFloatAbi) { report_fatal_error("Bad FloatABI."); } -/// getLongestEntryLength - Return the length of the longest entry in the table. -template <typename KV> static size_t getLongestEntryLength(ArrayRef<KV> Table) { - size_t MaxLen = 0; - for (auto &I : Table) - MaxLen = std::max(MaxLen, std::strlen(I.Key)); - return MaxLen; -} - extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, RustStringRef OutStr) { ArrayRef<SubtargetSubTypeKV> CPUTable = @@ -395,8 +271,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool TrapUnreachable, bool Singlethread, bool VerboseAsm, bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray, const char *SplitDwarfFile, const char *OutputObjFile, - const char *DebugInfoCompression, bool UseEmulatedTls, - const char *ArgsCstrBuff, size_t ArgsCstrBuffLen, bool UseWasmEH) { + const char *DebugInfoCompression, bool UseEmulatedTls, const char *Argv0, + size_t Argv0Len, const char *CommandLineArgs, size_t CommandLineArgsLen, + bool UseWasmEH) { auto OptLevel = fromRust(RustOptLevel); auto RM = fromRust(RustReloc); @@ -467,53 +344,10 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.EmitStackSizeSection = EmitStackSizeSection; - if (ArgsCstrBuff != nullptr) { -#if LLVM_VERSION_GE(20, 0) - size_t buffer_offset = 0; - assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); - auto Arg0 = std::string(ArgsCstrBuff); - buffer_offset = Arg0.size() + 1; - - std::string CommandlineArgs; - raw_string_ostream OS(CommandlineArgs); - ListSeparator LS(" "); - for (StringRef Arg : split(StringRef(ArgsCstrBuff + buffer_offset, - ArgsCstrBuffLen - buffer_offset), - '\0')) { - OS << LS; - sys::printArg(OS, Arg, /*Quote=*/true); - } - OS.flush(); - Options.MCOptions.Argv0 = Arg0; - Options.MCOptions.CommandlineArgs = CommandlineArgs; -#else - size_t buffer_offset = 0; - assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); - - const size_t arg0_len = std::strlen(ArgsCstrBuff); - char *arg0 = new char[arg0_len + 1]; - memcpy(arg0, ArgsCstrBuff, arg0_len); - arg0[arg0_len] = '\0'; - buffer_offset += arg0_len + 1; - - const size_t num_cmd_arg_strings = std::count( - &ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0'); - - std::string *cmd_arg_strings = new std::string[num_cmd_arg_strings]; - for (size_t i = 0; i < num_cmd_arg_strings; ++i) { - assert(buffer_offset < ArgsCstrBuffLen); - const size_t len = std::strlen(ArgsCstrBuff + buffer_offset); - cmd_arg_strings[i] = std::string(&ArgsCstrBuff[buffer_offset], len); - buffer_offset += len + 1; - } - - assert(buffer_offset == ArgsCstrBuffLen); - - Options.MCOptions.Argv0 = arg0; - Options.MCOptions.CommandLineArgs = - llvm::ArrayRef<std::string>(cmd_arg_strings, num_cmd_arg_strings); -#endif - } + if (Argv0 != nullptr) + Options.MCOptions.Argv0 = {Argv0, Argv0Len}; + if (CommandLineArgs != nullptr) + Options.MCOptions.CommandlineArgs = {CommandLineArgs, CommandLineArgsLen}; #if LLVM_VERSION_GE(21, 0) TargetMachine *TM = TheTarget->createTargetMachine(Trip, CPU, Feature, @@ -525,16 +359,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( return wrap(TM); } -extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { -#if LLVM_VERSION_LT(20, 0) - MCTargetOptions &MCOptions = unwrap(TM)->Options.MCOptions; - delete[] MCOptions.Argv0; - delete[] MCOptions.CommandLineArgs.data(); -#endif - - delete unwrap(TM); -} - // Unfortunately, the LLVM C API doesn't provide a way to create the // TargetLibraryInfo pass, so we use this method to do so. extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M, @@ -745,25 +569,43 @@ extern "C" LLVMRustResult LLVMRustOptimize( } std::optional<PGOOptions> PGOOpt; +#if LLVM_VERSION_LT(22, 0) auto FS = vfs::getRealFileSystem(); +#endif if (PGOGenPath) { assert(!PGOUsePath && !PGOSampleUsePath); PGOOpt = PGOOptions( +#if LLVM_VERSION_GE(22, 0) + PGOGenPath, "", "", "", PGOOptions::IRInstr, PGOOptions::NoCSAction, +#else PGOGenPath, "", "", "", FS, PGOOptions::IRInstr, PGOOptions::NoCSAction, +#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOUsePath) { assert(!PGOSampleUsePath); PGOOpt = PGOOptions( +#if LLVM_VERSION_GE(22, 0) + PGOUsePath, "", "", "", PGOOptions::IRUse, PGOOptions::NoCSAction, +#else PGOUsePath, "", "", "", FS, PGOOptions::IRUse, PGOOptions::NoCSAction, +#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (PGOSampleUsePath) { PGOOpt = +#if LLVM_VERSION_GE(22, 0) + PGOOptions(PGOSampleUsePath, "", "", "", PGOOptions::SampleUse, +#else PGOOptions(PGOSampleUsePath, "", "", "", FS, PGOOptions::SampleUse, +#endif PGOOptions::NoCSAction, PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } else if (DebugInfoForProfiling) { PGOOpt = PGOOptions( +#if LLVM_VERSION_GE(22, 0) + "", "", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction, +#else "", "", "", "", FS, PGOOptions::NoAction, PGOOptions::NoCSAction, +#endif PGOOptions::ColdFuncOpt::Default, DebugInfoForProfiling); } @@ -812,14 +654,9 @@ extern "C" LLVMRustResult LLVMRustOptimize( // the PassBuilder does not create a pipeline. std::vector<std::function<void(ModulePassManager &, OptimizationLevel)>> PipelineStartEPCallbacks; -#if LLVM_VERSION_GE(20, 0) std::vector<std::function<void(ModulePassManager &, OptimizationLevel, ThinOrFullLTOPhase)>> OptimizerLastEPCallbacks; -#else - std::vector<std::function<void(ModulePassManager &, OptimizationLevel)>> - OptimizerLastEPCallbacks; -#endif if (!IsLinkerPluginLTO && SanitizerOptions && SanitizerOptions->SanitizeCFI && !NoPrepopulatePasses) { @@ -871,12 +708,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( SanitizerOptions->SanitizeDataFlowABIList + SanitizerOptions->SanitizeDataFlowABIListLen); OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) [ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level, ThinOrFullLTOPhase phase) { -#else - [ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level) { -#endif MPM.addPass(DataFlowSanitizerPass(ABIListFiles)); }); } @@ -887,66 +720,48 @@ extern "C" LLVMRustResult LLVMRustOptimize( SanitizerOptions->SanitizeMemoryRecover, /*CompileKernel=*/false, /*EagerChecks=*/true); - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [Options](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [Options](ModulePassManager &MPM, OptimizationLevel Level) { -#endif - MPM.addPass(MemorySanitizerPass(Options)); - }); + OptimizerLastEPCallbacks.push_back([Options](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + MPM.addPass(MemorySanitizerPass(Options)); + }); } if (SanitizerOptions->SanitizeThread) { - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [](ModulePassManager &MPM, OptimizationLevel Level) { -#endif - MPM.addPass(ModuleThreadSanitizerPass()); - MPM.addPass( - createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); - }); + OptimizerLastEPCallbacks.push_back([](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + MPM.addPass(ModuleThreadSanitizerPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); + }); } if (SanitizerOptions->SanitizeAddress || SanitizerOptions->SanitizeKernelAddress) { - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [SanitizerOptions, TM](ModulePassManager &MPM, - OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [SanitizerOptions, TM](ModulePassManager &MPM, - OptimizationLevel Level) { -#endif - auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; - AddressSanitizerOptions opts = AddressSanitizerOptions{ - CompileKernel, - SanitizerOptions->SanitizeAddressRecover || - SanitizerOptions->SanitizeKernelAddressRecover, - /*UseAfterScope=*/true, - AsanDetectStackUseAfterReturnMode::Runtime, - }; - MPM.addPass(AddressSanitizerPass( - opts, - /*UseGlobalGC*/ true, - // UseOdrIndicator should be false on windows machines - // https://reviews.llvm.org/D137227 - !TM->getTargetTriple().isOSWindows())); - }); + OptimizerLastEPCallbacks.push_back([SanitizerOptions, + TM](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; + AddressSanitizerOptions opts = AddressSanitizerOptions{ + CompileKernel, + SanitizerOptions->SanitizeAddressRecover || + SanitizerOptions->SanitizeKernelAddressRecover, + /*UseAfterScope=*/true, + AsanDetectStackUseAfterReturnMode::Runtime, + }; + MPM.addPass( + AddressSanitizerPass(opts, + /*UseGlobalGC*/ true, + // UseOdrIndicator should be false on windows + // machines https://reviews.llvm.org/D137227 + !TM->getTargetTriple().isOSWindows())); + }); } if (SanitizerOptions->SanitizeHWAddress) { OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level, ThinOrFullLTOPhase phase) { -#else - [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { -#endif HWAddressSanitizerOptions opts( /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover, @@ -1028,11 +843,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( for (const auto &C : PipelineStartEPCallbacks) C(MPM, OptLevel); for (const auto &C : OptimizerLastEPCallbacks) -#if LLVM_VERSION_GE(20, 0) C(MPM, OptLevel, ThinOrFullLTOPhase::None); -#else - C(MPM, OptLevel); -#endif } if (ExtraPassesLen) { @@ -1309,11 +1120,7 @@ struct LLVMRustThinLTOData { // Not 100% sure what these are, but they impact what's internalized and // what's inlined across modules, I believe. -#if LLVM_VERSION_GE(20, 0) FunctionImporter::ImportListsTy ImportLists; -#else - DenseMap<StringRef, FunctionImporter::ImportMapTy> ImportLists; -#endif DenseMap<StringRef, FunctionImporter::ExportSetTy> ExportLists; DenseMap<StringRef, GVSummaryMapTy> ModuleToDefinedGVSummaries; StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR; @@ -1655,13 +1462,8 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, const auto &ExportList = Data->ExportLists.lookup(ModId); const auto &ResolvedODR = Data->ResolvedODR.lookup(ModId); const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(ModId); -#if LLVM_VERSION_GE(20, 0) DenseSet<GlobalValue::GUID> CfiFunctionDefs; DenseSet<GlobalValue::GUID> CfiFunctionDecls; -#else - std::set<GlobalValue::GUID> CfiFunctionDefs; - std::set<GlobalValue::GUID> CfiFunctionDecls; -#endif // Based on the 'InProcessThinBackend' constructor in LLVM #if LLVM_VERSION_GE(21, 0) @@ -1680,15 +1482,9 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); #endif -#if LLVM_VERSION_GE(20, 0) Key = llvm::computeLTOCacheKey(conf, Data->Index, ModId, ImportList, ExportList, ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls); -#else - llvm::computeLTOCacheKey(Key, conf, Data->Index, ModId, ImportList, - ExportList, ResolvedODR, DefinedGlobals, - CfiFunctionDefs, CfiFunctionDecls); -#endif auto OS = RawRustStringOstream(KeyOut); OS << Key.str(); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 361a5f76551..4a778125918 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -553,6 +553,7 @@ enum class LLVMRustMemoryEffects { None, ReadOnly, InaccessibleMemOnly, + ReadOnlyNotPure, }; extern "C" LLVMAttributeRef @@ -568,6 +569,10 @@ LLVMRustCreateMemoryEffectsAttr(LLVMContextRef C, case LLVMRustMemoryEffects::InaccessibleMemOnly: return wrap(Attribute::getWithMemoryEffects( *unwrap(C), MemoryEffects::inaccessibleMemOnly())); + case LLVMRustMemoryEffects::ReadOnlyNotPure: + return wrap(Attribute::getWithMemoryEffects( + *unwrap(C), + MemoryEffects::readOnly() | MemoryEffects::inaccessibleMemOnly())); default: report_fatal_error("bad MemoryEffects."); } @@ -985,14 +990,6 @@ extern "C" void LLVMRustGlobalAddMetadata(LLVMValueRef Global, unsigned Kind, unwrap<GlobalObject>(Global)->addMetadata(Kind, *unwrap<MDNode>(MD)); } -extern "C" LLVMDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) { - return wrap(new DIBuilder(*unwrap(M))); -} - -extern "C" void LLVMRustDIBuilderDispose(LLVMDIBuilderRef Builder) { - delete unwrap(Builder); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit( LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef, const char *Producer, size_t ProducerLen, bool isOptimized, @@ -1026,13 +1023,6 @@ LLVMRustDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename, CSInfo, oSource)); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateSubroutineType(LLVMDIBuilderRef Builder, - LLVMMetadataRef ParameterTypes) { - return wrap(unwrap(Builder)->createSubroutineType( - DITypeRefArray(unwrap<MDTuple>(ParameterTypes)))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, @@ -1071,47 +1061,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod( return wrap(Sub); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateBasicType(LLVMDIBuilderRef Builder, const char *Name, - size_t NameLen, uint64_t SizeInBits, - unsigned Encoding) { - return wrap(unwrap(Builder)->createBasicType(StringRef(Name, NameLen), - SizeInBits, Encoding)); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Scope) { - return wrap(unwrap(Builder)->createTypedef( - unwrap<DIType>(Type), StringRef(Name, NameLen), unwrap<DIFile>(File), - LineNo, unwrapDIPtr<DIScope>(Scope))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( - LLVMDIBuilderRef Builder, LLVMMetadataRef PointeeTy, uint64_t SizeInBits, - uint32_t AlignInBits, unsigned AddressSpace, const char *Name, - size_t NameLen) { - return wrap(unwrap(Builder)->createPointerType( - unwrapDI<DIType>(PointeeTy), SizeInBits, AlignInBits, AddressSpace, - StringRef(Name, NameLen))); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, - LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, unsigned RunTimeLang, - LLVMMetadataRef VTableHolder, const char *UniqueId, size_t UniqueIdLen) { - return wrap(unwrap(Builder)->createStructType( - unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), - unwrapDI<DIFile>(File), LineNumber, SizeInBits, AlignInBits, - fromRust(Flags), unwrapDI<DIType>(DerivedFrom), - DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang, - unwrapDI<DIType>(VTableHolder), StringRef(UniqueId, UniqueIdLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, @@ -1126,17 +1075,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( StringRef(UniqueId, UniqueIdLen))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, - uint32_t AlignInBits, uint64_t OffsetInBits, LLVMDIFlags Flags, - LLVMMetadataRef Ty) { - return wrap(unwrap(Builder)->createMemberType( - unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), - unwrapDI<DIFile>(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, - fromRust(Flags), unwrapDI<DIType>(Ty))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, @@ -1152,23 +1090,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( fromRust(Flags), unwrapDI<DIType>(Ty))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, - LLVMDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) { - return wrap(unwrap(Builder)->createStaticMemberType( - unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), - unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), fromRust(Flags), - unwrap<llvm::ConstantInt>(val), llvm::dwarf::DW_TAG_member, AlignInBits)); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, - LLVMMetadataRef Type) { - return wrap( - unwrap(Builder)->createQualifiedType(Tag, unwrapDI<DIType>(Type))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, @@ -1200,60 +1121,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( return wrap(VarExpr); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( - LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Scope, - const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Ty, bool AlwaysPreserve, LLVMDIFlags Flags, unsigned ArgNo, - uint32_t AlignInBits) { - if (Tag == 0x100) { // DW_TAG_auto_variable - return wrap(unwrap(Builder)->createAutoVariable( - unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), - unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), AlwaysPreserve, - fromRust(Flags), AlignInBits)); - } else { - return wrap(unwrap(Builder)->createParameterVariable( - unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ArgNo, - unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), AlwaysPreserve, - fromRust(Flags))); - } -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateArrayType(LLVMDIBuilderRef Builder, uint64_t Size, - uint32_t AlignInBits, LLVMMetadataRef Ty, - LLVMMetadataRef Subscripts) { - return wrap(unwrap(Builder)->createArrayType( - Size, AlignInBits, unwrapDI<DIType>(Ty), - DINodeArray(unwrapDI<MDTuple>(Subscripts)))); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo, - int64_t Count) { - return wrap(unwrap(Builder)->getOrCreateSubrange(Lo, Count)); -} - -extern "C" LLVMMetadataRef -LLVMRustDIBuilderGetOrCreateArray(LLVMDIBuilderRef Builder, - LLVMMetadataRef *Ptr, unsigned Count) { - Metadata **DataValue = unwrap(Ptr); - return wrap(unwrap(Builder) - ->getOrCreateArray(ArrayRef<Metadata *>(DataValue, Count)) - .get()); -} - -extern "C" void -LLVMRustDIBuilderInsertDeclareAtEnd(LLVMDIBuilderRef Builder, LLVMValueRef V, - LLVMMetadataRef VarInfo, uint64_t *AddrOps, - unsigned AddrOpsCount, LLVMMetadataRef DL, - LLVMBasicBlockRef InsertAtEnd) { - unwrap(Builder)->insertDeclare( - unwrap(V), unwrap<DILocalVariable>(VarInfo), - unwrap(Builder)->createExpression( - llvm::ArrayRef<uint64_t>(AddrOps, AddrOpsCount)), - DebugLoc(cast<MDNode>(unwrap(DL))), unwrap(InsertAtEnd)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name, size_t NameLen, const uint64_t Value[2], @@ -1275,19 +1142,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( /* RunTimeLang */ 0, "", IsScoped)); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, - LLVMMetadataRef Elements, unsigned RunTimeLang, const char *UniqueId, - size_t UniqueIdLen) { - return wrap(unwrap(Builder)->createUnionType( - unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), - unwrapDI<DIFile>(File), LineNumber, SizeInBits, AlignInBits, - fromRust(Flags), DINodeArray(unwrapDI<MDTuple>(Elements)), RunTimeLang, - StringRef(UniqueId, UniqueIdLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef Ty) { @@ -1778,6 +1632,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( RemarkStreamer(std::move(RemarkStreamer)), LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {} +#if LLVM_VERSION_GE(22, 0) + ~RustDiagnosticHandler() { + if (RemarkStreamer) { + RemarkStreamer->releaseSerializer(); + } + } +#endif + virtual bool handleDiagnostics(const DiagnosticInfo &DI) override { // If this diagnostic is one of the optimization remark kinds, we can // check if it's enabled before emitting it. This can avoid many @@ -1872,9 +1734,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler( // Do not delete the file after we gather remarks RemarkFile->keep(); +#if LLVM_VERSION_GE(22, 0) + auto RemarkSerializer = remarks::createRemarkSerializer( + llvm::remarks::Format::YAML, RemarkFile->os()); +#else auto RemarkSerializer = remarks::createRemarkSerializer( llvm::remarks::Format::YAML, remarks::SerializerMode::Separate, RemarkFile->os()); +#endif if (Error E = RemarkSerializer.takeError()) { std::string Error = std::string("Cannot create remark serializer: ") + toString(std::move(E)); @@ -1945,3 +1812,15 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { MD.NoHWAddress = true; GV.setSanitizerMetadata(MD); } + +#ifdef ENZYME +extern "C" { +extern llvm::cl::opt<unsigned> EnzymeMaxTypeDepth; +} + +extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { return EnzymeMaxTypeDepth; } +#else +extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { + return 6; // Default fallback depth +} +#endif diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index ed5edeef161..14e94121d1c 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -180,12 +180,6 @@ pub fn initialize_available_targets() { LLVMInitializeSystemZAsmParser ); init_target!( - llvm_component = "jsbackend", - LLVMInitializeJSBackendTargetInfo, - LLVMInitializeJSBackendTarget, - LLVMInitializeJSBackendTargetMC - ); - init_target!( llvm_component = "msp430", LLVMInitializeMSP430TargetInfo, LLVMInitializeMSP430Target, diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 5821ffa3a30..5d32950875a 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -5,7 +5,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{ AttrStyle, Attribute, Block, Error, Expr, Ident, Pat, ReturnType, Token, Type, braced, - parenthesized, parse_macro_input, parse_quote, token, + parenthesized, parse_macro_input, token, }; mod kw { diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index e104be2c466..e624bfc5b8b 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -98,6 +98,13 @@ metadata_full_metadata_not_found = metadata_global_alloc_required = no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait +metadata_incompatible_with_immediate_abort = + the crate `{$crate_name}` was compiled with a panic strategy which is incompatible with `immediate-abort` + +metadata_incompatible_with_immediate_abort_core = + the crate `core` was compiled with a panic strategy which is incompatible with `immediate-abort` + .help = consider building the standard library from source with `cargo build -Zbuild-std` + metadata_incompatible_panic_in_drop_strategy = the crate `{$crate_name}` is compiled with the panic-in-drop strategy `{$found_strategy}` which is incompatible with this crate's strategy of `{$desired_strategy}` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 9e23da88f5e..7650acbd292 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -1027,6 +1027,10 @@ impl CStore { let name = match desired_strategy { PanicStrategy::Unwind => sym::panic_unwind, PanicStrategy::Abort => sym::panic_abort, + PanicStrategy::ImmediateAbort => { + // Immediate-aborting panics don't use a runtime. + return; + } }; info!("panic runtime not found -- loading {}", name); diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index fb9c2e23b71..8054a48d37a 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -61,11 +61,13 @@ use rustc_session::config::CrateType; use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; use rustc_span::sym; +use rustc_target::spec::PanicStrategy; use tracing::info; use crate::creader::CStore; use crate::errors::{ - BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, LibRequired, + BadPanicStrategy, CrateDepMultiple, IncompatiblePanicInDropStrategy, + IncompatibleWithImmediateAbort, IncompatibleWithImmediateAbortCore, LibRequired, NonStaticCrateDep, RequiredPanicStrategy, RlibRequired, RustcDriverHelp, RustcLibRequired, TwoPanicRuntimes, }; @@ -402,15 +404,43 @@ fn activate_injected_dep( /// there's only going to be one panic runtime in the output. fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { let sess = &tcx.sess; + let list: Vec<_> = list + .iter_enumerated() + .filter_map( + |(cnum, linkage)| if *linkage == Linkage::NotLinked { None } else { Some(cnum) }, + ) + .collect(); if list.is_empty() { return; } - let mut panic_runtime = None; - for (cnum, linkage) in list.iter_enumerated() { - if let Linkage::NotLinked = *linkage { - continue; + let desired_strategy = sess.panic_strategy(); + + // If we are panic=immediate-abort, make sure everything in the dependency tree has also been + // compiled with immediate-abort. + if list + .iter() + .any(|cnum| tcx.required_panic_strategy(*cnum) == Some(PanicStrategy::ImmediateAbort)) + { + let mut invalid_crates = Vec::new(); + for cnum in list.iter().copied() { + if tcx.required_panic_strategy(cnum) != Some(PanicStrategy::ImmediateAbort) { + invalid_crates.push(cnum); + // If core is incompatible, it's very likely that we'd emit an error for every + // sysroot crate, so instead of doing that emit a single fatal error that suggests + // using build-std. + if tcx.crate_name(cnum) == sym::core { + sess.dcx().emit_fatal(IncompatibleWithImmediateAbortCore); + } + } } + for cnum in invalid_crates { + sess.dcx() + .emit_err(IncompatibleWithImmediateAbort { crate_name: tcx.crate_name(cnum) }); + } + } + let mut panic_runtime = None; + for cnum in list.iter().copied() { if tcx.is_panic_runtime(cnum) { if let Some((prev, _)) = panic_runtime { let prev_name = tcx.crate_name(prev); @@ -430,8 +460,6 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { // only one, but we perform validation here that all the panic strategy // compilation modes for the whole DAG are valid. if let Some((runtime_cnum, found_strategy)) = panic_runtime { - let desired_strategy = sess.panic_strategy(); - // First up, validate that our selected panic runtime is indeed exactly // our same strategy. if found_strategy != desired_strategy { @@ -445,10 +473,7 @@ fn verify_ok(tcx: TyCtxt<'_>, list: &DependencyList) { // strategy. If the dep isn't linked, we ignore it, and if our strategy // is abort then it's compatible with everything. Otherwise all crates' // panic strategy must match our own. - for (cnum, linkage) in list.iter_enumerated() { - if let Linkage::NotLinked = *linkage { - continue; - } + for cnum in list.iter().copied() { if cnum == runtime_cnum || tcx.is_compiler_builtins(cnum) { continue; } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index e5a4fd48353..abfd078f746 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -76,6 +76,16 @@ pub struct RequiredPanicStrategy { } #[derive(Diagnostic)] +#[diag(metadata_incompatible_with_immediate_abort)] +pub struct IncompatibleWithImmediateAbort { + pub crate_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(metadata_incompatible_with_immediate_abort_core)] +pub struct IncompatibleWithImmediateAbortCore; + +#[derive(Diagnostic)] #[diag(metadata_incompatible_panic_in_drop_strategy)] pub struct IncompatiblePanicInDropStrategy { pub crate_name: Symbol, diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 110b26c62ef..b895feb9062 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1194,10 +1194,6 @@ impl<'a> CrateMetadataRef<'a> { self.root.tables.default_fields.get(self, id).map(|d| d.decode(self)) } - fn get_trait_item_def_id(self, id: DefIndex) -> Option<DefId> { - self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self)) - } - fn get_expn_that_defined(self, id: DefIndex, sess: &Session) -> ExpnId { self.root .tables @@ -1359,14 +1355,9 @@ impl<'a> CrateMetadataRef<'a> { } _ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)), }; - let container = self.root.tables.assoc_container.get(self, id).unwrap(); + let container = self.root.tables.assoc_container.get(self, id).unwrap().decode(self); - ty::AssocItem { - kind, - def_id: self.local_def_id(id), - trait_item_def_id: self.get_trait_item_def_id(id), - container, - } + ty::AssocItem { kind, def_id: self.local_def_id(id), container } } fn get_ctor(self, node_id: DefIndex) -> Option<(CtorKind, DefId)> { @@ -1564,7 +1555,7 @@ impl<'a> CrateMetadataRef<'a> { } #[inline] - fn def_path_hash_to_def_index(self, hash: DefPathHash) -> DefIndex { + fn def_path_hash_to_def_index(self, hash: DefPathHash) -> Option<DefIndex> { self.def_path_hash_map.def_path_hash_to_def_index(&hash) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 11fef3be5d0..df3add316ec 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -691,8 +691,8 @@ fn provide_cstore_hooks(providers: &mut Providers) { .get(&stable_crate_id) .unwrap_or_else(|| bug!("uninterned StableCrateId: {stable_crate_id:?}")); assert_ne!(cnum, LOCAL_CRATE); - let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash); - DefId { krate: cnum, index: def_index } + let def_index = cstore.get_crate_data(cnum).def_path_hash_to_def_index(hash)?; + Some(DefId { krate: cnum, index: def_index }) }; providers.hooks.expn_hash_to_expn_id = |tcx, cnum, index_guess, hash| { diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index f3917b55782..a17b3e1047d 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -12,11 +12,12 @@ pub(crate) enum DefPathHashMapRef<'tcx> { impl DefPathHashMapRef<'_> { #[inline] - pub(crate) fn def_path_hash_to_def_index(&self, def_path_hash: &DefPathHash) -> DefIndex { + pub(crate) fn def_path_hash_to_def_index( + &self, + def_path_hash: &DefPathHash, + ) -> Option<DefIndex> { match *self { - DefPathHashMapRef::OwnedFromMetadata(ref map) => { - map.get(&def_path_hash.local_hash()).unwrap() - } + DefPathHashMapRef::OwnedFromMetadata(ref map) => map.get(&def_path_hash.local_hash()), DefPathHashMapRef::BorrowedFromTcx(_) => { panic!("DefPathHashMap::BorrowedFromTcx variant only exists for serialization") } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index a7e7e9985f4..db66938457f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -22,7 +22,7 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; -use rustc_middle::ty::AssocItemContainer; +use rustc_middle::ty::AssocContainer; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, TreatParams}; use rustc_middle::{bug, span_bug}; @@ -1254,8 +1254,8 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> DefKind::AssocTy => { let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocItemContainer::Impl => true, - ty::AssocItemContainer::Trait => assoc_item.defaultness(tcx).has_value(), + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, + ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), } } DefKind::TyParam => { @@ -1725,24 +1725,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let item = tcx.associated_item(def_id); - self.tables.defaultness.set_some(def_id.index, item.defaultness(tcx)); - self.tables.assoc_container.set_some(def_id.index, item.container); - - match item.container { - AssocItemContainer::Trait => { - if item.is_type() { - self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_self_bounds(def_id); - if tcx.is_conditionally_const(def_id) { - record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] - <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); - } - } - } - AssocItemContainer::Impl => { - if let Some(trait_item_def_id) = item.trait_item_def_id { - self.tables.trait_item_def_id.set_some(def_id.index, trait_item_def_id.into()); - } + if matches!(item.container, AssocContainer::Trait | AssocContainer::TraitImpl(_)) { + self.tables.defaultness.set_some(def_id.index, item.defaultness(tcx)); + } + + record!(self.tables.assoc_container[def_id] <- item.container); + + if let AssocContainer::Trait = item.container + && item.is_type() + { + self.encode_explicit_item_bounds(def_id); + self.encode_explicit_item_self_bounds(def_id); + if tcx.is_conditionally_const(def_id) { + record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] + <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } if let ty::AssocKind::Type { data: ty::AssocTypeData::Rpitit(rpitit_info) } = item.kind { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 1f7d142d330..720970bbaf9 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -447,7 +447,6 @@ define_tables! { coroutine_by_move_body_def_id: Table<DefIndex, RawDefId>, eval_static_initializer: Table<DefIndex, LazyValue<mir::interpret::ConstAllocation<'static>>>, trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>, - trait_item_def_id: Table<DefIndex, RawDefId>, expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>, default_fields: Table<DefIndex, LazyValue<DefId>>, params_in_repr: Table<DefIndex, LazyValue<DenseBitSet<u32>>>, @@ -459,7 +458,7 @@ define_tables! { def_keys: Table<DefIndex, LazyValue<DefKey>>, proc_macro_quoted_spans: Table<usize, LazyValue<Span>>, variant_data: Table<DefIndex, LazyValue<VariantData>>, - assoc_container: Table<DefIndex, ty::AssocItemContainer>, + assoc_container: Table<DefIndex, LazyValue<ty::AssocContainer>>, macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>, proc_macro: Table<DefIndex, MacroKind>, deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>, diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index 34180001f80..4b2dc2c814e 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -102,7 +102,7 @@ trivially_parameterized_over_tcx! { rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault, rustc_middle::mir::ConstQualifs, rustc_middle::ty::AnonConstKind, - rustc_middle::ty::AssocItemContainer, + rustc_middle::ty::AssocContainer, rustc_middle::ty::AsyncDestructor, rustc_middle::ty::Asyncness, rustc_middle::ty::DeducedParamAttrs, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 2cb07a28a8a..a882ee4f2b9 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -222,13 +222,6 @@ fixed_size_enum! { } fixed_size_enum! { - ty::AssocItemContainer { - ( Trait ) - ( Impl ) - } -} - -fixed_size_enum! { MacroKind { ( Attr ) ( Bang ) diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 69adb2fe391..82f3df8da3b 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -84,7 +84,7 @@ middle_failed_writing_file = # Note: We only mention patterns here since the error can only occur with references, and those # are forbidden in const generics. middle_invalid_const_in_valtree = constant {$global_const_id} cannot be used as pattern - .note = constants that reference mutable or external memory cannot be used as pattern + .note = constants that reference mutable or external memory cannot be used as patterns middle_layout_cycle = a cycle occurred during layout computation @@ -98,6 +98,12 @@ middle_layout_references_error = middle_layout_size_overflow = values of the type `{$ty}` are too big for the target architecture +middle_layout_simd_too_many = + the SIMD type `{$ty}` has more elements than the limit {$max_lanes} + +middle_layout_simd_zero_length = + the SIMD type `{$ty}` has zero elements + middle_layout_too_generic = the type `{$ty}` does not have a fixed layout middle_layout_unknown = diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 52fbe19c9f2..feaad5bb96e 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -27,7 +27,7 @@ macro_rules! arena_types { rustc_middle::mir::Body<'tcx> >, [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, - [decode] borrowck_result: rustc_middle::mir::ConcreteOpaqueTypes<'tcx>, + [decode] borrowck_result: rustc_middle::mir::DefinitionSiteHiddenTypes<'tcx>, [] resolver: rustc_data_structures::steal::Steal<( rustc_middle::ty::ResolverAstLowering, std::sync::Arc<rustc_ast::Crate>, @@ -109,7 +109,6 @@ macro_rules! arena_types { rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>> >, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<rustc_middle::ty::TyCtxt<'tcx>>, - [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<rustc_middle::ty::TyCtxt<'tcx>>, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [] stripped_cfg_items: rustc_hir::attrs::StrippedCfgItem, [] mod_child: rustc_middle::metadata::ModChild, diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index dad402ec696..3ff9eea8cc4 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -37,7 +37,6 @@ pub(crate) struct OpaqueHiddenTypeMismatch<'tcx> { pub sub: TypeMismatchReason, } -// FIXME(autodiff): I should get used somewhere #[derive(Diagnostic)] #[diag(middle_unsupported_union)] pub struct UnsupportedUnion { @@ -71,6 +70,8 @@ pub enum TypeMismatchReason { #[diag(middle_recursion_limit_reached)] #[help] pub(crate) struct RecursionLimitReached<'tcx> { + #[primary_span] + pub span: Span, pub ty: Ty<'tcx>, pub suggested_limit: rustc_hir::limit::Limit, } @@ -141,6 +142,12 @@ pub enum LayoutError<'tcx> { #[diag(middle_layout_size_overflow)] Overflow { ty: Ty<'tcx> }, + #[diag(middle_layout_simd_too_many)] + SimdTooManyLanes { ty: Ty<'tcx>, max_lanes: u64 }, + + #[diag(middle_layout_simd_zero_length)] + SimdZeroLength { ty: Ty<'tcx> }, + #[diag(middle_layout_normalization_failure)] NormalizationFailure { ty: Ty<'tcx>, failure_ty: String }, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 4370816d38e..430cd329408 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -370,7 +370,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn hir_rustc_coherence_is_core(self) -> bool { - find_attr!(self.hir_krate_attrs(), AttributeKind::CoherenceIsCore) + find_attr!(self.hir_krate_attrs(), AttributeKind::RustcCoherenceIsCore(..)) } pub fn hir_get_module(self, module: LocalModDefId) -> (&'tcx Mod<'tcx>, Span, HirId) { diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 67bc89692ff..9e3162785f4 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -18,7 +18,7 @@ use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, Span}; use crate::query::Providers; -use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; +use crate::ty::TyCtxt; /// Gather the LocalDefId for each item-like within a module, including items contained within /// bodies. The Ids are in visitor order. This is used to partition a pass between modules. @@ -154,13 +154,6 @@ impl<'tcx> TyCtxt<'tcx> { LocalModDefId::new_unchecked(id) } - pub fn impl_subject(self, def_id: DefId) -> EarlyBinder<'tcx, ImplSubject<'tcx>> { - match self.impl_trait_ref(def_id) { - Some(t) => t.map_bound(ImplSubject::Trait), - None => self.type_of(def_id).map_bound(ImplSubject::Inherent), - } - } - /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). pub fn is_foreign_item(self, def_id: impl Into<DefId>) -> bool { self.opt_parent(def_id.into()) diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 9d2f0a45237..dc6a3334a4c 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -77,7 +77,7 @@ declare_hooks! { /// session, if it still exists. This is used during incremental compilation to /// turn a deserialized `DefPathHash` into its current `DefId`. /// Will fetch a DefId from a DefPathHash for a foreign crate. - hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> DefId; + hook def_path_hash_to_def_id_extern(hash: DefPathHash, stable_crate_id: StableCrateId) -> Option<DefId>; /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 5023b2740ef..754a258eef9 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -29,7 +29,6 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::direct_use_of_rustc_type_ir)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(bootstrap, feature(round_char_boundary))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(allocator_api)] diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 8b4503073b0..f0d96c6ac89 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -69,6 +69,10 @@ pub struct CodegenFnAttrs { /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. pub patchable_function_entry: Option<PatchableFunctionEntry>, + /// The `#[rustc_objc_class = "..."]` attribute. + pub objc_class: Option<Symbol>, + /// The `#[rustc_objc_selector = "..."]` attribute. + pub objc_selector: Option<Symbol>, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)] @@ -185,6 +189,8 @@ impl CodegenFnAttrs { instruction_set: None, alignment: None, patchable_function_entry: None, + objc_class: None, + objc_selector: None, } } diff --git a/compiler/rustc_middle/src/middle/debugger_visualizer.rs b/compiler/rustc_middle/src/middle/debugger_visualizer.rs index 5a811321f58..a7f0095dcdf 100644 --- a/compiler/rustc_middle/src/middle/debugger_visualizer.rs +++ b/compiler/rustc_middle/src/middle/debugger_visualizer.rs @@ -1,15 +1,9 @@ use std::path::PathBuf; use std::sync::Arc; +use rustc_hir::attrs::DebuggerVisualizerType; use rustc_macros::{Decodable, Encodable, HashStable}; -#[derive(HashStable)] -#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] -pub enum DebuggerVisualizerType { - Natvis, - GdbPrettyPrinter, -} - /// A single debugger visualizer file. #[derive(HashStable)] #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)] diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs index 93264f02cc2..a0e4c288c4a 100644 --- a/compiler/rustc_middle/src/middle/lang_items.rs +++ b/compiler/rustc_middle/src/middle/lang_items.rs @@ -98,5 +98,6 @@ pub fn required(tcx: TyCtxt<'_>, lang_item: LangItem) -> bool { lang_item != LangItem::EhPersonality && lang_item != LangItem::EhCatchTypeinfo } PanicStrategy::Unwind => true, + PanicStrategy::ImmediateAbort => false, } } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 67962813ae4..8e603ce1b91 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -849,8 +849,13 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> /// /// This is dangerous to use as it can violate internal `Allocation` invariants! /// It only exists to support an efficient implementation of `mem_copy_repeatedly`. - pub fn provenance_apply_copy(&mut self, copy: ProvenanceCopy<Prov>) { - self.provenance.apply_copy(copy) + pub fn provenance_apply_copy( + &mut self, + copy: ProvenanceCopy<Prov>, + range: AllocRange, + repeat: u64, + ) { + self.provenance.apply_copy(copy, range, repeat) } /// Applies a previously prepared copy of the init mask. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 720e58d7aa0..67baf63bbfa 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -278,90 +278,78 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { /// A partial, owned list of provenance to transfer into another allocation. /// -/// Offsets are already adjusted to the destination allocation. +/// Offsets are relative to the beginning of the copied range. pub struct ProvenanceCopy<Prov> { - dest_ptrs: Option<Box<[(Size, Prov)]>>, - dest_bytes: Option<Box<[(Size, (Prov, u8))]>>, + ptrs: Box<[(Size, Prov)]>, + bytes: Box<[(Size, (Prov, u8))]>, } impl<Prov: Provenance> ProvenanceMap<Prov> { pub fn prepare_copy( &self, - src: AllocRange, - dest: Size, - count: u64, + range: AllocRange, cx: &impl HasDataLayout, ) -> AllocResult<ProvenanceCopy<Prov>> { - let shift_offset = move |idx, offset| { - // compute offset for current repetition - let dest_offset = dest + src.size * idx; // `Size` operations - // shift offsets from source allocation to destination allocation - (offset - src.start) + dest_offset // `Size` operations - }; + let shift_offset = move |offset| offset - range.start; let ptr_size = cx.data_layout().pointer_size(); // # Pointer-sized provenances // Get the provenances that are entirely within this range. // (Different from `range_get_ptrs` which asks if they overlap the range.) // Only makes sense if we are copying at least one pointer worth of bytes. - let mut dest_ptrs_box = None; - if src.size >= ptr_size { - let adjusted_end = Size::from_bytes(src.end().bytes() - (ptr_size.bytes() - 1)); - let ptrs = self.ptrs.range(src.start..adjusted_end); - // If `count` is large, this is rather wasteful -- we are allocating a big array here, which - // is mostly filled with redundant information since it's just N copies of the same `Prov`s - // at slightly adjusted offsets. The reason we do this is so that in `mark_provenance_range` - // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces - // the right sequence of provenance for all N copies. - // Basically, this large array would have to be created anyway in the target allocation. - let mut dest_ptrs = Vec::with_capacity(ptrs.len() * (count as usize)); - for i in 0..count { - dest_ptrs - .extend(ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc))); - } - debug_assert_eq!(dest_ptrs.len(), dest_ptrs.capacity()); - dest_ptrs_box = Some(dest_ptrs.into_boxed_slice()); + let mut ptrs_box: Box<[_]> = Box::new([]); + if range.size >= ptr_size { + let adjusted_end = Size::from_bytes(range.end().bytes() - (ptr_size.bytes() - 1)); + let ptrs = self.ptrs.range(range.start..adjusted_end); + ptrs_box = ptrs.iter().map(|&(offset, reloc)| (shift_offset(offset), reloc)).collect(); }; // # Byte-sized provenances // This includes the existing bytewise provenance in the range, and ptr provenance // that overlaps with the begin/end of the range. - let mut dest_bytes_box = None; - let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); - let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); + let mut bytes_box: Box<[_]> = Box::new([]); + let begin_overlap = self.range_ptrs_get(alloc_range(range.start, Size::ZERO), cx).first(); + let end_overlap = self.range_ptrs_get(alloc_range(range.end(), Size::ZERO), cx).first(); // We only need to go here if there is some overlap or some bytewise provenance. if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); // First, if there is a part of a pointer at the start, add that. if let Some(entry) = begin_overlap { trace!("start overlapping entry: {entry:?}"); - // For really small copies, make sure we don't run off the end of the `src` range. - let entry_end = cmp::min(entry.0 + ptr_size, src.end()); - for offset in src.start..entry_end { - bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); + // For really small copies, make sure we don't run off the end of the range. + let entry_end = cmp::min(entry.0 + ptr_size, range.end()); + for offset in range.start..entry_end { + bytes.push((shift_offset(offset), (entry.1, (offset - entry.0).bytes() as u8))); } } else { trace!("no start overlapping entry"); } // Then the main part, bytewise provenance from `self.bytes`. - bytes.extend(self.range_bytes_get(src)); + bytes.extend( + self.range_bytes_get(range) + .iter() + .map(|&(offset, reloc)| (shift_offset(offset), reloc)), + ); // And finally possibly parts of a pointer at the end. if let Some(entry) = end_overlap { trace!("end overlapping entry: {entry:?}"); - // For really small copies, make sure we don't start before `src` does. - let entry_start = cmp::max(entry.0, src.start); - for offset in entry_start..src.end() { + // For really small copies, make sure we don't start before `range` does. + let entry_start = cmp::max(entry.0, range.start); + for offset in entry_start..range.end() { if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { // The last entry, if it exists, has a lower offset than us, so we // can add it at the end and remain sorted. - bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); + bytes.push(( + shift_offset(offset), + (entry.1, (offset - entry.0).bytes() as u8), + )); } else { // There already is an entry for this offset in there! This can happen when the // start and end range checks actually end up hitting the same pointer, so we // already added this in the "pointer at the start" part above. - assert!(entry.0 <= src.start); + assert!(entry.0 <= range.start); } } } else { @@ -372,33 +360,40 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR { // FIXME(#146291): We need to ensure that we don't mix different pointers with // the same provenance. - return Err(AllocError::ReadPartialPointer(src.start)); + return Err(AllocError::ReadPartialPointer(range.start)); } // And again a buffer for the new list on the target side. - let mut dest_bytes = Vec::with_capacity(bytes.len() * (count as usize)); - for i in 0..count { - dest_bytes - .extend(bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc))); - } - debug_assert_eq!(dest_bytes.len(), dest_bytes.capacity()); - dest_bytes_box = Some(dest_bytes.into_boxed_slice()); + bytes_box = bytes.into_boxed_slice(); } - Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) + Ok(ProvenanceCopy { ptrs: ptrs_box, bytes: bytes_box }) } /// Applies a provenance copy. /// The affected range, as defined in the parameters to `prepare_copy` is expected /// to be clear of provenance. - pub fn apply_copy(&mut self, copy: ProvenanceCopy<Prov>) { - if let Some(dest_ptrs) = copy.dest_ptrs { - self.ptrs.insert_presorted(dest_ptrs.into()); + pub fn apply_copy(&mut self, copy: ProvenanceCopy<Prov>, range: AllocRange, repeat: u64) { + let shift_offset = |idx: u64, offset: Size| offset + range.start + idx * range.size; + if !copy.ptrs.is_empty() { + // We want to call `insert_presorted` only once so that, if possible, the entries + // after the range we insert are moved back only once. + let chunk_len = copy.ptrs.len() as u64; + self.ptrs.insert_presorted((0..chunk_len * repeat).map(|i| { + let chunk = i / chunk_len; + let (offset, reloc) = copy.ptrs[(i % chunk_len) as usize]; + (shift_offset(chunk, offset), reloc) + })); } - if let Some(dest_bytes) = copy.dest_bytes - && !dest_bytes.is_empty() - { - self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); + if !copy.bytes.is_empty() { + let chunk_len = copy.bytes.len() as u64; + self.bytes.get_or_insert_with(Box::default).insert_presorted( + (0..chunk_len * repeat).map(|i| { + let chunk = i / chunk_len; + let (offset, reloc) = copy.bytes[(i % chunk_len) as usize]; + (shift_offset(chunk, offset), reloc) + }), + ); } } } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 7c72a8ec243..951aac503fe 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -499,10 +499,7 @@ pub enum ValidationErrorKind<'tcx> { MutableRefInConst, NullFnPtr, NeverVal, - NullablePtrOutOfRange { - range: WrappingRange, - max_value: u128, - }, + NonnullPtrMaybeNull, PtrOutOfRange { range: WrappingRange, max_value: u128, @@ -544,6 +541,8 @@ pub enum ValidationErrorKind<'tcx> { }, NullPtr { ptr_kind: PointerKind, + /// Records whether this pointer is definitely null or just may be null. + maybe: bool, }, DanglingPtrNoProvenance { ptr_kind: PointerKind, diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index da2245b12d2..28142382b13 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -471,7 +471,7 @@ impl<'tcx> Body<'tcx> { /// Returns an iterator over all function arguments. #[inline] - pub fn args_iter(&self) -> impl Iterator<Item = Local> + ExactSizeIterator { + pub fn args_iter(&self) -> impl Iterator<Item = Local> + ExactSizeIterator + use<> { (1..self.arg_count + 1).map(Local::new) } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index f9d0a5f0a3b..96148fd5b92 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1062,7 +1062,6 @@ impl<'tcx> Debug for Rvalue<'tcx> { pretty_print_const(b, fmt, false)?; write!(fmt, "]") } - Len(ref a) => write!(fmt, "Len({a:?})"), Cast(ref kind, ref place, ref ty) => { with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})")) } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 466b9c7a3c2..2e6c9f207e2 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -17,7 +17,7 @@ use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty}; rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] - #[debug_format = "_{}"] + #[debug_format = "_s{}"] pub struct CoroutineSavedLocal {} } @@ -84,11 +84,10 @@ impl Debug for CoroutineLayout<'_> { } } -/// All the opaque types that are restricted to concrete types -/// by this function. Unlike the value in `TypeckResults`, this has -/// unerased regions. +/// All the opaque types that have had their hidden type fully computed. +/// Unlike the value in `TypeckResults`, this has unerased regions. #[derive(Default, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct ConcreteOpaqueTypes<'tcx>(pub FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>); +pub struct DefinitionSiteHiddenTypes<'tcx>(pub FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>); /// The result of the `mir_const_qualif` query. /// diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index ec2a8e86077..28294b47e90 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -697,7 +697,6 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::Ref(_, _, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) - | Rvalue::Len(_) | Rvalue::Cast( CastKind::IntToInt | CastKind::FloatToInt @@ -739,7 +738,6 @@ impl<'tcx> Rvalue<'tcx> { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) } - Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs_ty = lhs.ty(local_decls, tcx); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 3b8def67f92..e6c8512564e 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -327,9 +327,11 @@ pub enum StatementKind<'tcx> { /// interesting for optimizations? Do we want to allow such optimizations? /// /// **Needs clarification**: We currently require that the LHS place not overlap with any place - /// read as part of computation of the RHS for some rvalues (generally those not producing - /// primitives). This requirement is under discussion in [#68364]. As a part of this discussion, - /// it is also unclear in what order the components are evaluated. + /// read as part of computation of the RHS for some rvalues. This requirement is under + /// discussion in [#68364]. Specifically, overlap is permitted only for assignments of a type + /// with `BackendRepr::Scalar | BackendRepr::ScalarPair` where all the scalar fields are + /// [`Scalar::Initialized`][rustc_abi::Scalar::Initialized]. As a part of this discussion, it is + /// also unclear in what order the components are evaluated. /// /// [#68364]: https://github.com/rust-lang/rust/issues/68364 /// @@ -1405,16 +1407,6 @@ pub enum Rvalue<'tcx> { /// model. RawPtr(RawPtrKind, Place<'tcx>), - /// Yields the length of the place, as a `usize`. - /// - /// If the type of the place is an array, this is the array length. For slices (`[T]`, not - /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is - /// ill-formed for places of other types. - /// - /// This cannot be a `UnOp(PtrMetadata, _)` because that expects a value, and we only - /// have a place, and `UnOp(PtrMetadata, RawPtr(place))` is not a thing. - Len(Place<'tcx>), - /// Performs essentially all of the casts that can be performed via `as`. /// /// This allows for casts from/to a variety of types. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index b498b7b8912..81df239dee4 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -717,14 +717,6 @@ macro_rules! make_mir_visitor { self.visit_place(path, ctx, location); } - Rvalue::Len(path) => { - self.visit_place( - path, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), - location - ); - } - Rvalue::Cast(_cast_kind, operand, ty) => { self.visit_operand(operand, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); @@ -1483,3 +1475,20 @@ impl PlaceContext { } } } + +/// Small utility to visit places and locals without manually implementing a full visitor. +pub struct VisitPlacesWith<F>(pub F); + +impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F> +where + F: FnMut(Place<'tcx>, PlaceContext), +{ + fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { + (self.0)(local.into(), ctxt); + } + + fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { + (self.0)(*place, ctxt); + self.visit_projection(place.as_ref(), ctxt, location); + } +} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index bea2191c560..4c00b769237 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -318,7 +318,7 @@ trivial! { rustc_middle::traits::WellFormedLoc, rustc_middle::ty::adjustment::CoerceUnsizedInfo, rustc_middle::ty::AssocItem, - rustc_middle::ty::AssocItemContainer, + rustc_middle::ty::AssocContainer, rustc_middle::ty::Asyncness, rustc_middle::ty::AsyncDestructor, rustc_middle::ty::BoundVariableKind, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 2aa32dfa0d8..895c8c0295a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -124,7 +124,7 @@ use crate::query::plumbing::{ }; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, - CanonicalPredicateGoal, CanonicalTyGoal, CanonicalTypeOpAscribeUserTypeGoal, + CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpNormalizeGoal, CanonicalTypeOpProvePredicateGoal, DropckConstraint, DropckOutlivesResult, MethodAutoderefStepsResult, NoSolution, NormalizationResult, OutlivesBound, @@ -1244,7 +1244,7 @@ rustc_queries! { /// Borrow-checks the given typeck root, e.g. functions, const/static items, /// and its children, e.g. closures, inline consts. - query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::ConcreteOpaqueTypes<'tcx>, ErrorGuaranteed> { + query mir_borrowck(key: LocalDefId) -> Result<&'tcx mir::DefinitionSiteHiddenTypes<'tcx>, ErrorGuaranteed> { desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) } } @@ -1881,6 +1881,7 @@ rustc_queries! { } /// Returns whether the impl or associated function has the `default` keyword. + /// Note: This will ICE on inherent impl items. Consider using `AssocItem::defaultness`. query defaultness(def_id: DefId) -> hir::Defaultness { desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } separate_provide_extern @@ -2558,9 +2559,9 @@ rustc_queries! { } query method_autoderef_steps( - goal: CanonicalTyGoal<'tcx> + goal: CanonicalMethodAutoderefStepsGoal<'tcx> ) -> MethodAutoderefStepsResult<'tcx> { - desc { "computing autoderef types for `{}`", goal.canonical.value.value } + desc { "computing autoderef types for `{}`", goal.canonical.value.value.self_ty } } /// Used by `-Znext-solver` to compute proof trees. diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index ab8a3142953..0c7bddf60d9 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -823,6 +823,9 @@ impl DynCompatibilityViolation { DynCompatibilityViolation::Method(name, MethodViolationCode::AsyncFn, _) => { format!("method `{name}` is `async`").into() } + DynCompatibilityViolation::Method(name, MethodViolationCode::CVariadic, _) => { + format!("method `{name}` is C-variadic").into() + } DynCompatibilityViolation::Method( name, MethodViolationCode::WhereClauseReferencesSelf, @@ -977,6 +980,9 @@ pub enum MethodViolationCode { /// e.g., `fn foo<A>()` Generic, + /// e.g., `fn (mut ap: ...)` + CVariadic, + /// the method's receiver (`self` argument) can't be dispatched on UndispatchableReceiver(Option<Span>), } diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 3f6faa1a572..c5cd7c54e4e 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -10,6 +10,7 @@ use rustc_span::Span; use crate::error::DropCheckOverflow; use crate::infer::canonical::{Canonical, CanonicalQueryInput, QueryResponse}; +use crate::traits::solve; pub use crate::traits::solve::NoSolution; use crate::ty::{self, GenericArg, Ty, TyCtxt}; @@ -67,7 +68,16 @@ pub mod type_op { pub type CanonicalAliasGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>; -pub type CanonicalTyGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>; +pub type CanonicalMethodAutoderefStepsGoal<'tcx> = + CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, MethodAutoderefSteps<'tcx>>>; +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)] +pub struct MethodAutoderefSteps<'tcx> { + /// The list of opaque types currently in the storage. + /// + /// Only used by the new solver for now. + pub predefined_opaques_in_body: solve::PredefinedOpaques<'tcx>, + pub self_ty: Ty<'tcx>, +} pub type CanonicalPredicateGoal<'tcx> = CanonicalQueryInput<'tcx, ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>>; @@ -144,6 +154,7 @@ impl<'tcx> FromIterator<DropckConstraint<'tcx>> for DropckConstraint<'tcx> { #[derive(Debug, HashStable)] pub struct CandidateStep<'tcx> { pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + pub self_ty_is_opaque: bool, pub autoderefs: usize, /// `true` if the type results from a dereference of a raw pointer. /// when assembling candidates, we include these steps, but not when diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index ef5223de0e8..3343f270333 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -4,7 +4,7 @@ use rustc_type_ir as ir; pub use rustc_type_ir::solve::*; use crate::ty::{ - self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, + self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, try_visit, }; @@ -15,16 +15,7 @@ pub type CandidateSource<'tcx> = ir::solve::CandidateSource<TyCtxt<'tcx>>; pub type CanonicalInput<'tcx, P = ty::Predicate<'tcx>> = ir::solve::CanonicalInput<TyCtxt<'tcx>, P>; pub type CanonicalResponse<'tcx> = ir::solve::CanonicalResponse<TyCtxt<'tcx>>; -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] -pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<TyCtxt<'tcx>>>); - -impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> { - type Target = PredefinedOpaquesData<TyCtxt<'tcx>>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} +pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>; #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)] pub struct ExternalConstraints<'tcx>( @@ -93,35 +84,3 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> { self.normalization_nested_goals.visit_with(visitor) } } - -// FIXME: Having to clone `region_constraints` for folding feels bad and -// probably isn't great wrt performance. -// -// Not sure how to fix this, maybe we should also intern `opaque_types` and -// `region_constraints` here or something. -impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> { - fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>( - self, - folder: &mut F, - ) -> Result<Self, F::Error> { - Ok(FallibleTypeFolder::cx(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData { - opaque_types: self - .opaque_types - .iter() - .map(|opaque| opaque.try_fold_with(folder)) - .collect::<Result<_, F::Error>>()?, - })) - } - - fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self { - TypeFolder::cx(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData { - opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(), - }) - } -} - -impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> { - fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result { - self.opaque_types.visit_with(visitor) - } -} diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index a902a8a61e5..768646c7630 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -5,15 +5,17 @@ use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::{Ident, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Symbol}; use super::{TyCtxt, Visibility}; use crate::ty; #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash, Encodable, Decodable)] -pub enum AssocItemContainer { +pub enum AssocContainer { Trait, - Impl, + InherentImpl, + /// The `DefId` points to the trait item being implemented. + TraitImpl(Result<DefId, ErrorGuaranteed>), } /// Information about an associated item @@ -21,11 +23,7 @@ pub enum AssocItemContainer { pub struct AssocItem { pub def_id: DefId, pub kind: AssocKind, - pub container: AssocItemContainer, - - /// If this is an item in an impl of a trait then this is the `DefId` of - /// the associated item on the trait that this implements. - pub trait_item_def_id: Option<DefId>, + pub container: AssocContainer, } impl AssocItem { @@ -55,7 +53,34 @@ impl AssocItem { /// /// [`type_of`]: crate::ty::TyCtxt::type_of pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness { - tcx.defaultness(self.def_id) + match self.container { + AssocContainer::InherentImpl => hir::Defaultness::Final, + AssocContainer::Trait | AssocContainer::TraitImpl(_) => tcx.defaultness(self.def_id), + } + } + + pub fn expect_trait_impl(&self) -> Result<DefId, ErrorGuaranteed> { + let AssocContainer::TraitImpl(trait_item_id) = self.container else { + bug!("expected item to be in a trait impl: {:?}", self.def_id); + }; + trait_item_id + } + + /// If this is a trait impl item, returns the `DefId` of the trait item this implements. + /// Otherwise, returns `DefId` for self. Returns an Err in case the trait item was not + /// resolved successfully. + pub fn trait_item_or_self(&self) -> Result<DefId, ErrorGuaranteed> { + match self.container { + AssocContainer::TraitImpl(id) => id, + AssocContainer::Trait | AssocContainer::InherentImpl => Ok(self.def_id), + } + } + + pub fn trait_item_def_id(&self) -> Option<DefId> { + match self.container { + AssocContainer::TraitImpl(Ok(id)) => Some(id), + _ => None, + } } #[inline] @@ -71,16 +96,18 @@ impl AssocItem { #[inline] pub fn trait_container(&self, tcx: TyCtxt<'_>) -> Option<DefId> { match self.container { - AssocItemContainer::Impl => None, - AssocItemContainer::Trait => Some(tcx.parent(self.def_id)), + AssocContainer::InherentImpl | AssocContainer::TraitImpl(_) => None, + AssocContainer::Trait => Some(tcx.parent(self.def_id)), } } #[inline] pub fn impl_container(&self, tcx: TyCtxt<'_>) -> Option<DefId> { match self.container { - AssocItemContainer::Impl => Some(tcx.parent(self.def_id)), - AssocItemContainer::Trait => None, + AssocContainer::InherentImpl | AssocContainer::TraitImpl(_) => { + Some(tcx.parent(self.def_id)) + } + AssocContainer::Trait => None, } } @@ -156,11 +183,11 @@ impl AssocItem { return false; } - let def_id = match (self.container, self.trait_item_def_id) { - (AssocItemContainer::Trait, _) => self.def_id, - (AssocItemContainer::Impl, Some(trait_item_did)) => trait_item_did, - // Inherent impl but this attr is only applied to trait assoc items. - (AssocItemContainer::Impl, None) => return true, + let def_id = match self.container { + AssocContainer::Trait => self.def_id, + AssocContainer::TraitImpl(Ok(trait_item_did)) => trait_item_did, + AssocContainer::TraitImpl(Err(_)) => return false, + AssocContainer::InherentImpl => return true, }; find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_)) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8ea767dccd3..fe3fa024cd5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -75,7 +75,7 @@ use crate::thir::Thir; use crate::traits; use crate::traits::solve::{ self, CanonicalInput, ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, inspect, + QueryResult, inspect, }; use crate::ty::predicate::ExistentialPredicateStableCmpExt as _; use crate::ty::{ @@ -116,7 +116,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn mk_predefined_opaques_in_body( self, - data: PredefinedOpaquesData<Self>, + data: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], ) -> Self::PredefinedOpaques { self.mk_predefined_opaques_in_body(data) } @@ -574,7 +574,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) @@ -941,7 +941,7 @@ pub struct CtxtInterners<'tcx> { layout: InternedSet<'tcx, LayoutData<FieldIdx, VariantIdx>>, adt_def: InternedSet<'tcx, AdtDefData>, external_constraints: InternedSet<'tcx, ExternalConstraintsData<TyCtxt<'tcx>>>, - predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<TyCtxt<'tcx>>>, + predefined_opaques_in_body: InternedSet<'tcx, List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>>, fields: InternedSet<'tcx, List<FieldIdx>>, local_def_ids: InternedSet<'tcx, List<LocalDefId>>, captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>, @@ -2012,7 +2012,7 @@ impl<'tcx> TyCtxt<'tcx> { if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { Some(self.untracked.definitions.read().local_def_path_hash_to_def_id(hash)?.to_def_id()) } else { - Some(self.def_path_hash_to_def_id_extern(hash, stable_crate_id)) + self.def_path_hash_to_def_id_extern(hash, stable_crate_id) } } @@ -2276,7 +2276,16 @@ impl<'tcx> TyCtxt<'tcx> { let is_impl_item = match self.hir_node_by_def_id(suitable_region_binding_scope) { Node::Item(..) | Node::TraitItem(..) => false, - Node::ImplItem(..) => self.is_bound_region_in_impl_item(suitable_region_binding_scope), + Node::ImplItem(impl_item) => match impl_item.impl_kind { + // For now, we do not try to target impls of traits. This is + // because this message is going to suggest that the user + // change the fn signature, but they may not be free to do so, + // since the signature must match the trait. + // + // FIXME(#42706) -- in some cases, we could do better here. + hir::ImplItemImplKind::Trait { .. } => true, + _ => false, + }, _ => false, }; @@ -2330,21 +2339,6 @@ impl<'tcx> TyCtxt<'tcx> { None } - /// Checks if the bound region is in Impl Item. - pub fn is_bound_region_in_impl_item(self, suitable_region_binding_scope: LocalDefId) -> bool { - let container_id = self.parent(suitable_region_binding_scope.to_def_id()); - if self.impl_trait_ref(container_id).is_some() { - // For now, we do not try to target impls of traits. This is - // because this message is going to suggest that the user - // change the fn signature, but they may not be free to do so, - // since the signature must match the trait. - // - // FIXME(#42706) -- in some cases, we could do better here. - return true; - } - false - } - /// Determines whether identifiers in the assembly have strict naming rules. /// Currently, only NVPTX* targets need it. pub fn has_strict_asm_symbol_naming(self) -> bool { @@ -2754,8 +2748,6 @@ direct_interners! { adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, external_constraints: pub mk_external_constraints(ExternalConstraintsData<TyCtxt<'tcx>>): ExternalConstraints -> ExternalConstraints<'tcx>, - predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<TyCtxt<'tcx>>): - PredefinedOpaques -> PredefinedOpaques<'tcx>, } macro_rules! slice_interners { @@ -2792,6 +2784,7 @@ slice_interners!( offset_of: pub mk_offset_of((VariantIdx, FieldIdx)), patterns: pub mk_patterns(Pattern<'tcx>), outlives: pub mk_outlives(ty::ArgOutlivesPredicate<'tcx>), + predefined_opaques_in_body: pub mk_predefined_opaques_in_body((ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)), ); impl<'tcx> TyCtxt<'tcx> { @@ -3135,6 +3128,14 @@ impl<'tcx> TyCtxt<'tcx> { T::collect_and_apply(iter, |xs| self.mk_poly_existential_predicates(xs)) } + pub fn mk_predefined_opaques_in_body_from_iter<I, T>(self, iter: I) -> T::Output + where + I: Iterator<Item = T>, + T: CollectAndApply<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>), PredefinedOpaques<'tcx>>, + { + T::collect_and_apply(iter, |xs| self.mk_predefined_opaques_in_body(xs)) + } + pub fn mk_clauses_from_iter<I, T>(self, iter: I) -> T::Output where I: Iterator<Item = T>, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index e76993e0542..34ead91b4f6 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -18,8 +18,8 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::print::{FmtPrinter, Print}; use crate::ty::{ - self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + self, AssocContainer, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; /// An `InstanceKind` along with the args that are needed to substitute the instance. @@ -611,26 +611,23 @@ impl<'tcx> Instance<'tcx> { debug!(" => fn pointer created for virtual call"); resolved.def = InstanceKind::ReifyShim(def_id, reason); } - // Reify `Trait::method` implementations if KCFI is enabled - // FIXME(maurer) only reify it if it is a vtable-safe function - _ if tcx.sess.is_sanitizer_kcfi_enabled() - && tcx - .opt_associated_item(def_id) - .and_then(|assoc| assoc.trait_item_def_id) - .is_some() => - { - // If this function could also go in a vtable, we need to `ReifyShim` it with - // KCFI because it can only attach one type per function. - resolved.def = InstanceKind::ReifyShim(resolved.def_id(), reason) - } - // Reify `::call`-like method implementations if KCFI is enabled - _ if tcx.sess.is_sanitizer_kcfi_enabled() - && tcx.is_closure_like(resolved.def_id()) => - { - // Reroute through a reify via the *unresolved* instance. The resolved one can't - // be directly reified because it's closure-like. The reify can handle the - // unresolved instance. - resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } + _ if tcx.sess.is_sanitizer_kcfi_enabled() => { + // Reify `::call`-like method implementations + if tcx.is_closure_like(resolved.def_id()) { + // Reroute through a reify via the *unresolved* instance. The resolved one can't + // be directly reified because it's closure-like. The reify can handle the + // unresolved instance. + resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } + // Reify `Trait::method` implementations + // FIXME(maurer) only reify it if it is a vtable-safe function + } else if let Some(assoc) = tcx.opt_associated_item(def_id) + && let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) = + assoc.container + { + // If this function could also go in a vtable, we need to `ReifyShim` it with + // KCFI because it can only attach one type per function. + resolved.def = InstanceKind::ReifyShim(resolved.def_id(), reason) + } } _ => {} } @@ -683,7 +680,7 @@ impl<'tcx> Instance<'tcx> { && !matches!( tcx.opt_associated_item(def), Some(ty::AssocItem { - container: ty::AssocItemContainer::Trait, + container: ty::AssocContainer::Trait, .. }) ); diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2114d080dfa..507a25a4325 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -16,12 +16,13 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use rustc_target::callconv::FnAbi; -use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, PanicStrategy, Target, X86Abi}; +use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, X86Abi}; use tracing::debug; use {rustc_abi as abi, rustc_hir as hir}; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; +use crate::traits::ObligationCause; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt}; @@ -218,6 +219,15 @@ impl fmt::Display for ValidityRequirement { } #[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] +pub enum SimdLayoutError { + /// The vector has 0 lanes. + ZeroLength, + /// The vector has more lanes than supported or permitted by + /// #\[rustc_simd_monomorphize_lane_limit\]. + TooManyLanes(u64), +} + +#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)] pub enum LayoutError<'tcx> { /// A type doesn't have a sensible layout. /// @@ -229,6 +239,8 @@ pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), /// The size of a type exceeds [`TargetDataLayout::obj_size_bound`]. SizeOverflow(Ty<'tcx>), + /// A SIMD vector has invalid layout, such as zero-length or too many lanes. + InvalidSimd { ty: Ty<'tcx>, kind: SimdLayoutError }, /// The layout can vary due to a generic parameter. /// /// Unlike `Unknown`, this variant is a "soft" error and indicates that the layout @@ -256,6 +268,10 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(_) => middle_layout_unknown, SizeOverflow(_) => middle_layout_size_overflow, + InvalidSimd { kind: SimdLayoutError::TooManyLanes(_), .. } => { + middle_layout_simd_too_many + } + InvalidSimd { kind: SimdLayoutError::ZeroLength, .. } => middle_layout_simd_zero_length, TooGeneric(_) => middle_layout_too_generic, NormalizationFailure(_, _) => middle_layout_normalization_failure, Cycle(_) => middle_layout_cycle, @@ -270,6 +286,10 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(ty) => E::Unknown { ty }, SizeOverflow(ty) => E::Overflow { ty }, + InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => { + E::SimdTooManyLanes { ty, max_lanes } + } + InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => E::SimdZeroLength { ty }, TooGeneric(ty) => E::TooGeneric { ty }, NormalizationFailure(ty, e) => { E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } @@ -292,6 +312,12 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { LayoutError::SizeOverflow(ty) => { write!(f, "values of the type `{ty}` are too big for the target architecture") } + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => { + write!(f, "the SIMD type `{ty}` has more elements than the limit {max_lanes}") + } + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => { + write!(f, "the SIMD type `{ty}` has zero elements") + } LayoutError::NormalizationFailure(t, e) => write!( f, "unable to determine layout for `{}` because `{}` cannot be normalized", @@ -373,6 +399,7 @@ impl<'tcx> SizeSkeleton<'tcx> { e @ LayoutError::Cycle(_) | e @ LayoutError::Unknown(_) | e @ LayoutError::SizeOverflow(_) + | e @ LayoutError::InvalidSimd { .. } | e @ LayoutError::NormalizationFailure(..) | e @ LayoutError::ReferencesError(_), ) => return Err(e), @@ -384,6 +411,7 @@ impl<'tcx> SizeSkeleton<'tcx> { let tail = tcx.struct_tail_raw( pointee, + &ObligationCause::dummy(), |ty| match tcx.try_normalize_erasing_regions(typing_env, ty) { Ok(ty) => ty, Err(e) => Ty::new_error_with_message( @@ -812,7 +840,7 @@ where | ty::CoroutineWitness(..) | ty::Foreign(..) | ty::Pat(_, _) - | ty::Dynamic(_, _, ty::Dyn) => { + | ty::Dynamic(_, _) => { bug!("TyAndLayout::field({:?}): not applicable", this) } @@ -878,7 +906,7 @@ where // `std::mem::uninitialized::<&dyn Trait>()`, for example. if let ty::Adt(def, args) = metadata.kind() && tcx.is_lang_item(def.did(), LangItem::DynMetadata) - && let ty::Dynamic(data, _, ty::Dyn) = args.type_at(0).kind() + && let ty::Dynamic(data, _) = args.type_at(0).kind() { mk_dyn_vtable(data.principal()) } else { @@ -887,7 +915,7 @@ where } else { match tcx.struct_tail_for_codegen(pointee, cx.typing_env()).kind() { ty::Slice(_) | ty::Str => tcx.types.usize, - ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()), + ty::Dynamic(data, _) => mk_dyn_vtable(data.principal()), _ => bug!("TyAndLayout::field({:?}): not applicable", this), } }; @@ -1196,7 +1224,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) // // Note that this is true regardless ABI specified on the function -- a `extern "C-unwind"` // function defined in Rust is also required to abort. - if tcx.sess.panic_strategy() == PanicStrategy::Abort && !tcx.is_foreign_item(did) { + if !tcx.sess.panic_strategy().unwinds() && !tcx.is_foreign_item(did) { return false; } @@ -1204,7 +1232,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) // // This is not part of `codegen_fn_attrs` as it can differ between crates // and therefore cannot be computed in core. - if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort + if !tcx.sess.opts.unstable_opts.panic_in_drop.unwinds() && tcx.is_lang_item(did, LangItem::DropInPlace) { return false; @@ -1243,7 +1271,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi) | RiscvInterruptS | RustInvalid | Unadjusted => false, - Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind, + Rust | RustCall | RustCold => tcx.sess.panic_strategy().unwinds(), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index da17ec1f9f3..ce4de6b95e0 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -25,6 +25,7 @@ pub use generic_args::{GenericArgKind, TermKind, *}; pub use generics::*; pub use intrinsic::IntrinsicDef; use rustc_abi::{Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, VariantIdx}; +use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; @@ -62,7 +63,7 @@ pub use rustc_type_ir::solve::SizedTraitKind; pub use rustc_type_ir::*; #[allow(hidden_glob_reexports, unused_imports)] use rustc_type_ir::{InferCtxtLike, Interner}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; pub use vtable::*; use {rustc_ast as ast, rustc_hir as hir}; @@ -254,12 +255,6 @@ pub struct ImplTraitHeader<'tcx> { pub constness: hir::Constness, } -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] -pub enum ImplSubject<'tcx> { - Trait(TraitRef<'tcx>), - Inherent(Ty<'tcx>), -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] #[derive(TypeFoldable, TypeVisitable)] pub enum Asyncness { @@ -1934,6 +1929,11 @@ impl<'tcx> TyCtxt<'tcx> { Some((parent, def_kind)) } + /// Returns the trait item that is implemented by the given item `DefId`. + pub fn trait_item_of(self, def_id: impl IntoQueryParam<DefId>) -> Option<DefId> { + self.opt_associated_item(def_id.into_query_param())?.trait_item_def_id() + } + /// If the given `DefId` is an associated item of a trait, /// returns the `DefId` of the trait; otherwise, returns `None`. pub fn trait_of_assoc(self, def_id: DefId) -> Option<DefId> { @@ -2149,17 +2149,12 @@ impl<'tcx> TyCtxt<'tcx> { let Some(item) = self.opt_associated_item(def_id) else { return false; }; - if item.container != ty::AssocItemContainer::Impl { - return false; - } - let Some(trait_item_def_id) = item.trait_item_def_id else { + let AssocContainer::TraitImpl(Ok(trait_item_def_id)) = item.container else { return false; }; - return !self - .associated_types_for_impl_traits_in_associated_fn(trait_item_def_id) - .is_empty(); + !self.associated_types_for_impl_traits_in_associated_fn(trait_item_def_id).is_empty() } } @@ -2222,3 +2217,225 @@ pub struct DestructuredConst<'tcx> { pub variant: Option<VariantIdx>, pub fields: &'tcx [ty::Const<'tcx>], } + +/// Generate TypeTree information for autodiff. +/// This function creates TypeTree metadata that describes the memory layout +/// of function parameters and return types for Enzyme autodiff. +pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree { + // Check if TypeTrees are disabled via NoTT flag + if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Check if this is actually a function type + if !fn_ty.is_fn() { + return FncTree { args: vec![], ret: TypeTree::new() }; + } + + // Get the function signature + let fn_sig = fn_ty.fn_sig(tcx); + let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + // Create TypeTrees for each input parameter + let mut args = vec![]; + for ty in sig.inputs().iter() { + let type_tree = typetree_from_ty(tcx, *ty); + args.push(type_tree); + } + + // Create TypeTree for return type + let ret = typetree_from_ty(tcx, sig.output()); + + FncTree { args, ret } +} + +/// Generate TypeTree for a specific type. +/// This function analyzes a Rust type and creates appropriate TypeTree metadata. +pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree { + let mut visited = Vec::new(); + typetree_from_ty_inner(tcx, ty, 0, &mut visited) +} + +/// Maximum recursion depth for TypeTree generation to prevent stack overflow +/// from pathological deeply nested types. Combined with cycle detection. +const MAX_TYPETREE_DEPTH: usize = 6; + +/// Internal recursive function for TypeTree generation with cycle detection and depth limiting. +fn typetree_from_ty_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec<Ty<'tcx>>, +) -> TypeTree { + if depth >= MAX_TYPETREE_DEPTH { + trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty); + return TypeTree::new(); + } + + if visited.contains(&ty) { + return TypeTree::new(); + } + + visited.push(ty); + let result = typetree_from_ty_impl(tcx, ty, depth, visited); + visited.pop(); + result +} + +/// Implementation of TypeTree generation logic. +fn typetree_from_ty_impl<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec<Ty<'tcx>>, +) -> TypeTree { + typetree_from_ty_impl_inner(tcx, ty, depth, visited, false) +} + +/// Internal implementation with context about whether this is for a reference target. +fn typetree_from_ty_impl_inner<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + depth: usize, + visited: &mut Vec<Ty<'tcx>>, + is_reference_target: bool, +) -> TypeTree { + if ty.is_scalar() { + let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() { + (Kind::Integer, ty.primitive_size(tcx).bytes_usize()) + } else if ty.is_floating_point() { + match ty { + x if x == tcx.types.f16 => (Kind::Half, 2), + x if x == tcx.types.f32 => (Kind::Float, 4), + x if x == tcx.types.f64 => (Kind::Double, 8), + x if x == tcx.types.f128 => (Kind::F128, 16), + _ => (Kind::Integer, 0), + } + } else { + (Kind::Integer, 0) + }; + + // Use offset 0 for scalars that are direct targets of references (like &f64) + // Use offset -1 for scalars used directly (like function return types) + let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 }; + return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]); + } + + if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() { + let inner_ty = if let Some(inner) = ty.builtin_deref(true) { + inner + } else { + return TypeTree::new(); + }; + + let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true); + return TypeTree(vec![Type { + offset: -1, + size: tcx.data_layout.pointer_size().bytes_usize(), + kind: Kind::Pointer, + child, + }]); + } + + if ty.is_array() { + if let ty::Array(element_ty, len_const) = ty.kind() { + let len = len_const.try_to_target_usize(tcx).unwrap_or(0); + if len == 0 { + return TypeTree::new(); + } + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); + let mut types = Vec::new(); + for elem_type in &element_tree.0 { + types.push(Type { + offset: -1, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + + return TypeTree(types); + } + } + + if ty.is_slice() { + if let ty::Slice(element_ty) = ty.kind() { + let element_tree = + typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false); + return element_tree; + } + } + + if let ty::Tuple(tuple_types) = ty.kind() { + if tuple_types.is_empty() { + return TypeTree::new(); + } + + let mut types = Vec::new(); + let mut current_offset = 0; + + for tuple_ty in tuple_types.iter() { + let element_tree = + typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false); + + let element_layout = tcx + .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty)) + .ok() + .map(|layout| layout.size.bytes_usize()) + .unwrap_or(0); + + for elem_type in &element_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + current_offset as isize + } else { + current_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + + current_offset += element_layout; + } + + return TypeTree(types); + } + + if let ty::Adt(adt_def, args) = ty.kind() { + if adt_def.is_struct() { + let struct_layout = + tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)); + if let Ok(layout) = struct_layout { + let mut types = Vec::new(); + + for (field_idx, field_def) in adt_def.all_fields().enumerate() { + let field_ty = field_def.ty(tcx, args); + let field_tree = + typetree_from_ty_impl_inner(tcx, field_ty, depth + 1, visited, false); + + let field_offset = layout.fields.offset(field_idx).bytes_usize(); + + for elem_type in &field_tree.0 { + types.push(Type { + offset: if elem_type.offset == -1 { + field_offset as isize + } else { + field_offset as isize + elem_type.offset + }, + size: elem_type.size, + kind: elem_type.kind, + child: elem_type.child.clone(), + }); + } + } + + return TypeTree(types); + } + } + } + + TypeTree::new() +} diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index fc821ffdaa6..8f7c8170f7a 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -784,14 +784,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }, }, ty::Adt(def, args) => self.print_def_path(def.did(), args)?, - ty::Dynamic(data, r, repr) => { + ty::Dynamic(data, r) => { let print_r = self.should_print_optional_region(r); if print_r { write!(self, "(")?; } - match repr { - ty::Dyn => write!(self, "dyn ")?, - } + write!(self, "dyn ")?; data.print(self)?; if print_r { write!(self, " + ")?; @@ -3179,8 +3177,7 @@ define_print! { write!(p, "` can be evaluated")?; } ty::ClauseKind::UnstableFeature(symbol) => { - write!(p, "unstable feature: ")?; - write!(p, "`{symbol}`")?; + write!(p, "feature({symbol}) is enabled")?; } } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index dc1d60f3d43..2f96970af47 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -7,30 +7,6 @@ use crate::ty::{self as ty, Ty, TyCtxt}; pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>; -impl<'tcx> Relate<TyCtxt<'tcx>> for ty::ImplSubject<'tcx> { - #[inline] - fn relate<R: TypeRelation<TyCtxt<'tcx>>>( - relation: &mut R, - a: ty::ImplSubject<'tcx>, - b: ty::ImplSubject<'tcx>, - ) -> RelateResult<'tcx, ty::ImplSubject<'tcx>> { - match (a, b) { - (ty::ImplSubject::Trait(trait_ref_a), ty::ImplSubject::Trait(trait_ref_b)) => { - let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?; - Ok(ty::ImplSubject::Trait(trait_ref)) - } - (ty::ImplSubject::Inherent(ty_a), ty::ImplSubject::Inherent(ty_b)) => { - let ty = Ty::relate(relation, ty_a, ty_b)?; - Ok(ty::ImplSubject::Inherent(ty)) - } - (ty::ImplSubject::Trait(_), ty::ImplSubject::Inherent(_)) - | (ty::ImplSubject::Inherent(_), ty::ImplSubject::Trait(_)) => { - bug!("can not relate TraitRef and Ty"); - } - } - } -} - impl<'tcx> Relate<TyCtxt<'tcx>> for Ty<'tcx> { #[inline] fn relate<R: TypeRelation<TyCtxt<'tcx>>>( diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index 5ada9ecc80c..f1aa7076d98 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -132,7 +132,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Span> { | ty::Ref(_, _, _) | ty::FnPtr(_, _) | ty::Tuple(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 89ef46b1ae5..8dc4dfd677b 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -390,11 +390,9 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?), ty::Slice(typ) => ty::Slice(typ.try_fold_with(folder)?), ty::Adt(tid, args) => ty::Adt(tid, args.try_fold_with(folder)?), - ty::Dynamic(trait_ty, region, representation) => ty::Dynamic( - trait_ty.try_fold_with(folder)?, - region.try_fold_with(folder)?, - representation, - ), + ty::Dynamic(trait_ty, region) => { + ty::Dynamic(trait_ty.try_fold_with(folder)?, region.try_fold_with(folder)?) + } ty::Tuple(ts) => ty::Tuple(ts.try_fold_with(folder)?), ty::FnDef(def_id, args) => ty::FnDef(def_id, args.try_fold_with(folder)?), ty::FnPtr(sig_tys, hdr) => ty::FnPtr(sig_tys.try_fold_with(folder)?, hdr), @@ -437,8 +435,8 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), ty::Adt(tid, args) => ty::Adt(tid, args.fold_with(folder)), - ty::Dynamic(trait_ty, region, representation) => { - ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder), representation) + ty::Dynamic(trait_ty, region) => { + ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) } ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), ty::FnDef(def_id, args) => ty::FnDef(def_id, args.fold_with(folder)), @@ -481,7 +479,7 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'tcx> { } ty::Slice(typ) => typ.visit_with(visitor), ty::Adt(_, args) => args.visit_with(visitor), - ty::Dynamic(trait_ty, reg, _) => { + ty::Dynamic(trait_ty, reg) => { try_visit!(trait_ty.visit_with(visitor)); reg.visit_with(visitor) } @@ -798,6 +796,7 @@ macro_rules! list_fold { list_fold! { &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> : mk_poly_existential_predicates, + &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>: mk_predefined_opaques_in_body, &'tcx ty::List<PlaceElem<'tcx>> : mk_place_elems, &'tcx ty::List<ty::Pattern<'tcx>> : mk_patterns, &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>> : mk_outlives, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 755fc68d86f..de35e5e847c 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -17,12 +17,13 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_type_ir::TyKind::*; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::walk::TypeWalker; -use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate}; +use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, TypeVisitableExt, elaborate}; use tracing::instrument; use ty::util::IntTypeExt; use super::GenericParamDefKind; use crate::infer::canonical::Canonical; +use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, @@ -734,7 +735,6 @@ impl<'tcx> Ty<'tcx> { tcx: TyCtxt<'tcx>, obj: &'tcx List<ty::PolyExistentialPredicate<'tcx>>, reg: ty::Region<'tcx>, - repr: DynKind, ) -> Ty<'tcx> { if cfg!(debug_assertions) { let projection_count = obj @@ -767,7 +767,7 @@ impl<'tcx> Ty<'tcx> { but it has {projection_count}" ); } - Ty::new(tcx, Dynamic(obj, reg, repr)) + Ty::new(tcx, Dynamic(obj, reg)) } #[inline] @@ -980,9 +980,8 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { interner: TyCtxt<'tcx>, preds: &'tcx List<ty::PolyExistentialPredicate<'tcx>>, region: ty::Region<'tcx>, - kind: ty::DynKind, ) -> Self { - Ty::new_dynamic(interner, preds, region, kind) + Ty::new_dynamic(interner, preds, region) } fn new_coroutine( @@ -1356,7 +1355,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_trait(self) -> bool { - matches!(self.kind(), Dynamic(_, _, ty::Dyn)) + matches!(self.kind(), Dynamic(_, _)) } #[inline] @@ -1640,7 +1639,7 @@ impl<'tcx> Ty<'tcx> { tcx: TyCtxt<'tcx>, normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, ) -> Result<Ty<'tcx>, Ty<'tcx>> { - let tail = tcx.struct_tail_raw(self, normalize, || {}); + let tail = tcx.struct_tail_raw(self, &ObligationCause::dummy(), normalize, || {}); match tail.kind() { // Sized types ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) @@ -1671,7 +1670,7 @@ impl<'tcx> Ty<'tcx> { ty::Str | ty::Slice(_) => Ok(tcx.types.usize), - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, DUMMY_SP); Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()])) } @@ -1853,7 +1852,7 @@ impl<'tcx> Ty<'tcx> { | ty::Never | ty::Error(_) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => match sizedness { SizedTraitKind::Sized => false, SizedTraitKind::MetaSized => true, }, diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 8dd80aab946..944bd9756a9 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -167,7 +167,7 @@ pub struct TypeckResults<'tcx> { /// We also store the type here, so that the compiler can use it as a hint /// for figuring out hidden types, even if they are only set in dead code /// (which doesn't show up in MIR). - pub concrete_opaque_types: FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>, + pub hidden_types: FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>, /// Tracks the minimum captures required for a closure; /// see `MinCaptureInformationMap` for more details. @@ -250,7 +250,7 @@ impl<'tcx> TypeckResults<'tcx> { coercion_casts: Default::default(), used_trait_imports: Default::default(), tainted_by_errors: None, - concrete_opaque_types: Default::default(), + hidden_types: Default::default(), closure_min_captures: Default::default(), closure_fake_reads: Default::default(), rvalue_scopes: Default::default(), diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 029586a9c55..a4422abc688 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -24,6 +24,7 @@ use super::TypingEnv; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::query::Providers; +use crate::traits::ObligationCause; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, @@ -216,7 +217,12 @@ impl<'tcx> TyCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, ) -> Ty<'tcx> { let tcx = self; - tcx.struct_tail_raw(ty, |ty| tcx.normalize_erasing_regions(typing_env, ty), || {}) + tcx.struct_tail_raw( + ty, + &ObligationCause::dummy(), + |ty| tcx.normalize_erasing_regions(typing_env, ty), + || {}, + ) } /// Returns true if a type has metadata. @@ -248,6 +254,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn struct_tail_raw( self, mut ty: Ty<'tcx>, + cause: &ObligationCause<'tcx>, mut normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, // This is currently used to allow us to walk a ValTree // in lockstep with the type in order to get the ValTree branch that @@ -261,9 +268,11 @@ impl<'tcx> TyCtxt<'tcx> { Limit(0) => Limit(2), limit => limit * 2, }; - let reported = self - .dcx() - .emit_err(crate::error::RecursionLimitReached { ty, suggested_limit }); + let reported = self.dcx().emit_err(crate::error::RecursionLimitReached { + span: cause.span, + ty, + suggested_limit, + }); return Ty::new_error(self, reported); } match *ty.kind() { @@ -1371,10 +1380,12 @@ impl<'tcx> Ty<'tcx> { _ => self, }; - // FIXME(#86868): We should be canonicalizing, or else moving this to a method of inference - // context, or *something* like that, but for now just avoid passing inference - // variables to queries that can't cope with them. Instead, conservatively - // return "true" (may change drop order). + // FIXME + // We should be canonicalizing, or else moving this to a method of inference + // context, or *something* like that, + // but for now just avoid passing inference variables + // to queries that can't cope with them. + // Instead, conservatively return "true" (may change drop order). if query_ty.has_infer() { return true; } diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index e2f09fdcb4b..a3e9054fdcb 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -104,7 +104,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( .expect("failed to build vtable representation"); assert!(layout.is_sized(), "can't create a vtable for an unsized type"); let size = layout.size.bytes(); - let align = layout.align.abi.bytes(); + let align = layout.align.bytes(); let ptr_size = tcx.data_layout.pointer_size(); let ptr_align = tcx.data_layout.pointer_align().abi; diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 41d3aefcbe6..54490e00509 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -229,6 +229,11 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let source = self.parse_operand(args[0])?; Ok(Rvalue::Cast(CastKind::PtrToPtr, source, expr.ty)) }, + @call(mir_cast_unsize, args) => { + let source = self.parse_operand(args[0])?; + let kind = CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, CoercionSource::AsCast); + Ok(Rvalue::Cast(kind, source, expr.ty)) + }, @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", ExprKind::Binary { op, lhs, rhs } => { @@ -247,7 +252,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let offset = self.parse_operand(args[1])?; Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, - @call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), @call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)), @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 7c851ec465b..5a6bd2f413c 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -663,7 +663,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// For arrays it'll be `Operand::Constant` with the actual length; /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. - fn len_of_slice_or_array( + pub(in crate::builder) fn len_of_slice_or_array( &mut self, block: BasicBlock, place: Place<'tcx>, diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index a4ef6e92739..3a5839f2d40 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -8,6 +8,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::thir::*; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::cast::{CastTy, mir_cast_kind}; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, Ty, UpvarArgs}; @@ -656,6 +657,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(rvalue) } + /// Recursively inspect a THIR expression and probe through unsizing + /// operations that can be const-folded today. + fn check_constness(&self, mut kind: &'a ExprKind<'tcx>) -> bool { + loop { + debug!(?kind, "check_constness"); + match kind { + &ExprKind::ValueTypeAscription { source: eid, user_ty: _, user_ty_span: _ } + | &ExprKind::Use { source: eid } + | &ExprKind::PointerCoercion { + cast: PointerCoercion::Unsize, + source: eid, + is_from_as_cast: _, + } + | &ExprKind::Scope { region_scope: _, lint_level: _, value: eid } => { + kind = &self.thir[eid].kind + } + _ => return matches!(Category::of(&kind), Some(Category::Constant)), + } + } + } + fn build_zero_repeat( &mut self, mut block: BasicBlock, @@ -666,7 +688,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let this = self; let value_expr = &this.thir[value]; let elem_ty = value_expr.ty; - if let Some(Category::Constant) = Category::of(&value_expr.kind) { + if this.check_constness(&value_expr.kind) { // Repeating a const does nothing } else { // For a non-const, we may need to generate an appropriate `Drop` diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index d03794fe2d5..1b6d96e49f0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -309,7 +309,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let actual = self.temp(usize_ty, test.span); // actual = len(place) - self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); + let length_op = self.len_of_slice_or_array(block, place, test.span, source_info); + self.cfg.push_assign(block, source_info, actual, Rvalue::Use(length_op)); // expected = <N> let expected = self.push_usize(block, source_info, len); diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index cdb2c5561ce..7ca94e655fb 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -395,12 +395,10 @@ enum NeedsTemporary { Maybe, } -/////////////////////////////////////////////////////////////////////////// /// The `BlockAnd` "monad" packages up the new basic block along with a /// produced value (sometimes just unit, of course). The `unpack!` /// macro (and methods below) makes working with `BlockAnd` much more /// convenient. - #[must_use = "if you don't use one of these results, you're leaving a dangling edge"] struct BlockAnd<T>(BasicBlock, T); @@ -438,9 +436,7 @@ macro_rules! unpack { }}; } -/////////////////////////////////////////////////////////////////////////// -/// the main entry point for building MIR for a function - +/// The main entry point for building MIR for a function. fn construct_fn<'tcx>( tcx: TyCtxt<'tcx>, fn_def: LocalDefId, diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index b5e165c7517..195d45c2c4c 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -554,6 +554,21 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_expr(self, &self.thir[arg]); return; } + + // Secondly, we allow raw borrows of union field accesses. Peel + // any of those off, and recurse normally on the LHS, which should + // reject any unsafe operations within. + let mut peeled = arg; + while let ExprKind::Scope { value: arg, .. } = self.thir[peeled].kind + && let ExprKind::Field { lhs, name: _, variant_index: _ } = self.thir[arg].kind + && let ty::Adt(def, _) = &self.thir[lhs].ty.kind() + && def.is_union() + { + peeled = lhs; + } + visit::walk_expr(self, &self.thir[peeled]); + // And return so we don't recurse directly onto the union field access(es). + return; } ExprKind::Deref { arg } => { if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) = 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 ae67bb5075e..3929a97eed8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1275,13 +1275,13 @@ fn report_non_exhaustive_match<'p, 'tcx>( if ty.is_ptr_sized_integral() { if ty.inner() == cx.tcx.types.usize { err.note(format!( - "`{ty}` does not have a fixed maximum value, so half-open ranges are \ - necessary to match exhaustively", + "`{ty}::MAX` is not treated as exhaustive, \ + so half-open ranges are necessary to match exhaustively", )); } else if ty.inner() == cx.tcx.types.isize { err.note(format!( - "`{ty}` does not have fixed minimum and maximum values, so half-open \ - ranges are necessary to match exhaustively", + "`{ty}::MIN` and `{ty}::MAX` are not treated as exhaustive, \ + so half-open ranges are necessary to match exhaustively", )); } } else if ty.inner() == cx.tcx.types.str_ { diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 9abb8343432..a4e4e30a8bb 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -91,7 +91,6 @@ where | Rvalue::Use(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Repeat(..) - | Rvalue::Len(..) | Rvalue::BinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 48718cad597..72d4cd72c2b 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -152,7 +152,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) @@ -196,7 +196,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) @@ -413,7 +413,6 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) - | Rvalue::Len(..) | Rvalue::NullaryOp( NullOp::SizeOf | NullOp::AlignOf diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 08c43a4648c..511c1960e40 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" +hashbrown = "0.15" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 5bd6fdcf485..35a21a2a834 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -3,6 +3,7 @@ use rustc_ast::InlineAsmOptions; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::ty::{self, TyCtxt, layout}; +use rustc_span::sym; use rustc_target::spec::PanicStrategy; /// A pass that runs which is targeted at ensuring that codegen guarantees about @@ -33,6 +34,19 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { return; } + // Represent whether this compilation target fundamentally doesn't + // support unwinding at all at an ABI level. If this the target has no + // support for unwinding then cleanup actions, for example, are all + // unnecessary and can be considered unreachable. + // + // Currently this is only true for wasm targets on panic=abort when the + // `exception-handling` target feature is disabled. In such a + // configuration it's illegal to emit exception-related instructions so + // it's not possible to unwind. + let target_supports_unwinding = !(tcx.sess.target.is_like_wasm + && tcx.sess.panic_strategy() == PanicStrategy::Abort + && !tcx.asm_target_features(def_id).contains(&sym::exception_handling)); + // Here we test for this function itself whether its ABI allows // unwinding or not. let body_ty = tcx.type_of(def_id).skip_binder(); @@ -54,12 +68,18 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { let Some(terminator) = &mut block.terminator else { continue }; let span = terminator.source_info.span; - // If we see an `UnwindResume` terminator inside a function that cannot unwind, we need - // to replace it with `UnwindTerminate`. - if let TerminatorKind::UnwindResume = &terminator.kind - && !body_can_unwind - { - terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi); + // If we see an `UnwindResume` terminator inside a function then: + // + // * If the target doesn't support unwinding at all, then this is an + // unreachable block. + // * If the body cannot unwind, we need to replace it with + // `UnwindTerminate`. + if let TerminatorKind::UnwindResume = &terminator.kind { + if !target_supports_unwinding { + terminator.kind = TerminatorKind::Unreachable; + } else if !body_can_unwind { + terminator.kind = TerminatorKind::UnwindTerminate(UnwindTerminateReason::Abi); + } } if block.is_cleanup { @@ -93,8 +113,9 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { _ => continue, }; - if !call_can_unwind { - // If this function call can't unwind, then there's no need for it + if !call_can_unwind || !target_supports_unwinding { + // If this function call can't unwind, or if the target doesn't + // support unwinding at all, then there's no need for it // to have a landing pad. This means that we can remove any cleanup // registered for it (and turn it into `UnwindAction::Unreachable`). let cleanup = block.terminator_mut().unwind_mut().unwrap(); diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 4603c695ded..c5cd06f170c 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -86,7 +86,6 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::source_map::dummy_spanned; use rustc_span::symbol::sym; use rustc_span::{DUMMY_SP, Span}; -use rustc_target::spec::PanicStrategy; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; @@ -1149,7 +1148,7 @@ fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, typing_env: ty::Typing fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { // Nothing can unwind when landing pads are off. - if tcx.sess.panic_strategy() == PanicStrategy::Abort { + if !tcx.sess.panic_strategy().unwinds() { return false; } @@ -1340,14 +1339,13 @@ fn create_cases<'tcx>( } } - if operation == Operation::Resume { + if operation == Operation::Resume && point.resume_arg != CTX_ARG.into() { // Move the resume argument to the destination place of the `Yield` terminator - let resume_arg = CTX_ARG; statements.push(Statement::new( source_info, StatementKind::Assign(Box::new(( point.resume_arg, - Rvalue::Use(Operand::Move(resume_arg.into())), + Rvalue::Use(Operand::Move(CTX_ARG.into())), ))), )); } @@ -1439,7 +1437,10 @@ fn check_field_tys_sized<'tcx>( } impl<'tcx> crate::MirPass<'tcx> for StateTransform { + #[instrument(level = "debug", skip(self, tcx, body), ret)] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + debug!(def_id = ?body.source.def_id()); + let Some(old_yield_ty) = body.yield_ty() else { // This only applies to coroutines return; @@ -1518,31 +1519,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { cleanup_async_drops(body); } - // We also replace the resume argument and insert an `Assign`. - // This is needed because the resume argument `_2` might be live across a `yield`, in which - // case there is no `Assign` to it that the transform can turn into a store to the coroutine - // state. After the yield the slot in the coroutine state would then be uninitialized. - let resume_local = CTX_ARG; - let resume_ty = body.local_decls[resume_local].ty; - let old_resume_local = replace_local(resume_local, resume_ty, body, tcx); - - // When first entering the coroutine, move the resume argument into its old local - // (which is now a generator interior). - let source_info = SourceInfo::outermost(body.span); - let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements; - stmts.insert( - 0, - Statement::new( - source_info, - StatementKind::Assign(Box::new(( - old_resume_local.into(), - Rvalue::Use(Operand::Move(resume_local.into())), - ))), - ), - ); - let always_live_locals = always_storage_live_locals(body); - let movable = coroutine_kind.movability() == hir::Movability::Movable; let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); @@ -1583,6 +1560,21 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { }; transform.visit_body(body); + // MIR parameters are not explicitly assigned-to when entering the MIR body. + // If we want to save their values inside the coroutine state, we need to do so explicitly. + let source_info = SourceInfo::outermost(body.span); + let args_iter = body.args_iter(); + body.basic_blocks.as_mut()[START_BLOCK].statements.splice( + 0..0, + args_iter.filter_map(|local| { + let (ty, variant_index, idx) = transform.remap[local]?; + let lhs = transform.make_field(variant_index, idx, ty); + let rhs = Rvalue::Use(Operand::Move(local.into())); + let assign = StatementKind::Assign(Box::new((lhs, rhs))); + Some(Statement::new(source_info, assign)) + }), + ); + // Update our MIR struct to reflect the changes we've made body.arg_count = 2; // self, resume arg body.spread_arg = None; @@ -1891,7 +1883,7 @@ fn check_must_not_suspend_ty<'tcx>( } has_emitted } - ty::Dynamic(binder, _, _) => { + ty::Dynamic(binder, _) => { let mut has_emitted = false; for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index fe53de31f75..491e910ff6f 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -412,18 +412,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { state: &mut State<FlatSet<Scalar>>, ) -> ValueOrPlace<FlatSet<Scalar>> { let val = match rvalue { - Rvalue::Len(place) => { - let place_ty = place.ty(self.local_decls, self.tcx); - if let ty::Array(_, len) = place_ty.ty.kind() { - Const::Ty(self.tcx.types.usize, *len) - .try_eval_scalar(self.tcx, self.typing_env) - .map_or(FlatSet::Top, FlatSet::Elem) - } else if let [ProjectionElem::Deref] = place.projection[..] { - state.get_len(place.local.into(), &self.map) - } else { - FlatSet::Top - } - } Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => { let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { return ValueOrPlace::Value(FlatSet::Top); @@ -465,22 +453,30 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { let (val, _overflow) = self.binary_op(state, *op, left, right); val } - Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) { - FlatSet::Elem(value) => self - .ecx - .unary_op(*op, &value) - .discard_err() - .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), - FlatSet::Bottom => FlatSet::Bottom, - FlatSet::Top => FlatSet::Top, - }, + Rvalue::UnaryOp(op, operand) => { + if let UnOp::PtrMetadata = op + && let Some(place) = operand.place() + && let Some(len) = self.map.find_len(place.as_ref()) + { + return ValueOrPlace::Place(len); + } + match self.eval_operand(operand, state) { + FlatSet::Elem(value) => self + .ecx + .unary_op(*op, &value) + .discard_err() + .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), + FlatSet::Bottom => FlatSet::Bottom, + FlatSet::Top => FlatSet::Top, + } + } Rvalue::NullaryOp(null_op, ty) => { let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { return ValueOrPlace::Value(FlatSet::Top); }; let val = match null_op { NullOp::SizeOf if layout.is_sized() => layout.size.bytes(), - NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(), + NullOp::AlignOf if layout.is_sized() => layout.align.bytes(), NullOp::OffsetOf(fields) => self .ecx .tcx diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 9ba2d274691..74c22ff10c1 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -141,7 +141,7 @@ use rustc_data_structures::union_find::UnionFind; use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_index::{IndexVec, newtype_index}; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals}; @@ -153,15 +153,7 @@ pub(super) struct DestinationPropagation; impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // For now, only run at MIR opt level 3. Two things need to be changed before this can be - // turned on by default: - // 1. Because of the overeager removal of storage statements, this can cause stack space - // regressions. This opt is not the place to fix this though, it's a more general - // problem in MIR. - // 2. Despite being an overall perf improvement, this still causes a 30% regression in - // keccak. We can temporarily fix this by bounding function size, but in the long term - // we should fix this by being smarter about invalidating analysis results. - sess.mir_opt_level() >= 3 + sess.mir_opt_level() >= 2 } #[tracing::instrument(level = "trace", skip(self, tcx, body))] @@ -511,22 +503,6 @@ impl TwoStepIndex { } } -struct VisitPlacesWith<F>(F); - -impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith<F> -where - F: FnMut(Place<'tcx>, PlaceContext), -{ - fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { - (self.0)(local.into(), ctxt); - } - - fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { - (self.0)(*place, ctxt); - self.visit_projection(place.as_ref(), ctxt, location); - } -} - /// Add points depending on the result of the given dataflow analysis. fn save_as_intervals<'tcx>( elements: &DenseLocationMap, @@ -567,13 +543,15 @@ fn save_as_intervals<'tcx>( // the written-to locals as live in the second half of the statement. // We also ensure that operands read by terminators conflict with writes by that terminator. // For instance a function call may read args after having written to the destination. - VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { - DefUse::Def | DefUse::Use | DefUse::PartialWrite => { - if let Some(relevant) = relevant.shrink[place.local] { - values.insert(relevant, twostep); + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + if let Some(relevant) = relevant.shrink[place.local] { + match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::Use | DefUse::PartialWrite => { + values.insert(relevant, twostep); + } + DefUse::NonUse => {} } } - DefUse::NonUse => {} }) .visit_terminator(term, loc); @@ -588,15 +566,32 @@ fn save_as_intervals<'tcx>( twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1); debug_assert_eq!(twostep, two_step_loc(loc, Effect::After)); append_at(&mut values, &state, twostep); - // Ensure we have a non-zero live range even for dead stores. This is done by marking - // all the written-to locals as live in the second half of the statement. - VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { - DefUse::Def | DefUse::PartialWrite => { - if let Some(relevant) = relevant.shrink[place.local] { - values.insert(relevant, twostep); + // Like terminators, ensure we have a non-zero live range even for dead stores. + // Some rvalues interleave reads and writes, for instance `Rvalue::Aggregate`, see + // https://github.com/rust-lang/rust/issues/146383. By precaution, treat statements + // as behaving so by default. + // We make an exception for simple assignments `_a.stuff = {copy|move} _b.stuff`, + // as marking `_b` live here would prevent unification. + let is_simple_assignment = match stmt.kind { + StatementKind::Assign(box ( + lhs, + Rvalue::CopyForDeref(rhs) + | Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), + )) => lhs.projection == rhs.projection, + _ => false, + }; + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + if let Some(relevant) = relevant.shrink[place.local] { + match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::PartialWrite => { + values.insert(relevant, twostep); + } + DefUse::Use if !is_simple_assignment => { + values.insert(relevant, twostep); + } + DefUse::Use | DefUse::NonUse => {} } } - DefUse::Use | DefUse::NonUse => {} }) .visit_statement(stmt, loc); diff --git a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs index abbff1c48dd..7c66783548e 100644 --- a/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs +++ b/compiler/rustc_mir_transform/src/ffi_unwind_calls.rs @@ -101,12 +101,15 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool { } fn required_panic_strategy(tcx: TyCtxt<'_>, _: LocalCrate) -> Option<PanicStrategy> { + let local_strategy = tcx.sess.panic_strategy(); + if tcx.is_panic_runtime(LOCAL_CRATE) { - return Some(tcx.sess.panic_strategy()); + return Some(local_strategy); } - if tcx.sess.panic_strategy() == PanicStrategy::Abort { - return Some(PanicStrategy::Abort); + match local_strategy { + PanicStrategy::Abort | PanicStrategy::ImmediateAbort => return Some(local_strategy), + _ => {} } for def_id in tcx.hir_body_owners() { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bf6aa800d20..29f6879aacd 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -85,8 +85,10 @@ //! that contain `AllocId`s. use std::borrow::Cow; +use std::hash::{Hash, Hasher}; use either::Either; +use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_const_eval::const_eval::DummyMachine; @@ -94,7 +96,7 @@ use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, intern_const_alloc_for_constprop, }; -use rustc_data_structures::fx::{FxIndexSet, MutableValues}; +use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; @@ -152,9 +154,29 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { } newtype_index! { + /// This represents a `Value` in the symbolic execution. + #[debug_format = "_v{}"] struct VnIndex {} } +/// Marker type to forbid hashing and comparing opaque values. +/// This struct should only be constructed by `ValueSet::insert_unique` to ensure we use that +/// method to create non-unifiable values. It will ICE if used in `ValueSet::insert`. +#[derive(Copy, Clone, Debug, Eq)] +struct VnOpaque; +impl PartialEq for VnOpaque { + fn eq(&self, _: &VnOpaque) -> bool { + // ICE if we try to compare unique values + unreachable!() + } +} +impl Hash for VnOpaque { + fn hash<T: Hasher>(&self, _: &mut T) { + // ICE if we try to hash unique values + unreachable!() + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), @@ -166,15 +188,17 @@ enum Value<'tcx> { // Root values. /// Used to represent values we know nothing about. /// The `usize` is a counter incremented by `new_opaque`. - Opaque(usize), + Opaque(VnOpaque), /// Evaluated or unevaluated constant value. Constant { value: Const<'tcx>, /// Some constants do not have a deterministic value. To avoid merging two instances of the /// same `Const`, we assign them an additional integer index. - // `disambiguator` is 0 iff the constant is deterministic. - disambiguator: usize, + // `disambiguator` is `None` iff the constant is deterministic. + disambiguator: Option<VnOpaque>, }, + + // Aggregates. /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(VariantIdx, Vec<VnIndex>), @@ -192,7 +216,7 @@ enum Value<'tcx> { place: Place<'tcx>, kind: AddressKind, /// Give each borrow and pointer a different provenance, so we don't merge them. - provenance: usize, + provenance: VnOpaque, }, // Extractions. @@ -200,8 +224,6 @@ enum Value<'tcx> { Projection(VnIndex, ProjectionElem<VnIndex, ()>), /// Discriminant of the given value. Discriminant(VnIndex), - /// Length of an array or slice. - Len(VnIndex), // Operations. NullaryOp(NullOp<'tcx>, Ty<'tcx>), @@ -213,6 +235,107 @@ enum Value<'tcx> { }, } +/// Stores and deduplicates pairs of `(Value, Ty)` into in `VnIndex` numbered values. +/// +/// This data structure is mostly a partial reimplementation of `FxIndexMap<VnIndex, (Value, Ty)>`. +/// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction, +/// like opaque values, address with provenance and non-deterministic constants. +struct ValueSet<'tcx> { + indices: HashTable<VnIndex>, + hashes: IndexVec<VnIndex, u64>, + values: IndexVec<VnIndex, Value<'tcx>>, + types: IndexVec<VnIndex, Ty<'tcx>>, +} + +impl<'tcx> ValueSet<'tcx> { + fn new(num_values: usize) -> ValueSet<'tcx> { + ValueSet { + indices: HashTable::with_capacity(num_values), + hashes: IndexVec::with_capacity(num_values), + values: IndexVec::with_capacity(num_values), + types: IndexVec::with_capacity(num_values), + } + } + + /// Insert a `(Value, Ty)` pair without hashing or deduplication. + /// This always creates a new `VnIndex`. + #[inline] + fn insert_unique( + &mut self, + ty: Ty<'tcx>, + value: impl FnOnce(VnOpaque) -> Value<'tcx>, + ) -> VnIndex { + let value = value(VnOpaque); + + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => true, + Value::Constant { disambiguator, .. } => disambiguator.is_some(), + _ => false, + }); + + let index = self.hashes.push(0); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + index + } + + /// Insert a `(Value, Ty)` pair to be deduplicated. + /// Returns `true` as second tuple field if this value did not exist previously. + #[allow(rustc::pass_by_value)] // closures take `&VnIndex` + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => false, + Value::Constant { disambiguator, .. } => disambiguator.is_none(), + _ => true, + }); + + let hash: u64 = { + let mut h = FxHasher::default(); + value.hash(&mut h); + ty.hash(&mut h); + h.finish() + }; + + let eq = |index: &VnIndex| self.values[*index] == value && self.types[*index] == ty; + let hasher = |index: &VnIndex| self.hashes[*index]; + match self.indices.entry(hash, eq, hasher) { + Entry::Occupied(entry) => { + let index = *entry.get(); + (index, false) + } + Entry::Vacant(entry) => { + let index = self.hashes.push(hash); + entry.insert(index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + (index, true) + } + } + } + + /// Return the `Value` associated with the given `VnIndex`. + #[inline] + fn value(&self, index: VnIndex) -> &Value<'tcx> { + &self.values[index] + } + + /// Return the type associated with the given `VnIndex`. + #[inline] + fn ty(&self, index: VnIndex) -> Ty<'tcx> { + self.types[index] + } + + /// Replace the value associated with `index` with an opaque value. + #[inline] + fn forget(&mut self, index: VnIndex) { + self.values[index] = Value::Opaque(VnOpaque); + } +} + struct VnState<'body, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, @@ -223,11 +346,9 @@ struct VnState<'body, 'tcx> { /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec<VnIndex, SmallVec<[Local; 1]>>, - values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>, + values: ValueSet<'tcx>, /// Values evaluated as constants if possible. evaluated: IndexVec<VnIndex, Option<OpTy<'tcx>>>, - /// Counter to generate different values. - next_opaque: usize, /// Cache the deref values. derefs: Vec<VnIndex>, ssa: &'body SsaLocals, @@ -258,9 +379,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { is_coroutine: body.coroutine.is_some(), locals: IndexVec::from_elem(None, local_decls), rev_locals: IndexVec::with_capacity(num_values), - values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), + values: ValueSet::new(num_values), evaluated: IndexVec::with_capacity(num_values), - next_opaque: 1, derefs: Vec::new(), ssa, dominators, @@ -274,8 +394,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { - let (index, new) = self.values.insert_full((value, ty)); - let index = VnIndex::from_usize(index); + let (index, new) = self.values.insert(ty, value); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. let evaluated = self.eval_to_const(index); @@ -287,18 +406,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { index } - fn next_opaque(&mut self) -> usize { - let next_opaque = self.next_opaque; - self.next_opaque += 1; - next_opaque - } - /// Create a new `Value` for which we have no information at all, except that it is distinct /// from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { - let value = Value::Opaque(self.next_opaque()); - self.insert(ty, value) + let index = self.values.insert_unique(ty, Value::Opaque); + let _index = self.evaluated.push(None); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index } /// Create a new `Value::Address` distinct from all the others. @@ -311,18 +428,49 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let value = Value::Address { place, kind, provenance: self.next_opaque() }; - self.insert(ty, value) + let index = + self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance }); + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index + } + + #[instrument(level = "trace", skip(self), ret)] + fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { + let (index, new) = if value.is_deterministic() { + // The constant is deterministic, no need to disambiguate. + let constant = Value::Constant { value, disambiguator: None }; + self.values.insert(value.ty(), constant) + } else { + // Multiple mentions of this constant will yield different values, + // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. + let index = self.values.insert_unique(value.ty(), |disambiguator| Value::Constant { + value, + disambiguator: Some(disambiguator), + }); + (index, true) + }; + if new { + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + } + index } #[inline] fn get(&self, index: VnIndex) -> &Value<'tcx> { - &self.values.get_index(index.as_usize()).unwrap().0 + self.values.value(index) } #[inline] fn ty(&self, index: VnIndex) -> Ty<'tcx> { - self.values.get_index(index.as_usize()).unwrap().1 + self.values.ty(index) } /// Record that `local` is assigned `value`. `local` must be SSA. @@ -333,33 +481,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.rev_locals[value].push(local); } - fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { - let disambiguator = if value.is_deterministic() { - // The constant is deterministic, no need to disambiguate. - 0 - } else { - // Multiple mentions of this constant will yield different values, - // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let disambiguator = self.next_opaque(); - // `disambiguator: 0` means deterministic. - debug_assert_ne!(disambiguator, 0); - disambiguator - }; - self.insert(value.ty(), Value::Constant { value, disambiguator }) - } - fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(ty, Value::Constant { value, disambiguator: 0 }) + self.insert(ty, Value::Constant { value, disambiguator: None }) } fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec<VnIndex>) -> VnIndex { @@ -374,8 +507,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn invalidate_derefs(&mut self) { for deref in std::mem::take(&mut self.derefs) { - let opaque = self.next_opaque(); - self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque); + self.values.forget(deref); } } @@ -477,11 +609,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?; discr_value.into() } - Len(slice) => { - let slice = self.evaluated[slice].as_ref()?; - let len = slice.len(&self.ecx).discard_err()?; - ImmTy::from_uint(len, ty).into() - } NullaryOp(null_op, arg_ty) => { let arg_layout = self.ecx.layout_of(arg_ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op @@ -491,7 +618,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } let val = match null_op { NullOp::SizeOf => arg_layout.size.bytes(), - NullOp::AlignOf => arg_layout.align.abi.bytes(), + NullOp::AlignOf => arg_layout.align.bytes(), NullOp::OffsetOf(fields) => self .ecx .tcx @@ -841,7 +968,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Operations. - Rvalue::Len(ref mut place) => return self.simplify_len(place, location), Rvalue::Cast(ref mut kind, ref mut value, to) => { return self.simplify_cast(kind, value, to, location); } @@ -1049,7 +1175,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if op == UnOp::PtrMetadata { let mut was_updated = false; loop { - match self.get(arg_index) { + arg_index = match self.get(arg_index) { // Pointer casts that preserve metadata, such as // `*const [i32]` <-> `*mut [i32]` <-> `*mut [f32]`. // It's critical that this not eliminate cases like @@ -1061,9 +1187,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Cast { kind: CastKind::PtrToPtr, value: inner } if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) => { - arg_index = *inner; - was_updated = true; - continue; + *inner + } + + // We have an unsizing cast, which assigns the length to wide pointer metadata. + Value::Cast { + kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), + value: from, + } if let Some(from) = self.ty(*from).builtin_deref(true) + && let ty::Array(_, len) = from.kind() + && let Some(to) = self.ty(arg_index).builtin_deref(true) + && let ty::Slice(..) = to.kind() => + { + return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } // `&mut *p`, `&raw *p`, etc don't change metadata. @@ -1072,18 +1208,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { place.as_ref() && let Some(local_index) = self.locals[local] => { - arg_index = local_index; - was_updated = true; - continue; + local_index } - _ => { - if was_updated && let Some(op) = self.try_as_operand(arg_index, location) { - *arg_op = op; - } - break; - } - } + _ => break, + }; + was_updated = true; + } + + if was_updated && let Some(op) = self.try_as_operand(arg_index, location) { + *arg_op = op; } } @@ -1407,39 +1541,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Some(self.insert(to, Value::Cast { kind, value })) } - fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option<VnIndex> { - // Trivial case: we are fetching a statically known length. - let place_ty = place.ty(self.local_decls, self.tcx).ty; - if let ty::Array(_, len) = place_ty.kind() { - return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); - } - - let mut inner = self.simplify_place_value(place, location)?; - - // The length information is stored in the wide pointer. - // Reborrowing copies length information from one pointer to the other. - while let Value::Address { place: borrowed, .. } = self.get(inner) - && let [PlaceElem::Deref] = borrowed.projection[..] - && let Some(borrowed) = self.locals[borrowed.local] - { - inner = borrowed; - } - - // We have an unsizing cast, which assigns the length to wide pointer metadata. - if let Value::Cast { kind, value: from } = self.get(inner) - && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind - && let Some(from) = self.ty(*from).builtin_deref(true) - && let ty::Array(_, len) = from.kind() - && let Some(to) = self.ty(inner).builtin_deref(true) - && let ty::Slice(..) = to.kind() - { - return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); - } - - // Fallback: a symbolic `Len`. - Some(self.insert(self.tcx.types.usize, Value::Len(inner))) - } - fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool { let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); @@ -1605,7 +1706,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: 0 } = *self.get(index) { + if let Value::Constant { value, disambiguator: None } = *self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index b03518de00a..883ee32bdec 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -28,6 +28,7 @@ use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind}; use rustc_middle::ty::{TyCtxt, TypeFlags, TypeVisitableExt}; +use rustc_span::def_id::DefId; use rustc_trait_selection::traits; use tracing::trace; @@ -35,23 +36,29 @@ use crate::pass_manager::MirPass; pub(crate) struct ImpossiblePredicates; +fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let predicates = tcx.predicates_of(def_id).instantiate_identity(tcx); + tracing::trace!(?predicates); + let predicates = predicates.predicates.into_iter().filter(|p| { + !p.has_type_flags( + // Only consider global clauses to simplify. + TypeFlags::HAS_FREE_LOCAL_NAMES + // Clauses that refer to unevaluated constants as they cause cycles. + | TypeFlags::HAS_CT_PROJECTION, + ) + }); + let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); + tracing::trace!(?predicates); + predicates.references_error() || traits::impossible_predicates(tcx, predicates) +} + impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { #[tracing::instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tracing::trace!(def_id = ?body.source.def_id()); - let predicates = tcx.predicates_of(body.source.def_id()).instantiate_identity(tcx); - tracing::trace!(?predicates); - let predicates = predicates.predicates.into_iter().filter(|p| { - !p.has_type_flags( - // Only consider global clauses to simplify. - TypeFlags::HAS_FREE_LOCAL_NAMES - // Clauses that refer to unevaluated constants as they cause cycles. - | TypeFlags::HAS_CT_PROJECTION, - ) - }); - let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); - tracing::trace!(?predicates); - if predicates.references_error() || traits::impossible_predicates(tcx, predicates) { + let impossible = body.tainted_by_errors.is_some() + || has_impossible_predicates(tcx, body.source.def_id()); + if impossible { trace!("found unsatisfiable predicates"); // Clear the body to only contain a single `unreachable` statement. let bbs = body.basic_blocks.as_mut(); diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index f9e642e28eb..68298767e7f 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -84,7 +84,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { body, arena, map: Map::new(tcx, body, Some(MAX_PLACES)), - loop_headers: loop_headers(body), + maybe_loop_headers: maybe_loop_headers(body), opportunities: Vec::new(), }; @@ -100,7 +100,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { // Verify that we do not thread through a loop header. for to in opportunities.iter() { - assert!(to.chain.iter().all(|&block| !finder.loop_headers.contains(block))); + assert!(to.chain.iter().all(|&block| !finder.maybe_loop_headers.contains(block))); } OpportunitySet::new(body, opportunities).apply(body); } @@ -124,7 +124,7 @@ struct TOFinder<'a, 'tcx> { ecx: InterpCx<'tcx, DummyMachine>, body: &'a Body<'tcx>, map: Map<'tcx>, - loop_headers: DenseBitSet<BasicBlock>, + maybe_loop_headers: DenseBitSet<BasicBlock>, /// We use an arena to avoid cloning the slices when cloning `state`. arena: &'a DroplessArena, opportunities: Vec<ThreadingOpportunity>, @@ -190,7 +190,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { #[instrument(level = "trace", skip(self))] fn start_from_switch(&mut self, bb: BasicBlock) { let bbdata = &self.body[bb]; - if bbdata.is_cleanup || self.loop_headers.contains(bb) { + if bbdata.is_cleanup || self.maybe_loop_headers.contains(bb) { return; } let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return }; @@ -235,7 +235,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { depth: usize, ) { // Do not thread through loop headers. - if self.loop_headers.contains(bb) { + if self.maybe_loop_headers.contains(bb) { return; } @@ -833,20 +833,28 @@ enum Update { Decr, } -/// Compute the set of loop headers in the given body. We define a loop header as a block which has -/// at least a predecessor which it dominates. This definition is only correct for reducible CFGs. -/// But if the CFG is already irreducible, there is no point in trying much harder. -/// is already irreducible. -fn loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> { - let mut loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); - let dominators = body.basic_blocks.dominators(); - // Only visit reachable blocks. - for (bb, bbdata) in traversal::preorder(body) { +/// Compute the set of loop headers in the given body. A loop header is usually defined as a block +/// which dominates one of its predecessors. This definition is only correct for reducible CFGs. +/// However, computing dominators is expensive, so we approximate according to the post-order +/// traversal order. A loop header for us is a block which is visited after its predecessor in +/// post-order. This is ok as we mostly need a heuristic. +fn maybe_loop_headers(body: &Body<'_>) -> DenseBitSet<BasicBlock> { + let mut maybe_loop_headers = DenseBitSet::new_empty(body.basic_blocks.len()); + let mut visited = DenseBitSet::new_empty(body.basic_blocks.len()); + for (bb, bbdata) in traversal::postorder(body) { + // Post-order means we visit successors before the block for acyclic CFGs. + // If the successor is not visited yet, consider it a loop header. for succ in bbdata.terminator().successors() { - if dominators.dominates(succ, bb) { - loop_headers.insert(succ); + if !visited.contains(succ) { + maybe_loop_headers.insert(succ); } } + + // Only mark `bb` as visited after we checked the successors, in case we have a self-loop. + // bb1: goto -> bb1; + let _new = visited.insert(bb); + debug_assert!(_new); } - loop_headers + + maybe_loop_headers } diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 481c7941909..5fffba55f17 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -441,7 +441,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | Rvalue::Use(..) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) - | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::Discriminant(..) @@ -604,27 +603,13 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - Len(place) => { - let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind() - { - n.try_to_target_usize(self.tcx)? - } else { - match self.get_const(place)? { - Value::Immediate(src) => src.len(&self.ecx).discard_err()?, - Value::Aggregate { fields, .. } => fields.len() as u64, - Value::Uninit => return None, - } - }; - ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into() - } - Ref(..) | RawPtr(..) => return None, NullaryOp(ref null_op, ty) => { let op_layout = self.ecx.layout_of(ty).ok()?; let val = match null_op { NullOp::SizeOf => op_layout.size.bytes(), - NullOp::AlignOf => op_layout.align.abi.bytes(), + NullOp::AlignOf => op_layout.align.bytes(), NullOp::OffsetOf(fields) => self .tcx .offset_of_subfield(self.typing_env, op_layout, fields.iter()) diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 1663dfa744f..9ff7e0b5500 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -156,7 +156,6 @@ declare_passes! { mod match_branches : MatchBranchSimplification; mod mentioned_items : MentionedItems; mod multiple_return_terminators : MultipleReturnTerminators; - mod nrvo : RenameReturnPlace; mod post_drop_elaboration : CheckLiveDrops; mod prettify : ReorderBasicBlocks, ReorderLocals; mod promote_consts : PromoteTemps; @@ -715,7 +714,6 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &jump_threading::JumpThreading, &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, - &dest_prop::DestinationPropagation, &o1(simplify_branches::SimplifyConstCondition::Final), &o1(remove_noop_landing_pads::RemoveNoopLandingPads), &o1(simplify::SimplifyCfg::Final), @@ -723,7 +721,7 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &strip_debuginfo::StripDebugInfo, ©_prop::CopyProp, &dead_store_elimination::DeadStoreElimination::Final, - &nrvo::RenameReturnPlace, + &dest_prop::DestinationPropagation, &simplify::SimplifyLocals::Final, &multiple_return_terminators::MultipleReturnTerminators, &large_enums::EnumSizeOpt { discrepancy: 128 }, diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index f472c7cb493..2ab49645dc4 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::DenseBitSet; -use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::visit::{PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive, always_storage_live_locals}; @@ -79,15 +79,39 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match &statement.kind { StatementKind::Assign(box (dest, rvalue)) => { - if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue { - // The sides of an assignment must not alias. Currently this just checks whether - // the places are identical. - if dest == src { - self.fail( - location, - "encountered `Assign` statement with overlapping memory", - ); - } + let forbid_aliasing = match rvalue { + Rvalue::Use(..) + | Rvalue::CopyForDeref(..) + | Rvalue::Repeat(..) + | Rvalue::Aggregate(..) + | Rvalue::Cast(..) + | Rvalue::ShallowInitBox(..) + | Rvalue::WrapUnsafeBinder(..) => true, + Rvalue::ThreadLocalRef(..) + | Rvalue::NullaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::BinaryOp(..) + | Rvalue::Ref(..) + | Rvalue::RawPtr(..) + | Rvalue::Discriminant(..) => false, + }; + // The sides of an assignment must not alias. + if forbid_aliasing { + VisitPlacesWith(|src: Place<'tcx>, _| { + if *dest == src + || (dest.local == src.local + && !dest.is_indirect() + && !src.is_indirect()) + { + self.fail( + location, + format!( + "encountered `{statement:?}` statement with overlapping memory" + ), + ); + } + }) + .visit_rvalue(rvalue, location); } } StatementKind::StorageLive(local) => { diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs deleted file mode 100644 index 965002aae04..00000000000 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! See the docs for [`RenameReturnPlace`]. - -use rustc_hir::Mutability; -use rustc_index::bit_set::DenseBitSet; -use rustc_middle::bug; -use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{self, BasicBlock, Local, Location}; -use rustc_middle::ty::TyCtxt; -use tracing::{debug, trace}; - -/// This pass looks for MIR that always copies the same local into the return place and eliminates -/// the copy by renaming all uses of that local to `_0`. -/// -/// This allows LLVM to perform an optimization similar to the named return value optimization -/// (NRVO) that is guaranteed in C++. This avoids a stack allocation and `memcpy` for the -/// relatively common pattern of allocating a buffer on the stack, mutating it, and returning it by -/// value like so: -/// -/// ```rust -/// fn foo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { -/// let mut buf = [0; 1024]; -/// init(&mut buf); -/// buf -/// } -/// ``` -/// -/// For now, this pass is very simple and only capable of eliminating a single copy. A more general -/// version of copy propagation, such as the one based on non-overlapping live ranges in [#47954] and -/// [#71003], could yield even more benefits. -/// -/// [#47954]: https://github.com/rust-lang/rust/pull/47954 -/// [#71003]: https://github.com/rust-lang/rust/pull/71003 -pub(super) struct RenameReturnPlace; - -impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // unsound: #111005 - sess.mir_opt_level() > 0 && sess.opts.unstable_opts.unsound_mir_opts - } - - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { - let def_id = body.source.def_id(); - let Some(returned_local) = local_eligible_for_nrvo(body) else { - debug!("`{:?}` was ineligible for NRVO", def_id); - return; - }; - - debug!( - "`{:?}` was eligible for NRVO, making {:?} the return place", - def_id, returned_local - ); - - RenameToReturnPlace { tcx, to_rename: returned_local }.visit_body_preserves_cfg(body); - - // Clean up the `NOP`s we inserted for statements made useless by our renaming. - for block_data in body.basic_blocks.as_mut_preserves_cfg() { - block_data.statements.retain(|stmt| stmt.kind != mir::StatementKind::Nop); - } - - // Overwrite the debuginfo of `_0` with that of the renamed local. - let (renamed_decl, ret_decl) = - body.local_decls.pick2_mut(returned_local, mir::RETURN_PLACE); - - // Sometimes, the return place is assigned a local of a different but coercible type, for - // example `&mut T` instead of `&T`. Overwriting the `LocalInfo` for the return place means - // its type may no longer match the return type of its function. This doesn't cause a - // problem in codegen because these two types are layout-compatible, but may be unexpected. - debug!("_0: {:?} = {:?}: {:?}", ret_decl.ty, returned_local, renamed_decl.ty); - ret_decl.clone_from(renamed_decl); - - // The return place is always mutable. - ret_decl.mutability = Mutability::Mut; - } - - fn is_required(&self) -> bool { - false - } -} - -/// MIR that is eligible for the NRVO must fulfill two conditions: -/// 1. The return place must not be read prior to the `Return` terminator. -/// 2. A simple assignment of a whole local to the return place (e.g., `_0 = _1`) must be the -/// only definition of the return place reaching the `Return` terminator. -/// -/// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned -/// to the return place along all possible paths through the control-flow graph. -fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option<Local> { - if IsReturnPlaceRead::run(body) { - return None; - } - - let mut copied_to_return_place = None; - for block in body.basic_blocks.indices() { - // Look for blocks with a `Return` terminator. - if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) { - continue; - } - - // Look for an assignment of a single local to the return place prior to the `Return`. - let returned_local = find_local_assigned_to_return_place(block, body)?; - match body.local_kind(returned_local) { - // FIXME: Can we do this for arguments as well? - mir::LocalKind::Arg => return None, - - mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"), - mir::LocalKind::Temp => {} - } - - // If multiple different locals are copied to the return place. We can't pick a - // single one to rename. - if copied_to_return_place.is_some_and(|old| old != returned_local) { - return None; - } - - copied_to_return_place = Some(returned_local); - } - - copied_to_return_place -} - -fn find_local_assigned_to_return_place(start: BasicBlock, body: &mir::Body<'_>) -> Option<Local> { - let mut block = start; - let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); - - // Iterate as long as `block` has exactly one predecessor that we have not yet visited. - while seen.insert(block) { - trace!("Looking for assignments to `_0` in {:?}", block); - - let local = body[block].statements.iter().rev().find_map(as_local_assigned_to_return_place); - if local.is_some() { - return local; - } - - match body.basic_blocks.predecessors()[block].as_slice() { - &[pred] => block = pred, - _ => return None, - } - } - - None -} - -// If this statement is an assignment of an unprojected local to the return place, -// return that local. -fn as_local_assigned_to_return_place(stmt: &mir::Statement<'_>) -> Option<Local> { - if let mir::StatementKind::Assign(box (lhs, rhs)) = &stmt.kind { - if lhs.as_local() == Some(mir::RETURN_PLACE) { - if let mir::Rvalue::Use(mir::Operand::Copy(rhs) | mir::Operand::Move(rhs)) = rhs { - return rhs.as_local(); - } - } - } - - None -} - -struct RenameToReturnPlace<'tcx> { - to_rename: Local, - tcx: TyCtxt<'tcx>, -} - -/// Replaces all uses of `self.to_rename` with `_0`. -impl<'tcx> MutVisitor<'tcx> for RenameToReturnPlace<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_statement(&mut self, stmt: &mut mir::Statement<'tcx>, loc: Location) { - // Remove assignments of the local being replaced to the return place, since it is now the - // return place: - // _0 = _1 - if as_local_assigned_to_return_place(stmt) == Some(self.to_rename) { - stmt.kind = mir::StatementKind::Nop; - return; - } - - // Remove storage annotations for the local being replaced: - // StorageLive(_1) - if let mir::StatementKind::StorageLive(local) | mir::StatementKind::StorageDead(local) = - stmt.kind - { - if local == self.to_rename { - stmt.kind = mir::StatementKind::Nop; - return; - } - } - - self.super_statement(stmt, loc) - } - - fn visit_terminator(&mut self, terminator: &mut mir::Terminator<'tcx>, loc: Location) { - // Ignore the implicit "use" of the return place in a `Return` statement. - if let mir::TerminatorKind::Return = terminator.kind { - return; - } - - self.super_terminator(terminator, loc); - } - - fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) { - if *l == mir::RETURN_PLACE { - assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo)); - } else if *l == self.to_rename { - *l = mir::RETURN_PLACE; - } - } -} - -struct IsReturnPlaceRead(bool); - -impl IsReturnPlaceRead { - fn run(body: &mir::Body<'_>) -> bool { - let mut vis = IsReturnPlaceRead(false); - vis.visit_body(body); - vis.0 - } -} - -impl<'tcx> Visitor<'tcx> for IsReturnPlaceRead { - fn visit_local(&mut self, l: Local, ctxt: PlaceContext, _: Location) { - if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() { - self.0 = true; - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, loc: Location) { - // Ignore the implicit "use" of the return place in a `Return` statement. - if let mir::TerminatorKind::Return = terminator.kind { - return; - } - - self.super_terminator(terminator, loc); - } -} diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index c781d1a5324..cc8ea76011b 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -1,4 +1,5 @@ -use rustc_index::{Idx, IndexVec}; +use rustc_data_structures::fx::FxHashMap; +use rustc_index::Idx; use rustc_middle::mir::*; use rustc_middle::ty::Ty; use rustc_span::Span; @@ -9,7 +10,9 @@ use tracing::debug; /// and replacement of terminators, and then apply the queued changes all at /// once with `apply`. This is useful for MIR transformation passes. pub(crate) struct MirPatch<'tcx> { - term_patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>, + term_patch_map: FxHashMap<BasicBlock, TerminatorKind<'tcx>>, + /// Set of statements that should be replaced by `Nop`. + nop_statements: Vec<Location>, new_blocks: Vec<BasicBlockData<'tcx>>, new_statements: Vec<(Location, StatementKind<'tcx>)>, new_locals: Vec<LocalDecl<'tcx>>, @@ -22,17 +25,22 @@ pub(crate) struct MirPatch<'tcx> { terminate_block: Option<(BasicBlock, UnwindTerminateReason)>, body_span: Span, next_local: usize, + /// The number of blocks at the start of the transformation. New blocks + /// get appended at the end. + next_block: usize, } impl<'tcx> MirPatch<'tcx> { /// Creates a new, empty patch. pub(crate) fn new(body: &Body<'tcx>) -> Self { let mut result = MirPatch { - term_patch_map: IndexVec::from_elem(None, &body.basic_blocks), + term_patch_map: Default::default(), + nop_statements: vec![], new_blocks: vec![], new_statements: vec![], new_locals: vec![], next_local: body.local_decls.len(), + next_block: body.basic_blocks.len(), resume_block: None, unreachable_cleanup_block: None, unreachable_no_cleanup_block: None, @@ -141,7 +149,7 @@ impl<'tcx> MirPatch<'tcx> { /// Has a replacement of this block's terminator been queued in this patch? pub(crate) fn is_term_patched(&self, bb: BasicBlock) -> bool { - self.term_patch_map[bb].is_some() + self.term_patch_map.contains_key(&bb) } /// Universal getter for block data, either it is in 'old' blocks or in patched ones @@ -194,18 +202,26 @@ impl<'tcx> MirPatch<'tcx> { /// Queues the addition of a new basic block. pub(crate) fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock { - let block = self.term_patch_map.next_index(); + let block = BasicBlock::from_usize(self.next_block + self.new_blocks.len()); debug!("MirPatch: new_block: {:?}: {:?}", block, data); self.new_blocks.push(data); - self.term_patch_map.push(None); block } /// Queues the replacement of a block's terminator. pub(crate) fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) { - assert!(self.term_patch_map[block].is_none()); + assert!(!self.term_patch_map.contains_key(&block)); debug!("MirPatch: patch_terminator({:?}, {:?})", block, new); - self.term_patch_map[block] = Some(new); + self.term_patch_map.insert(block, new); + } + + /// Mark given statement to be replaced by a `Nop`. + /// + /// This method only works on statements from the initial body, and cannot be used to remove + /// statements from `add_statement` or `add_assign`. + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn nop_statement(&mut self, loc: Location) { + self.nop_statements.push(loc); } /// Queues the insertion of a statement at a given location. The statement @@ -244,6 +260,7 @@ impl<'tcx> MirPatch<'tcx> { self.new_blocks.len(), body.basic_blocks.len() ); + debug_assert_eq!(self.next_block, body.basic_blocks.len()); let bbs = if self.term_patch_map.is_empty() && self.new_blocks.is_empty() { body.basic_blocks.as_mut_preserves_cfg() } else { @@ -251,11 +268,9 @@ impl<'tcx> MirPatch<'tcx> { }; bbs.extend(self.new_blocks); body.local_decls.extend(self.new_locals); - for (src, patch) in self.term_patch_map.into_iter_enumerated() { - if let Some(patch) = patch { - debug!("MirPatch: patching block {:?}", src); - bbs[src].terminator_mut().kind = patch; - } + + for loc in self.nop_statements { + bbs[loc.block].statements[loc.statement_index].make_nop(); } let mut new_statements = self.new_statements; @@ -273,12 +288,23 @@ impl<'tcx> MirPatch<'tcx> { } debug!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt, loc, delta); loc.statement_index += delta; - let source_info = Self::source_info_for_index(&body[loc.block], loc); - body[loc.block] + let source_info = Self::source_info_for_index(&bbs[loc.block], loc); + bbs[loc.block] .statements .insert(loc.statement_index, Statement::new(source_info, stmt)); delta += 1; } + + // The order in which we patch terminators does not change the result. + #[allow(rustc::potential_query_instability)] + for (src, patch) in self.term_patch_map { + debug!("MirPatch: patching block {:?}", src); + let bb = &mut bbs[src]; + if let TerminatorKind::Unreachable = patch { + bb.statements.clear(); + } + bb.terminator_mut().kind = patch; + } } fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9ea2eb4f25d..a0b0c8c990f 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -437,9 +437,7 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(op)? } - Rvalue::Discriminant(place) | Rvalue::Len(place) => { - self.validate_place(place.as_ref())? - } + Rvalue::Discriminant(place) => self.validate_place(place.as_ref())?, Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 6f61215cee2..b9d6e74ecae 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -195,10 +195,10 @@ fn compute_replacement<'tcx>( // including DEF. This violates the DEF dominates USE condition, and so is impossible. let is_constant_place = |place: Place<'_>| { // We only allow `Deref` as the first projection, to avoid surprises. - if place.projection.first() == Some(&PlaceElem::Deref) { + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() { // `place == (*some_local).xxx`, it is constant only if `some_local` is constant. // We approximate constness using SSAness. - ssa.is_ssa(place.local) && place.projection[1..].iter().all(PlaceElem::is_stable_offset) + ssa.is_ssa(place.local) && rest.iter().all(PlaceElem::is_stable_offset) } else { storage_live.has_single_storage(place.local) && place.projection[..].iter().all(PlaceElem::is_stable_offset) @@ -206,7 +206,7 @@ fn compute_replacement<'tcx>( }; let mut can_perform_opt = |target: Place<'tcx>, loc: Location| { - if target.projection.first() == Some(&PlaceElem::Deref) { + if target.is_indirect_first_projection() { // We are creating a reborrow. As `place.local` is a reference, removing the storage // statements should not make it much harder for LLVM to optimize. storage_to_remove.insert(target.local); @@ -266,7 +266,7 @@ fn compute_replacement<'tcx>( Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { let mut place = *place; // Try to see through `place` in order to collapse reborrow chains. - if place.projection.first() == Some(&PlaceElem::Deref) + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() && let Value::Pointer(target, inner_needs_unique) = targets[place.local] // Only see through immutable reference and pointers, as we do not know yet if // mutable references are fully replaced. @@ -274,7 +274,7 @@ fn compute_replacement<'tcx>( // Only collapse chain if the pointee is definitely live. && can_perform_opt(target, location) { - place = target.project_deeper(&place.projection[1..], tcx); + place = target.project_deeper(rest, tcx); } assert_ne!(place.local, local); if is_constant_place(place) { @@ -323,7 +323,7 @@ fn compute_replacement<'tcx>( return; } - if place.projection.first() != Some(&PlaceElem::Deref) { + if !place.is_indirect_first_projection() { // This is not a dereference, nothing to do. return; } @@ -392,20 +392,15 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } fn visit_var_debug_info(&mut self, debuginfo: &mut VarDebugInfo<'tcx>) { - // If the debuginfo is a pointer to another place: - // - if it's a reborrow, see through it; - // - if it's a direct borrow, increase `debuginfo.references`. + // If the debuginfo is a pointer to another place + // and it's a reborrow: see through it while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value && place.projection.is_empty() && let Value::Pointer(target, _) = self.targets[place.local] - && target.projection.iter().all(|p| p.can_use_in_debuginfo()) + && let &[PlaceElem::Deref] = &target.projection[..] { - if let Some((&PlaceElem::Deref, rest)) = target.projection.split_last() { - *place = Place::from(target.local).project_deeper(rest, self.tcx); - self.any_replacement = true; - } else { - break; - } + *place = Place::from(target.local); + self.any_replacement = true; } // Simplify eventual projections left inside `debuginfo`. @@ -414,9 +409,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) { loop { - if place.projection.first() != Some(&PlaceElem::Deref) { - return; - } + let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { return }; let Value::Pointer(target, _) = self.targets[place.local] else { return }; @@ -432,7 +425,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { return; } - *place = target.project_deeper(&place.projection[1..], self.tcx); + *place = target.project_deeper(rest, self.tcx); self.any_replacement = true; } } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 5b6d7ffb511..b53c1f6d202 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -1,7 +1,6 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use rustc_target::spec::PanicStrategy; use tracing::debug; use crate::patch::MirPatch; @@ -13,7 +12,7 @@ pub(super) struct RemoveNoopLandingPads; impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - sess.panic_strategy() != PanicStrategy::Abort + sess.panic_strategy().unwinds() } fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index 886f4d6e509..ed94a058ec6 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -2,6 +2,8 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::trace; +use crate::patch::MirPatch; + pub(super) enum SimplifyConstCondition { AfterConstProp, Final, @@ -19,8 +21,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { trace!("Running SimplifyConstCondition on {:?}", body.source); let typing_env = body.typing_env(tcx); - 'blocks: for block in body.basic_blocks_mut() { - for stmt in block.statements.iter_mut() { + let mut patch = MirPatch::new(body); + + 'blocks: for (bb, block) in body.basic_blocks.iter_enumerated() { + for (statement_index, stmt) in block.statements.iter().enumerate() { // Simplify `assume` of a known value: either a NOP or unreachable. if let StatementKind::Intrinsic(box ref intrinsic) = stmt.kind && let NonDivergingIntrinsic::Assume(discr) = intrinsic @@ -28,17 +32,16 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { && let Some(constant) = c.const_.try_eval_bool(tcx, typing_env) { if constant { - stmt.make_nop(); + patch.nop_statement(Location { block: bb, statement_index }); } else { - block.statements.clear(); - block.terminator_mut().kind = TerminatorKind::Unreachable; + patch.patch_terminator(bb, TerminatorKind::Unreachable); continue 'blocks; } } } - let terminator = block.terminator_mut(); - terminator.kind = match terminator.kind { + let terminator = block.terminator(); + let terminator = match terminator.kind { TerminatorKind::SwitchInt { discr: Operand::Constant(ref c), ref targets, .. } => { @@ -58,7 +61,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { }, _ => continue, }; + patch.patch_terminator(bb, terminator); } + patch.apply(body); } fn is_required(&self) -> bool { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 99e4782e470..c8a9a88dc3f 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1064,14 +1064,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(..) => {} - Rvalue::Len(p) => { - let pty = p.ty(&self.body.local_decls, self.tcx).ty; - check_kinds!( - pty, - "Cannot compute length of non-array type {:?}", - ty::Array(..) | ty::Slice(..) - ); - } Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index a8f2b4e8db6..b25671d676b 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -57,7 +57,7 @@ enum CanonicalizeMode { }, } -pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> { +pub(super) struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> { delegate: &'a D, // Immutable field. @@ -83,7 +83,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> { } impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { - pub fn canonicalize_response<T: TypeFoldable<I>>( + pub(super) fn canonicalize_response<T: TypeFoldable<I>>( delegate: &'a D, max_input_universe: ty::UniverseIndex, variables: &'a mut Vec<I::GenericArg>, @@ -112,7 +112,6 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { let (max_universe, variables) = canonicalizer.finalize(); Canonical { max_universe, variables, value } } - fn canonicalize_param_env( delegate: &'a D, variables: &'a mut Vec<I::GenericArg>, @@ -195,7 +194,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { /// /// We want to keep the option of canonicalizing `'static` to an existential /// variable in the future by changing the way we detect global where-bounds. - pub fn canonicalize_input<P: TypeFoldable<I>>( + pub(super) fn canonicalize_input<P: TypeFoldable<I>>( delegate: &'a D, variables: &'a mut Vec<I::GenericArg>, input: QueryInput<I, P>, @@ -390,7 +389,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs new file mode 100644 index 00000000000..b036ee6df7e --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -0,0 +1,362 @@ +//! Canonicalization is used to separate some goal from its context, +//! throwing away unnecessary information in the process. +//! +//! This is necessary to cache goals containing inference variables +//! and placeholders without restricting them to the current `InferCtxt`. +//! +//! Canonicalization is fairly involved, for more details see the relevant +//! section of the [rustc-dev-guide][c]. +//! +//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html + +use std::iter; + +use canonicalizer::Canonicalizer; +use rustc_index::IndexVec; +use rustc_type_ir::inherent::*; +use rustc_type_ir::relate::solver_relating::RelateExt; +use rustc_type_ir::{ + self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, + TypeFoldable, +}; +use tracing::instrument; + +use crate::delegate::SolverDelegate; +use crate::resolve::eager_resolve_vars; +use crate::solve::{ + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, Goal, + NestedNormalizationGoals, QueryInput, Response, inspect, +}; + +pub mod canonicalizer; + +trait ResponseT<I: Interner> { + fn var_values(&self) -> CanonicalVarValues<I>; +} + +impl<I: Interner> ResponseT<I> for Response<I> { + fn var_values(&self) -> CanonicalVarValues<I> { + self.var_values + } +} + +impl<I: Interner, T> ResponseT<I> for inspect::State<I, T> { + fn var_values(&self) -> CanonicalVarValues<I> { + self.var_values + } +} + +/// Canonicalizes the goal remembering the original values +/// for each bound variable. +/// +/// This expects `goal` and `opaque_types` to be eager resolved. +pub(super) fn canonicalize_goal<D, I>( + delegate: &D, + goal: Goal<I, I::Predicate>, + opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)], +) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + let mut orig_values = Default::default(); + let canonical = Canonicalizer::canonicalize_input( + delegate, + &mut orig_values, + QueryInput { + goal, + predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types), + }, + ); + let query_input = ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; + (orig_values, query_input) +} + +pub(super) fn canonicalize_response<D, I, T>( + delegate: &D, + max_input_universe: ty::UniverseIndex, + value: T, +) -> ty::Canonical<I, T> +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: TypeFoldable<I>, +{ + let mut orig_values = Default::default(); + let canonical = + Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut orig_values, value); + canonical +} + +/// After calling a canonical query, we apply the constraints returned +/// by the query using this function. +/// +/// This happens in three steps: +/// - we instantiate the bound variables of the query response +/// - we unify the `var_values` of the response with the `original_values` +/// - we apply the `external_constraints` returned by the query, returning +/// the `normalization_nested_goals` +pub(super) fn instantiate_and_apply_query_response<D, I>( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + response: CanonicalResponse<I>, + span: I::Span, +) -> (NestedNormalizationGoals<I>, Certainty) +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + let instantiation = + compute_query_response_instantiation_values(delegate, &original_values, &response, span); + + let Response { var_values, external_constraints, certainty } = + delegate.instantiate_canonical(response, instantiation); + + unify_query_var_values(delegate, param_env, &original_values, var_values, span); + + let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } = + &*external_constraints; + + register_region_constraints(delegate, region_constraints, span); + register_new_opaque_types(delegate, opaque_types, span); + + (normalization_nested_goals.clone(), certainty) +} + +/// This returns the canonical variable values to instantiate the bound variables of +/// the canonical response. This depends on the `original_values` for the +/// bound variables. +fn compute_query_response_instantiation_values<D, I, T>( + delegate: &D, + original_values: &[I::GenericArg], + response: &Canonical<I, T>, + span: I::Span, +) -> CanonicalVarValues<I> +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: ResponseT<I>, +{ + // FIXME: Longterm canonical queries should deal with all placeholders + // created inside of the query directly instead of returning them to the + // caller. + let prev_universe = delegate.universe(); + let universes_created_in_query = response.max_universe.index(); + for _ in 0..universes_created_in_query { + delegate.create_next_universe(); + } + + let var_values = response.value.var_values(); + assert_eq!(original_values.len(), var_values.len()); + + // If the query did not make progress with constraining inference variables, + // we would normally create a new inference variables for bound existential variables + // only then unify this new inference variable with the inference variable from + // the input. + // + // We therefore instantiate the existential variable in the canonical response with the + // inference variable of the input right away, which is more performant. + let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); + for (original_value, result_value) in iter::zip(original_values, var_values.var_values.iter()) { + match result_value.kind() { + ty::GenericArgKind::Type(t) => { + // We disable the instantiation guess for inference variables + // and only use it for placeholders. We need to handle the + // `sub_root` of type inference variables which would make this + // more involved. They are also a lot rarer than region variables. + if let ty::Bound(debruijn, b) = t.kind() + && !matches!( + response.variables.get(b.var().as_usize()).unwrap(), + CanonicalVarKind::Ty { .. } + ) + { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b.var()] = Some(*original_value); + } + } + ty::GenericArgKind::Lifetime(r) => { + if let ty::ReBound(debruijn, br) = r.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[br.var()] = Some(*original_value); + } + } + ty::GenericArgKind::Const(c) => { + if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[bv.var()] = Some(*original_value); + } + } + } + } + CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { + if kind.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { + prev_universe + idx.index() + }) + } else if kind.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // + // All new inference variables we create start out in the current universe of the caller. + // This is conceptually wrong as these inference variables would be able to name + // more placeholders then they should be able to. However the inference variables have + // to "come from somewhere", so by equating them with the original values of the caller + // later on, we pull them down into their correct universe again. + if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { + v + } else { + delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) + } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[kind.expect_placeholder_index()] + } + }) +} + +/// Unify the `original_values` with the `var_values` returned by the canonical query.. +/// +/// This assumes that this unification will always succeed. This is the case when +/// applying a query response right away. However, calling a canonical query, doing any +/// other kind of trait solving, and only then instantiating the result of the query +/// can cause the instantiation to fail. This is not supported and we ICE in this case. +/// +/// We always structurally instantiate aliases. Relating aliases needs to be different +/// depending on whether the alias is *rigid* or not. We're only really able to tell +/// whether an alias is rigid by using the trait solver. When instantiating a response +/// from the solver we assume that the solver correctly handled aliases and therefore +/// always relate them structurally here. +#[instrument(level = "trace", skip(delegate))] +fn unify_query_var_values<D, I>( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + var_values: CanonicalVarValues<I>, + span: I::Span, +) where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + assert_eq!(original_values.len(), var_values.len()); + + for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { + let goals = + delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); + assert!(goals.is_empty()); + } +} + +fn register_region_constraints<D, I>( + delegate: &D, + outlives: &[ty::OutlivesPredicate<I, I::GenericArg>], + span: I::Span, +) where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + for &ty::OutlivesPredicate(lhs, rhs) in outlives { + match lhs.kind() { + ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), + ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), + ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), + } + } +} + +fn register_new_opaque_types<D, I>( + delegate: &D, + opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)], + span: I::Span, +) where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + for &(key, ty) in opaque_types { + let prev = delegate.register_hidden_type_in_storage(key, ty, span); + // We eagerly resolve inference variables when computing the query response. + // This can cause previously distinct opaque type keys to now be structurally equal. + // + // To handle this, we store any duplicate entries in a separate list to check them + // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden + // types here. However, doing so is difficult as it may result in nested goals and + // any errors may make it harder to track the control flow for diagnostics. + if let Some(prev) = prev { + delegate.add_duplicate_opaque_type(key, prev, span); + } + } +} + +/// Used by proof trees to be able to recompute intermediate actions while +/// evaluating a goal. The `var_values` not only include the bound variables +/// of the query input, but also contain all unconstrained inference vars +/// created while evaluating this goal. +pub fn make_canonical_state<D, I, T>( + delegate: &D, + var_values: &[I::GenericArg], + max_input_universe: ty::UniverseIndex, + data: T, +) -> inspect::CanonicalState<I, T> +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: TypeFoldable<I>, +{ + let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; + let state = inspect::State { var_values, data }; + let state = eager_resolve_vars(delegate, state); + Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state) +} + +// FIXME: needs to be pub to be accessed by downstream +// `rustc_trait_selection::solve::inspect::analyse`. +pub fn instantiate_canonical_state<D, I, T>( + delegate: &D, + span: I::Span, + param_env: I::ParamEnv, + orig_values: &mut Vec<I::GenericArg>, + state: inspect::CanonicalState<I, T>, +) -> T +where + D: SolverDelegate<Interner = I>, + I: Interner, + T: TypeFoldable<I>, +{ + // In case any fresh inference variables have been created between `state` + // and the previous instantiation, extend `orig_values` for it. + orig_values.extend( + state.value.var_values.var_values.as_slice()[orig_values.len()..] + .iter() + .map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)), + ); + + let instantiation = + compute_query_response_instantiation_values(delegate, orig_values, &state, span); + + let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); + + unify_query_var_values(delegate, param_env, orig_values, var_values, span); + data +} + +pub fn response_no_constraints_raw<I: Interner>( + cx: I, + max_universe: ty::UniverseIndex, + variables: I::CanonicalVarKinds, + certainty: Certainty, +) -> CanonicalResponse<I> { + ty::Canonical { + max_universe, + variables, + value: Response { + var_values: ty::CanonicalVarValues::make_identity(cx, variables), + // FIXME: maybe we should store the "no response" version in cx, like + // we do for cx.types and stuff. + external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), + certainty, + }, + } +} diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index d3965e14c68..5fa29b7d9f8 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -10,7 +10,7 @@ #![allow(rustc::usage_of_type_ir_traits)] // tidy-alphabetical-end -pub mod canonicalizer; +pub mod canonical; pub mod coherence; pub mod delegate; pub mod placeholder; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index fb777496e31..a2e6ef6f0fe 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -23,7 +23,8 @@ use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, - MaybeCause, NoSolution, ParamEnvSource, QueryResult, has_no_inference_or_external_constraints, + MaybeCause, NoSolution, OpaqueTypesJank, ParamEnvSource, QueryResult, + has_no_inference_or_external_constraints, }; enum AliasBoundKind { @@ -86,7 +87,7 @@ where ) -> Result<Candidate<I>, NoSolution> { Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { let cx = ecx.cx(); - let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else { + let ty::Dynamic(bounds, _) = goal.predicate.self_ty().kind() else { panic!("expected object type in `probe_and_consider_object_bound_candidate`"); }; match structural_traits::predicates_for_object_candidate( @@ -474,7 +475,7 @@ where // // cc trait-system-refactor-initiative#105 let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); - let certainty = Certainty::Maybe(cause); + let certainty = Certainty::Maybe { cause, opaque_types_jank: OpaqueTypesJank::AllGood }; self.probe_trait_candidate(source) .enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty)) } @@ -974,11 +975,21 @@ where candidates: &mut Vec<Candidate<I>>, ) { let self_ty = goal.predicate.self_ty(); - // If the self type is sub unified with any opaque type, we - // also look at blanket impls for it. - let mut assemble_blanket_impls = false; - for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) { - assemble_blanket_impls = true; + // We only use this hack during HIR typeck. + let opaque_types = match self.typing_mode() { + TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty), + TypingMode::Coherence + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => vec![], + }; + + if opaque_types.is_empty() { + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + return; + } + + for &alias_ty in &opaque_types { debug!("self ty is sub unified with {alias_ty:?}"); struct ReplaceOpaque<I: Interner> { @@ -1028,10 +1039,11 @@ where } } - // We also need to consider blanket impls for not-yet-defined opaque types. + // If the self type is sub unified with any opaque type, we also look at blanket + // impls for it. // // See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example. - if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() { + if assemble_from.should_assemble_impl_candidates() { let cx = self.cx(); cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| { // For every `default impl`, there's always a non-default `impl` @@ -1062,7 +1074,15 @@ where } if candidates.is_empty() { - candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::ErrorIfRigidSelfTy, + }; + candidates + .extend(self.probe_trait_candidate(source).enter(|this| { + this.evaluate_added_goals_and_make_canonical_response(certainty) + })); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index f6eab286ba7..9b3dc1f691f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -383,7 +383,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Intern | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never @@ -557,7 +557,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never @@ -664,7 +664,7 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>( pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>( cx: I, self_ty: I::Ty, -) -> Result<(ty::Binder<I, (I::FnInputTys, I::Ty)>, I::FunctionId, I::GenericArgs), NoSolution> { +) -> Result<(ty::Binder<I, (I::Ty, I::Ty)>, I::FunctionId, I::GenericArgs), NoSolution> { match self_ty.kind() { ty::FnDef(def_id, args) => { let sig = cx.fn_sig(def_id); @@ -673,7 +673,8 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>( && cx.fn_is_const(def_id) { Ok(( - sig.instantiate(cx, args).map_bound(|sig| (sig.inputs(), sig.output())), + sig.instantiate(cx, args) + .map_bound(|sig| (Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())), def_id, args, )) @@ -706,7 +707,7 @@ pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>( | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index cb72c1cd92b..65a5edf6b72 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -234,12 +234,12 @@ where let self_ty = goal.predicate.self_ty(); let (inputs_and_output, def_id, args) = structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?; + let (inputs, output) = ecx.instantiate_binder_with_infer(inputs_and_output); // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) - }); + let output_is_sized_pred = + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]); let requirements = cx .const_conditions(def_id.into()) .iter_instantiated(cx, args) @@ -251,15 +251,12 @@ where }) .chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]); - let pred = inputs_and_output - .map_bound(|(inputs, _)| { - ty::TraitRef::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), Ty::new_tup(cx, inputs.as_slice())], - ) - }) - .to_host_effect_clause(cx, goal.predicate.constness); + let pred = ty::Binder::dummy(ty::TraitRef::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), inputs], + )) + .to_host_effect_clause(cx, goal.predicate.constness); Self::probe_and_consider_implied_clause( ecx, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs deleted file mode 100644 index 169832ca5fb..00000000000 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ /dev/null @@ -1,510 +0,0 @@ -//! Canonicalization is used to separate some goal from its context, -//! throwing away unnecessary information in the process. -//! -//! This is necessary to cache goals containing inference variables -//! and placeholders without restricting them to the current `InferCtxt`. -//! -//! Canonicalization is fairly involved, for more details see the relevant -//! section of the [rustc-dev-guide][c]. -//! -//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html - -use std::iter; - -use rustc_index::IndexVec; -use rustc_type_ir::data_structures::HashSet; -use rustc_type_ir::inherent::*; -use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::{ - self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, - TypeFoldable, -}; -use tracing::{debug, instrument, trace}; - -use crate::canonicalizer::Canonicalizer; -use crate::delegate::SolverDelegate; -use crate::resolve::eager_resolve_vars; -use crate::solve::eval_ctxt::CurrentGoalKind; -use crate::solve::{ - CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, - MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, - QueryResult, Response, inspect, response_no_constraints_raw, -}; - -trait ResponseT<I: Interner> { - fn var_values(&self) -> CanonicalVarValues<I>; -} - -impl<I: Interner> ResponseT<I> for Response<I> { - fn var_values(&self) -> CanonicalVarValues<I> { - self.var_values - } -} - -impl<I: Interner, T> ResponseT<I> for inspect::State<I, T> { - fn var_values(&self) -> CanonicalVarValues<I> { - self.var_values - } -} - -impl<D, I> EvalCtxt<'_, D> -where - D: SolverDelegate<Interner = I>, - I: Interner, -{ - /// Canonicalizes the goal remembering the original values - /// for each bound variable. - /// - /// This expects `goal` and `opaque_types` to be eager resolved. - pub(super) fn canonicalize_goal( - delegate: &D, - goal: Goal<I, I::Predicate>, - opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>, - ) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) { - let mut orig_values = Default::default(); - let canonical = Canonicalizer::canonicalize_input( - delegate, - &mut orig_values, - QueryInput { - goal, - predefined_opaques_in_body: delegate - .cx() - .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), - }, - ); - let query_input = - ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; - (orig_values, query_input) - } - - /// To return the constraints of a canonical query to the caller, we canonicalize: - /// - /// - `var_values`: a map from bound variables in the canonical goal to - /// the values inferred while solving the instantiated goal. - /// - `external_constraints`: additional constraints which aren't expressible - /// using simple unification of inference variables. - /// - /// This takes the `shallow_certainty` which represents whether we're confident - /// that the final result of the current goal only depends on the nested goals. - /// - /// In case this is `Certainty::Maybe`, there may still be additional nested goals - /// or inference constraints required for this candidate to be hold. The candidate - /// always requires all already added constraints and nested goals. - #[instrument(level = "trace", skip(self), ret)] - pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( - &mut self, - shallow_certainty: Certainty, - ) -> QueryResult<I> { - self.inspect.make_canonical_response(shallow_certainty); - - let goals_certainty = self.try_evaluate_added_goals()?; - assert_eq!( - self.tainted, - Ok(()), - "EvalCtxt is tainted -- nested goals may have been dropped in a \ - previous call to `try_evaluate_added_goals!`" - ); - - // We only check for leaks from universes which were entered inside - // of the query. - self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { - trace!("failed the leak check"); - NoSolution - })?; - - let (certainty, normalization_nested_goals) = - match (self.current_goal_kind, shallow_certainty) { - // When normalizing, we've replaced the expected term with an unconstrained - // inference variable. This means that we dropped information which could - // have been important. We handle this by instead returning the nested goals - // to the caller, where they are then handled. We only do so if we do not - // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly - // uplifting its nested goals. This is the case if the `shallow_certainty` is - // `Certainty::Yes`. - (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { - let goals = std::mem::take(&mut self.nested_goals); - // As we return all ambiguous nested goals, we can ignore the certainty - // returned by `self.try_evaluate_added_goals()`. - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); - } - ( - Certainty::Yes, - NestedNormalizationGoals( - goals.into_iter().map(|(s, g, _)| (s, g)).collect(), - ), - ) - } - _ => { - let certainty = shallow_certainty.and(goals_certainty); - (certainty, NestedNormalizationGoals::empty()) - } - }; - - if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) = - certainty - { - // If we have overflow, it's probable that we're substituting a type - // into itself infinitely and any partial substitutions in the query - // response are probably not useful anyways, so just return an empty - // query response. - // - // This may prevent us from potentially useful inference, e.g. - // 2 candidates, one ambiguous and one overflow, which both - // have the same inference constraints. - // - // Changing this to retain some constraints in the future - // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause)); - } - - let external_constraints = - self.compute_external_query_constraints(certainty, normalization_nested_goals); - let (var_values, mut external_constraints) = - eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); - - // Remove any trivial or duplicated region constraints once we've resolved regions - let mut unique = HashSet::default(); - external_constraints.region_constraints.retain(|outlives| { - outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) - }); - - let canonical = Canonicalizer::canonicalize_response( - self.delegate, - self.max_input_universe, - &mut Default::default(), - Response { - var_values, - certainty, - external_constraints: self.cx().mk_external_constraints(external_constraints), - }, - ); - - // HACK: We bail with overflow if the response would have too many non-region - // inference variables. This tends to only happen if we encounter a lot of - // ambiguous alias types which get replaced with fresh inference variables - // during generalization. This prevents hangs caused by an exponential blowup, - // see tests/ui/traits/next-solver/coherence-alias-hang.rs. - match self.current_goal_kind { - // We don't do so for `NormalizesTo` goals as we erased the expected term and - // bailing with overflow here would prevent us from detecting a type-mismatch, - // causing a coherence error in diesel, see #131969. We still bail with overflow - // when later returning from the parent AliasRelate goal. - CurrentGoalKind::NormalizesTo => {} - CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { - let num_non_region_vars = canonical - .variables - .iter() - .filter(|c| !c.is_region() && c.is_existential()) - .count(); - if num_non_region_vars > self.cx().recursion_limit() { - debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { - suggest_increasing_limit: true, - keep_constraints: false, - })); - } - } - } - - Ok(canonical) - } - - /// Constructs a totally unconstrained, ambiguous response to a goal. - /// - /// Take care when using this, since often it's useful to respond with - /// ambiguity but return constrained variables to guide inference. - pub(in crate::solve) fn make_ambiguous_response_no_constraints( - &self, - maybe_cause: MaybeCause, - ) -> CanonicalResponse<I> { - response_no_constraints_raw( - self.cx(), - self.max_input_universe, - self.variables, - Certainty::Maybe(maybe_cause), - ) - } - - /// Computes the region constraints and *new* opaque types registered when - /// proving a goal. - /// - /// If an opaque was already constrained before proving this goal, then the - /// external constraints do not need to record that opaque, since if it is - /// further constrained by inference, that will be passed back in the var - /// values. - #[instrument(level = "trace", skip(self), ret)] - fn compute_external_query_constraints( - &self, - certainty: Certainty, - normalization_nested_goals: NestedNormalizationGoals<I>, - ) -> ExternalConstraintsData<I> { - // We only return region constraints once the certainty is `Yes`. This - // is necessary as we may drop nested goals on ambiguity, which may result - // in unconstrained inference variables in the region constraints. It also - // prevents us from emitting duplicate region constraints, avoiding some - // unnecessary work. This slightly weakens the leak check in case it uses - // region constraints from an ambiguous nested goal. This is tested in both - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. - let region_constraints = if certainty == Certainty::Yes { - self.delegate.make_deduplicated_outlives_constraints() - } else { - Default::default() - }; - - // We only return *newly defined* opaque types from canonical queries. - // - // Constraints for any existing opaque types are already tracked by changes - // to the `var_values`. - let opaque_types = self - .delegate - .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); - - ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } - } - - /// After calling a canonical query, we apply the constraints returned - /// by the query using this function. - /// - /// This happens in three steps: - /// - we instantiate the bound variables of the query response - /// - we unify the `var_values` of the response with the `original_values` - /// - we apply the `external_constraints` returned by the query, returning - /// the `normalization_nested_goals` - pub(super) fn instantiate_and_apply_query_response( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - response: CanonicalResponse<I>, - span: I::Span, - ) -> (NestedNormalizationGoals<I>, Certainty) { - let instantiation = Self::compute_query_response_instantiation_values( - delegate, - &original_values, - &response, - span, - ); - - let Response { var_values, external_constraints, certainty } = - delegate.instantiate_canonical(response, instantiation); - - Self::unify_query_var_values(delegate, param_env, &original_values, var_values, span); - - let ExternalConstraintsData { - region_constraints, - opaque_types, - normalization_nested_goals, - } = &*external_constraints; - - Self::register_region_constraints(delegate, region_constraints, span); - Self::register_new_opaque_types(delegate, opaque_types, span); - - (normalization_nested_goals.clone(), certainty) - } - - /// This returns the canonical variable values to instantiate the bound variables of - /// the canonical response. This depends on the `original_values` for the - /// bound variables. - fn compute_query_response_instantiation_values<T: ResponseT<I>>( - delegate: &D, - original_values: &[I::GenericArg], - response: &Canonical<I, T>, - span: I::Span, - ) -> CanonicalVarValues<I> { - // FIXME: Longterm canonical queries should deal with all placeholders - // created inside of the query directly instead of returning them to the - // caller. - let prev_universe = delegate.universe(); - let universes_created_in_query = response.max_universe.index(); - for _ in 0..universes_created_in_query { - delegate.create_next_universe(); - } - - let var_values = response.value.var_values(); - assert_eq!(original_values.len(), var_values.len()); - - // If the query did not make progress with constraining inference variables, - // we would normally create a new inference variables for bound existential variables - // only then unify this new inference variable with the inference variable from - // the input. - // - // We therefore instantiate the existential variable in the canonical response with the - // inference variable of the input right away, which is more performant. - let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); - for (original_value, result_value) in - iter::zip(original_values, var_values.var_values.iter()) - { - match result_value.kind() { - ty::GenericArgKind::Type(t) => { - // We disable the instantiation guess for inference variables - // and only use it for placeholders. We need to handle the - // `sub_root` of type inference variables which would make this - // more involved. They are also a lot rarer than region variables. - if let ty::Bound(debruijn, b) = t.kind() - && !matches!( - response.variables.get(b.var().as_usize()).unwrap(), - CanonicalVarKind::Ty { .. } - ) - { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[b.var()] = Some(*original_value); - } - } - ty::GenericArgKind::Lifetime(r) => { - if let ty::ReBound(debruijn, br) = r.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[br.var()] = Some(*original_value); - } - } - ty::GenericArgKind::Const(c) => { - if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[bv.var()] = Some(*original_value); - } - } - } - } - CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { - if kind.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all (see the FIXME at the start of this method), we have to deal with - // them for now. - delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { - prev_universe + idx.index() - }) - } else if kind.is_existential() { - // As an optimization we sometimes avoid creating a new inference variable here. - // - // All new inference variables we create start out in the current universe of the caller. - // This is conceptually wrong as these inference variables would be able to name - // more placeholders then they should be able to. However the inference variables have - // to "come from somewhere", so by equating them with the original values of the caller - // later on, we pull them down into their correct universe again. - if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { - v - } else { - delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) - } - } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - original_values[kind.expect_placeholder_index()] - } - }) - } - - /// Unify the `original_values` with the `var_values` returned by the canonical query.. - /// - /// This assumes that this unification will always succeed. This is the case when - /// applying a query response right away. However, calling a canonical query, doing any - /// other kind of trait solving, and only then instantiating the result of the query - /// can cause the instantiation to fail. This is not supported and we ICE in this case. - /// - /// We always structurally instantiate aliases. Relating aliases needs to be different - /// depending on whether the alias is *rigid* or not. We're only really able to tell - /// whether an alias is rigid by using the trait solver. When instantiating a response - /// from the solver we assume that the solver correctly handled aliases and therefore - /// always relate them structurally here. - #[instrument(level = "trace", skip(delegate))] - fn unify_query_var_values( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - var_values: CanonicalVarValues<I>, - span: I::Span, - ) { - assert_eq!(original_values.len(), var_values.len()); - - for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { - let goals = - delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); - assert!(goals.is_empty()); - } - } - - fn register_region_constraints( - delegate: &D, - outlives: &[ty::OutlivesPredicate<I, I::GenericArg>], - span: I::Span, - ) { - for &ty::OutlivesPredicate(lhs, rhs) in outlives { - match lhs.kind() { - ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), - ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), - ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), - } - } - } - - fn register_new_opaque_types( - delegate: &D, - opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)], - span: I::Span, - ) { - for &(key, ty) in opaque_types { - let prev = delegate.register_hidden_type_in_storage(key, ty, span); - // We eagerly resolve inference variables when computing the query response. - // This can cause previously distinct opaque type keys to now be structurally equal. - // - // To handle this, we store any duplicate entries in a separate list to check them - // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden - // types here. However, doing so is difficult as it may result in nested goals and - // any errors may make it harder to track the control flow for diagnostics. - if let Some(prev) = prev { - delegate.add_duplicate_opaque_type(key, prev, span); - } - } - } -} - -/// Used by proof trees to be able to recompute intermediate actions while -/// evaluating a goal. The `var_values` not only include the bound variables -/// of the query input, but also contain all unconstrained inference vars -/// created while evaluating this goal. -pub(in crate::solve) fn make_canonical_state<D, T, I>( - delegate: &D, - var_values: &[I::GenericArg], - max_input_universe: ty::UniverseIndex, - data: T, -) -> inspect::CanonicalState<I, T> -where - D: SolverDelegate<Interner = I>, - I: Interner, - T: TypeFoldable<I>, -{ - let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; - let state = inspect::State { var_values, data }; - let state = eager_resolve_vars(delegate, state); - Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state) -} - -// FIXME: needs to be pub to be accessed by downstream -// `rustc_trait_selection::solve::inspect::analyse`. -pub fn instantiate_canonical_state<D, I, T: TypeFoldable<I>>( - delegate: &D, - span: I::Span, - param_env: I::ParamEnv, - orig_values: &mut Vec<I::GenericArg>, - state: inspect::CanonicalState<I, T>, -) -> T -where - D: SolverDelegate<Interner = I>, - I: Interner, -{ - // In case any fresh inference variables have been created between `state` - // and the previous instantiation, extend `orig_values` for it. - orig_values.extend( - state.value.var_values.var_values.as_slice()[orig_values.len()..] - .iter() - .map(|&arg| delegate.fresh_var_for_kind_with_span(arg, span)), - ); - - let instantiation = - EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state, span); - - let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); - - EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); - data -} diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 3e3a5246f3d..f25003bbfe9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -8,6 +8,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; +use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -16,6 +17,10 @@ use rustc_type_ir::{ use tracing::{debug, instrument, trace}; use super::has_only_region_constraints; +use crate::canonical::{ + canonicalize_goal, canonicalize_response, instantiate_and_apply_query_response, + response_no_constraints_raw, +}; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; @@ -23,12 +28,11 @@ use crate::resolve::eager_resolve_vars; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ - CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalSource, - GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, - inspect, + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, FIXPOINT_STEP_LIMIT, + Goal, GoalEvaluation, GoalSource, GoalStalledOn, HasChanged, MaybeCause, + NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, inspect, }; -pub(super) mod canonical; mod probe; /// The kind of goal we're currently proving. @@ -151,6 +155,15 @@ pub trait SolverDelegateEvalExt: SolverDelegate { stalled_on: Option<GoalStalledOn<Self::Interner>>, ) -> Result<GoalEvaluation<Self::Interner>, NoSolution>; + /// Checks whether evaluating `goal` may hold while treating not-yet-defined + /// opaque types as being kind of rigid. + /// + /// See the comment on [OpaqueTypesJank] for more details. + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>, + ) -> bool; + /// Check whether evaluating `goal` with a depth of `root_depth` may /// succeed. This only returns `false` if the goal is guaranteed to /// not hold. In case evaluation overflows and fails with ambiguity this @@ -181,7 +194,7 @@ where D: SolverDelegate<Interner = I>, I: Interner, { - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn evaluate_root_goal( &self, goal: Goal<I, I::Predicate>, @@ -193,6 +206,25 @@ where }) } + #[instrument(level = "debug", skip(self), ret)] + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>, + ) -> bool { + self.probe(|| { + EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| { + ecx.evaluate_goal(GoalSource::Misc, goal, None) + }) + .is_ok_and(|r| match r.certainty { + Certainty::Yes => true, + Certainty::Maybe { cause: _, opaque_types_jank } => match opaque_types_jank { + OpaqueTypesJank::AllGood => true, + OpaqueTypesJank::ErrorIfRigidSelfTy => false, + }, + }) + }) + } + fn root_goal_may_hold_with_depth( &self, root_depth: usize, @@ -326,7 +358,7 @@ where f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R, ) -> R { let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input); - for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { + for (key, ty) in input.predefined_opaques_in_body.iter() { let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy()); // It may be possible that two entries in the opaque type storage end up // with the same key after resolving contained inference variables. @@ -407,8 +439,12 @@ where // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. - if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) = - stalled_on + if let Some(GoalStalledOn { + num_opaques, + ref stalled_vars, + ref sub_roots, + stalled_certainty, + }) = stalled_on && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) && !sub_roots .iter() @@ -419,7 +455,7 @@ where NestedNormalizationGoals::empty(), GoalEvaluation { goal, - certainty: Certainty::Maybe(stalled_cause), + certainty: stalled_certainty, has_changed: HasChanged::No, stalled_on, }, @@ -432,8 +468,7 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = - Self::canonicalize_goal(self.delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, &opaque_types); let canonical_result = self.search_graph.evaluate_goal( self.cx(), canonical_goal, @@ -448,7 +483,7 @@ where let has_changed = if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; - let (normalization_nested_goals, certainty) = Self::instantiate_and_apply_query_response( + let (normalization_nested_goals, certainty) = instantiate_and_apply_query_response( self.delegate, goal.param_env, &orig_values, @@ -468,7 +503,7 @@ where let stalled_on = match certainty { Certainty::Yes => None, - Certainty::Maybe(stalled_cause) => match has_changed { + Certainty::Maybe { .. } => match has_changed { // FIXME: We could recompute a *new* set of stalled variables by walking // through the orig values, resolving, and computing the root vars of anything // that is not resolved. Only when *these* have changed is it meaningful @@ -514,11 +549,10 @@ where .canonical .value .predefined_opaques_in_body - .opaque_types .len(), stalled_vars, sub_roots, - stalled_cause, + stalled_certainty: certainty, }) } }, @@ -599,28 +633,19 @@ where // the certainty of all the goals. #[instrument(level = "trace", skip(self))] pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> { - let mut response = Ok(Certainty::overflow(false)); for _ in 0..FIXPOINT_STEP_LIMIT { - // FIXME: This match is a bit ugly, it might be nice to change the inspect - // stuff to use a closure instead. which should hopefully simplify this a bit. match self.evaluate_added_goals_step() { - Ok(Some(cert)) => { - response = Ok(cert); - break; - } Ok(None) => {} + Ok(Some(cert)) => return Ok(cert), Err(NoSolution) => { - response = Err(NoSolution); - break; + self.tainted = Err(NoSolution); + return Err(NoSolution); } } } - if response.is_err() { - self.tainted = Err(NoSolution); - } - - response + debug!("try_evaluate_added_goals: encountered overflow"); + Ok(Certainty::overflow(false)) } /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning. @@ -634,7 +659,7 @@ where if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, None)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -710,7 +735,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, with_resolved_vars, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -724,7 +749,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -1184,28 +1209,204 @@ where pub(crate) fn opaques_with_sub_unified_hidden_type( &self, self_ty: I::Ty, - ) -> impl Iterator<Item = ty::AliasTy<I>> + use<'a, D, I> { - let delegate = self.delegate; - delegate - .clone_opaque_types_lookup_table() - .into_iter() - .chain(delegate.clone_duplicate_opaque_types()) - .filter_map(move |(key, hidden_ty)| { - if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() { - if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() { - if delegate.sub_unification_table_root_var(self_vid) - == delegate.sub_unification_table_root_var(hidden_vid) - { - return Some(ty::AliasTy::new_from_args( - delegate.cx(), - key.def_id.into(), - key.args, - )); - } + ) -> Vec<ty::AliasTy<I>> { + if let ty::Infer(ty::TyVar(vid)) = self_ty.kind() { + self.delegate.opaques_with_sub_unified_hidden_type(vid) + } else { + vec![] + } + } + + /// To return the constraints of a canonical query to the caller, we canonicalize: + /// + /// - `var_values`: a map from bound variables in the canonical goal to + /// the values inferred while solving the instantiated goal. + /// - `external_constraints`: additional constraints which aren't expressible + /// using simple unification of inference variables. + /// + /// This takes the `shallow_certainty` which represents whether we're confident + /// that the final result of the current goal only depends on the nested goals. + /// + /// In case this is `Certainty::Maybe`, there may still be additional nested goals + /// or inference constraints required for this candidate to be hold. The candidate + /// always requires all already added constraints and nested goals. + #[instrument(level = "trace", skip(self), ret)] + pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( + &mut self, + shallow_certainty: Certainty, + ) -> QueryResult<I> { + self.inspect.make_canonical_response(shallow_certainty); + + let goals_certainty = self.try_evaluate_added_goals()?; + assert_eq!( + self.tainted, + Ok(()), + "EvalCtxt is tainted -- nested goals may have been dropped in a \ + previous call to `try_evaluate_added_goals!`" + ); + + // We only check for leaks from universes which were entered inside + // of the query. + self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { + trace!("failed the leak check"); + NoSolution + })?; + + let (certainty, normalization_nested_goals) = + match (self.current_goal_kind, shallow_certainty) { + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. We only do so if we do not + // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly + // uplifting its nested goals. This is the case if the `shallow_certainty` is + // `Certainty::Yes`. + (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { + let goals = std::mem::take(&mut self.nested_goals); + // As we return all ambiguous nested goals, we can ignore the certainty + // returned by `self.try_evaluate_added_goals()`. + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); } + ( + Certainty::Yes, + NestedNormalizationGoals( + goals.into_iter().map(|(s, g, _)| (s, g)).collect(), + ), + ) } - None - }) + _ => { + let certainty = shallow_certainty.and(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + } + }; + + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty + { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); + } + + let external_constraints = + self.compute_external_query_constraints(certainty, normalization_nested_goals); + let (var_values, mut external_constraints) = + eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); + + // Remove any trivial or duplicated region constraints once we've resolved regions + let mut unique = HashSet::default(); + external_constraints.region_constraints.retain(|outlives| { + outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) + }); + + let canonical = canonicalize_response( + self.delegate, + self.max_input_universe, + Response { + var_values, + certainty, + external_constraints: self.cx().mk_external_constraints(external_constraints), + }, + ); + + // HACK: We bail with overflow if the response would have too many non-region + // inference variables. This tends to only happen if we encounter a lot of + // ambiguous alias types which get replaced with fresh inference variables + // during generalization. This prevents hangs caused by an exponential blowup, + // see tests/ui/traits/next-solver/coherence-alias-hang.rs. + match self.current_goal_kind { + // We don't do so for `NormalizesTo` goals as we erased the expected term and + // bailing with overflow here would prevent us from detecting a type-mismatch, + // causing a coherence error in diesel, see #131969. We still bail with overflow + // when later returning from the parent AliasRelate goal. + CurrentGoalKind::NormalizesTo => {} + CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { + let num_non_region_vars = canonical + .variables + .iter() + .filter(|c| !c.is_region() && c.is_existential()) + .count(); + if num_non_region_vars > self.cx().recursion_limit() { + debug!(?num_non_region_vars, "too many inference variables -> overflow"); + return Ok(self.make_ambiguous_response_no_constraints( + MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + }, + OpaqueTypesJank::AllGood, + )); + } + } + } + + Ok(canonical) + } + + /// Constructs a totally unconstrained, ambiguous response to a goal. + /// + /// Take care when using this, since often it's useful to respond with + /// ambiguity but return constrained variables to guide inference. + pub(in crate::solve) fn make_ambiguous_response_no_constraints( + &self, + cause: MaybeCause, + opaque_types_jank: OpaqueTypesJank, + ) -> CanonicalResponse<I> { + response_no_constraints_raw( + self.cx(), + self.max_input_universe, + self.variables, + Certainty::Maybe { cause, opaque_types_jank }, + ) + } + + /// Computes the region constraints and *new* opaque types registered when + /// proving a goal. + /// + /// If an opaque was already constrained before proving this goal, then the + /// external constraints do not need to record that opaque, since if it is + /// further constrained by inference, that will be passed back in the var + /// values. + #[instrument(level = "trace", skip(self), ret)] + fn compute_external_query_constraints( + &self, + certainty: Certainty, + normalization_nested_goals: NestedNormalizationGoals<I>, + ) -> ExternalConstraintsData<I> { + // We only return region constraints once the certainty is `Yes`. This + // is necessary as we may drop nested goals on ambiguity, which may result + // in unconstrained inference variables in the region constraints. It also + // prevents us from emitting duplicate region constraints, avoiding some + // unnecessary work. This slightly weakens the leak check in case it uses + // region constraints from an ambiguous nested goal. This is tested in both + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. + let region_constraints = if certainty == Certainty::Yes { + self.delegate.make_deduplicated_outlives_constraints() + } else { + Default::default() + }; + + // We only return *newly defined* opaque types from canonical queries. + // + // Constraints for any existing opaque types are already tracked by changes + // to the `var_values`. + let opaque_types = self + .delegate + .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); + + ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } } } @@ -1347,7 +1548,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>, let opaque_types = delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = EvalCtxt::canonicalize_goal(delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, &opaque_types); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); @@ -1364,7 +1565,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree<D: SolverDelegate<Interner = I>, Ok(response) => response, }; - let (normalization_nested_goals, _certainty) = EvalCtxt::instantiate_and_apply_query_response( + let (normalization_nested_goals, _certainty) = instantiate_and_apply_query_response( delegate, goal.param_env, &proof_tree.orig_values, diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 2675ed0d0da..4369148baf9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -10,8 +10,8 @@ use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::{self as ty, Interner}; +use crate::canonical; use crate::delegate::SolverDelegate; -use crate::solve::eval_ctxt::canonical; use crate::solve::{Certainty, Goal, GoalSource, QueryResult, inspect}; /// We need to know whether to build a prove tree while evaluating. We diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs index 0d8c0060126..65f32f1947f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs @@ -2,5 +2,3 @@ pub use rustc_type_ir::solve::inspect::*; mod build; pub(in crate::solve) use build::*; - -pub use crate::solve::eval_ctxt::canonical::instantiate_canonical_state; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index cd27c9c26c1..a58caeecc33 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -158,9 +158,10 @@ where if self.may_use_unstable_feature(param_env, symbol) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Ambiguity, - )) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }) } } @@ -278,18 +279,21 @@ where fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> { debug_assert!(candidates.len() > 1); - let maybe_cause = - candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| { - // Pull down the certainty of `Certainty::Yes` to ambiguity when combining + let (cause, opaque_types_jank) = candidates.iter().fold( + (MaybeCause::Ambiguity, OpaqueTypesJank::AllGood), + |(c, jank), candidates| { + // We pull down the certainty of `Certainty::Yes` to ambiguity when combining // these responses, b/c we're combining more than one response and this we // don't know which one applies. - let candidate = match candidates.result.value.certainty { - Certainty::Yes => MaybeCause::Ambiguity, - Certainty::Maybe(candidate) => candidate, - }; - maybe_cause.or(candidate) - }); - self.make_ambiguous_response_no_constraints(maybe_cause) + match candidates.result.value.certainty { + Certainty::Yes => (c, jank), + Certainty::Maybe { cause, opaque_types_jank } => { + (c.or(cause), jank.or(opaque_types_jank)) + } + } + }, + ); + self.make_ambiguous_response_no_constraints(cause, opaque_types_jank) } /// If we fail to merge responses we flounder and return overflow or ambiguity. @@ -376,26 +380,8 @@ where } } -fn response_no_constraints_raw<I: Interner>( - cx: I, - max_universe: ty::UniverseIndex, - variables: I::CanonicalVarKinds, - certainty: Certainty, -) -> CanonicalResponse<I> { - ty::Canonical { - max_universe, - variables, - value: Response { - var_values: ty::CanonicalVarValues::make_identity(cx, variables), - // FIXME: maybe we should store the "no response" version in cx, like - // we do for cx.types and stuff. - external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), - certainty, - }, - } -} - /// The result of evaluating a goal. +#[derive_where(Debug; I: Interner)] pub struct GoalEvaluation<I: Interner> { /// The goal we've evaluated. This is the input goal, but potentially with its /// inference variables resolved. This never applies any inference constraints @@ -427,6 +413,7 @@ pub struct GoalStalledOn<I: Interner> { pub num_opaques: usize, pub stalled_vars: Vec<I::GenericArg>, pub sub_roots: Vec<TyVid>, - /// The cause that will be returned on subsequent evaluations if this goal remains stalled. - pub stalled_cause: MaybeCause, + /// The certainty that will be returned on subsequent evaluations if this + /// goal remains stalled. + pub stalled_certainty: Certainty, } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 54b92ebac1d..0674b3d42ab 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -451,23 +451,22 @@ where return ecx.forced_ambiguity(MaybeCause::Ambiguity); } }; + let (inputs, output) = ecx.instantiate_binder_with_infer(tupled_inputs_and_output); // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) - }); + let output_is_sized_pred = + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]); - let pred = tupled_inputs_and_output - .map_bound(|(inputs, output)| ty::ProjectionPredicate { - projection_term: ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), inputs], - ), - term: output.into(), - }) - .upcast(cx); + let pred = ty::ProjectionPredicate { + projection_term: ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), inputs], + ), + term: output.into(), + } + .upcast(cx); Self::probe_and_consider_implied_clause( ecx, @@ -497,76 +496,56 @@ where goal_kind, env_region, )?; + let AsyncCallableRelevantTypes { + tupled_inputs_ty, + output_coroutine_ty, + coroutine_return_ty, + } = ecx.instantiate_binder_with_infer(tupled_inputs_and_output_and_coroutine); // A built-in `AsyncFn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( - |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| { - ty::TraitRef::new( - cx, - cx.require_trait_lang_item(SolverTraitLangItem::Sized), - [output_ty], - ) - }, + let output_is_sized_pred = ty::TraitRef::new( + cx, + cx.require_trait_lang_item(SolverTraitLangItem::Sized), + [output_coroutine_ty], ); - let pred = tupled_inputs_and_output_and_coroutine - .map_bound( - |AsyncCallableRelevantTypes { - tupled_inputs_ty, - output_coroutine_ty, - coroutine_return_ty, - }| { - let (projection_term, term) = if cx - .is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) - { - ( - ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), tupled_inputs_ty], - ), - output_coroutine_ty.into(), - ) - } else if cx - .is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) - { - ( - ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - env_region.into(), - ], - ), - output_coroutine_ty.into(), - ) - } else if cx - .is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) - { - ( - ty::AliasTerm::new( - cx, - goal.predicate.def_id(), - [ - I::GenericArg::from(goal.predicate.self_ty()), - tupled_inputs_ty.into(), - ], - ), - coroutine_return_ty.into(), - ) - } else { - panic!( - "no such associated type in `AsyncFn*`: {:?}", - goal.predicate.def_id() - ) - }; - ty::ProjectionPredicate { projection_term, term } - }, - ) - .upcast(cx); + let (projection_term, term) = + if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallOnceFuture) { + ( + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::CallRefFuture) { + ( + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [ + I::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), + output_coroutine_ty.into(), + ) + } else if cx.is_lang_item(goal.predicate.def_id(), SolverLangItem::AsyncFnOnceOutput) { + ( + ty::AliasTerm::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + coroutine_return_ty.into(), + ) + } else { + panic!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id()) + }; + let pred = ty::ProjectionPredicate { projection_term, term }.upcast(cx); Self::probe_and_consider_implied_clause( ecx, @@ -665,7 +644,7 @@ where ty::Str | ty::Slice(_) => Ty::new_usize(cx), - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); cx.type_of(dyn_metadata) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) @@ -916,7 +895,7 @@ where | ty::Adt(_, _) | ty::Str | ty::Slice(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Tuple(_) | ty::Error(_) => self_ty.discriminant_ty(ecx.cx()), diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index f0342e0523f..109c8476ccb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -6,6 +6,7 @@ use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult}; use rustc_type_ir::{Interner, TypingMode}; +use crate::canonical::response_no_constraints_raw; use crate::delegate::SolverDelegate; use crate::solve::{ EvalCtxt, FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints, inspect, @@ -73,27 +74,35 @@ where } } - fn is_initial_provisional_result( - cx: Self::Cx, - kind: PathKind, - input: CanonicalInput<I>, - result: QueryResult<I>, - ) -> bool { - Self::initial_provisional_result(cx, kind, input) == result + fn is_initial_provisional_result(result: QueryResult<I>) -> Option<PathKind> { + match result { + Ok(response) => { + if has_no_inference_or_external_constraints(response) { + if response.value.certainty == Certainty::Yes { + return Some(PathKind::Coinductive); + } else if response.value.certainty == Certainty::overflow(false) { + return Some(PathKind::Unknown); + } + } + + None + } + Err(NoSolution) => Some(PathKind::Inductive), + } } - fn on_stack_overflow(cx: I, input: CanonicalInput<I>) -> QueryResult<I> { + fn stack_overflow_result(cx: I, input: CanonicalInput<I>) -> QueryResult<I> { response_no_constraints(cx, input, Certainty::overflow(true)) } - fn on_fixpoint_overflow(cx: I, input: CanonicalInput<I>) -> QueryResult<I> { + fn fixpoint_overflow_result(cx: I, input: CanonicalInput<I>) -> QueryResult<I> { response_no_constraints(cx, input, Certainty::overflow(false)) } fn is_ambiguous_result(result: QueryResult<I>) -> bool { result.is_ok_and(|response| { has_no_inference_or_external_constraints(response) - && matches!(response.value.certainty, Certainty::Maybe(_)) + && matches!(response.value.certainty, Certainty::Maybe { .. }) }) } @@ -127,7 +136,7 @@ fn response_no_constraints<I: Interner>( input: CanonicalInput<I>, certainty: Certainty, ) -> QueryResult<I> { - Ok(super::response_no_constraints_raw( + Ok(response_no_constraints_raw( cx, input.canonical.max_universe, input.canonical.variables, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index a69e867289c..e790ecd595b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -369,18 +369,16 @@ where return ecx.forced_ambiguity(MaybeCause::Ambiguity); } }; + let (inputs, output) = ecx.instantiate_binder_with_infer(tupled_inputs_and_output); // A built-in `Fn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { - ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) - }); + let output_is_sized_pred = + ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]); - let pred = tupled_inputs_and_output - .map_bound(|(inputs, _)| { - ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) - }) - .upcast(cx); + let pred = + ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + .upcast(cx); Self::probe_and_consider_implied_clause( ecx, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), @@ -408,28 +406,26 @@ where // This region doesn't matter because we're throwing away the coroutine type Region::new_static(cx), )?; + let AsyncCallableRelevantTypes { + tupled_inputs_ty, + output_coroutine_ty, + coroutine_return_ty: _, + } = ecx.instantiate_binder_with_infer(tupled_inputs_and_output_and_coroutine); // A built-in `AsyncFn` impl only holds if the output is sized. // (FIXME: technically we only need to check this if the type is a fn ptr...) - let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( - |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| { - ty::TraitRef::new( - cx, - cx.require_trait_lang_item(SolverTraitLangItem::Sized), - [output_coroutine_ty], - ) - }, + let output_is_sized_pred = ty::TraitRef::new( + cx, + cx.require_trait_lang_item(SolverTraitLangItem::Sized), + [output_coroutine_ty], ); - let pred = tupled_inputs_and_output_and_coroutine - .map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| { - ty::TraitRef::new( - cx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), tupled_inputs_ty], - ) - }) - .upcast(cx); + let pred = ty::TraitRef::new( + cx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ) + .upcast(cx); Self::probe_and_consider_implied_clause( ecx, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), @@ -817,15 +813,13 @@ where } // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. - ( - ty::Dynamic(a_data, a_region, ty::Dyn), - ty::Dynamic(b_data, b_region, ty::Dyn), - ) => ecx.consider_builtin_dyn_upcast_candidates( - goal, a_data, a_region, b_data, b_region, - ), + (ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => ecx + .consider_builtin_dyn_upcast_candidates( + goal, a_data, a_region, b_data, b_region, + ), // `T` -> `dyn Trait` unsizing. - (_, ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single( + (_, ty::Dynamic(b_region, b_data)) => result_to_single( ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data), ), diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 72cd75f6d89..f83cf645f82 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -189,6 +189,10 @@ parse_dotdotdot = unexpected token: `...` parse_dotdotdot_rest_pattern = unexpected `...` .label = not a valid pattern .suggestion = for a rest pattern, use `..` instead of `...` + .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +parse_dotdotdot_rest_type = unexpected `...` + .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list parse_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 00ca5acd84d..1abeee6fe43 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2723,7 +2723,9 @@ pub(crate) struct DotDotDotRestPattern { #[label] pub span: Span, #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] - pub suggestion: Span, + pub suggestion: Option<Span>, + #[note] + pub var_args: Option<()>, } #[derive(Diagnostic)] @@ -3031,6 +3033,14 @@ pub(crate) struct NestedCVariadicType { } #[derive(Diagnostic)] +#[diag(parse_dotdotdot_rest_type)] +#[note] +pub(crate) struct InvalidCVariadicType { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_invalid_dyn_keyword)] #[help] pub(crate) struct InvalidDynKeyword { diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 88b67d792de..c26c7b9122a 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -9,6 +9,7 @@ #![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(iter_order_by)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c4d30b3d328..fda19d62bc7 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -756,7 +756,7 @@ impl<'a> Parser<'a> { self.bump(); // `..` PatKind::Rest } else if self.check(exp!(DotDotDot)) && !self.is_pat_range_end_start(1) { - self.recover_dotdotdot_rest_pat(lo) + self.recover_dotdotdot_rest_pat(lo, expected) } else if let Some(form) = self.parse_range_end() { self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. } else if self.eat(exp!(Bang)) { @@ -886,16 +886,27 @@ impl<'a> Parser<'a> { /// Recover from a typoed `...` pattern that was encountered /// Ref: Issue #70388 - fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind { + fn recover_dotdotdot_rest_pat(&mut self, lo: Span, expected: Option<Expected>) -> PatKind { // A typoed rest pattern `...`. self.bump(); // `...` - // The user probably mistook `...` for a rest pattern `..`. - self.dcx().emit_err(DotDotDotRestPattern { - span: lo, - suggestion: lo.with_lo(lo.hi() - BytePos(1)), - }); - PatKind::Rest + if let Some(Expected::ParameterName) = expected { + // We have `...` in a closure argument, likely meant to be var-arg, which aren't + // supported in closures (#146489). + PatKind::Err(self.dcx().emit_err(DotDotDotRestPattern { + span: lo, + suggestion: None, + var_args: Some(()), + })) + } else { + // The user probably mistook `...` for a rest pattern `..`. + self.dcx().emit_err(DotDotDotRestPattern { + span: lo, + suggestion: Some(lo.with_lo(lo.hi() - BytePos(1))), + var_args: None, + }); + PatKind::Rest + } } /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`. diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index 19b2c98f5af..63177a72744 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -15,7 +15,7 @@ fn sp(a: u32, b: u32) -> Span { } fn cmp_token_stream(a: &TokenStream, b: &TokenStream) -> bool { - a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.eq_unspanned(y)) + a.iter().eq_by(b.iter(), |x, y| x.eq_unspanned(y)) } #[test] diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 23aaafac934..65347496599 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -15,8 +15,8 @@ use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; use crate::errors::{ self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg, - HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, - NestedCVariadicType, ReturnTypesUseThinArrow, + HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut, + NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; use crate::parser::item::FrontMatterParsingMode; use crate::parser::{FnContext, FnParseMode}; @@ -106,6 +106,15 @@ fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool { impl<'a> Parser<'a> { /// Parses a type. pub fn parse_ty(&mut self) -> PResult<'a, Box<Ty>> { + if self.token == token::DotDotDot { + // We special case this so that we don't talk about "nested C-variadics" in types. + // We still pass in `AllowCVariadic::No` so that `parse_ty_common` can complain about + // things like `Vec<...>`. + let span = self.token.span; + self.bump(); + let kind = TyKind::Err(self.dcx().emit_err(InvalidCVariadicType { span })); + return Ok(self.mk_ty(span, kind)); + } // Make sure deeply nested types don't overflow the stack. ensure_sufficient_stack(|| { self.parse_ty_common( diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index ab7885905a6..df4016dfa1b 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -75,7 +75,7 @@ passes_const_stable_not_stable = .label = attribute specified here passes_custom_mir_incompatible_dialect_and_phase = - The {$dialect} dialect is not compatible with the {$phase} phase + the {$dialect} dialect is not compatible with the {$phase} phase .dialect_span = this dialect... .phase_span = ... is not compatible with this phase @@ -93,15 +93,6 @@ passes_dead_codes = } } never {$participle} -passes_debug_visualizer_invalid = - invalid argument - .note_1 = expected: `natvis_file = "..."` - .note_2 = OR - .note_3 = expected: `gdb_script_file = "..."` - -passes_debug_visualizer_placement = - attribute should be applied to a module - passes_debug_visualizer_unreadable = couldn't read {$file}: {$error} @@ -149,8 +140,17 @@ passes_doc_attribute_not_attribute = nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]` .help = only existing builtin attributes are allowed in core/std -passes_doc_cfg_hide_takes_list = - `#[doc(cfg_hide(...))]` takes a list of attributes +passes_doc_auto_cfg_expects_hide_or_show = + only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` + +passes_doc_auto_cfg_hide_show_expects_list = + `#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items + +passes_doc_auto_cfg_hide_show_unexpected_item = + `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items + +passes_doc_auto_cfg_wrong_literal = + expected boolean for `#[doc(auto_cfg = ...)]` passes_doc_expect_str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] @@ -349,10 +349,6 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} -passes_invalid_macro_export_arguments = invalid `#[macro_export]` argument - -passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments - passes_lang_item_fn = {$name -> [panic_impl] `#[panic_handler]` *[other] `{$name}` lang item @@ -392,9 +388,6 @@ passes_loop_match_attr = `#[loop_match]` should be applied to a loop .label = not a loop -passes_macro_export = - `#[macro_export]` only has an effect on macro definitions - passes_macro_export_on_decl_macro = `#[macro_export]` has no effect on declarative macro definitions .note = declarative macros follow the same exporting rules as regular items diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2562d2e0b83..4ea237cfa03 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,7 +10,7 @@ use std::collections::hash_map::Entry; use std::slice; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, ast}; +use rustc_ast::{AttrStyle, LitKind, MetaItem, MetaItemInner, MetaItemKind, ast}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; @@ -38,8 +38,8 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, - MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_ATTRIBUTES, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -217,7 +217,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, Attribute::Parsed(AttributeKind::Link(_, attr_span)) => { self.check_link(hir_id, *attr_span, span, target) - } + }, + Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => { + self.check_macro_export(hir_id, *span, target) + }, Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -246,7 +249,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Repr { .. } | AttributeKind::Cold(..) | AttributeKind::ExportName { .. } - | AttributeKind::CoherenceIsCore | AttributeKind::Fundamental | AttributeKind::Optimize(..) | AttributeKind::LinkSection { .. } @@ -254,6 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MacroEscape( .. ) | AttributeKind::RustcLayoutScalarValidRangeStart(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) + | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) | AttributeKind::ExportStable | AttributeKind::FfiConst(..) | AttributeKind::UnstableFeatureBound(..) @@ -276,6 +279,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::PatternComplexityLimit { .. } | AttributeKind::NoCore { .. } | AttributeKind::NoStd { .. } + | AttributeKind::ObjcClass { .. } + | AttributeKind::ObjcSelector { .. } + | AttributeKind::RustcCoherenceIsCore(..) + | AttributeKind::DebuggerVisualizer(..) ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -296,7 +303,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &mut doc_aliases, ), [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), [sym::rustc_no_implicit_autorefs, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } @@ -329,7 +335,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_has_incoherent_inherent_impls, ..] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -1155,16 +1160,59 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Check that the `#![doc(cfg_hide(...))]` attribute only contains a list of attributes. - /// - fn check_doc_cfg_hide(&self, meta: &MetaItemInner, hir_id: HirId) { - if meta.meta_item_list().is_none() { - self.tcx.emit_node_span_lint( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span(), - errors::DocCfgHideTakesList, - ); + /// Check that the `#![doc(auto_cfg)]` attribute has the expected input. + fn check_doc_auto_cfg(&self, meta: &MetaItem, hir_id: HirId) { + match &meta.kind { + MetaItemKind::Word => {} + MetaItemKind::NameValue(lit) => { + if !matches!(lit.kind, LitKind::Bool(_)) { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span, + errors::DocAutoCfgWrongLiteral, + ); + } + } + MetaItemKind::List(list) => { + for item in list { + let Some(attr_name @ (sym::hide | sym::show)) = item.name() else { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span, + errors::DocAutoCfgExpectsHideOrShow, + ); + continue; + }; + if let Some(list) = item.meta_item_list() { + for item in list { + let valid = item.meta_item().is_some_and(|meta| { + meta.path.segments.len() == 1 + && matches!( + &meta.kind, + MetaItemKind::Word | MetaItemKind::NameValue(_) + ) + }); + if !valid { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + item.span(), + errors::DocAutoCfgHideShowUnexpectedItem { attr_name }, + ); + } + } + } else { + self.tcx.emit_node_span_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span, + errors::DocAutoCfgHideShowExpectsList { attr_name }, + ); + } + } + } } } @@ -1240,10 +1288,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_attr_crate_level(attr, style, meta, hir_id); } - Some(sym::cfg_hide) => { - if self.check_attr_crate_level(attr, style, meta, hir_id) { - self.check_doc_cfg_hide(meta, hir_id); - } + Some(sym::auto_cfg) => { + self.check_doc_auto_cfg(i_meta, hir_id); } Some(sym::inline | sym::no_inline) => { @@ -1778,20 +1824,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the items on the `#[debugger_visualizer]` attribute are valid. - fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) { - // Here we only check that the #[debugger_visualizer] attribute is attached - // to nothing other than a module. All other checks are done in the - // `debugger_visualizer` query where they need to be done for decoding - // anyway. - match target { - Target::Mod => {} - _ => { - self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span() }); - } - } - } - /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. /// (Allows proc_macro functions) fn check_rustc_allow_const_fn_unstable( @@ -1848,45 +1880,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { + fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) { if target != Target::MacroDef { + return; + } + + // special case when `#[macro_export]` is applied to a macro 2.0 + let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); + let is_decl_macro = !macro_definition.macro_rules; + + if is_decl_macro { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), - errors::MacroExport::Normal, + attr_span, + errors::MacroExport::OnDeclMacro, ); - } else if let Some(meta_item_list) = attr.meta_item_list() - && !meta_item_list.is_empty() - { - if meta_item_list.len() > 1 { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - attr.span(), - errors::MacroExport::TooManyItems, - ); - } else if !meta_item_list[0].has_name(sym::local_inner_macros) { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - meta_item_list[0].span(), - errors::MacroExport::InvalidArgument, - ); - } - } else { - // special case when `#[macro_export]` is applied to a macro 2.0 - let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); - let is_decl_macro = !macro_definition.macro_rules; - - if is_decl_macro { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span(), - errors::MacroExport::OnDeclMacro, - ); - } } } @@ -2251,7 +2260,9 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // In the long run, the checks should be harmonized. if let ItemKind::Macro(_, macro_def, _) = item.kind { let def_id = item.owner_id.to_def_id(); - if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { + if macro_def.macro_rules + && !find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) + { check_non_exported_macro_for_invalid_attrs(self.tcx, item); } } @@ -2382,7 +2393,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { // which were unsuccessfully resolved due to cannot determine // resolution for the attribute macro error. const ATTRS_TO_CHECK: &[Symbol] = &[ - sym::macro_export, sym::rustc_main, sym::derive, sym::test, diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index 6eded3a9eb9..fee920221e1 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -295,7 +295,7 @@ impl<'tcx, 'a> TypeVisitor<TyCtxt<'tcx>> for ExportableItemsChecker<'tcx, 'a> { | ty::Ref(_, _, _) | ty::Param(_) | ty::Closure(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::Foreign(_) | ty::Str diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index fc33405d455..3c2c9683a4d 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -373,31 +373,27 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// Automatically generated items marked with `rustc_trivial_field_reads` /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). - fn should_ignore_item(&mut self, def_id: DefId) -> bool { - if let Some(impl_of) = self.tcx.trait_impl_of_assoc(def_id) { - if !self.tcx.is_automatically_derived(impl_of) { - return false; - } - - if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) - && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) + fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { + if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind + && let impl_of = self.tcx.parent(impl_item.owner_id.to_def_id()) + && self.tcx.is_automatically_derived(impl_of) + && let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().instantiate_identity() + && self.tcx.has_attr(trait_ref.def_id, sym::rustc_trivial_field_reads) + { + if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() + && let Some(adt_def_id) = adt_def.did().as_local() { - let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().instantiate_identity(); - if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() - && let Some(adt_def_id) = adt_def.did().as_local() - { - self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_of); - } - return true; + self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_ref.def_id); } + return true; } false } fn visit_node(&mut self, node: Node<'tcx>) { - if let Node::ImplItem(hir::ImplItem { owner_id, .. }) = node - && self.should_ignore_item(owner_id.to_def_id()) + if let Node::ImplItem(impl_item) = node + && self.should_ignore_impl_item(impl_item) { return; } @@ -439,7 +435,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } Node::ImplItem(impl_item) => { let item = self.tcx.local_parent(impl_item.owner_id.def_id); - if self.tcx.impl_trait_ref(item).is_none() { + if let hir::ImplItemImplKind::Inherent { .. } = impl_item.impl_kind { //// If it's a type whose items are live, then it's live, too. //// This is done to handle the case where, for example, the static //// method of a private type is used, but the type itself is never @@ -484,13 +480,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn check_impl_or_impl_item_live(&mut self, local_def_id: LocalDefId) -> bool { let (impl_block_id, trait_def_id) = match self.tcx.def_kind(local_def_id) { // assoc impl items of traits are live if the corresponding trait items are live - DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => ( - self.tcx.local_parent(local_def_id), - self.tcx - .associated_item(local_def_id) - .trait_item_def_id - .and_then(|def_id| def_id.as_local()), - ), + DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => { + let trait_item_id = + self.tcx.trait_item_of(local_def_id).and_then(|def_id| def_id.as_local()); + (self.tcx.local_parent(local_def_id), trait_item_id) + } // impl items are live if the corresponding traits are live DefKind::Impl { of_trait: true } => ( local_def_id, diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 7a7a8175e55..7211f3cf85b 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -1,67 +1,60 @@ //! Detecting usage of the `#[debugger_visualizer]` attribute. -use rustc_ast::Attribute; +use rustc_ast::ast::NodeId; +use rustc_ast::{HasNodeId, ItemKind, ast}; +use rustc_attr_parsing::AttributeParser; use rustc_expand::base::resolve_path; -use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_hir::Attribute; +use rustc_hir::attrs::{AttributeKind, DebugVisualizer}; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::sym; +use rustc_span::{DUMMY_SP, Span, sym}; -use crate::errors::{DebugVisualizerInvalid, DebugVisualizerUnreadable}; +use crate::errors::DebugVisualizerUnreadable; impl DebuggerVisualizerCollector<'_> { - fn check_for_debugger_visualizer(&mut self, attr: &Attribute) { - if attr.has_name(sym::debugger_visualizer) { - let Some(hints) = attr.meta_item_list() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; + fn check_for_debugger_visualizer( + &mut self, + attrs: &[ast::Attribute], + span: Span, + node_id: NodeId, + ) { + if let Some(Attribute::Parsed(AttributeKind::DebuggerVisualizer(visualizers))) = + AttributeParser::parse_limited( + &self.sess, + attrs, + sym::debugger_visualizer, + span, + node_id, + None, + ) + { + for DebugVisualizer { span, visualizer_type, path } in visualizers { + let file = match resolve_path(&self.sess, path.as_str(), span) { + Ok(file) => file, + Err(err) => { + err.emit(); + return; + } + }; - let [hint] = hints.as_slice() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; - - let Some(meta_item) = hint.meta_item() else { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: attr.span }); - return; - }; - - let (visualizer_type, visualizer_path) = match (meta_item.name(), meta_item.value_str()) - { - (Some(sym::natvis_file), Some(value)) => (DebuggerVisualizerType::Natvis, value), - (Some(sym::gdb_script_file), Some(value)) => { - (DebuggerVisualizerType::GdbPrettyPrinter, value) - } - (_, _) => { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span }); - return; - } - }; - - let file = match resolve_path(&self.sess, visualizer_path.as_str(), attr.span) { - Ok(file) => file, - Err(err) => { - err.emit(); - return; - } - }; - - match self.sess.source_map().load_binary_file(&file) { - Ok((source, _)) => { - self.visualizers.push(DebuggerVisualizerFile::new( - source, - visualizer_type, - file, - )); - } - Err(error) => { - self.sess.dcx().emit_err(DebugVisualizerUnreadable { - span: meta_item.span, - file: &file, - error, - }); + match self.sess.source_map().load_binary_file(&file) { + Ok((source, _)) => { + self.visualizers.push(DebuggerVisualizerFile::new( + source, + visualizer_type, + file, + )); + } + Err(error) => { + self.sess.dcx().emit_err(DebugVisualizerUnreadable { + span, + file: &file, + error, + }); + } } } } @@ -74,9 +67,15 @@ struct DebuggerVisualizerCollector<'a> { } impl<'ast> rustc_ast::visit::Visitor<'ast> for DebuggerVisualizerCollector<'_> { - fn visit_attribute(&mut self, attr: &'ast Attribute) { - self.check_for_debugger_visualizer(attr); - rustc_ast::visit::walk_attribute(self, attr); + fn visit_item(&mut self, item: &'ast rustc_ast::Item) -> Self::Result { + if let ItemKind::Mod(..) = item.kind { + self.check_for_debugger_visualizer(&item.attrs, item.span, item.node_id()); + } + rustc_ast::visit::walk_item(self, item); + } + fn visit_crate(&mut self, krate: &'ast ast::Crate) -> Self::Result { + self.check_for_debugger_visualizer(&krate.attrs, DUMMY_SP, krate.id); + rustc_ast::visit::walk_crate(self, krate); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 2da4b6f52cf..f0726014e0a 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -309,8 +309,24 @@ pub(crate) struct DocTestLiteral; pub(crate) struct DocTestTakesList; #[derive(LintDiagnostic)] -#[diag(passes_doc_cfg_hide_takes_list)] -pub(crate) struct DocCfgHideTakesList; +#[diag(passes_doc_auto_cfg_wrong_literal)] +pub(crate) struct DocAutoCfgWrongLiteral; + +#[derive(LintDiagnostic)] +#[diag(passes_doc_auto_cfg_expects_hide_or_show)] +pub(crate) struct DocAutoCfgExpectsHideOrShow; + +#[derive(LintDiagnostic)] +#[diag(passes_doc_auto_cfg_hide_show_expects_list)] +pub(crate) struct DocAutoCfgHideShowExpectsList { + pub attr_name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(passes_doc_auto_cfg_hide_show_unexpected_item)] +pub(crate) struct DocAutoCfgHideShowUnexpectedItem { + pub attr_name: Symbol, +} #[derive(LintDiagnostic)] #[diag(passes_doc_test_unknown_any)] @@ -476,23 +492,6 @@ pub(crate) struct MacroOnlyAttribute { } #[derive(Diagnostic)] -#[diag(passes_debug_visualizer_placement)] -pub(crate) struct DebugVisualizerPlacement { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(passes_debug_visualizer_invalid)] -#[note(passes_note_1)] -#[note(passes_note_2)] -#[note(passes_note_3)] -pub(crate) struct DebugVisualizerInvalid { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(passes_debug_visualizer_unreadable)] pub(crate) struct DebugVisualizerUnreadable<'a> { #[primary_span] @@ -530,18 +529,9 @@ pub(crate) struct RustcForceInlineCoro { #[derive(LintDiagnostic)] pub(crate) enum MacroExport { - #[diag(passes_macro_export)] - Normal, - #[diag(passes_macro_export_on_decl_macro)] #[note] OnDeclMacro, - - #[diag(passes_invalid_macro_export_arguments)] - InvalidArgument, - - #[diag(passes_invalid_macro_export_arguments_too_many_items)] - TooManyItems, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 71650c6b9b9..2ee1bd0dfd1 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -21,8 +21,8 @@ use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::middle::stability::{AllowUnstable, Deprecated, DeprecationEntry, EvalResult}; use rustc_middle::query::{LocalCrate, Providers}; -use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{AssocContainer, TyCtxt}; use rustc_session::lint; use rustc_session::lint::builtin::{DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL}; use rustc_span::{Span, Symbol, sym}; @@ -486,8 +486,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { self.check_compatible_stability(ii.owner_id.def_id); - let impl_def_id = self.tcx.hir_get_parent_item(ii.hir_id()); - if self.tcx.impl_trait_ref(impl_def_id).is_none() { + if let hir::ImplItemImplKind::Inherent { .. } = ii.impl_kind { self.check_missing_stability(ii.owner_id.def_id); self.check_missing_const_stability(ii.owner_id.def_id); } @@ -711,7 +710,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { for impl_item_ref in items { let impl_item = self.tcx.associated_item(impl_item_ref.owner_id); - if let Some(def_id) = impl_item.trait_item_def_id { + if let AssocContainer::TraitImpl(Ok(def_id)) = impl_item.container { // Pass `None` to skip deprecation warnings. self.tcx.check_stability( def_id, diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index c9bf4fe4449..0652461e975 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -120,7 +120,7 @@ impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> { impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// Type inference occasionally gives us opaque types in places where corresponding patterns /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited - /// types, we use the corresponding concrete type if possible. + /// types, we use the corresponding hidden type if possible. // FIXME(#132279): This will be unnecessary once we have a TypingMode which supports revealing // opaque types defined in a body. #[inline] @@ -146,7 +146,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { /// know it. fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> { self.typeck_results - .concrete_opaque_types + .hidden_types .get(&key.def_id) .map(|x| ty::EarlyBinder::bind(x.ty).instantiate(self.tcx, key.args)) } @@ -407,7 +407,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::Pat(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 1bddbd03cc3..c9dbb204f61 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -45,7 +45,7 @@ use tracing::debug; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } //////////////////////////////////////////////////////////////////////////////// -/// Generic infrastructure used to implement specific visitors below. +// Generic infrastructure used to implement specific visitors below. //////////////////////////////////////////////////////////////////////////////// struct LazyDefPathStr<'tcx> { @@ -309,10 +309,7 @@ fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visib if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 } } -//////////////////////////////////////////////////////////////////////////////// /// Visitor used to determine impl visibility and reachability. -//////////////////////////////////////////////////////////////////////////////// - struct FindMin<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> { tcx: TyCtxt<'tcx>, effective_visibilities: &'a EffectiveVisibilities, @@ -387,10 +384,7 @@ impl VisibilityLike for EffectiveVisibility { } } -//////////////////////////////////////////////////////////////////////////////// /// The embargo visitor, used to determine the exports of the AST. -//////////////////////////////////////////////////////////////////////////////// - struct EmbargoVisitor<'tcx> { tcx: TyCtxt<'tcx>, @@ -849,9 +843,7 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> } } -//////////////////////////////////////////////////////////////////////////////// /// Visitor, used for EffectiveVisibilities table checking -//////////////////////////////////////////////////////////////////////////////// pub struct TestReachabilityVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, effective_visibilities: &'a EffectiveVisibilities, @@ -909,13 +901,11 @@ impl<'a, 'tcx> TestReachabilityVisitor<'a, 'tcx> { } } -////////////////////////////////////////////////////////////////////////////////////// /// Name privacy visitor, checks privacy and reports violations. +/// /// Most of name privacy checks are performed during the main resolution phase, /// or later in type checking when field accesses and associated items are resolved. /// This pass performs remaining checks for fields in struct expressions and patterns. -////////////////////////////////////////////////////////////////////////////////////// - struct NamePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>, @@ -1120,12 +1110,10 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { } } -//////////////////////////////////////////////////////////////////////////////////////////// /// Type privacy visitor, checks types for privacy and reports violations. +/// /// Both explicitly written types and inferred types of expressions and patterns are checked. /// Checks are performed on "semantic" types regardless of names and their hygiene. -//////////////////////////////////////////////////////////////////////////////////////////// - struct TypePrivacyVisitor<'tcx> { tcx: TyCtxt<'tcx>, module_def_id: LocalModDefId, @@ -1345,13 +1333,11 @@ impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> { } } -/////////////////////////////////////////////////////////////////////////////// /// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and /// finds any private components in it. +/// /// PrivateItemsInPublicInterfacesVisitor ensures there are no private types /// and traits in public interfaces. -/////////////////////////////////////////////////////////////////////////////// - struct SearchInterfaceForPrivateItemsVisitor<'tcx> { tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 1b5f0ed1429..0afb94c18d7 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -333,7 +333,7 @@ impl TyKind { #[inline] pub fn is_trait(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn))) + matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _))) } #[inline] @@ -472,7 +472,7 @@ impl TyKind { } pub fn trait_principal(&self) -> Option<Binder<ExistentialTraitRef>> { - if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self { + if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _)) = self { if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) = predicates.first() { @@ -562,7 +562,7 @@ pub enum RigidTy { Closure(ClosureDef, GenericArgs), Coroutine(CoroutineDef, GenericArgs), CoroutineClosure(CoroutineClosureDef, GenericArgs), - Dynamic(Vec<Binder<ExistentialPredicate>>, Region, DynKind), + Dynamic(Vec<Binder<ExistentialPredicate>>, Region), Never, Tuple(Vec<Ty>), CoroutineWitness(CoroutineWitnessDef, GenericArgs), @@ -1207,11 +1207,6 @@ pub enum BoundRegionKind { } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum DynKind { - Dyn, -} - -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ExistentialPredicate { Trait(ExistentialTraitRef), Projection(ExistentialProjection), @@ -1612,11 +1607,7 @@ crate_def! { pub struct AssocItem { pub def_id: AssocDef, pub kind: AssocKind, - pub container: AssocItemContainer, - - /// If this is an item in an impl of a trait then this is the `DefId` of - /// the associated item on the trait that this implements. - pub trait_item_def_id: Option<AssocDef>, + pub container: AssocContainer, } #[derive(Clone, PartialEq, Debug, Eq, Serialize)] @@ -1636,9 +1627,11 @@ pub enum AssocKind { } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AssocItemContainer { +pub enum AssocContainer { + InherentImpl, + /// The `AssocDef` points to the trait item being implemented. + TraitImpl(AssocDef), Trait, - Impl, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 66f767a98f5..dc9abd88614 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -14,7 +14,7 @@ use crate::mir::alloc::AllocId; use crate::mir::mono::{Instance, MonoItem, StaticDef}; use crate::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; use crate::ty::{ - Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind, + Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, GenericArgKind, GenericArgs, IntTy, MirConst, Movability, Pattern, Region, RigidTy, Span, TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx, @@ -188,10 +188,9 @@ impl RustcInternal for RigidTy { def.0.internal(tables, tcx), args.internal(tables, tcx), ), - RigidTy::Dynamic(predicate, region, dyn_kind) => rustc_ty::TyKind::Dynamic( + RigidTy::Dynamic(predicate, region) => rustc_ty::TyKind::Dynamic( tcx.mk_poly_existential_predicates(&predicate.internal(tables, tcx)), region.internal(tables, tcx), - dyn_kind.internal(tables, tcx), ), RigidTy::Tuple(tys) => { rustc_ty::TyKind::Tuple(tcx.mk_type_list(&tys.internal(tables, tcx))) @@ -460,20 +459,6 @@ impl RustcInternal for BoundVariableKind { } } -impl RustcInternal for DynKind { - type T<'tcx> = rustc_ty::DynKind; - - fn internal<'tcx>( - &self, - _tables: &mut Tables<'_, BridgeTys>, - _tcx: impl InternalCx<'tcx>, - ) -> Self::T<'tcx> { - match self { - DynKind::Dyn => rustc_ty::DynKind::Dyn, - } - } -} - impl RustcInternal for ExistentialPredicate { type T<'tcx> = rustc_ty::ExistentialPredicate<'tcx>; diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index be8ee80bed3..b10af6526ea 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -215,7 +215,6 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { mutability.stable(tables, cx), place.stable(tables, cx), ), - Len(place) => crate::mir::Rvalue::Len(place.stable(tables, cx)), Cast(cast_kind, op, ty) => crate::mir::Rvalue::Cast( cast_kind.stable(tables, cx), op.stable(tables, cx), diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 207038db40d..7f14f878d37 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -48,16 +48,6 @@ impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> { } } -impl<'tcx> Stable<'tcx> for ty::DynKind { - type T = crate::ty::DynKind; - - fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { - match self { - ty::Dyn => crate::ty::DynKind::Dyn, - } - } -} - impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> { type T = crate::ty::ExistentialPredicate; @@ -439,16 +429,13 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { } // FIXME(unsafe_binders): ty::UnsafeBinder(_) => todo!(), - ty::Dynamic(existential_predicates, region, dyn_kind) => { - TyKind::RigidTy(RigidTy::Dynamic( - existential_predicates - .iter() - .map(|existential_predicate| existential_predicate.stable(tables, cx)) - .collect(), - region.stable(tables, cx), - dyn_kind.stable(tables, cx), - )) - } + ty::Dynamic(existential_predicates, region) => TyKind::RigidTy(RigidTy::Dynamic( + existential_predicates + .iter() + .map(|existential_predicate| existential_predicate.stable(tables, cx)) + .collect(), + region.stable(tables, cx), + )), ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( tables.closure_def(*def_id), generic_args.stable(tables, cx), @@ -1076,14 +1063,21 @@ impl<'tcx> Stable<'tcx> for ty::AssocKind { } } -impl<'tcx> Stable<'tcx> for ty::AssocItemContainer { - type T = crate::ty::AssocItemContainer; +impl<'tcx> Stable<'tcx> for ty::AssocContainer { + type T = crate::ty::AssocContainer; - fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { - use crate::ty::AssocItemContainer; + fn stable( + &self, + tables: &mut Tables<'_, BridgeTys>, + _: &CompilerCtxt<'_, BridgeTys>, + ) -> Self::T { + use crate::ty::AssocContainer; match self { - ty::AssocItemContainer::Trait => AssocItemContainer::Trait, - ty::AssocItemContainer::Impl => AssocItemContainer::Impl, + ty::AssocContainer::Trait => AssocContainer::Trait, + ty::AssocContainer::InherentImpl => AssocContainer::InherentImpl, + ty::AssocContainer::TraitImpl(trait_item_id) => { + AssocContainer::TraitImpl(tables.assoc_def(trait_item_id.unwrap())) + } } } } @@ -1100,7 +1094,6 @@ impl<'tcx> Stable<'tcx> for ty::AssocItem { def_id: tables.assoc_def(self.def_id), kind: self.kind.stable(tables, cx), container: self.container.stable(tables, cx), - trait_item_def_id: self.trait_item_def_id.map(|did| tables.assoc_def(did)), } } } diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index 87f1cc6ae69..acc33347696 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -171,7 +171,7 @@ impl Visitable for RigidTy { | RigidTy::CoroutineClosure(_, args) | RigidTy::FnDef(_, args) => args.visit(visitor), RigidTy::FnPtr(sig) => sig.visit(visitor), - RigidTy::Dynamic(pred, r, _) => { + RigidTy::Dynamic(pred, r) => { pred.visit(visitor)?; r.visit(visitor) } diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 0e566e20a12..cc8fbc18b73 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -231,6 +231,9 @@ resolve_item_was_cfg_out = the item is gated here resolve_label_with_similar_name_reachable = a label with a similar name is reachable +resolve_legacy_derive_helpers = derive helper attribute is used before it is introduced + .label = the attribute is introduced here + resolve_lending_iterator_report_error = associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type .note = you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type @@ -260,6 +263,9 @@ resolve_macro_defined_later = resolve_macro_expanded_extern_crate_cannot_shadow_extern_arguments = macro-expanded `extern crate` items cannot shadow names passed with `--extern` +resolve_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths + .note = the macro is defined here + resolve_macro_expected_found = expected {$expected}, found {$found} `{$macro_path}` .label = not {$article} {$expected} @@ -268,6 +274,10 @@ resolve_macro_extern_deprecated = `#[macro_escape]` is a deprecated synonym for `#[macro_use]` .help = try an outer attribute: `#[macro_use]` +resolve_macro_use_deprecated = + applying the `#[macro_use]` attribute to an `extern crate` item is deprecated + .help = remove it and import macros at use sites with a `use` item instead + resolve_macro_use_extern_crate_self = `#[macro_use]` is not supported on `extern crate self` resolve_macro_use_name_already_in_use = @@ -339,6 +349,9 @@ resolve_param_in_ty_of_const_param = resolve_pattern_doesnt_bind_name = pattern doesn't bind `{$name}` +resolve_proc_macro_derive_resolution_fallback = cannot find {$ns_descr} `{$ident}` in this scope + .label = names from parent modules are not accessible without an explicit import + resolve_proc_macro_same_crate = can't use a procedural macro from the same crate that defines it .help = you can define integration tests in a directory named `tests` @@ -348,6 +361,9 @@ resolve_reexport_of_crate_public = resolve_reexport_of_private = re-export of private `{$ident}` +resolve_reexport_private_dependency = + {$kind} `{$name}` from private dependency '{$krate}' is re-exported + resolve_relative_2018 = relative paths are not supported in visibilities in 2018 edition or later .suggestion = try @@ -465,6 +481,14 @@ resolve_unreachable_label_suggestion_use_similarly_named = resolve_unreachable_label_with_similar_name_exists = a label with a similar name exists but is unreachable +resolve_unused_extern_crate = unused extern crate + .label = unused + .suggestion = remove the unused `extern crate` + +resolve_unused_label = unused label + +resolve_unused_macro_use = unused `#[macro_use]` import + resolve_variable_bound_with_different_mode = variable `{$variable_name}` is bound inconsistently across alternatives separated by `|` .label = bound in different ways diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index fa3c06059b3..c10b6ca7e71 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -5,7 +5,6 @@ //! unexpanded macros in the fragment are visited and registered. //! Imports are also considered items and placed into modules here, but not resolved yet. -use std::cell::Cell; use std::sync::Arc; use rustc_ast::visit::{self, AssocCtxt, Visitor, WalkItemKind}; @@ -35,6 +34,7 @@ use crate::Namespace::{MacroNS, TypeNS, ValueNS}; use crate::def_collector::collect_definitions; use crate::imports::{ImportData, ImportKind}; use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; +use crate::ref_mut::CmCell; use crate::{ BindingKey, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult, ResolutionError, Resolver, Segment, Used, @@ -87,7 +87,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // because they can be fetched by glob imports from those modules, and bring traits // into scope both directly and through glob imports. let key = BindingKey::new_disambiguated(ident, ns, || { - parent.underscore_disambiguator.update(|d| d + 1); + // FIXME(batched): Will be fixed in batched resolution. + parent.underscore_disambiguator.update_unchecked(|d| d + 1); parent.underscore_disambiguator.get() }); if self @@ -477,7 +478,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind, parent_scope: self.parent_scope, module_path, - imported_module: Cell::new(None), + imported_module: CmCell::new(None), span, use_span: item.span, use_span_with_attributes: item.span_with_attributes(), @@ -505,7 +506,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { }); } } - ImportKind::Glob { .. } => current_module.globs.borrow_mut().push(import), + ImportKind::Glob { .. } => current_module.globs.borrow_mut(self.r).push(import), _ => unreachable!(), } } @@ -668,7 +669,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } ast::UseTreeKind::Glob => { if !ast::attr::contains_name(&item.attrs, sym::prelude_import) { - let kind = ImportKind::Glob { max_vis: Cell::new(None), id }; + let kind = ImportKind::Glob { max_vis: CmCell::new(None), id }; self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis); } else { // Resolve the prelude import early. @@ -971,7 +972,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id }, root_id: item.id, parent_scope: self.parent_scope, - imported_module: Cell::new(module), + imported_module: CmCell::new(module), has_attributes: !item.attrs.is_empty(), use_span_with_attributes: item.span_with_attributes(), use_span: item.span, @@ -1103,7 +1104,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind: ImportKind::MacroUse { warn_private }, root_id: item.id, parent_scope: this.parent_scope, - imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), + imported_module: CmCell::new(Some(ModuleOrUniformRoot::Module(module))), use_span_with_attributes: item.span_with_attributes(), has_attributes: !item.attrs.is_empty(), use_span: item.span, @@ -1196,7 +1197,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { /// directly into its parent scope's module. fn visit_invoc_in_module(&mut self, id: NodeId) -> MacroRulesScopeRef<'ra> { let invoc_id = self.visit_invoc(id); - self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id); + self.parent_scope.module.unexpanded_invocations.borrow_mut(self.r).insert(invoc_id); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)) } @@ -1274,7 +1275,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { kind: ImportKind::MacroExport, root_id: item.id, parent_scope: self.parent_scope, - imported_module: Cell::new(None), + imported_module: CmCell::new(None), has_attributes: false, use_span_with_attributes: span, use_span: span, diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 50a1ad23a54..95f979a3fed 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -169,7 +169,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { UNUSED_EXTERN_CRATES, extern_crate.id, span, - BuiltinLintDiag::UnusedExternCrate { + crate::errors::UnusedExternCrate { span: extern_crate.span, removal_span: extern_crate.span_with_attributes, }, @@ -406,7 +406,7 @@ impl Resolver<'_, '_> { MACRO_USE_EXTERN_CRATE, import.root_id, import.span, - BuiltinLintDiag::MacroUseDeprecated, + crate::errors::MacroUseDeprecated, ); } } @@ -427,7 +427,7 @@ impl Resolver<'_, '_> { UNUSED_IMPORTS, import.root_id, import.span, - BuiltinLintDiag::UnusedMacroUse, + crate::errors::UnusedMacroUse, ); } _ => {} diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 689e713ef46..236ab1f09d3 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -138,7 +138,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, CRATE_NODE_ID, span_use, - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), + errors::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }, ); } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 72be94e58db..f0ea97ba8a0 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -3,7 +3,7 @@ use rustc_errors::{ Applicability, Diag, ElidedLifetimeInPathSubdiag, EmissionGuarantee, IntoDiagArg, MultiSpan, Subdiagnostic, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; use crate::late::PatternSource; @@ -566,6 +566,22 @@ pub(crate) struct ProcMacroSameCrate { pub(crate) is_test: bool, } +#[derive(LintDiagnostic)] +#[diag(resolve_proc_macro_derive_resolution_fallback)] +pub(crate) struct ProcMacroDeriveResolutionFallback { + #[label] + pub span: Span, + pub ns_descr: &'static str, + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_macro_expanded_macro_exports_accessed_by_absolute_paths)] +pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note] + pub definition: Span, +} + #[derive(Diagnostic)] #[diag(resolve_imported_crate)] pub(crate) struct CrateImported { @@ -1276,3 +1292,40 @@ pub(crate) struct TraitImplMismatch { #[label(resolve_trait_impl_mismatch_label_item)] pub(crate) trait_item_span: Span, } + +#[derive(LintDiagnostic)] +#[diag(resolve_legacy_derive_helpers)] +pub(crate) struct LegacyDeriveHelpers { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_extern_crate)] +pub(crate) struct UnusedExternCrate { + #[label] + pub span: Span, + #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + pub removal_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_reexport_private_dependency)] +pub(crate) struct ReexportPrivateDependency { + pub name: Symbol, + pub kind: &'static str, + pub krate: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_label)] +pub(crate) struct UnusedLabel; + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_macro_use)] +pub(crate) struct UnusedMacroUse; + +#[derive(LintDiagnostic)] +#[diag(resolve_macro_use_deprecated)] +#[help] +pub(crate) struct MacroUseDeprecated; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index bc06a227571..4415300777f 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -4,7 +4,6 @@ use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; use rustc_middle::bug; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; @@ -493,14 +492,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => Err(Determinacy::Determined), }, Scope::Module(module, derive_fallback_lint_id) => { - // FIXME: use `finalize_scope` here. let (adjusted_parent_scope, adjusted_finalize) = if matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..)) { - (parent_scope, finalize) + (parent_scope, finalize_scope!()) } else { ( &ParentScope { module, ..*parent_scope }, - finalize.map(|f| Finalize { used: Used::Scope, ..f }), + finalize_scope!().map(|f| Finalize { used: Used::Scope, ..f }), ) }; let binding = this.reborrow().resolve_ident_in_module_unadjusted( @@ -520,7 +518,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, orig_ident.span, - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { + errors::ProcMacroDeriveResolutionFallback { span: orig_ident.span, ns_descr: ns.descr(), ident, @@ -558,8 +556,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => Err(Determinacy::Determined), }, Scope::ExternPreludeItems => { - // FIXME: use `finalize_scope` here. - match this.reborrow().extern_prelude_get_item(ident, finalize.is_some()) { + match this + .reborrow() + .extern_prelude_get_item(ident, finalize_scope!().is_some()) + { Some(binding) => { extern_prelude_item_binding = Some(binding); Ok((binding, Flags::empty())) @@ -901,6 +901,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding, if resolution.non_glob_binding.is_some() { resolution.glob_binding } else { None }, parent_scope, + module, finalize, shadowing, ); @@ -1025,6 +1026,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding: Option<NameBinding<'ra>>, shadowed_glob: Option<NameBinding<'ra>>, parent_scope: &ParentScope<'ra>, + module: Module<'ra>, finalize: Finalize, shadowing: Shadowing, ) -> Result<NameBinding<'ra>, (Determinacy, Weak)> { @@ -1076,6 +1078,37 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.macro_expanded_macro_export_errors.insert((path_span, binding.span)); } + // If we encounter a re-export for a type with private fields, it will not be able to + // be constructed through this re-export. We track that case here to expand later + // privacy errors with appropriate information. + if let Res::Def(_, def_id) = binding.res() { + let struct_ctor = match def_id.as_local() { + Some(def_id) => self.struct_constructors.get(&def_id).cloned(), + None => { + let ctor = self.cstore().ctor_untracked(def_id); + ctor.map(|(ctor_kind, ctor_def_id)| { + let ctor_res = Res::Def( + DefKind::Ctor(rustc_hir::def::CtorOf::Struct, ctor_kind), + ctor_def_id, + ); + let ctor_vis = self.tcx.visibility(ctor_def_id); + let field_visibilities = self + .tcx + .associated_item_def_ids(def_id) + .iter() + .map(|field_id| self.tcx.visibility(field_id)) + .collect(); + (ctor_res, ctor_vis, field_visibilities) + }) + } + }; + if let Some((_, _, fields)) = struct_ctor + && fields.iter().any(|vis| !self.is_accessible_from(*vis, module)) + { + self.inaccessible_ctor_reexport.insert(path_span, binding.span); + } + } + self.record_use(ident, binding, used); return Ok(binding); } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d7fc028287f..ce90a1bcd31 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1,6 +1,5 @@ //! A bunch of methods and structures more or less related to resolving imports. -use std::cell::Cell; use std::mem; use rustc_ast::NodeId; @@ -32,6 +31,7 @@ use crate::errors::{ CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates, ConsiderAddingMacroExport, ConsiderMarkingAsPub, ConsiderMarkingAsPubCrate, }; +use crate::ref_mut::CmCell; use crate::{ AmbiguityError, AmbiguityKind, BindingKey, CmResolver, Determinacy, Finalize, ImportSuggestion, Module, ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, @@ -68,7 +68,7 @@ pub(crate) enum ImportKind<'ra> { /// It will directly use `source` when the format is `use prefix::source`. target: Ident, /// Bindings introduced by the import. - bindings: PerNS<Cell<PendingBinding<'ra>>>, + bindings: PerNS<CmCell<PendingBinding<'ra>>>, /// `true` for `...::{self [as target]}` imports, `false` otherwise. type_ns_only: bool, /// Did this import result from a nested import? ie. `use foo::{bar, baz};` @@ -89,7 +89,7 @@ pub(crate) enum ImportKind<'ra> { Glob { // The visibility of the greatest re-export. // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors. - max_vis: Cell<Option<Visibility>>, + max_vis: CmCell<Option<Visibility>>, id: NodeId, }, ExternCrate { @@ -182,7 +182,7 @@ pub(crate) struct ImportData<'ra> { /// |`use ::foo` | `ModuleOrUniformRoot::ExternPrelude` | 2018+ editions | /// |`use ::foo` | `ModuleOrUniformRoot::ModuleAndExternPrelude` | a special case in 2015 edition | /// |`use foo` | `ModuleOrUniformRoot::CurrentScope` | - | - pub imported_module: Cell<Option<ModuleOrUniformRoot<'ra>>>, + pub imported_module: CmCell<Option<ModuleOrUniformRoot<'ra>>>, pub vis: Visibility, /// Span of the visibility. @@ -320,7 +320,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && (vis == import_vis || max_vis.get().is_none_or(|max_vis| vis.is_at_least(max_vis, self.tcx))) { - max_vis.set(Some(vis.expect_local())) + // FIXME(batched): Will be fixed in batched import resolution. + max_vis.set_unchecked(Some(vis.expect_local())) } self.arenas.alloc_name_binding(NameBindingData { @@ -349,7 +350,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // because they can be fetched by glob imports from those modules, and bring traits // into scope both directly and through glob imports. let key = BindingKey::new_disambiguated(ident, ns, || { - module.underscore_disambiguator.update(|d| d + 1); + // FIXME(batched): Will be fixed in batched resolution. + module.underscore_disambiguator.update_unchecked(|d| d + 1); module.underscore_disambiguator.get() }); self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| { @@ -482,7 +484,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }; - let Ok(glob_importers) = module.glob_importers.try_borrow_mut() else { + let Ok(glob_importers) = module.glob_importers.try_borrow_mut_unchecked() else { return t; }; @@ -720,9 +722,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { EXPORTED_PRIVATE_DEPENDENCIES, binding_id, binding.span, - BuiltinLintDiag::ReexportPrivateDependency { - kind: binding.res().descr().to_string(), - name: key.ident.name.to_string(), + crate::errors::ReexportPrivateDependency { + name: key.ident.name, + kind: binding.res().descr(), krate: self.tcx.crate_name(reexported_def_id.krate), }, ); @@ -862,7 +864,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }; - import.imported_module.set(Some(module)); + // FIXME(batched): Will be fixed in batched import resolution. + import.imported_module.set_unchecked(Some(module)); let (source, target, bindings, type_ns_only) = match import.kind { ImportKind::Single { source, target, ref bindings, type_ns_only, .. } => { (source, target, bindings, type_ns_only) @@ -937,7 +940,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PendingBinding::Pending } }; - bindings[ns].set(binding); + // FIXME(batched): Will be fixed in batched import resolution. + bindings[ns].set_unchecked(binding); } }); @@ -1508,7 +1512,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Add to module's glob_importers - module.glob_importers.borrow_mut().push(import); + module.glob_importers.borrow_mut_unchecked().push(import); // Ensure that `resolutions` isn't borrowed during `try_define`, // since it might get updated via a glob cycle. @@ -1550,7 +1554,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // reporting conflicts, and reporting unresolved imports. fn finalize_resolutions_in(&mut self, module: Module<'ra>) { // Since import resolution is finished, globs will not define any more names. - *module.globs.borrow_mut() = Vec::new(); + *module.globs.borrow_mut(self) = Vec::new(); let Some(def_id) = module.opt_def_id() else { return }; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 521fef2b9d4..4d4acc60ca8 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -29,7 +29,7 @@ use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::ty::{DelegationFnSig, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; -use rustc_session::lint::{self, BuiltinLintDiag}; +use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::source_map::{Spanned, respan}; use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; @@ -5225,7 +5225,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { lint::builtin::UNUSED_LABELS, *id, *span, - BuiltinLintDiag::UnusedLabel, + errors::UnusedLabel, ); } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 9e3c0938836..8c2ddda7f98 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1942,44 +1942,77 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return true; }; + let update_message = + |this: &mut Self, err: &mut Diag<'_>, source: &PathSource<'_, '_, '_>| { + match source { + // e.g. `if let Enum::TupleVariant(field1, field2) = _` + PathSource::TupleStruct(_, pattern_spans) => { + err.primary_message( + "cannot match against a tuple struct which contains private fields", + ); + + // Use spans of the tuple struct pattern. + Some(Vec::from(*pattern_spans)) + } + // e.g. `let _ = Enum::TupleVariant(field1, field2);` + PathSource::Expr(Some(Expr { + kind: ExprKind::Call(path, args), + span: call_span, + .. + })) => { + err.primary_message( + "cannot initialize a tuple struct which contains private fields", + ); + this.suggest_alternative_construction_methods( + def_id, + err, + path.span, + *call_span, + &args[..], + ); + // Use spans of the tuple struct definition. + this.r + .field_idents(def_id) + .map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>()) + } + _ => None, + } + }; let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); + if let Some(use_span) = self.r.inaccessible_ctor_reexport.get(&span) + && is_accessible + { + err.span_note( + *use_span, + "the type is accessed through this re-export, but the type's constructor \ + is not visible in this import's scope due to private fields", + ); + if is_accessible + && fields + .iter() + .all(|vis| self.r.is_accessible_from(*vis, self.parent_scope.module)) + { + err.span_suggestion_verbose( + span, + "the type can be constructed directly, because its fields are \ + available from the current scope", + // Using `tcx.def_path_str` causes the compiler to hang. + // We don't need to handle foreign crate types because in that case you + // can't access the ctor either way. + format!( + "crate{}", // The method already has leading `::`. + self.r.tcx.def_path(def_id).to_string_no_crate_verbose(), + ), + Applicability::MachineApplicable, + ); + } + update_message(self, err, &source); + } if !is_expected(ctor_def) || is_accessible { return true; } - let field_spans = match source { - // e.g. `if let Enum::TupleVariant(field1, field2) = _` - PathSource::TupleStruct(_, pattern_spans) => { - err.primary_message( - "cannot match against a tuple struct which contains private fields", - ); - - // Use spans of the tuple struct pattern. - Some(Vec::from(pattern_spans)) - } - // e.g. `let _ = Enum::TupleVariant(field1, field2);` - PathSource::Expr(Some(Expr { - kind: ExprKind::Call(path, args), - span: call_span, - .. - })) => { - err.primary_message( - "cannot initialize a tuple struct which contains private fields", - ); - self.suggest_alternative_construction_methods( - def_id, - err, - path.span, - *call_span, - &args[..], - ); - // Use spans of the tuple struct definition. - self.r - .field_idents(def_id) - .map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>()) - } - _ => None, - }; + let field_spans = update_message(self, err, &source); if let Some(spans) = field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len()) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 8b185ce7ef2..b44b1c966a4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -19,6 +19,7 @@ #![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(ptr_as_ref_unchecked)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] #![recursion_limit = "256"] @@ -26,7 +27,7 @@ use std::cell::{Cell, Ref, RefCell}; use std::collections::BTreeSet; -use std::fmt; +use std::fmt::{self}; use std::sync::Arc; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; @@ -95,6 +96,8 @@ pub mod rustdoc; pub use macros::registered_tools_ast; +use crate::ref_mut::{CmCell, CmRefCell}; + rustc_fluent_macro::fluent_messages! { "../messages.ftl" } #[derive(Debug)] @@ -592,22 +595,22 @@ struct ModuleData<'ra> { /// Resolutions in modules from other crates are not populated until accessed. lazy_resolutions: Resolutions<'ra>, /// True if this is a module from other crate that needs to be populated on access. - populate_on_access: Cell<bool>, + populate_on_access: Cell<bool>, // FIXME(parallel): Use an atomic in parallel import resolution /// Used to disambiguate underscore items (`const _: T = ...`) in the module. - underscore_disambiguator: Cell<u32>, + underscore_disambiguator: CmCell<u32>, /// Macro invocations that can expand into items in this module. - unexpanded_invocations: RefCell<FxHashSet<LocalExpnId>>, + unexpanded_invocations: CmRefCell<FxHashSet<LocalExpnId>>, /// Whether `#[no_implicit_prelude]` is active. no_implicit_prelude: bool, - glob_importers: RefCell<Vec<Import<'ra>>>, - globs: RefCell<Vec<Import<'ra>>>, + glob_importers: CmRefCell<Vec<Import<'ra>>>, + globs: CmRefCell<Vec<Import<'ra>>>, /// Used to memoize the traits in this module for faster searches through all traits in scope. traits: - RefCell<Option<Box<[(Macros20NormalizedIdent, NameBinding<'ra>, Option<Module<'ra>>)]>>>, + CmRefCell<Option<Box<[(Macros20NormalizedIdent, NameBinding<'ra>, Option<Module<'ra>>)]>>>, /// Span of the module itself. Used for error reporting. span: Span, @@ -656,12 +659,12 @@ impl<'ra> ModuleData<'ra> { kind, lazy_resolutions: Default::default(), populate_on_access: Cell::new(is_foreign), - underscore_disambiguator: Cell::new(0), + underscore_disambiguator: CmCell::new(0), unexpanded_invocations: Default::default(), no_implicit_prelude, - glob_importers: RefCell::new(Vec::new()), - globs: RefCell::new(Vec::new()), - traits: RefCell::new(None), + glob_importers: CmRefCell::new(Vec::new()), + globs: CmRefCell::new(Vec::new()), + traits: CmRefCell::new(None), span, expansion, self_binding, @@ -696,7 +699,7 @@ impl<'ra> Module<'ra> { /// This modifies `self` in place. The traits will be stored in `self.traits`. fn ensure_traits<'tcx>(self, resolver: &impl AsRef<Resolver<'ra, 'tcx>>) { - let mut traits = self.traits.borrow_mut(); + let mut traits = self.traits.borrow_mut(resolver.as_ref()); if traits.is_none() { let mut collected_traits = Vec::new(); self.for_each_child(resolver, |r, name, ns, binding| { @@ -1164,6 +1167,11 @@ pub struct Resolver<'ra, 'tcx> { /// Crate-local macro expanded `macro_export` referred to by a module-relative path. macro_expanded_macro_export_errors: BTreeSet<(Span, Span)> = BTreeSet::new(), + /// When a type is re-exported that has an inaccessible constructor because it has fields that + /// are inaccessible from the import's scope, we mark that as the type won't be able to be built + /// through the re-export. We use this information to extend the existing diagnostic. + inaccessible_ctor_reexport: FxHashMap<Span, Span>, + arenas: &'ra ResolverArenas<'ra>, dummy_binding: NameBinding<'ra>, builtin_types_bindings: FxHashMap<Symbol, NameBinding<'ra>>, @@ -1592,6 +1600,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { glob_map: Default::default(), used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), + inaccessible_ctor_reexport: Default::default(), arenas, dummy_binding: arenas.new_pub_res_binding(Res::Err, DUMMY_SP, LocalExpnId::ROOT), @@ -1974,6 +1983,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolutions(&self, module: Module<'ra>) -> &'ra Resolutions<'ra> { if module.populate_on_access.get() { + // FIXME(batched): Will be fixed in batched import resolution. module.populate_on_access.set(false); self.build_reduced_graph_external(module); } @@ -2504,9 +2514,20 @@ pub fn provide(providers: &mut Providers) { providers.registered_tools = macros::registered_tools; } +/// A wrapper around `&mut Resolver` that may be mutable or immutable, depending on a conditions. +/// +/// `Cm` stands for "conditionally mutable". +/// +/// Prefer constructing it through [`Resolver::cm`] to ensure correctness. +type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; + mod ref_mut { + use std::cell::{BorrowMutError, Cell, Ref, RefCell, RefMut}; + use std::fmt; use std::ops::Deref; + use crate::Resolver; + /// A wrapper around a mutable reference that conditionally allows mutable access. pub(crate) struct RefOrMut<'a, T> { p: &'a mut T, @@ -2555,11 +2576,86 @@ mod ref_mut { self.p } } -} -/// A wrapper around `&mut Resolver` that may be mutable or immutable, depending on a conditions. -/// -/// `Cm` stands for "conditionally mutable". -/// -/// Prefer constructing it through [`Resolver::cm`] to ensure correctness. -type CmResolver<'r, 'ra, 'tcx> = ref_mut::RefOrMut<'r, Resolver<'ra, 'tcx>>; + /// A wrapper around a [`Cell`] that only allows mutation based on a condition in the resolver. + #[derive(Default)] + pub(crate) struct CmCell<T>(Cell<T>); + + impl<T: Copy + fmt::Debug> fmt::Debug for CmCell<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("CmCell").field(&self.get()).finish() + } + } + + impl<T: Copy> Clone for CmCell<T> { + #[inline] + fn clone(&self) -> CmCell<T> { + CmCell::new(self.get()) + } + } + + impl<T: Copy> CmCell<T> { + pub(crate) const fn get(&self) -> T { + self.0.get() + } + + pub(crate) fn update_unchecked(&self, f: impl FnOnce(T) -> T) + where + T: Copy, + { + let old = self.get(); + self.set_unchecked(f(old)); + } + } + + impl<T> CmCell<T> { + pub(crate) const fn new(value: T) -> CmCell<T> { + CmCell(Cell::new(value)) + } + + pub(crate) fn set_unchecked(&self, val: T) { + self.0.set(val); + } + + pub(crate) fn into_inner(self) -> T { + self.0.into_inner() + } + } + + /// A wrapper around a [`RefCell`] that only allows mutable borrows based on a condition in the resolver. + #[derive(Default)] + pub(crate) struct CmRefCell<T>(RefCell<T>); + + impl<T> CmRefCell<T> { + pub(crate) const fn new(value: T) -> CmRefCell<T> { + CmRefCell(RefCell::new(value)) + } + + #[inline] + #[track_caller] + pub(crate) fn borrow_mut_unchecked(&self) -> RefMut<'_, T> { + self.0.borrow_mut() + } + + #[inline] + #[track_caller] + pub(crate) fn borrow_mut<'ra, 'tcx>(&self, r: &Resolver<'ra, 'tcx>) -> RefMut<'_, T> { + if r.assert_speculative { + panic!("Not allowed to mutably borrow a CmRefCell during speculative resolution"); + } + self.borrow_mut_unchecked() + } + + #[inline] + #[track_caller] + pub(crate) fn try_borrow_mut_unchecked(&self) -> Result<RefMut<'_, T>, BorrowMutError> { + self.0.try_borrow_mut() + } + + #[inline] + #[track_caller] + pub(crate) fn borrow(&self) -> Ref<'_, T> { + self.0.borrow() + } + } +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 5fa87b4cd9b..c50dfd41b51 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -189,7 +189,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); - parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); + parent_scope.module.unexpanded_invocations.borrow_mut(self).remove(&expansion); if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion)) { @@ -979,7 +979,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { LEGACY_DERIVE_HELPERS, node_id, ident.span, - BuiltinLintDiag::LegacyDeriveHelpers(binding.span), + errors::LegacyDeriveHelpers { span: binding.span }, ); } } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index f4a14b36ce5..ec7a4a81a71 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -626,12 +626,10 @@ pub(crate) fn encode_ty<'tcx>( } // Trait types - ty::Dynamic(predicates, region, kind) => { + ty::Dynamic(predicates, region) => { // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as // vendor extended type. - let mut s = String::from(match kind { - ty::Dyn => "u3dynI", - }); + let mut s = String::from("u3dynI"); s.push_str(&encode_predicates(tcx, predicates, dict, options)); s.push_str(&encode_region(*region, dict)); s.push('E'); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index d81fa062e01..82a2a64f230 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -268,7 +268,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc let preds = tcx.mk_poly_existential_predicates_from_iter( iter::once(principal_pred).chain(assoc_preds.into_iter()), ); - Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased) } /// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI @@ -334,7 +334,7 @@ pub(crate) fn transform_instance<'tcx>( ty::List::empty(), )); let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); - let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); + let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased); instance.args = tcx.mk_args_trait(self_ty, List::empty()); } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def { // Transform self into a trait object of the trait that defines the method for virtual @@ -347,7 +347,7 @@ pub(crate) fn transform_instance<'tcx>( // drop_in_place won't have a defining trait, skip the upcast None => instance.args.type_at(0), }; - let ty::Dynamic(preds, lifetime, kind) = upcast_ty.kind() else { + let ty::Dynamic(preds, lifetime) = upcast_ty.kind() else { bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}"); }; let self_ty = if preds.principal().is_some() { @@ -355,7 +355,7 @@ pub(crate) fn transform_instance<'tcx>( tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) })); - Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + Ty::new_dynamic(tcx, filtered_preds, *lifetime) } else { // If there's no principal type, re-encode it as a unit, since we don't know anything // about it. This technically discards the knowledge that it was a type that was made @@ -469,8 +469,7 @@ fn implemented_method<'tcx>( let ancestor = if let Some(impl_id) = tcx.impl_of_assoc(instance.def_id()) { // Implementation in an `impl` block trait_ref = tcx.impl_trait_ref(impl_id)?; - let impl_method = tcx.associated_item(instance.def_id()); - method_id = impl_method.trait_item_def_id?; + method_id = tcx.trait_item_of(instance.def_id())?; trait_method = tcx.associated_item(method_id); trait_id = trait_ref.skip_binder().def_id; impl_id diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 297df7c2c97..d1426ff55fb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -29,7 +29,8 @@ use rustc_span::{ SourceFileHashAlgorithm, Symbol, sym, }; use rustc_target::spec::{ - FramePointer, LinkSelfContainedComponents, LinkerFeatures, SplitDebuginfo, Target, TargetTuple, + FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, + Target, TargetTuple, }; use tracing::debug; @@ -257,6 +258,8 @@ pub enum AutoDiff { LooseTypes, /// Runs Enzyme's aggressive inlining Inline, + /// Disable Type Tree + NoTT, } /// Settings for `-Z instrument-xray` flag. @@ -1192,6 +1195,7 @@ pub struct OutputFilenames { filestem: String, pub single_output_file: Option<OutFileName>, temps_directory: Option<PathBuf>, + explicit_dwo_out_directory: Option<PathBuf>, pub outputs: OutputTypes, } @@ -1224,6 +1228,7 @@ impl OutputFilenames { out_filestem: String, single_output_file: Option<OutFileName>, temps_directory: Option<PathBuf>, + explicit_dwo_out_directory: Option<PathBuf>, extra: String, outputs: OutputTypes, ) -> Self { @@ -1231,6 +1236,7 @@ impl OutputFilenames { out_directory, single_output_file, temps_directory, + explicit_dwo_out_directory, outputs, crate_stem: format!("{out_crate_name}{extra}"), filestem: format!("{out_filestem}{extra}"), @@ -1280,7 +1286,14 @@ impl OutputFilenames { codegen_unit_name: &str, invocation_temp: Option<&str>, ) -> PathBuf { - self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp) + let p = self.temp_path_ext_for_cgu(DWARF_OBJECT_EXT, codegen_unit_name, invocation_temp); + if let Some(dwo_out) = &self.explicit_dwo_out_directory { + let mut o = dwo_out.clone(); + o.push(p.file_name().unwrap()); + o + } else { + p + } } /// Like `temp_path`, but also supports things where there is no corresponding @@ -1509,6 +1522,11 @@ impl Options { pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) } + + #[inline] + pub fn autodiff_enabled(&self) -> bool { + self.unstable_opts.autodiff.contains(&AutoDiff::Enable) + } } impl UnstableOptions { @@ -1615,6 +1633,7 @@ pub struct PacRet { pub struct BranchProtection { pub bti: bool, pub pac_ret: Option<PacRet>, + pub gcs: bool, } pub(crate) const fn default_lib_output() -> CrateType { @@ -2794,6 +2813,12 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M } } + if !unstable_options_enabled && cg.panic == Some(PanicStrategy::ImmediateAbort) { + early_dcx.early_fatal( + "`-Cpanic=immediate-abort` requires `-Zunstable-options` and a nightly compiler", + ) + } + 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/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 8f63ce6f0ae..a72f6201dce 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -125,7 +125,9 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) { None | Some(_), ) => disallow(cfg, "-Z sanitizer=cfi"), (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"), - (sym::panic, Some(sym::abort | sym::unwind)) => disallow(cfg, "-C panic"), + (sym::panic, Some(sym::abort | sym::unwind | sym::immediate_abort)) => { + disallow(cfg, "-C panic") + } (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"), (sym::unix, None) | (sym::windows, None) @@ -203,7 +205,14 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { ins_none!(sym::overflow_checks); } + // We insert a cfg for the name of session's panic strategy. + // Since the ImmediateAbort strategy is new, it also sets cfg(panic="abort"), so that code + // which is trying to detect whether unwinding is enabled by checking for cfg(panic="abort") + // does not need to be updated. ins_sym!(sym::panic, sess.panic_strategy().desc_symbol()); + if sess.panic_strategy() == PanicStrategy::ImmediateAbort { + ins_sym!(sym::panic, PanicStrategy::Abort.desc_symbol()); + } // JUSTIFICATION: before wrapper fn is available #[allow(rustc::bad_opt_access)] @@ -250,11 +259,11 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg { }); let mut has_atomic = false; for (i, align) in [ - (8, layout.i8_align.abi), - (16, layout.i16_align.abi), - (32, layout.i32_align.abi), - (64, layout.i64_align.abi), - (128, layout.i128_align.abi), + (8, layout.i8_align), + (16, layout.i16_align), + (32, layout.i32_align), + (64, layout.i64_align), + (128, layout.i128_align), ] { if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() { if !has_atomic { @@ -374,11 +383,13 @@ impl CheckCfg { ins!(sym::overflow_checks, no_values); - ins!(sym::panic, empty_values).extend(&PanicStrategy::all()); + ins!(sym::panic, empty_values) + .extend(PanicStrategy::ALL.iter().map(PanicStrategy::desc_symbol)); ins!(sym::proc_macro, no_values); - ins!(sym::relocation_model, empty_values).extend(RelocModel::all()); + ins!(sym::relocation_model, empty_values) + .extend(RelocModel::ALL.iter().map(RelocModel::desc_symbol)); let sanitize_values = SanitizerSet::all() .into_iter() diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 69facde6936..6dd90546de1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -792,7 +792,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; - pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`, `NoTT`"; pub(crate) const parse_offload: &str = "a comma separated list of settings: `Enable`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; @@ -802,7 +802,7 @@ mod desc { pub(crate) const parse_threads: &str = parse_number; pub(crate) const parse_time_passes_format: &str = "`text` (default) or `json`"; pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; - pub(crate) const parse_panic_strategy: &str = "either `unwind` or `abort`"; + pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; @@ -866,7 +866,7 @@ mod desc { pub(crate) const parse_polonius: &str = "either no value or `legacy` (the default), or `next`"; pub(crate) const parse_stack_protector: &str = "one of (`none` (default), `basic`, `strong`, or `all`)"; - pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `pac-ret`, followed by a combination of `pc`, `b-key`, or `leaf`"; + pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set)"; pub(crate) const parse_proc_macro_execution_strategy: &str = "one of supported execution strategies (`same-thread`, or `cross-thread`)"; pub(crate) const parse_remap_path_scope: &str = @@ -1165,6 +1165,7 @@ pub mod parse { match v { Some("unwind") => *slot = Some(PanicStrategy::Unwind), Some("abort") => *slot = Some(PanicStrategy::Abort), + Some("immediate-abort") => *slot = Some(PanicStrategy::ImmediateAbort), _ => return false, } true @@ -1174,6 +1175,7 @@ pub mod parse { match v { Some("unwind") => *slot = PanicStrategy::Unwind, Some("abort") => *slot = PanicStrategy::Abort, + Some("immediate-abort") => *slot = PanicStrategy::ImmediateAbort, _ => return false, } true @@ -1479,6 +1481,7 @@ pub mod parse { "PrintPasses" => AutoDiff::PrintPasses, "LooseTypes" => AutoDiff::LooseTypes, "Inline" => AutoDiff::Inline, + "NoTT" => AutoDiff::NoTT, _ => { // FIXME(ZuseZ4): print an error saying which value is not recognized return false; @@ -1903,6 +1906,7 @@ pub mod parse { Some(pac) => pac.pc = true, _ => return false, }, + "gcs" => slot.gcs = true, _ => return false, }; } @@ -2630,6 +2634,8 @@ written to standard error output)"), file which is ignored by the linker `single`: sections which do not require relocation are written into object file but ignored by the linker"), + split_dwarf_out_dir : Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED], + "location for writing split DWARF objects (`.dwo`) if enabled"), split_lto_unit: Option<bool> = (None, parse_opt_bool, [TRACKED], "enable LTO unit splitting (default: no)"), src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3525c7c1d1a..25b46241c52 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -600,6 +600,13 @@ impl Session { /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { + // Autodiff currently requires fat-lto to have access to the llvm-ir of all (indirectly) used functions and types. + // fat-lto is the easiest solution to this requirement, but quite expensive. + // FIXME(autodiff): Make autodiff also work with embed-bc instead of fat-lto. + if self.opts.autodiff_enabled() { + return config::Lto::Fat; + } + // If our target has codegen requirements ignore the command line if self.target.requires_lto { return config::Lto::Fat; @@ -770,9 +777,11 @@ impl Session { // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>` // value, if it is provided, or disable them, if not. self.target.requires_uwtable - || self.opts.cg.force_unwind_tables.unwrap_or( - self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable, - ) + || self + .opts + .cg + .force_unwind_tables + .unwrap_or(self.panic_strategy().unwinds() || self.target.default_uwtable) } /// Returns the number of query threads that should be used for this @@ -1222,7 +1231,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } // KCFI requires panic=abort - if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy() != PanicStrategy::Abort { + if sess.is_sanitizer_kcfi_enabled() && sess.panic_strategy().unwinds() { sess.dcx().emit_err(errors::SanitizerKcfiRequiresPanicAbort); } diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index c32593a6d95..bb2cda77dff 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -81,8 +81,8 @@ cfg_select! { // use `loadu`, which supports unaligned loading. let chunk = unsafe { _mm_loadu_si128(chunk.as_ptr() as *const __m128i) }; - // For character in the chunk, see if its byte value is < 0, which - // indicates that it's part of a UTF-8 char. + // For each character in the chunk, see if its byte value is < 0, + // which indicates that it's part of a UTF-8 char. let multibyte_test = _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)); // Create a bit mask from the comparison results. let multibyte_mask = _mm_movemask_epi8(multibyte_test); @@ -132,8 +132,111 @@ cfg_select! { } } } + target_arch = "loongarch64" => { + fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec<RelativeBytePos>, + multi_byte_chars: &mut Vec<MultiByteChar>, + ) { + use std::arch::is_loongarch_feature_detected; + + if is_loongarch_feature_detected!("lsx") { + unsafe { + analyze_source_file_lsx(src, lines, multi_byte_chars); + } + } else { + analyze_source_file_generic( + src, + src.len(), + RelativeBytePos::from_u32(0), + lines, + multi_byte_chars, + ); + } + } + + /// Checks 16 byte chunks of text at a time. If the chunk contains + /// something other than printable ASCII characters and newlines, the + /// function falls back to the generic implementation. Otherwise it uses + /// LSX intrinsics to quickly find all newlines. + #[target_feature(enable = "lsx")] + unsafe fn analyze_source_file_lsx( + src: &str, + lines: &mut Vec<RelativeBytePos>, + multi_byte_chars: &mut Vec<MultiByteChar>, + ) { + use std::arch::loongarch64::*; + + const CHUNK_SIZE: usize = 16; + + let (chunks, tail) = src.as_bytes().as_chunks::<CHUNK_SIZE>(); + + // This variable keeps track of where we should start decoding a + // chunk. If a multi-byte character spans across chunk boundaries, + // we need to skip that part in the next chunk because we already + // handled it. + let mut intra_chunk_offset = 0; + + for (chunk_index, chunk) in chunks.iter().enumerate() { + // All LSX memory instructions support unaligned access, so using + // vld is fine. + let chunk = unsafe { lsx_vld::<0>(chunk.as_ptr() as *const i8) }; + + // For each character in the chunk, see if its byte value is < 0, + // which indicates that it's part of a UTF-8 char. + let multibyte_mask = lsx_vmskltz_b(chunk); + // Create a bit mask from the comparison results. + let multibyte_mask = lsx_vpickve2gr_w::<0>(multibyte_mask); + + // If the bit mask is all zero, we only have ASCII chars here: + if multibyte_mask == 0 { + assert!(intra_chunk_offset == 0); + + // Check for newlines in the chunk + let newlines_test = lsx_vseqi_b::<{b'\n' as i32}>(chunk); + let newlines_mask = lsx_vmskltz_b(newlines_test); + let mut newlines_mask = lsx_vpickve2gr_w::<0>(newlines_mask); + + let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); + + while newlines_mask != 0 { + let index = newlines_mask.trailing_zeros(); + + lines.push(RelativeBytePos(index) + output_offset); + + // Clear the bit, so we can find the next one. + newlines_mask &= newlines_mask - 1; + } + } else { + // The slow path. + // There are multibyte chars in here, fallback to generic decoding. + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start..], + CHUNK_SIZE - intra_chunk_offset, + RelativeBytePos::from_usize(scan_start), + lines, + multi_byte_chars, + ); + } + } + + // There might still be a tail left to analyze + let tail_start = src.len() - tail.len() + intra_chunk_offset; + if tail_start < src.len() { + analyze_source_file_generic( + &src[tail_start..], + src.len() - tail_start, + RelativeBytePos::from_usize(tail_start), + lines, + multi_byte_chars, + ); + } + } + } _ => { - // The target (or compiler version) does not support SSE2 ... + // The target (or compiler version) does not support vector instructions + // our specialized implementations need (x86 SSE2, loongarch64 LSX)... fn analyze_source_file_dispatch( src: &str, lines: &mut Vec<RelativeBytePos>, diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 19494ffc37e..633cbc3a4ae 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -46,6 +46,8 @@ use crate::symbol::{Symbol, kw, sym}; use crate::{DUMMY_SP, HashStableContext, Span, SpanDecoder, SpanEncoder, with_session_globals}; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". +/// +/// See <https://rustc-dev-guide.rust-lang.org/macro-expansion.html> for more explanation. #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct SyntaxContext(u32); @@ -61,7 +63,10 @@ pub type SyntaxContextKey = (SyntaxContext, ExpnId, Transparency); #[derive(Clone, Copy, Debug)] struct SyntaxContextData { + /// The last macro expansion in the chain. + /// (Here we say the most deeply nested macro expansion is the "outermost" expansion.) outer_expn: ExpnId, + /// Transparency of the last macro expansion outer_transparency: Transparency, parent: SyntaxContext, /// This context, but with all transparent and semi-opaque expansions filtered away. @@ -450,11 +455,13 @@ impl HygieneData { self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque } + /// See [`SyntaxContextData::outer_expn`] #[inline] fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { self.syntax_context_data[ctxt.0 as usize].outer_expn } + /// The last macro expansion and its Transparency #[inline] fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { let data = &self.syntax_context_data[ctxt.0 as usize]; @@ -900,6 +907,7 @@ impl SyntaxContext { HygieneData::with(|data| data.normalize_to_macro_rules(self)) } + /// See [`SyntaxContextData::outer_expn`] #[inline] pub fn outer_expn(self) -> ExpnId { HygieneData::with(|data| data.outer_expn(self)) @@ -912,6 +920,7 @@ impl SyntaxContext { HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone()) } + /// See [`HygieneData::outer_mark`] #[inline] fn outer_mark(self) -> (ExpnId, Transparency) { HygieneData::with(|data| data.outer_mark(self)) @@ -982,19 +991,20 @@ impl Span { #[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] pub struct ExpnData { // --- The part unique to each expansion. - /// The kind of this expansion - macro or compiler desugaring. pub kind: ExpnKind, - /// The expansion that produced this expansion. + /// The expansion that contains the definition of the macro for this expansion. pub parent: ExpnId, - /// The location of the actual macro invocation or syntax sugar , e.g. - /// `let x = foo!();` or `if let Some(y) = x {}` + /// The span of the macro call which produced this expansion. + /// + /// This span will typically have a different `ExpnData` and `call_site`. + /// This recursively traces back through any macro calls which expanded into further + /// macro calls, until the "source call-site" is reached at the root SyntaxContext. + /// For example, if `food!()` expands to `fruit!()` which then expands to `grape`, + /// then the call-site of `grape` is `fruit!()` and the call-site of `fruit!()` + /// is `food!()`. /// - /// This may recursively refer to other macro invocations, e.g., if - /// `foo!()` invoked `bar!()` internally, and there was an - /// expression inside `bar!`; the call_site of the expression in - /// the expansion would point to the `bar!` invocation; that - /// call_site span would have its own ExpnData, with the call_site - /// pointing to the `foo!` invocation. + /// For a desugaring expansion, this is the span of the expression or node that was + /// desugared. pub call_site: Span, /// Used to force two `ExpnData`s to have different `Fingerprint`s. /// Due to macro expansion, it's possible to end up with two `ExpnId`s diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index ae6755f0764..ededbea57e9 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,7 +17,7 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(round_char_boundary))] +#![cfg_attr(target_arch = "loongarch64", feature(stdarch_loongarch))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] @@ -710,8 +710,8 @@ impl Span { if !ctxt.is_root() { ctxt.outer_expn_data().call_site.source_callsite() } else { self } } - /// The `Span` for the tokens in the previous macro expansion from which `self` was generated, - /// if any. + /// Returns the call-site span of the last macro expansion which produced this `Span`. + /// (see [`ExpnData::call_site`]). Returns `None` if this is not an expansion. pub fn parent_callsite(self) -> Option<Span> { let ctxt = self.ctxt(); (!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da..b34a64108e3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -545,6 +545,7 @@ symbols! { attributes, audit_that, augmented_assignments, + auto_cfg, auto_traits, autodiff, autodiff_forward, @@ -628,7 +629,6 @@ symbols! { cfg_emscripten_wasm_eh, cfg_eval, cfg_fmt_debug, - cfg_hide, cfg_overflow_checks, cfg_panic, cfg_relocation_model, @@ -679,6 +679,7 @@ symbols! { cmpxchg16b_target_feature, cmse_nonsecure_entry, coerce_pointee_validated, + coerce_shared, coerce_unsized, cold, cold_path, @@ -794,6 +795,7 @@ symbols! { ctlz, ctlz_nonzero, ctpop, + ctr, cttz, cttz_nonzero, custom_attribute, @@ -938,6 +940,7 @@ symbols! { ermsb_target_feature, exact_div, except, + exception_handling: "exception-handling", exchange_malloc, exclusive_range_pattern, exhaustive_integer_patterns, @@ -1148,6 +1151,7 @@ symbols! { hashset_iter_ty, hexagon_target_feature, hidden, + hide, hint, homogeneous_aggregate, host, @@ -1195,6 +1199,7 @@ symbols! { if_let_rescope, if_while_or_patterns, ignore, + immediate_abort: "immediate-abort", impl_header_lifetime_elision, impl_lint_pass, impl_trait_in_assoc_type, @@ -1333,6 +1338,7 @@ symbols! { loongarch_target_feature, loop_break_value, loop_match, + lr, lt, m68k_target_feature, macro_at_most_once_rep, @@ -1413,6 +1419,7 @@ symbols! { mir_call, mir_cast_ptr_to_ptr, mir_cast_transmute, + mir_cast_unsize, mir_checked, mir_copy_for_deref, mir_debuginfo, @@ -1917,6 +1924,8 @@ symbols! { rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, + rustc_objc_class, + rustc_objc_selector, rustc_object_lifetime_default, rustc_on_unimplemented, rustc_outlives, @@ -1937,6 +1946,7 @@ symbols! { rustc_regions, rustc_reservation_impl, rustc_serialize, + rustc_simd_monomorphize_lane_limit, rustc_skip_during_method_dispatch, rustc_specialization_trait, rustc_std_internal_symbol, @@ -1980,6 +1990,7 @@ symbols! { shl_assign, shorter_tail_lifetimes, should_panic, + show, shr, shr_assign, sig_dfl, diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 76ac82cf95a..3896e06a627 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -110,7 +110,7 @@ impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { | ty::RawPtr(_, _) | ty::FnDef(_, _) | ty::FnPtr(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 1605b4958ba..d24924b424a 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -262,15 +262,16 @@ impl<'tcx> V0SymbolMangler<'tcx> { fn print_pat(&mut self, pat: ty::Pattern<'tcx>) -> Result<(), std::fmt::Error> { Ok(match *pat { ty::PatternKind::Range { start, end } => { - let consts = [start, end]; - for ct in consts { - Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?; - } + self.push("R"); + self.print_const(start)?; + self.print_const(end)?; } ty::PatternKind::Or(patterns) => { + self.push("O"); for pat in patterns { self.print_pat(pat)?; } + self.push("E"); } }) } @@ -498,12 +499,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { } ty::Pat(ty, pat) => { - // HACK: Represent as tuple until we have something better. - // HACK: constants are used in arrays, even if the types don't match. - self.push("T"); + self.push("W"); ty.print(self)?; self.print_pat(pat)?; - self.push("E"); } ty::Array(ty, len) => { @@ -577,10 +575,8 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // FIXME(unsafe_binder): ty::UnsafeBinder(..) => todo!(), - ty::Dynamic(predicates, r, kind) => { - self.push(match kind { - ty::Dyn => "D", - }); + ty::Dynamic(predicates, r) => { + self.push("D"); self.print_dyn_existential(predicates)?; r.print(self)?; } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index e06f881e4b1..0601613567e 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -1260,11 +1260,12 @@ impl InlineAsmClobberAbi { v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, - // cr0-cr1, cr5-cr7, xer + // cr0-cr1, cr5-cr7, ctr, lr, xer cr0, cr1, cr5, cr6, cr7, + ctr, + lr, xer, - // lr and ctr are reserved } }, InlineAsmClobberAbi::S390x => clobbered_regs! { diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index f3934afa6d9..2348a0fd202 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -13,6 +13,8 @@ def_reg_class! { freg, vreg, cr, + ctr, + lr, xer, } } @@ -56,7 +58,7 @@ impl PowerPCInlineAsmRegClass { altivec: VecI8(16), VecI16(8), VecI32(4), VecF32(4); vsx: F32, F64, VecI64(2), VecF64(2); }, - Self::cr | Self::xer => &[], + Self::cr | Self::ctr | Self::lr | Self::xer => &[], } } } @@ -195,6 +197,8 @@ def_regs! { cr5: cr = ["cr5"], cr6: cr = ["cr6"], cr7: cr = ["cr7"], + ctr: ctr = ["ctr"], + lr: lr = ["lr"], xer: xer = ["xer"], #error = ["r1", "1", "sp"] => "the stack pointer cannot be used as an operand for inline asm", @@ -206,10 +210,6 @@ def_regs! { "r30 is used internally by LLVM and cannot be used as an operand for inline asm", #error = ["r31", "31", "fp"] => "the frame pointer cannot be used as an operand for inline asm", - #error = ["lr"] => - "the link register cannot be used as an operand for inline asm", - #error = ["ctr"] => - "the counter register cannot be used as an operand for inline asm", #error = ["vrsave"] => "the vrsave register cannot be used as an operand for inline asm", } @@ -247,6 +247,8 @@ impl PowerPCInlineAsmReg { (v24, "24"), (v25, "25"), (v26, "26"), (v27, "27"), (v28, "28"), (v29, "29"), (v30, "30"), (v31, "31"); (cr, "cr"); (cr0, "0"), (cr1, "1"), (cr2, "2"), (cr3, "3"), (cr4, "4"), (cr5, "5"), (cr6, "6"), (cr7, "7"); + (ctr, "ctr"); + (lr, "lr"); (xer, "xer"); } } diff --git a/compiler/rustc_target/src/callconv/arm.rs b/compiler/rustc_target/src/callconv/arm.rs index 70830fa07b6..abc9a404e2e 100644 --- a/compiler/rustc_target/src/callconv/arm.rs +++ b/compiler/rustc_target/src/callconv/arm.rs @@ -77,7 +77,7 @@ where } } - let align = arg.layout.align.abi.bytes(); + let align = arg.layout.align.bytes(); let total = arg.layout.size; arg.cast_to(Uniform::consecutive(if align <= 4 { Reg::i32() } else { Reg::i64() }, total)); } diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 9213d73e24e..bc3c9601fa3 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -322,7 +322,7 @@ fn classify_arg<'a, Ty, C>( } let total = arg.layout.size; - let align = arg.layout.align.abi.bits(); + let align = arg.layout.align.bits(); // "Scalars wider than 2✕XLEN are passed by reference and are replaced in // the argument list with the address." diff --git a/compiler/rustc_target/src/callconv/mips.rs b/compiler/rustc_target/src/callconv/mips.rs index 48a01da865b..8ffd7bd1778 100644 --- a/compiler/rustc_target/src/callconv/mips.rs +++ b/compiler/rustc_target/src/callconv/mips.rs @@ -24,7 +24,7 @@ where } let dl = cx.data_layout(); let size = arg.layout.size; - let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; + let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 0209838bec1..8386a15933c 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -110,9 +110,9 @@ where // We only care about aligned doubles if let BackendRepr::Scalar(scalar) = field.backend_repr { if scalar.primitive() == Primitive::Float(Float::F64) { - if offset.is_aligned(dl.f64_align.abi) { + if offset.is_aligned(dl.f64_align) { // Insert enough integers to cover [last_offset, offset) - assert!(last_offset.is_aligned(dl.f64_align.abi)); + assert!(last_offset.is_aligned(dl.f64_align)); for _ in 0..((offset - last_offset).bits() / 64) .min((prefix.len() - prefix_index) as u64) { diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 7a7c63c475b..c59af581a1f 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -332,7 +332,7 @@ impl CastTarget { self.prefix .iter() .filter_map(|x| x.map(|reg| reg.align(cx))) - .fold(cx.data_layout().aggregate_align.abi.max(self.rest.align(cx)), |acc, align| { + .fold(cx.data_layout().aggregate_align.max(self.rest.align(cx)), |acc, align| { acc.max(align) }) } diff --git a/compiler/rustc_target/src/callconv/nvptx64.rs b/compiler/rustc_target/src/callconv/nvptx64.rs index 44977de7fcb..dc32dd87a7e 100644 --- a/compiler/rustc_target/src/callconv/nvptx64.rs +++ b/compiler/rustc_target/src/callconv/nvptx64.rs @@ -21,7 +21,7 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { /// the pass mode used for aggregates in arg and ret position fn classify_aggregate<Ty>(arg: &mut ArgAbi<'_, Ty>) { - let align_bytes = arg.layout.align.abi.bytes(); + let align_bytes = arg.layout.align.bytes(); let size = arg.layout.size; let reg = match align_bytes { @@ -60,7 +60,7 @@ where // "`extern \"ptx-kernel\"` doesn't allow passing types other than primitives and structs" // ); - let align_bytes = arg.layout.align.abi.bytes(); + let align_bytes = arg.layout.align.bytes(); let unit = match align_bytes { 1 => Reg::i8(), diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 89ec85e4b66..be1d13816ef 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -89,7 +89,7 @@ where // Aggregates larger than i64 should be padded at the tail to fill out a whole number // of i64s or i128s, depending on the aggregate alignment. Always use an array for // this, even if there is only a single element. - let reg = if arg.layout.align.abi.bytes() > 8 { Reg::i128() } else { Reg::i64() }; + let reg = if arg.layout.align.bytes() > 8 { Reg::i128() } else { Reg::i64() }; arg.cast_to(Uniform::consecutive( reg, size.align_to(Align::from_bytes(reg.size.bytes()).unwrap()), diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index 161e2c1645f..16de3fe070d 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -328,7 +328,7 @@ fn classify_arg<'a, Ty, C>( } let total = arg.layout.size; - let align = arg.layout.align.abi.bits(); + let align = arg.layout.align.bits(); // "Scalars wider than 2✕XLEN are passed by reference and are replaced in // the argument list with the address." diff --git a/compiler/rustc_target/src/callconv/sparc.rs b/compiler/rustc_target/src/callconv/sparc.rs index 48a01da865b..8ffd7bd1778 100644 --- a/compiler/rustc_target/src/callconv/sparc.rs +++ b/compiler/rustc_target/src/callconv/sparc.rs @@ -24,7 +24,7 @@ where } let dl = cx.data_layout(); let size = arg.layout.size; - let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; + let align = arg.layout.align.abi.max(dl.i32_align).min(dl.i64_align); if arg.layout.is_aggregate() { let pad_i32 = !offset.is_aligned(align); diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index ecc9067ced3..62c8ed1dc21 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -29,7 +29,7 @@ where data.has_float = true; - if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset { + if !data.last_offset.is_aligned(dl.f64_align) && data.last_offset < offset { if data.prefix_index == data.prefix.len() { return data; } diff --git a/compiler/rustc_target/src/callconv/xtensa.rs b/compiler/rustc_target/src/callconv/xtensa.rs index a73a70a1a0c..561ee98787d 100644 --- a/compiler/rustc_target/src/callconv/xtensa.rs +++ b/compiler/rustc_target/src/callconv/xtensa.rs @@ -48,7 +48,7 @@ where } let size = arg.layout.size.bits(); - let needed_align = arg.layout.align.abi.bits(); + let needed_align = arg.layout.align.bits(); let mut must_use_stack = false; // Determine the number of GPRs needed to pass the current argument diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index b3fe1fffcce..8c6a77cba8b 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -76,10 +76,10 @@ fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { macro_rules! target_spec_enum { ( $( #[$attr:meta] )* - pub enum $name:ident { + pub enum $Name:ident { $( $( #[$variant_attr:meta] )* - $variant:ident = $string:literal, + $Variant:ident = $string:literal, )* } parse_error_type = $parse_error_type:literal; @@ -87,20 +87,20 @@ macro_rules! target_spec_enum { $( #[$attr] )* #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(schemars::JsonSchema)] - pub enum $name { + pub enum $Name { $( $( #[$variant_attr] )* #[serde(rename = $string)] // for JSON schema generation only - $variant, + $Variant, )* } - impl FromStr for $name { + impl FromStr for $Name { type Err = String; fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(match s { - $( $string => Self::$variant, )* + $( $string => Self::$Variant, )* _ => { let all = [$( concat!("'", $string, "'") ),*].join(", "); return Err(format!("invalid {}: '{s}'. allowed values: {all}", $parse_error_type)); @@ -109,24 +109,25 @@ macro_rules! target_spec_enum { } } - impl $name { + impl $Name { + pub const ALL: &'static [$Name] = &[ $( $Name::$Variant, )* ]; pub fn desc(&self) -> &'static str { match self { - $( Self::$variant => $string, )* + $( Self::$Variant => $string, )* } } } - impl crate::json::ToJson for $name { + impl crate::json::ToJson for $Name { fn to_json(&self) -> crate::json::Json { self.desc().to_json() } } - crate::json::serde_deserialize_from_str!($name); + crate::json::serde_deserialize_from_str!($Name); - impl std::fmt::Display for $name { + impl std::fmt::Display for $Name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.desc()) } diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index be15da7329d..6ab8597a4ec 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -21,6 +21,7 @@ pub(crate) mod linux_uclibc; pub(crate) mod linux_wasm; pub(crate) mod lynxos178; pub(crate) mod managarm_mlibc; +pub(crate) mod motor; pub(crate) mod msvc; pub(crate) mod netbsd; pub(crate) mod nto_qnx; diff --git a/compiler/rustc_target/src/spec/base/motor.rs b/compiler/rustc_target/src/spec/base/motor.rs new file mode 100644 index 00000000000..18485b2cef2 --- /dev/null +++ b/compiler/rustc_target/src/spec/base/motor.rs @@ -0,0 +1,34 @@ +use crate::spec::{ + Cc, FramePointer, LinkerFlavor, Lld, PanicStrategy, StackProbeType, TargetOptions, +}; + +pub(crate) fn opts() -> TargetOptions { + let pre_link_args = TargetOptions::link_args( + LinkerFlavor::Gnu(Cc::No, Lld::No), + &[ + "-e", + "motor_start", + "--no-undefined", + "--error-unresolved-symbols", + "--no-undefined-version", + "-u", + "__rust_abort", + ], + ); + TargetOptions { + os: "motor".into(), + executables: true, + // TLS is false below because if true, the compiler assumes + // we handle TLS at the ELF loading level, which we don't. + // We use "OS level" TLS (see thread/local.rs in stdlib). + has_thread_local: false, + frame_pointer: FramePointer::NonLeaf, + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No), + main_needs_argc_argv: true, + panic_strategy: PanicStrategy::Abort, + pre_link_args, + stack_probes: StackProbeType::Inline, + supports_stack_protector: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/base/wasm.rs b/compiler/rustc_target/src/spec/base/wasm.rs index 88e7af5e669..7ede45766ea 100644 --- a/compiler/rustc_target/src/spec/base/wasm.rs +++ b/compiler/rustc_target/src/spec/base/wasm.rs @@ -81,11 +81,6 @@ pub(crate) fn options() -> TargetOptions { // threaded model which will legalize atomics to normal operations. singlethread: true, - // Symbol visibility takes care of this for the WebAssembly. - // Additionally the only known linker, LLD, doesn't support the script - // arguments just yet - limit_rdylib_exports: false, - // we use the LLD shipped with the Rust toolchain by default linker: Some("rust-lld".into()), linker_flavor: LinkerFlavor::WasmLld(Cc::No), diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs index 4ba11021988..2867428e42f 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs @@ -93,10 +93,7 @@ pub(crate) fn opts() -> TargetOptions { binary_format: BinaryFormat::Coff, allows_weak_linkage: false, pre_link_args, - pre_link_objects: crt_objects::pre_mingw(), - post_link_objects: crt_objects::post_mingw(), pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), - post_link_objects_self_contained: crt_objects::post_mingw_self_contained(), link_self_contained: LinkSelfContainedDefault::InferredForMingw, late_link_args, late_link_args_dynamic, diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index e3b6430a463..2d84e78f255 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -86,6 +86,17 @@ pub(super) fn post_musl_self_contained() -> CrtObjects { pub(super) fn pre_mingw_self_contained() -> CrtObjects { new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt2.o"]), + (LinkOutputKind::DynamicPicExe, &["crt2.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt2.o"]), + (LinkOutputKind::StaticPicExe, &["crt2.o"]), + (LinkOutputKind::DynamicDylib, &["dllcrt2.o"]), + (LinkOutputKind::StaticDylib, &["dllcrt2.o"]), + ]) +} + +pub(super) fn pre_i686_mingw_self_contained() -> CrtObjects { + new(&[ (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]), (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]), (LinkOutputKind::StaticNoPicExe, &["crt2.o", "rsbegin.o"]), @@ -95,15 +106,15 @@ pub(super) fn pre_mingw_self_contained() -> CrtObjects { ]) } -pub(super) fn post_mingw_self_contained() -> CrtObjects { +pub(super) fn post_i686_mingw_self_contained() -> CrtObjects { all("rsend.o") } -pub(super) fn pre_mingw() -> CrtObjects { +pub(super) fn pre_i686_mingw() -> CrtObjects { all("rsbegin.o") } -pub(super) fn post_mingw() -> CrtObjects { +pub(super) fn post_i686_mingw() -> CrtObjects { all("rsend.o") } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 07fb1ce63f7..4a82a8bd888 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -834,6 +834,7 @@ crate::target_spec_enum! { pub enum PanicStrategy { Unwind = "unwind", Abort = "abort", + ImmediateAbort = "immediate-abort", } parse_error_type = "panic strategy"; @@ -852,11 +853,12 @@ impl PanicStrategy { match *self { PanicStrategy::Unwind => sym::unwind, PanicStrategy::Abort => sym::abort, + PanicStrategy::ImmediateAbort => sym::immediate_abort, } } - pub const fn all() -> [Symbol; 2] { - [Self::Abort.desc_symbol(), Self::Unwind.desc_symbol()] + pub fn unwinds(self) -> bool { + matches!(self, PanicStrategy::Unwind) } } @@ -974,18 +976,6 @@ impl RelocModel { RelocModel::RopiRwpi => sym::ropi_rwpi, } } - - pub const fn all() -> [Symbol; 7] { - [ - RelocModel::Static.desc_symbol(), - RelocModel::Pic.desc_symbol(), - RelocModel::Pie.desc_symbol(), - RelocModel::DynamicNoPic.desc_symbol(), - RelocModel::Ropi.desc_symbol(), - RelocModel::Rwpi.desc_symbol(), - RelocModel::RopiRwpi.desc_symbol(), - ] - } } crate::target_spec_enum! { @@ -1658,6 +1648,7 @@ supported_targets! { ("aarch64-unknown-hermit", aarch64_unknown_hermit), ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit), ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("x86_64-unknown-motor", x86_64_unknown_motor), ("x86_64-unikraft-linux-musl", x86_64_unikraft_linux_musl), diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs index d227d63c4a3..0ae7cd7a377 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs @@ -12,7 +12,7 @@ pub(crate) fn target() -> Target { llvm_target: "armebv7r-none-eabi".into(), metadata: TargetMetadata { description: Some("Bare Armv7-R, Big Endian".into()), - tier: Some(2), + tier: Some(3), host_tools: Some(false), std: Some(false), }, diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs index c373afb914e..71ffd8baed5 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs @@ -12,7 +12,7 @@ pub(crate) fn target() -> Target { llvm_target: "armebv7r-none-eabihf".into(), metadata: TargetMetadata { description: Some("Bare Armv7-R, Big Endian, hardfloat".into()), - tier: Some(2), + tier: Some(3), host_tools: Some(false), std: Some(false), }, diff --git a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs index e78f7839974..06dd2629775 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs @@ -34,7 +34,7 @@ pub(crate) fn target() -> Target { description: Some("ARMv7-A Cortex-A9 VEX V5 Brain".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs index e775c8fc524..a0d403bd05e 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs @@ -1,4 +1,6 @@ -use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, TargetMetadata, base}; +use crate::spec::{ + Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, TargetMetadata, base, crt_objects, +}; pub(crate) fn target() -> Target { let mut base = base::windows_gnu::opts(); @@ -15,6 +17,10 @@ pub(crate) fn target() -> Target { &["-m", "i386pe", "--large-address-aware"], ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,--large-address-aware"]); + base.pre_link_objects = crt_objects::pre_i686_mingw(); + base.post_link_objects = crt_objects::post_i686_mingw(); + base.pre_link_objects_self_contained = crt_objects::pre_i686_mingw_self_contained(); + base.post_link_objects_self_contained = crt_objects::post_i686_mingw_self_contained(); Target { llvm_target: "i686-pc-windows-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index d42e097b0fd..38c3c7dfaa1 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -5,8 +5,6 @@ pub(crate) fn target() -> Target { base.cpu = "mips64r2".into(); base.features = "+mips64r2,+xgot".into(); base.max_atomic_width = Some(64); - // FIXME(compiler-team#422): musl targets should be dynamically linked by default. - base.crt_static_default = true; Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64el-unknown-linux-musl".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs new file mode 100644 index 00000000000..0fd43357a76 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_motor.rs @@ -0,0 +1,38 @@ +use crate::spec::{ + CodeModel, LinkSelfContainedDefault, LldFlavor, RelocModel, RelroLevel, Target, base, +}; + +pub(crate) fn target() -> Target { + let mut base = base::motor::opts(); + base.cpu = "x86-64".into(); + base.max_atomic_width = Some(64); + base.code_model = Some(CodeModel::Small); + + // We want fully static relocatable binaries. It was surprisingly + // difficult to make it happen reliably, especially various + // linker-related options below. Mostly trial and error. + base.position_independent_executables = true; + base.relro_level = RelroLevel::Full; + base.static_position_independent_executables = true; + base.relocation_model = RelocModel::Pic; + base.lld_flavor_json = LldFlavor::Ld; + base.link_self_contained = LinkSelfContainedDefault::True; + base.dynamic_linking = false; + base.crt_static_default = true; + base.crt_static_respected = true; + + Target { + llvm_target: "x86_64-unknown-none-elf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("Motor OS".into()), + tier: Some(3), + host_tools: None, + std: None, + }, + pointer_width: 64, + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: base, + } +} diff --git a/compiler/rustc_thread_pool/src/latch.rs b/compiler/rustc_thread_pool/src/latch.rs index 18d654d9f78..58dabaf35c0 100644 --- a/compiler/rustc_thread_pool/src/latch.rs +++ b/compiler/rustc_thread_pool/src/latch.rs @@ -388,13 +388,17 @@ impl Latch for CountLatch { #[inline] unsafe fn set(this: *const Self) { if unsafe { (*this).counter.fetch_sub(1, Ordering::SeqCst) == 1 } { - // NOTE: Once we call `set` on the internal `latch`, + // SAFETY: Once we call `set` on the internal `latch`, // the target may proceed and invalidate `this`! match unsafe { &(*this).kind } { CountLatchKind::Stealing { latch, registry, worker_index } => { let registry = Arc::clone(registry); + let worker_index = *worker_index; + // SAFETY: We don't use any references from `this` after this call. if unsafe { CoreLatch::set(latch) } { - registry.notify_worker_latch_is_set(*worker_index); + // We **must not** access any part of `this` anymore, which + // is why we read and shadowed these fields beforehand. + registry.notify_worker_latch_is_set(worker_index); } } CountLatchKind::Blocking { latch } => unsafe { LockLatch::set(latch) }, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index edab530590b..75283dc4ffa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -4,11 +4,12 @@ use std::path::PathBuf; use rustc_errors::codes::*; use rustc_errors::{Diag, IntoDiagArg}; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; +use rustc_hir::{ + self as hir, Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource, PatKind, +}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; @@ -512,7 +513,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { type_name: ty_to_string(self, ty, def_id), }); } - InferSourceKind::ClosureArg { insert_span, ty } => { + InferSourceKind::ClosureArg { insert_span, ty, .. } => { infer_subdiags.push(SourceKindSubdiag::LetLike { span: insert_span, name: String::new(), @@ -652,6 +653,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }), }; *err.long_ty_path() = long_ty_path; + if let InferSourceKind::ClosureArg { kind: PatKind::Err(_), .. } = kind { + // We will have already emitted an error about this pattern. + err.downgrade_to_delayed_bug(); + } err } } @@ -673,6 +678,7 @@ enum InferSourceKind<'tcx> { ClosureArg { insert_span: Span, ty: Ty<'tcx>, + kind: PatKind<'tcx>, }, GenericArg { insert_span: Span, @@ -1197,6 +1203,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { kind: InferSourceKind::ClosureArg { insert_span: param.pat.span.shrink_to_hi(), ty: param_ty, + kind: param.pat.kind, }, }) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3edc365c886..5b831584176 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -399,7 +399,7 @@ pub struct TraitObjectVisitor(pub FxIndexSet<DefId>); impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { - ty::Dynamic(preds, re, _) if re.is_static() => { + ty::Dynamic(preds, re) if re.is_static() => { if let Some(def_id) = preds.principal_def_id() { self.0.insert(def_id); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index e042ce84955..b0b858aa270 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -292,7 +292,7 @@ impl<T> Trait<T> for X { ); } } - (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) + (ty::Dynamic(t, _), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx .explicit_item_self_bounds(alias.def_id) @@ -314,9 +314,7 @@ impl<T> Trait<T> for X { values.found, values.expected, )); } - (ty::Dynamic(t, _, ty::DynKind::Dyn), _) - if let Some(def_id) = t.principal_def_id() => - { + (ty::Dynamic(t, _), _) if let Some(def_id) = t.principal_def_id() => { let mut has_matching_impl = false; tcx.for_each_relevant_impl(def_id, values.found, |did| { if DeepRejectCtxt::relate_rigid_infer(tcx) @@ -335,9 +333,7 @@ impl<T> Trait<T> for X { )); } } - (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) - if let Some(def_id) = t.principal_def_id() => - { + (_, ty::Dynamic(t, _)) if let Some(def_id) = t.principal_def_id() => { let mut has_matching_impl = false; tcx.for_each_relevant_impl(def_id, values.expected, |did| { if DeepRejectCtxt::relate_rigid_infer(tcx) @@ -489,7 +485,7 @@ impl<T> Trait<T> for X { && let Some(then) = blk.expr && def.is_box() && let boxed_ty = args.type_at(0) - && let ty::Dynamic(t, _, _) = boxed_ty.kind() + && let ty::Dynamic(t, _) = boxed_ty.kind() && let Some(def_id) = t.principal_def_id() && let mut impl_def_ids = vec![] && let _ = @@ -857,11 +853,11 @@ fn foo(&self) -> Self::T { String::new() } && self.infcx.can_eq(param_env, assoc_ty, found) { let msg = match assoc_item.container { - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Trait => { "associated type defaults can't be assumed inside the \ trait defining them" } - ty::AssocItemContainer::Impl => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { "associated type is `default` and may be overridden" } }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 7369134420c..9a8ccea3aca 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -430,7 +430,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut alt_span = None; if let Some(ty) = ty && sub.is_static() - && let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind() + && let ty::Dynamic(preds, _) = ty.kind() && let Some(def_id) = preds.principal_def_id() { for (clause, span) in @@ -571,13 +571,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // but right now it's not really very smart when it comes to implicit `Sized` // predicates and bounds on the trait itself. - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { + let Some(impl_def_id) = self.tcx.trait_impl_of_assoc(impl_item_def_id.to_def_id()) else { return; }; + let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_args = trait_ref .instantiate_identity() // Replace the explicit self type with `Self` for better suggestion rendering diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index 8a67e4ccd45..f54ebd76cab 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -5,7 +5,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, lang_items}; -use rustc_middle::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; +use rustc_middle::ty::{AssocContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, sym}; use tracing::debug; @@ -76,8 +76,9 @@ pub fn call_kind<'tcx>( let parent = tcx.opt_associated_item(method_did).and_then(|assoc| { let container_id = assoc.container_id(tcx); match assoc.container { - AssocItemContainer::Impl => tcx.trait_id_of_impl(container_id), - AssocItemContainer::Trait => Some(container_id), + AssocContainer::InherentImpl => None, + AssocContainer::TraitImpl(_) => tcx.trait_id_of_impl(container_id), + AssocContainer::Trait => Some(container_id), } }); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 149f5e638b1..d485eb7266b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1305,8 +1305,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { if ty.is_structural_eq_shallow(self.tcx) { diag.span_suggestion( - span, - "add `#[derive(ConstParamTy)]` to the struct", + span.shrink_to_lo(), + format!("add `#[derive(ConstParamTy)]` to the {}", def.descr()), "#[derive(ConstParamTy)]\n", Applicability::MachineApplicable, ); @@ -1314,8 +1314,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // FIXME(adt_const_params): We should check there's not already an // overlapping `Eq`/`PartialEq` impl. diag.span_suggestion( - span, - "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", + span.shrink_to_lo(), + format!( + "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the {}", + def.descr() + ), "#[derive(ConstParamTy, PartialEq, Eq)]\n", Applicability::MachineApplicable, ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index f794ff632c5..9052031ce4f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -21,7 +21,7 @@ use rustc_infer::traits::{ }; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::{ErrorGuaranteed, ExpnKind, Span}; +use rustc_span::{DesugaringKind, ErrorGuaranteed, ExpnKind, Span}; use tracing::{info, instrument}; pub use self::overflow::*; @@ -154,9 +154,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .collect(); - // Ensure `T: Sized`, `T: MetaSized`, `T: PointeeSized` and `T: WF` obligations come last. + // Ensure `T: Sized`, `T: MetaSized`, `T: PointeeSized` and `T: WF` obligations come last, + // and `Subtype` obligations from `FormatLiteral` desugarings come first. // This lets us display diagnostics with more relevant type information and hide redundant // E0282 errors. + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] + enum ErrorSortKey { + SubtypeFormat(usize, usize), + OtherKind, + SizedTrait, + MetaSizedTrait, + PointeeSizedTrait, + Coerce, + WellFormed, + } errors.sort_by_key(|e| { let maybe_sizedness_did = match e.obligation.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred.def_id()), @@ -165,12 +176,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; match e.obligation.predicate.kind().skip_binder() { - _ if maybe_sizedness_did == self.tcx.lang_items().sized_trait() => 1, - _ if maybe_sizedness_did == self.tcx.lang_items().meta_sized_trait() => 2, - _ if maybe_sizedness_did == self.tcx.lang_items().pointee_sized_trait() => 3, - ty::PredicateKind::Coerce(_) => 4, - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 5, - _ => 0, + ty::PredicateKind::Subtype(_) + if matches!( + e.obligation.cause.span.desugaring_kind(), + Some(DesugaringKind::FormatLiteral { .. }) + ) => + { + let (_, row, col, ..) = + self.tcx.sess.source_map().span_to_location_info(e.obligation.cause.span); + ErrorSortKey::SubtypeFormat(row, col) + } + _ if maybe_sizedness_did == self.tcx.lang_items().sized_trait() => { + ErrorSortKey::SizedTrait + } + _ if maybe_sizedness_did == self.tcx.lang_items().meta_sized_trait() => { + ErrorSortKey::MetaSizedTrait + } + _ if maybe_sizedness_did == self.tcx.lang_items().pointee_sized_trait() => { + ErrorSortKey::PointeeSizedTrait + } + ty::PredicateKind::Coerce(_) => ErrorSortKey::Coerce, + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => { + ErrorSortKey::WellFormed + } + _ => ErrorSortKey::OtherKind, } }); @@ -554,7 +583,7 @@ fn attempt_dyn_to_enum_suggestion( // defaults to assuming that things are *not* sized, whereas we want to // fall back to assuming that things may be sized. match impl_type.kind() { - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::DynKind::Dyn) => { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => { return None; } _ => {} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index bb5c6469f34..00c123981e1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -237,7 +237,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } } - if let ty::Dynamic(traits, _, _) = self_ty.kind() { + if let ty::Dynamic(traits, _) = self_ty.kind() { for t in traits.iter() { if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { self_types.push(self.tcx.def_path_str(trait_ref.def_id)); 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 cb84d583e6e..37e622102e7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) | hir::Node::ImplItem(hir::ImplItem { generics, - trait_item_def_id: None, + impl_kind: hir::ImplItemImplKind::Inherent { .. }, kind: hir::ImplItemKind::Fn(..), .. }) if finder.can_suggest_bound(generics) => { @@ -1131,7 +1131,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }, ) } - ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { + ty::Dynamic(data, _) => data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) // for existential projection, args are shifted over by 1 @@ -1520,7 +1520,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; }; - let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { + let ty::Dynamic(predicates, _) = object_ty.kind() else { return; }; let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty); @@ -1883,7 +1883,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::SizedReturnType = obligation.cause.code() else { return false; }; - let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else { + let ty::Dynamic(_, _) = trait_pred.self_ty().skip_binder().kind() else { return false; }; diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 4c50c44b841..cd076d1cb69 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -9,7 +9,7 @@ use rustc_middle::infer::canonical::{ Canonical, CanonicalQueryInput, CanonicalQueryResponse, QueryResponse, }; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast}; +use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, Upcast}; use rustc_span::DUMMY_SP; use tracing::instrument; @@ -31,19 +31,7 @@ impl<'tcx> InferCtxt<'tcx> { fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let ty = self.resolve_vars_if_possible(ty); - - // FIXME(#132279): This should be removed as it causes us to incorrectly - // handle opaques in their defining scope, and stalled coroutines. - if !self.next_trait_solver() && !(param_env, ty).has_infer() && !ty.has_coroutines() { - return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); - } - let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, DUMMY_SP); - - // This can get called from typeck (by euv), and `moves_by_default` - // rightly refuses to work with inference variables, but - // moves_by_default has a cache, which we want to use in other - // cases. traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 575e0472e0e..bff4f6ce3fc 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -204,7 +204,7 @@ where // // Only goals proven via the trait solver should be region dependent. Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.obligations.register(obligation, None); } } @@ -258,7 +258,7 @@ where infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); } } - Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on), } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index e31d1052d16..eef0ddcbf59 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -95,15 +95,17 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( root_obligation.cause.span, None, ) { - Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } + Ok(GoalEvaluation { + certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), Ok(GoalEvaluation { certainty: - Certainty::Maybe(MaybeCause::Overflow { - suggest_increasing_limit, - keep_constraints: _, - }), + Certainty::Maybe { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }, .. }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, @@ -266,7 +268,8 @@ impl<'tcx> BestObligation<'tcx> { ); // Skip nested goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, nested_goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + | (false, Err(_)) => {} _ => continue, } @@ -407,7 +410,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { let tcx = goal.infcx().tcx; // Skip goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { + } _ => return ControlFlow::Continue(()), } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 342d7121fc3..c010add0fc5 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -18,9 +18,9 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; use rustc_middle::{bug, ty}; +use rustc_next_trait_solver::canonical::instantiate_canonical_state; use rustc_next_trait_solver::resolve::eager_resolve_vars; -use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; -use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _}; +use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _, inspect}; use rustc_span::Span; use tracing::instrument; @@ -332,7 +332,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { assert_matches!( shallow_certainty.replace(c), - None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) ); } inspect::ProbeStep::NestedProbe(ref probe) => { diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index fb1adc2fd2a..8d01c880f8c 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -62,7 +62,7 @@ impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) { return ControlFlow::Break(Ok(None)); } @@ -95,7 +95,7 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( ) -> bool { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + if matches!(other.result().unwrap(), Certainty::Maybe { .. }) { return false; } @@ -143,13 +143,13 @@ fn to_selection<'tcx>( span: Span, cand: inspect::InspectCandidate<'_, 'tcx>, ) -> Option<Selection<'tcx>> { - if let Certainty::Maybe(..) = cand.shallow_certainty() { + if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } let nested = match cand.result().expect("expected positive result") { Certainty::Yes => thin_vec![], - Certainty::Maybe(_) => cand + Certainty::Maybe { .. } => cand .instantiate_nested_goals(span) .into_iter() .map(|nested| { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 39afd77a8b6..8e8c7dd7c9d 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -682,7 +682,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { // was irrelevant. match goal.result() { Ok(Certainty::Yes) | Err(NoSolution) => return, - Ok(Certainty::Maybe(_)) => {} + Ok(Certainty::Maybe { .. }) => {} } // For bound predicates we simply call `infcx.enter_forall` diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index bcd11d6918d..3260dd712b9 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -426,6 +426,9 @@ fn virtual_call_violations_for_method<'tcx>( if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { errors.push(code); } + if sig.skip_binder().c_variadic { + errors.push(MethodViolationCode::CVariadic); + } // We can't monomorphize things like `fn foo<A>(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 393f458bea2..4c25882daa9 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -1,15 +1,14 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. -use std::assert_matches::assert_matches; - use hir::LangItem; use rustc_ast::Mutability; use rustc_hir as hir; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_span::sym; use crate::regions::InferCtxtRegionExt; -use crate::traits::{self, FulfillmentError, ObligationCause}; +use crate::traits::{self, FulfillmentError, Obligation, ObligationCause}; pub enum CopyImplementationError<'tcx> { InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), @@ -98,10 +97,9 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, - lang_item: LangItem, parent_cause: ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>> { - assert_matches!(lang_item, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy); + let mut need_unstable_feature_bound = false; let inner_tys: Vec<_> = match *self_type.kind() { // Trivially okay as these types are all: @@ -112,18 +110,14 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( // Handle types gated under `feature(unsized_const_params)` // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references - ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) - if lang_item == LangItem::UnsizedConstParamTy => - { + ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) => { + need_unstable_feature_bound = true; vec![inner_ty] } - ty::Str if lang_item == LangItem::UnsizedConstParamTy => { + ty::Str => { + need_unstable_feature_bound = true; vec![Ty::new_slice(tcx, tcx.types.u8)] } - ty::Str | ty::Slice(..) | ty::Ref(_, _, Mutability::Not) => { - return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); - } - ty::Array(inner_ty, _) => vec![inner_ty], // `str` morally acts like a newtype around `[u8]` @@ -137,7 +131,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( adt, args, parent_cause.clone(), - lang_item, + LangItem::ConstParamTy, ) .map_err(ConstParamTyImplementationError::InfrigingFields)?; @@ -153,11 +147,25 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); + // Make sure impls certain types are gated with #[unstable_feature_bound(unsized_const_params)] + if need_unstable_feature_bound { + ocx.register_obligation(Obligation::new( + tcx, + parent_cause.clone(), + param_env, + ty::ClauseKind::UnstableFeature(sym::unsized_const_params), + )); + + if !ocx.select_all_or_error().is_empty() { + return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); + } + } + ocx.register_bound( parent_cause.clone(), param_env, inner_ty, - tcx.require_lang_item(lang_item, parent_cause.span), + tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span), ); let errors = ocx.select_all_or_error(); diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 884d53732fe..042d6def84c 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1057,6 +1057,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( Some(LangItem::PointeeTrait) => { let tail = selcx.tcx().struct_tail_raw( self_ty, + &obligation.cause, |ty| { // We throw away any obligations we get from this, since we normalize // and confirm these obligations once again during confirmation diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 9e1a2a3e7d2..34e0176d213 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,8 +1,11 @@ +use rustc_infer::traits::solve::Goal; use rustc_macros::extension; -use rustc_middle::span_bug; +use rustc_middle::{span_bug, ty}; +use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use crate::infer::InferCtxt; use crate::infer::canonical::OriginalQueryValues; +use crate::solve::SolverDelegate; use crate::traits::{ EvaluationResult, ObligationCtxt, OverflowError, PredicateObligation, SelectionContext, }; @@ -15,6 +18,27 @@ impl<'tcx> InferCtxt<'tcx> { self.evaluate_obligation_no_overflow(obligation).may_apply() } + /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank) + /// for more details. + fn predicate_may_hold_opaque_types_jank(&self, obligation: &PredicateObligation<'tcx>) -> bool { + if self.next_trait_solver() { + self.goal_may_hold_opaque_types_jank(Goal::new( + self.tcx, + obligation.param_env, + obligation.predicate, + )) + } else { + self.predicate_may_hold(obligation) + } + } + + /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank) + /// for more details. + fn goal_may_hold_opaque_types_jank(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> bool { + assert!(self.next_trait_solver()); + <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(goal) + } + /// Evaluates whether the predicate can be satisfied in the given /// `ParamEnv`, and returns `false` if not certain. However, this is /// not entirely accurate if inference variables are involved. diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 6ce68507d65..a96cb738b81 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -100,9 +100,9 @@ where } else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) { Err(guar) } else { - Err(infcx - .dcx() - .delayed_bug(format!("errors selecting obligation during MIR typeck: {errors:?}"))) + Err(infcx.dcx().delayed_bug(format!( + "errors selecting obligation during MIR typeck: {name} {root_def_id:?} {errors:?}" + ))) } })?; 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 62795c8a3a6..60f1fcb26c0 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -669,7 +669,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // These may potentially implement `FnPtr` ty::Placeholder(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Alias(_, _) | ty::Infer(_) | ty::Param(..) @@ -991,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(a_data, a_region, ty::Dyn), &ty::Dynamic(b_data, b_region, ty::Dyn)) => { + (&ty::Dynamic(a_data, a_region), &ty::Dynamic(b_data, b_region)) => { // Upcast coercions permit several things: // // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` @@ -1054,7 +1054,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `Trait` - (_, &ty::Dynamic(_, _, ty::Dyn)) => { + (_, &ty::Dynamic(_, _)) => { candidates.vec.push(BuiltinUnsizeCandidate); } @@ -1327,7 +1327,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Pat(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 488094b15ac..7ad65a1df8e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1023,10 +1023,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let a_ty = self.infcx.shallow_resolve(predicate.self_ty()); let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1)); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + let ty::Dynamic(a_data, a_region) = *a_ty.kind() else { bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + let ty::Dynamic(b_data, b_region) = *b_ty.kind() else { bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") }; @@ -1062,10 +1062,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?source, ?target, "confirm_builtin_unsize_candidate"); Ok(match (source.kind(), target.kind()) { - // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). - (&ty::Dynamic(data_a, r_a, dyn_a), &ty::Dynamic(data_b, r_b, dyn_b)) - if dyn_a == dyn_b => - { + // `dyn Trait + Kx + 'a` -> `dyn Trait + Ky + 'b` (auto traits and lifetime subtyping). + (&ty::Dynamic(data_a, r_a), &ty::Dynamic(data_b, r_b)) => { // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let existential_predicates = if data_b.principal().is_some() { @@ -1098,7 +1096,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map(ty::Binder::dummy), ) }; - let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a); + let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -1122,7 +1120,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `dyn Trait` - (_, &ty::Dynamic(data, r, ty::Dyn)) => { + (_, &ty::Dynamic(data, r)) => { let mut object_dids = data.auto_traits().chain(data.principal_def_id()); if let Some(did) = object_dids.find(|did| !tcx.is_dyn_compatible(*did)) { return Err(SelectionError::TraitDynIncompatible(did)); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1dd31990ab7..fb4f28412d4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2359,7 +2359,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { if self.infcx.can_define_opaque_ty(def_id) { unreachable!() } else { - // We can resolve the `impl Trait` to its concrete type, + // We can resolve the opaque type to its hidden type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. match self.tcx().type_of_opaque(def_id) { diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 19eb85506b6..4d0465777dd 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -387,7 +387,7 @@ pub(crate) fn assoc_def( if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { // Ensure that the impl is constrained, otherwise projection may give us // bad unconstrained infer vars. - if assoc_item.item.container == ty::AssocItemContainer::Impl + if let ty::AssocContainer::TraitImpl(_) = assoc_item.item.container && let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local() { tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 7e8a41457d4..584c8e2a27c 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -317,7 +317,7 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe "vtable trait ref should be normalized" ); - let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { + let ty::Dynamic(source, _) = *key.self_ty().kind() else { bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( @@ -384,13 +384,13 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let (source, target) = key; // If the target principal is `None`, we can just return `None`. - let ty::Dynamic(target_data, _, _) = *target.kind() else { + let ty::Dynamic(target_data, _) = *target.kind() else { bug!(); }; let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?); // Given that we have a target principal, it is a bug for there not to be a source principal. - let ty::Dynamic(source_data, _, _) = *source.kind() else { + let ty::Dynamic(source_data, _) = *source.kind() else { bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index adce9850b59..45cbb56b1c2 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -915,7 +915,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> { // We recurse into the binder below. } - ty::Dynamic(data, r, _) => { + ty::Dynamic(data, r) => { // WfObject // // Here, we defer WF checking due to higher-ranked diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index d7ea26a2ab1..7f626e8c4e8 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -279,6 +279,7 @@ pub(crate) mod rustc { LayoutError::Unknown(..) | LayoutError::ReferencesError(..) | LayoutError::TooGeneric(..) + | LayoutError::InvalidSimd { .. } | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, LayoutError::Cycle(err) => Self::TypeError(*err), @@ -360,7 +361,7 @@ pub(crate) mod rustc { ty::Ref(region, ty, mutability) => { let layout = layout_of(cx, *ty)?; - let referent_align = layout.align.abi.bytes_usize(); + let referent_align = layout.align.bytes_usize(); let referent_size = layout.size.bytes_usize(); Ok(Tree::Ref(Reference { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index e9629e31482..84f52e7fc9d 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -2,7 +2,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, ItemKind}; +use rustc_hir::{self as hir, ImplItemImplKind, ItemKind}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -63,7 +63,7 @@ fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems { fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> DefIdMap<DefId> { tcx.associated_items(impl_id) .in_definition_order() - .filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id))) + .filter_map(|item| item.trait_item_def_id().map(|trait_item| (trait_item, item.def_id))) .collect() } @@ -97,12 +97,7 @@ fn associated_item_from_trait_item( } }; - ty::AssocItem { - kind, - def_id: owner_id.to_def_id(), - trait_item_def_id: Some(owner_id.to_def_id()), - container: ty::AssocItemContainer::Trait, - } + ty::AssocItem { kind, def_id: owner_id.to_def_id(), container: ty::AssocContainer::Trait } } fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) -> ty::AssocItem { @@ -118,12 +113,13 @@ fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_> } }; - ty::AssocItem { - kind, - def_id: owner_id.to_def_id(), - trait_item_def_id: impl_item.trait_item_def_id, - container: ty::AssocItemContainer::Impl, - } + let container = match impl_item.impl_kind { + ImplItemImplKind::Inherent { .. } => ty::AssocContainer::InherentImpl, + ImplItemImplKind::Trait { trait_item_def_id, .. } => { + ty::AssocContainer::TraitImpl(trait_item_def_id) + } + }; + ty::AssocItem { kind, def_id: owner_id.to_def_id(), container } } struct RPITVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -190,7 +186,10 @@ fn associated_types_for_impl_traits_in_trait_or_impl<'tcx>( } let did = item.owner_id.def_id.to_def_id(); let item = tcx.hir_impl_item(*item); - let Some(trait_item_def_id) = item.trait_item_def_id else { + let ImplItemImplKind::Trait { + trait_item_def_id: Ok(trait_item_def_id), .. + } = item.impl_kind + else { return Some((did, vec![])); }; let iter = in_trait_def[&trait_item_def_id].iter().map(|&id| { @@ -256,8 +255,7 @@ fn associated_type_for_impl_trait_in_trait( }), }, def_id, - trait_item_def_id: None, - container: ty::AssocItemContainer::Trait, + container: ty::AssocContainer::Trait, }); // Copy visility of the containing function. @@ -322,8 +320,7 @@ fn associated_type_for_impl_trait_in_impl( }), }, def_id, - trait_item_def_id: Some(trait_assoc_def_id), - container: ty::AssocItemContainer::Impl, + container: ty::AssocContainer::TraitImpl(Ok(trait_assoc_def_id)), }); // Copy visility of the containing function. diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index 0298e7e0e95..f92c405242c 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -79,19 +79,6 @@ pub(crate) struct UnexpectedFnPtrAssociatedItem { } #[derive(Diagnostic)] -#[diag(ty_utils_zero_length_simd_type)] -pub(crate) struct ZeroLengthSimdType<'tcx> { - pub ty: Ty<'tcx>, -} - -#[derive(Diagnostic)] -#[diag(ty_utils_oversized_simd_type)] -pub(crate) struct OversizedSimdType<'tcx> { - pub ty: Ty<'tcx>, - pub max_lanes: u64, -} - -#[derive(Diagnostic)] #[diag(ty_utils_non_primitive_simd_type)] pub(crate) struct NonPrimitiveSimdType<'tcx> { pub ty: Ty<'tcx>, diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index cdfb93c4e7d..543f6a3ccf7 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -107,7 +107,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' // the assumed wf types of the trait's RPITIT GAT. ty::ImplTraitInTraitData::Impl { .. } => { let impl_def_id = tcx.local_parent(def_id); - let rpitit_def_id = tcx.associated_item(def_id).trait_item_def_id.unwrap(); + let rpitit_def_id = tcx.trait_item_of(def_id).unwrap(); let args = ty::GenericArgs::identity_for_item(tcx, def_id).rebase_onto( tcx, impl_def_id.to_def_id(), diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 79f7e228e2a..317d101dafe 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -7,11 +7,14 @@ use rustc_abi::{ VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::query::Providers; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::layout::{ - FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, + FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, SimdLayoutError, TyAndLayout, }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ @@ -22,7 +25,7 @@ use rustc_span::{Symbol, sym}; use tracing::{debug, instrument}; use {rustc_abi as abi, rustc_hir as hir}; -use crate::errors::{NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType}; +use crate::errors::NonPrimitiveSimdType; mod invariant; @@ -120,11 +123,11 @@ fn map_error<'tcx>( } LayoutCalculatorError::ZeroLengthSimdType => { // Can't be caught in typeck if the array length is generic. - cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty }) + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } } LayoutCalculatorError::OversizedSimdType { max_lanes } => { // Can't be caught in typeck if the array length is generic. - cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes }) + LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } } LayoutCalculatorError::NonPrimitiveSimdType(field) => { // This error isn't caught in typeck, e.g., if @@ -390,30 +393,31 @@ fn layout_of_uncached<'tcx>( let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { let pointee_metadata = Ty::new_projection(tcx, metadata_def_id, [pointee]); - let metadata_ty = - match tcx.try_normalize_erasing_regions(cx.typing_env, pointee_metadata) { - Ok(metadata_ty) => metadata_ty, - Err(mut err) => { - // Usually `<Ty as Pointee>::Metadata` can't be normalized because - // its struct tail cannot be normalized either, so try to get a - // more descriptive layout error here, which will lead to less confusing - // diagnostics. - // - // We use the raw struct tail function here to get the first tail - // that is an alias, which is likely the cause of the normalization - // error. - match tcx.try_normalize_erasing_regions( - cx.typing_env, - tcx.struct_tail_raw(pointee, |ty| ty, || {}), - ) { - Ok(_) => {} - Err(better_err) => { - err = better_err; - } + let metadata_ty = match tcx + .try_normalize_erasing_regions(cx.typing_env, pointee_metadata) + { + Ok(metadata_ty) => metadata_ty, + Err(mut err) => { + // Usually `<Ty as Pointee>::Metadata` can't be normalized because + // its struct tail cannot be normalized either, so try to get a + // more descriptive layout error here, which will lead to less confusing + // diagnostics. + // + // We use the raw struct tail function here to get the first tail + // that is an alias, which is likely the cause of the normalization + // error. + match tcx.try_normalize_erasing_regions( + cx.typing_env, + tcx.struct_tail_raw(pointee, &ObligationCause::dummy(), |ty| ty, || {}), + ) { + Ok(_) => {} + Err(better_err) => { + err = better_err; } - return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); } - }; + return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); + } + }; let metadata_layout = cx.layout_of(metadata_ty)?; // If the metadata is a 1-zst, then the pointer is thin. @@ -476,7 +480,7 @@ fn layout_of_uncached<'tcx>( } // Odd unit types. - ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { + ty::FnDef(..) | ty::Dynamic(_, _) | ty::Foreign(..) => { let sized = matches!(ty.kind(), ty::FnDef(..)); tcx.mk_layout(LayoutData::unit(cx, sized)) } @@ -561,6 +565,22 @@ fn layout_of_uncached<'tcx>( let e_ly = cx.layout_of(e_ty)?; + // Check for the rustc_simd_monomorphize_lane_limit attribute and check the lane limit + if let Some(limit) = find_attr!( + tcx.get_all_attrs(def.did()), + AttributeKind::RustcSimdMonomorphizeLaneLimit(limit) => limit + ) { + if !limit.value_within_limit(e_len as usize) { + return Err(map_error( + &cx, + ty, + rustc_abi::LayoutCalculatorError::OversizedSimdType { + max_lanes: limit.0 as u64, + }, + )); + } + } + map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))? } @@ -775,7 +795,7 @@ fn variant_info_for_adt<'tcx>( name, offset: offset.bytes(), size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), + align: field_layout.align.bytes(), type_name: None, } }) @@ -784,7 +804,7 @@ fn variant_info_for_adt<'tcx>( VariantInfo { name: n, kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, - align: layout.align.abi.bytes(), + align: layout.align.bytes(), size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, fields: field_info, } @@ -857,7 +877,7 @@ fn variant_info_for_coroutine<'tcx>( name: *name, offset: offset.bytes(), size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), + align: field_layout.align.bytes(), type_name: None, } }) @@ -885,7 +905,7 @@ fn variant_info_for_coroutine<'tcx>( }), offset: offset.bytes(), size: field_layout.size.bytes(), - align: field_layout.align.abi.bytes(), + align: field_layout.align.bytes(), // Include the type name if there is no field name, or if the name is the // __awaitee placeholder symbol which means a child future being `.await`ed. type_name: (field_name.is_none() || field_name == Some(sym::__awaitee)) @@ -926,7 +946,7 @@ fn variant_info_for_coroutine<'tcx>( name: Some(Symbol::intern(&ty::CoroutineArgs::variant_name(variant_idx))), kind: SizeKind::Exact, size: variant_size.bytes(), - align: variant_layout.align.abi.bytes(), + align: variant_layout.align.bytes(), fields, } }) diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 1311ee31182..b768269215f 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayout<'tcx>) { let tcx = cx.tcx(); - if !layout.size.bytes().is_multiple_of(layout.align.abi.bytes()) { + if !layout.size.bytes().is_multiple_of(layout.align.bytes()) { bug!("size is not a multiple of align, in the following layout:\n{layout:#?}"); } if layout.size.bytes() >= tcx.data_layout.obj_size_bound() { @@ -300,8 +300,8 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou if variant.align.abi > layout.align.abi { bug!( "Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}", - layout.align.abi.bytes(), - variant.align.abi.bytes(), + layout.align.bytes(), + variant.align.bytes(), ) } // Skip empty variants. diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 4a7263d0ccd..eb3236d3006 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -245,7 +245,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> { for &assoc in self.tcx.associated_items(parent).in_definition_order() { trace!(?assoc); - if assoc.trait_item_def_id != Some(alias_ty.def_id) { + if assoc.expect_trait_impl() != Ok(alias_ty.def_id) { continue; } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index b22c326b9f2..e91e5055e90 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -40,7 +40,7 @@ fn sizedness_constraint_for_ty<'tcx>( | ty::CoroutineWitness(..) | ty::Never => None, - ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + ty::Str | ty::Slice(..) | ty::Dynamic(_, _) => match sizedness { // Never `Sized` SizedTraitKind::Sized => Some(ty), // Always `MetaSized` @@ -88,7 +88,10 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { }), .. }) - | hir::Node::ImplItem(hir::ImplItem { defaultness, .. }) + | hir::Node::ImplItem(hir::ImplItem { + impl_kind: hir::ImplItemImplKind::Trait { defaultness, .. }, + .. + }) | hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness, node => { bug!("`defaultness` called on {:?}", node); @@ -165,7 +168,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { if tcx.def_kind(def_id) == DefKind::AssocFn && let assoc_item = tcx.associated_item(def_id) - && assoc_item.container == ty::AssocItemContainer::Trait + && assoc_item.container == ty::AssocContainer::Trait && assoc_item.defaultness(tcx).has_value() { let sig = tcx.fn_sig(def_id).instantiate_identity(); @@ -350,6 +353,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) let tail = tcx.struct_tail_raw( tcx.type_of(impl_def_id).instantiate_identity(), + &cause, |ty| { ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| { Ty::new_error_with_message( @@ -363,7 +367,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) ); match tail.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, + ty::Dynamic(_, _) | ty::Slice(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 23b7f55fbbe..24704c5bb53 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -288,7 +288,7 @@ impl<I: Interner> FlagComputation<I> { self.add_alias_ty(data); } - ty::Dynamic(obj, r, _) => { + ty::Dynamic(obj, r) => { for predicate in obj.iter() { self.bound_computation(predicate, |computation, predicate| match predicate { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 56962b4597b..feafcee7bad 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -6,7 +6,7 @@ use crate::fold::TypeFoldable; use crate::inherent::*; use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; -use crate::{self as ty, Interner}; +use crate::{self as ty, Interner, TyVid}; /// The current typing mode of an inference context. We unfortunately have some /// slightly different typing rules depending on the current context. See the @@ -80,7 +80,7 @@ pub enum TypingMode<I: Interner> { /// the old solver as well. PostBorrowckAnalysis { defined_opaque_types: I::LocalDefIds }, /// After analysis, mostly during codegen and MIR optimizations, we're able to - /// reveal all opaque types. As the concrete type should *never* be observable + /// reveal all opaque types. As the hidden type should *never* be observable /// directly by the user, this should not be used by checks which may expose /// such details to the user. /// @@ -271,6 +271,7 @@ pub trait InferCtxtLike: Sized { &self, prev_entries: Self::OpaqueTypeStorageEntries, ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>; + fn opaques_with_sub_unified_hidden_type(&self, ty: TyVid) -> Vec<ty::AliasTy<Self::Interner>>; fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index ecfc05a6e20..b5b552dbaec 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -76,12 +76,7 @@ pub trait Ty<I: Interner<Ty = Self>>: fn new_foreign(interner: I, def_id: I::ForeignId) -> Self; - fn new_dynamic( - interner: I, - preds: I::BoundExistentialPredicates, - region: I::Region, - kind: ty::DynKind, - ) -> Self; + fn new_dynamic(interner: I, preds: I::BoundExistentialPredicates, region: I::Region) -> Self; fn new_coroutine(interner: I, def_id: I::CoroutineId, args: I::GenericArgs) -> Self; @@ -167,7 +162,7 @@ pub trait Ty<I: Interner<Ty = Self>>: fn is_guaranteed_unsized_raw(self) -> bool { match self.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, + ty::Dynamic(_, _) | ty::Slice(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index cf58aec14a6..886d1a78bcb 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -11,9 +11,7 @@ use crate::inherent::*; use crate::ir_print::IrPrint; use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use crate::relate::Relate; -use crate::solve::{ - CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, inspect, -}; +use crate::solve::{CanonicalInput, ExternalConstraintsData, QueryResult, inspect}; use crate::visit::{Flags, TypeVisitable}; use crate::{self as ty, CanonicalParamEnvCacheEntry, search_graph}; @@ -70,10 +68,10 @@ pub trait Interner: + Hash + Eq + TypeFoldable<Self> - + Deref<Target = PredefinedOpaquesData<Self>>; + + SliceLike<Item = (ty::OpaqueTypeKey<Self>, Self::Ty)>; fn mk_predefined_opaques_in_body( self, - data: PredefinedOpaquesData<Self>, + data: &[(ty::OpaqueTypeKey<Self>, Self::Ty)], ) -> Self::PredefinedOpaques; type LocalDefIds: Copy diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 50b588029ae..61e0b67b163 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -57,7 +57,6 @@ mod upcast; mod visit; pub use AliasTyKind::*; -pub use DynKind::*; pub use InferTy::*; pub use RegionKind::*; pub use TyKind::*; diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index b09a378d341..c7dccea6adc 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -203,7 +203,7 @@ impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> { | ty::Ref(_, _, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Tuple(_) => { ty.super_visit_with(self); } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 690a5f65e08..09add529286 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -412,16 +412,11 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>( (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)), - (ty::Dynamic(a_obj, a_region, a_repr), ty::Dynamic(b_obj, b_region, b_repr)) - if a_repr == b_repr => - { - Ok(Ty::new_dynamic( - cx, - relation.relate(a_obj, b_obj)?, - relation.relate(a_region, b_region)?, - a_repr, - )) - } + (ty::Dynamic(a_obj, a_region), ty::Dynamic(b_obj, b_region)) => Ok(Ty::new_dynamic( + cx, + relation.relate(a_obj, b_obj)?, + relation.relate(a_region, b_region)?, + )), (ty::Coroutine(a_id, a_args), ty::Coroutine(b_id, b_args)) if a_id == b_id => { // All Coroutine types with the same id represent diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 8f8f019510f..7aa58d096d5 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -86,14 +86,12 @@ pub trait Delegate: Sized { kind: PathKind, input: <Self::Cx as Cx>::Input, ) -> <Self::Cx as Cx>::Result; - fn is_initial_provisional_result( + fn is_initial_provisional_result(result: <Self::Cx as Cx>::Result) -> Option<PathKind>; + fn stack_overflow_result( cx: Self::Cx, - kind: PathKind, input: <Self::Cx as Cx>::Input, - result: <Self::Cx as Cx>::Result, - ) -> bool; - fn on_stack_overflow(cx: Self::Cx, input: <Self::Cx as Cx>::Input) -> <Self::Cx as Cx>::Result; - fn on_fixpoint_overflow( + ) -> <Self::Cx as Cx>::Result; + fn fixpoint_overflow_result( cx: Self::Cx, input: <Self::Cx as Cx>::Input, ) -> <Self::Cx as Cx>::Result; @@ -215,6 +213,27 @@ impl HeadUsages { let HeadUsages { inductive, unknown, coinductive, forced_ambiguity } = self; inductive == 0 && unknown == 0 && coinductive == 0 && forced_ambiguity == 0 } + + fn is_single(self, path_kind: PathKind) -> bool { + match path_kind { + PathKind::Inductive => matches!( + self, + HeadUsages { inductive: _, unknown: 0, coinductive: 0, forced_ambiguity: 0 }, + ), + PathKind::Unknown => matches!( + self, + HeadUsages { inductive: 0, unknown: _, coinductive: 0, forced_ambiguity: 0 }, + ), + PathKind::Coinductive => matches!( + self, + HeadUsages { inductive: 0, unknown: 0, coinductive: _, forced_ambiguity: 0 }, + ), + PathKind::ForcedAmbiguity => matches!( + self, + HeadUsages { inductive: 0, unknown: 0, coinductive: 0, forced_ambiguity: _ }, + ), + } + } } #[derive(Debug, Default)] @@ -869,7 +888,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { } debug!("encountered stack overflow"); - D::on_stack_overflow(cx, input) + D::stack_overflow_result(cx, input) } /// When reevaluating a goal with a changed provisional result, all provisional cache entry @@ -888,7 +907,29 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { !entries.is_empty() }); } +} +/// We need to rebase provisional cache entries when popping one of their cycle +/// heads from the stack. This may not necessarily mean that we've actually +/// reached a fixpoint for that cycle head, which impacts the way we rebase +/// provisional cache entries. +enum RebaseReason { + NoCycleUsages, + Ambiguity, + Overflow, + /// We've actually reached a fixpoint. + /// + /// This either happens in the first evaluation step for the cycle head. + /// In this case the used provisional result depends on the cycle `PathKind`. + /// We store this path kind to check whether the the provisional cache entry + /// we're rebasing relied on the same cycles. + /// + /// In later iterations cycles always return `stack_entry.provisional_result` + /// so we no longer depend on the `PathKind`. We store `None` in that case. + ReachedFixpoint(Option<PathKind>), +} + +impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> { /// A necessary optimization to handle complex solver cycles. A provisional cache entry /// relies on a set of cycle heads and the path towards these heads. When popping a cycle /// head from the stack after we've finished computing it, we can't be sure that the @@ -908,8 +949,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { /// to me. fn rebase_provisional_cache_entries( &mut self, + cx: X, stack_entry: &StackEntry<X>, - mut mutate_result: impl FnMut(X::Input, X::Result) -> X::Result, + rebase_reason: RebaseReason, ) { let popped_head_index = self.stack.next_index(); #[allow(rustc::potential_query_instability)] @@ -927,6 +969,10 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { return true; }; + let Some(new_highest_head_index) = heads.opt_highest_cycle_head_index() else { + return false; + }; + // We're rebasing an entry `e` over a head `p`. This head // has a number of own heads `h` it depends on. // @@ -977,22 +1023,37 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { let eph = ep.extend_with_paths(ph); heads.insert(head_index, eph, head.usages); } - } - let Some(head_index) = heads.opt_highest_cycle_head_index() else { - return false; - }; + // The provisional cache entry does depend on the provisional result + // of the popped cycle head. We need to mutate the result of our + // provisional cache entry in case we did not reach a fixpoint. + match rebase_reason { + // If the cycle head does not actually depend on itself, then + // the provisional result used by the provisional cache entry + // is not actually equal to the final provisional result. We + // need to discard the provisional cache entry in this case. + RebaseReason::NoCycleUsages => return false, + RebaseReason::Ambiguity => { + *result = D::propagate_ambiguity(cx, input, *result); + } + RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), + RebaseReason::ReachedFixpoint(None) => {} + RebaseReason::ReachedFixpoint(Some(path_kind)) => { + if !popped_head.usages.is_single(path_kind) { + return false; + } + } + }; + } // We now care about the path from the next highest cycle head to the // provisional cache entry. *path_from_head = path_from_head.extend(Self::cycle_path_kind( &self.stack, stack_entry.step_kind_from_parent, - head_index, + new_highest_head_index, )); - // Mutate the result of the provisional cache entry in case we did - // not reach a fixpoint. - *result = mutate_result(input, *result); + true }); !entries.is_empty() @@ -1209,33 +1270,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { /// Whether we've reached a fixpoint when evaluating a cycle head. fn reached_fixpoint( &mut self, - cx: X, stack_entry: &StackEntry<X>, usages: HeadUsages, result: X::Result, - ) -> bool { + ) -> Result<Option<PathKind>, ()> { let provisional_result = stack_entry.provisional_result; - if usages.is_empty() { - true - } else if let Some(provisional_result) = provisional_result { - provisional_result == result + if let Some(provisional_result) = provisional_result { + if provisional_result == result { Ok(None) } else { Err(()) } + } else if let Some(path_kind) = D::is_initial_provisional_result(result) + .filter(|&path_kind| usages.is_single(path_kind)) + { + Ok(Some(path_kind)) } else { - let check = |k| D::is_initial_provisional_result(cx, k, stack_entry.input, result); - match usages { - HeadUsages { inductive: _, unknown: 0, coinductive: 0, forced_ambiguity: 0 } => { - check(PathKind::Inductive) - } - HeadUsages { inductive: 0, unknown: _, coinductive: 0, forced_ambiguity: 0 } => { - check(PathKind::Unknown) - } - HeadUsages { inductive: 0, unknown: 0, coinductive: _, forced_ambiguity: 0 } => { - check(PathKind::Coinductive) - } - HeadUsages { inductive: 0, unknown: 0, coinductive: 0, forced_ambiguity: _ } => { - check(PathKind::ForcedAmbiguity) - } - _ => false, - } + Err(()) } } @@ -1280,8 +1327,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // is equal to the provisional result of the previous iteration, or because // this was only the head of either coinductive or inductive cycles, and the // final result is equal to the initial response for that case. - if self.reached_fixpoint(cx, &stack_entry, usages, result) { - self.rebase_provisional_cache_entries(&stack_entry, |_, result| result); + if let Ok(fixpoint) = self.reached_fixpoint(&stack_entry, usages, result) { + self.rebase_provisional_cache_entries( + cx, + &stack_entry, + RebaseReason::ReachedFixpoint(fixpoint), + ); + return EvaluationResult::finalize(stack_entry, encountered_overflow, result); + } else if usages.is_empty() { + self.rebase_provisional_cache_entries( + cx, + &stack_entry, + RebaseReason::NoCycleUsages, + ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } @@ -1298,9 +1356,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { // we also taint all provisional cache entries which depend on the // current goal. if D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries(&stack_entry, |input, _| { - D::propagate_ambiguity(cx, input, result) - }); + self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Ambiguity); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; @@ -1309,10 +1365,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> { i += 1; if i >= D::FIXPOINT_STEP_LIMIT { debug!("canonical cycle overflow"); - let result = D::on_fixpoint_overflow(cx, input); - self.rebase_provisional_cache_entries(&stack_entry, |input, _| { - D::on_fixpoint_overflow(cx, input) - }); + let result = D::fixpoint_overflow_result(cx, input); + self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Overflow); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index b48a8f46ebe..9d9a8e49847 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -109,19 +109,6 @@ pub struct QueryInput<I: Interner, P> { impl<I: Interner, P: Eq> Eq for QueryInput<I, P> {} -/// Opaques that are defined in the inference context before a query is called. -#[derive_where(Clone, Hash, PartialEq, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -pub struct PredefinedOpaquesData<I: Interner> { - pub opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>, -} - -impl<I: Interner> Eq for PredefinedOpaquesData<I> {} - /// Possible ways the given goal can be proven. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] pub enum CandidateSource<I: Interner> { @@ -269,11 +256,70 @@ impl<I: Interner> NestedNormalizationGoals<I> { #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum Certainty { Yes, - Maybe(MaybeCause), + Maybe { cause: MaybeCause, opaque_types_jank: OpaqueTypesJank }, +} + +/// Supporting not-yet-defined opaque types in HIR typeck is somewhat +/// challenging. Ideally we'd normalize them to a new inference variable +/// and just defer type inference which relies on the opaque until we've +/// constrained the hidden type. +/// +/// This doesn't work for method and function calls as we need to guide type +/// inference for the function arguments. We treat not-yet-defined opaque types +/// as if they were rigid instead in these places. +/// +/// When we encounter a `?hidden_type_of_opaque: Trait<?var>` goal, we use the +/// item bounds and blanket impls to guide inference by constraining other type +/// variables, see `EvalCtxt::try_assemble_bounds_via_registered_opaques`. We +/// always keep the certainty as `Maybe` so that we properly prove these goals +/// once the hidden type has been constrained. +/// +/// If we fail to prove the trait goal via item bounds or blanket impls, the +/// goal would have errored if the opaque type were rigid. In this case, we +/// set `OpaqueTypesJank::ErrorIfRigidSelfTy` in the [Certainty]. +/// +/// Places in HIR typeck where we want to treat not-yet-defined opaque types as if +/// they were kind of rigid then use `fn root_goal_may_hold_opaque_types_jank` which +/// returns `false` if the goal doesn't hold or if `OpaqueTypesJank::ErrorIfRigidSelfTy` +/// is set (i.e. proving it required relies on some `?hidden_ty: NotInItemBounds` goal). +/// +/// This is subtly different from actually treating not-yet-defined opaque types as +/// rigid, e.g. it allows constraining opaque types if they are not the self-type of +/// a goal. It is good enough for now and only matters for very rare type inference +/// edge cases. We can improve this later on if necessary. +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum OpaqueTypesJank { + AllGood, + ErrorIfRigidSelfTy, +} +impl OpaqueTypesJank { + fn and(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::AllGood, OpaqueTypesJank::AllGood) => OpaqueTypesJank::AllGood, + (OpaqueTypesJank::ErrorIfRigidSelfTy, _) | (_, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + } + } + + pub fn or(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::ErrorIfRigidSelfTy, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + (OpaqueTypesJank::AllGood, _) | (_, OpaqueTypesJank::AllGood) => { + OpaqueTypesJank::AllGood + } + } + } } impl Certainty { - pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + pub const AMBIGUOUS: Certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }; /// Use this function to merge the certainty of multiple nested subgoals. /// @@ -290,14 +336,23 @@ impl Certainty { pub fn and(self, other: Certainty) -> Certainty { match (self, other) { (Certainty::Yes, Certainty::Yes) => Certainty::Yes, - (Certainty::Yes, Certainty::Maybe(_)) => other, - (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)), + (Certainty::Yes, Certainty::Maybe { .. }) => other, + (Certainty::Maybe { .. }, Certainty::Yes) => self, + ( + Certainty::Maybe { cause: a_cause, opaque_types_jank: a_jank }, + Certainty::Maybe { cause: b_cause, opaque_types_jank: b_jank }, + ) => Certainty::Maybe { + cause: a_cause.and(b_cause), + opaque_types_jank: a_jank.and(b_jank), + }, } } pub const fn overflow(suggest_increasing_limit: bool) -> Certainty { - Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }) + Certainty::Maybe { + cause: MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }, + opaque_types_jank: OpaqueTypesJank::AllGood, + } } } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 225d85f79c3..dda59283677 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -19,20 +19,6 @@ use crate::{self as ty, DebruijnIndex, FloatTy, IntTy, Interner, UintTy}; mod closure; -/// Specifies how a trait object is represented. -/// -/// This used to have a variant `DynStar`, but that variant has been removed, -/// and it's likely this whole enum will be removed soon. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr( - feature = "nightly", - derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) -)] -pub enum DynKind { - /// An unsized `dyn Trait` object - Dyn, -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[cfg_attr( feature = "nightly", @@ -165,7 +151,7 @@ pub enum TyKind<I: Interner> { UnsafeBinder(UnsafeBinderInner<I>), /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`. - Dynamic(I::BoundExistentialPredicates, I::Region, DynKind), + Dynamic(I::BoundExistentialPredicates, I::Region), /// The anonymous type of a closure. Used to represent the type of `|a| a`. /// @@ -314,7 +300,7 @@ impl<I: Interner> TyKind<I> { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) @@ -367,9 +353,7 @@ impl<I: Interner> fmt::Debug for TyKind<I> { FnPtr(sig_tys, hdr) => write!(f, "{:?}", sig_tys.with(*hdr)), // FIXME(unsafe_binder): print this like `unsafe<'a> T<'a>`. UnsafeBinder(binder) => write!(f, "{:?}", binder), - Dynamic(p, r, repr) => match repr { - DynKind::Dyn => write!(f, "dyn {p:?} + {r:?}"), - }, + Dynamic(p, r) => write!(f, "dyn {p:?} + {r:?}"), Closure(d, s) => f.debug_tuple("Closure").field(d).field(&s).finish(), CoroutineClosure(d, s) => f.debug_tuple("CoroutineClosure").field(d).field(&s).finish(), Coroutine(d, s) => f.debug_tuple("Coroutine").field(d).field(&s).finish(), diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 9912fad1756..6d51817a7bf 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -109,7 +109,7 @@ fn push_inner<I: Interner>(stack: &mut TypeWalkerStack<I>, parent: I::GenericArg ty::Alias(_, data) => { stack.extend(data.args.iter().rev()); } - ty::Dynamic(obj, lt, _) => { + ty::Dynamic(obj, lt) => { stack.push(lt.into()); stack.extend( obj.iter() diff --git a/compiler/rustc_windows_rc/Cargo.toml b/compiler/rustc_windows_rc/Cargo.toml index 080acd35c38..13f716897fa 100644 --- a/compiler/rustc_windows_rc/Cargo.toml +++ b/compiler/rustc_windows_rc/Cargo.toml @@ -5,7 +5,5 @@ edition = "2024" [dependencies] #tidy-alphabetical-start -# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_codegen_ssa`. -cc = "=1.2.16" +find-msvc-tools = "0.1.2" #tidy-alphabetical-end diff --git a/compiler/rustc_windows_rc/src/lib.rs b/compiler/rustc_windows_rc/src/lib.rs index caa5e5ef276..15afaf7b94b 100644 --- a/compiler/rustc_windows_rc/src/lib.rs +++ b/compiler/rustc_windows_rc/src/lib.rs @@ -2,9 +2,7 @@ //! //! Uses values from the `CFG_VERSION` and `CFG_RELEASE` environment variables //! to set the product and file version information in the Windows resource file. -use std::{env, ffi, fs, path, process}; - -use cc::windows_registry; +use std::{env, fs, path, process}; /// The template for the Windows resource file. const RESOURCE_TEMPLATE: &str = include_str!("../rustc.rc.in"); @@ -35,8 +33,14 @@ pub fn compile_windows_resource_file( resources_dir.push("resources"); fs::create_dir_all(&resources_dir).unwrap(); - let resource_compiler = - find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe"); + let resource_compiler = if let Ok(path) = env::var("RUSTC_WINDOWS_RC") { + path.into() + } else { + find_msvc_tools::find_tool(&env::var("CARGO_CFG_TARGET_ARCH").unwrap(), "rc.exe") + .expect("found rc.exe") + .path() + .to_owned() + }; let rc_path = resources_dir.join(file_stem.with_extension("rc")); @@ -131,28 +135,3 @@ fn parse_version(version: &str) -> Option<ResourceVersion> { Some(ResourceVersion { major, minor, patch, build: 0 }) } } - -/// Find the Windows SDK resource compiler `rc.exe` for the given architecture or target triple. -/// Returns `None` if the tool could not be found. -fn find_resource_compiler(arch_or_target: &str) -> Option<path::PathBuf> { - find_windows_sdk_tool(arch_or_target, "rc.exe") -} - -/// Find a Windows SDK tool for the given architecture or target triple. -/// Returns `None` if the tool could not be found. -fn find_windows_sdk_tool(arch_or_target: &str, tool_name: &str) -> Option<path::PathBuf> { - // windows_registry::find_tool can only find MSVC tools, not Windows SDK tools, but - // cc does include the Windows SDK tools in the PATH environment of MSVC tools. - - let msvc_linker = windows_registry::find_tool(arch_or_target, "link.exe")?; - let path = &msvc_linker.env().iter().find(|(k, _)| k == "PATH")?.1; - find_tool_in_path(tool_name, path) -} - -/// Find a tool in the directories in a given PATH-like string. -fn find_tool_in_path<P: AsRef<ffi::OsStr>>(tool_name: &str, path: P) -> Option<path::PathBuf> { - env::split_paths(path.as_ref()).find_map(|p| { - let tool_path = p.join(tool_name); - if tool_path.try_exists().unwrap_or(false) { Some(tool_path) } else { None } - }) -} |
