diff --git a/Cargo.lock b/Cargo.lock index 07de5d221685..1cd998a0bd2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3613,6 +3613,7 @@ dependencies = [ "gimli 0.31.1", "itertools", "libc", + "libloading 0.9.0", "measureme", "object 0.37.3", "rustc-demangle", diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 4e850810a30a..63bf50722ca0 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -191,6 +191,31 @@ # Currently, this is only supported for the `x86_64-unknown-linux-gnu` target. #gcc.download-ci-gcc = false +# Provide a directory of prebuilt libgccjit.so dylibs for given (host, target) compilation pairs. +# This is useful when you want to cross-compile `rustc` to another target since GCC is not a +# multi-target compiler. +# You have to use a directory structure that looks like this: +# `///libgccjit.so`. +# For example: +# +# ``` +# +# ├── m68k-unknown-linux-gnu +# │ └── m68k-unknown-linux-gnu +# │ └── libgccjit.so +# └── x86_64-unknown-linux-gnu +# ├── m68k-unknown-linux-gnu +# │ └── libgccjit.so +# └── x86_64-unknown-linux-gnu +# └── libgccjit.so +# ``` +# The directory above would allow you to cross-compile rustc from x64 to m68k +# +# Note that this option has priority over `gcc.download-ci-gcc`. +# If you set both, bootstrap will first try to load libgccjit.so from this directory. +# Only if it isn't found, it will try to download it from CI or build it locally. +#gcc.libgccjit-libs-dir = "/path/to/libgccjit-libs-dir" + # ============================================================================= # General build configuration options # ============================================================================= diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index a21e1aee9b08..5b43a6c5881d 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -60,6 +60,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { /// This is public so that it can be used in unit tests, but /// should generally only be relevant to the ABI details of /// specific targets. + #[tracing::instrument(skip(cx), level = "debug")] pub fn homogeneous_aggregate(&self, cx: &C) -> Result where Ty: TyAbiInterface<'a, C> + Copy, @@ -82,6 +83,10 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { })) } + BackendRepr::ScalableVector { .. } => { + unreachable!("`homogeneous_aggregate` should not be called for scalable vectors") + } + BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { // Helper for computing `homogeneous_aggregate`, allowing a custom // starting offset (used below for handling variants). diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index b078fc92482f..4f1594d02a82 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -11,7 +11,7 @@ use tracing::{debug, trace}; use crate::{ AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer, LayoutData, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding, - Variants, WrappingRange, + TargetDataLayout, Variants, WrappingRange, }; mod coroutine; @@ -143,58 +143,32 @@ impl LayoutCalculator { }) } - pub fn simd_type< + pub fn scalable_vector_type( + &self, + element: F, + count: u64, + ) -> LayoutCalculatorResult + where FieldIdx: Idx, VariantIdx: Idx, F: AsRef> + fmt::Debug, - >( + { + vector_type_layout(VectorKind::Scalable, self.cx.data_layout(), element, count) + } + + pub fn simd_type( &self, element: F, count: u64, repr_packed: bool, - ) -> LayoutCalculatorResult { - let elt = element.as_ref(); - if count == 0 { - return Err(LayoutCalculatorError::ZeroLengthSimdType); - } else if count > crate::MAX_SIMD_LANES { - return Err(LayoutCalculatorError::OversizedSimdType { - max_lanes: crate::MAX_SIMD_LANES, - }); - } - - let BackendRepr::Scalar(e_repr) = elt.backend_repr else { - return Err(LayoutCalculatorError::NonPrimitiveSimdType(element)); - }; - - // Compute the size and alignment of the vector - let dl = self.cx.data_layout(); - let size = - elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?; - let (repr, align) = if repr_packed && !count.is_power_of_two() { - // 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 }, Align::max_aligned_factor(size)) - } else { - (BackendRepr::SimdVector { element: e_repr, count }, dl.llvmlike_vector_align(size)) - }; - let size = size.align_to(align); - - Ok(LayoutData { - variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Arbitrary { - offsets: [Size::ZERO].into(), - memory_index: [0].into(), - }, - backend_repr: repr, - largest_niche: elt.largest_niche, - uninhabited: false, - size, - align: AbiAlign::new(align), - max_repr_align: None, - unadjusted_abi_align: elt.align.abi, - randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)), - }) + ) -> LayoutCalculatorResult + where + FieldIdx: Idx, + VariantIdx: Idx, + F: AsRef> + fmt::Debug, + { + let kind = if repr_packed { VectorKind::PackedFixed } else { VectorKind::Fixed }; + vector_type_layout(kind, self.cx.data_layout(), element, count) } /// Compute the layout for a coroutine. @@ -453,6 +427,7 @@ impl LayoutCalculator { BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..) | BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } | BackendRepr::Memory { .. } => repr, }, }; @@ -524,7 +499,8 @@ impl LayoutCalculator { hide_niches(a); hide_niches(b); } - BackendRepr::SimdVector { element, count: _ } => hide_niches(element), + BackendRepr::SimdVector { element, .. } + | BackendRepr::ScalableVector { element, .. } => hide_niches(element), BackendRepr::Memory { sized: _ } => {} } st.largest_niche = None; @@ -738,7 +714,7 @@ impl LayoutCalculator { }, fields: FieldsShape::Arbitrary { offsets: [niche_offset].into(), - memory_index: [0].into(), + in_memory_order: [FieldIdx::new(0)].into(), }, backend_repr: abi, largest_niche, @@ -1032,8 +1008,8 @@ impl LayoutCalculator { let pair = LayoutData::::scalar_pair(&self.cx, tag, prim_scalar); let pair_offsets = match pair.fields { - FieldsShape::Arbitrary { ref offsets, ref memory_index } => { - assert_eq!(memory_index.raw, [0, 1]); + FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => { + assert_eq!(in_memory_order.raw, [FieldIdx::new(0), FieldIdx::new(1)]); offsets } _ => panic!("encountered a non-arbitrary layout during enum layout"), @@ -1085,7 +1061,7 @@ impl LayoutCalculator { }, fields: FieldsShape::Arbitrary { offsets: [Size::ZERO].into(), - memory_index: [0].into(), + in_memory_order: [FieldIdx::new(0)].into(), }, largest_niche, uninhabited, @@ -1134,10 +1110,10 @@ impl LayoutCalculator { let pack = repr.pack; let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align }; let mut max_repr_align = repr.align; - let mut inverse_memory_index: IndexVec = fields.indices().collect(); + let mut in_memory_order: IndexVec = fields.indices().collect(); let optimize_field_order = !repr.inhibit_struct_field_reordering(); let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() }; - let optimizing = &mut inverse_memory_index.raw[..end]; + let optimizing = &mut in_memory_order.raw[..end]; let fields_excluding_tail = &fields.raw[..end]; // unsizable tail fields are excluded so that we use the same seed for the sized and unsized layouts. let field_seed = fields_excluding_tail @@ -1272,12 +1248,10 @@ impl LayoutCalculator { // regardless of the status of `-Z randomize-layout` } } - // inverse_memory_index holds field indices by increasing memory offset. - // That is, if field 5 has offset 0, the first element of inverse_memory_index is 5. + // in_memory_order holds field indices by increasing memory offset. + // That is, if field 5 has offset 0, the first element of in_memory_order is 5. // We now write field offsets to the corresponding offset slot; // field 5 with offset 0 puts 0 in offsets[5]. - // At the bottom of this function, we invert `inverse_memory_index` to - // produce `memory_index` (see `invert_mapping`). let mut unsized_field = None::<&F>; let mut offsets = IndexVec::from_elem(Size::ZERO, fields); let mut offset = Size::ZERO; @@ -1289,7 +1263,7 @@ impl LayoutCalculator { align = align.max(prefix_align); offset = prefix_size.align_to(prefix_align); } - for &i in &inverse_memory_index { + for &i in &in_memory_order { let field = &fields[i]; if let Some(unsized_field) = unsized_field { return Err(LayoutCalculatorError::UnexpectedUnsized(*unsized_field)); @@ -1346,18 +1320,6 @@ impl LayoutCalculator { debug!("univariant min_size: {:?}", offset); let min_size = offset; - // As stated above, inverse_memory_index holds field indices by increasing offset. - // This makes it an already-sorted view of the offsets vec. - // To invert it, consider: - // If field 5 has offset 0, offsets[0] is 5, and memory_index[5] should be 0. - // Field 5 would be the first element, so memory_index is i: - // Note: if we didn't optimize, it's already right. - let memory_index = if optimize_field_order { - inverse_memory_index.invert_bijective_mapping() - } else { - 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); // FIXME(oli-obk): deduplicate and harden these checks if size.bytes() >= dl.obj_size_bound() { @@ -1413,8 +1375,11 @@ impl LayoutCalculator { let pair = LayoutData::::scalar_pair(&self.cx, a, b); let pair_offsets = match pair.fields { - FieldsShape::Arbitrary { ref offsets, ref memory_index } => { - assert_eq!(memory_index.raw, [0, 1]); + FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => { + assert_eq!( + in_memory_order.raw, + [FieldIdx::new(0), FieldIdx::new(1)] + ); offsets } FieldsShape::Primitive @@ -1458,7 +1423,7 @@ impl LayoutCalculator { Ok(LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, - fields: FieldsShape::Arbitrary { offsets, memory_index }, + fields: FieldsShape::Arbitrary { offsets, in_memory_order }, backend_repr: abi, largest_niche, uninhabited, @@ -1501,3 +1466,70 @@ impl LayoutCalculator { s } } + +enum VectorKind { + /// `#[rustc_scalable_vector]` + Scalable, + /// `#[repr(simd, packed)]` + PackedFixed, + /// `#[repr(simd)]` + Fixed, +} + +fn vector_type_layout( + kind: VectorKind, + dl: &TargetDataLayout, + element: F, + count: u64, +) -> LayoutCalculatorResult +where + FieldIdx: Idx, + VariantIdx: Idx, + F: AsRef> + fmt::Debug, +{ + let elt = element.as_ref(); + if count == 0 { + return Err(LayoutCalculatorError::ZeroLengthSimdType); + } else if count > crate::MAX_SIMD_LANES { + return Err(LayoutCalculatorError::OversizedSimdType { max_lanes: crate::MAX_SIMD_LANES }); + } + + let BackendRepr::Scalar(element) = elt.backend_repr else { + return Err(LayoutCalculatorError::NonPrimitiveSimdType(element)); + }; + + // Compute the size and alignment of the vector + let size = + elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?; + let (repr, align) = match kind { + VectorKind::Scalable => { + (BackendRepr::ScalableVector { element, count }, dl.llvmlike_vector_align(size)) + } + // 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. + VectorKind::PackedFixed if !count.is_power_of_two() => { + (BackendRepr::Memory { sized: true }, Align::max_aligned_factor(size)) + } + VectorKind::PackedFixed | VectorKind::Fixed => { + (BackendRepr::SimdVector { element, count }, dl.llvmlike_vector_align(size)) + } + }; + let size = size.align_to(align); + + Ok(LayoutData { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Arbitrary { + offsets: [Size::ZERO].into(), + in_memory_order: [FieldIdx::new(0)].into(), + }, + backend_repr: repr, + largest_niche: elt.largest_niche, + uninhabited: false, + size, + align: AbiAlign::new(align), + max_repr_align: None, + unadjusted_abi_align: elt.align.abi, + randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)), + }) +} diff --git a/compiler/rustc_abi/src/layout/coroutine.rs b/compiler/rustc_abi/src/layout/coroutine.rs index 2b22276d4aed..815cf1e28a08 100644 --- a/compiler/rustc_abi/src/layout/coroutine.rs +++ b/compiler/rustc_abi/src/layout/coroutine.rs @@ -182,33 +182,29 @@ pub(super) fn layout< // CoroutineLayout. debug!("prefix = {:#?}", prefix); let (outer_fields, promoted_offsets, promoted_memory_index) = match prefix.fields { - FieldsShape::Arbitrary { mut offsets, memory_index } => { - let mut inverse_memory_index = memory_index.invert_bijective_mapping(); - + FieldsShape::Arbitrary { mut offsets, in_memory_order } => { // "a" (`0..b_start`) and "b" (`b_start..`) correspond to // "outer" and "promoted" fields respectively. let b_start = tag_index.plus(1); let offsets_b = IndexVec::from_raw(offsets.raw.split_off(b_start.index())); let offsets_a = offsets; - // Disentangle the "a" and "b" components of `inverse_memory_index` + // Disentangle the "a" and "b" components of `in_memory_order` // by preserving the order but keeping only one disjoint "half" each. // FIXME(eddyb) build a better abstraction for permutations, if possible. - let inverse_memory_index_b: IndexVec = inverse_memory_index - .iter() - .filter_map(|&i| i.index().checked_sub(b_start.index()).map(FieldIdx::new)) - .collect(); - inverse_memory_index.raw.retain(|&i| i.index() < b_start.index()); - let inverse_memory_index_a = inverse_memory_index; - - // Since `inverse_memory_index_{a,b}` each only refer to their - // respective fields, they can be safely inverted - let memory_index_a = inverse_memory_index_a.invert_bijective_mapping(); - let memory_index_b = inverse_memory_index_b.invert_bijective_mapping(); + let mut in_memory_order_a = IndexVec::::new(); + let mut in_memory_order_b = IndexVec::::new(); + for i in in_memory_order { + if let Some(j) = i.index().checked_sub(b_start.index()) { + in_memory_order_b.push(FieldIdx::new(j)); + } else { + in_memory_order_a.push(i); + } + } let outer_fields = - FieldsShape::Arbitrary { offsets: offsets_a, memory_index: memory_index_a }; - (outer_fields, offsets_b, memory_index_b) + FieldsShape::Arbitrary { offsets: offsets_a, in_memory_order: in_memory_order_a }; + (outer_fields, offsets_b, in_memory_order_b.invert_bijective_mapping()) } _ => unreachable!(), }; @@ -236,7 +232,7 @@ pub(super) fn layout< )?; variant.variants = Variants::Single { index }; - let FieldsShape::Arbitrary { offsets, memory_index } = variant.fields else { + let FieldsShape::Arbitrary { offsets, in_memory_order } = variant.fields else { unreachable!(); }; @@ -249,8 +245,9 @@ pub(super) fn layout< // promoted fields were being used, but leave the elements not in the // subset as `invalid_field_idx`, which we can filter out later to // obtain a valid (bijective) mapping. + let memory_index = in_memory_order.invert_bijective_mapping(); let invalid_field_idx = promoted_memory_index.len() + memory_index.len(); - let mut combined_inverse_memory_index = + let mut combined_in_memory_order = IndexVec::from_elem_n(FieldIdx::new(invalid_field_idx), invalid_field_idx); let mut offsets_and_memory_index = iter::zip(offsets, memory_index); @@ -268,19 +265,18 @@ pub(super) fn layout< (promoted_offsets[field_idx], promoted_memory_index[field_idx]) } }; - combined_inverse_memory_index[memory_index] = i; + combined_in_memory_order[memory_index] = i; offset }) .collect(); - // Remove the unused slots and invert the mapping to obtain the - // combined `memory_index` (also see previous comment). - combined_inverse_memory_index.raw.retain(|&i| i.index() != invalid_field_idx); - let combined_memory_index = combined_inverse_memory_index.invert_bijective_mapping(); + // Remove the unused slots to obtain the combined `in_memory_order` + // (also see previous comment). + combined_in_memory_order.raw.retain(|&i| i.index() != invalid_field_idx); variant.fields = FieldsShape::Arbitrary { offsets: combined_offsets, - memory_index: combined_memory_index, + in_memory_order: combined_in_memory_order, }; size = size.max(variant.size); diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs index b3807c872739..3784611b157b 100644 --- a/compiler/rustc_abi/src/layout/simple.rs +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -16,7 +16,7 @@ impl LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { offsets: IndexVec::new(), - memory_index: IndexVec::new(), + in_memory_order: IndexVec::new(), }, backend_repr: BackendRepr::Memory { sized }, largest_niche: None, @@ -108,7 +108,7 @@ impl LayoutData { variants: Variants::Single { index: VariantIdx::new(0) }, fields: FieldsShape::Arbitrary { offsets: [Size::ZERO, b_offset].into(), - memory_index: [0, 1].into(), + in_memory_order: [FieldIdx::new(0), FieldIdx::new(1)].into(), }, backend_repr: BackendRepr::ScalarPair(a, b), largest_niche, @@ -133,7 +133,7 @@ impl LayoutData { Some(fields) => FieldsShape::Union(fields), None => FieldsShape::Arbitrary { offsets: IndexVec::new(), - memory_index: IndexVec::new(), + in_memory_order: IndexVec::new(), }, }, backend_repr: BackendRepr::Memory { sized: true }, diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index ca2753ea2fd9..41ad14f550ab 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -155,7 +155,7 @@ impl<'a, Ty> AsRef> for TyAndLayout<'a, Ty> { /// Trait that needs to be implemented by the higher-level type representation /// (e.g. `rustc_middle::ty::Ty`), to provide `rustc_target::abi` functionality. -pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug { +pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug + std::fmt::Display { fn ty_and_layout_for_variant( this: TyAndLayout<'a, Self>, cx: &C, @@ -172,6 +172,7 @@ pub trait TyAbiInterface<'a, C>: Sized + std::fmt::Debug { fn is_tuple(this: TyAndLayout<'a, Self>) -> bool; fn is_unit(this: TyAndLayout<'a, Self>) -> bool; fn is_transparent(this: TyAndLayout<'a, Self>) -> bool; + fn is_scalable_vector(this: TyAndLayout<'a, Self>) -> bool; /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'a, Self>) -> bool; } @@ -271,6 +272,13 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::is_transparent(self) } + pub fn is_scalable_vector(self) -> bool + where + Ty: TyAbiInterface<'a, C>, + { + Ty::is_scalable_vector(self) + } + /// If this method returns `true`, then this type should always have a `PassMode` of /// `Indirect { on_stack: false, .. }` when being used as the argument type of a function with a /// non-Rustic ABI (this is true for structs annotated with the diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index cd85efb2753a..061ad8617893 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -96,9 +96,11 @@ bitflags! { /// If true, the type is always passed indirectly by non-Rustic ABIs. /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. const PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS = 1 << 5; - /// Any of these flags being set prevent field reordering optimisation. - const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits() + const IS_SCALABLE = 1 << 6; + // Any of these flags being set prevent field reordering optimisation. + const FIELD_ORDER_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits() + | ReprFlags::IS_SCALABLE.bits() | ReprFlags::IS_LINEAR.bits(); const ABI_UNOPTIMIZABLE = ReprFlags::IS_C.bits() | ReprFlags::IS_SIMD.bits(); } @@ -135,6 +137,19 @@ impl IntegerType { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr( + feature = "nightly", + derive(Encodable_NoContext, Decodable_NoContext, HashStable_Generic) +)] +pub enum ScalableElt { + /// `N` in `rustc_scalable_vector(N)` - the element count of the scalable vector + ElementCount(u16), + /// `rustc_scalable_vector` w/out `N`, used for tuple types of scalable vectors that only + /// contain other scalable vectors + Container, +} + /// Represents the repr options provided by the user. #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] #[cfg_attr( @@ -146,6 +161,8 @@ pub struct ReprOptions { pub align: Option, pub pack: Option, pub flags: ReprFlags, + /// `#[rustc_scalable_vector]` + pub scalable: Option, /// The seed to be used for randomizing a type's layout /// /// Note: This could technically be a `u128` which would @@ -162,6 +179,11 @@ impl ReprOptions { self.flags.contains(ReprFlags::IS_SIMD) } + #[inline] + pub fn scalable(&self) -> bool { + self.flags.contains(ReprFlags::IS_SCALABLE) + } + #[inline] pub fn c(&self) -> bool { self.flags.contains(ReprFlags::IS_C) @@ -1614,19 +1636,14 @@ pub enum FieldsShape { // FIXME(eddyb) use small vector optimization for the common case. offsets: IndexVec, - /// Maps source order field indices to memory order indices, + /// Maps memory order field indices to source order indices, /// depending on how the fields were reordered (if at all). /// This is a permutation, with both the source order and the /// memory order using the same (0..n) index ranges. /// - /// Note that during computation of `memory_index`, sometimes - /// it is easier to operate on the inverse mapping (that is, - /// from memory order to source order), and that is usually - /// named `inverse_memory_index`. - /// // FIXME(eddyb) build a better abstraction for permutations, if possible. // FIXME(camlorn) also consider small vector optimization here. - memory_index: IndexVec, + in_memory_order: IndexVec, }, } @@ -1660,51 +1677,17 @@ impl FieldsShape { } } - #[inline] - pub fn memory_index(&self, i: usize) -> usize { - match *self { - FieldsShape::Primitive => { - unreachable!("FieldsShape::memory_index: `Primitive`s have no fields") - } - FieldsShape::Union(_) | FieldsShape::Array { .. } => i, - FieldsShape::Arbitrary { ref memory_index, .. } => { - memory_index[FieldIdx::new(i)].try_into().unwrap() - } - } - } - /// Gets source indices of the fields by increasing offsets. #[inline] pub fn index_by_increasing_offset(&self) -> impl ExactSizeIterator { - let mut inverse_small = [0u8; 64]; - let mut inverse_big = IndexVec::new(); - let use_small = self.count() <= inverse_small.len(); - - // We have to write this logic twice in order to keep the array small. - if let FieldsShape::Arbitrary { ref memory_index, .. } = *self { - if use_small { - for (field_idx, &mem_idx) in memory_index.iter_enumerated() { - inverse_small[mem_idx as usize] = field_idx.index() as u8; - } - } else { - inverse_big = memory_index.invert_bijective_mapping(); - } - } - // Primitives don't really have fields in the way that structs do, // but having this return an empty iterator for them is unhelpful // since that makes them look kinda like ZSTs, which they're not. let pseudofield_count = if let FieldsShape::Primitive = self { 1 } else { self.count() }; - (0..pseudofield_count).map(move |i| match *self { + (0..pseudofield_count).map(move |i| match self { FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i, - FieldsShape::Arbitrary { .. } => { - if use_small { - inverse_small[i] as usize - } else { - inverse_big[i as u32].index() - } - } + FieldsShape::Arbitrary { in_memory_order, .. } => in_memory_order[i as u32].index(), }) } } @@ -1736,6 +1719,10 @@ impl AddressSpace { pub enum BackendRepr { Scalar(Scalar), ScalarPair(Scalar, Scalar), + ScalableVector { + element: Scalar, + count: u64, + }, SimdVector { element: Scalar, count: u64, @@ -1754,6 +1741,12 @@ impl BackendRepr { match *self { BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) + // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the + // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is + // fully implemented, scalable vectors will remain `Sized`, they just won't be + // `const Sized` - whether `is_unsized` continues to return `false` at that point will + // need to be revisited and will depend on what `is_unsized` is used for. + | BackendRepr::ScalableVector { .. } | BackendRepr::SimdVector { .. } => false, BackendRepr::Memory { sized } => !sized, } @@ -1794,7 +1787,9 @@ impl BackendRepr { BackendRepr::Scalar(s) => Some(s.align(cx).abi), BackendRepr::ScalarPair(s1, s2) => Some(s1.align(cx).max(s2.align(cx)).abi), // The align of a Vector can vary in surprising ways - BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None, + BackendRepr::SimdVector { .. } + | BackendRepr::Memory { .. } + | BackendRepr::ScalableVector { .. } => None, } } @@ -1816,7 +1811,9 @@ impl BackendRepr { Some(size) } // The size of a Vector can vary in surprising ways - BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => None, + BackendRepr::SimdVector { .. } + | BackendRepr::Memory { .. } + | BackendRepr::ScalableVector { .. } => None, } } @@ -1831,6 +1828,9 @@ impl BackendRepr { BackendRepr::SimdVector { element: element.to_union(), count } } BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true }, + BackendRepr::ScalableVector { element, count } => { + BackendRepr::ScalableVector { element: element.to_union(), count } + } } } @@ -2071,7 +2071,9 @@ impl LayoutData { /// Returns `true` if this is an aggregate type (including a ScalarPair!) pub fn is_aggregate(&self) -> bool { match self.backend_repr { - BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => false, + BackendRepr::Scalar(_) + | BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } => false, BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => true, } } @@ -2165,6 +2167,19 @@ impl LayoutData { self.is_sized() && self.size.bytes() == 0 && self.align.bytes() == 1 } + /// Returns `true` if the size of the type is only known at runtime. + pub fn is_runtime_sized(&self) -> bool { + matches!(self.backend_repr, BackendRepr::ScalableVector { .. }) + } + + /// Returns the elements count of a scalable vector. + pub fn scalable_vector_element_count(&self) -> Option { + match self.backend_repr { + BackendRepr::ScalableVector { count, .. } => Some(count), + _ => None, + } + } + /// Returns `true` if the type is a ZST and not unsized. /// /// Note that this does *not* imply that the type is irrelevant for layout! It can still have @@ -2173,6 +2188,7 @@ impl LayoutData { match self.backend_repr { BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) + | BackendRepr::ScalableVector { .. } | BackendRepr::SimdVector { .. } => false, BackendRepr::Memory { sized } => sized && self.size.bytes() == 0, } diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 5151c358774e..a0350dd6ff31 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -10,7 +10,6 @@ // tidy-alphabetical-start #![allow(clippy::mut_from_ref)] // Arena allocators are one place where this pattern is fine. #![allow(internal_features)] -#![cfg_attr(bootstrap, feature(maybe_uninit_slice))] #![cfg_attr(test, feature(test))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(test(no_crate_inject, attr(deny(warnings), allow(internal_features))))] diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 10a8e181c840..cbdc89f9deed 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -6,7 +6,6 @@ // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(array_windows))] -#![deny(clippy::manual_let_else)] #![doc(test(attr(deny(warnings), allow(internal_features))))] #![feature(associated_type_defaults)] #![feature(box_patterns)] diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl index 370b15d2871a..e1fccead4d21 100644 --- a/compiler/rustc_ast_lowering/messages.ftl +++ b/compiler/rustc_ast_lowering/messages.ftl @@ -56,6 +56,8 @@ ast_lowering_coroutine_too_many_parameters = ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs .label = default fields are only supported on structs +ast_lowering_delegation_cycle_in_signature_resolution = encountered a cycle during delegation signature resolution +ast_lowering_delegation_unresolved_callee = failed to resolve delegation callee ast_lowering_does_not_support_modifiers = the `{$class_name}` register class does not support template modifiers diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 82bade8829a2..532a0ce520cd 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -44,6 +44,7 @@ use hir::{BodyId, HirId}; use rustc_abi::ExternAbi; use rustc_ast::*; use rustc_attr_parsing::{AttributeParser, ShouldEmit}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::ErrorGuaranteed; use rustc_hir::Target; use rustc_hir::attrs::{AttributeKind, InlineAttr}; @@ -55,6 +56,7 @@ use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use super::{GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode}; +use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee}; use crate::{AllowReturnTypeNotation, ImplTraitPosition, ResolverAstLoweringExt}; pub(crate) struct DelegationResults<'hir> { @@ -119,10 +121,14 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, delegation: &Delegation, item_id: NodeId, - is_in_trait_impl: bool, ) -> DelegationResults<'hir> { let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span); - let sig_id = self.get_delegation_sig_id(item_id, delegation.id, span, is_in_trait_impl); + + let sig_id = self.get_delegation_sig_id( + self.resolver.delegation_sig_resolution_nodes[&self.local_def_id(item_id)], + span, + ); + match sig_id { Ok(sig_id) => { self.add_attributes_if_needed(span, sig_id); @@ -238,24 +244,48 @@ impl<'hir> LoweringContext<'_, 'hir> { fn get_delegation_sig_id( &self, - item_id: NodeId, - path_id: NodeId, + mut node_id: NodeId, span: Span, - is_in_trait_impl: bool, ) -> Result { - let sig_id = if is_in_trait_impl { item_id } else { path_id }; - self.get_resolution_id(sig_id, span) + let mut visited: FxHashSet = Default::default(); + + loop { + visited.insert(node_id); + + let Some(def_id) = self.get_resolution_id(node_id) else { + return Err(self.tcx.dcx().span_delayed_bug( + span, + format!( + "LoweringContext: couldn't resolve node {:?} in delegation item", + node_id + ), + )); + }; + + // If def_id is in local crate and it corresponds to another delegation + // it means that we refer to another delegation as a callee, so in order to obtain + // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it. + if let Some(local_id) = def_id.as_local() + && let Some(next_node_id) = + self.resolver.delegation_sig_resolution_nodes.get(&local_id) + { + node_id = *next_node_id; + if visited.contains(&node_id) { + // We encountered a cycle in the resolution, or delegation callee refers to non-existent + // entity, in this case emit an error. + return Err(match visited.len() { + 1 => self.dcx().emit_err(UnresolvedDelegationCallee { span }), + _ => self.dcx().emit_err(CycleInDelegationSignatureResolution { span }), + }); + } + } else { + return Ok(def_id); + } + } } - fn get_resolution_id(&self, node_id: NodeId, span: Span) -> Result { - let def_id = - self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id()); - def_id.ok_or_else(|| { - self.tcx.dcx().span_delayed_bug( - span, - format!("LoweringContext: couldn't resolve node {:?} in delegation item", node_id), - ) - }) + fn get_resolution_id(&self, node_id: NodeId) -> Option { + self.resolver.get_partial_res(node_id).and_then(|r| r.expect_full_res().opt_def_id()) } fn lower_delegation_generics(&mut self, span: Span) -> &'hir hir::Generics<'hir> { @@ -271,8 +301,6 @@ impl<'hir> LoweringContext<'_, 'hir> { // Function parameter count, including C variadic `...` if present. fn param_count(&self, sig_id: DefId) -> (usize, bool /*c_variadic*/) { if let Some(local_sig_id) = sig_id.as_local() { - // Map may be filled incorrectly due to recursive delegation. - // Error will be emitted later during HIR ty lowering. match self.resolver.delegation_fn_sigs.get(&local_sig_id) { Some(sig) => (sig.param_count, sig.c_variadic), None => (0, false), @@ -489,8 +517,8 @@ impl<'hir> LoweringContext<'_, 'hir> { delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some()); let call = if self - .get_resolution_id(delegation.id, span) - .and_then(|def_id| Ok(self.is_method(def_id, span))) + .get_resolution_id(delegation.id) + .map(|def_id| self.is_method(def_id, span)) .unwrap_or_default() && delegation.qself.is_none() && !has_generic_args diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 83f3a976e83f..35c37fe91d49 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -475,3 +475,17 @@ pub(crate) struct UnionWithDefault { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(ast_lowering_delegation_unresolved_callee)] +pub(crate) struct UnresolvedDelegationCallee { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_lowering_delegation_cycle_in_signature_resolution)] +pub(crate) struct CycleInDelegationSignatureResolution { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index a68d63bf1464..bfce7c25b75d 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -541,7 +541,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ItemKind::Macro(ident, macro_def, macro_kinds) } ItemKind::Delegation(box delegation) => { - let delegation_results = self.lower_delegation(delegation, id, false); + let delegation_results = self.lower_delegation(delegation, id); hir::ItemKind::Fn { sig: delegation_results.sig, ident: delegation_results.ident, @@ -1026,7 +1026,7 @@ impl<'hir> LoweringContext<'_, 'hir> { (*ident, generics, kind, ty.is_some()) } AssocItemKind::Delegation(box delegation) => { - let delegation_results = self.lower_delegation(delegation, i.id, false); + let delegation_results = self.lower_delegation(delegation, i.id); let item_kind = hir::TraitItemKind::Fn( delegation_results.sig, hir::TraitFn::Provided(delegation_results.body_id), @@ -1196,7 +1196,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) } AssocItemKind::Delegation(box delegation) => { - let delegation_results = self.lower_delegation(delegation, i.id, is_in_trait_impl); + let delegation_results = self.lower_delegation(delegation, i.id); ( delegation.ident, ( diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 10093da97fe3..d0871b0b98f4 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -237,25 +237,27 @@ impl SpanLowerer { #[extension(trait ResolverAstLoweringExt)] impl ResolverAstLowering { fn legacy_const_generic_args(&self, expr: &Expr) -> Option> { - if let ExprKind::Path(None, path) = &expr.kind { - // Don't perform legacy const generics rewriting if the path already - // has generic arguments. - if path.segments.last().unwrap().args.is_some() { - return None; - } + let ExprKind::Path(None, path) = &expr.kind else { + return None; + }; - if let Res::Def(DefKind::Fn, def_id) = self.partial_res_map.get(&expr.id)?.full_res()? { - // We only support cross-crate argument rewriting. Uses - // within the same crate should be updated to use the new - // const generics style. - if def_id.is_local() { - return None; - } + // Don't perform legacy const generics rewriting if the path already + // has generic arguments. + if path.segments.last().unwrap().args.is_some() { + return None; + } - if let Some(v) = self.legacy_const_generic_args.get(&def_id) { - return v.clone(); - } - } + let def_id = self.partial_res_map.get(&expr.id)?.full_res()?.opt_def_id()?; + + // We only support cross-crate argument rewriting. Uses + // within the same crate should be updated to use the new + // const generics style. + if def_id.is_local() { + return None; + } + + if let Some(v) = self.legacy_const_generic_args.get(&def_id) { + return v.clone(); } None diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index f1f734b855c1..b9117c83ae2f 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -270,6 +270,8 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc} +ast_passes_scalable_vector_not_tuple_struct = scalable vectors must be tuple structs + ast_passes_static_without_body = free static item without body .suggestion = provide a definition for the static diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0d34ba6c2ca8..a083ca7d133e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1319,6 +1319,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } ItemKind::Struct(ident, generics, vdata) => { self.with_tilde_const(Some(TildeConstReason::Struct { span: item.span }), |this| { + // Scalable vectors can only be tuple structs + let is_scalable_vector = + item.attrs.iter().any(|attr| attr.has_name(sym::rustc_scalable_vector)); + if is_scalable_vector && !matches!(vdata, VariantData::Tuple(..)) { + this.dcx() + .emit_err(errors::ScalableVectorNotTupleStruct { span: item.span }); + } + match vdata { VariantData::Struct { fields, .. } => { this.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index bf9309614fe2..22adaae8c6f2 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -990,3 +990,10 @@ pub(crate) struct AbiX86Interrupt { pub spans: Vec, pub param_count: usize, } + +#[derive(Diagnostic)] +#[diag(ast_passes_scalable_vector_not_tuple_struct)] +pub(crate) struct ScalableVectorNotTupleStruct { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 61f816f0baf8..5caa98b05357 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -50,11 +50,6 @@ attr_parsing_expects_feature_list = attr_parsing_expects_features = `{$name}` expects feature names -attr_parsing_ill_formed_attribute_input = {$num_suggestions -> - [1] attribute must be of the form {$suggestions} - *[other] valid forms for the attribute are {$suggestions} - } - attr_parsing_import_name_type_raw = import name type can only be used with link kind `raw-dylib` @@ -205,6 +200,9 @@ attr_parsing_rustc_allowed_unstable_pairing = attr_parsing_rustc_promotable_pairing = `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute +attr_parsing_rustc_scalable_vector_count_out_of_range = element count in `rustc_scalable_vector` is too large: `{$n}` + .note = the value may not exceed `u16::MAX` + attr_parsing_soft_no_args = `soft` should not have any arguments @@ -213,10 +211,6 @@ attr_parsing_stability_outside_std = stability attributes may not be used outsid attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) -attr_parsing_unknown_meta_item = - unknown meta item '{$item}' - .label = expected one of {$expected} - attr_parsing_unknown_version_literal = unknown version literal format, assuming it refers to a future version diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs index 79f7171cc0c8..7323db06a8f1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -61,7 +61,6 @@ impl CombineAttributeParser for AllowConstFnUnstableParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), - Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), ]); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 5b4786a64ef2..798cc1076541 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -42,7 +42,7 @@ pub fn parse_cfg( args: &ArgParser, ) -> Option { let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; let Some(single) = list.single() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 0c0915558089..00a2d12106e7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -28,6 +28,33 @@ pub struct CfgSelectBranches { pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>, } +impl CfgSelectBranches { + /// Removes the top-most branch for which `predicate` returns `true`, + /// or the wildcard if none of the reachable branches satisfied the predicate. + pub fn pop_first_match(&mut self, predicate: F) -> Option<(TokenStream, Span)> + where + F: Fn(&CfgEntry) -> bool, + { + for (index, (cfg, _, _)) in self.reachable.iter().enumerate() { + if predicate(cfg) { + let matched = self.reachable.remove(index); + return Some((matched.1, matched.2)); + } + } + + self.wildcard.take().map(|(_, tts, span)| (tts, span)) + } + + /// Consume this value and iterate over all the `TokenStream`s that it stores. + pub fn into_iter_tts(self) -> impl Iterator { + let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span)); + let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span)); + let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span)); + + it1.chain(it2).chain(it3) + } +} + pub fn parse_cfg_select( p: &mut Parser<'_>, sess: &Session, diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 05da89167778..c68c66b27185 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -25,7 +25,7 @@ impl SingleAttributeParser for OptimizeParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; @@ -57,7 +57,6 @@ impl NoArgsAttributeParser for ColdParser { Allow(Target::Fn), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), - Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::Method(MethodKind::Inherent)), Allow(Target::ForeignFn), Allow(Target::Closure), @@ -343,7 +342,7 @@ impl NoArgsAttributeParser for TrackCallerParser { Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), - Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods Allow(Target::ForeignFn), Allow(Target::Closure), Warn(Target::MacroDef), @@ -478,7 +477,7 @@ fn parse_tf_attribute( ) -> impl IntoIterator { let mut features = Vec::new(); let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return features; }; if list.is_empty() { @@ -601,7 +600,7 @@ impl SingleAttributeParser for SanitizeParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; @@ -691,6 +690,16 @@ impl SingleAttributeParser for SanitizeParser { } } +pub(crate) struct ThreadLocalParser; + +impl NoArgsAttributeParser for ThreadLocalParser { + const PATH: &[Symbol] = &[sym::thread_local]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal; +} + pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser; impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser { diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 97e78dfb136b..0b7ac989346a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -13,7 +13,7 @@ impl AttributeParser for ConfusablesParser { template!(List: &[r#""name1", "name2", ..."#]), |this, cx, args| { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/debugger.rs b/compiler/rustc_attr_parsing/src/attributes/debugger.rs index c88b795aab03..52a66942cf93 100644 --- a/compiler/rustc_attr_parsing/src/attributes/debugger.rs +++ b/compiler/rustc_attr_parsing/src/attributes/debugger.rs @@ -21,7 +21,7 @@ impl CombineAttributeParser for DebuggerViualizerParser { args: &ArgParser, ) -> impl IntoIterator { let Some(l) = args.list() else { - cx.expected_list(args.span().unwrap_or(cx.attr_span)); + cx.expected_list(cx.attr_span, args); return None; }; let Some(single) = l.single() else { diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index ad3e2ced60c7..2d79e3a103d6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -110,13 +110,12 @@ impl SingleAttributeParser for DeprecationParser { Some(get(cx, name, param.span(), param.args(), &suggestion)?); } _ => { - cx.unknown_key( + cx.expected_specific_argument( param.span(), - param.path().to_string(), if features.deprecated_suggestion() { - &["since", "note", "suggestion"] + &[sym::since, sym::note, sym::suggestion] } else { - &["since", "note"] + &[sym::since, sym::note] }, ); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index b6fea37c92aa..16dbb04b48eb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -106,7 +106,7 @@ impl DocParser { } Some(sym::attr) => { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index fe8f3578fe14..61f975555884 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -76,7 +76,7 @@ impl CombineAttributeParser for LinkParser { return None; } _ => { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; } }; @@ -379,7 +379,7 @@ impl LinkParser { return true; } let Some(link_cfg) = item.args().list() else { - cx.expected_list(item.span()); + cx.expected_list(item.span(), item.args()); return true; }; let Some(link_cfg) = link_cfg.single() else { @@ -469,7 +469,6 @@ impl SingleAttributeParser for LinkSectionParser { Allow(Target::Static), Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), - Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), ]); @@ -587,12 +586,12 @@ impl SingleAttributeParser for LinkageParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::Fn), Allow(Target::Method(MethodKind::Inherent)), - Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), Allow(Target::Static), Allow(Target::ForeignStatic), Allow(Target::ForeignFn), + Warn(Target::Method(MethodKind::Trait { body: false })), // Not inherited ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: [ diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index e4209c3edd85..0f1ab02fca25 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,9 +1,7 @@ -use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; use rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS; use super::prelude::*; -use crate::session_diagnostics::IllFormedAttributeInputLint; pub(crate) struct MacroEscapeParser; impl NoArgsAttributeParser for MacroEscapeParser { @@ -101,15 +99,8 @@ impl AttributeParser for MacroUseParser { } } } - ArgParser::NameValue(_) => { - let suggestions = cx.suggestions(); - cx.emit_err(IllFormedAttributeInputLint { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - span, - }); + ArgParser::NameValue(nv) => { + cx.expected_list_or_no_args(nv.args_span()); } } }, @@ -164,16 +155,8 @@ impl SingleAttributeParser for MacroExportParser { } } } - ArgParser::NameValue(_) => { - let span = cx.attr_span; - let suggestions = cx.suggestions(); - cx.emit_err(IllFormedAttributeInputLint { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - span, - }); + ArgParser::NameValue(nv) => { + cx.expected_list_or_no_args(nv.args_span()); return None; } }; diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 64f29a4729c3..f7290bd7e6f2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -47,6 +47,7 @@ pub(crate) mod loop_match; pub(crate) mod macro_attrs; pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; +pub(crate) mod no_link; pub(crate) mod non_exhaustive; pub(crate) mod path; pub(crate) mod pin_v2; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs index a27e1ecb707e..673e2c902da0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -1,7 +1,4 @@ -use rustc_errors::DiagArgValue; - use super::prelude::*; -use crate::session_diagnostics::IllFormedAttributeInputLint; pub(crate) struct MustUseParser; @@ -44,15 +41,8 @@ impl SingleAttributeParser for MustUseParser { }; Some(value_str) } - ArgParser::List(_) => { - let suggestions = cx.suggestions(); - cx.emit_err(IllFormedAttributeInputLint { - num_suggestions: suggestions.len(), - suggestions: DiagArgValue::StrListSepByAnd( - suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), - ), - span: cx.attr_span, - }); + ArgParser::List(list) => { + cx.expected_nv_or_no_args(list.span); return None; } }, diff --git a/compiler/rustc_attr_parsing/src/attributes/no_link.rs b/compiler/rustc_attr_parsing/src/attributes/no_link.rs new file mode 100644 index 000000000000..43cd1c5406e9 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/no_link.rs @@ -0,0 +1,14 @@ +use super::prelude::*; + +pub(crate) struct NoLinkParser; +impl NoArgsAttributeParser for NoLinkParser { + const PATH: &[Symbol] = &[sym::no_link]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::ExternCrate), + Warn(Target::Field), + Warn(Target::Arm), + Warn(Target::MacroDef), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoLink; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index e1762005d4c4..3674aa7124ab 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -65,7 +65,7 @@ fn parse_derive_like( if args.no_args().is_ok() && !trait_name_mandatory { return Some((None, ThinVec::new())); } - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; let mut items = list.mixed(); @@ -96,7 +96,7 @@ fn parse_derive_like( let mut attributes = ThinVec::new(); if let Some(attrs) = items.next() { let Some(attr_list) = attrs.meta_item() else { - cx.expected_list(attrs.span()); + cx.unexpected_literal(attrs.span()); return None; }; if !attr_list.path().word_is(sym::attributes) { @@ -104,7 +104,7 @@ fn parse_derive_like( return None; } let Some(attr_list) = attr_list.args().list() else { - cx.expected_list(attrs.span()); + cx.expected_list(attrs.span(), attr_list.args()); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index cd7c84f45fe5..ac50fe33839d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -27,7 +27,7 @@ impl SingleAttributeParser for CustomMirParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; @@ -46,9 +46,8 @@ impl SingleAttributeParser for CustomMirParser { extract_value(cx, sym::dialect, arg, meta_item.span(), &mut dialect, &mut failed); } else if let Some(arg) = meta_item.word_is(sym::phase) { extract_value(cx, sym::phase, arg, meta_item.span(), &mut phase, &mut failed); - } else if let Some(word) = meta_item.path().word() { - let word = word.to_string(); - cx.unknown_key(meta_item.span(), word, &["dialect", "phase"]); + } else if let Some(..) = meta_item.path().word() { + cx.expected_specific_argument(meta_item.span(), &[sym::dialect, sym::phase]); failed = true; } else { cx.expected_name_value(meta_item.span(), None); diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 4520e4f5dbac..34d4957edc70 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -33,7 +33,7 @@ impl CombineAttributeParser for ReprParser { let mut reprs = Vec::new(); let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return reprs; }; @@ -278,7 +278,7 @@ impl AlignParser { fn parse(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) { match args { ArgParser::NoArgs | ArgParser::NameValue(_) => { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); } ArgParser::List(list) => { let Some(align) = list.single() else { @@ -315,7 +315,7 @@ impl AttributeParser for AlignParser { Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Method(MethodKind::Trait { body: true })), Allow(Target::Method(MethodKind::TraitImpl)), - Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: false })), // `#[align]` is inherited from trait methods Allow(Target::ForeignFn), ]); diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index d51fa2510b93..be4e2210c5ee 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,5 +1,8 @@ +use rustc_ast::{LitIntType, LitKind, MetaItemLit}; + use super::prelude::*; use super::util::parse_single_integer; +use crate::session_diagnostics::RustcScalableVectorCountOutOfRange; pub(crate) struct RustcMainParser; @@ -10,6 +13,36 @@ impl NoArgsAttributeParser for RustcMainParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain; } +pub(crate) struct RustcNeverReturnsNullPointerParser; + +impl NoArgsAttributeParser for RustcNeverReturnsNullPointerParser { + const PATH: &[Symbol] = &[sym::rustc_never_returns_null_ptr]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNeverReturnsNullPointer; +} +pub(crate) struct RustcNoImplicitAutorefsParser; + +impl NoArgsAttributeParser for RustcNoImplicitAutorefsParser { + const PATH: &[Symbol] = &[sym::rustc_no_implicit_autorefs]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitAutorefs; +} + pub(crate) struct RustcLayoutScalarValidRangeStartParser; impl SingleAttributeParser for RustcLayoutScalarValidRangeStartParser { @@ -40,6 +73,129 @@ impl SingleAttributeParser for RustcLayoutScalarValidRangeEndParser } } +pub(crate) struct RustcLegacyConstGenericsParser; + +impl SingleAttributeParser for RustcLegacyConstGenericsParser { + const PATH: &[Symbol] = &[sym::rustc_legacy_const_generics]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + const TEMPLATE: AttributeTemplate = template!(List: &["N"]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let ArgParser::List(meta_items) = args else { + cx.expected_list(cx.attr_span, args); + return None; + }; + + let mut parsed_indexes = ThinVec::new(); + let mut errored = false; + + for possible_index in meta_items.mixed() { + if let MetaItemOrLitParser::Lit(MetaItemLit { + kind: LitKind::Int(index, LitIntType::Unsuffixed), + .. + }) = possible_index + { + parsed_indexes.push((index.0 as usize, possible_index.span())); + } else { + cx.expected_integer_literal(possible_index.span()); + errored = true; + } + } + if errored { + return None; + } else if parsed_indexes.is_empty() { + cx.expected_at_least_one_argument(args.span()?); + return None; + } + + Some(AttributeKind::RustcLegacyConstGenerics { + fn_indexes: parsed_indexes, + attr_span: cx.attr_span, + }) + } +} + +pub(crate) struct RustcLintDiagnosticsParser; + +impl NoArgsAttributeParser for RustcLintDiagnosticsParser { + const PATH: &[Symbol] = &[sym::rustc_lint_diagnostics]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintDiagnostics; +} + +pub(crate) struct RustcLintOptDenyFieldAccessParser; + +impl SingleAttributeParser for RustcLintOptDenyFieldAccessParser { + const PATH: &[Symbol] = &[sym::rustc_lint_opt_deny_field_access]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Field)]); + const TEMPLATE: AttributeTemplate = template!(Word); + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + let Some(arg) = args.list().and_then(MetaItemListParser::single) else { + cx.expected_single_argument(cx.attr_span); + return None; + }; + + let MetaItemOrLitParser::Lit(MetaItemLit { kind: LitKind::Str(lint_message, _), .. }) = arg + else { + cx.expected_string_literal(arg.span(), arg.lit()); + return None; + }; + + Some(AttributeKind::RustcLintOptDenyFieldAccess { lint_message: *lint_message }) + } +} + +pub(crate) struct RustcLintOptTyParser; + +impl NoArgsAttributeParser for RustcLintOptTyParser { + const PATH: &[Symbol] = &[sym::rustc_lint_opt_ty]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintOptTy; +} + +pub(crate) struct RustcLintQueryInstabilityParser; + +impl NoArgsAttributeParser for RustcLintQueryInstabilityParser { + const PATH: &[Symbol] = &[sym::rustc_lint_query_instability]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintQueryInstability; +} + +pub(crate) struct RustcLintUntrackedQueryInformationParser; + +impl NoArgsAttributeParser for RustcLintUntrackedQueryInformationParser { + const PATH: &[Symbol] = &[sym::rustc_lint_untracked_query_information]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcLintUntrackedQueryInformation; +} + pub(crate) struct RustcObjectLifetimeDefaultParser; impl SingleAttributeParser for RustcObjectLifetimeDefaultParser { @@ -76,3 +232,29 @@ impl SingleAttributeParser for RustcSimdMonomorphizeLaneLimitParser Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?)) } } + +pub(crate) struct RustcScalableVectorParser; + +impl SingleAttributeParser for RustcScalableVectorParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_scalable_vector]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]); + const TEMPLATE: AttributeTemplate = template!(Word, List: &["count"]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { + if args.no_args().is_ok() { + return Some(AttributeKind::RustcScalableVector { + element_count: None, + span: cx.attr_span, + }); + } + + let n = parse_single_integer(cx, args)?; + let Ok(n) = n.try_into() else { + cx.emit_err(RustcScalableVectorCountOutOfRange { span: cx.attr_span, n }); + return None; + }; + Some(AttributeKind::RustcScalableVector { element_count: Some(n), span: cx.attr_span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 571cb884c1fc..6d4f77ef1751 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -295,7 +295,7 @@ pub(crate) fn parse_stability( let mut since = None; let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; @@ -315,11 +315,7 @@ pub(crate) fn parse_stability( insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())? } _ => { - cx.emit_err(session_diagnostics::UnknownMetaItem { - span: param_span, - item: param.path().to_string(), - expected: &["feature", "since"], - }); + cx.expected_specific_argument(param_span, &[sym::feature, sym::since]); return None; } } @@ -371,7 +367,7 @@ pub(crate) fn parse_unstability( let mut old_name = None; let ArgParser::List(list) = args else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; @@ -426,11 +422,17 @@ pub(crate) fn parse_unstability( insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())? } _ => { - cx.emit_err(session_diagnostics::UnknownMetaItem { - span: param.span(), - item: param.path().to_string(), - expected: &["feature", "reason", "issue", "soft", "implied_by", "old_name"], - }); + cx.expected_specific_argument( + param.span(), + &[ + sym::feature, + sym::reason, + sym::issue, + sym::soft, + sym::implied_by, + sym::old_name, + ], + ); return None; } } diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index a9b76021a989..ee5895a6efd0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -22,7 +22,7 @@ impl SingleAttributeParser for SkipDuringMethodDispatchParser { let mut array = false; let mut boxed_slice = false; let Some(args) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; if args.is_empty() { diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs index 4e3478abbf4f..431ba539b2ba 100644 --- a/compiler/rustc_attr_parsing/src/attributes/util.rs +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -43,7 +43,7 @@ pub(crate) fn parse_single_integer( args: &ArgParser, ) -> Option { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span); + cx.expected_list(cx.attr_span, args); return None; }; let Some(single) = list.single() else { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index e5cabd4f7142..df4970d8aa90 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -23,7 +23,7 @@ use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, EiiExternItemParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser, - TrackCallerParser, UsedParser, + ThreadLocalParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ @@ -50,6 +50,7 @@ use crate::attributes::macro_attrs::{ }; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; +use crate::attributes::no_link::NoLinkParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::path::PathParser as PathAttributeParser; use crate::attributes::pin_v2::PinV2Parser; @@ -59,8 +60,12 @@ use crate::attributes::proc_macro_attrs::{ use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ - RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcMainParser, - RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser, + RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, + RustcLegacyConstGenericsParser, RustcLintDiagnosticsParser, RustcLintOptDenyFieldAccessParser, + RustcLintOptTyParser, RustcLintQueryInstabilityParser, + RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcNeverReturnsNullPointerParser, + RustcNoImplicitAutorefsParser, RustcObjectLifetimeDefaultParser, RustcScalableVectorParser, + RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -77,7 +82,7 @@ use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, RefPathParser}; use crate::session_diagnostics::{ - AttributeParseError, AttributeParseErrorReason, ParsedDescription, UnknownMetaItem, + AttributeParseError, AttributeParseErrorReason, ParsedDescription, }; use crate::target_checking::AllowedTargets; @@ -208,7 +213,10 @@ attribute_parsers!( Single, Single, Single, + Single, + Single, Single, + Single, Single, Single, Single, @@ -238,6 +246,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, @@ -249,11 +258,18 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, + Single>, + Single>, + Single>, Single>, + Single>, + Single>, Single>, Single>, Single>, Single>, + Single>, Single>, Single>, Single>, @@ -426,13 +442,20 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { } impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { - pub(crate) fn unknown_key( + fn emit_parse_error( &self, span: Span, - found: String, - options: &[&'static str], + reason: AttributeParseErrorReason<'_>, ) -> ErrorGuaranteed { - self.emit_err(UnknownMetaItem { span, item: found, expected: options }) + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + path: self.attr_path.clone(), + description: self.parsed_description, + reason, + suggestions: self.suggestions(), + }) } /// error that a string literal was expected. @@ -444,133 +467,69 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, actual_literal: Option<&MetaItemLit>, ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedStringLiteral { + AttributeParseErrorReason::ExpectedStringLiteral { byte_string: actual_literal.and_then(|i| { i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) }), }, - suggestions: self.suggestions(), - }) + ) } pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedIntegerLiteral, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } - pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedList, - suggestions: self.suggestions(), - }) + pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed { + let span = match args { + ArgParser::NoArgs => span, + ArgParser::List(list) => list.span, + ArgParser::NameValue(nv) => nv.args_span(), + }; + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedList) } - pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span: args_span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedNoArgs, - suggestions: self.suggestions(), - }) + pub(crate) fn expected_list_or_no_args(&self, span: Span) -> ErrorGuaranteed { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedListOrNoArgs) + } + + pub(crate) fn expected_nv_or_no_args(&self, span: Span) -> ErrorGuaranteed { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValueOrNoArgs) + } + + pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNoArgs) } /// emit an error that a `name` was expected here pub(crate) fn expected_identifier(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedIdentifier, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIdentifier) } /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for /// a nicer error message talking about the specific name that was found lacking a value. pub(crate) fn expected_name_value(&self, span: Span, name: Option) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedNameValue(name), - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedNameValue(name)) } /// emit an error that a `name = value` pair was found where that name was already seen. pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::DuplicateKey(key), - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::DuplicateKey(key)) } /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) /// was expected *not* to be a literal, but instead a meta item. pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::UnexpectedLiteral, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::UnexpectedLiteral) } pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSingleArgument, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedSingleArgument) } pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument, - suggestions: self.suggestions(), - }) + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedAtLeastOneArgument) } /// produces an error along the lines of `expected one of [foo, meow]` @@ -579,19 +538,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSpecificArgument { + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, list: false, }, - suggestions: self.suggestions(), - }) + ) } /// produces an error along the lines of `expected one of [foo, meow] as an argument`. @@ -601,19 +555,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSpecificArgument { + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: false, list: true, }, - suggestions: self.suggestions(), - }) + ) } /// produces an error along the lines of `expected one of ["foo", "meow"]` @@ -622,19 +571,14 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { span: Span, possibilities: &[Symbol], ) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { + self.emit_parse_error( span, - attr_span: self.attr_span, - template: self.template.clone(), - path: self.attr_path.clone(), - description: self.parsed_description, - reason: AttributeParseErrorReason::ExpectedSpecificArgument { + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings: true, list: false, }, - suggestions: self.suggestions(), - }) + ) } pub(crate) fn warn_empty_attribute(&mut self, span: Span) { diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 09ecfaedb5ed..9551744d5ec5 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -177,7 +177,7 @@ impl ArgParser { match self { Self::NoArgs => Ok(()), Self::List(args) => Err(args.span), - Self::NameValue(args) => Err(args.eq_span.to(args.value_span)), + Self::NameValue(args) => Err(args.args_span()), } } } @@ -314,6 +314,10 @@ impl NameValueParser { pub fn value_as_str(&self) -> Option { self.value_as_lit().kind.str() } + + pub fn args_span(&self) -> Span { + self.eq_span.to(self.value_span) + } } fn expr_to_lit( diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index b50a7f92fcdc..73b65193fd34 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -64,26 +64,6 @@ pub(crate) struct DocAttributeNotAttribute { pub attribute: Symbol, } -/// Error code: E0541 -pub(crate) struct UnknownMetaItem<'a> { - pub span: Span, - pub item: String, - pub expected: &'a [&'a str], -} - -// Manual implementation to be able to format `expected` items correctly. -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::>(); - Diag::new(dcx, level, fluent::attr_parsing_unknown_meta_item) - .with_span(self.span) - .with_code(E0541) - .with_arg("item", self.item) - .with_arg("expected", expected.join(", ")) - .with_span_label(self.span, fluent::attr_parsing_label) - } -} - #[derive(Diagnostic)] #[diag(attr_parsing_missing_since, code = E0542)] pub(crate) struct MissingSince { @@ -400,15 +380,6 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } -#[derive(Diagnostic)] -#[diag(attr_parsing_ill_formed_attribute_input)] -pub(crate) struct IllFormedAttributeInputLint { - #[primary_span] - pub span: Span, - pub num_suggestions: usize, - pub suggestions: DiagArgValue, -} - #[derive(Diagnostic)] #[diag(attr_parsing_null_on_export, code = E0648)] pub(crate) struct NullOnExport { @@ -530,6 +501,15 @@ pub(crate) struct LinkOrdinalOutOfRange { pub ordinal: u128, } +#[derive(Diagnostic)] +#[diag(attr_parsing_rustc_scalable_vector_count_out_of_range)] +#[note] +pub(crate) struct RustcScalableVectorCountOutOfRange { + #[primary_span] + pub span: Span, + pub n: u128, +} + pub(crate) enum AttributeParseErrorReason<'a> { ExpectedNoArgs, ExpectedStringLiteral { @@ -539,6 +519,8 @@ pub(crate) enum AttributeParseErrorReason<'a> { ExpectedAtLeastOneArgument, ExpectedSingleArgument, ExpectedList, + ExpectedListOrNoArgs, + ExpectedNameValueOrNoArgs, UnexpectedLiteral, ExpectedNameValue(Option), DuplicateKey(Symbol), @@ -611,6 +593,12 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { AttributeParseErrorReason::ExpectedList => { diag.span_label(self.span, "expected this to be a list"); } + AttributeParseErrorReason::ExpectedListOrNoArgs => { + diag.span_label(self.span, "expected a list or no arguments here"); + } + AttributeParseErrorReason::ExpectedNameValueOrNoArgs => { + diag.span_label(self.span, "didn't expect a list here"); + } AttributeParseErrorReason::DuplicateKey(key) => { diag.span_label(self.span, format!("found `{key}` used as a key more than once")); diag.code(E0538); diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d82357fca2d4..8d61ffde116c 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2,7 +2,6 @@ // tidy-alphabetical-start #![allow(internal_features)] -#![deny(clippy::manual_let_else)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(file_buffered)] diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 80f1098de85e..6ed70b39c5b7 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1275,29 +1275,81 @@ impl<'tcx> RegionInferenceContext<'tcx> { shorter_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec>>, ) -> RegionRelationCheckResult { - if let Some(propagated_outlives_requirements) = propagated_outlives_requirements - // Shrink `longer_fr` until we find a non-local region (if we do). - // We'll call it `fr-` -- it's ever so slightly smaller than + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + // Shrink `longer_fr` until we find some non-local regions. + // We'll call them `longer_fr-` -- they are ever so slightly smaller than // `longer_fr`. - && let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr) - { - debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus); + let longer_fr_minus = self.universal_region_relations.non_local_lower_bounds(longer_fr); + + debug!("try_propagate_universal_region_error: fr_minus={:?}", longer_fr_minus); + + // If we don't find a any non-local regions, we should error out as there is nothing + // to propagate. + if longer_fr_minus.is_empty() { + return RegionRelationCheckResult::Error; + } let blame_constraint = self .best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr) .0; - // Grow `shorter_fr` until we find some non-local regions. (We - // always will.) We'll call them `shorter_fr+` -- they're ever - // so slightly larger than `shorter_fr`. + // Grow `shorter_fr` until we find some non-local regions. + // We will always find at least one: `'static`. We'll call + // them `shorter_fr+` -- they're ever so slightly larger + // than `shorter_fr`. let shorter_fr_plus = self.universal_region_relations.non_local_upper_bounds(shorter_fr); debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus); - for fr in shorter_fr_plus { - // Push the constraint `fr-: shorter_fr+` + + // We then create constraints `longer_fr-: shorter_fr+` that may or may not + // be propagated (see below). + let mut constraints = vec![]; + for fr_minus in longer_fr_minus { + for shorter_fr_plus in &shorter_fr_plus { + constraints.push((fr_minus, *shorter_fr_plus)); + } + } + + // We only need to propagate at least one of the constraints for + // soundness. However, we want to avoid arbitrary choices here + // and currently don't support returning OR constraints. + // + // If any of the `shorter_fr+` regions are already outlived by `longer_fr-`, + // we propagate only those. + // + // Consider this example (`'b: 'a` == `a -> b`), where we try to propagate `'d: 'a`: + // a --> b --> d + // \ + // \-> c + // Here, `shorter_fr+` of `'a` == `['b, 'c]`. + // Propagating `'d: 'b` is correct and should occur; `'d: 'c` is redundant because of + // `'d: 'b` and could reject valid code. + // + // So we filter the constraints to regions already outlived by `longer_fr-`, but if + // the filter yields an empty set, we fall back to the original one. + let subset: Vec<_> = constraints + .iter() + .filter(|&&(fr_minus, shorter_fr_plus)| { + self.eval_outlives(fr_minus, shorter_fr_plus) + }) + .copied() + .collect(); + let propagated_constraints = if subset.is_empty() { constraints } else { subset }; + debug!( + "try_propagate_universal_region_error: constraints={:?}", + propagated_constraints + ); + + assert!( + !propagated_constraints.is_empty(), + "Expected at least one constraint to propagate here" + ); + + for (fr_minus, fr_plus) in propagated_constraints { + // Push the constraint `long_fr-: shorter_fr+` propagated_outlives_requirements.push(ClosureOutlivesRequirement { subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: fr, + outlived_free_region: fr_plus, blame_span: blame_constraint.cause.span, category: blame_constraint.category, }); diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index d27a73535bab..279625cb87c9 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -94,28 +94,10 @@ impl UniversalRegionRelations<'_> { /// words, returns the largest (*) known region `fr1` that (a) is /// outlived by `fr` and (b) is not local. /// - /// (*) If there are multiple competing choices, we pick the "postdominating" - /// one. See `TransitiveRelation::postdom_upper_bound` for details. - pub(crate) fn non_local_lower_bound(&self, fr: RegionVid) -> Option { + /// (*) If there are multiple competing choices, we return all of them. + pub(crate) fn non_local_lower_bounds(&self, fr: RegionVid) -> Vec { debug!("non_local_lower_bound(fr={:?})", fr); - let lower_bounds = self.non_local_bounds(&self.outlives, fr); - - // In case we find more than one, reduce to one for - // convenience. This is to prevent us from generating more - // complex constraints, but it will cause spurious errors. - let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds); - - debug!("non_local_bound: post_dom={:?}", post_dom); - - post_dom.and_then(|post_dom| { - // If the mutual immediate postdom is not local, then - // there is no non-local result we can return. - if !self.universal_regions.is_local_free_region(post_dom) { - Some(post_dom) - } else { - None - } - }) + self.non_local_bounds(&self.outlives, fr) } /// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`. diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index dc8077b2a1ff..f11190b28105 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -1,22 +1,65 @@ use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{Expr, ast}; use rustc_attr_parsing as attr; use rustc_attr_parsing::{ CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select, }; -use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; +use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult}; use rustc_span::{Ident, Span, sym}; +use smallvec::SmallVec; use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; -/// Selects the first arm whose predicate evaluates to true. -fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> { - for (cfg, tt, arm_span) in branches.reachable { - if let EvalConfigResult::True = attr::eval_config_entry(&ecx.sess, &cfg) { - return Some((tt, arm_span)); - } - } +/// This intermediate structure is used to emit parse errors for the branches that are not chosen. +/// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only +/// keeps the parse result for the selected branch. +struct CfgSelectResult<'cx, 'sess> { + ecx: &'cx mut ExtCtxt<'sess>, + site_span: Span, + selected_tts: TokenStream, + selected_span: Span, + other_branches: CfgSelectBranches, +} - branches.wildcard.map(|(_, tt, span)| (tt, span)) +fn tts_to_mac_result<'cx, 'sess>( + ecx: &'cx mut ExtCtxt<'sess>, + site_span: Span, + tts: TokenStream, + span: Span, +) -> Box { + match ExpandResult::from_tts(ecx, tts, site_span, span, Ident::with_dummy_span(sym::cfg_select)) + { + ExpandResult::Ready(x) => x, + _ => unreachable!("from_tts always returns Ready"), + } +} + +macro_rules! forward_to_parser_any_macro { + ($method_name:ident, $ret_ty:ty) => { + fn $method_name(self: Box) -> Option<$ret_ty> { + let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } = *self; + + for (tts, span) in self.other_branches.into_iter_tts() { + let _ = tts_to_mac_result(ecx, site_span, tts, span).$method_name(); + } + + tts_to_mac_result(ecx, site_span, selected_tts, selected_span).$method_name() + } + }; +} + +impl<'cx, 'sess> MacResult for CfgSelectResult<'cx, 'sess> { + forward_to_parser_any_macro!(make_expr, Box); + forward_to_parser_any_macro!(make_stmts, SmallVec<[ast::Stmt; 1]>); + forward_to_parser_any_macro!(make_items, SmallVec<[Box; 1]>); + + forward_to_parser_any_macro!(make_impl_items, SmallVec<[Box; 1]>); + forward_to_parser_any_macro!(make_trait_impl_items, SmallVec<[Box; 1]>); + forward_to_parser_any_macro!(make_trait_items, SmallVec<[Box; 1]>); + forward_to_parser_any_macro!(make_foreign_items, SmallVec<[Box; 1]>); + + forward_to_parser_any_macro!(make_ty, Box); + forward_to_parser_any_macro!(make_pat, Box); } pub(super) fn expand_cfg_select<'cx>( @@ -31,7 +74,7 @@ pub(super) fn expand_cfg_select<'cx>( Some(ecx.ecfg.features), ecx.current_expansion.lint_node_id, ) { - Ok(branches) => { + Ok(mut branches) => { if let Some((underscore, _, _)) = branches.wildcard { // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt. for (predicate, _, _) in &branches.unreachable { @@ -44,14 +87,17 @@ pub(super) fn expand_cfg_select<'cx>( } } - if let Some((tts, arm_span)) = select_arm(ecx, branches) { - return ExpandResult::from_tts( + if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| { + matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True) + }) { + let mac = CfgSelectResult { ecx, - tts, - sp, - arm_span, - Ident::with_dummy_span(sym::cfg_select), - ); + selected_tts, + selected_span, + other_branches: branches, + site_span: sp, + }; + return ExpandResult::Ready(Box::new(mac)); } else { // Emit a compiler error when none of the predicates matched. let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp }); diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index b97cb1daec53..e29b29fc6ccb 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -1,11 +1,12 @@ use rustc_ast::token::{Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{ - DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, Stmt, StmtKind, ast, token, tokenstream, + Attribute, DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, MetaItem, Path, Stmt, StmtKind, + Visibility, ast, }; use rustc_ast_pretty::pprust::path_to_string; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ @@ -52,12 +53,12 @@ pub(crate) fn unsafe_eii( fn eii_( ecx: &mut ExtCtxt<'_>, - span: Span, + eii_attr_span: Span, meta_item: &ast::MetaItem, item: Annotatable, impl_unsafe: bool, ) -> Vec { - let span = ecx.with_def_site_ctxt(span); + let eii_attr_span = ecx.with_def_site_ctxt(eii_attr_span); let (item, stmt) = if let Annotatable::Item(item) = item { (item, false) @@ -67,7 +68,7 @@ fn eii_( (item.clone(), true) } else { ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span, + span: eii_attr_span, name: path_to_string(&meta_item.path), }); return vec![item]; @@ -77,113 +78,218 @@ fn eii_( let item = *item; - let ast::Item { attrs, id: _, span: item_span, vis, kind: ItemKind::Fn(mut func), tokens: _ } = - item - else { + let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } = item else { ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span, + span: eii_attr_span, name: path_to_string(&meta_item.path), }); return vec![Annotatable::Item(Box::new(item))]; }; - // Detect when this is the *second* eii attribute on an item. - let mut new_attrs = ThinVec::new(); - for i in attrs { - if i.has_name(sym::eii) { - ecx.dcx().emit_err(EiiOnlyOnce { - span: i.span, - first_span: span, - name: path_to_string(&meta_item.path), - }); - } else { - new_attrs.push(i); - } - } - let attrs = new_attrs; + let attrs_from_decl = + filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path); - let macro_name = if meta_item.is_word() { - func.ident - } else if let Some([first]) = meta_item.meta_item_list() - && let Some(m) = first.meta_item() - && m.path.segments.len() == 1 - { - m.path.segments[0].ident - } else { - ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument { - span: meta_item.span, - name: path_to_string(&meta_item.path), - }); + let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else { return vec![Annotatable::Item(orig_item)]; }; + // span of the declaring item without attributes + let item_span = func.sig.span; + // span of the eii attribute and the item below it, i.e. the full declaration + let decl_span = eii_attr_span.to(item_span); + let foreign_item_name = func.ident; + let mut return_items = Vec::new(); if func.body.is_some() { - let mut default_func = func.clone(); - func.body = None; - default_func.eii_impls.push(ast::EiiImpl { - node_id: DUMMY_NODE_ID, - eii_macro_path: ast::Path::from_ident(macro_name), - impl_safety: if impl_unsafe { ast::Safety::Unsafe(span) } else { ast::Safety::Default }, - span, - inner_span: macro_name.span, - is_default: true, // important! - }); - - return_items.push(Box::new(ast::Item { - attrs: ThinVec::new(), - id: ast::DUMMY_NODE_ID, - span, - vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None }, - kind: ast::ItemKind::Const(Box::new(ast::ConstItem { - ident: Ident { name: kw::Underscore, span }, - defaultness: ast::Defaultness::Final, - generics: ast::Generics::default(), - ty: Box::new(ast::Ty { - id: DUMMY_NODE_ID, - kind: ast::TyKind::Tup(ThinVec::new()), - span, - tokens: None, - }), - rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr { - id: DUMMY_NODE_ID, - kind: ast::ExprKind::Block( - Box::new(ast::Block { - stmts: thin_vec![ast::Stmt { - id: DUMMY_NODE_ID, - kind: ast::StmtKind::Item(Box::new(ast::Item { - attrs: thin_vec![], // FIXME: re-add some original attrs - id: DUMMY_NODE_ID, - span: item_span, - vis: ast::Visibility { - span, - kind: ast::VisibilityKind::Inherited, - tokens: None - }, - kind: ItemKind::Fn(default_func), - tokens: None, - })), - span - }], - id: DUMMY_NODE_ID, - rules: ast::BlockCheckMode::Default, - span, - tokens: None, - }), - None, - ), - span, - attrs: ThinVec::new(), - tokens: None, - }))), - define_opaque: None, - })), - tokens: None, - })) + return_items.push(Box::new(generate_default_impl( + &func, + impl_unsafe, + macro_name, + eii_attr_span, + item_span, + ))) } - let decl_span = span.to(func.sig.span); + return_items.push(Box::new(generate_foreign_item( + ecx, + eii_attr_span, + item_span, + *func, + vis, + &attrs_from_decl, + ))); + return_items.push(Box::new(generate_attribute_macro_to_implement( + ecx, + eii_attr_span, + macro_name, + foreign_item_name, + impl_unsafe, + decl_span, + ))); + + if stmt { + return_items + .into_iter() + .map(|i| { + Annotatable::Stmt(Box::new(Stmt { + id: DUMMY_NODE_ID, + kind: StmtKind::Item(i), + span: eii_attr_span, + })) + }) + .collect() + } else { + return_items.into_iter().map(|i| Annotatable::Item(i)).collect() + } +} + +/// Decide on the name of the macro that can be used to implement the EII. +/// This is either an explicitly given name, or the name of the item in the +/// declaration of the EII. +fn name_for_impl_macro( + ecx: &mut ExtCtxt<'_>, + func: &ast::Fn, + meta_item: &MetaItem, +) -> Result { + if meta_item.is_word() { + Ok(func.ident) + } else if let Some([first]) = meta_item.meta_item_list() + && let Some(m) = first.meta_item() + && m.path.segments.len() == 1 + { + Ok(m.path.segments[0].ident) + } else { + Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument { + span: meta_item.span, + name: path_to_string(&meta_item.path), + })) + } +} + +/// Ensure that in the list of attrs, there's only a single `eii` attribute. +fn filter_attrs_for_multiple_eii_attr( + ecx: &mut ExtCtxt<'_>, + attrs: ThinVec, + eii_attr_span: Span, + eii_attr_path: &Path, +) -> ThinVec { + attrs + .into_iter() + .filter(|i| { + if i.has_name(sym::eii) { + ecx.dcx().emit_err(EiiOnlyOnce { + span: i.span, + first_span: eii_attr_span, + name: path_to_string(eii_attr_path), + }); + false + } else { + true + } + }) + .collect() +} + +fn generate_default_impl( + func: &ast::Fn, + impl_unsafe: bool, + macro_name: Ident, + eii_attr_span: Span, + item_span: Span, +) -> ast::Item { + // FIXME: re-add some original attrs + let attrs = ThinVec::new(); + + let mut default_func = func.clone(); + default_func.eii_impls.push(EiiImpl { + node_id: DUMMY_NODE_ID, + inner_span: macro_name.span, + eii_macro_path: ast::Path::from_ident(macro_name), + impl_safety: if impl_unsafe { + ast::Safety::Unsafe(eii_attr_span) + } else { + ast::Safety::Default + }, + span: eii_attr_span, + is_default: true, + }); + + ast::Item { + attrs: ThinVec::new(), + id: ast::DUMMY_NODE_ID, + span: eii_attr_span, + vis: ast::Visibility { + span: eii_attr_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, + kind: ast::ItemKind::Const(Box::new(ast::ConstItem { + ident: Ident { name: kw::Underscore, span: eii_attr_span }, + defaultness: ast::Defaultness::Final, + generics: ast::Generics::default(), + ty: Box::new(ast::Ty { + id: DUMMY_NODE_ID, + kind: ast::TyKind::Tup(ThinVec::new()), + span: eii_attr_span, + tokens: None, + }), + rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr { + id: DUMMY_NODE_ID, + kind: ast::ExprKind::Block( + Box::new(ast::Block { + stmts: thin_vec![ast::Stmt { + id: DUMMY_NODE_ID, + kind: ast::StmtKind::Item(Box::new(ast::Item { + attrs, + id: DUMMY_NODE_ID, + span: item_span, + vis: ast::Visibility { + span: eii_attr_span, + kind: ast::VisibilityKind::Inherited, + tokens: None + }, + kind: ItemKind::Fn(Box::new(default_func)), + tokens: None, + })), + span: eii_attr_span + }], + id: DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + span: eii_attr_span, + tokens: None, + }), + None, + ), + span: eii_attr_span, + attrs: ThinVec::new(), + tokens: None, + }))), + define_opaque: None, + })), + tokens: None, + } +} + +/// Generates a foreign item, like +/// +/// ```rust, ignore +/// extern "…" { safe fn item(); } +/// ``` +fn generate_foreign_item( + ecx: &mut ExtCtxt<'_>, + eii_attr_span: Span, + item_span: Span, + mut func: ast::Fn, + vis: Visibility, + attrs_from_decl: &[Attribute], +) -> ast::Item { + let mut foreign_item_attrs = ThinVec::new(); + foreign_item_attrs.extend_from_slice(attrs_from_decl); + + // Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled. + // This attribute makes sure that we later know that this foreign item's symbol should not be. + foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_extern_item, eii_attr_span)); let abi = match func.sig.header.ext { // extern "X" fn => extern "X" {} @@ -196,85 +302,69 @@ fn eii_( suffix: None, symbol_unescaped: sym::Rust, style: ast::StrStyle::Cooked, - span, + span: eii_attr_span, }), }; // ABI has been moved to the extern {} block, so we remove it from the fn item. func.sig.header.ext = ast::Extern::None; + func.body = None; // And mark safe functions explicitly as `safe fn`. if func.sig.header.safety == ast::Safety::Default { func.sig.header.safety = ast::Safety::Safe(func.sig.span); } - // extern "…" { safe fn item(); } - let mut extern_item_attrs = attrs.clone(); - extern_item_attrs.push(ast::Attribute { - kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr { - item: ast::AttrItem { - unsafety: ast::Safety::Default, - // Add the rustc_eii_extern_item on the foreign item. Usually, foreign items are mangled. - // This attribute makes sure that we later know that this foreign item's symbol should not be. - path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)), - args: ast::AttrArgs::Empty, - tokens: None, - }, - tokens: None, - })), - id: ecx.sess.psess.attr_id_generator.mk_attr_id(), - style: ast::AttrStyle::Outer, - span, - }); - - let extern_block = Box::new(ast::Item { + ast::Item { attrs: ast::AttrVec::default(), id: ast::DUMMY_NODE_ID, - span, - vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None }, + span: eii_attr_span, + vis: ast::Visibility { + span: eii_attr_span, + kind: ast::VisibilityKind::Inherited, + tokens: None, + }, kind: ast::ItemKind::ForeignMod(ast::ForeignMod { - extern_span: span, - safety: ast::Safety::Unsafe(span), + extern_span: eii_attr_span, + safety: ast::Safety::Unsafe(eii_attr_span), abi, items: From::from([Box::new(ast::ForeignItem { - attrs: extern_item_attrs, + attrs: foreign_item_attrs, id: ast::DUMMY_NODE_ID, span: item_span, vis, - kind: ast::ForeignItemKind::Fn(func.clone()), + kind: ast::ForeignItemKind::Fn(Box::new(func.clone())), tokens: None, })]), }), tokens: None, - }); + } +} - let mut macro_attrs = attrs.clone(); - macro_attrs.push( - // #[builtin_macro(eii_shared_macro)] - ast::Attribute { - kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr { - item: ast::AttrItem { - unsafety: ast::Safety::Default, - path: ast::Path::from_ident(Ident::new(sym::rustc_builtin_macro, span)), - args: ast::AttrArgs::Delimited(ast::DelimArgs { - dspan: DelimSpan::from_single(span), - delim: Delimiter::Parenthesis, - tokens: TokenStream::new(vec![tokenstream::TokenTree::token_alone( - token::TokenKind::Ident(sym::eii_shared_macro, token::IdentIsRaw::No), - span, - )]), - }), - tokens: None, - }, - tokens: None, - })), - id: ecx.sess.psess.attr_id_generator.mk_attr_id(), - style: ast::AttrStyle::Outer, - span, - }, - ); +/// Generate a stub macro (a bit like in core) that will roughly look like: +/// +/// ```rust, ignore, example +/// // Since this a stub macro, the actual code that expands it lives in the compiler. +/// // This attribute tells the compiler that +/// #[builtin_macro(eii_shared_macro)] +/// // the metadata to link this macro to the generated foreign item. +/// #[eii_extern_target()] +/// macro macro_name { () => {} } +/// ``` +fn generate_attribute_macro_to_implement( + ecx: &mut ExtCtxt<'_>, + span: Span, + macro_name: Ident, + foreign_item_name: Ident, + impl_unsafe: bool, + decl_span: Span, +) -> ast::Item { + let mut macro_attrs = ThinVec::new(); - let macro_def = Box::new(ast::Item { + // #[builtin_macro(eii_shared_macro)] + macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span)); + + ast::Item { attrs: macro_attrs, id: ast::DUMMY_NODE_ID, span, @@ -305,33 +395,15 @@ fn eii_( ]), }), macro_rules: false, - // #[eii_extern_target(func.ident)] + // #[eii_extern_target(foreign_item_ident)] eii_extern_target: Some(ast::EiiExternTarget { - extern_item_path: ast::Path::from_ident(func.ident), + extern_item_path: ast::Path::from_ident(foreign_item_name), impl_unsafe, span: decl_span, }), }, ), tokens: None, - }); - - return_items.push(extern_block); - return_items.push(macro_def); - - if stmt { - return_items - .into_iter() - .map(|i| { - Annotatable::Stmt(Box::new(Stmt { - id: DUMMY_NODE_ID, - kind: StmtKind::Item(i), - span, - })) - }) - .collect() - } else { - return_items.into_iter().map(|i| Annotatable::Item(i)).collect() } } @@ -436,10 +508,10 @@ pub(crate) fn eii_shared_macro( f.eii_impls.push(EiiImpl { node_id: DUMMY_NODE_ID, + inner_span: meta_item.path.span, eii_macro_path: meta_item.path.clone(), impl_safety: meta_item.unsafety, span, - inner_span: meta_item.path.span, is_default, }); diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml index 0930b924d177..07d9af4a9b54 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: - name: Avoid installing rustc-dev run: | - sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain + sed -i 's/components.*/components = ["rustfmt"]/' rust-toolchain.toml rustfmt -v - name: Rustfmt @@ -88,7 +88,7 @@ jobs: uses: actions/cache@v4 with: path: build/cg_clif - key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-build-target-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }} - name: Set MinGW as the default toolchain if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' @@ -158,7 +158,7 @@ jobs: uses: actions/cache@v4 with: path: build/cg_clif - key: ${{ runner.os }}-x86_64-unknown-linux-gnu-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + key: ${{ runner.os }}-x86_64-unknown-linux-gnu-cargo-build-target-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }} - name: Install hyperfine run: | @@ -207,7 +207,7 @@ jobs: uses: actions/cache@v4 with: path: build/cg_clif - key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-dist-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-dist-cargo-build-target-${{ hashFiles('rust-toolchain.toml', '**/Cargo.lock') }} - name: Set MinGW as the default toolchain if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml index 9253ab96353c..b22725fdc9d4 100644 --- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml +++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml @@ -20,7 +20,7 @@ jobs: uses: actions/cache@v4 with: path: build/cg_clif - key: ${{ runner.os }}-rustc-test-cargo-build-target-${{ hashFiles('rust-toolchain', 'Cargo.lock') }} + key: ${{ runner.os }}-rustc-test-cargo-build-target-${{ hashFiles('rust-toolchain.toml', 'Cargo.lock') }} - name: Test run: ./scripts/test_bootstrap.sh @@ -40,7 +40,7 @@ jobs: uses: actions/cache@v4 with: path: build/cg_clif - key: ${{ runner.os }}-rustc-test-cargo-build-target-${{ hashFiles('rust-toolchain', 'Cargo.lock') }} + key: ${{ runner.os }}-rustc-test-cargo-build-target-${{ hashFiles('rust-toolchain.toml', 'Cargo.lock') }} - name: Install ripgrep run: | diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index c569ef0ef829..33db75f0943a 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -259,6 +259,9 @@ unsafe fn test_simd() { test_mm_cvttps_epi32(); test_mm_cvtsi128_si64(); + #[cfg(not(jit))] + test_mm_cvtps_ph(); + test_mm_extract_epi8(); test_mm_insert_epi16(); test_mm_shuffle_epi8(); @@ -558,6 +561,21 @@ unsafe fn test_mm_cvttps_epi32() { } } +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "f16c")] +#[cfg(not(jit))] +unsafe fn test_mm_cvtps_ph() { + const F16_ONE: i16 = 0x3c00; + const F16_TWO: i16 = 0x4000; + const F16_THREE: i16 = 0x4200; + const F16_FOUR: i16 = 0x4400; + + let a = _mm_set_ps(1.0, 2.0, 3.0, 4.0); + let r = _mm_cvtps_ph::<_MM_FROUND_CUR_DIRECTION>(a); + let e = _mm_set_epi16(0, 0, 0, 0, F16_ONE, F16_TWO, F16_THREE, F16_FOUR); + assert_eq_m128i(r, e); +} + fn test_checked_mul() { let u: Option = u8::from_str_radix("1000", 10).ok(); assert_eq!(u, None); diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain deleted file mode 100644 index 461dbcdb0fb5..000000000000 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "nightly-2025-12-08" -components = ["rust-src", "rustc-dev", "llvm-tools"] -profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain.toml b/compiler/rustc_codegen_cranelift/rust-toolchain.toml new file mode 100644 index 000000000000..6ce49eb4ccf0 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2025-12-18" +components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"] +profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh index 1a82193303e2..e26be307328f 100755 --- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -22,8 +22,7 @@ case $1 in "prepare") echo "=> Installing new nightly" rustup toolchain install --profile minimal "nightly-${TOOLCHAIN}" # Sanity check to see if the nightly exists - sed -i "s/\"nightly-.*\"/\"nightly-${TOOLCHAIN}\"/" rust-toolchain - rustup component add rustfmt || true + sed -i "s/\"nightly-.*\"/\"nightly-${TOOLCHAIN}\"/" rust-toolchain.toml echo "=> Uninstalling all old nightlies" for nightly in $(rustup toolchain list | grep nightly | grep -v "$TOOLCHAIN" | grep -v nightly-x86_64); do @@ -35,7 +34,7 @@ case $1 in ./y.sh prepare ;; "commit") - git add rust-toolchain + git add rust-toolchain.toml git commit -m "Rustup to $(rustc -V)" ;; "push") diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index b5af585a732e..b25269d1430a 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -35,6 +35,7 @@ git checkout -- tests/ui/entry-point/auxiliary/bad_main_functions.rs rm tests/ui/asm/x86_64/evex512-implicit-feature.rs # unimplemented AVX512 x86 vendor intrinsic rm tests/ui/simd/dont-invalid-bitcast-x86_64.rs # unimplemented llvm.x86.sse41.round.ps rm tests/ui/simd/intrinsic/generic-arithmetic-pass.rs # unimplemented simd_funnel_{shl,shr} +rm -r tests/ui/scalable-vectors # scalable vectors are unsupported # exotic linkages rm tests/incremental/hashes/function_interfaces.rs @@ -53,23 +54,29 @@ rm tests/ui/sanitizer/kcfi-c-variadic.rs # same rm tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs # variadics for calling conventions other than C unsupported rm tests/ui/delegation/fn-header.rs +# inline assembly features +rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't work entirely correctly +rm tests/ui/asm/global-asm-mono-sym-fn.rs # same +rm tests/ui/asm/naked-asm-mono-sym-fn.rs # same +rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported +rm tests/ui/asm/label-operand.rs # same +rm tests/ui/asm/may_unwind.rs # asm unwinding not supported +rm tests/ui/asm/aarch64/may_unwind.rs # same + # misc unimplemented things rm tests/ui/target-feature/missing-plusminus.rs # error not implemented rm -r tests/run-make/repr128-dwarf # debuginfo test rm -r tests/run-make/split-debuginfo # same rm -r tests/run-make/target-specs # i686 not supported by Cranelift rm -r tests/run-make/mismatching-target-triples # same -rm tests/ui/asm/x86_64/issue-96797.rs # const and sym inline asm operands don't work entirely correctly -rm tests/ui/asm/global-asm-mono-sym-fn.rs # same -rm tests/ui/asm/naked-asm-mono-sym-fn.rs # same -rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported -rm tests/ui/asm/label-operand.rs # same rm tests/ui/simd/simd-bitmask-notpow2.rs # non-pow-of-2 simd vector sizes rm -r tests/run-make/used-proc-macro # used(linker) isn't supported yet rm tests/ui/linking/no-gc-encapsulation-symbols.rs # same rm tests/ui/attributes/fn-align-dyn.rs # per-function alignment not supported rm -r tests/ui/explicit-tail-calls # tail calls rm -r tests/run-make/pointer-auth-link-with-c # pointer auth +rm -r tests/ui/eii # EII not yet implemented +rm -r tests/run-make/forced-unwind-terminate-pof # forced unwinding doesn't take precedence # requires LTO rm -r tests/run-make/cdylib @@ -78,6 +85,7 @@ rm -r tests/run-make/lto-* rm -r tests/run-make/reproducible-build-2 rm -r tests/run-make/no-builtins-lto rm -r tests/run-make/reachable-extern-fn-available-lto +rm -r tests/run-make/no-builtins-linker-plugin-lto # coverage instrumentation rm tests/ui/consts/precise-drop-with-coverage.rs @@ -87,6 +95,7 @@ rm -r tests/ui/instrument-coverage/ # ================== rm tests/ui/codegen/issue-28950.rs # depends on stack size optimizations rm tests/ui/codegen/init-large-type.rs # same +rm tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs # same rm tests/ui/statics/const_generics.rs # tests an optimization rm tests/ui/linking/executable-no-mangle-strip.rs # requires --gc-sections to work for statics @@ -143,6 +152,15 @@ rm tests/ui/errors/remap-path-prefix-sysroot.rs # different sysroot source path rm -r tests/run-make/export/extern-opt # something about rustc version mismatches rm -r tests/run-make/export # same rm -r tests/ui/compiletest-self-test/compile-flags-incremental.rs # needs compiletest compiled with panic=unwind +rm tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs # something going wrong with stdlib source remapping +rm tests/ui/consts/miri_unleashed/drop.rs # same +rm tests/ui/error-emitter/multiline-removal-suggestion.rs # same +rm tests/ui/lint/lint-const-item-mutation.rs # same +rm tests/ui/lint/use-redundant/use-redundant-issue-71450.rs # same +rm tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs # same +rm tests/ui/specialization/const_trait_impl.rs # same +rm tests/ui/thir-print/offset_of.rs # same +rm tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs # same # genuine bugs # ============ @@ -157,6 +175,7 @@ rm tests/ui/lint/non-snake-case/lint-non-snake-case-crate.rs # same rm tests/ui/async-await/async-drop/async-drop-initial.rs # same (rust-lang/rust#140493) rm -r tests/ui/codegen/equal-pointers-unequal # make incorrect assumptions about the location of stack variables rm -r tests/run-make-cargo/rustdoc-scrape-examples-paths # FIXME(rust-lang/rust#145580) incr comp bug +rm -r tests/incremental/extern_static/issue-49153.rs # assumes reference to undefined static gets optimized away rm tests/ui/intrinsics/panic-uninitialized-zeroed.rs # really slow with unoptimized libstd rm tests/ui/process/process-panic-after-fork.rs # same diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 9dba46363936..3a8ca25a5fc0 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -190,7 +190,7 @@ fn dep_symbol_lookup_fn( diag.emit(); } Linkage::Dynamic => { - dylib_paths.push(src.dylib.as_ref().unwrap().0.clone()); + dylib_paths.push(src.dylib.as_ref().unwrap().clone()); } } } diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index 08cabe9d695c..ac0da06cbb8e 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -857,19 +857,9 @@ fn call_inline_asm<'tcx>( fn asm_clif_type<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> Option { match ty.kind() { - // Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151 + // Adapted from https://github.com/rust-lang/rust/blob/df44a57fd29fca899ce473f85ed64efd0708dd7c/compiler/rustc_hir_typeck/src/inline_asm.rs#L180-L183 ty::Adt(adt, args) if fx.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => { - let fields = &adt.non_enum_variant().fields; - let ty = fields[FieldIdx::ONE].ty(fx.tcx, args); - let ty::Adt(ty, args) = ty.kind() else { - unreachable!("expected first field of `MaybeUninit` to be an ADT") - }; - assert!( - ty.is_manually_drop(), - "expected first field of `MaybeUninit` to be `ManuallyDrop`" - ); - let fields = &ty.non_enum_variant().fields; - let ty = fields[FieldIdx::ZERO].ty(fx.tcx, args); + let ty = args.type_at(0); fx.clif_type(ty) } _ => fx.clif_type(ty), diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs index 37fbe4be1b0f..61f48fa97743 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs @@ -1313,6 +1313,35 @@ pub(super) fn codegen_x86_llvm_intrinsic_call<'tcx>( ret.write_cvalue_transmute(fx, res); } + "llvm.x86.vcvtps2ph.128" => { + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_ph + intrinsic_args!(fx, args => (a, _imm8); intrinsic); + let a = a.load_scalar(fx); + + let imm8 = + if let Some(imm8) = crate::constant::mir_operand_get_const_val(fx, &args[1].node) { + imm8 + } else { + fx.tcx + .dcx() + .span_fatal(span, "Index argument for `_mm_cvtps_ph` is not a constant"); + }; + + let imm8 = imm8.to_u32(); + + codegen_inline_asm_inner( + fx, + &[InlineAsmTemplatePiece::String(format!("vcvtps2ph xmm0, xmm0, {imm8}").into())], + &[CInlineAsmOperand::InOut { + reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)), + _late: true, + in_value: a, + out_place: Some(ret), + }], + InlineAsmOptions::NOSTACK | InlineAsmOptions::PURE | InlineAsmOptions::NOMEM, + ); + } + _ => { fx.tcx .dcx() diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs index 2878fa7aa298..65779b38ad1c 100644 --- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -10,7 +10,7 @@ //! function u0:22(i64) -> i8, i8 system_v { //! ; symbol _ZN97_$LT$example..IsNotEmpty$u20$as$u20$mini_core..FnOnce$LT$$LP$$RF$$RF$$u5b$u16$u5d$$C$$RP$$GT$$GT$9call_once17hd361e9f5c3d1c4deE //! ; instance Instance { def: Item(DefId(0:42 ~ example[3895]::{impl#0}::call_once)), args: ['{erased}, '{erased}] } -//! ; abi FnAbi { args: [ArgAbi { layout: TyAndLayout { ty: IsNotEmpty, layout: Layout { size: Size(0 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: Memory { sized: true }, fields: Arbitrary { offsets: [], memory_index: [] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 12266848898570219025 } }, mode: Ignore }, ArgAbi { layout: TyAndLayout { ty: &&[u16], layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) }], ret: ArgAbi { layout: TyAndLayout { ty: (u8, u8), layout: Layout { size: Size(2 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=255 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }), fields: Arbitrary { offsets: [Size(0 bytes), Size(1 bytes)], memory_index: [0, 1] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 71776127651151873 } }, mode: Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) }, c_variadic: false, fixed_count: 1, conv: Rust, can_unwind: false } +//! ; abi FnAbi { args: [ArgAbi { layout: TyAndLayout { ty: IsNotEmpty, layout: Layout { size: Size(0 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: Memory { sized: true }, fields: Arbitrary { offsets: [], in_memory_order: [] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 12266848898570219025 } }, mode: Ignore }, ArgAbi { layout: TyAndLayout { ty: &&[u16], layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) }], ret: ArgAbi { layout: TyAndLayout { ty: (u8, u8), layout: Layout { size: Size(2 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=255 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }), fields: Arbitrary { offsets: [Size(0 bytes), Size(1 bytes)], in_memory_order: [0, 1] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 71776127651151873 } }, mode: Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) }, c_variadic: false, fixed_count: 1, conv: Rust, can_unwind: false } //! //! ; kind loc.idx param pass mode ty //! ; ssa _0 (u8, u8) 2b 1 var=(0, 1) @@ -41,7 +41,7 @@ //! ; //! ; _0 = >::call_mut(move _3, copy _2) //! v2 = stack_load.i64 ss0 -//! ; abi: FnAbi { args: [ArgAbi { layout: TyAndLayout { ty: &mut IsNotEmpty, layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(1 bytes)) }) }, ArgAbi { layout: TyAndLayout { ty: &&[u16], layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) }], ret: ArgAbi { layout: TyAndLayout { ty: (u8, u8), layout: Layout { size: Size(2 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=255 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }), fields: Arbitrary { offsets: [Size(0 bytes), Size(1 bytes)], memory_index: [0, 1] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 71776127651151873 } }, mode: Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) }, c_variadic: false, fixed_count: 1, conv: Rust, can_unwind: false } +//! ; abi: FnAbi { args: [ArgAbi { layout: TyAndLayout { ty: &mut IsNotEmpty, layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(1 bytes)) }) }, ArgAbi { layout: TyAndLayout { ty: &&[u16], layout: Layout { size: Size(8 bytes), align: AbiAndPrefAlign { abi: Align(8 bytes), pref: Align(8 bytes) }, backend_repr: Scalar(Initialized { value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), fields: Primitive, largest_niche: Some(Niche { offset: Size(0 bytes), value: Pointer(AddressSpace(0)), valid_range: 1..=18446744073709551615 }), uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(8 bytes), randomization_seed: 281492156579847 } }, mode: Direct(ArgAttributes { regular: NonNull | NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: Some(Align(8 bytes)) }) }], ret: ArgAbi { layout: TyAndLayout { ty: (u8, u8), layout: Layout { size: Size(2 bytes), align: AbiAndPrefAlign { abi: Align(1 bytes), pref: Align(8 bytes) }, backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=255 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }), fields: Arbitrary { offsets: [Size(0 bytes), Size(1 bytes)], in_memory_order: [0, 1] }, largest_niche: None, uninhabited: false, variants: Single { index: 0 }, max_repr_align: None, unadjusted_abi_align: Align(1 bytes), randomization_seed: 71776127651151873 } }, mode: Pair(ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }, ArgAttributes { regular: NoUndef, arg_ext: None, pointee_size: Size(0 bytes), pointee_align: None }) }, c_variadic: false, fixed_count: 1, conv: Rust, can_unwind: false } //! v3, v4 = call fn0(v1, v2) ; v1 = 1 //! v5 -> v3 //! v6 -> v4 diff --git a/compiler/rustc_codegen_cranelift/triagebot.toml b/compiler/rustc_codegen_cranelift/triagebot.toml index 13da0a87def3..eb0c7b011f60 100644 --- a/compiler/rustc_codegen_cranelift/triagebot.toml +++ b/compiler/rustc_codegen_cranelift/triagebot.toml @@ -2,6 +2,3 @@ # Prevents un-canonicalized issue links (to avoid wrong issues being linked in r-l/rust) [issue-links] - -# Prevents mentions in commits to avoid users being spammed -[no-mentions] diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs index 27476465d740..9b7ee8380ca5 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/build.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs @@ -111,14 +111,20 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu // Symlink libgccjit.so to sysroot. let lib_path = start_dir.join("sysroot").join("lib"); + let rustlib_target_path = lib_path + .join("rustlib") + .join(&config.host_triple) + .join("codegen-backends") + .join("lib") + .join(&config.target_triple); let libgccjit_path = PathBuf::from(config.gcc_path.as_ref().expect("libgccjit should be set by this point")) .join("libgccjit.so"); - let libgccjit_in_sysroot_path = lib_path.join("libgccjit.so"); + let libgccjit_in_sysroot_path = rustlib_target_path.join("libgccjit.so"); // First remove the file to be able to create the symlink even when the file already exists. let _ = fs::remove_file(&libgccjit_in_sysroot_path); - create_dir(&lib_path)?; - symlink(libgccjit_path, libgccjit_in_sysroot_path) + create_dir(&rustlib_target_path)?; + symlink(libgccjit_path, &libgccjit_in_sysroot_path) .map_err(|error| format!("Cannot create symlink for libgccjit.so: {}", error))?; let library_dir = start_dir.join("sysroot_src").join("library"); diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index f9645451e964..86ae738d4483 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-11-24" +channel = "nightly-2025-12-20" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index df1e64c75b96..79228c20d292 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -943,6 +943,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { .get_address(self.location) } + fn scalable_alloca(&mut self, _elt: u64, _align: Align, _element_ty: Ty<'_>) -> RValue<'gcc> { + todo!() + } + fn load(&mut self, pointee_ty: Type<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { let block = self.llbb(); let function = block.get_function(); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs index bb8bcbf66f38..43e7c352c34a 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs @@ -70,10 +70,6 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "sve.sm4e" => "__builtin_sve_svsm4e_u32", "sve.sm4ekey" => "__builtin_sve_svsm4ekey_u32", "sve.wrffr" => "__builtin_sve_svwrffr", - "tcancel" => "__builtin_arm_tcancel", - "tcommit" => "__builtin_arm_tcommit", - "tstart" => "__builtin_arm_tstart", - "ttest" => "__builtin_arm_ttest", _ => unimplemented!("***** unsupported LLVM intrinsic {full_name}"), } } @@ -1632,6 +1628,14 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "V6.vabs.f8.128B" => "__builtin_HEXAGON_V6_vabs_f8_128B", "V6.vabs.hf" => "__builtin_HEXAGON_V6_vabs_hf", "V6.vabs.hf.128B" => "__builtin_HEXAGON_V6_vabs_hf_128B", + "V6.vabs.qf16.hf" => "__builtin_HEXAGON_V6_vabs_qf16_hf", + "V6.vabs.qf16.hf.128B" => "__builtin_HEXAGON_V6_vabs_qf16_hf_128B", + "V6.vabs.qf16.qf16" => "__builtin_HEXAGON_V6_vabs_qf16_qf16", + "V6.vabs.qf16.qf16.128B" => "__builtin_HEXAGON_V6_vabs_qf16_qf16_128B", + "V6.vabs.qf32.qf32" => "__builtin_HEXAGON_V6_vabs_qf32_qf32", + "V6.vabs.qf32.qf32.128B" => "__builtin_HEXAGON_V6_vabs_qf32_qf32_128B", + "V6.vabs.qf32.sf" => "__builtin_HEXAGON_V6_vabs_qf32_sf", + "V6.vabs.qf32.sf.128B" => "__builtin_HEXAGON_V6_vabs_qf32_sf_128B", "V6.vabs.sf" => "__builtin_HEXAGON_V6_vabs_sf", "V6.vabs.sf.128B" => "__builtin_HEXAGON_V6_vabs_sf_128B", "V6.vabsb" => "__builtin_HEXAGON_V6_vabsb", @@ -1744,6 +1748,8 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "V6.vaddwsat.128B" => "__builtin_HEXAGON_V6_vaddwsat_128B", "V6.vaddwsat.dv" => "__builtin_HEXAGON_V6_vaddwsat_dv", "V6.vaddwsat.dv.128B" => "__builtin_HEXAGON_V6_vaddwsat_dv_128B", + "V6.valign4" => "__builtin_HEXAGON_V6_valign4", + "V6.valign4.128B" => "__builtin_HEXAGON_V6_valign4_128B", "V6.valignb" => "__builtin_HEXAGON_V6_valignb", "V6.valignb.128B" => "__builtin_HEXAGON_V6_valignb_128B", "V6.valignbi" => "__builtin_HEXAGON_V6_valignbi", @@ -1862,14 +1868,30 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "V6.vcl0w.128B" => "__builtin_HEXAGON_V6_vcl0w_128B", "V6.vcombine" => "__builtin_HEXAGON_V6_vcombine", "V6.vcombine.128B" => "__builtin_HEXAGON_V6_vcombine_128B", + "V6.vconv.bf.qf32" => "__builtin_HEXAGON_V6_vconv_bf_qf32", + "V6.vconv.bf.qf32.128B" => "__builtin_HEXAGON_V6_vconv_bf_qf32_128B", + "V6.vconv.f8.qf16" => "__builtin_HEXAGON_V6_vconv_f8_qf16", + "V6.vconv.f8.qf16.128B" => "__builtin_HEXAGON_V6_vconv_f8_qf16_128B", "V6.vconv.h.hf" => "__builtin_HEXAGON_V6_vconv_h_hf", "V6.vconv.h.hf.128B" => "__builtin_HEXAGON_V6_vconv_h_hf_128B", + "V6.vconv.h.hf.rnd" => "__builtin_HEXAGON_V6_vconv_h_hf_rnd", + "V6.vconv.h.hf.rnd.128B" => "__builtin_HEXAGON_V6_vconv_h_hf_rnd_128B", "V6.vconv.hf.h" => "__builtin_HEXAGON_V6_vconv_hf_h", "V6.vconv.hf.h.128B" => "__builtin_HEXAGON_V6_vconv_hf_h_128B", "V6.vconv.hf.qf16" => "__builtin_HEXAGON_V6_vconv_hf_qf16", "V6.vconv.hf.qf16.128B" => "__builtin_HEXAGON_V6_vconv_hf_qf16_128B", "V6.vconv.hf.qf32" => "__builtin_HEXAGON_V6_vconv_hf_qf32", "V6.vconv.hf.qf32.128B" => "__builtin_HEXAGON_V6_vconv_hf_qf32_128B", + "V6.vconv.qf16.f8" => "__builtin_HEXAGON_V6_vconv_qf16_f8", + "V6.vconv.qf16.f8.128B" => "__builtin_HEXAGON_V6_vconv_qf16_f8_128B", + "V6.vconv.qf16.hf" => "__builtin_HEXAGON_V6_vconv_qf16_hf", + "V6.vconv.qf16.hf.128B" => "__builtin_HEXAGON_V6_vconv_qf16_hf_128B", + "V6.vconv.qf16.qf16" => "__builtin_HEXAGON_V6_vconv_qf16_qf16", + "V6.vconv.qf16.qf16.128B" => "__builtin_HEXAGON_V6_vconv_qf16_qf16_128B", + "V6.vconv.qf32.qf32" => "__builtin_HEXAGON_V6_vconv_qf32_qf32", + "V6.vconv.qf32.qf32.128B" => "__builtin_HEXAGON_V6_vconv_qf32_qf32_128B", + "V6.vconv.qf32.sf" => "__builtin_HEXAGON_V6_vconv_qf32_sf", + "V6.vconv.qf32.sf.128B" => "__builtin_HEXAGON_V6_vconv_qf32_sf_128B", "V6.vconv.sf.qf32" => "__builtin_HEXAGON_V6_vconv_sf_qf32", "V6.vconv.sf.qf32.128B" => "__builtin_HEXAGON_V6_vconv_sf_qf32_128B", "V6.vconv.sf.w" => "__builtin_HEXAGON_V6_vconv_sf_w", @@ -1984,6 +2006,22 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "V6.veqh.or.128B" => "__builtin_HEXAGON_V6_veqh_or_128B", "V6.veqh.xor" => "__builtin_HEXAGON_V6_veqh_xor", "V6.veqh.xor.128B" => "__builtin_HEXAGON_V6_veqh_xor_128B", + "V6.veqhf" => "__builtin_HEXAGON_V6_veqhf", + "V6.veqhf.128B" => "__builtin_HEXAGON_V6_veqhf_128B", + "V6.veqhf.and" => "__builtin_HEXAGON_V6_veqhf_and", + "V6.veqhf.and.128B" => "__builtin_HEXAGON_V6_veqhf_and_128B", + "V6.veqhf.or" => "__builtin_HEXAGON_V6_veqhf_or", + "V6.veqhf.or.128B" => "__builtin_HEXAGON_V6_veqhf_or_128B", + "V6.veqhf.xor" => "__builtin_HEXAGON_V6_veqhf_xor", + "V6.veqhf.xor.128B" => "__builtin_HEXAGON_V6_veqhf_xor_128B", + "V6.veqsf" => "__builtin_HEXAGON_V6_veqsf", + "V6.veqsf.128B" => "__builtin_HEXAGON_V6_veqsf_128B", + "V6.veqsf.and" => "__builtin_HEXAGON_V6_veqsf_and", + "V6.veqsf.and.128B" => "__builtin_HEXAGON_V6_veqsf_and_128B", + "V6.veqsf.or" => "__builtin_HEXAGON_V6_veqsf_or", + "V6.veqsf.or.128B" => "__builtin_HEXAGON_V6_veqsf_or_128B", + "V6.veqsf.xor" => "__builtin_HEXAGON_V6_veqsf_xor", + "V6.veqsf.xor.128B" => "__builtin_HEXAGON_V6_veqsf_xor_128B", "V6.veqw" => "__builtin_HEXAGON_V6_veqw", "V6.veqw.128B" => "__builtin_HEXAGON_V6_veqw_128B", "V6.veqw.and" => "__builtin_HEXAGON_V6_veqw_and", @@ -2096,6 +2134,14 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "V6.vgtw.or.128B" => "__builtin_HEXAGON_V6_vgtw_or_128B", "V6.vgtw.xor" => "__builtin_HEXAGON_V6_vgtw_xor", "V6.vgtw.xor.128B" => "__builtin_HEXAGON_V6_vgtw_xor_128B", + "V6.vilog2.hf" => "__builtin_HEXAGON_V6_vilog2_hf", + "V6.vilog2.hf.128B" => "__builtin_HEXAGON_V6_vilog2_hf_128B", + "V6.vilog2.qf16" => "__builtin_HEXAGON_V6_vilog2_qf16", + "V6.vilog2.qf16.128B" => "__builtin_HEXAGON_V6_vilog2_qf16_128B", + "V6.vilog2.qf32" => "__builtin_HEXAGON_V6_vilog2_qf32", + "V6.vilog2.qf32.128B" => "__builtin_HEXAGON_V6_vilog2_qf32_128B", + "V6.vilog2.sf" => "__builtin_HEXAGON_V6_vilog2_sf", + "V6.vilog2.sf.128B" => "__builtin_HEXAGON_V6_vilog2_sf_128B", "V6.vinsertwr" => "__builtin_HEXAGON_V6_vinsertwr", "V6.vinsertwr.128B" => "__builtin_HEXAGON_V6_vinsertwr_128B", "V6.vlalignb" => "__builtin_HEXAGON_V6_vlalignb", @@ -2350,6 +2396,14 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "V6.vnavgub.128B" => "__builtin_HEXAGON_V6_vnavgub_128B", "V6.vnavgw" => "__builtin_HEXAGON_V6_vnavgw", "V6.vnavgw.128B" => "__builtin_HEXAGON_V6_vnavgw_128B", + "V6.vneg.qf16.hf" => "__builtin_HEXAGON_V6_vneg_qf16_hf", + "V6.vneg.qf16.hf.128B" => "__builtin_HEXAGON_V6_vneg_qf16_hf_128B", + "V6.vneg.qf16.qf16" => "__builtin_HEXAGON_V6_vneg_qf16_qf16", + "V6.vneg.qf16.qf16.128B" => "__builtin_HEXAGON_V6_vneg_qf16_qf16_128B", + "V6.vneg.qf32.qf32" => "__builtin_HEXAGON_V6_vneg_qf32_qf32", + "V6.vneg.qf32.qf32.128B" => "__builtin_HEXAGON_V6_vneg_qf32_qf32_128B", + "V6.vneg.qf32.sf" => "__builtin_HEXAGON_V6_vneg_qf32_sf", + "V6.vneg.qf32.sf.128B" => "__builtin_HEXAGON_V6_vneg_qf32_sf_128B", "V6.vnormamth" => "__builtin_HEXAGON_V6_vnormamth", "V6.vnormamth.128B" => "__builtin_HEXAGON_V6_vnormamth_128B", "V6.vnormamtw" => "__builtin_HEXAGON_V6_vnormamtw", @@ -2684,6 +2738,24 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "iocsrwr.d" => "__builtin_loongarch_iocsrwr_d", "iocsrwr.h" => "__builtin_loongarch_iocsrwr_h", "iocsrwr.w" => "__builtin_loongarch_iocsrwr_w", + "lasx.cast.128" => "__builtin_lasx_cast_128", + "lasx.cast.128.d" => "__builtin_lasx_cast_128_d", + "lasx.cast.128.s" => "__builtin_lasx_cast_128_s", + "lasx.concat.128" => "__builtin_lasx_concat_128", + "lasx.concat.128.d" => "__builtin_lasx_concat_128_d", + "lasx.concat.128.s" => "__builtin_lasx_concat_128_s", + "lasx.extract.128.hi" => "__builtin_lasx_extract_128_hi", + "lasx.extract.128.hi.d" => "__builtin_lasx_extract_128_hi_d", + "lasx.extract.128.hi.s" => "__builtin_lasx_extract_128_hi_s", + "lasx.extract.128.lo" => "__builtin_lasx_extract_128_lo", + "lasx.extract.128.lo.d" => "__builtin_lasx_extract_128_lo_d", + "lasx.extract.128.lo.s" => "__builtin_lasx_extract_128_lo_s", + "lasx.insert.128.hi" => "__builtin_lasx_insert_128_hi", + "lasx.insert.128.hi.d" => "__builtin_lasx_insert_128_hi_d", + "lasx.insert.128.hi.s" => "__builtin_lasx_insert_128_hi_s", + "lasx.insert.128.lo" => "__builtin_lasx_insert_128_lo", + "lasx.insert.128.lo.d" => "__builtin_lasx_insert_128_lo_d", + "lasx.insert.128.lo.s" => "__builtin_lasx_insert_128_lo_s", "lasx.vext2xv.d.b" => "__builtin_lasx_vext2xv_d_b", "lasx.vext2xv.d.h" => "__builtin_lasx_vext2xv_d_h", "lasx.vext2xv.d.w" => "__builtin_lasx_vext2xv_d_w", @@ -4950,8 +5022,20 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "f16x2.to.e5m2x2.rn.relu" => "__nvvm_f16x2_to_e5m2x2_rn_relu", "f2bf16.rn" => "__nvvm_f2bf16_rn", "f2bf16.rn.relu" => "__nvvm_f2bf16_rn_relu", + "f2bf16.rn.relu.satfinite" => "__nvvm_f2bf16_rn_relu_satfinite", + "f2bf16.rn.satfinite" => "__nvvm_f2bf16_rn_satfinite", "f2bf16.rz" => "__nvvm_f2bf16_rz", "f2bf16.rz.relu" => "__nvvm_f2bf16_rz_relu", + "f2bf16.rz.relu.satfinite" => "__nvvm_f2bf16_rz_relu_satfinite", + "f2bf16.rz.satfinite" => "__nvvm_f2bf16_rz_satfinite", + "f2f16.rn" => "__nvvm_f2f16_rn", + "f2f16.rn.relu" => "__nvvm_f2f16_rn_relu", + "f2f16.rn.relu.satfinite" => "__nvvm_f2f16_rn_relu_satfinite", + "f2f16.rn.satfinite" => "__nvvm_f2f16_rn_satfinite", + "f2f16.rz" => "__nvvm_f2f16_rz", + "f2f16.rz.relu" => "__nvvm_f2f16_rz_relu", + "f2f16.rz.relu.satfinite" => "__nvvm_f2f16_rz_relu_satfinite", + "f2f16.rz.satfinite" => "__nvvm_f2f16_rz_satfinite", "f2h.rn" => "__nvvm_f2h_rn", "f2h.rn.ftz" => "__nvvm_f2h_rn_ftz", "f2i.rm" => "__nvvm_f2i_rm", @@ -5035,20 +5119,28 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "ff.to.ue8m0x2.rz.satfinite" => "__nvvm_ff_to_ue8m0x2_rz_satfinite", "ff2bf16x2.rn" => "__nvvm_ff2bf16x2_rn", "ff2bf16x2.rn.relu" => "__nvvm_ff2bf16x2_rn_relu", + "ff2bf16x2.rn.relu.satfinite" => "__nvvm_ff2bf16x2_rn_relu_satfinite", + "ff2bf16x2.rn.satfinite" => "__nvvm_ff2bf16x2_rn_satfinite", "ff2bf16x2.rs" => "__nvvm_ff2bf16x2_rs", "ff2bf16x2.rs.relu" => "__nvvm_ff2bf16x2_rs_relu", "ff2bf16x2.rs.relu.satfinite" => "__nvvm_ff2bf16x2_rs_relu_satfinite", "ff2bf16x2.rs.satfinite" => "__nvvm_ff2bf16x2_rs_satfinite", "ff2bf16x2.rz" => "__nvvm_ff2bf16x2_rz", "ff2bf16x2.rz.relu" => "__nvvm_ff2bf16x2_rz_relu", + "ff2bf16x2.rz.relu.satfinite" => "__nvvm_ff2bf16x2_rz_relu_satfinite", + "ff2bf16x2.rz.satfinite" => "__nvvm_ff2bf16x2_rz_satfinite", "ff2f16x2.rn" => "__nvvm_ff2f16x2_rn", "ff2f16x2.rn.relu" => "__nvvm_ff2f16x2_rn_relu", + "ff2f16x2.rn.relu.satfinite" => "__nvvm_ff2f16x2_rn_relu_satfinite", + "ff2f16x2.rn.satfinite" => "__nvvm_ff2f16x2_rn_satfinite", "ff2f16x2.rs" => "__nvvm_ff2f16x2_rs", "ff2f16x2.rs.relu" => "__nvvm_ff2f16x2_rs_relu", "ff2f16x2.rs.relu.satfinite" => "__nvvm_ff2f16x2_rs_relu_satfinite", "ff2f16x2.rs.satfinite" => "__nvvm_ff2f16x2_rs_satfinite", "ff2f16x2.rz" => "__nvvm_ff2f16x2_rz", "ff2f16x2.rz.relu" => "__nvvm_ff2f16x2_rz_relu", + "ff2f16x2.rz.relu.satfinite" => "__nvvm_ff2f16x2_rz_relu_satfinite", + "ff2f16x2.rz.satfinite" => "__nvvm_ff2f16x2_rz_satfinite", "floor.d" => "__nvvm_floor_d", "floor.f" => "__nvvm_floor_f", "floor.ftz.f" => "__nvvm_floor_ftz_f", @@ -5942,6 +6034,8 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "altivec.vupklsb" => "__builtin_altivec_vupklsb", "altivec.vupklsh" => "__builtin_altivec_vupklsh", "altivec.vupklsw" => "__builtin_altivec_vupklsw", + "amo.ldat" => "__builtin_amo_ldat", + "amo.lwat" => "__builtin_amo_lwat", "bcdadd" => "__builtin_ppc_bcdadd", "bcdadd.p" => "__builtin_ppc_bcdadd_p", "bcdcopysign" => "__builtin_ppc_bcdcopysign", @@ -6202,6 +6296,7 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "vsx.xvminsp" => "__builtin_vsx_xvminsp", "vsx.xvredp" => "__builtin_vsx_xvredp", "vsx.xvresp" => "__builtin_vsx_xvresp", + "vsx.xvrlw" => "__builtin_vsx_xvrlw", "vsx.xvrsqrtedp" => "__builtin_vsx_xvrsqrtedp", "vsx.xvrsqrtesp" => "__builtin_vsx_xvrsqrtesp", "vsx.xvtdivdp" => "__builtin_vsx_xvtdivdp", @@ -10158,24 +10253,16 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "stui" => "__builtin_ia32_stui", "subborrow.u32" => "__builtin_ia32_subborrow_u32", "subborrow.u64" => "__builtin_ia32_subborrow_u64", - "t2rpntlvwz0" => "__builtin_ia32_t2rpntlvwz0", "t2rpntlvwz0rs" => "__builtin_ia32_t2rpntlvwz0rs", "t2rpntlvwz0rst1" => "__builtin_ia32_t2rpntlvwz0rst1", - "t2rpntlvwz0t1" => "__builtin_ia32_t2rpntlvwz0t1", - "t2rpntlvwz1" => "__builtin_ia32_t2rpntlvwz1", "t2rpntlvwz1rs" => "__builtin_ia32_t2rpntlvwz1rs", "t2rpntlvwz1rst1" => "__builtin_ia32_t2rpntlvwz1rst1", - "t2rpntlvwz1t1" => "__builtin_ia32_t2rpntlvwz1t1", "tbm.bextri.u32" => "__builtin_ia32_bextri_u32", "tbm.bextri.u64" => "__builtin_ia32_bextri_u64", "tcmmimfp16ps" => "__builtin_ia32_tcmmimfp16ps", "tcmmimfp16ps.internal" => "__builtin_ia32_tcmmimfp16ps_internal", "tcmmrlfp16ps" => "__builtin_ia32_tcmmrlfp16ps", "tcmmrlfp16ps.internal" => "__builtin_ia32_tcmmrlfp16ps_internal", - "tconjtcmmimfp16ps" => "__builtin_ia32_tconjtcmmimfp16ps", - "tconjtcmmimfp16ps.internal" => "__builtin_ia32_tconjtcmmimfp16ps_internal", - "tconjtfp16" => "__builtin_ia32_tconjtfp16", - "tconjtfp16.internal" => "__builtin_ia32_tconjtfp16_internal", "tcvtrowd2ps" => "__builtin_ia32_tcvtrowd2ps", "tcvtrowd2ps.internal" => "__builtin_ia32_tcvtrowd2ps_internal", "tcvtrowps2bf16h" => "__builtin_ia32_tcvtrowps2bf16h", @@ -10225,18 +10312,6 @@ fn map_arch_intrinsic(full_name: &str) -> &'static str { "tmmultf32ps" => "__builtin_ia32_tmmultf32ps", "tmmultf32ps.internal" => "__builtin_ia32_tmmultf32ps_internal", "tpause" => "__builtin_ia32_tpause", - "ttcmmimfp16ps" => "__builtin_ia32_ttcmmimfp16ps", - "ttcmmimfp16ps.internal" => "__builtin_ia32_ttcmmimfp16ps_internal", - "ttcmmrlfp16ps" => "__builtin_ia32_ttcmmrlfp16ps", - "ttcmmrlfp16ps.internal" => "__builtin_ia32_ttcmmrlfp16ps_internal", - "ttdpbf16ps" => "__builtin_ia32_ttdpbf16ps", - "ttdpbf16ps.internal" => "__builtin_ia32_ttdpbf16ps_internal", - "ttdpfp16ps" => "__builtin_ia32_ttdpfp16ps", - "ttdpfp16ps.internal" => "__builtin_ia32_ttdpfp16ps_internal", - "ttmmultf32ps" => "__builtin_ia32_ttmmultf32ps", - "ttmmultf32ps.internal" => "__builtin_ia32_ttmmultf32ps_internal", - "ttransposed" => "__builtin_ia32_ttransposed", - "ttransposed.internal" => "__builtin_ia32_ttransposed_internal", "umonitor" => "__builtin_ia32_umonitor", "umwait" => "__builtin_ia32_umwait", "urdmsr" => "__builtin_ia32_urdmsr", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a38dcf8e4c93..d2714ba7914f 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -504,7 +504,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let layout = self.layout_of(tp_ty).layout; let _use_integer_compare = match layout.backend_repr() { Scalar(_) | ScalarPair(_, _) => true, - SimdVector { .. } => false, + SimdVector { .. } | ScalableVector { .. } => false, Memory { .. } => { // For rusty ABIs, small aggregates are actually passed // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`), diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index a77239e23b4e..96d3a0024f41 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -98,7 +98,6 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames}; -use rustc_session::filesearch::make_target_lib_path; use rustc_span::Symbol; use rustc_target::spec::{Arch, RelocModel}; use tempfile::TempDir; @@ -207,18 +206,38 @@ impl CodegenBackend for GccCodegenBackend { } fn init(&self, sess: &Session) { + fn file_path(sysroot_path: &Path, sess: &Session) -> PathBuf { + let rustlib_path = + rustc_target::relative_target_rustlib_path(sysroot_path, &sess.host.llvm_target); + sysroot_path + .join(rustlib_path) + .join("codegen-backends") + .join("lib") + .join(sess.target.llvm_target.as_ref()) + .join("libgccjit.so") + } + // We use all_paths() instead of only path() in case the path specified by --sysroot is // invalid. // This is the case for instance in Rust for Linux where they specify --sysroot=/dev/null. for path in sess.opts.sysroot.all_paths() { - let libgccjit_target_lib_file = - make_target_lib_path(path, &sess.target.llvm_target).join("libgccjit.so"); + let libgccjit_target_lib_file = file_path(path, sess); if let Ok(true) = fs::exists(&libgccjit_target_lib_file) { load_libgccjit_if_needed(&libgccjit_target_lib_file); break; } } + if !gccjit::is_loaded() { + let mut paths = vec![]; + for path in sess.opts.sysroot.all_paths() { + let libgccjit_target_lib_file = file_path(path, sess); + paths.push(libgccjit_target_lib_file); + } + + panic!("Could not load libgccjit.so. Attempted paths: {:#?}", paths); + } + #[cfg(feature = "master")] { let target_cpu = target_cpu(sess); diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 93202483eed8..68fca5a17ad3 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -85,6 +85,7 @@ fn uncached_gcc_type<'gcc, 'tcx>( ); } BackendRepr::Memory { .. } => {} + BackendRepr::ScalableVector { .. } => todo!(), } let name = match *layout.ty.kind() { @@ -179,6 +180,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { fn is_gcc_immediate(&self) -> bool { match self.backend_repr { BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => true, + // FIXME(rustc_scalable_vector): Not yet implemented in rustc_codegen_gcc. + BackendRepr::ScalableVector { .. } => todo!(), BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false, } } @@ -188,6 +191,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { BackendRepr::ScalarPair(..) => true, BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } | BackendRepr::Memory { .. } => false, } } diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 2380bd0fc137..99a80fa7e4f4 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -88,3 +88,12 @@ tests/ui/test-attrs/test-panic-while-printing.rs tests/ui/thir-print/offset_of.rs tests/ui/iterators/rangefrom-overflow-debug.rs tests/ui/iterators/rangefrom-overflow-overflow-checks.rs +tests/ui/iterators/iter-filter-count-debug-check.rs +tests/ui/eii/codegen_single_crate.rs +tests/ui/eii/codegen_cross_crate.rs +tests/ui/eii/default/local_crate.rs +tests/ui/eii/multiple_impls.rs +tests/ui/eii/default/call_default.rs +tests/ui/eii/same-symbol.rs +tests/ui/eii/privacy1.rs +tests/ui/eii/default/call_impl.rs diff --git a/compiler/rustc_codegen_gcc/triagebot.toml b/compiler/rustc_codegen_gcc/triagebot.toml index 13da0a87def3..eb0c7b011f60 100644 --- a/compiler/rustc_codegen_gcc/triagebot.toml +++ b/compiler/rustc_codegen_gcc/triagebot.toml @@ -2,6 +2,3 @@ # Prevents un-canonicalized issue links (to avoid wrong issues being linked in r-l/rust) [issue-links] - -# Prevents mentions in commits to avoid users being spammed -[no-mentions] diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 0544a94fd59f..076ec5e59eb6 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -14,6 +14,7 @@ bitflags = "2.4.1" gimli = "0.31" itertools = "0.12" libc = "0.2" +libloading = { version = "0.9.0", optional = true } measureme = "12.0.1" object = { version = "0.37.0", default-features = false, features = ["std", "read"] } rustc-demangle = "0.1.21" @@ -46,7 +47,7 @@ tracing = "0.1" [features] # tidy-alphabetical-start check_only = ["rustc_llvm/check_only"] -llvm_enzyme = [] +llvm_enzyme = ["dep:libloading"] llvm_offload = [] # tidy-alphabetical-end diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 0e7b00d0bcb7..b3ef9840f5dc 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -1,9 +1,10 @@ +codegen_llvm_autodiff_component_unavailable = failed to load our autodiff backend. Did you install it via rustup? + codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable codegen_llvm_autodiff_without_lto = using the autodiff feature requires setting `lto="fat"` in your Cargo.toml codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} - codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture codegen_llvm_from_llvm_diag = {$message} diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 482e95413855..314e64272ffe 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -528,31 +528,34 @@ fn thin_lto( } } -fn enable_autodiff_settings(ad: &[config::AutoDiff]) { +#[cfg(feature = "llvm_enzyme")] +pub(crate) fn enable_autodiff_settings(ad: &[config::AutoDiff]) { + let mut enzyme = llvm::EnzymeWrapper::get_instance(); + for val in ad { // We intentionally don't use a wildcard, to not forget handling anything new. match val { config::AutoDiff::PrintPerf => { - llvm::set_print_perf(true); + enzyme.set_print_perf(true); } config::AutoDiff::PrintAA => { - llvm::set_print_activity(true); + enzyme.set_print_activity(true); } config::AutoDiff::PrintTA => { - llvm::set_print_type(true); + enzyme.set_print_type(true); } config::AutoDiff::PrintTAFn(fun) => { - llvm::set_print_type(true); // Enable general type printing - llvm::set_print_type_fun(&fun); // Set specific function to analyze + enzyme.set_print_type(true); // Enable general type printing + enzyme.set_print_type_fun(&fun); // Set specific function to analyze } config::AutoDiff::Inline => { - llvm::set_inline(true); + enzyme.set_inline(true); } config::AutoDiff::LooseTypes => { - llvm::set_loose_types(true); + enzyme.set_loose_types(true); } config::AutoDiff::PrintSteps => { - llvm::set_print(true); + enzyme.set_print(true); } // We handle this in the PassWrapper.cpp config::AutoDiff::PrintPasses => {} @@ -571,9 +574,9 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff]) { } } // This helps with handling enums for now. - llvm::set_strict_aliasing(false); + enzyme.set_strict_aliasing(false); // FIXME(ZuseZ4): Test this, since it was added a long time ago. - llvm::set_rust_rules(true); + enzyme.set_rust_rules(true); } pub(crate) fn run_pass_manager( @@ -607,10 +610,6 @@ pub(crate) fn run_pass_manager( if enable_ad { write::AutodiffStage::DuringAD } else { write::AutodiffStage::PostAD } }; - if enable_ad { - enable_autodiff_settings(&config.autodiff); - } - unsafe { write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 2c4943e835a6..1afb087178f1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -707,8 +707,7 @@ pub(crate) unsafe fn llvm_optimize( if cgcx.target_is_like_gpu && config.offload.contains(&config::Offload::Enable) { let cx = SimpleCx::new(module.module_llvm.llmod(), module.module_llvm.llcx, cgcx.pointer_size); - // For now we only support up to 10 kernels named kernel_0 ... kernel_9, a follow-up PR is - // introducing a proper offload intrinsic to solve this limitation. + for func in cx.get_functions() { let offload_kernel = "offload-kernel"; if attributes::has_string_attr(func, offload_kernel) { @@ -730,6 +729,13 @@ pub(crate) unsafe fn llvm_optimize( let llvm_plugins = config.llvm_plugins.join(","); + let enzyme_fn = if consider_ad { + let wrapper = llvm::EnzymeWrapper::get_instance(); + wrapper.registerEnzymeAndPassPipeline + } else { + std::ptr::null() + }; + let result = unsafe { llvm::LLVMRustOptimize( module.module_llvm.llmod(), @@ -749,7 +755,7 @@ pub(crate) unsafe fn llvm_optimize( vectorize_loop, config.no_builtins, config.emit_lifetime_markers, - run_enzyme, + enzyme_fn, print_before_enzyme, print_after_enzyme, print_passes, diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 6cbddfec4631..16455b4c79cd 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -23,13 +23,14 @@ use rustc_middle::dep_graph; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, SanitizerFnAttrs}; use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::TyCtxt; -use rustc_session::config::DebugInfo; +use rustc_session::config::{DebugInfo, Offload}; use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; use super::ModuleLlvm; use crate::attributes; use crate::builder::Builder; +use crate::builder::gpu_offload::OffloadGlobals; use crate::context::CodegenCx; use crate::llvm::{self, Value}; @@ -85,6 +86,19 @@ pub(crate) fn compile_codegen_unit( let llvm_module = ModuleLlvm::new(tcx, cgu_name.as_str()); { let mut cx = CodegenCx::new(tcx, cgu, &llvm_module); + + // Declare and store globals shared by all offload kernels + // + // These globals are left in the LLVM-IR host module so all kernels can access them. + // They are necessary for correct offload execution. We do this here to simplify the + // `offload` intrinsic, avoiding the need for tracking whether it's the first + // intrinsic call or not. + if cx.sess().opts.unstable_opts.offload.contains(&Offload::Enable) + && !cx.sess().target.is_like_gpu + { + cx.offload_globals.replace(Some(OffloadGlobals::declare(&cx))); + } + let mono_items = cx.codegen_unit.items_in_deterministic_order(cx.tcx); for &(mono_item, data) in &mono_items { mono_item.predefine::>( diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 3442abece2f7..9a9e4dcdbb3c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -613,6 +613,25 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } + fn scalable_alloca(&mut self, elt: u64, align: Align, element_ty: Ty<'_>) -> Self::Value { + let mut bx = Builder::with_cx(self.cx); + bx.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) }); + let llvm_ty = match element_ty.kind() { + ty::Bool => bx.type_i1(), + ty::Int(int_ty) => self.cx.type_int_from_ty(*int_ty), + ty::Uint(uint_ty) => self.cx.type_uint_from_ty(*uint_ty), + ty::Float(float_ty) => self.cx.type_float_from_ty(*float_ty), + _ => unreachable!("scalable vectors can only contain a bool, int, uint or float"), + }; + + unsafe { + let ty = llvm::LLVMScalableVectorType(llvm_ty, elt.try_into().unwrap()); + let alloca = llvm::LLVMBuildAlloca(&bx.llbuilder, ty, UNNAMED); + llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); + alloca + } + } + fn load(&mut self, ty: &'ll Type, ptr: &'ll Value, align: Align) -> &'ll Value { unsafe { let load = llvm::LLVMBuildLoad2(self.llbuilder, ty, ptr, UNNAMED); diff --git a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs index 5d1ddd057d88..046501d08c48 100644 --- a/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs +++ b/compiler/rustc_codegen_llvm/src/builder/gpu_offload.rs @@ -2,17 +2,76 @@ use std::ffi::CString; use llvm::Linkage::*; use rustc_abi::Align; -use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_middle::ty::offload_meta::OffloadMetadata; -use crate::builder::SBuilder; +use crate::builder::Builder; +use crate::common::CodegenCx; use crate::llvm::AttributePlace::Function; -use crate::llvm::{self, BasicBlock, Linkage, Type, Value}; +use crate::llvm::{self, Linkage, Type, Value}; use crate::{SimpleCx, attributes}; +// LLVM kernel-independent globals required for offloading +pub(crate) struct OffloadGlobals<'ll> { + pub launcher_fn: &'ll llvm::Value, + pub launcher_ty: &'ll llvm::Type, + + pub bin_desc: &'ll llvm::Type, + + pub kernel_args_ty: &'ll llvm::Type, + + pub offload_entry_ty: &'ll llvm::Type, + + pub begin_mapper: &'ll llvm::Value, + pub end_mapper: &'ll llvm::Value, + pub mapper_fn_ty: &'ll llvm::Type, + + pub ident_t_global: &'ll llvm::Value, + + pub register_lib: &'ll llvm::Value, + pub unregister_lib: &'ll llvm::Value, + pub init_rtls: &'ll llvm::Value, +} + +impl<'ll> OffloadGlobals<'ll> { + pub(crate) fn declare(cx: &CodegenCx<'ll, '_>) -> Self { + let (launcher_fn, launcher_ty) = generate_launcher(cx); + let kernel_args_ty = KernelArgsTy::new_decl(cx); + let offload_entry_ty = TgtOffloadEntry::new_decl(cx); + let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx); + let ident_t_global = generate_at_one(cx); + + let tptr = cx.type_ptr(); + let ti32 = cx.type_i32(); + let tgt_bin_desc_ty = vec![ti32, tptr, tptr, tptr]; + let bin_desc = cx.type_named_struct("struct.__tgt_bin_desc"); + cx.set_struct_body(bin_desc, &tgt_bin_desc_ty, false); + + let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", mapper_fn_ty); + let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", mapper_fn_ty); + let init_ty = cx.type_func(&[], cx.type_void()); + let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty); + + OffloadGlobals { + launcher_fn, + launcher_ty, + bin_desc, + kernel_args_ty, + offload_entry_ty, + begin_mapper, + end_mapper, + mapper_fn_ty, + ident_t_global, + register_lib, + unregister_lib, + init_rtls, + } + } +} + // ; Function Attrs: nounwind // declare i32 @__tgt_target_kernel(ptr, i64, i32, i32, ptr, ptr) #2 -fn generate_launcher<'ll>(cx: &'ll SimpleCx<'_>) -> (&'ll llvm::Value, &'ll llvm::Type) { +fn generate_launcher<'ll>(cx: &CodegenCx<'ll, '_>) -> (&'ll llvm::Value, &'ll llvm::Type) { let tptr = cx.type_ptr(); let ti64 = cx.type_i64(); let ti32 = cx.type_i32(); @@ -30,7 +89,7 @@ fn generate_launcher<'ll>(cx: &'ll SimpleCx<'_>) -> (&'ll llvm::Value, &'ll llvm // @1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @0 }, align 8 // FIXME(offload): @0 should include the file name (e.g. lib.rs) in which the function to be // offloaded was defined. -fn generate_at_one<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll llvm::Value { +pub(crate) fn generate_at_one<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll llvm::Value { let unknown_txt = ";unknown;unknown;0;0;;"; let c_entry_name = CString::new(unknown_txt).unwrap(); let c_val = c_entry_name.as_bytes_with_nul(); @@ -68,7 +127,7 @@ pub(crate) struct TgtOffloadEntry { } impl TgtOffloadEntry { - pub(crate) fn new_decl<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll llvm::Type { + pub(crate) fn new_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll llvm::Type { let offload_entry_ty = cx.type_named_struct("struct.__tgt_offload_entry"); let tptr = cx.type_ptr(); let ti64 = cx.type_i64(); @@ -82,7 +141,7 @@ impl TgtOffloadEntry { } fn new<'ll>( - cx: &'ll SimpleCx<'_>, + cx: &CodegenCx<'ll, '_>, region_id: &'ll Value, llglobal: &'ll Value, ) -> [&'ll Value; 9] { @@ -126,7 +185,7 @@ impl KernelArgsTy { const OFFLOAD_VERSION: u64 = 3; const FLAGS: u64 = 0; const TRIPCOUNT: u64 = 0; - fn new_decl<'ll>(cx: &'ll SimpleCx<'_>) -> &'ll Type { + fn new_decl<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Type { let kernel_arguments_ty = cx.type_named_struct("struct.__tgt_kernel_arguments"); let tptr = cx.type_ptr(); let ti64 = cx.type_i64(); @@ -140,8 +199,8 @@ impl KernelArgsTy { kernel_arguments_ty } - fn new<'ll>( - cx: &'ll SimpleCx<'_>, + fn new<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, num_args: u64, memtransfer_types: &'ll Value, geps: [&'ll Value; 3], @@ -171,7 +230,8 @@ impl KernelArgsTy { } // Contains LLVM values needed to manage offloading for a single kernel. -pub(crate) struct OffloadKernelData<'ll> { +#[derive(Copy, Clone)] +pub(crate) struct OffloadKernelGlobals<'ll> { pub offload_sizes: &'ll llvm::Value, pub memtransfer_types: &'ll llvm::Value, pub region_id: &'ll llvm::Value, @@ -179,7 +239,7 @@ pub(crate) struct OffloadKernelData<'ll> { } fn gen_tgt_data_mappers<'ll>( - cx: &'ll SimpleCx<'_>, + cx: &CodegenCx<'ll, '_>, ) -> (&'ll llvm::Value, &'ll llvm::Value, &'ll llvm::Value, &'ll llvm::Type) { let tptr = cx.type_ptr(); let ti64 = cx.type_i64(); @@ -241,12 +301,18 @@ pub(crate) fn add_global<'ll>( // mapped to/from the gpu. It also returns a region_id with the name of this kernel, to be // concatenated into the list of region_ids. pub(crate) fn gen_define_handling<'ll>( - cx: &SimpleCx<'ll>, - offload_entry_ty: &'ll llvm::Type, + cx: &CodegenCx<'ll, '_>, metadata: &[OffloadMetadata], - types: &[&Type], - symbol: &str, -) -> OffloadKernelData<'ll> { + types: &[&'ll Type], + symbol: String, + offload_globals: &OffloadGlobals<'ll>, +) -> OffloadKernelGlobals<'ll> { + if let Some(entry) = cx.offload_kernel_cache.borrow().get(&symbol) { + return *entry; + } + + let offload_entry_ty = offload_globals.offload_entry_ty; + // It seems like non-pointer values are automatically mapped. So here, we focus on pointer (or // reference) types. let ptr_meta = types.iter().zip(metadata).filter_map(|(&x, meta)| match cx.type_kind(x) { @@ -272,9 +338,9 @@ pub(crate) fn gen_define_handling<'ll>( let name = format!(".{symbol}.region_id"); let initializer = cx.get_const_i8(0); - let region_id = add_unnamed_global(&cx, &name, initializer, WeakAnyLinkage); + let region_id = add_global(&cx, &name, initializer, WeakAnyLinkage); - let c_entry_name = CString::new(symbol).unwrap(); + let c_entry_name = CString::new(symbol.clone()).unwrap(); let c_val = c_entry_name.as_bytes_with_nul(); let offload_entry_name = format!(".offloading.entry_name.{symbol}"); @@ -298,11 +364,16 @@ pub(crate) fn gen_define_handling<'ll>( let c_section_name = CString::new("llvm_offload_entries").unwrap(); llvm::set_section(offload_entry, &c_section_name); - OffloadKernelData { offload_sizes, memtransfer_types, region_id, offload_entry } + let result = + OffloadKernelGlobals { offload_sizes, memtransfer_types, region_id, offload_entry }; + + cx.offload_kernel_cache.borrow_mut().insert(symbol, result); + + result } fn declare_offload_fn<'ll>( - cx: &'ll SimpleCx<'_>, + cx: &CodegenCx<'ll, '_>, name: &str, ty: &'ll llvm::Type, ) -> &'ll llvm::Value { @@ -335,28 +406,28 @@ fn declare_offload_fn<'ll>( // 4. set insert point after kernel call. // 5. generate all the GEPS and stores, to be used in 6) // 6. generate __tgt_target_data_end calls to move data from the GPU -pub(crate) fn gen_call_handling<'ll>( - cx: &SimpleCx<'ll>, - bb: &BasicBlock, - offload_data: &OffloadKernelData<'ll>, +pub(crate) fn gen_call_handling<'ll, 'tcx>( + builder: &mut Builder<'_, 'll, 'tcx>, + offload_data: &OffloadKernelGlobals<'ll>, args: &[&'ll Value], types: &[&Type], metadata: &[OffloadMetadata], + offload_globals: &OffloadGlobals<'ll>, ) { - let OffloadKernelData { offload_sizes, offload_entry, memtransfer_types, region_id } = + let cx = builder.cx; + let OffloadKernelGlobals { offload_sizes, offload_entry, memtransfer_types, region_id } = offload_data; - let (tgt_decl, tgt_target_kernel_ty) = generate_launcher(&cx); + + let tgt_decl = offload_globals.launcher_fn; + let tgt_target_kernel_ty = offload_globals.launcher_ty; + // %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr } - let tptr = cx.type_ptr(); - let ti32 = cx.type_i32(); - let tgt_bin_desc_ty = vec![ti32, tptr, tptr, tptr]; - let tgt_bin_desc = cx.type_named_struct("struct.__tgt_bin_desc"); - cx.set_struct_body(tgt_bin_desc, &tgt_bin_desc_ty, false); + let tgt_bin_desc = offload_globals.bin_desc; - let tgt_kernel_decl = KernelArgsTy::new_decl(&cx); - let (begin_mapper_decl, _, end_mapper_decl, fn_ty) = gen_tgt_data_mappers(&cx); - - let mut builder = SBuilder::build(cx, bb); + let tgt_kernel_decl = offload_globals.kernel_args_ty; + let begin_mapper_decl = offload_globals.begin_mapper; + let end_mapper_decl = offload_globals.end_mapper; + let fn_ty = offload_globals.mapper_fn_ty; let num_args = types.len() as u64; let ip = unsafe { llvm::LLVMRustGetInsertPoint(&builder.llbuilder) }; @@ -378,9 +449,8 @@ pub(crate) fn gen_call_handling<'ll>( // Step 0) // %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr } // %6 = alloca %struct.__tgt_bin_desc, align 8 - let llfn = unsafe { llvm::LLVMGetBasicBlockParent(bb) }; unsafe { - llvm::LLVMRustPositionBuilderPastAllocas(&builder.llbuilder, llfn); + llvm::LLVMRustPositionBuilderPastAllocas(&builder.llbuilder, builder.llfn()); } let tgt_bin_desc_alloca = builder.direct_alloca(tgt_bin_desc, Align::EIGHT, "EmptyDesc"); @@ -413,16 +483,16 @@ pub(crate) fn gen_call_handling<'ll>( } let mapper_fn_ty = cx.type_func(&[cx.type_ptr()], cx.type_void()); - let register_lib_decl = declare_offload_fn(&cx, "__tgt_register_lib", mapper_fn_ty); - let unregister_lib_decl = declare_offload_fn(&cx, "__tgt_unregister_lib", mapper_fn_ty); + let register_lib_decl = offload_globals.register_lib; + let unregister_lib_decl = offload_globals.unregister_lib; let init_ty = cx.type_func(&[], cx.type_void()); - let init_rtls_decl = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty); + let init_rtls_decl = offload_globals.init_rtls; // FIXME(offload): Later we want to add them to the wrapper code, rather than our main function. // call void @__tgt_register_lib(ptr noundef %6) - builder.call(mapper_fn_ty, register_lib_decl, &[tgt_bin_desc_alloca], None); + builder.call(mapper_fn_ty, None, None, register_lib_decl, &[tgt_bin_desc_alloca], None, None); // call void @__tgt_init_all_rtls() - builder.call(init_ty, init_rtls_decl, &[], None); + builder.call(init_ty, None, None, init_rtls_decl, &[], None, None); for i in 0..num_args { let idx = cx.get_const_i32(i); @@ -437,15 +507,15 @@ pub(crate) fn gen_call_handling<'ll>( // For now we have a very simplistic indexing scheme into our // offload_{baseptrs,ptrs,sizes}. We will probably improve this along with our gpu frontend pr. - fn get_geps<'a, 'll>( - builder: &mut SBuilder<'a, 'll>, - cx: &'ll SimpleCx<'ll>, + fn get_geps<'ll, 'tcx>( + builder: &mut Builder<'_, 'll, 'tcx>, ty: &'ll Type, ty2: &'ll Type, a1: &'ll Value, a2: &'ll Value, a4: &'ll Value, ) -> [&'ll Value; 3] { + let cx = builder.cx; let i32_0 = cx.get_const_i32(0); let gep1 = builder.inbounds_gep(ty, a1, &[i32_0, i32_0]); @@ -454,9 +524,8 @@ pub(crate) fn gen_call_handling<'ll>( [gep1, gep2, gep3] } - fn generate_mapper_call<'a, 'll>( - builder: &mut SBuilder<'a, 'll>, - cx: &'ll SimpleCx<'ll>, + fn generate_mapper_call<'ll, 'tcx>( + builder: &mut Builder<'_, 'll, 'tcx>, geps: [&'ll Value; 3], o_type: &'ll Value, fn_to_call: &'ll Value, @@ -464,20 +533,20 @@ pub(crate) fn gen_call_handling<'ll>( num_args: u64, s_ident_t: &'ll Value, ) { + let cx = builder.cx; let nullptr = cx.const_null(cx.type_ptr()); let i64_max = cx.get_const_i64(u64::MAX); let num_args = cx.get_const_i32(num_args); let args = vec![s_ident_t, i64_max, num_args, geps[0], geps[1], geps[2], o_type, nullptr, nullptr]; - builder.call(fn_ty, fn_to_call, &args, None); + builder.call(fn_ty, None, None, fn_to_call, &args, None, None); } // Step 2) - let s_ident_t = generate_at_one(&cx); - let geps = get_geps(&mut builder, &cx, ty, ty2, a1, a2, a4); + let s_ident_t = offload_globals.ident_t_global; + let geps = get_geps(builder, ty, ty2, a1, a2, a4); generate_mapper_call( - &mut builder, - &cx, + builder, geps, memtransfer_types, begin_mapper_decl, @@ -504,14 +573,13 @@ pub(crate) fn gen_call_handling<'ll>( region_id, a5, ]; - builder.call(tgt_target_kernel_ty, tgt_decl, &args, None); + builder.call(tgt_target_kernel_ty, None, None, tgt_decl, &args, None, None); // %41 = call i32 @__tgt_target_kernel(ptr @1, i64 -1, i32 2097152, i32 256, ptr @.kernel_1.region_id, ptr %kernel_args) // Step 4) - let geps = get_geps(&mut builder, &cx, ty, ty2, a1, a2, a4); + let geps = get_geps(builder, ty, ty2, a1, a2, a4); generate_mapper_call( - &mut builder, - &cx, + builder, geps, memtransfer_types, end_mapper_decl, @@ -520,7 +588,5 @@ pub(crate) fn gen_call_handling<'ll>( s_ident_t, ); - builder.call(mapper_fn_ty, unregister_lib_decl, &[tgt_bin_desc_alloca], None); - - drop(builder); + builder.call(mapper_fn_ty, None, None, unregister_lib_decl, &[tgt_bin_desc_alloca], None, None); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 6caf60e3cc41..03da70d48f7d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -35,6 +35,7 @@ use smallvec::SmallVec; use crate::abi::to_llvm_calling_convention; use crate::back::write::to_llvm_code_model; +use crate::builder::gpu_offload::{OffloadGlobals, OffloadKernelGlobals}; use crate::callee::get_fn; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; use crate::llvm::{self, Metadata, MetadataKindId, Module, Type, Value}; @@ -156,6 +157,12 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// Cache of Objective-C selector references pub objc_selrefs: RefCell>, + + /// Globals shared by the offloading runtime + pub offload_globals: RefCell>>, + + /// Cache of kernel-specific globals + pub offload_kernel_cache: RefCell>>, } fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { @@ -639,6 +646,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { objc_class_t: Cell::new(None), objc_classrefs: Default::default(), objc_selrefs: Default::default(), + offload_globals: Default::default(), + offload_kernel_cache: Default::default(), }, PhantomData, ) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index bc54657a380a..a728f3ea1e66 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::FxIndexMap; use rustc_index::IndexVec; use rustc_middle::ty::TyCtxt; -use rustc_span::{RemapPathScopeComponents, SourceFile, StableSourceFileId}; +use rustc_span::{FileName, RemapPathScopeComponents, SourceFile, StableSourceFileId}; use tracing::debug; use crate::common::CodegenCx; @@ -125,7 +125,19 @@ impl GlobalFileTable { for file in all_files { raw_file_table.entry(file.stable_id).or_insert_with(|| { - file.name.display(RemapPathScopeComponents::COVERAGE).to_string_lossy().into_owned() + // Prefer using the embeddable filename as this filename is going to + // end-up in the coverage artifacts (see rust-lang/rust#150020). + if let FileName::Real(real) = &file.name { + let (_work_dir, abs_name) = + real.embeddable_name(RemapPathScopeComponents::COVERAGE); + + abs_name.to_string_lossy().into_owned() + } else { + file.name + .display(RemapPathScopeComponents::COVERAGE) + .to_string_lossy() + .into_owned() + } }); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs b/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs index 5d874631ca1a..0b5ef6c68740 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/di_builder.rs @@ -1,8 +1,37 @@ +use std::ptr; + use libc::c_uint; use rustc_abi::Align; use crate::llvm::debuginfo::DIBuilder; -use crate::llvm::{self, ToLlvmBool}; +use crate::llvm::{self, Module, ToLlvmBool}; + +/// Owning pointer to a `DIBuilder<'ll>` that will dispose of the builder +/// when dropped. Use `.as_ref()` to get the underlying `&DIBuilder` +/// needed for debuginfo FFI calls. +pub(crate) struct DIBuilderBox<'ll> { + raw: ptr::NonNull>, +} + +impl<'ll> DIBuilderBox<'ll> { + pub(crate) fn new(llmod: &'ll Module) -> Self { + let raw = unsafe { llvm::LLVMCreateDIBuilder(llmod) }; + let raw = ptr::NonNull::new(raw).unwrap(); + Self { raw } + } + + pub(crate) fn as_ref(&self) -> &DIBuilder<'ll> { + // SAFETY: This is an owning pointer, so `&DIBuilder` is valid + // for as long as `&self` is. + unsafe { self.raw.as_ref() } + } +} + +impl<'ll> Drop for DIBuilderBox<'ll> { + fn drop(&mut self) { + unsafe { llvm::LLVMDisposeDIBuilder(self.raw) }; + } +} /// Extension trait for defining safe wrappers and helper methods on /// `&DIBuilder<'ll>`, without requiring it to be defined in the same crate. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c2c55ee2b64f..1c63bbcd1710 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -38,8 +38,9 @@ use self::namespace::mangled_name_of_instance; use self::utils::{DIB, create_DIArray, is_node_local_to_unit}; use crate::builder::Builder; use crate::common::{AsCCharPtr, CodegenCx}; +use crate::debuginfo::di_builder::DIBuilderBox; use crate::llvm::debuginfo::{ - DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, + DIArray, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DITemplateTypeParameter, DIType, DIVariable, }; use crate::llvm::{self, Value}; diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index dd9fde0b08c6..f2e147b1ee0a 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -32,6 +32,11 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { } } +#[cfg(feature = "llvm_enzyme")] +#[derive(Diagnostic)] +#[diag(codegen_llvm_autodiff_component_unavailable)] +pub(crate) struct AutoDiffComponentUnavailable; + #[derive(Diagnostic)] #[diag(codegen_llvm_autodiff_without_lto)] pub(crate) struct AutoDiffWithoutLto; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index e43bd8c2feef..3bc890310cc8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -26,7 +26,7 @@ use tracing::debug; use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; -use crate::builder::gpu_offload::TgtOffloadEntry; +use crate::builder::gpu_offload::{gen_call_handling, gen_define_handling}; use crate::context::CodegenCx; use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, OffloadWithoutEnable, OffloadWithoutFatLTO, @@ -480,6 +480,14 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let use_integer_compare = match layout.backend_repr() { Scalar(_) | ScalarPair(_, _) => true, SimdVector { .. } => false, + ScalableVector { .. } => { + tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType { + span, + name: sym::raw_eq, + ty: tp_ty, + }); + return Ok(()); + } Memory { .. } => { // For rusty ABIs, small aggregates are actually passed // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`), @@ -1287,8 +1295,6 @@ fn codegen_offload<'ll, 'tcx>( let args = get_args_from_tuple(bx, args[1], fn_target); let target_symbol = symbol_name_for_instance_in_crate(tcx, fn_target, LOCAL_CRATE); - let offload_entry_ty = TgtOffloadEntry::new_decl(&cx); - let sig = tcx.fn_sig(fn_target.def_id()).skip_binder().skip_binder(); let inputs = sig.inputs(); @@ -1296,17 +1302,16 @@ fn codegen_offload<'ll, 'tcx>( let types = inputs.iter().map(|ty| cx.layout_of(*ty).llvm_type(cx)).collect::>(); - let offload_data = crate::builder::gpu_offload::gen_define_handling( - cx, - offload_entry_ty, - &metadata, - &types, - &target_symbol, - ); - - // FIXME(Sa4dUs): pass the original builder once we separate kernel launch logic from globals - let bb = unsafe { llvm::LLVMGetInsertBlock(bx.llbuilder) }; - crate::builder::gpu_offload::gen_call_handling(cx, bb, &offload_data, &args, &types, &metadata); + let offload_globals_ref = cx.offload_globals.borrow(); + let offload_globals = match offload_globals_ref.as_ref() { + Some(globals) => globals, + None => { + // Offload is not initialized, cannot continue + return; + } + }; + let offload_data = gen_define_handling(&cx, &metadata, &types, target_symbol, offload_globals); + gen_call_handling(bx, &offload_data, &args, &types, &metadata, offload_globals); } fn get_args_from_tuple<'ll, 'tcx>( @@ -1679,11 +1684,27 @@ fn generic_simd_intrinsic<'ll, 'tcx>( m_len == v_len, InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } ); - let in_elem_bitwidth = require_int_or_uint_ty!( - m_elem_ty.kind(), - InvalidMonomorphization::MaskWrongElementType { span, name, ty: m_elem_ty } - ); - let m_i1s = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len); + + let m_i1s = if args[1].layout.ty.is_scalable_vector() { + match m_elem_ty.kind() { + ty::Bool => {} + _ => return_error!(InvalidMonomorphization::MaskWrongElementType { + span, + name, + ty: m_elem_ty + }), + }; + let i1 = bx.type_i1(); + let i1xn = bx.type_scalable_vector(i1, m_len as u64); + bx.trunc(args[0].immediate(), i1xn) + } else { + let in_elem_bitwidth = require_int_or_uint_ty!( + m_elem_ty.kind(), + InvalidMonomorphization::MaskWrongElementType { span, name, ty: m_elem_ty } + ); + vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len) + }; + return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 0da5810518c5..65e1bc5c8434 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,7 +5,6 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(slice_as_array))] #![feature(assert_matches)] #![feature(extern_types)] #![feature(file_buffered)] @@ -13,6 +12,7 @@ #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] #![feature(macro_derive)] +#![feature(once_cell_try)] #![feature(trim_prefix_suffix)] #![feature(try_blocks)] // tidy-alphabetical-end @@ -240,6 +240,19 @@ impl CodegenBackend for LlvmCodegenBackend { fn init(&self, sess: &Session) { llvm_util::init(sess); // Make sure llvm is inited + + #[cfg(feature = "llvm_enzyme")] + { + use rustc_session::config::AutoDiff; + + use crate::back::lto::enable_autodiff_settings; + if sess.opts.unstable_opts.autodiff.contains(&AutoDiff::Enable) { + if let Err(_) = llvm::EnzymeWrapper::get_or_init(&sess.opts.sysroot) { + sess.dcx().emit_fatal(crate::errors::AutoDiffComponentUnavailable); + } + enable_autodiff_settings(&sess.opts.unstable_opts.autodiff); + } + } } fn provide(&self, providers: &mut Providers) { diff --git a/compiler/rustc_codegen_llvm/src/llvm/conversions.rs b/compiler/rustc_codegen_llvm/src/llvm/conversions.rs index 9e9f9339ade8..3bc790ca7cff 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/conversions.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/conversions.rs @@ -1,6 +1,6 @@ //! Conversions from backend-independent data types to/from LLVM FFI types. -use rustc_codegen_ssa::common::{AtomicRmwBinOp, IntPredicate, RealPredicate}; +use rustc_codegen_ssa::common::{AtomicRmwBinOp, IntPredicate, RealPredicate, TypeKind}; use rustc_middle::ty::AtomicOrdering; use rustc_session::config::DebugInfo; use rustc_target::spec::SymbolVisibility; @@ -9,10 +9,22 @@ use crate::llvm; /// Helper trait for converting backend-independent types to LLVM-specific /// types, for FFI purposes. +/// +/// FIXME(#147327): These trait/method names were chosen to avoid churn in +/// existing code, but are not great and could probably be made clearer. pub(crate) trait FromGeneric { fn from_generic(other: T) -> Self; } +/// Helper trait for converting LLVM-specific types to backend-independent +/// types, for FFI purposes. +/// +/// FIXME(#147327): These trait/method names were chosen to avoid churn in +/// existing code, but are not great and could probably be made clearer. +pub(crate) trait ToGeneric { + fn to_generic(&self) -> T; +} + impl FromGeneric for llvm::Visibility { fn from_generic(visibility: SymbolVisibility) -> Self { match visibility { @@ -113,3 +125,29 @@ impl FromGeneric for llvm::debuginfo::DebugEmissionKind { } } } + +impl ToGeneric for llvm::TypeKind { + fn to_generic(&self) -> TypeKind { + match self { + Self::Void => TypeKind::Void, + Self::Half => TypeKind::Half, + Self::Float => TypeKind::Float, + Self::Double => TypeKind::Double, + Self::X86_FP80 => TypeKind::X86_FP80, + Self::FP128 => TypeKind::FP128, + Self::PPC_FP128 => TypeKind::PPC_FP128, + Self::Label => TypeKind::Label, + Self::Integer => TypeKind::Integer, + Self::Function => TypeKind::Function, + Self::Struct => TypeKind::Struct, + Self::Array => TypeKind::Array, + Self::Pointer => TypeKind::Pointer, + Self::Vector => TypeKind::Vector, + Self::Metadata => TypeKind::Metadata, + Self::Token => TypeKind::Token, + Self::ScalableVector => TypeKind::ScalableVector, + Self::BFloat => TypeKind::BFloat, + Self::X86_AMX => TypeKind::X86_AMX, + } + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index e63043b21227..956a4e43a7de 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -91,102 +91,361 @@ pub(crate) use self::Enzyme_AD::*; #[cfg(feature = "llvm_enzyme")] pub(crate) mod Enzyme_AD { - use std::ffi::{CString, c_char}; + use std::ffi::{c_char, c_void}; + use std::sync::{Mutex, MutexGuard, OnceLock}; - use libc::c_void; + use rustc_middle::bug; + use rustc_session::config::{Sysroot, host_tuple}; + use rustc_session::filesearch; use super::{CConcreteType, CTypeTreeRef, Context}; + use crate::llvm::{EnzymeTypeTree, LLVMRustVersionMajor}; - 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); + type EnzymeSetCLBoolFn = unsafe extern "C" fn(*mut c_void, u8); + type EnzymeSetCLStringFn = unsafe extern "C" fn(*mut c_void, *const c_char); + + type EnzymeNewTypeTreeFn = unsafe extern "C" fn() -> CTypeTreeRef; + type EnzymeNewTypeTreeCTFn = unsafe extern "C" fn(CConcreteType, &Context) -> CTypeTreeRef; + type EnzymeNewTypeTreeTRFn = unsafe extern "C" fn(CTypeTreeRef) -> CTypeTreeRef; + type EnzymeFreeTypeTreeFn = unsafe extern "C" fn(CTypeTreeRef); + type EnzymeMergeTypeTreeFn = unsafe extern "C" fn(CTypeTreeRef, CTypeTreeRef) -> bool; + type EnzymeTypeTreeOnlyEqFn = unsafe extern "C" fn(CTypeTreeRef, i64); + type EnzymeTypeTreeData0EqFn = unsafe extern "C" fn(CTypeTreeRef); + type EnzymeTypeTreeShiftIndiciesEqFn = + unsafe extern "C" fn(CTypeTreeRef, *const c_char, i64, i64, u64); + type EnzymeTypeTreeInsertEqFn = + unsafe extern "C" fn(CTypeTreeRef, *const i64, usize, CConcreteType, &Context); + type EnzymeTypeTreeToStringFn = unsafe extern "C" fn(CTypeTreeRef) -> *const c_char; + type EnzymeTypeTreeToStringFreeFn = unsafe extern "C" fn(*const c_char); + + #[allow(non_snake_case)] + pub(crate) struct EnzymeWrapper { + EnzymeNewTypeTree: EnzymeNewTypeTreeFn, + EnzymeNewTypeTreeCT: EnzymeNewTypeTreeCTFn, + EnzymeNewTypeTreeTR: EnzymeNewTypeTreeTRFn, + EnzymeFreeTypeTree: EnzymeFreeTypeTreeFn, + EnzymeMergeTypeTree: EnzymeMergeTypeTreeFn, + EnzymeTypeTreeOnlyEq: EnzymeTypeTreeOnlyEqFn, + EnzymeTypeTreeData0Eq: EnzymeTypeTreeData0EqFn, + EnzymeTypeTreeShiftIndiciesEq: EnzymeTypeTreeShiftIndiciesEqFn, + EnzymeTypeTreeInsertEq: EnzymeTypeTreeInsertEqFn, + EnzymeTypeTreeToString: EnzymeTypeTreeToStringFn, + EnzymeTypeTreeToStringFree: EnzymeTypeTreeToStringFreeFn, + + EnzymePrintPerf: *mut c_void, + EnzymePrintActivity: *mut c_void, + EnzymePrintType: *mut c_void, + EnzymeFunctionToAnalyze: *mut c_void, + EnzymePrint: *mut c_void, + EnzymeStrictAliasing: *mut c_void, + EnzymeInline: *mut c_void, + EnzymeMaxTypeDepth: *mut c_void, + RustTypeRules: *mut c_void, + looseTypeAnalysis: *mut c_void, + + EnzymeSetCLBool: EnzymeSetCLBoolFn, + EnzymeSetCLString: EnzymeSetCLStringFn, + pub registerEnzymeAndPassPipeline: *const c_void, + lib: libloading::Library, } - // 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, + unsafe impl Sync for EnzymeWrapper {} + unsafe impl Send for EnzymeWrapper {} + + fn load_ptr_by_symbol_mut_void( + lib: &libloading::Library, + bytes: &[u8], + ) -> Result<*mut c_void, Box> { + unsafe { + let s: libloading::Symbol<'_, *mut c_void> = lib.get(bytes)?; + // libloading = 0.9.0: try_as_raw_ptr always succeeds and returns Some + let s = s.try_as_raw_ptr().unwrap(); + Ok(s) + } + } + + // e.g. + // load_ptrs_by_symbols_mut_void(ABC, XYZ); + // => + // let ABC = load_ptr_mut_void(&lib, b"ABC")?; + // let XYZ = load_ptr_mut_void(&lib, b"XYZ")?; + macro_rules! load_ptrs_by_symbols_mut_void { + ($lib:expr, $($name:ident),* $(,)?) => { + $( + #[allow(non_snake_case)] + let $name = load_ptr_by_symbol_mut_void(&$lib, stringify!($name).as_bytes())?; + )* + }; + } + + // e.g. + // load_ptrs_by_symbols_fn(ABC: ABCFn, XYZ: XYZFn); + // => + // let ABC: libloading::Symbol<'_, ABCFn> = unsafe { lib.get(b"ABC")? }; + // let XYZ: libloading::Symbol<'_, XYZFn> = unsafe { lib.get(b"XYZ")? }; + macro_rules! load_ptrs_by_symbols_fn { + ($lib:expr, $($name:ident : $ty:ty),* $(,)?) => { + $( + #[allow(non_snake_case)] + let $name: $ty = *unsafe { $lib.get::<$ty>(stringify!($name).as_bytes())? }; + )* + }; + } + + static ENZYME_INSTANCE: OnceLock> = OnceLock::new(); + + impl EnzymeWrapper { + /// Initialize EnzymeWrapper with the given sysroot if not already initialized. + /// Safe to call multiple times - subsequent calls are no-ops due to OnceLock. + pub(crate) fn get_or_init( + sysroot: &rustc_session::config::Sysroot, + ) -> Result, Box> { + let mtx: &'static Mutex = ENZYME_INSTANCE.get_or_try_init(|| { + let w = Self::call_dynamic(sysroot)?; + Ok::<_, Box>(Mutex::new(w)) + })?; + + Ok(mtx.lock().unwrap()) + } + + /// Get the EnzymeWrapper instance. Panics if not initialized. + pub(crate) fn get_instance() -> MutexGuard<'static, Self> { + ENZYME_INSTANCE + .get() + .expect("EnzymeWrapper not initialized. Call get_or_init with sysroot first.") + .lock() + .unwrap() + } + + pub(crate) fn new_type_tree(&self) -> CTypeTreeRef { + unsafe { (self.EnzymeNewTypeTree)() } + } + + pub(crate) fn new_type_tree_ct( + &self, + t: CConcreteType, + ctx: &Context, + ) -> *mut EnzymeTypeTree { + unsafe { (self.EnzymeNewTypeTreeCT)(t, ctx) } + } + + pub(crate) fn new_type_tree_tr(&self, tree: CTypeTreeRef) -> CTypeTreeRef { + unsafe { (self.EnzymeNewTypeTreeTR)(tree) } + } + + pub(crate) fn free_type_tree(&self, tree: CTypeTreeRef) { + unsafe { (self.EnzymeFreeTypeTree)(tree) } + } + + pub(crate) fn merge_type_tree(&self, tree1: CTypeTreeRef, tree2: CTypeTreeRef) -> bool { + unsafe { (self.EnzymeMergeTypeTree)(tree1, tree2) } + } + + pub(crate) fn tree_only_eq(&self, tree: CTypeTreeRef, num: i64) { + unsafe { (self.EnzymeTypeTreeOnlyEq)(tree, num) } + } + + pub(crate) fn tree_data0_eq(&self, tree: CTypeTreeRef) { + unsafe { (self.EnzymeTypeTreeData0Eq)(tree) } + } + + pub(crate) fn shift_indicies_eq( + &self, + tree: CTypeTreeRef, data_layout: *const c_char, offset: i64, max_size: i64, add_offset: u64, - ); - pub(crate) fn EnzymeTypeTreeInsertEq( - CTT: CTypeTreeRef, + ) { + unsafe { + (self.EnzymeTypeTreeShiftIndiciesEq)( + tree, + data_layout, + offset, + max_size, + add_offset, + ) + } + } + + pub(crate) fn tree_insert_eq( + &self, + tree: 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 { (self.EnzymeTypeTreeInsertEq)(tree, indices, len, ct, ctx) } + } - unsafe extern "C" { - static mut EnzymePrintPerf: c_void; - static mut EnzymePrintActivity: c_void; - static mut EnzymePrintType: c_void; - static mut EnzymeFunctionToAnalyze: c_void; - static mut EnzymePrint: c_void; - static mut EnzymeStrictAliasing: c_void; - static mut looseTypeAnalysis: c_void; - static mut EnzymeInline: c_void; - static mut RustTypeRules: c_void; - } - pub(crate) fn set_print_perf(print: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintPerf), print as u8); + pub(crate) fn tree_to_string(&self, tree: *mut EnzymeTypeTree) -> *const c_char { + unsafe { (self.EnzymeTypeTreeToString)(tree) } } - } - pub(crate) fn set_print_activity(print: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintActivity), print as u8); + + pub(crate) fn tree_to_string_free(&self, ch: *const c_char) { + unsafe { (self.EnzymeTypeTreeToStringFree)(ch) } } - } - pub(crate) fn set_print_type(print: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrintType), print as u8); + + pub(crate) fn get_max_type_depth(&self) -> usize { + unsafe { std::ptr::read::(self.EnzymeMaxTypeDepth as *const u32) as usize } } - } - pub(crate) fn set_print_type_fun(fun_name: &str) { - let c_fun_name = CString::new(fun_name).unwrap(); - unsafe { - EnzymeSetCLString( - std::ptr::addr_of_mut!(EnzymeFunctionToAnalyze), - c_fun_name.as_ptr() as *const c_char, + + pub(crate) fn set_print_perf(&mut self, print: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.EnzymePrintPerf, print as u8); + } + } + + pub(crate) fn set_print_activity(&mut self, print: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.EnzymePrintActivity, print as u8); + } + } + + pub(crate) fn set_print_type(&mut self, print: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.EnzymePrintType, print as u8); + } + } + + pub(crate) fn set_print_type_fun(&mut self, fun_name: &str) { + let c_fun_name = std::ffi::CString::new(fun_name) + .unwrap_or_else(|err| bug!("failed to set_print_type_fun: {err}")); + unsafe { + (self.EnzymeSetCLString)( + self.EnzymeFunctionToAnalyze, + c_fun_name.as_ptr() as *const c_char, + ); + } + } + + pub(crate) fn set_print(&mut self, print: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.EnzymePrint, print as u8); + } + } + + pub(crate) fn set_strict_aliasing(&mut self, strict: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.EnzymeStrictAliasing, strict as u8); + } + } + + pub(crate) fn set_loose_types(&mut self, loose: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.looseTypeAnalysis, loose as u8); + } + } + + pub(crate) fn set_inline(&mut self, val: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.EnzymeInline, val as u8); + } + } + + pub(crate) fn set_rust_rules(&mut self, val: bool) { + unsafe { + (self.EnzymeSetCLBool)(self.RustTypeRules, val as u8); + } + } + + #[allow(non_snake_case)] + fn call_dynamic( + sysroot: &rustc_session::config::Sysroot, + ) -> Result> { + let enzyme_path = Self::get_enzyme_path(sysroot)?; + let lib = unsafe { libloading::Library::new(enzyme_path)? }; + + load_ptrs_by_symbols_fn!( + lib, + EnzymeNewTypeTree: EnzymeNewTypeTreeFn, + EnzymeNewTypeTreeCT: EnzymeNewTypeTreeCTFn, + EnzymeNewTypeTreeTR: EnzymeNewTypeTreeTRFn, + EnzymeFreeTypeTree: EnzymeFreeTypeTreeFn, + EnzymeMergeTypeTree: EnzymeMergeTypeTreeFn, + EnzymeTypeTreeOnlyEq: EnzymeTypeTreeOnlyEqFn, + EnzymeTypeTreeData0Eq: EnzymeTypeTreeData0EqFn, + EnzymeTypeTreeShiftIndiciesEq: EnzymeTypeTreeShiftIndiciesEqFn, + EnzymeTypeTreeInsertEq: EnzymeTypeTreeInsertEqFn, + EnzymeTypeTreeToString: EnzymeTypeTreeToStringFn, + EnzymeTypeTreeToStringFree: EnzymeTypeTreeToStringFreeFn, + EnzymeSetCLBool: EnzymeSetCLBoolFn, + EnzymeSetCLString: EnzymeSetCLStringFn, ); + + load_ptrs_by_symbols_mut_void!( + lib, + registerEnzymeAndPassPipeline, + EnzymePrintPerf, + EnzymePrintActivity, + EnzymePrintType, + EnzymeFunctionToAnalyze, + EnzymePrint, + EnzymeStrictAliasing, + EnzymeInline, + EnzymeMaxTypeDepth, + RustTypeRules, + looseTypeAnalysis, + ); + + Ok(Self { + EnzymeNewTypeTree, + EnzymeNewTypeTreeCT, + EnzymeNewTypeTreeTR, + EnzymeFreeTypeTree, + EnzymeMergeTypeTree, + EnzymeTypeTreeOnlyEq, + EnzymeTypeTreeData0Eq, + EnzymeTypeTreeShiftIndiciesEq, + EnzymeTypeTreeInsertEq, + EnzymeTypeTreeToString, + EnzymeTypeTreeToStringFree, + EnzymePrintPerf, + EnzymePrintActivity, + EnzymePrintType, + EnzymeFunctionToAnalyze, + EnzymePrint, + EnzymeStrictAliasing, + EnzymeInline, + EnzymeMaxTypeDepth, + RustTypeRules, + looseTypeAnalysis, + EnzymeSetCLBool, + EnzymeSetCLString, + registerEnzymeAndPassPipeline, + lib, + }) } - } - pub(crate) fn set_print(print: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymePrint), print as u8); - } - } - pub(crate) fn set_strict_aliasing(strict: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymeStrictAliasing), strict as u8); - } - } - pub(crate) fn set_loose_types(loose: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(looseTypeAnalysis), loose as u8); - } - } - pub(crate) fn set_inline(val: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(EnzymeInline), val as u8); - } - } - pub(crate) fn set_rust_rules(val: bool) { - unsafe { - EnzymeSetCLBool(std::ptr::addr_of_mut!(RustTypeRules), val as u8); + + fn get_enzyme_path(sysroot: &Sysroot) -> Result { + let llvm_version_major = unsafe { LLVMRustVersionMajor() }; + + let path_buf = sysroot + .all_paths() + .map(|sysroot_path| { + filesearch::make_target_lib_path(sysroot_path, host_tuple()) + .join("lib") + .with_file_name(format!("libEnzyme-{llvm_version_major}")) + .with_extension(std::env::consts::DLL_EXTENSION) + }) + .find(|f| f.exists()) + .ok_or_else(|| { + let candidates = sysroot + .all_paths() + .map(|p| p.join("lib").display().to_string()) + .collect::>() + .join("\n* "); + format!( + "failed to find a `libEnzyme-{llvm_version_major}` folder \ + in the sysroot candidates:\n* {candidates}" + ) + })?; + + Ok(path_buf + .to_str() + .ok_or_else(|| format!("invalid UTF-8 in path: {}", path_buf.display()))? + .to_string()) } } } @@ -198,111 +457,156 @@ pub(crate) use self::Fallback_AD::*; pub(crate) mod Fallback_AD { #![allow(unused_variables)] + use std::ffi::c_void; + use std::sync::{Mutex, MutexGuard}; + use libc::c_char; + use rustc_codegen_ssa::back::write::CodegenContext; + use rustc_codegen_ssa::traits::WriteBackendMethods; - use super::{CConcreteType, CTypeTreeRef, Context}; + use super::{CConcreteType, CTypeTreeRef, Context, EnzymeTypeTree}; - // TypeTree function fallbacks - pub(crate) unsafe fn EnzymeNewTypeTree() -> CTypeTreeRef { - unimplemented!() + pub(crate) struct EnzymeWrapper { + pub registerEnzymeAndPassPipeline: *const c_void, } - pub(crate) unsafe fn EnzymeNewTypeTreeCT(arg1: CConcreteType, ctx: &Context) -> CTypeTreeRef { - unimplemented!() - } + impl EnzymeWrapper { + pub(crate) fn get_or_init( + _sysroot: &rustc_session::config::Sysroot, + ) -> Result, Box> { + unimplemented!("Enzyme not available: build with llvm_enzyme feature") + } - pub(crate) unsafe fn EnzymeNewTypeTreeTR(arg1: CTypeTreeRef) -> CTypeTreeRef { - unimplemented!() - } + pub(crate) fn init<'a, B: WriteBackendMethods>( + _cgcx: &'a CodegenContext, + ) -> &'static Mutex { + unimplemented!("Enzyme not available: build with llvm_enzyme feature") + } - pub(crate) unsafe fn EnzymeFreeTypeTree(CTT: CTypeTreeRef) { - unimplemented!() - } + pub(crate) fn get_instance() -> MutexGuard<'static, Self> { + unimplemented!("Enzyme not available: build with llvm_enzyme feature") + } - pub(crate) unsafe fn EnzymeMergeTypeTree(arg1: CTypeTreeRef, arg2: CTypeTreeRef) -> bool { - unimplemented!() - } + pub(crate) fn new_type_tree(&self) -> CTypeTreeRef { + unimplemented!() + } - pub(crate) unsafe fn EnzymeTypeTreeOnlyEq(arg1: CTypeTreeRef, pos: i64) { - unimplemented!() - } + pub(crate) fn new_type_tree_ct( + &self, + t: CConcreteType, + ctx: &Context, + ) -> *mut EnzymeTypeTree { + unimplemented!() + } - pub(crate) unsafe fn EnzymeTypeTreeData0Eq(arg1: CTypeTreeRef) { - unimplemented!() - } + pub(crate) fn new_type_tree_tr(&self, tree: CTypeTreeRef) -> 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) fn free_type_tree(&self, tree: CTypeTreeRef) { + unimplemented!() + } - pub(crate) unsafe fn EnzymeTypeTreeInsertEq( - CTT: CTypeTreeRef, - indices: *const i64, - len: usize, - ct: CConcreteType, - ctx: &Context, - ) { - unimplemented!() - } + pub(crate) fn merge_type_tree(&self, tree1: CTypeTreeRef, tree2: CTypeTreeRef) -> bool { + unimplemented!() + } - pub(crate) unsafe fn EnzymeTypeTreeToString(arg1: CTypeTreeRef) -> *const c_char { - unimplemented!() - } + pub(crate) fn tree_only_eq(&self, tree: CTypeTreeRef, num: i64) { + unimplemented!() + } - pub(crate) unsafe fn EnzymeTypeTreeToStringFree(arg1: *const c_char) { - unimplemented!() - } + pub(crate) fn tree_data0_eq(&self, tree: CTypeTreeRef) { + unimplemented!() + } - pub(crate) fn set_inline(val: bool) { - unimplemented!() - } - pub(crate) fn set_print_perf(print: bool) { - unimplemented!() - } - pub(crate) fn set_print_activity(print: bool) { - unimplemented!() - } - pub(crate) fn set_print_type(print: bool) { - unimplemented!() - } - pub(crate) fn set_print_type_fun(fun_name: &str) { - unimplemented!() - } - pub(crate) fn set_print(print: bool) { - unimplemented!() - } - pub(crate) fn set_strict_aliasing(strict: bool) { - unimplemented!() - } - pub(crate) fn set_loose_types(loose: bool) { - unimplemented!() - } - pub(crate) fn set_rust_rules(val: bool) { - unimplemented!() + pub(crate) fn shift_indicies_eq( + &self, + tree: CTypeTreeRef, + data_layout: *const c_char, + offset: i64, + max_size: i64, + add_offset: u64, + ) { + unimplemented!() + } + + pub(crate) fn tree_insert_eq( + &self, + tree: CTypeTreeRef, + indices: *const i64, + len: usize, + ct: CConcreteType, + ctx: &Context, + ) { + unimplemented!() + } + + pub(crate) fn tree_to_string(&self, tree: *mut EnzymeTypeTree) -> *const c_char { + unimplemented!() + } + + pub(crate) fn tree_to_string_free(&self, ch: *const c_char) { + unimplemented!() + } + + pub(crate) fn get_max_type_depth(&self) -> usize { + unimplemented!() + } + + pub(crate) fn set_inline(&mut self, val: bool) { + unimplemented!() + } + + pub(crate) fn set_print_perf(&mut self, print: bool) { + unimplemented!() + } + + pub(crate) fn set_print_activity(&mut self, print: bool) { + unimplemented!() + } + + pub(crate) fn set_print_type(&mut self, print: bool) { + unimplemented!() + } + + pub(crate) fn set_print_type_fun(&mut self, fun_name: &str) { + unimplemented!() + } + + pub(crate) fn set_print(&mut self, print: bool) { + unimplemented!() + } + + pub(crate) fn set_strict_aliasing(&mut self, strict: bool) { + unimplemented!() + } + + pub(crate) fn set_loose_types(&mut self, loose: bool) { + unimplemented!() + } + + pub(crate) fn set_rust_rules(&mut self, val: bool) { + unimplemented!() + } } } impl TypeTree { pub(crate) fn new() -> TypeTree { - let inner = unsafe { EnzymeNewTypeTree() }; + let wrapper = EnzymeWrapper::get_instance(); + let inner = wrapper.new_type_tree(); TypeTree { inner } } pub(crate) fn from_type(t: CConcreteType, ctx: &Context) -> TypeTree { - let inner = unsafe { EnzymeNewTypeTreeCT(t, ctx) }; + let wrapper = EnzymeWrapper::get_instance(); + let inner = wrapper.new_type_tree_ct(t, ctx); TypeTree { inner } } pub(crate) fn merge(self, other: Self) -> Self { - unsafe { - EnzymeMergeTypeTree(self.inner, other.inner); - } + let wrapper = EnzymeWrapper::get_instance(); + wrapper.merge_type_tree(self.inner, other.inner); drop(other); self } @@ -316,37 +620,36 @@ impl TypeTree { 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, - ); - } + let wrapper = EnzymeWrapper::get_instance(); + wrapper.shift_indicies_eq( + 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); - } + let wrapper = EnzymeWrapper::get_instance(); + wrapper.tree_insert_eq(self.inner, indices.as_ptr(), indices.len(), ct, ctx); } } impl Clone for TypeTree { fn clone(&self) -> Self { - let inner = unsafe { EnzymeNewTypeTreeTR(self.inner) }; + let wrapper = EnzymeWrapper::get_instance(); + let inner = wrapper.new_type_tree_tr(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 wrapper = EnzymeWrapper::get_instance(); + let ptr = wrapper.tree_to_string(self.inner); let cstr = unsafe { std::ffi::CStr::from_ptr(ptr) }; match cstr.to_str() { Ok(x) => write!(f, "{}", x)?, @@ -354,9 +657,7 @@ impl std::fmt::Display for TypeTree { } // delete C string pointer - unsafe { - EnzymeTypeTreeToStringFree(ptr); - } + wrapper.tree_to_string_free(ptr); Ok(()) } @@ -370,6 +671,7 @@ impl std::fmt::Debug for TypeTree { impl Drop for TypeTree { fn drop(&mut self) { - unsafe { EnzymeFreeTypeTree(self.inner) } + let wrapper = EnzymeWrapper::get_instance(); + wrapper.free_type_tree(self.inner) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8abf8b24d9d6..a86b4cc38915 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -363,33 +363,6 @@ pub(crate) enum TypeKind { X86_AMX = 19, } -impl TypeKind { - pub(crate) fn to_generic(self) -> rustc_codegen_ssa::common::TypeKind { - use rustc_codegen_ssa::common::TypeKind as Common; - match self { - Self::Void => Common::Void, - Self::Half => Common::Half, - Self::Float => Common::Float, - Self::Double => Common::Double, - Self::X86_FP80 => Common::X86_FP80, - Self::FP128 => Common::FP128, - Self::PPC_FP128 => Common::PPC_FP128, - Self::Label => Common::Label, - Self::Integer => Common::Integer, - Self::Function => Common::Function, - Self::Struct => Common::Struct, - Self::Array => Common::Array, - Self::Pointer => Common::Pointer, - Self::Vector => Common::Vector, - Self::Metadata => Common::Metadata, - Self::Token => Common::Token, - Self::ScalableVector => Common::ScalableVector, - Self::BFloat => Common::BFloat, - Self::X86_AMX => Common::X86_AMX, - } - } -} - /// LLVMAtomicRmwBinOp #[derive(Copy, Clone)] #[repr(C)] @@ -738,12 +711,9 @@ unsafe extern "C" { pub(crate) type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void); pub(crate) mod debuginfo { - use std::ptr; - use bitflags::bitflags; use super::{InvariantOpaque, Metadata}; - use crate::llvm::{self, Module}; /// Opaque target type for references to an LLVM debuginfo builder. /// @@ -756,33 +726,6 @@ pub(crate) mod debuginfo { #[repr(C)] pub(crate) struct DIBuilder<'ll>(InvariantOpaque<'ll>); - /// Owning pointer to a `DIBuilder<'ll>` that will dispose of the builder - /// when dropped. Use `.as_ref()` to get the underlying `&DIBuilder` - /// needed for debuginfo FFI calls. - pub(crate) struct DIBuilderBox<'ll> { - raw: ptr::NonNull>, - } - - impl<'ll> DIBuilderBox<'ll> { - pub(crate) fn new(llmod: &'ll Module) -> Self { - let raw = unsafe { llvm::LLVMCreateDIBuilder(llmod) }; - let raw = ptr::NonNull::new(raw).unwrap(); - Self { raw } - } - - pub(crate) fn as_ref(&self) -> &DIBuilder<'ll> { - // SAFETY: This is an owning pointer, so `&DIBuilder` is valid - // for as long as `&self` is. - unsafe { self.raw.as_ref() } - } - } - - impl<'ll> Drop for DIBuilderBox<'ll> { - fn drop(&mut self) { - unsafe { llvm::LLVMDisposeDIBuilder(self.raw) }; - } - } - pub(crate) type DIDescriptor = Metadata; pub(crate) type DILocation = Metadata; pub(crate) type DIScope = DIDescriptor; @@ -998,6 +941,7 @@ unsafe extern "C" { // Operations on array, pointer, and vector types (sequence types) pub(crate) safe fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; + pub(crate) fn LLVMScalableVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; pub(crate) fn LLVMGetVectorSize(VectorTy: &Type) -> c_uint; @@ -2411,7 +2355,7 @@ unsafe extern "C" { LoopVectorize: bool, DisableSimplifyLibCalls: bool, EmitLifetimeMarkers: bool, - RunEnzyme: bool, + RunEnzyme: *const c_void, PrintBeforeEnzyme: bool, PrintAfterEnzyme: bool, PrintPasses: bool, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 682484595a89..a5a8f8e6a25e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -266,6 +266,10 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("hasleoncasa")), s => Some(LLVMFeature::new(s)), }, + Arch::Wasm32 | Arch::Wasm64 => match s { + "gc" if major < 22 => None, + s => Some(LLVMFeature::new(s)), + }, Arch::X86 | Arch::X86_64 => { match s { "sse4.2" => Some(LLVMFeature::with_dependencies( @@ -360,25 +364,25 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { let target_abi = &sess.target.options.abi; let target_pointer_width = sess.target.pointer_width; let version = get_version(); - let lt_20_1_1 = version < (20, 1, 1); - let lt_21_0_0 = version < (21, 0, 0); + let (major, _, _) = version; cfg.has_reliable_f16 = match (target_arch, target_os) { - // LLVM crash without neon (fixed in llvm20) + // LLVM crash without neon (fixed in LLVM 20.1.1) (Arch::AArch64, _) - if !cfg.target_features.iter().any(|f| f.as_str() == "neon") && lt_20_1_1 => + if !cfg.target_features.iter().any(|f| f.as_str() == "neon") + && version < (20, 1, 1) => { false } // Unsupported (Arch::Arm64EC, _) => false, // Selection failure (fixed in llvm21) - (Arch::S390x, _) if lt_21_0_0 => false, + (Arch::S390x, _) if major < 21 => false, // MinGW ABI bugs (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false, // Infinite recursion (Arch::CSky, _) => false, - (Arch::Hexagon, _) if lt_21_0_0 => false, // (fixed in llvm21) + (Arch::Hexagon, _) if major < 21 => false, // (fixed in llvm21) (Arch::PowerPC | Arch::PowerPC64, _) => false, (Arch::Sparc | Arch::Sparc64, _) => false, (Arch::Wasm32 | Arch::Wasm64, _) => false, @@ -389,15 +393,15 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { }; cfg.has_reliable_f128 = match (target_arch, target_os) { + // Unsupported https://github.com/llvm/llvm-project/issues/121122 + (Arch::AmdGpu, _) => false, // Unsupported (Arch::Arm64EC, _) => false, - // Selection bug (fixed in llvm20) - (Arch::Mips64 | Arch::Mips64r6, _) if lt_20_1_1 => false, + // Selection bug (fixed in LLVM 20.1.0) + (Arch::Mips64 | Arch::Mips64r6, _) if version < (20, 1, 0) => false, // Selection bug . This issue is closed // but basic math still does not work. (Arch::Nvptx64, _) => false, - // Unsupported https://github.com/llvm/llvm-project/issues/121122 - (Arch::AmdGpu, _) => false, // ABI bugs et al. (full // list at ) (Arch::PowerPC | Arch::PowerPC64, _) => false, @@ -405,7 +409,7 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { (Arch::Sparc, _) => false, // Stack alignment bug . NB: tests may // not fail if our compiler-builtins is linked. (fixed in llvm21) - (Arch::X86, _) if lt_21_0_0 => false, + (Arch::X86, _) if major < 21 => false, // MinGW ABI bugs (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false, // There are no known problems on other platforms, so the only requirement is that symbols diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 55f053f4fad3..d8b77369a34f 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -15,7 +15,7 @@ use rustc_target::callconv::{CastTarget, FnAbi}; use crate::abi::{FnAbiLlvmExt, LlvmType}; use crate::common; use crate::context::{CodegenCx, GenericCx, SCx}; -use crate::llvm::{self, FALSE, Metadata, TRUE, ToLlvmBool, Type, Value}; +use crate::llvm::{self, FALSE, Metadata, TRUE, ToGeneric, ToLlvmBool, Type, Value}; use crate::type_of::LayoutLlvmExt; impl PartialEq for Type { @@ -68,6 +68,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { unsafe { llvm::LLVMVectorType(ty, len as c_uint) } } + pub(crate) fn type_scalable_vector(&self, ty: &'ll Type, count: u64) -> &'ll Type { + unsafe { llvm::LLVMScalableVectorType(ty, count as c_uint) } + } + pub(crate) fn add_func(&self, name: &str, ty: &'ll Type) -> &'ll Value { let name = SmallCStr::new(name); unsafe { llvm::LLVMAddFunction(self.llmod(), name.as_ptr(), ty) } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 2b2ac1c6eb29..1ed06fbd2821 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -24,6 +24,15 @@ fn uncached_llvm_type<'a, 'tcx>( let element = layout.scalar_llvm_type_at(cx, element); return cx.type_vector(element, count); } + BackendRepr::ScalableVector { ref element, count } => { + let element = if element.is_bool() { + cx.type_i1() + } else { + layout.scalar_llvm_type_at(cx, *element) + }; + + return cx.type_scalable_vector(element, count); + } BackendRepr::Memory { .. } | BackendRepr::ScalarPair(..) => {} } @@ -176,7 +185,9 @@ pub(crate) trait LayoutLlvmExt<'tcx> { impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { fn is_llvm_immediate(&self) -> bool { match self.backend_repr { - BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => true, + BackendRepr::Scalar(_) + | BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } => true, BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false, } } @@ -186,6 +197,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { BackendRepr::ScalarPair(..) => true, BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } | BackendRepr::Memory { .. } => false, } } diff --git a/compiler/rustc_codegen_llvm/src/typetree.rs b/compiler/rustc_codegen_llvm/src/typetree.rs index 7e2635037008..513a832e7fe8 100644 --- a/compiler/rustc_codegen_llvm/src/typetree.rs +++ b/compiler/rustc_codegen_llvm/src/typetree.rs @@ -2,6 +2,7 @@ use rustc_ast::expand::typetree::FncTree; #[cfg(feature = "llvm_enzyme")] use { crate::attributes, + crate::llvm::EnzymeWrapper, rustc_ast::expand::typetree::TypeTree as RustTypeTree, std::ffi::{CString, c_char, c_uint}, }; @@ -77,7 +78,8 @@ pub(crate) fn add_tt<'ll>( 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 enzyme_wrapper = EnzymeWrapper::get_instance(); + let c_str = enzyme_wrapper.tree_to_string(enzyme_tt.inner); let c_str = std::ffi::CStr::from_ptr(c_str); let attr = llvm::LLVMCreateStringAttribute( @@ -89,13 +91,14 @@ pub(crate) fn add_tt<'ll>( ); attributes::apply_to_llfn(fn_def, llvm::AttributePlace::Argument(i as u32), &[attr]); - llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + enzyme_wrapper.tree_to_string_free(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 enzyme_wrapper = EnzymeWrapper::get_instance(); + let c_str = enzyme_wrapper.tree_to_string(enzyme_tt.inner); let c_str = std::ffi::CStr::from_ptr(c_str); let ret_attr = llvm::LLVMCreateStringAttribute( @@ -107,7 +110,7 @@ pub(crate) fn add_tt<'ll>( ); attributes::apply_to_llfn(fn_def, llvm::AttributePlace::ReturnValue, &[ret_attr]); - llvm::EnzymeTypeTreeToStringFree(c_str.as_ptr()); + enzyme_wrapper.tree_to_string_free(c_str.as_ptr()); } } diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index add25da025b2..b23415a732cc 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::{ }; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; -use rustc_target::spec::{Abi, Arch}; +use rustc_target::spec::{Abi, Arch, Env}; use crate::builder::Builder; use crate::llvm::{Type, Value}; @@ -549,7 +549,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( registers_for_primitive(scalar1.primitive()); registers_for_primitive(scalar2.primitive()); } - BackendRepr::SimdVector { .. } => { + BackendRepr::SimdVector { .. } | BackendRepr::ScalableVector { .. } => { // Because no instance of VaArgSafe uses a non-scalar `BackendRepr`. unreachable!( "No x86-64 SysV va_arg implementation for {:?}", @@ -689,7 +689,9 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( } } // The Previous match on `BackendRepr` means control flow already escaped. - BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => unreachable!(), + BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } + | BackendRepr::Memory { .. } => unreachable!(), }; // AMD64-ABI 3.5.7p5: Step 5. Set: @@ -780,6 +782,129 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( mem_addr } +fn emit_hexagon_va_arg_musl<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + // Implementation of va_arg for Hexagon musl target. + // Based on LLVM's HexagonBuiltinVaList implementation. + // + // struct __va_list_tag { + // void *__current_saved_reg_area_pointer; + // void *__saved_reg_area_end_pointer; + // void *__overflow_area_pointer; + // }; + // + // All variadic arguments are passed on the stack, but the musl implementation + // uses a register save area for compatibility. + let va_list_addr = list.immediate(); + let layout = bx.cx.layout_of(target_ty); + let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi; + let ptr_size = bx.tcx().data_layout.pointer_size().bytes(); + + // Check if argument fits in register save area + let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg"); + let from_overflow = bx.append_sibling_block("va_arg.from_overflow"); + let end = bx.append_sibling_block("va_arg.end"); + + // Load the three pointers from va_list + let current_ptr_addr = va_list_addr; + let end_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(ptr_size)); + let overflow_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(2 * ptr_size)); + + let current_ptr = bx.load(bx.type_ptr(), current_ptr_addr, ptr_align_abi); + let end_ptr = bx.load(bx.type_ptr(), end_ptr_addr, ptr_align_abi); + let overflow_ptr = bx.load(bx.type_ptr(), overflow_ptr_addr, ptr_align_abi); + + // Align current pointer based on argument type size (following LLVM's implementation) + // Arguments <= 32 bits (4 bytes) use 4-byte alignment, > 32 bits use 8-byte alignment + let type_size_bits = bx.cx.size_of(target_ty).bits(); + let arg_align = if type_size_bits > 32 { + Align::from_bytes(8).unwrap() + } else { + Align::from_bytes(4).unwrap() + }; + let aligned_current = round_pointer_up_to_alignment(bx, current_ptr, arg_align, bx.type_ptr()); + + // Calculate next pointer position (following LLVM's logic) + // Arguments <= 32 bits take 4 bytes, > 32 bits take 8 bytes + let arg_size = if type_size_bits > 32 { 8 } else { 4 }; + let next_ptr = bx.inbounds_ptradd(aligned_current, bx.const_usize(arg_size)); + + // Check if argument fits in register save area + let fits_in_regs = bx.icmp(IntPredicate::IntULE, next_ptr, end_ptr); + bx.cond_br(fits_in_regs, maybe_reg, from_overflow); + + // Load from register save area + bx.switch_to_block(maybe_reg); + let reg_value_addr = aligned_current; + // Update current pointer + bx.store(next_ptr, current_ptr_addr, ptr_align_abi); + bx.br(end); + + // Load from overflow area (stack) + bx.switch_to_block(from_overflow); + + // Align overflow pointer using the same alignment rules + let aligned_overflow = + round_pointer_up_to_alignment(bx, overflow_ptr, arg_align, bx.type_ptr()); + + let overflow_value_addr = aligned_overflow; + // Update overflow pointer - use the same size calculation + let next_overflow = bx.inbounds_ptradd(aligned_overflow, bx.const_usize(arg_size)); + bx.store(next_overflow, overflow_ptr_addr, ptr_align_abi); + + // IMPORTANT: Also update the current saved register area pointer to match + // This synchronizes the pointers when switching to overflow area + bx.store(next_overflow, current_ptr_addr, ptr_align_abi); + bx.br(end); + + // Return the value + bx.switch_to_block(end); + let value_addr = + bx.phi(bx.type_ptr(), &[reg_value_addr, overflow_value_addr], &[maybe_reg, from_overflow]); + bx.load(layout.llvm_type(bx), value_addr, layout.align.abi) +} + +fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + // Implementation of va_arg for Hexagon bare-metal (non-musl) targets. + // Based on LLVM's EmitVAArgForHexagon implementation. + // + // va_list is a simple pointer (char *) + let va_list_addr = list.immediate(); + let layout = bx.cx.layout_of(target_ty); + let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi; + + // Load current pointer from va_list + let current_ptr = bx.load(bx.type_ptr(), va_list_addr, ptr_align_abi); + + // Handle address alignment for types with alignment > 4 bytes + let ty_align = layout.align.abi; + let aligned_ptr = if ty_align.bytes() > 4 { + // Ensure alignment is a power of 2 + debug_assert!(ty_align.bytes().is_power_of_two(), "Alignment is not power of 2!"); + round_pointer_up_to_alignment(bx, current_ptr, ty_align, bx.type_ptr()) + } else { + current_ptr + }; + + // Calculate offset: round up type size to 4-byte boundary (minimum stack slot size) + let type_size = layout.size.bytes(); + let offset = type_size.next_multiple_of(4); // align to 4 bytes + + // Update va_list to point to next argument + let next_ptr = bx.inbounds_ptradd(aligned_ptr, bx.const_usize(offset)); + bx.store(next_ptr, va_list_addr, ptr_align_abi); + + // Load and return the argument value + bx.load(layout.llvm_type(bx), aligned_ptr, layout.align.abi) +} + fn emit_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -964,6 +1089,13 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64. Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty), Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty), + Arch::Hexagon => { + if target.env == Env::Musl { + emit_hexagon_va_arg_musl(bx, addr, target_ty) + } else { + emit_hexagon_va_arg_bare_metal(bx, addr, target_ty) + } + } // For all other architecture/OS combinations fall back to using // the LLVM va_arg instruction. // https://llvm.org/docs/LangRef.html#va-arg-instruction diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index fc6e4fb12574..ff78bea5b67c 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -129,6 +129,7 @@ codegen_ssa_invalid_monomorphization_mask_wrong_element_type = invalid monomorph codegen_ssa_invalid_monomorphization_mismatched_lengths = invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}` +codegen_ssa_invalid_monomorphization_non_scalable_type = invalid monomorphization of `{$name}` intrinsic: expected non-scalable type, found scalable type `{$ty}` codegen_ssa_invalid_monomorphization_return_element = invalid monomorphization of `{$name}` intrinsic: expected return element type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}` with element type `{$out_ty}` codegen_ssa_invalid_monomorphization_return_integer_type = invalid monomorphization of `{$name}` intrinsic: expected return type with integer elements, found `{$ret_ty}` with non-integer `{$out_ty}` diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 9abd7c1dd9c6..9b789d9e62b7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2792,11 +2792,9 @@ fn add_upstream_rust_crates( // We must always link crates `compiler_builtins` and `profiler_builtins` statically. // Even if they were already included into a dylib // (e.g. `libstd` when `-C prefer-dynamic` is used). - // FIXME: `dependency_formats` can report `profiler_builtins` as `NotLinked` for some - // reason, it shouldn't do that because `profiler_builtins` should indeed be linked. let linkage = data[cnum]; let link_static_crate = linkage == Linkage::Static - || (linkage == Linkage::IncludedFromDylib || linkage == Linkage::NotLinked) + || linkage == Linkage::IncludedFromDylib && (codegen_results.crate_info.compiler_builtins == Some(cnum) || codegen_results.crate_info.profiler_runtime == Some(cnum)); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 473598bad394..8135fd43dd93 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -350,6 +350,9 @@ fn process_builtin_attrs( codegen_fn_attrs.flags |= CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM; } } + AttributeKind::ThreadLocal => { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL + } _ => {} } } @@ -366,7 +369,6 @@ fn process_builtin_attrs( sym::rustc_allocator_zeroed => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED } - sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::instruction_set => { codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 84be2af3c9a7..c3d63e332989 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1094,6 +1094,14 @@ pub enum InvalidMonomorphization<'tcx> { expected_element: Ty<'tcx>, vector_type: Ty<'tcx>, }, + + #[diag(codegen_ssa_invalid_monomorphization_non_scalable_type, code = E0511)] + NonScalableType { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, } pub enum ExpectedPointerMutability { diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 0c9acf087f9f..f068a51f7d4a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; use std::marker::PhantomData; use std::ops::Range; -use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, Size, VariantIdx}; +use rustc_abi::{BackendRepr, FieldIdx, FieldsShape, ScalableElt, Size, VariantIdx}; use rustc_data_structures::fx::FxHashMap; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -408,6 +408,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } + // Don't spill `` for `N != 16`: + // + // SVE predicates are only one bit for each byte in an SVE vector (which makes + // sense, the predicate only needs to keep track of whether a lane is + // enabled/disabled). i.e. a `` vector has a `` + // predicate type. `` corresponds to two bytes of storage, + // multiplied by the `vscale`, with one bit for each of the sixteen lanes. + // + // For a vector with fewer elements, such as `svint32_t`/``, + // while only a `` predicate type would be strictly necessary, + // relevant intrinsics still take a `svbool_t`/`` - this is + // because a `` is only half of a byte (for `vscale=1`), and with + // memory being byte-addressable, it's unclear how to store that. + // + // Due to this, LLVM ultimately decided not to support stores of `` + // for `N != 16`. As for `vscale=1` and `N` fewer than sixteen, partial bytes would + // need to be stored (except for `N=8`, but that also isn't supported). `N` can + // never be greater than sixteen as that ends up larger than the 128-bit increment + // size. + // + // Internally, with an intrinsic operating on a `svint32_t`/`` + // (for example), the intrinsic takes the `svbool_t`/`` predicate + // and casts it to a `svbool4_t`/``. Therefore, it's important that + // the `` never spills because that'll cause errors during + // instruction selection. Spilling to the stack to create debuginfo for these + // intermediate values must be avoided and won't degrade the debugging experience + // anyway. + if operand.layout.ty.is_scalable_vector() + && bx.sess().target.arch == rustc_target::spec::Arch::AArch64 + && let ty::Adt(adt, args) = &operand.layout.ty.kind() + && let Some(marker_type_field) = + adt.non_enum_variant().fields.get(FieldIdx::from_u32(0)) + { + let marker_type = marker_type_field.ty(bx.tcx(), args); + // i.e. `` when `N != 16` + if let ty::Slice(element_ty) = marker_type.kind() + && element_ty.is_bool() + && adt.repr().scalable != Some(ScalableElt::ElementCount(16)) + { + return; + } + } + Self::spill_operand_to_stack(*operand, name, bx) } diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 5a139702f81d..6fd118a60d4d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -405,7 +405,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { imm } } - BackendRepr::ScalarPair(_, _) | BackendRepr::Memory { .. } => bug!(), + BackendRepr::ScalarPair(_, _) + | BackendRepr::Memory { .. } + | BackendRepr::ScalableVector { .. } => bug!(), }) }; @@ -692,7 +694,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { BackendRepr::ScalarPair(a, b) => { OperandValueBuilder::Pair(Either::Right(a), Either::Right(b)) } - BackendRepr::SimdVector { .. } => OperandValueBuilder::Vector(Either::Right(())), + BackendRepr::SimdVector { .. } | BackendRepr::ScalableVector { .. } => { + OperandValueBuilder::Vector(Either::Right(())) + } BackendRepr::Memory { .. } => { bug!("Cannot use non-ZST Memory-ABI type in operand builder: {layout:?}"); } diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 50f56f913a51..d62e622b6fed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -109,7 +109,11 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx: &mut Bx, layout: TyAndLayout<'tcx>, ) -> Self { - Self::alloca_size(bx, layout.size, layout) + if layout.is_runtime_sized() { + Self::alloca_runtime_sized(bx, layout) + } else { + Self::alloca_size(bx, layout.size, layout) + } } pub fn alloca_size>( @@ -146,6 +150,18 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bug!("unexpected layout `{:#?}` in PlaceRef::len", self.layout) } } + + fn alloca_runtime_sized>( + bx: &mut Bx, + layout: TyAndLayout<'tcx>, + ) -> Self { + let (element_count, ty) = layout.ty.scalable_vector_element_count_and_type(bx.tcx()); + PlaceValue::new_sized( + bx.scalable_alloca(element_count as u64, layout.align.abi, ty), + layout.align.abi, + ) + .with_type(layout) + } } impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index f57f9108f744..4f45c614e003 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -235,6 +235,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value; fn alloca(&mut self, size: Size, align: Align) -> Self::Value; + fn scalable_alloca(&mut self, elt: u64, align: Align, element_ty: Ty<'_>) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index a7a3bbebed5f..44c817b33184 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -6,14 +6,17 @@ mod simd; use std::assert_matches::assert_matches; -use rustc_abi::{FieldIdx, HasDataLayout, Size, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, HasDataLayout, Size, VariantIdx}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; +use rustc_hir::def_id::CRATE_DEF_ID; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint}; use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_middle::ty::{FloatTy, Ty, TyCtxt}; +use rustc_middle::ty::{FloatTy, PolyExistentialPredicate, Ty, TyCtxt, TypeFoldable}; use rustc_middle::{bug, span_bug, ty}; use rustc_span::{Symbol, sym}; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use tracing::trace; use super::memory::MemoryKind; @@ -219,6 +222,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(Scalar::from_target_usize(offset, self), dest)?; } + sym::vtable_for => { + let tp_ty = instance.args.type_at(0); + let result_ty = instance.args.type_at(1); + + ensure_monomorphic_enough(tcx, tp_ty)?; + ensure_monomorphic_enough(tcx, result_ty)?; + let ty::Dynamic(preds, _) = result_ty.kind() else { + span_bug!( + self.find_closest_untracked_caller_location(), + "Invalid type provided to vtable_for::. U must be dyn Trait, got {result_ty}." + ); + }; + + let (infcx, param_env) = + self.tcx.infer_ctxt().build_with_typing_env(self.typing_env); + + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligations(preds.iter().map(|pred: PolyExistentialPredicate<'_>| { + let pred = pred.with_self_ty(tcx, tp_ty); + // Lifetimes can only be 'static because of the bound on T + let pred = pred.fold_with(&mut ty::BottomUpFolder { + tcx, + ty_op: |ty| ty, + lt_op: |lt| { + if lt == tcx.lifetimes.re_erased { tcx.lifetimes.re_static } else { lt } + }, + ct_op: |ct| ct, + }); + Obligation::new(tcx, ObligationCause::dummy(), param_env, pred) + })); + let type_impls_trait = ocx.evaluate_obligations_error_on_ambiguity().is_empty(); + // Since `assumed_wf_tys=[]` the choice of LocalDefId is irrelevant, so using the "default" + let regions_are_valid = ocx.resolve_regions(CRATE_DEF_ID, param_env, []).is_empty(); + + if regions_are_valid && type_impls_trait { + let vtable_ptr = self.get_vtable_ptr(tp_ty, preds)?; + // Writing a non-null pointer into an `Option` will automatically make it `Some`. + self.write_pointer(vtable_ptr, dest)?; + } else { + // Write `None` + self.write_discriminant(FIRST_VARIANT, dest)?; + } + } sym::variant_count => { let tp_ty = instance.args.type_at(0); let ty = match tp_ty.kind() { diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 88a116094758..9a4b9786eb15 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -561,8 +561,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable }, )?; // Sanity-check that `eval_fn_call` either pushed a new frame or - // did a jump to another block. - if self.frame_idx() == old_stack && self.frame().loc == old_loc { + // did a jump to another block. We disable the sanity check for functions that + // can't return, since Miri sometimes does have to keep the location the same + // for those (which is fine since execution will continue on a different thread). + if target.is_some() && self.frame_idx() == old_stack && self.frame().loc == old_loc + { span_bug!(terminator.source_info.span, "evaluating this call made no progress"); } } diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 34296b6d8de3..5d8ae42f5ecc 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1315,7 +1315,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.visit_scalar(b, b_layout)?; } } - BackendRepr::SimdVector { .. } => { + BackendRepr::SimdVector { .. } | BackendRepr::ScalableVector { .. } => { // No checks here, we assume layout computation gets this right. // (This is harder to check since Miri does not represent these as `Immediate`. We // also cannot use field projections since this might be a newtype around a vector.) diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index b5de10c7dcd1..95106c7c143b 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_abi::{FieldIdx, FieldsShape, VariantIdx, Variants}; -use rustc_index::IndexVec; +use rustc_index::{Idx as _, IndexVec}; use rustc_middle::mir::interpret::InterpResult; use rustc_middle::ty::{self, Ty}; use tracing::trace; @@ -27,13 +27,15 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { /// This function provides the chance to reorder the order in which fields are visited for /// `FieldsShape::Aggregate`. /// - /// The default means we iterate in source declaration order; alternatively this can do some - /// work with `memory_index` to iterate in memory order. + /// The default means we iterate in source declaration order; alternatively this can use + /// `in_memory_order` to iterate in memory order. #[inline(always)] fn aggregate_field_iter( - memory_index: &IndexVec, - ) -> impl Iterator + 'static { - memory_index.indices() + in_memory_order: &IndexVec, + ) -> impl Iterator { + // Allow the optimizer to elide the bounds checking when creating each index. + let _ = FieldIdx::new(in_memory_order.len()); + (0..in_memory_order.len()).map(FieldIdx::new) } // Recursive actions, ready to be overloaded. @@ -168,8 +170,8 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { &FieldsShape::Union(fields) => { self.visit_union(v, fields)?; } - FieldsShape::Arbitrary { memory_index, .. } => { - for idx in Self::aggregate_field_iter(memory_index) { + FieldsShape::Arbitrary { in_memory_order, .. } => { + for idx in Self::aggregate_field_iter(in_memory_order) { let field = self.ecx().project_field(v, idx)?; self.visit_field(v, idx.as_usize(), &field)?; } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 6c74ed2a5121..2fce4b8c0566 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -1,6 +1,5 @@ // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] -#![deny(clippy::manual_let_else)] #![feature(array_try_map)] #![feature(assert_matches)] #![feature(box_patterns)] 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 34bd8423e8e1..939f9151680b 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -119,7 +119,9 @@ fn check_validity_requirement_lax<'tcx>( } BackendRepr::SimdVector { element: s, count } => count == 0 || scalar_allows_raw_init(s), BackendRepr::Memory { .. } => true, // Fields are checked below. + BackendRepr::ScalableVector { element, .. } => scalar_allows_raw_init(element), }; + if !valid { // This is definitely not okay. return Ok(false); diff --git a/compiler/rustc_error_codes/src/error_codes/E0541.md b/compiler/rustc_error_codes/src/error_codes/E0541.md index 96334088feee..f1f97b39fa28 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0541.md +++ b/compiler/rustc_error_codes/src/error_codes/E0541.md @@ -1,8 +1,10 @@ +#### Note: this error code is no longer emitted by the compiler. + An unknown meta item was used. Erroneous code example: -```compile_fail,E0541 +```compile_fail (no longer emitted) #[deprecated( since="1.0.0", // error: unknown meta item diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 7f64dc3df23e..9bb318973958 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -303,17 +303,6 @@ impl AnnotateSnippetEmitter { } } - let suggestions_expected = suggestions - .iter() - .filter(|s| { - matches!( - s.style, - SuggestionStyle::HideCodeInline - | SuggestionStyle::ShowCode - | SuggestionStyle::ShowAlways - ) - }) - .count(); for suggestion in suggestions { match suggestion.style { SuggestionStyle::CompletelyHidden => { @@ -526,12 +515,6 @@ impl AnnotateSnippetEmitter { } } - // FIXME: This hack should be removed once annotate_snippets is the - // default emitter. - if suggestions_expected > 0 && report.is_empty() { - group = group.element(Padding); - } - if !group.is_empty() { report.push(group); } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 78feb60261bd..2e41f74ee25d 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -46,17 +46,14 @@ const DEFAULT_COLUMN_WIDTH: usize = 140; /// Describes the way the content of the `rendered` field of the json output is generated #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum HumanReadableErrorType { - Default { short: bool }, - AnnotateSnippet { short: bool, unicode: bool }, +pub struct HumanReadableErrorType { + pub short: bool, + pub unicode: bool, } impl HumanReadableErrorType { pub fn short(&self) -> bool { - match self { - HumanReadableErrorType::Default { short } - | HumanReadableErrorType::AnnotateSnippet { short, .. } => *short, - } + self.short } } @@ -607,7 +604,7 @@ pub enum OutputTheme { Unicode, } -/// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` +/// Handles the writing of `HumanReadableErrorType` #[derive(Setters)] pub struct HumanEmitter { #[setters(skip)] diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 3b8c8baa5eff..85801245bea9 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -28,8 +28,8 @@ use serde::Serialize; use crate::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use crate::diagnostic::IsLint; use crate::emitter::{ - ColorConfig, Destination, Emitter, HumanEmitter, HumanReadableErrorType, OutputTheme, - TimingEvent, should_show_source_code, + ColorConfig, Destination, Emitter, HumanReadableErrorType, OutputTheme, TimingEvent, + should_show_source_code, }; use crate::registry::Registry; use crate::timings::{TimingRecord, TimingSection}; @@ -378,38 +378,17 @@ impl Diagnostic { choice => choice, }, ); - match je.json_rendered { - HumanReadableErrorType::AnnotateSnippet { short, unicode } => { - AnnotateSnippetEmitter::new(dst, je.translator.clone()) - .short_message(short) - .sm(je.sm.clone()) - .diagnostic_width(je.diagnostic_width) - .macro_backtrace(je.macro_backtrace) - .track_diagnostics(je.track_diagnostics) - .terminal_url(je.terminal_url) - .ui_testing(je.ui_testing) - .ignored_directories_in_source_blocks( - je.ignored_directories_in_source_blocks.clone(), - ) - .theme(if unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) - .emit_diagnostic(diag, registry) - } - HumanReadableErrorType::Default { short } => { - HumanEmitter::new(dst, je.translator.clone()) - .short_message(short) - .sm(je.sm.clone()) - .diagnostic_width(je.diagnostic_width) - .macro_backtrace(je.macro_backtrace) - .track_diagnostics(je.track_diagnostics) - .terminal_url(je.terminal_url) - .ui_testing(je.ui_testing) - .ignored_directories_in_source_blocks( - je.ignored_directories_in_source_blocks.clone(), - ) - .theme(OutputTheme::Ascii) - .emit_diagnostic(diag, registry) - } - } + AnnotateSnippetEmitter::new(dst, je.translator.clone()) + .short_message(je.json_rendered.short) + .sm(je.sm.clone()) + .diagnostic_width(je.diagnostic_width) + .macro_backtrace(je.macro_backtrace) + .track_diagnostics(je.track_diagnostics) + .terminal_url(je.terminal_url) + .ui_testing(je.ui_testing) + .ignored_directories_in_source_blocks(je.ignored_directories_in_source_blocks.clone()) + .theme(if je.json_rendered.unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) + .emit_diagnostic(diag, registry); let buf = Arc::try_unwrap(buf.0).unwrap().into_inner().unwrap(); let buf = String::from_utf8(buf).unwrap(); diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 30be06ae0bd3..c1c9ecf7198f 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -54,7 +54,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { Some(sm), translator, true, // pretty - HumanReadableErrorType::Default { short: true }, + HumanReadableErrorType { short: true, unicode: false }, ColorConfig::Never, ); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 7e765918d7b6..13c1f2219bed 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -61,7 +61,7 @@ declare_features! ( /// Allows explicit discriminants on non-unit enum variants. (accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553)), /// Allows #[cfg(...)] on inline assembly templates and operands. - (accepted, asm_cfg, "CURRENT_RUSTC_VERSION", Some(140364)), + (accepted, asm_cfg, "1.93.0", Some(140364)), /// Allows using `const` operands in inline assembly. (accepted, asm_const, "1.82.0", Some(93332)), /// Allows using `label` operands in inline assembly. @@ -218,7 +218,7 @@ declare_features! ( /// Allows access to crate names passed via `--extern` through prelude. (accepted, extern_prelude, "1.30.0", Some(44660)), /// Allows using `system` as a calling convention with varargs. - (accepted, extern_system_varargs, "CURRENT_RUSTC_VERSION", Some(136946)), + (accepted, extern_system_varargs, "1.93.0", Some(136946)), /// Allows using F16C intrinsics from `core::arch::{x86, x86_64}`. (accepted, f16c_target_feature, "1.68.0", Some(44839)), /// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions. @@ -392,7 +392,7 @@ declare_features! ( /// Allows code like `let x: &'static u32 = &42` to work (RFC 1414). (accepted, rvalue_static_promotion, "1.21.0", Some(38865)), /// Allows use of the `vector` and related s390x target features. - (accepted, s390x_target_feature_vector, "CURRENT_RUSTC_VERSION", Some(145649)), + (accepted, s390x_target_feature_vector, "1.93.0", Some(145649)), /// Allows `Self` in type definitions (RFC 2300). (accepted, self_in_typedefs, "1.32.0", Some(49303)), /// Allows `Self` struct constructor (RFC 2302). diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index c70dbc15e552..a7e8515e415f 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1123,7 +1123,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ template!(Word, List: &[r#""...""#]), DuplicatesOk, EncodeCrossCrate::Yes, ), - rustc_attr!( + rustc_attr!( rustc_offload_kernel, Normal, template!(Word), DuplicatesOk, EncodeCrossCrate::Yes, @@ -1422,6 +1422,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes, "`#[rustc_force_inline]` forces a free function to be inlined" ), + rustc_attr!( + rustc_scalable_vector, Normal, template!(List: &["count"]), WarnFollowing, EncodeCrossCrate::Yes, + "`#[rustc_scalable_vector]` defines a scalable vector type" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index cd61facb39bb..e5d66364c2a6 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -190,7 +190,7 @@ declare_features! ( /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8 (removed, negate_unsigned, "1.0.0", Some(29645), None), /// Allows diverging expressions to fall back to `!` rather than `()`. - (removed, never_type_fallback, "CURRENT_RUSTC_VERSION", Some(65992), Some("removed in favor of unconditional fallback"), 148871), + (removed, never_type_fallback, "1.93.0", Some(65992), Some("removed in favor of unconditional fallback"), 148871), /// Allows `#[no_coverage]` on functions. /// The feature was renamed to `coverage_attribute` and the attribute to `#[coverage(on|off)]` (removed, no_coverage, "1.74.0", Some(84605), Some("renamed to `coverage_attribute`"), 114656), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index fe053935f9e6..692cba8035c4 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -221,7 +221,7 @@ declare_features! ( (internal, compiler_builtins, "1.13.0", None), /// Allows writing custom MIR (internal, custom_mir, "1.65.0", None), - /// Implementation details of externally implementatble items + /// Implementation details of externally implementable items (internal, eii_internals, "CURRENT_RUSTC_VERSION", None), /// Outputs useful `assert!` messages (unstable, generic_assert, "1.63.0", None), @@ -412,7 +412,7 @@ declare_features! ( (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows defining c-variadic naked functions with any extern ABI that is allowed /// on c-variadic foreign functions. - (unstable, c_variadic_naked_functions, "CURRENT_RUSTC_VERSION", Some(148767)), + (unstable, c_variadic_naked_functions, "1.93.0", Some(148767)), /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. (unstable, cfg_contract_checks, "1.86.0", Some(128044)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. @@ -486,7 +486,7 @@ declare_features! ( /// Allows deriving the From trait on single-field structs. (unstable, derive_from, "1.91.0", Some(144889)), /// Allows giving non-const impls custom diagnostic messages if attempted to be used as const - (unstable, diagnostic_on_const, "CURRENT_RUSTC_VERSION", Some(143874)), + (unstable, diagnostic_on_const, "1.93.0", Some(143874)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. @@ -503,7 +503,7 @@ declare_features! ( (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), /// Allows using `#[export_stable]` which indicates that an item is exportable. (incomplete, export_stable, "1.88.0", Some(139939)), - /// Externally implementatble items + /// Externally implementable items (unstable, extern_item_impls, "CURRENT_RUSTC_VERSION", Some(125418)), /// Allows defining `extern type`s. (unstable, extern_types, "1.23.0", Some(43467)), diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 34d78afca9b2..e9f9b2445deb 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -530,7 +530,7 @@ pub struct CfgHideShow { pub values: ThinVec, } -#[derive(Clone, Debug, Default, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +#[derive(Clone, Debug, Default, HashStable_Generic, Decodable, PrintAttribute)] pub struct DocAttribute { pub aliases: FxIndexMap, pub hidden: Option, @@ -566,6 +566,62 @@ pub struct DocAttribute { pub no_crate_inject: Option, } +impl rustc_serialize::Encodable for DocAttribute { + fn encode(&self, encoder: &mut E) { + let DocAttribute { + aliases, + hidden, + inline, + cfg, + auto_cfg, + auto_cfg_change, + fake_variadic, + keyword, + attribute, + masked, + notable_trait, + search_unbox, + html_favicon_url, + html_logo_url, + html_playground_url, + html_root_url, + html_no_source, + issue_tracker_base_url, + rust_logo, + test_attrs, + no_crate_inject, + } = self; + rustc_serialize::Encodable::::encode(aliases, encoder); + rustc_serialize::Encodable::::encode(hidden, encoder); + + // FIXME: The `doc(inline)` attribute is never encoded, but is it actually the right thing + // to do? I suspect the condition was broken, should maybe instead not encode anything if we + // have `doc(no_inline)`. + let inline: ThinVec<_> = + inline.iter().filter(|(i, _)| *i != DocInline::Inline).cloned().collect(); + rustc_serialize::Encodable::::encode(&inline, encoder); + + rustc_serialize::Encodable::::encode(cfg, encoder); + rustc_serialize::Encodable::::encode(auto_cfg, encoder); + rustc_serialize::Encodable::::encode(auto_cfg_change, encoder); + rustc_serialize::Encodable::::encode(fake_variadic, encoder); + rustc_serialize::Encodable::::encode(keyword, encoder); + rustc_serialize::Encodable::::encode(attribute, encoder); + rustc_serialize::Encodable::::encode(masked, encoder); + rustc_serialize::Encodable::::encode(notable_trait, encoder); + rustc_serialize::Encodable::::encode(search_unbox, encoder); + rustc_serialize::Encodable::::encode(html_favicon_url, encoder); + rustc_serialize::Encodable::::encode(html_logo_url, encoder); + rustc_serialize::Encodable::::encode(html_playground_url, encoder); + rustc_serialize::Encodable::::encode(html_root_url, encoder); + rustc_serialize::Encodable::::encode(html_no_source, encoder); + rustc_serialize::Encodable::::encode(issue_tracker_base_url, encoder); + rustc_serialize::Encodable::::encode(rust_logo, encoder); + rustc_serialize::Encodable::::encode(test_attrs, encoder); + rustc_serialize::Encodable::::encode(no_crate_inject, encoder); + } +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -803,6 +859,9 @@ pub enum AttributeKind { /// Represents `#[no_implicit_prelude]` NoImplicitPrelude(Span), + /// Represents `#[no_link]` + NoLink, + /// Represents `#[no_mangle]` NoMangle(Span), @@ -869,15 +928,47 @@ pub enum AttributeKind { /// Represents `#[rustc_layout_scalar_valid_range_start]`. RustcLayoutScalarValidRangeStart(Box, Span), + /// Represents `#[rustc_legacy_const_generics]` + RustcLegacyConstGenerics { fn_indexes: ThinVec<(usize, Span)>, attr_span: Span }, + + /// Represents `#[rustc_lint_diagnostics]` + RustcLintDiagnostics, + + /// Represents `#[rustc_lint_opt_deny_field_access]` + RustcLintOptDenyFieldAccess { lint_message: Symbol }, + + /// Represents `#[rustc_lint_opt_ty]` + RustcLintOptTy, + + /// Represents `#[rustc_lint_query_instability]` + RustcLintQueryInstability, + + /// Represents `#[rustc_lint_untracked_query_information]` + RustcLintUntrackedQueryInformation, + /// Represents `#[rustc_main]`. RustcMain, + /// Represents `#[rustc_never_returns_null_ptr]` + RustcNeverReturnsNullPointer, + + /// Represents `#[rustc_no_implicit_autorefs]` + RustcNoImplicitAutorefs, + /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, /// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]` RustcPassIndirectlyInNonRusticAbis(Span), + /// Represents `#[rustc_scalable_vector(N)]` + RustcScalableVector { + /// The base multiple of lanes that are in a scalable vector, if provided. `element_count` + /// is not provided for representing tuple types. + element_count: Option, + span: Span, + }, + /// Represents `#[rustc_should_not_be_called_on_const_items]` RustcShouldNotBeCalledOnConstItems(Span), @@ -919,6 +1010,9 @@ pub enum AttributeKind { /// `#[unsafe(force_target_feature(enable = "...")]`. TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool }, + /// Represents `#[thread_local]` + ThreadLocal, + /// Represents `#[track_caller]` TrackCaller(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 4cb786f59bfe..68456dfe4c29 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -70,6 +70,7 @@ impl AttributeKind { Naked(..) => No, NoCore(..) => No, NoImplicitPrelude(..) => No, + NoLink => No, NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc @@ -92,9 +93,18 @@ impl AttributeKind { RustcCoherenceIsCore(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, + RustcLegacyConstGenerics { .. } => Yes, + RustcLintDiagnostics => Yes, + RustcLintOptDenyFieldAccess { .. } => Yes, + RustcLintOptTy => Yes, + RustcLintQueryInstability => Yes, + RustcLintUntrackedQueryInformation => Yes, RustcMain => No, + RustcNeverReturnsNullPointer => Yes, + RustcNoImplicitAutorefs => Yes, RustcObjectLifetimeDefault => No, RustcPassIndirectlyInNonRusticAbis(..) => No, + RustcScalableVector { .. } => Yes, RustcShouldNotBeCalledOnConstItems(..) => Yes, RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate Sanitize { .. } => No, @@ -104,6 +114,7 @@ impl AttributeKind { Stability { .. } => Yes, StdInternalSymbol(..) => No, TargetFeature { .. } => No, + ThreadLocal => No, TrackCaller(..) => Yes, TypeConst(..) => Yes, TypeLengthLimit { .. } => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index 0ad486cef43d..f8ac2a547ca8 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -1,4 +1,5 @@ use std::num::NonZero; +use std::ops::Deref; use rustc_abi::Align; use rustc_ast::token::{CommentKind, DocFragmentKind}; @@ -37,6 +38,15 @@ impl PrintAttribute for &T { T::print_attribute(self, p) } } +impl PrintAttribute for Box { + fn should_render(&self) -> bool { + self.deref().should_render() + } + + fn print_attribute(&self, p: &mut Printer) { + T::print_attribute(self.deref(), p) + } +} impl PrintAttribute for Option { fn should_render(&self) -> bool { self.as_ref().is_some_and(|x| x.should_render()) @@ -159,7 +169,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); -print_disp!(u16, u128, bool, NonZero, Limit); +print_disp!(u16, u128, usize, bool, NonZero, Limit); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index c27954b6d14e..7c9c15c16df4 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -3,8 +3,6 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(debug_closure_helpers))] -#![deny(clippy::manual_let_else)] #![feature(associated_type_defaults)] #![feature(closure_track_caller)] #![feature(const_default)] diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index beba0165549e..a81df02f023f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1,7 +1,7 @@ use std::cell::LazyCell; use std::ops::ControlFlow; -use rustc_abi::{ExternAbi, FieldIdx}; +use rustc_abi::{ExternAbi, FieldIdx, ScalableElt}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; use rustc_errors::{EmissionGuarantee, MultiSpan}; @@ -92,7 +92,9 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated - if def.repr().simd() { + if let Some(scalable) = def.repr().scalable { + check_scalable_vector(tcx, span, def_id, scalable); + } else if def.repr().simd() { check_simd(tcx, span, def_id); } @@ -1426,6 +1428,100 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { } } +#[tracing::instrument(skip(tcx), level = "debug")] +fn check_scalable_vector(tcx: TyCtxt<'_>, span: Span, def_id: LocalDefId, scalable: ScalableElt) { + let ty = tcx.type_of(def_id).instantiate_identity(); + let ty::Adt(def, args) = ty.kind() else { return }; + if !def.is_struct() { + tcx.dcx().delayed_bug("`rustc_scalable_vector` applied to non-struct"); + return; + } + + let fields = &def.non_enum_variant().fields; + match scalable { + ScalableElt::ElementCount(..) if fields.is_empty() => { + let mut err = + tcx.dcx().struct_span_err(span, "scalable vectors must have a single field"); + err.help("scalable vector types' only field must be a primitive scalar type"); + err.emit(); + return; + } + ScalableElt::ElementCount(..) if fields.len() >= 2 => { + tcx.dcx().struct_span_err(span, "scalable vectors cannot have multiple fields").emit(); + return; + } + ScalableElt::Container if fields.is_empty() => { + let mut err = + tcx.dcx().struct_span_err(span, "scalable vectors must have a single field"); + err.help("tuples of scalable vectors can only contain multiple of the same scalable vector type"); + err.emit(); + return; + } + _ => {} + } + + match scalable { + ScalableElt::ElementCount(..) => { + let element_ty = &fields[FieldIdx::ZERO].ty(tcx, args); + + // Check that `element_ty` only uses types valid in the lanes of a scalable vector + // register: scalar types which directly match a "machine" type - integers, floats and + // bools + match element_ty.kind() { + ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Bool => (), + _ => { + let mut err = tcx.dcx().struct_span_err( + span, + "element type of a scalable vector must be a primitive scalar", + ); + err.help("only `u*`, `i*`, `f*` and `bool` types are accepted"); + err.emit(); + } + } + } + ScalableElt::Container => { + let mut prev_field_ty = None; + for field in fields.iter() { + let element_ty = field.ty(tcx, args); + if let ty::Adt(def, _) = element_ty.kind() + && def.repr().scalable() + { + match def + .repr() + .scalable + .expect("`repr().scalable.is_some()` != `repr().scalable()`") + { + ScalableElt::ElementCount(_) => { /* expected field */ } + ScalableElt::Container => { + tcx.dcx().span_err( + tcx.def_span(field.did), + "scalable vector structs cannot contain other scalable vector structs", + ); + break; + } + } + } else { + tcx.dcx().span_err( + tcx.def_span(field.did), + "scalable vector structs can only have scalable vector fields", + ); + break; + } + + if let Some(prev_ty) = prev_field_ty.replace(element_ty) + && prev_ty != element_ty + { + tcx.dcx().span_err( + tcx.def_span(field.did), + "all fields in a scalable vector struct must be the same type", + ); + break; + } + } + } + } +} + pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 676c9a980aff..4e8333f678b6 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -215,6 +215,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_name | sym::ub_checks | sym::variant_count + | sym::vtable_for | sym::wrapping_add | sym::wrapping_mul | sym::wrapping_sub @@ -643,6 +644,20 @@ pub(crate) fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, tcx.types.unit)], tcx.types.usize) } + sym::vtable_for => { + let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, span); + let dyn_metadata_adt_ref = tcx.adt_def(dyn_metadata); + let dyn_metadata_args = tcx.mk_args(&[param(1).into()]); + let dyn_ty = Ty::new_adt(tcx, dyn_metadata_adt_ref, dyn_metadata_args); + + let option_did = tcx.require_lang_item(LangItem::Option, span); + let option_adt_ref = tcx.adt_def(option_did); + let option_args = tcx.mk_args(&[dyn_ty.into()]); + let ret_ty = Ty::new_adt(tcx, option_adt_ref, option_args); + + (2, 0, vec![], ret_ty) + } + // This type check is not particularly useful, but the `where` bounds // on the definition in `core` do the heavy lifting for checking it. sym::aggregate_raw_ptr => (3, 0, vec![param(1), param(2)], param(0)), diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 5ed0e0018f8d..16f5222f621c 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2,7 +2,7 @@ use std::cell::LazyCell; use std::ops::{ControlFlow, Deref}; use hir::intravisit::{self, Visitor}; -use rustc_abi::ExternAbi; +use rustc_abi::{ExternAbi, ScalableElt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; @@ -1038,7 +1038,21 @@ fn check_type_defn<'tcx>( hir_ty.span, Some(WellFormedLoc::Ty(field_id)), ty.into(), - ) + ); + + if matches!(ty.kind(), ty::Adt(def, _) if def.repr().scalable()) + && !matches!(adt_def.repr().scalable, Some(ScalableElt::Container)) + { + // Scalable vectors can only be fields of structs if the type has a + // `rustc_scalable_vector` attribute w/out specifying an element count + tcx.dcx().span_err( + hir_ty.span, + format!( + "scalable vectors cannot be fields of a {}", + adt_def.variant_descr() + ), + ); + } } // For DST, or when drop needs to copy things around, all diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 125fc21a5cc1..4ab13140bf9c 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -401,12 +401,6 @@ fn check_constraints<'tcx>( })); }; - if let Some(local_sig_id) = sig_id.as_local() - && tcx.hir_opt_delegation_sig_id(local_sig_id).is_some() - { - emit("recursive delegation is not supported yet"); - } - if tcx.fn_sig(sig_id).skip_binder().skip_binder().c_variadic { // See issue #127443 for explanation. emit("delegation to C-variadic functions is not allowed"); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 79c5fbab1ffa..65f6a11e8e9b 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -58,8 +58,6 @@ This API is completely unstable and subject to change. // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![cfg_attr(bootstrap, feature(debug_closure_helpers))] -#![deny(clippy::manual_let_else)] #![feature(assert_matches)] #![feature(gen_blocks)] #![feature(if_let_guard)] diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 6467adb54dab..ded03c88a16e 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -10,7 +10,7 @@ use rustc_trait_selection::traits::{ }; use tracing::{debug, instrument}; -use crate::coercion::{AsCoercionSite, CoerceMany}; +use crate::coercion::CoerceMany; use crate::{Diverges, Expectation, FnCtxt, GatherLocalsVisitor, Needs}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Expectation::ExpectHasType(ety) if ety != tcx.types.unit => ety, _ => self.next_ty_var(expr.span), }; - CoerceMany::with_coercion_sites(coerce_first, arms) + CoerceMany::with_capacity(coerce_first, arms.len()) }; let mut prior_non_diverging_arms = vec![]; // Used only for diagnostics. @@ -269,16 +269,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Handle the fallback arm of a desugared if(-let) like a missing else. /// /// Returns `true` if there was an error forcing the coercion to the `()` type. - pub(super) fn if_fallback_coercion( + pub(super) fn if_fallback_coercion( &self, if_span: Span, cond_expr: &'tcx hir::Expr<'tcx>, then_expr: &'tcx hir::Expr<'tcx>, - coercion: &mut CoerceMany<'tcx, '_, T>, - ) -> bool - where - T: AsCoercionSite, - { + coercion: &mut CoerceMany<'tcx>, + ) -> bool { // If this `if` expr is the parent's function return expr, // the cause of the type coercion is the return type, point at it. (#25228) let hir_id = self.tcx.parent_hir_id(self.tcx.parent_hir_id(then_expr.hir_id)); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 127965cb4b30..b4a43548dcda 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1165,17 +1165,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// This is really an internal helper. From outside the coercion /// module, you should instantiate a `CoerceMany` instance. - fn try_find_coercion_lub( + fn try_find_coercion_lub( &self, cause: &ObligationCause<'tcx>, - exprs: &[E], + exprs: &[&'tcx hir::Expr<'tcx>], prev_ty: Ty<'tcx>, new: &hir::Expr<'_>, new_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - E: AsCoercionSite, - { + ) -> RelateResult<'tcx, Ty<'tcx>> { let prev_ty = self.try_structurally_resolve_type(cause.span, prev_ty); let new_ty = self.try_structurally_resolve_type(new.span, new_ty); debug!( @@ -1269,7 +1266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer(sig.safety())), _ => span_bug!(new.span, "should not try to coerce a {new_ty} to a fn pointer"), }; - for expr in exprs.iter().map(|e| e.as_coercion_site()) { + for expr in exprs.iter() { self.apply_adjustments( expr, vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }], @@ -1316,7 +1313,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (adjustments, target) = self.register_infer_ok_obligations(ok); for expr in exprs { - let expr = expr.as_coercion_site(); self.apply_adjustments(expr, adjustments.clone()); } debug!( @@ -1382,41 +1378,23 @@ pub fn can_coerce<'tcx>( /// } /// let final_ty = coerce.complete(fcx); /// ``` -pub(crate) struct CoerceMany<'tcx, 'exprs, E: AsCoercionSite> { +pub(crate) struct CoerceMany<'tcx> { expected_ty: Ty<'tcx>, final_ty: Option>, - expressions: Expressions<'tcx, 'exprs, E>, - pushed: usize, + expressions: Vec<&'tcx hir::Expr<'tcx>>, } -/// The type of a `CoerceMany` that is storing up the expressions into -/// a buffer. We use this in `check/mod.rs` for things like `break`. -pub(crate) type DynamicCoerceMany<'tcx> = CoerceMany<'tcx, 'tcx, &'tcx hir::Expr<'tcx>>; - -enum Expressions<'tcx, 'exprs, E: AsCoercionSite> { - Dynamic(Vec<&'tcx hir::Expr<'tcx>>), - UpFront(&'exprs [E]), -} - -impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { - /// The usual case; collect the set of expressions dynamically. - /// If the full set of coercion sites is known before hand, - /// consider `with_coercion_sites()` instead to avoid allocation. +impl<'tcx> CoerceMany<'tcx> { + /// Creates a `CoerceMany` with a default capacity of 1. If the full set of + /// coercion sites is known before hand, consider `with_capacity()` instead + /// to avoid allocation. pub(crate) fn new(expected_ty: Ty<'tcx>) -> Self { - Self::make(expected_ty, Expressions::Dynamic(vec![])) + Self::with_capacity(expected_ty, 1) } - /// As an optimization, you can create a `CoerceMany` with a - /// preexisting slice of expressions. In this case, you are - /// expected to pass each element in the slice to `coerce(...)` in - /// order. This is used with arrays in particular to avoid - /// needlessly cloning the slice. - pub(crate) fn with_coercion_sites(expected_ty: Ty<'tcx>, coercion_sites: &'exprs [E]) -> Self { - Self::make(expected_ty, Expressions::UpFront(coercion_sites)) - } - - fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'tcx, 'exprs, E>) -> Self { - CoerceMany { expected_ty, final_ty: None, expressions, pushed: 0 } + /// Creates a `CoerceMany` with a given capacity. + pub(crate) fn with_capacity(expected_ty: Ty<'tcx>, capacity: usize) -> Self { + CoerceMany { expected_ty, final_ty: None, expressions: Vec::with_capacity(capacity) } } /// Returns the "expected type" with which this coercion was @@ -1529,7 +1507,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Handle the actual type unification etc. let result = if let Some(expression) = expression { - if self.pushed == 0 { + if self.expressions.is_empty() { // Special-case the first expression we are coercing. // To be honest, I'm not entirely sure why we do this. // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why @@ -1541,22 +1519,13 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { Some(cause.clone()), ) } else { - match self.expressions { - Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub( - cause, - exprs, - self.merged_ty(), - expression, - expression_ty, - ), - Expressions::UpFront(coercion_sites) => fcx.try_find_coercion_lub( - cause, - &coercion_sites[0..self.pushed], - self.merged_ty(), - expression, - expression_ty, - ), - } + fcx.try_find_coercion_lub( + cause, + &self.expressions, + self.merged_ty(), + expression, + expression_ty, + ) } } else { // this is a hack for cases where we default to `()` because @@ -1591,18 +1560,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { Ok(v) => { self.final_ty = Some(v); if let Some(e) = expression { - match self.expressions { - Expressions::Dynamic(ref mut buffer) => buffer.push(e), - Expressions::UpFront(coercion_sites) => { - // if the user gave us an array to validate, check that we got - // the next expression in the list, as expected - assert_eq!( - coercion_sites[self.pushed].as_coercion_site().hir_id, - e.hir_id - ); - } - } - self.pushed += 1; + self.expressions.push(e); } } Err(coercion_error) => { @@ -1955,45 +1913,12 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } else { // If we only had inputs that were of type `!` (or no // inputs at all), then the final type is `!`. - assert_eq!(self.pushed, 0); + assert!(self.expressions.is_empty()); fcx.tcx.types.never } } } -/// Something that can be converted into an expression to which we can -/// apply a coercion. -pub(crate) trait AsCoercionSite { - fn as_coercion_site(&self) -> &hir::Expr<'_>; -} - -impl AsCoercionSite for hir::Expr<'_> { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - self - } -} - -impl<'a, T> AsCoercionSite for &'a T -where - T: AsCoercionSite, -{ - fn as_coercion_site(&self) -> &hir::Expr<'_> { - (**self).as_coercion_site() - } -} - -impl AsCoercionSite for ! { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - *self - } -} - -impl AsCoercionSite for hir::Arm<'_> { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - self.body - } -} - /// Recursively visit goals to decide whether an unsizing is possible. /// `Break`s when it isn't, and an error should be raised. /// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item. diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 48082560bb83..c4fa39c6c2c8 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -40,7 +40,7 @@ use tracing::{debug, instrument, trace}; use {rustc_ast as ast, rustc_hir as hir}; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::coercion::{CoerceMany, DynamicCoerceMany}; +use crate::coercion::CoerceMany; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, @@ -1227,7 +1227,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (`only_has_type`); otherwise, we just go with a // fresh type variable. let coerce_to_ty = expected.coercion_target_type(self, sp); - let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); + let mut coerce = CoerceMany::with_capacity(coerce_to_ty, 2); coerce.coerce(self, &self.misc(sp), then_expr, then_ty); @@ -1681,7 +1681,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .to_option(self) .and_then(|uty| self.try_structurally_resolve_type(expr.span, uty).builtin_index()) .unwrap_or_else(|| self.next_ty_var(expr.span)); - let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); + let mut coerce = CoerceMany::with_capacity(coerce_to, args.len()); for e in args { let e_ty = self.check_expr_with_hint(e, coerce_to); diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 27b66d07f98e..ad34994526ed 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -7,9 +7,7 @@ use std::cell::{Ref, RefCell}; use std::ops::Deref; -use std::slice::from_ref; -use hir::Expr; use hir::def::DefKind; use hir::pat_util::EnumerateAndAdjustIterator as _; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; @@ -23,6 +21,7 @@ use rustc_middle::hir::place::ProjectionKind; // Export these here so that Clippy can use them. pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; use rustc_middle::mir::FakeReadCause; +use rustc_middle::thir::DerefPatBorrowMode; use rustc_middle::ty::{ self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment, }; @@ -313,7 +312,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx let param_place = self.cat_rvalue(param.hir_id, param_ty); - self.walk_irrefutable_pat(¶m_place, param.pat)?; + self.fake_read_scrutinee(¶m_place, false)?; + self.walk_pat(¶m_place, param.pat, false)?; } self.consume_expr(body.value)?; @@ -455,13 +455,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx hir::ExprKind::Match(discr, arms, _) => { let discr_place = self.cat_expr(discr)?; - self.maybe_read_scrutinee( - discr, - discr_place.clone(), - arms.iter().map(|arm| arm.pat), - )?; + self.fake_read_scrutinee(&discr_place, true)?; + self.walk_expr(discr)?; - // treatment of the discriminant is handled while walking the arms. for arm in arms { self.walk_arm(&discr_place, arm)?; } @@ -598,116 +594,25 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Ok(()) } - fn maybe_read_scrutinee<'t>( + #[instrument(skip(self), level = "debug")] + fn fake_read_scrutinee( &self, - discr: &Expr<'_>, - discr_place: PlaceWithHirId<'tcx>, - pats: impl Iterator>, + discr_place: &PlaceWithHirId<'tcx>, + refutable: bool, ) -> Result<(), Cx::Error> { - // Matching should not always be considered a use of the place, hence - // discr does not necessarily need to be borrowed. - // We only want to borrow discr if the pattern contain something other - // than wildcards. - let mut needs_to_be_read = false; - for pat in pats { - self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| { - match &pat.kind { - PatKind::Missing => unreachable!(), - PatKind::Binding(.., opt_sub_pat) => { - // If the opt_sub_pat is None, then the binding does not count as - // a wildcard for the purpose of borrowing discr. - if opt_sub_pat.is_none() { - needs_to_be_read = true; - } - } - PatKind::Never => { - // A never pattern reads the value. - // FIXME(never_patterns): does this do what I expect? - needs_to_be_read = true; - } - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { - // A `Path` pattern is just a name like `Foo`. This is either a - // named constant or else it refers to an ADT variant + let closure_def_id = match discr_place.place.base { + PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id), + _ => None, + }; - let res = self.cx.typeck_results().qpath_res(qpath, *hir_id); - match res { - Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { - // Named constants have to be equated with the value - // being matched, so that's a read of the value being matched. - // - // FIXME: We don't actually reads for ZSTs. - needs_to_be_read = true; - } - _ => { - // Otherwise, this is a struct/enum variant, and so it's - // only a read if we need to read the discriminant. - needs_to_be_read |= - self.is_multivariant_adt(place.place.ty(), *span); - } - } - } - PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { - // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching - // against a multivariant enum or struct. In that case, we have to read - // the discriminant. Otherwise this kind of pattern doesn't actually - // read anything (we'll get invoked for the `...`, which may indeed - // perform some reads). - - let place_ty = place.place.ty(); - needs_to_be_read |= self.is_multivariant_adt(place_ty, pat.span); - } - PatKind::Expr(_) | PatKind::Range(..) => { - // If the PatKind is a Lit or a Range then we want - // to borrow discr. - needs_to_be_read = true; - } - PatKind::Slice(lhs, wild, rhs) => { - // We don't need to test the length if the pattern is `[..]` - if matches!((lhs, wild, rhs), (&[], Some(_), &[])) - // Arrays have a statically known size, so - // there is no need to read their length - || place.place.ty().peel_refs().is_array() - { - } else { - needs_to_be_read = true; - } - } - PatKind::Or(_) - | PatKind::Box(_) - | PatKind::Deref(_) - | PatKind::Ref(..) - | PatKind::Guard(..) - | PatKind::Wild - | PatKind::Err(_) => { - // If the PatKind is Or, Box, or Ref, the decision is made later - // as these patterns contains subpatterns - // If the PatKind is Wild or Err, the decision is made based on the other patterns - // being examined - } - } - - Ok(()) - })? - } - - if needs_to_be_read { - self.borrow_expr(discr, BorrowKind::Immutable)?; + let cause = if refutable { + FakeReadCause::ForMatchedPlace(closure_def_id) } else { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id), - _ => None, - }; + FakeReadCause::ForLet(closure_def_id) + }; - self.delegate.borrow_mut().fake_read( - &discr_place, - FakeReadCause::ForMatchedPlace(closure_def_id), - discr_place.hir_id, - ); + self.delegate.borrow_mut().fake_read(discr_place, cause, discr_place.hir_id); - // We always want to walk the discriminant. We want to make sure, for instance, - // that the discriminant has been initialized. - self.walk_expr(discr)?; - } Ok(()) } @@ -724,12 +629,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.walk_expr(expr)?; let expr_place = self.cat_expr(expr)?; f()?; + self.fake_read_scrutinee(&expr_place, els.is_some())?; + self.walk_pat(&expr_place, pat, false)?; if let Some(els) = els { - // borrowing because we need to test the discriminant - self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter())?; self.walk_block(els)?; } - self.walk_irrefutable_pat(&expr_place, pat)?; Ok(()) } @@ -901,16 +805,6 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>, ) -> Result<(), Cx::Error> { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id), - _ => None, - }; - - self.delegate.borrow_mut().fake_read( - discr_place, - FakeReadCause::ForMatchedPlace(closure_def_id), - discr_place.hir_id, - ); self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?; if let Some(ref e) = arm.guard { @@ -921,28 +815,20 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Ok(()) } - /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or - /// let binding, and *not* a match arm or nested pat.) - fn walk_irrefutable_pat( - &self, - discr_place: &PlaceWithHirId<'tcx>, - pat: &hir::Pat<'_>, - ) -> Result<(), Cx::Error> { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id), - _ => None, - }; - - self.delegate.borrow_mut().fake_read( - discr_place, - FakeReadCause::ForLet(closure_def_id), - discr_place.hir_id, - ); - self.walk_pat(discr_place, pat, false)?; - Ok(()) - } - /// The core driver for walking a pattern + /// + /// This should mirror how pattern-matching gets lowered to MIR, as + /// otherwise lowering will ICE when trying to resolve the upvars. + /// + /// However, it is okay to approximate it here by doing *more* accesses than + /// the actual MIR builder will, which is useful when some checks are too + /// cumbersome to perform here. For example, if after typeck it becomes + /// clear that only one variant of an enum is inhabited, and therefore a + /// read of the discriminant is not necessary, `walk_pat` will have + /// over-approximated the necessary upvar capture granularity. + /// + /// Do note that discrepancies like these do still create obscure corners + /// in the semantics of the language, and should be avoided if possible. #[instrument(skip(self), level = "debug")] fn walk_pat( &self, @@ -952,6 +838,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx ) -> Result<(), Cx::Error> { let tcx = self.cx.tcx(); self.cat_pattern(discr_place.clone(), pat, &mut |place, pat| { + debug!("walk_pat: pat.kind={:?}", pat.kind); + let read_discriminant = || { + self.delegate.borrow_mut().borrow(place, discr_place.hir_id, BorrowKind::Immutable); + }; + match pat.kind { PatKind::Binding(_, canonical_id, ..) => { debug!("walk_pat: binding place={:?} pat={:?}", place, pat); @@ -974,11 +865,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // binding when lowering pattern guards to ensure that the guard does not // modify the scrutinee. if has_guard { - self.delegate.borrow_mut().borrow( - place, - discr_place.hir_id, - BorrowKind::Immutable, - ); + read_discriminant(); } // It is also a borrow or copy/move of the value being matched. @@ -1004,7 +891,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Deref patterns on boxes don't borrow, so we ignore them here. // HACK: this could be a fake pattern corresponding to a deref inserted by match // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. - if let hir::ByRef::Yes(_, mutability) = + if let DerefPatBorrowMode::Borrow(mutability) = self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern) { let bk = ty::BorrowKind::from_mutbl(mutability); @@ -1014,13 +901,73 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx PatKind::Never => { // A `!` pattern always counts as an immutable read of the discriminant, // even in an irrefutable pattern. - self.delegate.borrow_mut().borrow( - place, - discr_place.hir_id, - BorrowKind::Immutable, - ); + read_discriminant(); + } + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { + // A `Path` pattern is just a name like `Foo`. This is either a + // named constant or else it refers to an ADT variant + + let res = self.cx.typeck_results().qpath_res(qpath, *hir_id); + match res { + Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { + // Named constants have to be equated with the value + // being matched, so that's a read of the value being matched. + // + // FIXME: Does the MIR code skip this read when matching on a ZST? + // If so, we can also skip it here. + read_discriminant(); + } + _ => { + // Otherwise, this is a struct/enum variant, and so it's + // only a read if we need to read the discriminant. + if self.is_multivariant_adt(place.place.ty(), *span) { + read_discriminant(); + } + } + } + } + PatKind::Expr(_) | PatKind::Range(..) => { + // When matching against a literal or range, we need to + // borrow the place to compare it against the pattern. + // + // Note that we do this read even if the range matches all + // possible values, such as 0..=u8::MAX. This is because + // we don't want to depend on consteval here. + // + // FIXME: What if the type being matched only has one + // possible value? + read_discriminant(); + } + PatKind::Struct(..) | PatKind::TupleStruct(..) => { + if self.is_multivariant_adt(place.place.ty(), pat.span) { + read_discriminant(); + } + } + PatKind::Slice(lhs, wild, rhs) => { + // We don't need to test the length if the pattern is `[..]` + if matches!((lhs, wild, rhs), (&[], Some(_), &[])) + // Arrays have a statically known size, so + // there is no need to read their length + || place.place.ty().peel_refs().is_array() + { + // No read necessary + } else { + read_discriminant(); + } + } + PatKind::Or(_) + | PatKind::Box(_) + | PatKind::Ref(..) + | PatKind::Guard(..) + | PatKind::Tuple(..) + | PatKind::Wild + | PatKind::Missing + | PatKind::Err(_) => { + // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses + // are made later as these patterns contains subpatterns. + // If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing + // the other patterns that are part of the match } - _ => {} } Ok(()) @@ -1891,9 +1838,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx ) -> Result, Cx::Error> { match self.cx.typeck_results().deref_pat_borrow_mode(base_place.place.ty(), inner) { // Deref patterns on boxes are lowered using a built-in deref. - hir::ByRef::No => self.cat_deref(hir_id, base_place), + DerefPatBorrowMode::Box => self.cat_deref(hir_id, base_place), // For other types, we create a temporary to match on. - hir::ByRef::Yes(_, mutability) => { + DerefPatBorrowMode::Borrow(mutability) => { let re_erased = self.cx.tcx().lifetimes.re_erased; let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability); // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... @@ -1904,6 +1851,20 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } + /// Checks whether a type has multiple variants, and therefore, whether a + /// read of the discriminant might be necessary. Note that the actual MIR + /// builder code does a more specific check, filtering out variants that + /// happen to be uninhabited. + /// + /// Here, it is not practical to perform such a check, because inhabitedness + /// queries require typeck results, and typeck requires closure capture analysis. + /// + /// Moreover, the language is moving towards uninhabited variants still semantically + /// causing a discriminant read, so we *shouldn't* perform any such check. + /// + /// FIXME(never_patterns): update this comment once the aforementioned MIR builder + /// code is changed to be insensitive to inhhabitedness. + #[instrument(skip(self, span), level = "debug")] fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool { if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() { // Note that if a non-exhaustive SingleVariant is defined in another crate, we need diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d04133ccee97..c07cbfae256d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1033,11 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // break 'a 22; }` would not force the type of the block // to be `()`). let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - CoerceMany::with_coercion_sites(coerce_to_ty, blk.expr.as_slice()) - }; + let coerce = CoerceMany::new(coerce_to_ty); let prev_diverges = self.diverges.get(); let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 998c5e6cd25a..c875e2e50d70 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -25,7 +25,7 @@ use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, }; -use crate::coercion::DynamicCoerceMany; +use crate::coercion::CoerceMany; use crate::fallback::DivergingFallbackBehavior; use crate::fn_ctxt::checks::DivergingBlockBehavior; use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt}; @@ -56,13 +56,13 @@ pub(crate) struct FnCtxt<'a, 'tcx> { /// expressions. If `None`, this is in a context where return is /// inappropriate, such as a const expression. /// - /// This is a `RefCell`, which means that we + /// This is a `RefCell`, which means that we /// can track all the return expressions and then use them to /// compute a useful coercion from the set, similar to a match /// expression or other branching context. You can use methods /// like `expected_ty` to access the declared return type (if /// any). - pub(super) ret_coercion: Option>>, + pub(super) ret_coercion: Option>>, /// First span of a return site that we find. Used in error messages. pub(super) ret_coercion_span: Cell>, diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 6626c3edb546..7c1655f8201d 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -178,19 +178,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Never if is_input => return None, _ if ty.references_error() => return None, ty::Adt(adt, args) if self.tcx().is_lang_item(adt.did(), LangItem::MaybeUninit) => { - let fields = &adt.non_enum_variant().fields; - let ty = fields[FieldIdx::ONE].ty(self.tcx(), args); - // FIXME: Are we just trying to map to the `T` in `MaybeUninit`? - // If so, just get it from the args. - let ty::Adt(ty, args) = ty.kind() else { - unreachable!("expected first field of `MaybeUninit` to be an ADT") - }; - assert!( - ty.is_manually_drop(), - "expected first field of `MaybeUninit` to be `ManuallyDrop`" - ); - let fields = &ty.non_enum_variant().fields; - let ty = fields[FieldIdx::ZERO].ty(self.tcx(), args); + let ty = args.type_at(0); self.get_asm_ty(expr.span, ty) } _ => self.get_asm_ty(expr.span, ty), diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9ca5ddd494ae..39c28c4f4e99 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -1,5 +1,4 @@ // tidy-alphabetical-start -#![deny(clippy::manual_let_else)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] @@ -62,7 +61,7 @@ use tracing::{debug, instrument}; use typeck_root_ctxt::TypeckRootCtxt; use crate::check::check_fn; -use crate::coercion::DynamicCoerceMany; +use crate::coercion::CoerceMany; use crate::diverges::Diverges; use crate::expectation::Expectation; use crate::fn_ctxt::LoweredTy; @@ -350,7 +349,7 @@ pub struct BreakableCtxt<'tcx> { // this is `null` for loops where break with a value is illegal, // such as `while`, `for`, and `while let` - coerce: Option>, + coerce: Option>, } pub struct EnclosingBreakables<'tcx> { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 445386412058..1a2b76485f35 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -761,6 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ], /// } /// ``` + #[instrument(level = "debug", skip(self))] fn compute_min_captures( &self, closure_def_id: LocalDefId, @@ -2029,6 +2030,7 @@ struct InferBorrowKind<'tcx> { } impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { + #[instrument(skip(self), level = "debug")] fn fake_read( &mut self, place_with_id: &PlaceWithHirId<'tcx>, @@ -2119,6 +2121,7 @@ impl<'tcx> euv::Delegate<'tcx> for InferBorrowKind<'tcx> { } /// Rust doesn't permit moving fields out of a type that implements drop +#[instrument(skip(fcx), ret, level = "debug")] fn restrict_precision_for_drop_types<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, mut place: Place<'tcx>, @@ -2179,6 +2182,7 @@ fn restrict_precision_for_unsafe( /// - No unsafe block is required to capture `place`. /// /// Returns the truncated place and updated capture mode. +#[instrument(ret, level = "debug")] fn restrict_capture_precision( place: Place<'_>, curr_mode: ty::UpvarCapture, @@ -2208,6 +2212,7 @@ fn restrict_capture_precision( } /// Truncate deref of any reference. +#[instrument(ret, level = "debug")] fn adjust_for_move_closure( mut place: Place<'_>, mut kind: ty::UpvarCapture, @@ -2222,6 +2227,7 @@ fn adjust_for_move_closure( } /// Truncate deref of any reference. +#[instrument(ret, level = "debug")] fn adjust_for_use_closure( mut place: Place<'_>, mut kind: ty::UpvarCapture, @@ -2237,6 +2243,7 @@ fn adjust_for_use_closure( /// Adjust closure capture just that if taking ownership of data, only move data /// from enclosing stack frame. +#[instrument(ret, level = "debug")] fn adjust_for_non_move_closure( mut place: Place<'_>, mut kind: ty::UpvarCapture, @@ -2559,6 +2566,7 @@ fn determine_place_ancestry_relation<'tcx>( /// // it is constrained to `'a` /// } /// ``` +#[instrument(ret, level = "debug")] fn truncate_capture_for_optimization( mut place: Place<'_>, mut curr_mode: ty::UpvarCapture, diff --git a/compiler/rustc_index/src/slice.rs b/compiler/rustc_index/src/slice.rs index d2702bdb0571..415fe370b702 100644 --- a/compiler/rustc_index/src/slice.rs +++ b/compiler/rustc_index/src/slice.rs @@ -181,9 +181,6 @@ impl IndexSlice { /// Invert a bijective mapping, i.e. `invert(map)[y] = x` if `map[x] = y`, /// assuming the values in `self` are a permutation of `0..self.len()`. /// - /// This is used to go between `memory_index` (source field order to memory order) - /// and `inverse_memory_index` (memory order to source field order). - /// See also `FieldsShape::Arbitrary::memory_index` for more details. // FIXME(eddyb) build a better abstraction for permutations, if possible. pub fn invert_bijective_mapping(&self) -> IndexVec { debug_assert_eq!( diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 89b8a5fddb89..8dab3a7f37f5 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -321,7 +321,7 @@ fn test_search_paths_tracking_hash_different_order() { let early_dcx = EarlyDiagCtxt::new(JSON); const JSON: ErrorOutputType = ErrorOutputType::Json { pretty: false, - json_rendered: HumanReadableErrorType::Default { short: false }, + json_rendered: HumanReadableErrorType { short: false, unicode: false }, color_config: ColorConfig::Never, }; diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 56a0a3ceebf5..1160bd9ffa74 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -205,15 +205,17 @@ lint_dangling_pointers_from_locals = {$fn_kind} returns a dangling pointer to dr .ret_ty = return type is `{$ret_ty}` .local_var = local variable `{$local_var_name}` is dropped at the end of the {$fn_kind} .created_at = dangling pointer created here - .note = a dangling pointer is safe, but dereferencing one is undefined behavior + .note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior + .note_more_info = for more information, see + +lint_dangling_pointers_from_temporaries = this creates a dangling pointer because temporary `{$ty}` is dropped at end of statement + .label_ptr = pointer created here + .label_temporary = this `{$ty}` is dropped at end of statement + .help_bind = bind the `{$ty}` to a variable such that it outlives the pointer returned by `{$callee}` + .note_safe = a dangling pointer is safe, but dereferencing one is undefined behavior + .note_return = returning a pointer to a local variable will always result in a dangling pointer + .note_more_info = for more information, see -lint_dangling_pointers_from_temporaries = a dangling pointer will be produced because the temporary `{$ty}` will be dropped - .label_ptr = this pointer will immediately be invalid - .label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime - .note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - .help_bind = you must make sure that the variable you bind the `{$ty}` to lives at least as long as the pointer returned by the call to `{$callee}` - .help_returned = in particular, if this pointer is returned from the current function, binding the `{$ty}` inside the function will not suffice - .help_visit = for more information, see lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 5490a3aac9b7..ed7ac0e33244 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -1,8 +1,8 @@ use rustc_ast::{BorrowKind, UnOp}; -use rustc_hir::{Expr, ExprKind, Mutability}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Expr, ExprKind, Mutability, find_attr}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; use rustc_session::{declare_lint, declare_lint_pass}; -use rustc_span::sym; use crate::lints::{ ImplicitUnsafeAutorefsDiag, ImplicitUnsafeAutorefsMethodNote, ImplicitUnsafeAutorefsOrigin, @@ -106,7 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitAutorefs { ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), _ => None, } - && method_did.map(|did| cx.tcx.has_attr(did, sym::rustc_no_implicit_autorefs)).unwrap_or(true) + && method_did.map(|did| find_attr!(cx.tcx.get_all_attrs(did), AttributeKind::RustcNoImplicitAutorefs)).unwrap_or(true) { cx.emit_span_lint( DANGEROUS_IMPLICIT_AUTOREFS, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 810760e9f53e..e2a061cab680 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2683,7 +2683,6 @@ declare_lint! { /// /// ```rust,compile_fail /// # #![allow(unused)] - /// # #![cfg_attr(bootstrap, deny(deref_nullptr))] /// use std::ptr; /// unsafe { /// let x = &*ptr::null::(); diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index c00ba4959c94..68885e14f40d 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -1,9 +1,10 @@ //! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Clippy. +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, find_attr}; use rustc_middle::ty::{self, GenericArgsRef, PredicatePolarity, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -90,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for QueryStability { ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args) { let def_id = instance.def_id(); - if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { + if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcLintQueryInstability) { cx.emit_span_lint( POTENTIAL_QUERY_INSTABILITY, span, @@ -105,7 +106,10 @@ impl<'tcx> LateLintPass<'tcx> for QueryStability { ); } - if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) { + if find_attr!( + cx.tcx.get_all_attrs(def_id), + AttributeKind::RustcLintUntrackedQueryInformation + ) { cx.emit_span_lint( UNTRACKED_QUERY_INFORMATION, span, @@ -150,7 +154,10 @@ fn has_unstable_into_iter_predicate<'tcx>( }; // Does the input type's `IntoIterator` implementation have the // `rustc_lint_query_instability` attribute on its `into_iter` method? - if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) { + if find_attr!( + cx.tcx.get_all_attrs(instance.def_id()), + AttributeKind::RustcLintQueryInstability + ) { return true; } } @@ -602,14 +609,14 @@ impl Diagnostics { else { return; }; - let has_attr = cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics); - if !has_attr { + + if !find_attr!(cx.tcx.get_all_attrs(inst.def_id()), AttributeKind::RustcLintDiagnostics) { return; }; for (hir_id, _parent) in cx.tcx.hir_parent_iter(current_id) { if let Some(owner_did) = hir_id.as_owner() - && cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics) + && find_attr!(cx.tcx.get_all_attrs(owner_did), AttributeKind::RustcLintDiagnostics) { // The parent method is marked with `#[rustc_lint_diagnostics]` return; @@ -658,23 +665,18 @@ impl LateLintPass<'_> for BadOptAccess { let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return }; // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be // avoided. - if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) { + if !find_attr!(cx.tcx.get_all_attrs(adt_def.did()), AttributeKind::RustcLintOptTy) { return; } for field in adt_def.all_fields() { if field.name == target.name - && let Some(attr) = - cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) - && let Some(items) = attr.meta_item_list() - && let Some(item) = items.first() - && let Some(lit) = item.lit() - && let ast::LitKind::Str(val, _) = lit.kind + && let Some(lint_message) = find_attr!(cx.tcx.get_all_attrs(field.did), AttributeKind::RustcLintOptDenyFieldAccess { lint_message, } => lint_message) { cx.emit_span_lint( BAD_OPT_ACCESS, expr.span, - BadOptAccessDiag { msg: val.as_str() }, + BadOptAccessDiag { msg: lint_message.as_str() }, ); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 49929a0a9bc7..4e7a3e405176 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -22,7 +22,6 @@ // tidy-alphabetical-start #![allow(internal_features)] #![cfg_attr(bootstrap, feature(array_windows))] -#![deny(clippy::manual_let_else)] #![feature(assert_matches)] #![feature(box_patterns)] #![feature(if_let_guard)] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5017ce7caa52..fde9546bd1c7 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1138,10 +1138,10 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { // dangling.rs #[derive(LintDiagnostic)] #[diag(lint_dangling_pointers_from_temporaries)] -#[note] #[help(lint_help_bind)] -#[help(lint_help_returned)] -#[help(lint_help_visit)] +#[note(lint_note_safe)] +#[note(lint_note_return)] +#[note(lint_note_more_info)] // FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts pub(crate) struct DanglingPointersFromTemporaries<'tcx> { pub callee: Ident, @@ -1154,7 +1154,8 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> { #[derive(LintDiagnostic)] #[diag(lint_dangling_pointers_from_locals)] -#[note] +#[note(lint_note_safe)] +#[note(lint_note_more_info)] pub(crate) struct DanglingPointersFromLocals<'tcx> { pub ret_ty: Ty<'tcx>, #[label(lint_ret_ty)] diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index b2fa0fba76d9..b89e00dcbaae 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -1,5 +1,6 @@ use rustc_ast::LitKind; -use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind, find_attr}; use rustc_middle::ty::RawPtr; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{Span, sym}; @@ -72,14 +73,14 @@ fn useless_check<'a, 'tcx: 'a>( e = e.peel_blocks(); if let ExprKind::MethodCall(_, _expr, [], _) = e.kind && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcNeverReturnsNullPointer) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); } else if let ExprKind::Call(path, _args) = e.kind && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcNeverReturnsNullPointer) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 99cce0c44b86..8c69cc089caf 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2346,8 +2346,7 @@ declare_lint! { /// [sanitize]: https://doc.rust-lang.org/nightly/unstable-book/language-features/no-sanitize.html /// ### Example /// - #[cfg_attr(bootstrap, doc = "```ignore")] - #[cfg_attr(not(bootstrap), doc = "```rust,no_run")] + /// ```rust,no_run /// #![feature(sanitize)] /// /// #[sanitize(realtime = "nonblocking")] @@ -2356,8 +2355,7 @@ declare_lint! { /// fn main() { /// x(); /// } - #[cfg_attr(bootstrap, doc = "```")] - #[cfg_attr(not(bootstrap), doc = "```")] + /// ``` /// /// {{produces}} /// @@ -4907,8 +4905,7 @@ declare_lint! { /// /// ### Example /// - #[cfg_attr(bootstrap, doc = "```ignore")] - #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] + /// ```rust,compile_fail /// #![feature(supertrait_item_shadowing)] /// #![deny(resolving_to_items_shadowing_supertrait_items)] /// @@ -4924,8 +4921,7 @@ declare_lint! { /// /// struct MyType; /// MyType.hello(); - #[cfg_attr(bootstrap, doc = "```")] - #[cfg_attr(not(bootstrap), doc = "```")] + /// ``` /// /// {{produces}} /// @@ -4951,8 +4947,7 @@ declare_lint! { /// /// ### Example /// - #[cfg_attr(bootstrap, doc = "```ignore")] - #[cfg_attr(not(bootstrap), doc = "```rust,compile_fail")] + /// ```rust,compile_fail /// #![feature(supertrait_item_shadowing)] /// #![deny(shadowing_supertrait_items)] /// @@ -4965,8 +4960,7 @@ declare_lint! { /// fn hello(&self) {} /// } /// impl Downstream for T {} - #[cfg_attr(bootstrap, doc = "```")] - #[cfg_attr(not(bootstrap), doc = "```")] + /// ``` /// /// {{produces}} /// diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 714ba0f177a8..95cbec1b37b4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -550,17 +550,8 @@ struct LLVMRustSanitizerOptions { bool SanitizeKernelAddressRecover; }; -// This symbol won't be available or used when Enzyme is not enabled. -// Always set AugmentPassBuilder to true, since it registers optimizations which -// will improve the performance for Enzyme. -#ifdef ENZYME -extern "C" void registerEnzymeAndPassPipeline(llvm::PassBuilder &PB, - /* augmentPassBuilder */ bool); - -extern "C" { -extern llvm::cl::opt EnzymeFunctionToAnalyze; -} -#endif +extern "C" typedef void (*registerEnzymeAndPassPipelineFn)( + llvm::PassBuilder &PB, bool augment); extern "C" LLVMRustResult LLVMRustOptimize( LLVMModuleRef ModuleRef, LLVMTargetMachineRef TMRef, @@ -569,8 +560,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( bool LintIR, LLVMRustThinLTOBuffer **ThinLTOBufferRef, bool EmitThinLTO, bool EmitThinLTOSummary, bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, bool DisableSimplifyLibCalls, - bool EmitLifetimeMarkers, bool RunEnzyme, bool PrintBeforeEnzyme, - bool PrintAfterEnzyme, bool PrintPasses, + bool EmitLifetimeMarkers, registerEnzymeAndPassPipelineFn EnzymePtr, + bool PrintBeforeEnzyme, bool PrintAfterEnzyme, bool PrintPasses, LLVMRustSanitizerOptions *SanitizerOptions, const char *PGOGenPath, const char *PGOUsePath, bool InstrumentCoverage, const char *InstrProfileOutput, const char *PGOSampleUsePath, @@ -907,8 +898,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( } // now load "-enzyme" pass: -#ifdef ENZYME - if (RunEnzyme) { + // With dlopen, ENZYME macro may not be defined, so check EnzymePtr directly + if (EnzymePtr) { if (PrintBeforeEnzyme) { // Handle the Rust flag `-Zautodiff=PrintModBefore`. @@ -916,29 +907,19 @@ extern "C" LLVMRustResult LLVMRustOptimize( MPM.addPass(PrintModulePass(outs(), Banner, true, false)); } - registerEnzymeAndPassPipeline(PB, false); + EnzymePtr(PB, false); if (auto Err = PB.parsePassPipeline(MPM, "enzyme")) { std::string ErrMsg = toString(std::move(Err)); LLVMRustSetLastError(ErrMsg.c_str()); return LLVMRustResult::Failure; } - // Check if PrintTAFn was used and add type analysis pass if needed - if (!EnzymeFunctionToAnalyze.empty()) { - if (auto Err = PB.parsePassPipeline(MPM, "print-type-analysis")) { - std::string ErrMsg = toString(std::move(Err)); - LLVMRustSetLastError(ErrMsg.c_str()); - return LLVMRustResult::Failure; - } - } - if (PrintAfterEnzyme) { // Handle the Rust flag `-Zautodiff=PrintModAfter`. std::string Banner = "Module after EnzymeNewPM"; MPM.addPass(PrintModulePass(outs(), Banner, true, false)); } } -#endif if (PrintPasses) { // Print all passes from the PM: std::string Pipeline; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index dda06e9b2bf6..0720af0eb7e0 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1791,18 +1791,6 @@ extern "C" void LLVMRustSetNoSanitizeHWAddress(LLVMValueRef Global) { GV.setSanitizerMetadata(MD); } -#ifdef ENZYME -extern "C" { -extern llvm::cl::opt EnzymeMaxTypeDepth; -} - -extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { return EnzymeMaxTypeDepth; } -#else -extern "C" size_t LLVMRustEnzymeGetMaxTypeDepth() { - return 6; // Default fallback depth -} -#endif - // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index b02029cd9a19..4000f12459a9 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -156,8 +156,8 @@ impl<'a> std::fmt::Debug for CrateDump<'a> { enum CrateOrigin<'a> { /// This crate was a dependency of another crate. IndirectDependency { - /// Where this dependency was included from. - dep_root: &'a CratePaths, + /// Where this dependency was included from. Should only be used in error messages. + dep_root_for_errors: &'a CratePaths, /// True if the parent is private, meaning the dependent should also be private. parent_private: bool, /// Dependency info about this crate. @@ -171,9 +171,11 @@ enum CrateOrigin<'a> { impl<'a> CrateOrigin<'a> { /// Return the dependency root, if any. - fn dep_root(&self) -> Option<&'a CratePaths> { + fn dep_root_for_errors(&self) -> Option<&'a CratePaths> { match self { - CrateOrigin::IndirectDependency { dep_root, .. } => Some(dep_root), + CrateOrigin::IndirectDependency { dep_root_for_errors, .. } => { + Some(dep_root_for_errors) + } _ => None, } } @@ -193,6 +195,7 @@ impl<'a> CrateOrigin<'a> { CrateOrigin::IndirectDependency { parent_private, dep, .. } => { Some(dep.is_private || *parent_private) } + CrateOrigin::Injected => Some(true), _ => None, } } @@ -544,17 +547,7 @@ impl CStore { /// Sometimes the directly dependent crate is not specified by `--extern`, in this case, /// `private-dep` is none during loading. This is equivalent to the scenario where the /// command parameter is set to `public-dependency` - fn is_private_dep( - &self, - externs: &Externs, - name: Symbol, - private_dep: Option, - origin: CrateOrigin<'_>, - ) -> bool { - if matches!(origin, CrateOrigin::Injected) { - return true; - } - + fn is_private_dep(&self, externs: &Externs, name: Symbol, private_dep: Option) -> bool { let extern_private = externs.get(name.as_str()).map(|e| e.is_private_dep); match (extern_private, private_dep) { // Explicit non-private via `--extern`, explicit non-private from metadata, or @@ -581,7 +574,7 @@ impl CStore { let Library { source, metadata } = lib; let crate_root = metadata.get_root(); let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); - let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep, origin); + let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep); // Claim this crate number and cache it let feed = self.intern_stable_crate_id(tcx, &crate_root)?; @@ -597,8 +590,8 @@ impl CStore { // Maintain a reference to the top most crate. // Stash paths for top-most crate locally if necessary. let crate_paths; - let dep_root = if let Some(dep_root) = origin.dep_root() { - dep_root + let dep_root_for_errors = if let Some(dep_root_for_errors) = origin.dep_root_for_errors() { + dep_root_for_errors } else { crate_paths = CratePaths::new(crate_root.name(), source.clone()); &crate_paths @@ -606,7 +599,7 @@ impl CStore { let cnum_map = self.resolve_crate_deps( tcx, - dep_root, + dep_root_for_errors, &crate_root, &metadata, cnum, @@ -726,7 +719,7 @@ impl CStore { self.used_extern_options.insert(name); match self.maybe_resolve_crate(tcx, name, dep_kind, origin) { Ok(cnum) => { - self.set_used_recursively(tcx, cnum); + self.set_used_recursively(cnum); Some(cnum) } Err(err) => { @@ -735,7 +728,7 @@ impl CStore { .maybe_resolve_crate( tcx, sym::core, - CrateDepKind::Explicit, + CrateDepKind::Unconditional, CrateOrigin::Extern, ) .is_err(); @@ -757,7 +750,7 @@ impl CStore { return Err(CrateError::NonAsciiName(name)); } - let dep_root = origin.dep_root(); + let dep_root_for_errors = origin.dep_root_for_errors(); let dep = origin.dep(); let hash = dep.map(|d| d.hash); let host_hash = dep.map(|d| d.host_hash).flatten(); @@ -795,7 +788,11 @@ impl CStore { host_hash, )? { Some(res) => res, - None => return Err(locator.into_error(crate_rejections, dep_root.cloned())), + None => { + return Err( + locator.into_error(crate_rejections, dep_root_for_errors.cloned()) + ); + } } } } @@ -808,8 +805,7 @@ impl CStore { // not specified by `--extern` on command line parameters, it may be // `private-dependency` when `register_crate` is called for the first time. Then it must be updated to // `public-dependency` here. - let private_dep = - self.is_private_dep(&tcx.sess.opts.externs, name, private_dep, origin); + let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep); let data = self.get_crate_data_mut(cnum); if data.is_proc_macro_crate() { dep_kind = CrateDepKind::MacrosOnly; @@ -856,7 +852,7 @@ impl CStore { fn resolve_crate_deps( &mut self, tcx: TyCtxt<'_>, - dep_root: &CratePaths, + dep_root_for_errors: &CratePaths, crate_root: &CrateRoot, metadata: &MetadataBlob, krate: CrateNum, @@ -866,7 +862,7 @@ impl CStore { debug!( "resolving deps of external crate `{}` with dep root `{}`", crate_root.name(), - dep_root.name + dep_root_for_errors.name ); if crate_root.is_proc_macro_crate() { return Ok(CrateNumMap::new()); @@ -896,7 +892,7 @@ impl CStore { dep.name, dep_kind, CrateOrigin::IndirectDependency { - dep_root, + dep_root_for_errors, parent_private: parent_is_private, dep: &dep, }, @@ -979,9 +975,15 @@ impl CStore { }; info!("panic runtime not found -- loading {}", name); - let Some(cnum) = - self.resolve_crate(tcx, name, DUMMY_SP, CrateDepKind::Implicit, CrateOrigin::Injected) - else { + // This has to be conditional as both panic_unwind and panic_abort may be present in the + // crate graph at the same time. One of them will later be activated in dependency_formats. + let Some(cnum) = self.resolve_crate( + tcx, + name, + DUMMY_SP, + CrateDepKind::Conditional, + CrateOrigin::Injected, + ) else { return; }; let data = self.get_crate_data(cnum); @@ -1009,9 +1011,13 @@ impl CStore { info!("loading profiler"); let name = Symbol::intern(&tcx.sess.opts.unstable_opts.profiler_runtime); - let Some(cnum) = - self.resolve_crate(tcx, name, DUMMY_SP, CrateDepKind::Implicit, CrateOrigin::Injected) - else { + let Some(cnum) = self.resolve_crate( + tcx, + name, + DUMMY_SP, + CrateDepKind::Unconditional, + CrateOrigin::Injected, + ) else { return; }; let data = self.get_crate_data(cnum); @@ -1131,7 +1137,7 @@ impl CStore { tcx, name_interned, DUMMY_SP, - CrateDepKind::Explicit, + CrateDepKind::Unconditional, CrateOrigin::Extern, ); } @@ -1163,7 +1169,7 @@ impl CStore { tcx, sym::compiler_builtins, krate.spans.inner_span.shrink_to_lo(), - CrateDepKind::Explicit, + CrateDepKind::Unconditional, CrateOrigin::Injected, ) else { info!("`compiler_builtins` not resolved"); @@ -1280,7 +1286,7 @@ impl CStore { let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) { CrateDepKind::MacrosOnly } else { - CrateDepKind::Explicit + CrateDepKind::Unconditional }; let cnum = @@ -1310,7 +1316,7 @@ impl CStore { span: Span, ) -> Option { let cnum = - self.resolve_crate(tcx, name, span, CrateDepKind::Explicit, CrateOrigin::Extern)?; + self.resolve_crate(tcx, name, span, CrateDepKind::Unconditional, CrateOrigin::Extern)?; self.update_extern_crate( cnum, @@ -1328,7 +1334,7 @@ impl CStore { } pub fn maybe_process_path_extern(&mut self, tcx: TyCtxt<'_>, name: Symbol) -> Option { - self.maybe_resolve_crate(tcx, name, CrateDepKind::Explicit, CrateOrigin::Extern).ok() + self.maybe_resolve_crate(tcx, name, CrateDepKind::Unconditional, CrateOrigin::Extern).ok() } } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 8054a48d37af..7b8e8cb42e53 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -242,7 +242,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { let src = tcx.used_crate_source(cnum); if src.dylib.is_none() && !formats.contains_key(&cnum) - && tcx.dep_kind(cnum) == CrateDepKind::Explicit + && tcx.dep_kind(cnum) == CrateDepKind::Unconditional { assert!(src.rlib.is_some() || src.rmeta.is_some()); info!("adding staticlib: {}", tcx.crate_name(cnum)); @@ -355,8 +355,8 @@ fn attempt_static(tcx: TyCtxt<'_>, unavailable: &mut Vec) -> Option Linkage::Static, - CrateDepKind::MacrosOnly | CrateDepKind::Implicit => Linkage::NotLinked, + CrateDepKind::Unconditional => Linkage::Static, + CrateDepKind::MacrosOnly | CrateDepKind::Conditional => Linkage::NotLinked, }), cnum ); diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index abfd078f7462..af8ff5643f2c 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -251,9 +251,7 @@ impl<'a> MissingNativeLibrary<'a> { // if it looks like the user has provided a complete filename rather just the bare lib name, // then provide a note that they might want to try trimming the name let suggested_name = if !verbatim { - if let Some(libname) = libname.strip_prefix("lib") - && let Some(libname) = libname.strip_suffix(".a") - { + if let Some(libname) = libname.strip_circumfix("lib", ".a") { // this is a unix style filename so trim prefix & suffix Some(libname) } else if let Some(libname) = libname.strip_suffix(".lib") { diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index f31c4938e118..f3b738f93d2d 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -10,6 +10,7 @@ #![feature(never_type)] #![feature(proc_macro_internals)] #![feature(result_option_map_or_default)] +#![feature(strip_circumfix)] #![feature(trusted_len)] // tidy-alphabetical-end diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 5f7bda9c9064..f8610f0c5b03 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -116,8 +116,6 @@ pub(crate) struct CrateMetadata { /// Maps crate IDs as they are were seen from this crate's compilation sessions into /// IDs as they are seen from the current compilation session. cnum_map: CrateNumMap, - /// Same ID set as `cnum_map` plus maybe some injected crates like panic runtime. - dependencies: Vec, /// How to link (or not link) this crate to the currently compiled crate. dep_kind: CrateDepKind, /// Filesystem location of this crate. @@ -1677,15 +1675,14 @@ impl<'a> CrateMetadataRef<'a> { for virtual_dir in virtual_source_base_dir.iter().flatten() { if let Some(real_dir) = &real_source_base_dir && let rustc_span::FileName::Real(old_name) = name - && let (_working_dir, embeddable_name) = - old_name.embeddable_name(RemapPathScopeComponents::MACRO) - && let Ok(rest) = embeddable_name.strip_prefix(virtual_dir) + && let virtual_path = old_name.path(RemapPathScopeComponents::MACRO) + && let Ok(rest) = virtual_path.strip_prefix(virtual_dir) { let new_path = real_dir.join(rest); debug!( "try_to_translate_virtual_to_real: `{}` -> `{}`", - embeddable_name.display(), + virtual_path.display(), new_path.display(), ); @@ -1897,7 +1894,6 @@ impl CrateMetadata { .collect(); let alloc_decoding_state = AllocDecodingState::new(root.interpret_alloc_index.decode(&blob).collect()); - let dependencies = cnum_map.iter().copied().collect(); // Pre-decode the DefPathHash->DefIndex table. This is a cheap operation // that does not copy any data. It just does some data verification. @@ -1915,7 +1911,6 @@ impl CrateMetadata { alloc_decoding_state, cnum, cnum_map, - dependencies, dep_kind, source: Arc::new(source), private_dep, @@ -1941,7 +1936,7 @@ impl CrateMetadata { } pub(crate) fn dependencies(&self) -> impl Iterator { - self.dependencies.iter().copied() + self.cnum_map.iter().copied() } pub(crate) fn target_modifiers(&self) -> TargetModifiers { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index f441788fdcb6..36fe7f380069 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -623,15 +623,15 @@ impl CStore { self.get_crate_data(cnum).get_proc_macro_quoted_span(tcx, id) } - pub fn set_used_recursively(&mut self, tcx: TyCtxt<'_>, cnum: CrateNum) { + pub fn set_used_recursively(&mut self, cnum: CrateNum) { let cmeta = self.get_crate_data_mut(cnum); if !cmeta.used { cmeta.used = true; - let dependencies = mem::take(&mut cmeta.dependencies); - for &dep_cnum in &dependencies { - self.set_used_recursively(tcx, dep_cnum); + let cnum_map = mem::take(&mut cmeta.cnum_map); + for &dep_cnum in cnum_map.iter() { + self.set_used_recursively(dep_cnum); } - self.get_crate_data_mut(cnum).dependencies = dependencies; + self.get_crate_data_mut(cnum).cnum_map = cnum_map; } } @@ -663,11 +663,11 @@ impl CStore { if cmeta.update_extern_crate_diagnostics(extern_crate) { // Propagate the extern crate info to dependencies if it was updated. let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate }; - let dependencies = mem::take(&mut cmeta.dependencies); - for &dep_cnum in &dependencies { + let cnum_map = mem::take(&mut cmeta.cnum_map); + for &dep_cnum in cnum_map.iter() { self.update_transitive_extern_crate_diagnostics(dep_cnum, extern_crate); } - self.get_crate_data_mut(cnum).dependencies = dependencies; + self.get_crate_data_mut(cnum).cnum_map = cnum_map; } } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b15ed34fc356..94bc4d3fa530 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -879,13 +879,9 @@ fn analyze_attr(attr: &hir::Attribute, state: &mut AnalyzeAttrState<'_>) -> bool should_encode = true; } } else if let hir::Attribute::Parsed(AttributeKind::Doc(d)) = attr { - // If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in - // `#[doc(inline)]`), then we can remove it. It won't be inlinable in downstream crates. - if d.inline.is_empty() { - should_encode = true; - if d.hidden.is_some() { - state.is_doc_hidden = true; - } + should_encode = true; + if d.hidden.is_some() { + state.is_doc_hidden = true; } } else if let &[sym::diagnostic, seg] = &*attr.path() { should_encode = rustc_feature::is_stable_diagnostic_attribute(seg, state.features); @@ -1451,7 +1447,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { _ => false, } { - continue; + // MGCA doesn't have unnecessary DefIds + if !tcx.features().min_generic_const_args() { + continue; + } } if def_kind == DefKind::Field diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index ee3e89e57bd4..5f62d44df6b6 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -30,7 +30,6 @@ #![allow(rustc::direct_use_of_rustc_type_ir)] #![allow(rustc::untranslatable_diagnostic)] #![cfg_attr(bootstrap, feature(array_windows))] -#![deny(clippy::manual_let_else)] #![feature(allocator_api)] #![feature(assert_matches)] #![feature(associated_type_defaults)] diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index edbb736128cf..3d893bf75e0b 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -14,7 +14,7 @@ use std::ops::Index; use std::sync::Arc; use rustc_abi::{FieldIdx, Integer, Size, VariantIdx}; -use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_ast::{AsmMacro, InlineAsmOptions, InlineAsmTemplatePiece, Mutability}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd}; @@ -811,11 +811,11 @@ pub enum PatKind<'tcx> { DerefPattern { subpattern: Box>, /// Whether the pattern scrutinee needs to be borrowed in order to call `Deref::deref` or - /// `DerefMut::deref_mut`, and if so, which. This is `ByRef::No` for deref patterns on + /// `DerefMut::deref_mut`, and if so, which. This is `DerefPatBorrowMode::Box` for deref patterns on /// boxes; they are lowered using a built-in deref rather than a method call, thus they /// don't borrow the scrutinee. #[type_visitable(ignore)] - borrow: ByRef, + borrow: DerefPatBorrowMode, }, /// One of the following: @@ -879,6 +879,12 @@ pub enum PatKind<'tcx> { Error(ErrorGuaranteed), } +#[derive(Copy, Clone, Debug, HashStable)] +pub enum DerefPatBorrowMode { + Borrow(Mutability), + Box, +} + /// A range pattern. /// The boundaries must be of the same type and that type must be numeric. #[derive(Clone, Debug, PartialEq, HashStable, TypeVisitable)] diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index d0fd2f02a336..81b8142d03f3 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1178,6 +1178,10 @@ where matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().transparent()) } + fn is_scalable_vector(this: TyAndLayout<'tcx>) -> bool { + this.ty.is_scalable_vector() + } + /// See [`TyAndLayout::pass_indirectly_in_non_rustic_abis`] for details. fn is_pass_indirectly_in_non_rustic_abis_flag_set(this: TyAndLayout<'tcx>) -> bool { matches!(this.ty.kind(), ty::Adt(def, _) if def.repr().flags.contains(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS)) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b91176e3d486..c967312d8bb8 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -24,7 +24,9 @@ pub use assoc::*; 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_abi::{ + Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, VariantIdx, +}; use rustc_ast::AttrVec; use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; @@ -195,7 +197,6 @@ pub struct ResolverGlobalCtxt { #[derive(Debug)] pub struct ResolverAstLowering { pub legacy_const_generic_args: FxHashMap>>, - /// Resolutions for nodes that have a single resolution. pub partial_res_map: NodeMap, /// Resolutions for import nodes, which have multiple resolutions in different namespaces. @@ -220,6 +221,9 @@ pub struct ResolverAstLowering { /// Information about functions signatures for delegation items expansion pub delegation_fn_sigs: LocalDefIdMap, + // NodeIds (either delegation.id or item_id in case of a trait impl) for signature resolution, + // for details see https://github.com/rust-lang/rust/issues/118212#issuecomment-2160686914 + pub delegation_sig_resolution_nodes: LocalDefIdMap, } bitflags::bitflags! { @@ -1515,6 +1519,17 @@ impl<'tcx> TyCtxt<'tcx> { } let attributes = self.get_all_attrs(did); + let elt = find_attr!( + attributes, + AttributeKind::RustcScalableVector { element_count, .. } => element_count + ) + .map(|elt| match elt { + Some(n) => ScalableElt::ElementCount(*n), + None => ScalableElt::Container, + }); + if elt.is_some() { + flags.insert(ReprFlags::IS_SCALABLE); + } if let Some(reprs) = find_attr!(attributes, AttributeKind::Repr { reprs, .. } => reprs) { for (r, _) in reprs { flags.insert(match *r { @@ -1579,7 +1594,14 @@ impl<'tcx> TyCtxt<'tcx> { flags.insert(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS); } - ReprOptions { int: size, align: max_align, pack: min_pack, flags, field_shuffle_seed } + ReprOptions { + int: size, + align: max_align, + pack: min_pack, + flags, + field_shuffle_seed, + scalable: elt, + } } /// Look up the name of a definition across crates. This does not look at HIR. diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 852a0017687f..c3834607e923 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -7,7 +7,7 @@ use std::borrow::Cow; use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; -use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, ScalableElt, VariantIdx}; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::LangItem; @@ -1253,6 +1253,14 @@ impl<'tcx> Ty<'tcx> { } } + #[inline] + pub fn is_scalable_vector(self) -> bool { + match self.kind() { + Adt(def, _) => def.repr().scalable(), + _ => false, + } + } + pub fn sequence_element_type(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { match self.kind() { Array(ty, _) | Slice(ty) => *ty, @@ -1261,6 +1269,19 @@ impl<'tcx> Ty<'tcx> { } } + pub fn scalable_vector_element_count_and_type(self, tcx: TyCtxt<'tcx>) -> (u16, Ty<'tcx>) { + let Adt(def, args) = self.kind() else { + bug!("`scalable_vector_size_and_type` called on invalid type") + }; + let Some(ScalableElt::ElementCount(element_count)) = def.repr().scalable else { + bug!("`scalable_vector_size_and_type` called on non-scalable vector type"); + }; + let variant = def.non_enum_variant(); + assert_eq!(variant.fields.len(), 1); + let field_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args); + (element_count, field_ty) + } + pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { let Adt(def, args) = self.kind() else { bug!("`simd_size_and_type` called on invalid type") diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index a3353661d9dd..2c5e4957d0ae 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -11,7 +11,6 @@ use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap}; use rustc_hir::hir_id::OwnerId; use rustc_hir::{ self as hir, BindingMode, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability, - Pinnedness, }; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; @@ -21,6 +20,7 @@ use rustc_span::Span; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::Canonical; use crate::mir::FakeReadCause; +use crate::thir::DerefPatBorrowMode; use crate::traits::ObligationCause; use crate::ty::{ self, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData, GenericArgKind, GenericArgs, @@ -491,13 +491,18 @@ impl<'tcx> TypeckResults<'tcx> { /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner /// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`. /// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee; - /// in this case, we return `ByRef::No`. - pub fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: &hir::Pat<'_>) -> ByRef { + /// in this case, we return `DerefPatBorrowMode::Box`. + pub fn deref_pat_borrow_mode( + &self, + pointer_ty: Ty<'_>, + inner: &hir::Pat<'_>, + ) -> DerefPatBorrowMode { if pointer_ty.is_box() { - ByRef::No + DerefPatBorrowMode::Box } else { - let mutable = self.pat_has_ref_mut_binding(inner); - ByRef::Yes(Pinnedness::Not, if mutable { Mutability::Mut } else { Mutability::Not }) + let mutability = + if self.pat_has_ref_mut_binding(inner) { Mutability::Mut } else { Mutability::Not }; + DerefPatBorrowMode::Borrow(mutability) } } diff --git a/compiler/rustc_mir_build/src/builder/matches/buckets.rs b/compiler/rustc_mir_build/src/builder/matches/buckets.rs new file mode 100644 index 000000000000..6c5f0ed28a82 --- /dev/null +++ b/compiler/rustc_mir_build/src/builder/matches/buckets.rs @@ -0,0 +1,352 @@ +use std::cmp::Ordering; + +use rustc_data_structures::fx::FxIndexMap; +use rustc_middle::mir::{BinOp, Place}; +use rustc_middle::span_bug; +use tracing::debug; + +use crate::builder::Builder; +use crate::builder::matches::test::is_switch_ty; +use crate::builder::matches::{Candidate, Test, TestBranch, TestKind, TestableCase}; + +/// Output of [`Builder::partition_candidates_into_buckets`]. +pub(crate) struct PartitionedCandidates<'tcx, 'b, 'c> { + /// For each possible outcome of the test, the candidates that are matched in that outcome. + pub(crate) target_candidates: FxIndexMap, Vec<&'b mut Candidate<'tcx>>>, + /// The remaining candidates that weren't associated with any test outcome. + pub(crate) remaining_candidates: &'b mut [&'c mut Candidate<'tcx>], +} + +impl<'a, 'tcx> Builder<'a, 'tcx> { + /// Given a test, we partition the input candidates into several buckets. + /// If a candidate matches in exactly one of the branches of `test` + /// (and no other branches), we put it into the corresponding bucket. + /// If it could match in more than one of the branches of `test`, the test + /// doesn't usefully apply to it, and we stop partitioning candidates. + /// + /// Importantly, we also **mutate** the branched candidates to remove match pairs + /// that are entailed by the outcome of the test, and add any sub-pairs of the + /// removed pairs. + /// + /// For example: + /// ``` + /// # let (x, y, z) = (true, true, true); + /// match (x, y, z) { + /// (true , _ , true ) => true, // (0) + /// (false, false, _ ) => false, // (1) + /// (_ , true , _ ) => true, // (2) + /// (true , _ , false) => false, // (3) + /// } + /// # ; + /// ``` + /// + /// Assume we are testing on `x`. Conceptually, there are 2 overlapping candidate sets: + /// - If the outcome is that `x` is true, candidates {0, 2, 3} are possible + /// - If the outcome is that `x` is false, candidates {1, 2} are possible + /// + /// Following our algorithm: + /// - Candidate 0 is bucketed into outcome `x == true` + /// - Candidate 1 is bucketed into outcome `x == false` + /// - Candidate 2 remains unbucketed, because testing `x` has no effect on it + /// - Candidate 3 remains unbucketed, because a previous candidate (2) was unbucketed + /// - This helps preserve the illusion that candidates are tested "in order" + /// + /// The bucketed candidates are mutated to remove entailed match pairs: + /// - candidate 0 becomes `[z @ true]` since we know that `x` was `true`; + /// - candidate 1 becomes `[y @ false]` since we know that `x` was `false`. + pub(super) fn partition_candidates_into_buckets<'b, 'c>( + &mut self, + match_place: Place<'tcx>, + test: &Test<'tcx>, + mut candidates: &'b mut [&'c mut Candidate<'tcx>], + ) -> PartitionedCandidates<'tcx, 'b, 'c> { + // For each of the possible outcomes, collect a vector of candidates that apply if the test + // has that particular outcome. + let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_>>> = Default::default(); + + let total_candidate_count = candidates.len(); + + // Partition the candidates into the appropriate vector in `target_candidates`. + // Note that at some point we may encounter a candidate where the test is not relevant; + // at that point, we stop partitioning. + while let Some(candidate) = candidates.first_mut() { + let Some(branch) = + self.choose_bucket_for_candidate(match_place, test, candidate, &target_candidates) + else { + break; + }; + let (candidate, rest) = candidates.split_first_mut().unwrap(); + target_candidates.entry(branch).or_insert_with(Vec::new).push(candidate); + candidates = rest; + } + + // At least the first candidate ought to be tested + assert!( + total_candidate_count > candidates.len(), + "{total_candidate_count}, {candidates:#?}" + ); + debug!("tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("untested_candidates: {}", candidates.len()); + + PartitionedCandidates { target_candidates, remaining_candidates: candidates } + } + + /// Given that we are performing `test` against `test_place`, this job + /// sorts out what the status of `candidate` will be after the test. See + /// `test_candidates` for the usage of this function. The candidate may + /// be modified to update its `match_pairs`. + /// + /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is + /// a variant test, then we would modify the candidate to be `(x as + /// Option).0 @ P0` and return the index corresponding to the variant + /// `Some`. + /// + /// However, in some cases, the test may just not be relevant to candidate. + /// For example, suppose we are testing whether `foo.x == 22`, but in one + /// match arm we have `Foo { x: _, ... }`... in that case, the test for + /// the value of `x` has no particular relevance to this candidate. In + /// such cases, this function just returns None without doing anything. + /// This is used by the overall `match_candidates` algorithm to structure + /// the match as a whole. See `match_candidates` for more details. + /// + /// FIXME(#29623). In some cases, we have some tricky choices to make. for + /// example, if we are testing that `x == 22`, but the candidate is `x @ + /// 13..55`, what should we do? In the event that the test is true, we know + /// that the candidate applies, but in the event of false, we don't know + /// that it *doesn't* apply. For now, we return false, indicate that the + /// test does not apply to this candidate, but it might be we can get + /// tighter match code if we do something a bit different. + fn choose_bucket_for_candidate( + &mut self, + test_place: Place<'tcx>, + test: &Test<'tcx>, + candidate: &mut Candidate<'tcx>, + // Other candidates that have already been partitioned into a bucket for this test, if any + prior_candidates: &FxIndexMap, Vec<&mut Candidate<'tcx>>>, + ) -> Option> { + // Find the match_pair for this place (if any). At present, + // afaik, there can be at most one. (In the future, if we + // adopted a more general `@` operator, there might be more + // than one, but it'd be very unusual to have two sides that + // both require tests; you'd expect one side to be simplified + // away.) + let (match_pair_index, match_pair) = candidate + .match_pairs + .iter() + .enumerate() + .find(|&(_, mp)| mp.place == Some(test_place))?; + + // If true, the match pair is completely entailed by its corresponding test + // branch, so it can be removed. If false, the match pair is _compatible_ + // with its test branch, but still needs a more specific test. + let fully_matched; + let ret = match (&test.kind, &match_pair.testable_case) { + // If we are performing a variant switch, then this + // informs variant patterns, but nothing else. + ( + &TestKind::Switch { adt_def: tested_adt_def }, + &TestableCase::Variant { adt_def, variant_index }, + ) => { + assert_eq!(adt_def, tested_adt_def); + fully_matched = true; + Some(TestBranch::Variant(variant_index)) + } + + // If we are performing a switch over integers, then this informs integer + // equality, but nothing else. + // + // FIXME(#29623) we could use PatKind::Range to rule + // things out here, in some cases. + // + // FIXME(Zalathar): Is the `is_switch_ty` test unnecessary? + (TestKind::SwitchInt, &TestableCase::Constant { value }) + if is_switch_ty(match_pair.pattern_ty) => + { + // An important invariant of candidate bucketing is that a candidate + // must not match in multiple branches. For `SwitchInt` tests, adding + // a new value might invalidate that property for range patterns that + // have already been partitioned into the failure arm, so we must take care + // not to add such values here. + let is_covering_range = |testable_case: &TestableCase<'tcx>| { + testable_case.as_range().is_some_and(|range| { + matches!(range.contains(value, self.tcx), None | Some(true)) + }) + }; + let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| { + candidate.match_pairs.iter().any(|mp| { + mp.place == Some(test_place) && is_covering_range(&mp.testable_case) + }) + }; + if prior_candidates + .get(&TestBranch::Failure) + .is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate)) + { + fully_matched = false; + None + } else { + fully_matched = true; + Some(TestBranch::Constant(value)) + } + } + (TestKind::SwitchInt, TestableCase::Range(range)) => { + // When performing a `SwitchInt` test, a range pattern can be + // sorted into the failure arm if it doesn't contain _any_ of + // the values being tested. (This restricts what values can be + // added to the test by subsequent candidates.) + fully_matched = false; + let not_contained = prior_candidates + .keys() + .filter_map(|br| br.as_constant()) + .all(|val| matches!(range.contains(val, self.tcx), Some(false))); + + not_contained.then(|| { + // No switch values are contained in the pattern range, + // so the pattern can be matched only if this test fails. + TestBranch::Failure + }) + } + + (TestKind::If, TestableCase::Constant { value }) => { + fully_matched = true; + let value = value.try_to_bool().unwrap_or_else(|| { + span_bug!(test.span, "expected boolean value but got {value:?}") + }); + Some(if value { TestBranch::Success } else { TestBranch::Failure }) + } + + ( + &TestKind::Len { len: test_len, op: BinOp::Eq }, + &TestableCase::Slice { len, variable_length }, + ) => { + match (test_len.cmp(&(len as u64)), variable_length) { + (Ordering::Equal, false) => { + // on true, min_len = len = $actual_length, + // on false, len != $actual_length + fully_matched = true; + Some(TestBranch::Success) + } + (Ordering::Less, _) => { + // test_len < pat_len. If $actual_len = test_len, + // then $actual_len < pat_len and we don't have + // enough elements. + fully_matched = false; + Some(TestBranch::Failure) + } + (Ordering::Equal | Ordering::Greater, true) => { + // This can match both if $actual_len = test_len >= pat_len, + // and if $actual_len > test_len. We can't advance. + fully_matched = false; + None + } + (Ordering::Greater, false) => { + // test_len != pat_len, so if $actual_len = test_len, then + // $actual_len != pat_len. + fully_matched = false; + Some(TestBranch::Failure) + } + } + } + ( + &TestKind::Len { len: test_len, op: BinOp::Ge }, + &TestableCase::Slice { len, variable_length }, + ) => { + // the test is `$actual_len >= test_len` + match (test_len.cmp(&(len as u64)), variable_length) { + (Ordering::Equal, true) => { + // $actual_len >= test_len = pat_len, + // so we can match. + fully_matched = true; + Some(TestBranch::Success) + } + (Ordering::Less, _) | (Ordering::Equal, false) => { + // test_len <= pat_len. If $actual_len < test_len, + // then it is also < pat_len, so the test passing is + // necessary (but insufficient). + fully_matched = false; + Some(TestBranch::Success) + } + (Ordering::Greater, false) => { + // test_len > pat_len. If $actual_len >= test_len > pat_len, + // then we know we won't have a match. + fully_matched = false; + Some(TestBranch::Failure) + } + (Ordering::Greater, true) => { + // test_len < pat_len, and is therefore less + // strict. This can still go both ways. + fully_matched = false; + None + } + } + } + + (TestKind::Range(test), TestableCase::Range(pat)) => { + if test == pat { + fully_matched = true; + Some(TestBranch::Success) + } else { + fully_matched = false; + // If the testing range does not overlap with pattern range, + // the pattern can be matched only if this test fails. + if !test.overlaps(pat, self.tcx)? { Some(TestBranch::Failure) } else { None } + } + } + (TestKind::Range(range), &TestableCase::Constant { value }) => { + fully_matched = false; + if !range.contains(value, self.tcx)? { + // `value` is not contained in the testing range, + // so `value` can be matched only if this test fails. + Some(TestBranch::Failure) + } else { + None + } + } + + (TestKind::Eq { value: test_val, .. }, TestableCase::Constant { value: case_val }) => { + if test_val == case_val { + fully_matched = true; + Some(TestBranch::Success) + } else { + fully_matched = false; + Some(TestBranch::Failure) + } + } + + (TestKind::Deref { temp: test_temp, .. }, TestableCase::Deref { temp, .. }) + if test_temp == temp => + { + fully_matched = true; + Some(TestBranch::Success) + } + + (TestKind::Never, _) => { + fully_matched = true; + Some(TestBranch::Success) + } + + ( + TestKind::Switch { .. } + | TestKind::SwitchInt { .. } + | TestKind::If + | TestKind::Len { .. } + | TestKind::Range { .. } + | TestKind::Eq { .. } + | TestKind::Deref { .. }, + _, + ) => { + fully_matched = false; + None + } + }; + + if fully_matched { + // Replace the match pair by its sub-pairs. + let match_pair = candidate.match_pairs.remove(match_pair_index); + candidate.match_pairs.extend(match_pair.subpairs); + // Move or-patterns to the end. + candidate.sort_match_pairs(); + } + + ret + } +} diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index a0d54354a9c6..6b4ba9243479 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,14 +1,13 @@ use std::sync::Arc; use rustc_abi::FieldIdx; -use rustc_hir::ByRef; use rustc_middle::mir::*; use rustc_middle::thir::*; -use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; -use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestCase}; +use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestableCase}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in @@ -132,7 +131,7 @@ impl<'tcx> MatchPairTree<'tcx> { let place = place_builder.try_to_place(cx); let mut subpairs = Vec::new(); - let test_case = match pattern.kind { + let testable_case = match pattern.kind { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, PatKind::Or { ref pats } => { @@ -146,18 +145,18 @@ impl<'tcx> MatchPairTree<'tcx> { // FIXME(@dianne): this needs updating/removing if we always merge or-patterns extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); } - Some(TestCase::Or { pats }) + Some(TestableCase::Or { pats }) } PatKind::Range(ref range) => { if range.is_full_range(cx.tcx) == Some(true) { None } else { - Some(TestCase::Range(Arc::clone(range))) + Some(TestableCase::Range(Arc::clone(range))) } } - PatKind::Constant { value } => Some(TestCase::Constant { value }), + PatKind::Constant { value } => Some(TestableCase::Constant { value }), PatKind::AscribeUserType { ascription: Ascription { ref annotation, variance }, @@ -256,7 +255,7 @@ impl<'tcx> MatchPairTree<'tcx> { if prefix.is_empty() && slice.is_some() && suffix.is_empty() { None } else { - Some(TestCase::Slice { + Some(TestableCase::Slice { len: prefix.len() + suffix.len(), variable_length: slice.is_some(), }) @@ -275,7 +274,11 @@ impl<'tcx> MatchPairTree<'tcx> { cx.def_id.into(), ) }) && !adt_def.variant_list_has_applicable_non_exhaustive(); - if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) } + if irrefutable { + None + } else { + Some(TestableCase::Variant { adt_def, variant_index }) + } } PatKind::Leaf { ref subpatterns } => { @@ -283,8 +286,9 @@ impl<'tcx> MatchPairTree<'tcx> { None } + // FIXME: Pin-patterns should probably have their own pattern kind, + // instead of overloading `PatKind::Deref` via the pattern type. PatKind::Deref { ref subpattern } - | PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } if let Some(ref_ty) = pattern.ty.pinned_ty() && ref_ty.is_ref() => { @@ -298,12 +302,8 @@ impl<'tcx> MatchPairTree<'tcx> { None } - PatKind::DerefPattern { borrow: ByRef::Yes(Pinnedness::Pinned, _), .. } => { - rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty) - } - PatKind::Deref { ref subpattern } - | PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => { + | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => { MatchPairTree::for_pattern( place_builder.deref(), subpattern, @@ -316,7 +316,7 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::DerefPattern { ref subpattern, - borrow: ByRef::Yes(Pinnedness::Not, mutability), + borrow: DerefPatBorrowMode::Borrow(mutability), } => { // Create a new temporary for each deref pattern. // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? @@ -331,17 +331,23 @@ impl<'tcx> MatchPairTree<'tcx> { &mut subpairs, extra_data, ); - Some(TestCase::Deref { temp, mutability }) + Some(TestableCase::Deref { temp, mutability }) } - PatKind::Never => Some(TestCase::Never), + PatKind::Never => Some(TestableCase::Never), }; - if let Some(test_case) = test_case { + if let Some(testable_case) = testable_case { // This pattern is refutable, so push a new match-pair node. + // + // Note: unless test_case is TestCase::Or, place must not be None. + // This means that the closure capture analysis in + // rustc_hir_typeck::upvar, and in particular the pattern handling + // code of ExprUseVisitor, must capture all of the places we'll use. + // Make sure to keep these two parts in sync! match_pairs.push(MatchPairTree { place, - test_case, + testable_case, subpairs, pattern_ty: pattern.ty, pattern_span: pattern.span, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 433c3e5aaaee..66e36a99d3c4 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -11,10 +11,10 @@ use std::mem; use std::sync::Arc; use itertools::{Itertools, Position}; -use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node, Pinnedness}; +use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node}; use rustc_middle::middle::region::{self, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; @@ -27,6 +27,7 @@ use tracing::{debug, instrument}; use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard}; use crate::builder::expr::as_place::PlaceBuilder; +use crate::builder::matches::buckets::PartitionedCandidates; use crate::builder::matches::user_ty::ProjectedUserTypesNode; use crate::builder::scope::DropKind; use crate::builder::{ @@ -34,6 +35,7 @@ use crate::builder::{ }; // helper functions, broken out by category: +mod buckets; mod match_pair; mod test; mod user_ty; @@ -918,10 +920,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { visit_subpat(self, subpattern, &user_tys.deref(), f); } - PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => { - visit_subpat(self, subpattern, &user_tys.leaf(FieldIdx::ZERO).deref(), f); - } - PatKind::DerefPattern { ref subpattern, .. } => { visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f); } @@ -1068,7 +1066,7 @@ struct Candidate<'tcx> { /// Key mutations include: /// /// - When a match pair is fully satisfied by a test, it is removed from the - /// list, and its subpairs are added instead (see [`Builder::sort_candidate`]). + /// list, and its subpairs are added instead (see [`Builder::choose_bucket_for_candidate`]). /// - During or-pattern expansion, any leading or-pattern is removed, and is /// converted into subcandidates (see [`Builder::expand_and_match_or_candidates`]). /// - After a candidate's subcandidates have been lowered, a copy of any remaining @@ -1076,7 +1074,7 @@ struct Candidate<'tcx> { /// (see [`Builder::test_remaining_match_pairs_after_or`]). /// /// Invariants: - /// - All or-patterns ([`TestCase::Or`]) have been sorted to the end. + /// - All or-patterns ([`TestableCase::Or`]) have been sorted to the end. match_pairs: Vec>, /// ...and if this is non-empty, one of these subcandidates also has to match... @@ -1162,12 +1160,15 @@ impl<'tcx> Candidate<'tcx> { /// Restores the invariant that or-patterns must be sorted to the end. fn sort_match_pairs(&mut self) { - self.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); + self.match_pairs.sort_by_key(|pair| matches!(pair.testable_case, TestableCase::Or { .. })); } /// Returns whether the first match pair of this candidate is an or-pattern. fn starts_with_or_pattern(&self) -> bool { - matches!(&*self.match_pairs, [MatchPairTree { test_case: TestCase::Or { .. }, .. }, ..]) + matches!( + &*self.match_pairs, + [MatchPairTree { testable_case: TestableCase::Or { .. }, .. }, ..] + ) } /// Visit the leaf candidates (those with no subcandidates) contained in @@ -1253,13 +1254,13 @@ struct Ascription<'tcx> { /// /// Created by [`MatchPairTree::for_pattern`], and then inspected primarily by: /// - [`Builder::pick_test_for_match_pair`] (to choose a test) -/// - [`Builder::sort_candidate`] (to see how the test interacts with a match pair) +/// - [`Builder::choose_bucket_for_candidate`] (to see how the test interacts with a match pair) /// /// Note that or-patterns are not tested directly like the other variants. /// Instead they participate in or-pattern expansion, where they are transformed into /// subcandidates. See [`Builder::expand_and_match_or_candidates`]. #[derive(Debug, Clone)] -enum TestCase<'tcx> { +enum TestableCase<'tcx> { Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx }, Constant { value: ty::Value<'tcx> }, Range(Arc>), @@ -1269,7 +1270,7 @@ enum TestCase<'tcx> { Or { pats: Box<[FlatPat<'tcx>]> }, } -impl<'tcx> TestCase<'tcx> { +impl<'tcx> TestableCase<'tcx> { fn as_range(&self) -> Option<&PatRange<'tcx>> { if let Self::Range(v) = self { Some(v.as_ref()) } else { None } } @@ -1287,12 +1288,12 @@ pub(crate) struct MatchPairTree<'tcx> { /// --- /// This can be `None` if it referred to a non-captured place in a closure. /// - /// Invariant: Can only be `None` when `test_case` is `Or`. + /// Invariant: Can only be `None` when `testable_case` is `Or`. /// Therefore this must be `Some(_)` after or-pattern expansion. place: Option>, /// ... must pass this test... - test_case: TestCase<'tcx>, + testable_case: TestableCase<'tcx>, /// ... and these subpairs must match. /// @@ -1315,7 +1316,7 @@ enum TestKind<'tcx> { /// Test what enum variant a value is. /// /// The subset of expected variants is not stored here; instead they are - /// extracted from the [`TestCase`]s of the candidates participating in the + /// extracted from the [`TestableCase`]s of the candidates participating in the /// test. Switch { /// The enum type being tested. @@ -1325,7 +1326,7 @@ enum TestKind<'tcx> { /// Test what value an integer or `char` has. /// /// The test's target values are not stored here; instead they are extracted - /// from the [`TestCase`]s of the candidates participating in the test. + /// from the [`TestableCase`]s of the candidates participating in the test. SwitchInt, /// Test whether a `bool` is `true` or `false`. @@ -1946,7 +1947,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate: &mut Candidate<'tcx>, match_pair: MatchPairTree<'tcx>, ) { - let TestCase::Or { pats } = match_pair.test_case else { bug!() }; + let TestableCase::Or { pats } = match_pair.testable_case else { bug!() }; debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats); candidate.or_span = Some(match_pair.pattern_span); candidate.subcandidates = pats @@ -2114,7 +2115,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug_assert!( remaining_match_pairs .iter() - .all(|match_pair| matches!(match_pair.test_case, TestCase::Or { .. })) + .all(|match_pair| matches!(match_pair.testable_case, TestableCase::Or { .. })) ); // Visit each leaf candidate within this subtree, add a copy of the remaining @@ -2172,86 +2173,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (match_place, test) } - /// Given a test, we partition the input candidates into several buckets. - /// If a candidate matches in exactly one of the branches of `test` - /// (and no other branches), we put it into the corresponding bucket. - /// If it could match in more than one of the branches of `test`, the test - /// doesn't usefully apply to it, and we stop partitioning candidates. - /// - /// Importantly, we also **mutate** the branched candidates to remove match pairs - /// that are entailed by the outcome of the test, and add any sub-pairs of the - /// removed pairs. - /// - /// This returns a pair of - /// - the candidates that weren't sorted; - /// - for each possible outcome of the test, the candidates that match in that outcome. - /// - /// For example: - /// ``` - /// # let (x, y, z) = (true, true, true); - /// match (x, y, z) { - /// (true , _ , true ) => true, // (0) - /// (false, false, _ ) => false, // (1) - /// (_ , true , _ ) => true, // (2) - /// (true , _ , false) => false, // (3) - /// } - /// # ; - /// ``` - /// - /// Assume we are testing on `x`. Conceptually, there are 2 overlapping candidate sets: - /// - If the outcome is that `x` is true, candidates {0, 2, 3} are possible - /// - If the outcome is that `x` is false, candidates {1, 2} are possible - /// - /// Following our algorithm: - /// - Candidate 0 is sorted into outcome `x == true` - /// - Candidate 1 is sorted into outcome `x == false` - /// - Candidate 2 remains unsorted, because testing `x` has no effect on it - /// - Candidate 3 remains unsorted, because a previous candidate (2) was unsorted - /// - This helps preserve the illusion that candidates are tested "in order" - /// - /// The sorted candidates are mutated to remove entailed match pairs: - /// - candidate 0 becomes `[z @ true]` since we know that `x` was `true`; - /// - candidate 1 becomes `[y @ false]` since we know that `x` was `false`. - fn sort_candidates<'b, 'c>( - &mut self, - match_place: Place<'tcx>, - test: &Test<'tcx>, - mut candidates: &'b mut [&'c mut Candidate<'tcx>], - ) -> ( - &'b mut [&'c mut Candidate<'tcx>], - FxIndexMap, Vec<&'b mut Candidate<'tcx>>>, - ) { - // For each of the possible outcomes, collect vector of candidates that apply if the test - // has that particular outcome. - let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_>>> = Default::default(); - - let total_candidate_count = candidates.len(); - - // Sort the candidates into the appropriate vector in `target_candidates`. Note that at some - // point we may encounter a candidate where the test is not relevant; at that point, we stop - // sorting. - while let Some(candidate) = candidates.first_mut() { - let Some(branch) = - self.sort_candidate(match_place, test, candidate, &target_candidates) - else { - break; - }; - let (candidate, rest) = candidates.split_first_mut().unwrap(); - target_candidates.entry(branch).or_insert_with(Vec::new).push(candidate); - candidates = rest; - } - - // At least the first candidate ought to be tested - assert!( - total_candidate_count > candidates.len(), - "{total_candidate_count}, {candidates:#?}" - ); - debug!("tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("untested_candidates: {}", candidates.len()); - - (candidates, target_candidates) - } - /// This is the most subtle part of the match lowering algorithm. At this point, there are /// no fully-satisfied candidates, and no or-patterns to expand, so we actually need to /// perform some sort of test to make progress. @@ -2363,8 +2284,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // For each of the N possible test outcomes, build the vector of candidates that applies if // the test has that particular outcome. This also mutates the candidates to remove match // pairs that are fully satisfied by the relevant outcome. - let (remaining_candidates, target_candidates) = - self.sort_candidates(match_place, &test, candidates); + let PartitionedCandidates { target_candidates, remaining_candidates } = + self.partition_candidates_into_buckets(match_place, &test, candidates); // The block that we should branch to if none of the `target_candidates` match. let remainder_start = self.cfg.start_new_block(); diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 1b6d96e49f0c..55e21ff18aae 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -5,7 +5,6 @@ // identify what tests are needed, perform the tests, and then filter // the candidates based on the result. -use std::cmp::Ordering; use std::sync::Arc; use rustc_data_structures::fx::FxIndexMap; @@ -20,7 +19,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::builder::Builder; -use crate::builder::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind}; +use crate::builder::matches::{MatchPairTree, Test, TestBranch, TestKind, TestableCase}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. @@ -30,30 +29,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, match_pair: &MatchPairTree<'tcx>, ) -> Test<'tcx> { - let kind = match match_pair.test_case { - TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, + let kind = match match_pair.testable_case { + TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, - TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If, - TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt, - TestCase::Constant { value } => TestKind::Eq { value, cast_ty: match_pair.pattern_ty }, + TestableCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If, + TestableCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => { + TestKind::SwitchInt + } + TestableCase::Constant { value } => { + TestKind::Eq { value, cast_ty: match_pair.pattern_ty } + } - TestCase::Range(ref range) => { + TestableCase::Range(ref range) => { assert_eq!(range.ty, match_pair.pattern_ty); TestKind::Range(Arc::clone(range)) } - TestCase::Slice { len, variable_length } => { + TestableCase::Slice { len, variable_length } => { let op = if variable_length { BinOp::Ge } else { BinOp::Eq }; TestKind::Len { len: len as u64, op } } - TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability }, + TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability }, - TestCase::Never => TestKind::Never, + TestableCase::Never => TestKind::Never, // Or-patterns are not tested directly; instead they are expanded into subcandidates, // which are then distinguished by testing whatever non-or patterns they contain. - TestCase::Or { .. } => bug!("or-patterns should have already been handled"), + TestableCase::Or { .. } => bug!("or-patterns should have already been handled"), }; Test { span: match_pair.pattern_span, kind } @@ -486,266 +489,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TerminatorKind::if_(Operand::Move(eq_result), success_block, fail_block), ); } - - /// Given that we are performing `test` against `test_place`, this job - /// sorts out what the status of `candidate` will be after the test. See - /// `test_candidates` for the usage of this function. The candidate may - /// be modified to update its `match_pairs`. - /// - /// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is - /// a variant test, then we would modify the candidate to be `(x as - /// Option).0 @ P0` and return the index corresponding to the variant - /// `Some`. - /// - /// However, in some cases, the test may just not be relevant to candidate. - /// For example, suppose we are testing whether `foo.x == 22`, but in one - /// match arm we have `Foo { x: _, ... }`... in that case, the test for - /// the value of `x` has no particular relevance to this candidate. In - /// such cases, this function just returns None without doing anything. - /// This is used by the overall `match_candidates` algorithm to structure - /// the match as a whole. See `match_candidates` for more details. - /// - /// FIXME(#29623). In some cases, we have some tricky choices to make. for - /// example, if we are testing that `x == 22`, but the candidate is `x @ - /// 13..55`, what should we do? In the event that the test is true, we know - /// that the candidate applies, but in the event of false, we don't know - /// that it *doesn't* apply. For now, we return false, indicate that the - /// test does not apply to this candidate, but it might be we can get - /// tighter match code if we do something a bit different. - pub(super) fn sort_candidate( - &mut self, - test_place: Place<'tcx>, - test: &Test<'tcx>, - candidate: &mut Candidate<'tcx>, - sorted_candidates: &FxIndexMap, Vec<&mut Candidate<'tcx>>>, - ) -> Option> { - // Find the match_pair for this place (if any). At present, - // afaik, there can be at most one. (In the future, if we - // adopted a more general `@` operator, there might be more - // than one, but it'd be very unusual to have two sides that - // both require tests; you'd expect one side to be simplified - // away.) - let (match_pair_index, match_pair) = candidate - .match_pairs - .iter() - .enumerate() - .find(|&(_, mp)| mp.place == Some(test_place))?; - - // If true, the match pair is completely entailed by its corresponding test - // branch, so it can be removed. If false, the match pair is _compatible_ - // with its test branch, but still needs a more specific test. - let fully_matched; - let ret = match (&test.kind, &match_pair.test_case) { - // If we are performing a variant switch, then this - // informs variant patterns, but nothing else. - ( - &TestKind::Switch { adt_def: tested_adt_def }, - &TestCase::Variant { adt_def, variant_index }, - ) => { - assert_eq!(adt_def, tested_adt_def); - fully_matched = true; - Some(TestBranch::Variant(variant_index)) - } - - // If we are performing a switch over integers, then this informs integer - // equality, but nothing else. - // - // FIXME(#29623) we could use PatKind::Range to rule - // things out here, in some cases. - (TestKind::SwitchInt, &TestCase::Constant { value }) - if is_switch_ty(match_pair.pattern_ty) => - { - // An important invariant of candidate sorting is that a candidate - // must not match in multiple branches. For `SwitchInt` tests, adding - // a new value might invalidate that property for range patterns that - // have already been sorted into the failure arm, so we must take care - // not to add such values here. - let is_covering_range = |test_case: &TestCase<'tcx>| { - test_case.as_range().is_some_and(|range| { - matches!(range.contains(value, self.tcx), None | Some(true)) - }) - }; - let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| { - candidate - .match_pairs - .iter() - .any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case)) - }; - if sorted_candidates - .get(&TestBranch::Failure) - .is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate)) - { - fully_matched = false; - None - } else { - fully_matched = true; - Some(TestBranch::Constant(value)) - } - } - (TestKind::SwitchInt, TestCase::Range(range)) => { - // When performing a `SwitchInt` test, a range pattern can be - // sorted into the failure arm if it doesn't contain _any_ of - // the values being tested. (This restricts what values can be - // added to the test by subsequent candidates.) - fully_matched = false; - let not_contained = sorted_candidates - .keys() - .filter_map(|br| br.as_constant()) - .all(|val| matches!(range.contains(val, self.tcx), Some(false))); - - not_contained.then(|| { - // No switch values are contained in the pattern range, - // so the pattern can be matched only if this test fails. - TestBranch::Failure - }) - } - - (TestKind::If, TestCase::Constant { value }) => { - fully_matched = true; - let value = value.try_to_bool().unwrap_or_else(|| { - span_bug!(test.span, "expected boolean value but got {value:?}") - }); - Some(if value { TestBranch::Success } else { TestBranch::Failure }) - } - - ( - &TestKind::Len { len: test_len, op: BinOp::Eq }, - &TestCase::Slice { len, variable_length }, - ) => { - match (test_len.cmp(&(len as u64)), variable_length) { - (Ordering::Equal, false) => { - // on true, min_len = len = $actual_length, - // on false, len != $actual_length - fully_matched = true; - Some(TestBranch::Success) - } - (Ordering::Less, _) => { - // test_len < pat_len. If $actual_len = test_len, - // then $actual_len < pat_len and we don't have - // enough elements. - fully_matched = false; - Some(TestBranch::Failure) - } - (Ordering::Equal | Ordering::Greater, true) => { - // This can match both if $actual_len = test_len >= pat_len, - // and if $actual_len > test_len. We can't advance. - fully_matched = false; - None - } - (Ordering::Greater, false) => { - // test_len != pat_len, so if $actual_len = test_len, then - // $actual_len != pat_len. - fully_matched = false; - Some(TestBranch::Failure) - } - } - } - ( - &TestKind::Len { len: test_len, op: BinOp::Ge }, - &TestCase::Slice { len, variable_length }, - ) => { - // the test is `$actual_len >= test_len` - match (test_len.cmp(&(len as u64)), variable_length) { - (Ordering::Equal, true) => { - // $actual_len >= test_len = pat_len, - // so we can match. - fully_matched = true; - Some(TestBranch::Success) - } - (Ordering::Less, _) | (Ordering::Equal, false) => { - // test_len <= pat_len. If $actual_len < test_len, - // then it is also < pat_len, so the test passing is - // necessary (but insufficient). - fully_matched = false; - Some(TestBranch::Success) - } - (Ordering::Greater, false) => { - // test_len > pat_len. If $actual_len >= test_len > pat_len, - // then we know we won't have a match. - fully_matched = false; - Some(TestBranch::Failure) - } - (Ordering::Greater, true) => { - // test_len < pat_len, and is therefore less - // strict. This can still go both ways. - fully_matched = false; - None - } - } - } - - (TestKind::Range(test), TestCase::Range(pat)) => { - if test == pat { - fully_matched = true; - Some(TestBranch::Success) - } else { - fully_matched = false; - // If the testing range does not overlap with pattern range, - // the pattern can be matched only if this test fails. - if !test.overlaps(pat, self.tcx)? { Some(TestBranch::Failure) } else { None } - } - } - (TestKind::Range(range), &TestCase::Constant { value }) => { - fully_matched = false; - if !range.contains(value, self.tcx)? { - // `value` is not contained in the testing range, - // so `value` can be matched only if this test fails. - Some(TestBranch::Failure) - } else { - None - } - } - - (TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => { - if test_val == case_val { - fully_matched = true; - Some(TestBranch::Success) - } else { - fully_matched = false; - Some(TestBranch::Failure) - } - } - - (TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. }) - if test_temp == temp => - { - fully_matched = true; - Some(TestBranch::Success) - } - - (TestKind::Never, _) => { - fully_matched = true; - Some(TestBranch::Success) - } - - ( - TestKind::Switch { .. } - | TestKind::SwitchInt { .. } - | TestKind::If - | TestKind::Len { .. } - | TestKind::Range { .. } - | TestKind::Eq { .. } - | TestKind::Deref { .. }, - _, - ) => { - fully_matched = false; - None - } - }; - - if fully_matched { - // Replace the match pair by its sub-pairs. - let match_pair = candidate.match_pairs.remove(match_pair_index); - candidate.match_pairs.extend(match_pair.subpairs); - // Move or-patterns to the end. - candidate.sort_match_pairs(); - } - - ret - } } -fn is_switch_ty(ty: Ty<'_>) -> bool { +/// Returns true if this type be used with [`TestKind::SwitchInt`]. +pub(crate) fn is_switch_ty(ty: Ty<'_>) -> bool { ty.is_integral() || ty.is_char() } diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 2c8ad95b6afd..f1df90f93fd6 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -6,7 +6,7 @@ use tracing::debug; use crate::builder::Builder; use crate::builder::expr::as_place::PlaceBase; -use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestCase}; +use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestableCase}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Creates a false edge to `imaginary_target` and a real edge to @@ -159,11 +159,11 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { } fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) { - if let TestCase::Or { pats, .. } = &match_pair.test_case { + if let TestableCase::Or { pats, .. } = &match_pair.testable_case { for flat_pat in pats.iter() { self.visit_flat_pat(flat_pat) } - } else if matches!(match_pair.test_case, TestCase::Deref { .. }) { + } else if matches!(match_pair.testable_case, TestableCase::Deref { .. }) { // The subpairs of a deref pattern are all places relative to the deref temporary, so we // don't fake borrow them. Problem is, if we only shallowly fake-borrowed // `match_pair.place`, this would allow: diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 14a24265a8f4..40f1fe0d5d11 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -838,26 +838,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.parent_module, self.infcx.typing_env(self.param_env), ); - - // check if the function's return type is inhabited - // this was added here because of this regression - // https://github.com/rust-lang/rust/issues/149571 - let output_is_inhabited = - if matches!(self.tcx.def_kind(self.def_id), DefKind::Fn | DefKind::AssocFn) { - self.tcx - .fn_sig(self.def_id) - .instantiate_identity() - .skip_binder() - .output() - .is_inhabited_from( - self.tcx, - self.parent_module, - self.infcx.typing_env(self.param_env), - ) - } else { - true - }; - if !ty_is_inhabited { // Unreachable code warnings are already emitted during type checking. // However, during type checking, full type information is being @@ -868,7 +848,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // uninhabited types (e.g. empty enums). The check above is used so // that we do not emit the same warning twice if the uninhabited type // is indeed `!`. - if !ty.is_never() && output_is_inhabited { + if !ty.is_never() + && matches!(self.tcx.def_kind(self.def_id), DefKind::Fn | DefKind::AssocFn) + // check if the function's return type is inhabited + // this was added here because of this regression + // https://github.com/rust-lang/rust/issues/149571 + && self + .tcx + .fn_sig(self.def_id) + .instantiate_identity() + .skip_binder() + .output() + .is_inhabited_from( + self.tcx, + self.parent_module, + self.infcx.typing_env(self.param_env), + ) + { lints.push((target_bb, ty, term.source_info.span)); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 6316ccf1b8c5..c3c4c455b965 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -28,7 +28,7 @@ use crate::errors::{ PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern, }; -impl<'a, 'tcx> PatCtxt<'a, 'tcx> { +impl<'tcx> PatCtxt<'tcx> { /// Converts a constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). @@ -64,7 +64,7 @@ struct ConstToPat<'tcx> { } impl<'tcx> ConstToPat<'tcx> { - fn new(pat_ctxt: &PatCtxt<'_, 'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { + fn new(pat_ctxt: &PatCtxt<'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 02e6f8d6ce71..3eac6ae344bb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -11,12 +11,12 @@ use rustc_abi::{FieldIdx, Integer}; use rustc_errors::codes::*; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{self as hir, ByRef, LangItem, Mutability, Pinnedness, RangeEnd}; +use rustc_hir::{self as hir, LangItem, RangeEnd}; use rustc_index::Idx; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::thir::{ - Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, + Ascription, DerefPatBorrowMode, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment}; use rustc_middle::ty::layout::IntegerExt; @@ -30,19 +30,20 @@ pub(crate) use self::check_match::check_match; use self::migration::PatMigration; use crate::errors::*; -struct PatCtxt<'a, 'tcx> { +/// Context for lowering HIR patterns to THIR patterns. +struct PatCtxt<'tcx> { tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, /// Used by the Rust 2024 migration lint. - rust_2024_migration: Option>, + rust_2024_migration: Option>, } -pub(super) fn pat_from_hir<'a, 'tcx>( +pub(super) fn pat_from_hir<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, + typeck_results: &'tcx ty::TypeckResults<'tcx>, pat: &'tcx hir::Pat<'tcx>, ) -> Box> { let mut pcx = PatCtxt { @@ -62,7 +63,7 @@ pub(super) fn pat_from_hir<'a, 'tcx>( result } -impl<'a, 'tcx> PatCtxt<'a, 'tcx> { +impl<'tcx> PatCtxt<'tcx> { fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { let adjustments: &[PatAdjustment<'tcx>] = self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); @@ -114,16 +115,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat); PatKind::DerefPattern { subpattern: thir_pat, borrow } } - PatAdjust::PinDeref => { - let mutable = self.typeck_results.pat_has_ref_mut_binding(pat); - PatKind::DerefPattern { - subpattern: thir_pat, - borrow: ByRef::Yes( - Pinnedness::Pinned, - if mutable { Mutability::Mut } else { Mutability::Not }, - ), - } - } + PatAdjust::PinDeref => PatKind::Deref { subpattern: thir_pat }, }; Box::new(Pat { span, ty: adjust.source, kind }) }); @@ -334,7 +326,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Box(subpattern) => PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), - borrow: hir::ByRef::No, + borrow: DerefPatBorrowMode::Box, }, hir::PatKind::Slice(prefix, slice, suffix) => { diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 072733c97efb..7fad380ba5a1 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1891,6 +1891,16 @@ fn check_must_not_suspend_ty<'tcx>( SuspendCheckData { descr_pre: &format!("{}allocator ", data.descr_pre), ..data }, ) } + // FIXME(sized_hierarchy): This should be replaced with a requirement that types in + // coroutines implement `const Sized`. Scalable vectors are temporarily `Sized` while + // `feature(sized_hierarchy)` is not fully implemented, but in practice are + // non-`const Sized` and so do not have a known size at compilation time. Layout computation + // for a coroutine containing scalable vectors would be incorrect. + ty::Adt(def, _) if def.repr().scalable() => { + tcx.dcx() + .span_err(data.source_span, "scalable vectors cannot be held over await points"); + true + } ty::Adt(def, _) => check_must_not_suspend_def(tcx, def.did(), hir_id, data), // FIXME: support adding the attribute to TAITs ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 69248cf91f24..71bdafa958ca 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,6 +1,7 @@ use rustc_hir::attrs::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::query::Providers; @@ -110,6 +111,15 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { && checker.statements <= threshold } +// The threshold that CostChecker computes is balancing the desire to make more things +// inlinable cross crates against the growth in incremental CGU size that happens when too many +// things in the sysroot are made inlinable. +// Permitting calls causes the size of some incremental CGUs to grow, because more functions are +// made inlinable out of the sysroot or dependencies. +// Assert terminators are similar to calls, but do not have the same impact on compile time, so +// those are just treated as statements. +// A threshold exists at all because we don't want to blindly mark a huge function as inlinable. + struct CostChecker<'b, 'tcx> { tcx: TyCtxt<'tcx>, callee_body: &'b Body<'tcx>, @@ -129,9 +139,10 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) { + self.statements += 1; let tcx = self.tcx; - match terminator.kind { - TerminatorKind::Drop { ref place, unwind, .. } => { + match &terminator.kind { + TerminatorKind::Drop { place, unwind, .. } => { let ty = place.ty(self.callee_body, tcx).ty; if !ty.is_trivially_pure_clone_copy() { self.calls += 1; @@ -140,7 +151,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } } } - TerminatorKind::Call { ref func, unwind, .. } => { + TerminatorKind::Call { func, unwind, .. } => { // We track calls because they make our function not a leaf (and in theory, the // number of calls indicates how likely this function is to perturb other CGUs). // But intrinsics don't have a body that gets assigned to a CGU, so they are @@ -155,21 +166,31 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.landing_pads += 1; } } - TerminatorKind::Assert { unwind, .. } => { + TerminatorKind::TailCall { .. } => { self.calls += 1; + } + TerminatorKind::Assert { unwind, .. } => { if let UnwindAction::Cleanup(_) = unwind { self.landing_pads += 1; } } TerminatorKind::UnwindResume => self.resumes += 1, TerminatorKind::InlineAsm { unwind, .. } => { - self.statements += 1; if let UnwindAction::Cleanup(_) = unwind { self.landing_pads += 1; } } - TerminatorKind::Return => {} - _ => self.statements += 1, + TerminatorKind::Return + | TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Unreachable + | TerminatorKind::UnwindTerminate(_) => {} + kind @ (TerminatorKind::FalseUnwind { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::CoroutineDrop) => { + bug!("{kind:?} should not be in runtime MIR"); + } } } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 92c74e7fc276..526d0e96a36d 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1661,7 +1661,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { BackendRepr::ScalarPair(a, b) => { !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx) } - BackendRepr::SimdVector { .. } | BackendRepr::Memory { .. } => false, + BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } + | BackendRepr::Memory { .. } => false, } } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index bfb2edf286a2..edb91d8f2eda 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -2,7 +2,10 @@ monomorphize_abi_error_disabled_vector_type = this function {$is_call -> [true] call *[false] definition - } uses SIMD vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> + } uses {$is_scalable -> + [true] scalable + *[false] SIMD + } vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> [true] {" "}in the caller *[false] {""} } diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index a5040ef22dd0..4949d9ae3922 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -78,6 +78,8 @@ pub(crate) struct AbiErrorDisabledVectorType<'a> { pub ty: Ty<'a>, /// Whether this is a problem at a call site or at a declaration. pub is_call: bool, + /// Whether this is a problem with a fixed length vector or a scalable vector + pub is_scalable: bool, } #[derive(Diagnostic)] diff --git a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs index 2ee77e9deb0d..a5f2ba2f1ced 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/abi_check.rs @@ -10,14 +10,37 @@ use rustc_target::callconv::{FnAbi, PassMode}; use crate::errors; -fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool { +/// Are vector registers used? +enum UsesVectorRegisters { + /// e.g. `neon` + FixedVector, + /// e.g. `sve` + ScalableVector, + No, +} + +/// Determines whether the combination of `mode` and `repr` will use fixed vector registers, +/// scalable vector registers or no vector registers. +fn passes_vectors_by_value(mode: &PassMode, repr: &BackendRepr) -> UsesVectorRegisters { match mode { - PassMode::Ignore | PassMode::Indirect { .. } => false, - PassMode::Cast { pad_i32: _, cast } => { - cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector)) - || cast.rest.unit.kind == RegKind::Vector + PassMode::Ignore | PassMode::Indirect { .. } => UsesVectorRegisters::No, + PassMode::Cast { pad_i32: _, cast } + if cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector)) + || cast.rest.unit.kind == RegKind::Vector => + { + UsesVectorRegisters::FixedVector } - PassMode::Direct(..) | PassMode::Pair(..) => matches!(repr, BackendRepr::SimdVector { .. }), + PassMode::Direct(..) | PassMode::Pair(..) + if matches!(repr, BackendRepr::SimdVector { .. }) => + { + UsesVectorRegisters::FixedVector + } + PassMode::Direct(..) | PassMode::Pair(..) + if matches!(repr, BackendRepr::ScalableVector { .. }) => + { + UsesVectorRegisters::ScalableVector + } + _ => UsesVectorRegisters::No, } } @@ -32,37 +55,60 @@ fn do_check_simd_vector_abi<'tcx>( is_call: bool, loc: impl Fn() -> (Span, HirId), ) { - let feature_def = tcx.sess.target.features_for_correct_vector_abi(); let codegen_attrs = tcx.codegen_fn_attrs(def_id); let have_feature = |feat: Symbol| { - tcx.sess.unstable_target_features.contains(&feat) - || codegen_attrs.target_features.iter().any(|x| x.name == feat) + let target_feats = tcx.sess.unstable_target_features.contains(&feat); + let fn_feats = codegen_attrs.target_features.iter().any(|x| x.name == feat); + target_feats || fn_feats }; for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) { let size = arg_abi.layout.size; - if uses_vector_registers(&arg_abi.mode, &arg_abi.layout.backend_repr) { - // Find the first feature that provides at least this vector size. - let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { - Some((_, feature)) => feature, - None => { + match passes_vectors_by_value(&arg_abi.mode, &arg_abi.layout.backend_repr) { + UsesVectorRegisters::FixedVector => { + let feature_def = tcx.sess.target.features_for_correct_fixed_length_vector_abi(); + // Find the first feature that provides at least this vector size. + let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) { + Some((_, feature)) => feature, + None => { + let (span, _hir_id) = loc(); + tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType { + span, + ty: arg_abi.layout.ty, + is_call, + }); + continue; + } + }; + if !feature.is_empty() && !have_feature(Symbol::intern(feature)) { let (span, _hir_id) = loc(); - tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType { + tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { span, + required_feature: feature, ty: arg_abi.layout.ty, is_call, + is_scalable: false, }); - continue; } - }; - if !have_feature(Symbol::intern(feature)) { - // Emit error. - let (span, _hir_id) = loc(); - tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { - span, - required_feature: feature, - ty: arg_abi.layout.ty, - is_call, - }); + } + UsesVectorRegisters::ScalableVector => { + let Some(required_feature) = + tcx.sess.target.features_for_correct_scalable_vector_abi() + else { + continue; + }; + if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) { + let (span, _) = loc(); + tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { + span, + required_feature, + ty: arg_abi.layout.ty, + is_call, + is_scalable: true, + }); + } + } + UsesVectorRegisters::No => { + continue; } } } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 21191085253b..cf069b364be8 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -240,11 +240,6 @@ passes_has_incoherent_inherent_impl = `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits .label = only adts, extern types and traits are supported -passes_ignored_attr_with_macro = - `#[{$sym}]` is ignored on struct fields, match arms and macro defs - .warn = {-passes_previously_accepted} - .note = {-passes_see_issue(issue: "80564")} - passes_ignored_derived_impls = `{$name}` has {$trait_list_len -> [one] a derived impl @@ -379,10 +374,6 @@ passes_must_not_suspend = `must_not_suspend` attribute should be applied to a struct, enum, union, or trait .label = is not a struct, enum, union, or trait -passes_no_link = - attribute should be applied to an `extern crate` item - .label = not an `extern crate` item - passes_no_main_function = `main` function not found in crate `{$crate_name}` .here_is_main = here is a function named `main` @@ -478,20 +469,10 @@ passes_rustc_legacy_const_generics_index_exceed = *[other] arguments } -passes_rustc_legacy_const_generics_index_negative = - arguments should be non-negative integers - passes_rustc_legacy_const_generics_only = #[rustc_legacy_const_generics] functions must only have const generics .label = non-const generic parameter -passes_rustc_lint_opt_deny_field_access = - `#[rustc_lint_opt_deny_field_access]` should be applied to a field - .label = not a field - -passes_rustc_lint_opt_ty = - `#[rustc_lint_opt_ty]` should be applied to a struct - .label = not a struct passes_rustc_pub_transparent = attribute should be applied to `#[repr(transparent)]` types @@ -503,17 +484,6 @@ passes_sanitize_attribute_not_allowed = .no_body = function has no body .help = sanitize attribute can be applied to a function (with body), impl block, or module -passes_should_be_applied_to_fn = - attribute should be applied to a function definition - .label = {$on_crate -> - [true] cannot be applied to crates - *[false] not a function definition - } - -passes_should_be_applied_to_static = - attribute should be applied to a static - .label = not a static - passes_should_be_applied_to_trait = attribute should be applied to a trait .label = not a trait diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 34f73212e90b..7822614a05cb 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -10,9 +10,10 @@ use std::collections::hash_map::Entry; use std::slice; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_ast::{AttrStyle, LitKind, MetaItemKind, ast}; +use rustc_ast::{AttrStyle, MetaItemKind, ast}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, @@ -211,6 +212,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => { self.check_macro_export(hir_id, *span, target) }, + Attribute::Parsed(AttributeKind::RustcLegacyConstGenerics{attr_span, fn_indexes}) => { + self.check_rustc_legacy_const_generics(item, *attr_span, fn_indexes) + }, Attribute::Parsed(AttributeKind::Doc(attr)) => self.check_doc_attrs(attr, hir_id, target), Attribute::Parsed(AttributeKind::EiiImpls(impls)) => { self.check_eii_impl(impls, target) @@ -250,8 +254,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::LinkSection { .. } | AttributeKind::MacroUse { .. } | AttributeKind::MacroEscape( .. ) + | AttributeKind::NoLink + | AttributeKind::RustcNoImplicitAutorefs | AttributeKind::RustcLayoutScalarValidRangeStart(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) + | AttributeKind::RustcLintDiagnostics + | AttributeKind::RustcLintOptDenyFieldAccess { .. } + | AttributeKind::RustcLintOptTy + | AttributeKind::RustcLintQueryInstability + | AttributeKind::RustcLintUntrackedQueryInformation + | AttributeKind::RustcNeverReturnsNullPointer + | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) | AttributeKind::ExportStable @@ -284,6 +297,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) | AttributeKind::PinV2(..) | AttributeKind::WindowsSubsystem(..) + | AttributeKind::ThreadLocal ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -297,30 +311,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::diagnostic, sym::on_const, ..] => { self.check_diagnostic_on_const(attr.span(), hir_id, target, item) } - [sym::thread_local, ..] => self.check_thread_local(attr, span, target), - [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::rustc_no_implicit_autorefs, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) - } - [sym::rustc_never_returns_null_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) - } - [sym::rustc_legacy_const_generics, ..] => { - self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) - } - [sym::rustc_lint_query_instability, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) - } - [sym::rustc_lint_untracked_query_information, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) - } - [sym::rustc_lint_diagnostics, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) - } - [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), - [sym::rustc_lint_opt_deny_field_access, ..] => { - self.check_rustc_lint_opt_deny_field_access(attr, span, target) - } [sym::rustc_clean, ..] | [sym::rustc_dirty, ..] | [sym::rustc_if_this_changed, ..] @@ -455,15 +445,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_mix_no_mangle_export(hir_id, attrs); } - fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr_span: Span, sym: &str) { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr_span, - errors::IgnoredAttrWithMacro { sym }, - ); - } - fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { for EiiImpl { span, inner_span, eii_macro, impl_marked_unsafe, is_default: _ } in impls { match target { @@ -787,19 +768,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - /// Checks if the `#[thread_local]` attribute on `item` is valid. - fn check_thread_local(&self, attr: &Attribute, span: Span, target: Target) { - match target { - Target::ForeignStatic | Target::Static => {} - _ => { - self.dcx().emit_err(errors::AttrShouldBeAppliedToStatic { - attr_span: attr.span(), - defn_span: span, - }); - } - } - } - fn check_doc_alias_value(&self, span: Span, hir_id: HirId, target: Target, alias: Symbol) { if let Some(location) = match target { Target::AssocTy => { @@ -1197,53 +1165,20 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); } - /// Checks if `#[no_link]` is applied to an `extern crate`. - fn check_no_link(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { - match target { - Target::ExternCrate => {} - // FIXME(#80564): We permit struct fields, match arms and macro defs to have an - // `#[no_link]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to be compatible - // with crates depending on them, we can't throw an error here. - Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "no_link"); - } - _ => { - self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span }); - } - } - } - /// Checks if `#[rustc_legacy_const_generics]` is applied to a function and has a valid argument. fn check_rustc_legacy_const_generics( &self, - hir_id: HirId, - attr: &Attribute, - span: Span, - target: Target, item: Option>, + attr_span: Span, + index_list: &ThinVec<(usize, Span)>, ) { - let is_function = matches!(target, Target::Fn); - if !is_function { - self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span(), - defn_span: span, - on_crate: hir_id == CRATE_HIR_ID, - }); - return; - } - - let Some(list) = attr.meta_item_list() else { - // The attribute form is validated on AST. - return; - }; - let Some(ItemLike::Item(Item { kind: ItemKind::Fn { sig: FnSig { decl, .. }, generics, .. }, .. })) = item else { - bug!("should be a function item"); + // No error here, since it's already given by the parser + return; }; for param in generics.params { @@ -1251,7 +1186,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir::GenericParamKind::Const { .. } => {} _ => { self.dcx().emit_err(errors::RustcLegacyConstGenericsOnly { - attr_span: attr.span(), + attr_span, param_span: param.span, }); return; @@ -1259,73 +1194,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - if list.len() != generics.params.len() { + if index_list.len() != generics.params.len() { self.dcx().emit_err(errors::RustcLegacyConstGenericsIndex { - attr_span: attr.span(), + attr_span, generics_span: generics.span, }); return; } - let arg_count = decl.inputs.len() as u128 + generics.params.len() as u128; - let mut invalid_args = vec![]; - for meta in list { - if let Some(LitKind::Int(val, _)) = meta.lit().map(|lit| &lit.kind) { - if *val >= arg_count { - let span = meta.span(); - self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexExceed { - span, - arg_count: arg_count as usize, - }); - return; - } - } else { - invalid_args.push(meta.span()); - } - } - - if !invalid_args.is_empty() { - self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexNegative { invalid_args }); - } - } - - /// Helper function for checking that the provided attribute is only applied to a function or - /// method. - fn check_applied_to_fn_or_method( - &self, - hir_id: HirId, - attr_span: Span, - defn_span: Span, - target: Target, - ) { - let is_function = matches!(target, Target::Fn | Target::Method(..)); - if !is_function { - self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span, - defn_span, - on_crate: hir_id == CRATE_HIR_ID, - }); - } - } - - /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct. - fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) { - match target { - Target::Struct => {} - _ => { - self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span(), span }); - } - } - } - - /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field. - fn check_rustc_lint_opt_deny_field_access(&self, attr: &Attribute, span: Span, target: Target) { - match target { - Target::Field => {} - _ => { - self.tcx - .dcx() - .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span(), span }); + let arg_count = decl.inputs.len() + generics.params.len(); + for (index, span) in index_list { + if *index >= arg_count { + self.dcx().emit_err(errors::RustcLegacyConstGenericsIndexExceed { + span: *span, + arg_count, + }); } } } diff --git a/compiler/rustc_passes/src/eii.rs b/compiler/rustc_passes/src/eii.rs index 691576e6a05f..ab3f9f0d2182 100644 --- a/compiler/rustc_passes/src/eii.rs +++ b/compiler/rustc_passes/src/eii.rs @@ -116,7 +116,8 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): } if default_impls.len() > 1 { - panic!("multiple not supported right now"); + let decl_span = tcx.def_ident_span(decl_did).unwrap(); + tcx.dcx().span_delayed_bug(decl_span, "multiple not supported right now"); } let (local_impl, is_default) = diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 7abd2c703aeb..2b817c6dd106 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -80,22 +80,6 @@ pub(crate) struct OuterCrateLevelAttrSuggestion { #[diag(passes_inner_crate_level_attr)] pub(crate) struct InnerCrateLevelAttr; -#[derive(LintDiagnostic)] -#[diag(passes_ignored_attr_with_macro)] -pub(crate) struct IgnoredAttrWithMacro<'a> { - pub sym: &'a str, -} - -#[derive(Diagnostic)] -#[diag(passes_should_be_applied_to_fn)] -pub(crate) struct AttrShouldBeAppliedToFn { - #[primary_span] - pub attr_span: Span, - #[label] - pub defn_span: Span, - pub on_crate: bool, -} - #[derive(Diagnostic)] #[diag(passes_non_exhaustive_with_default_field_values)] pub(crate) struct NonExhaustiveWithDefaultFieldValues { @@ -114,15 +98,6 @@ pub(crate) struct AttrShouldBeAppliedToTrait { pub defn_span: Span, } -#[derive(Diagnostic)] -#[diag(passes_should_be_applied_to_static)] -pub(crate) struct AttrShouldBeAppliedToStatic { - #[primary_span] - pub attr_span: Span, - #[label] - pub defn_span: Span, -} - #[derive(Diagnostic)] #[diag(passes_doc_alias_bad_location)] pub(crate) struct DocAliasBadLocation<'a> { @@ -254,15 +229,6 @@ pub(crate) struct Link { pub span: Option, } -#[derive(Diagnostic)] -#[diag(passes_no_link)] -pub(crate) struct NoLink { - #[primary_span] - pub attr_span: Span, - #[label] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_rustc_legacy_const_generics_only)] pub(crate) struct RustcLegacyConstGenericsOnly { @@ -290,13 +256,6 @@ pub(crate) struct RustcLegacyConstGenericsIndexExceed { pub arg_count: usize, } -#[derive(Diagnostic)] -#[diag(passes_rustc_legacy_const_generics_index_negative)] -pub(crate) struct RustcLegacyConstGenericsIndexNegative { - #[primary_span] - pub invalid_args: Vec, -} - #[derive(Diagnostic)] #[diag(passes_rustc_dirty_clean)] pub(crate) struct RustcDirtyClean { @@ -434,24 +393,6 @@ pub(crate) struct UnusedMultiple { pub name: Symbol, } -#[derive(Diagnostic)] -#[diag(passes_rustc_lint_opt_ty)] -pub(crate) struct RustcLintOptTy { - #[primary_span] - pub attr_span: Span, - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(passes_rustc_lint_opt_deny_field_access)] -pub(crate) struct RustcLintOptDenyFieldAccess { - #[primary_span] - pub attr_span: Span, - #[label] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_collapse_debuginfo)] pub(crate) struct CollapseDebuginfo { diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 820c41acf5b2..aced39059f6b 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -225,6 +225,10 @@ pub enum ValueAbi { element: Scalar, count: u64, }, + ScalableVector { + element: Scalar, + count: u64, + }, Aggregate { /// If true, the size is exact, otherwise it's only a lower bound. sized: bool, @@ -235,7 +239,15 @@ impl ValueAbi { /// Returns `true` if the layout corresponds to an unsized type. pub fn is_unsized(&self) -> bool { match *self { - ValueAbi::Scalar(_) | ValueAbi::ScalarPair(..) | ValueAbi::Vector { .. } => false, + ValueAbi::Scalar(_) + | ValueAbi::ScalarPair(..) + | ValueAbi::Vector { .. } + // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the + // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is + // fully implemented, scalable vectors will remain `Sized`, they just won't be + // `const Sized` - whether `is_unsized` continues to return `false` at that point will + // need to be revisited and will depend on what `is_unsized` is used for. + | ValueAbi::ScalableVector { .. } => false, ValueAbi::Aggregate { sized } => !sized, } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 782e75a930e0..03328d084ee9 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -256,6 +256,9 @@ impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { rustc_abi::BackendRepr::SimdVector { element, count } => { ValueAbi::Vector { element: element.stable(tables, cx), count } } + rustc_abi::BackendRepr::ScalableVector { element, count } => { + ValueAbi::ScalableVector { element: element.stable(tables, cx), count } + } rustc_abi::BackendRepr::Memory { sized } => ValueAbi::Aggregate { sized }, } } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index ea64a1e6c64d..71bbd64ddc6c 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -357,6 +357,16 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } fn visit_anon_const(&mut self, constant: &'a AnonConst) { + // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so + // to avoid affecting stable we have to feature gate the not creating + // anon consts + if let MgcaDisambiguation::Direct = constant.mgca_disambiguation + && self.resolver.tcx.features().min_generic_const_args() + { + visit::walk_anon_const(self, constant); + return; + } + let parent = self.create_def(constant.id, None, DefKind::AnonConst, constant.value.span); self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 16f2212bd734..f4a594a4731d 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -743,10 +743,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let ambiguity_error_kind = if is_builtin(innermost_res) || is_builtin(res) { Some(AmbiguityKind::BuiltinAttr) - } else if innermost_res == derive_helper_compat - || res == derive_helper_compat && innermost_res != derive_helper - { + } else if innermost_res == derive_helper_compat { Some(AmbiguityKind::DeriveHelper) + } else if res == derive_helper_compat && innermost_res != derive_helper { + span_bug!(orig_ident.span, "impossible inner resolution kind") } else if innermost_flags.contains(Flags::MACRO_RULES) && flags.contains(Flags::MODULE) && !self.disambiguate_macro_rules_vs_modularized(innermost_binding, binding) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 5a4fbad6aaf8..d3428a4af348 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2928,7 +2928,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { item.id, LifetimeBinderKind::Function, span, - |this| this.resolve_delegation(delegation), + |this| this.resolve_delegation(delegation, item.id, false), ); } @@ -3257,7 +3257,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { item.id, LifetimeBinderKind::Function, delegation.path.segments.last().unwrap().ident.span, - |this| this.resolve_delegation(delegation), + |this| this.resolve_delegation(delegation, item.id, false), ); } AssocItemKind::Type(box TyAlias { generics, .. }) => self @@ -3550,7 +3550,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { |i, s, c| MethodNotMemberOfTrait(i, s, c), ); - this.resolve_delegation(delegation) + this.resolve_delegation(delegation, item.id, trait_id.is_some()); }, ); } @@ -3699,17 +3699,30 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }) } - fn resolve_delegation(&mut self, delegation: &'ast Delegation) { + fn resolve_delegation( + &mut self, + delegation: &'ast Delegation, + item_id: NodeId, + is_in_trait_impl: bool, + ) { self.smart_resolve_path( delegation.id, &delegation.qself, &delegation.path, PathSource::Delegation, ); + if let Some(qself) = &delegation.qself { self.visit_ty(&qself.ty); } + self.visit_path(&delegation.path); + + self.r.delegation_sig_resolution_nodes.insert( + self.r.local_def_id(item_id), + if is_in_trait_impl { item_id } else { delegation.id }, + ); + let Some(body) = &delegation.body else { return }; self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { let span = delegation.path.segments.last().unwrap().ident.span; @@ -4294,7 +4307,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); } - #[instrument(level = "debug", skip(self))] fn smart_resolve_path_fragment( &mut self, qself: &Option>, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 233a4c48862a..f75ac400dc0b 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -440,6 +440,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg); self.suggest_at_operator_in_slice_pat_with_range(&mut err, path); + self.suggest_range_struct_destructuring(&mut err, path, source); self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); if let Some((span, label)) = base_error.span_label { @@ -1383,6 +1384,84 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } + fn suggest_range_struct_destructuring( + &mut self, + err: &mut Diag<'_>, + path: &[Segment], + source: PathSource<'_, '_, '_>, + ) { + if !matches!(source, PathSource::Pat | PathSource::TupleStruct(..) | PathSource::Expr(..)) { + return; + } + + let Some(pat) = self.diag_metadata.current_pat else { return }; + let ast::PatKind::Range(start, end, end_kind) = &pat.kind else { return }; + + let [segment] = path else { return }; + let failing_span = segment.ident.span; + + let in_start = start.as_ref().is_some_and(|e| e.span.contains(failing_span)); + let in_end = end.as_ref().is_some_and(|e| e.span.contains(failing_span)); + + if !in_start && !in_end { + return; + } + + let start_snippet = + start.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok()); + let end_snippet = + end.as_ref().and_then(|e| self.r.tcx.sess.source_map().span_to_snippet(e.span).ok()); + + let field = |name: &str, val: String| { + if val == name { val } else { format!("{name}: {val}") } + }; + + let mut resolve_short_name = |short: Symbol, full: &str| -> String { + let ident = Ident::with_dummy_span(short); + let path = Segment::from_path(&Path::from_ident(ident)); + + match self.resolve_path(&path, Some(TypeNS), None, PathSource::Type) { + PathResult::NonModule(..) => short.to_string(), + _ => full.to_string(), + } + }; + // FIXME(new_range): Also account for new range types + let (struct_path, fields) = match (start_snippet, end_snippet, &end_kind.node) { + (Some(start), Some(end), ast::RangeEnd::Excluded) => ( + resolve_short_name(sym::Range, "std::ops::Range"), + vec![field("start", start), field("end", end)], + ), + (Some(start), Some(end), ast::RangeEnd::Included(_)) => ( + resolve_short_name(sym::RangeInclusive, "std::ops::RangeInclusive"), + vec![field("start", start), field("end", end)], + ), + (Some(start), None, _) => ( + resolve_short_name(sym::RangeFrom, "std::ops::RangeFrom"), + vec![field("start", start)], + ), + (None, Some(end), ast::RangeEnd::Excluded) => { + (resolve_short_name(sym::RangeTo, "std::ops::RangeTo"), vec![field("end", end)]) + } + (None, Some(end), ast::RangeEnd::Included(_)) => ( + resolve_short_name(sym::RangeToInclusive, "std::ops::RangeToInclusive"), + vec![field("end", end)], + ), + _ => return, + }; + + err.span_suggestion_verbose( + pat.span, + format!("if you meant to destructure a range use a struct pattern"), + format!("{} {{ {} }}", struct_path, fields.join(", ")), + Applicability::MaybeIncorrect, + ); + + err.note( + "range patterns match against the start and end of a range; \ + to bind the components, use a struct pattern", + ); + } + fn suggest_swapping_misplaced_self_ty_and_trait( &mut self, err: &mut Diag<'_>, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b3141406e467..097345395ba4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -10,7 +10,6 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] -#![deny(clippy::manual_let_else)] #![feature(arbitrary_self_types)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -44,7 +43,7 @@ use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::node_id::NodeMap; use rustc_ast::{ self as ast, AngleBracketedArg, CRATE_NODE_ID, Crate, Expr, ExprKind, GenericArg, GenericArgs, - LitKind, NodeId, Path, attr, + NodeId, Path, attr, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; @@ -54,7 +53,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer}; use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind}; use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir::attrs::StrippedCfgItem; +use rustc_hir::attrs::{AttributeKind, StrippedCfgItem}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{ self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes, @@ -62,7 +61,7 @@ use rustc_hir::def::{ }; use rustc_hir::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; use rustc_hir::definitions::DisambiguatorState; -use rustc_hir::{PrimTy, TraitCandidate}; +use rustc_hir::{PrimTy, TraitCandidate, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::CStore; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; @@ -1276,6 +1275,7 @@ pub struct Resolver<'ra, 'tcx> { /// Amount of lifetime parameters for each item in the crate. item_generics_num_lifetimes: FxHashMap, delegation_fn_sigs: LocalDefIdMap, + delegation_sig_resolution_nodes: LocalDefIdMap, main_def: Option = None, trait_impls: FxIndexMap>, @@ -1676,8 +1676,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { node_id_to_def_id, disambiguator: DisambiguatorState::new(), placeholder_field_indices: Default::default(), - invocation_parents, legacy_const_generic_args: Default::default(), + invocation_parents, item_generics_num_lifetimes: Default::default(), trait_impls: Default::default(), confused_type_with_std_module: Default::default(), @@ -1694,6 +1694,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { current_crate_outer_attr_insert_span, mods_with_parse_errors: Default::default(), impl_trait_names: Default::default(), + delegation_sig_resolution_nodes: Default::default(), .. }; @@ -1822,6 +1823,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { lifetime_elision_allowed: self.lifetime_elision_allowed, lint_buffer: Steal::new(self.lint_buffer), delegation_fn_sigs: self.delegation_fn_sigs, + delegation_sig_resolution_nodes: self.delegation_sig_resolution_nodes, }; ResolverOutputs { global_ctxt, ast_lowering } } @@ -2396,40 +2398,33 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// `#[rustc_legacy_const_generics]` and returns the argument index list /// from the attribute. fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option> { - if let ExprKind::Path(None, path) = &expr.kind { - // Don't perform legacy const generics rewriting if the path already - // has generic arguments. - if path.segments.last().unwrap().args.is_some() { - return None; - } - - let res = self.partial_res_map.get(&expr.id)?.full_res()?; - if let Res::Def(def::DefKind::Fn, def_id) = res { - // We only support cross-crate argument rewriting. Uses - // within the same crate should be updated to use the new - // const generics style. - if def_id.is_local() { - return None; - } - - if let Some(v) = self.legacy_const_generic_args.get(&def_id) { - return v.clone(); - } - - let attr = self.tcx.get_attr(def_id, sym::rustc_legacy_const_generics)?; - let mut ret = Vec::new(); - for meta in attr.meta_item_list()? { - match meta.lit()?.kind { - LitKind::Int(a, _) => ret.push(a.get() as usize), - _ => panic!("invalid arg index"), - } - } - // Cache the lookup to avoid parsing attributes for an item multiple times. - self.legacy_const_generic_args.insert(def_id, Some(ret.clone())); - return Some(ret); - } + let ExprKind::Path(None, path) = &expr.kind else { + return None; + }; + // Don't perform legacy const generics rewriting if the path already + // has generic arguments. + if path.segments.last().unwrap().args.is_some() { + return None; } - None + + let def_id = self.partial_res_map.get(&expr.id)?.full_res()?.opt_def_id()?; + + // We only support cross-crate argument rewriting. Uses + // within the same crate should be updated to use the new + // const generics style. + if def_id.is_local() { + return None; + } + + let indexes = find_attr!( + // we can use parsed attrs here since for other crates they're already available + self.tcx.get_all_attrs(def_id), + AttributeKind::RustcLegacyConstGenerics{fn_indexes,..} => fn_indexes + ) + .map(|fn_indexes| fn_indexes.iter().map(|(num, _)| *num).collect()); + + self.legacy_const_generic_args.insert(def_id, indexes.clone()); + indexes } fn resolve_main(&mut self) { diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index a4166951e350..2fdce628f0c9 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -57,7 +57,6 @@ session_int_literal_too_large = integer literal is too large .note = value exceeds limit of `{$limit}` session_invalid_character_in_crate_name = invalid character {$character} in crate name: `{$crate_name}` - .help = you can either pass `--crate-name` on the command line or add `#![crate_name = "…"]` to set the crate name session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal .label = invalid suffix `{$suffix}` diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index be4b36e3b926..a3a97dfec61d 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -806,7 +806,7 @@ pub enum ErrorOutputType { /// Output meant for the consumption of humans. #[default] HumanReadable { - kind: HumanReadableErrorType = HumanReadableErrorType::Default { short: false }, + kind: HumanReadableErrorType = HumanReadableErrorType { short: false, unicode: false }, color_config: ColorConfig = ColorConfig::Auto, }, /// Output that's consumed by other tools such as `rustfix` or the `RLS`. @@ -1965,16 +1965,8 @@ impl JsonUnusedExterns { /// /// The first value returned is how to render JSON diagnostics, and the second /// is whether or not artifact notifications are enabled. -pub fn parse_json( - early_dcx: &EarlyDiagCtxt, - matches: &getopts::Matches, - is_nightly_build: bool, -) -> JsonConfig { - let mut json_rendered = if is_nightly_build { - HumanReadableErrorType::AnnotateSnippet { short: false, unicode: false } - } else { - HumanReadableErrorType::Default { short: false } - }; +pub fn parse_json(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> JsonConfig { + let mut json_rendered = HumanReadableErrorType { short: false, unicode: false }; let mut json_color = ColorConfig::Never; let mut json_artifact_notifications = false; let mut json_unused_externs = JsonUnusedExterns::No; @@ -1991,15 +1983,10 @@ pub fn parse_json( for sub_option in option.split(',') { match sub_option { "diagnostic-short" => { - json_rendered = if is_nightly_build { - HumanReadableErrorType::AnnotateSnippet { short: true, unicode: false } - } else { - HumanReadableErrorType::Default { short: true } - }; + json_rendered = HumanReadableErrorType { short: true, unicode: false }; } "diagnostic-unicode" => { - json_rendered = - HumanReadableErrorType::AnnotateSnippet { short: false, unicode: true }; + json_rendered = HumanReadableErrorType { short: false, unicode: true }; } "diagnostic-rendered-ansi" => json_color = ColorConfig::Always, "artifacts" => json_artifact_notifications = true, @@ -2029,13 +2016,8 @@ pub fn parse_error_format( color_config: ColorConfig, json_color: ColorConfig, json_rendered: HumanReadableErrorType, - is_nightly_build: bool, ) -> ErrorOutputType { - let default_kind = if is_nightly_build { - HumanReadableErrorType::AnnotateSnippet { short: false, unicode: false } - } else { - HumanReadableErrorType::Default { short: false } - }; + let default_kind = HumanReadableErrorType { short: false, unicode: false }; // We need the `opts_present` check because the driver will send us Matches // with only stable options if no unstable options are used. Since error-format // is unstable, it will not be present. We have to use `opts_present` not @@ -2045,10 +2027,6 @@ pub fn parse_error_format( None | Some("human") => { ErrorOutputType::HumanReadable { color_config, kind: default_kind } } - Some("human-annotate-rs") => ErrorOutputType::HumanReadable { - kind: HumanReadableErrorType::AnnotateSnippet { short: false, unicode: false }, - color_config, - }, Some("json") => { ErrorOutputType::Json { pretty: false, json_rendered, color_config: json_color } } @@ -2056,15 +2034,11 @@ pub fn parse_error_format( ErrorOutputType::Json { pretty: true, json_rendered, color_config: json_color } } Some("short") => ErrorOutputType::HumanReadable { - kind: if is_nightly_build { - HumanReadableErrorType::AnnotateSnippet { short: true, unicode: false } - } else { - HumanReadableErrorType::Default { short: true } - }, + kind: HumanReadableErrorType { short: true, unicode: false }, color_config, }, Some("human-unicode") => ErrorOutputType::HumanReadable { - kind: HumanReadableErrorType::AnnotateSnippet { short: false, unicode: true }, + kind: HumanReadableErrorType { short: false, unicode: true }, color_config, }, Some(arg) => { @@ -2073,8 +2047,8 @@ pub fn parse_error_format( kind: default_kind, }); early_dcx.early_fatal(format!( - "argument for `--error-format` must be `human`, `human-annotate-rs`, \ - `human-unicode`, `json`, `pretty-json` or `short` (instead was `{arg}`)" + "argument for `--error-format` must be `human`, `human-unicode`, \ + `json`, `pretty-json` or `short` (instead was `{arg}`)" )) } } @@ -2136,8 +2110,7 @@ fn check_error_format_stability( let format = match format { ErrorOutputType::Json { pretty: true, .. } => "pretty-json", ErrorOutputType::HumanReadable { kind, .. } => match kind { - HumanReadableErrorType::AnnotateSnippet { unicode: false, .. } => "human-annotate-rs", - HumanReadableErrorType::AnnotateSnippet { unicode: true, .. } => "human-unicode", + HumanReadableErrorType { unicode: true, .. } => "human-unicode", _ => return, }, _ => return, @@ -2465,16 +2438,9 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M json_timings, json_unused_externs, json_future_incompat, - } = parse_json(early_dcx, matches, unstable_features.is_nightly_build()); + } = parse_json(early_dcx, matches); - let error_format = parse_error_format( - early_dcx, - matches, - color, - json_color, - json_rendered, - unstable_features.is_nightly_build(), - ); + let error_format = parse_error_format(early_dcx, matches, color, json_color, json_rendered); early_dcx.set_error_format(error_format); diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs index 9d000bc28edd..2f969aefda18 100644 --- a/compiler/rustc_session/src/cstore.rs +++ b/compiler/rustc_session/src/cstore.rs @@ -39,12 +39,13 @@ impl CrateSource { pub enum CrateDepKind { /// A dependency that is only used for its macros. MacrosOnly, - /// A dependency that is always injected into the dependency list and so - /// doesn't need to be linked to an rlib, e.g., the injected panic runtime. - Implicit, + /// A dependency that is injected into the crate graph but which only + /// sometimes needs to actually be linked in, e.g., the injected panic runtime. + Conditional, /// A dependency that is required by an rlib version of this crate. - /// Ordinary `extern crate`s result in `Explicit` dependencies. - Explicit, + /// Ordinary `extern crate`s as well as most injected dependencies result + /// in `Unconditional` dependencies. + Unconditional, } impl CrateDepKind { @@ -52,7 +53,7 @@ impl CrateDepKind { pub fn macros_only(self) -> bool { match self { CrateDepKind::MacrosOnly => true, - CrateDepKind::Implicit | CrateDepKind::Explicit => false, + CrateDepKind::Conditional | CrateDepKind::Unconditional => false, } } } diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 07b41a3de714..1f2d386a23cb 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -238,8 +238,6 @@ pub(crate) struct InvalidCharacterInCrateName { pub(crate) span: Option, pub(crate) character: char, pub(crate) crate_name: Symbol, - #[help] - pub(crate) help: Option<()>, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 99c736c3b1a5..de8f1d6009ab 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -69,7 +69,6 @@ pub fn validate_crate_name(sess: &Session, crate_name: Symbol, span: Option match kind { - HumanReadableErrorType::AnnotateSnippet { short, unicode } => { + HumanReadableErrorType { short, unicode } => { let emitter = AnnotateSnippetEmitter::new(stderr_destination(color_config), translator) .sm(source_map) @@ -938,20 +936,6 @@ fn default_emitter( ); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } - HumanReadableErrorType::Default { short } => { - let emitter = HumanEmitter::new(stderr_destination(color_config), translator) - .sm(source_map) - .short_message(short) - .diagnostic_width(sopts.diagnostic_width) - .macro_backtrace(macro_backtrace) - .track_diagnostics(track_diagnostics) - .terminal_url(terminal_url) - .theme(OutputTheme::Ascii) - .ignored_directories_in_source_blocks( - sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(), - ); - Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) - } }, config::ErrorOutputType::Json { pretty, json_rendered, color_config } => Box::new( JsonEmitter::new( @@ -1460,16 +1444,11 @@ fn mk_emitter(output: ErrorOutputType) -> Box { Translator::with_fallback_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); let emitter: Box = match output { config::ErrorOutputType::HumanReadable { kind, color_config } => match kind { - HumanReadableErrorType::AnnotateSnippet { short, unicode } => Box::new( + HumanReadableErrorType { short, unicode } => Box::new( AnnotateSnippetEmitter::new(stderr_destination(color_config), translator) .theme(if unicode { OutputTheme::Unicode } else { OutputTheme::Ascii }) .short_message(short), ), - HumanReadableErrorType::Default { short } => Box::new( - HumanEmitter::new(stderr_destination(color_config), translator) - .theme(OutputTheme::Ascii) - .short_message(short), - ), }, config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { Box::new(JsonEmitter::new( diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 49b2e0c1ff1a..3d641905d325 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -241,12 +241,14 @@ bitflags::bitflags! { } impl Encodable for RemapPathScopeComponents { + #[inline] fn encode(&self, s: &mut E) { s.emit_u8(self.bits()); } } impl Decodable for RemapPathScopeComponents { + #[inline] fn decode(s: &mut D) -> RemapPathScopeComponents { RemapPathScopeComponents::from_bits(s.read_u8()) .expect("invalid bits for RemapPathScopeComponents") @@ -308,12 +310,13 @@ struct InnerRealFileName { } impl Hash for RealFileName { + #[inline] fn hash(&self, state: &mut H) { // To prevent #70924 from happening again we should only hash the // remapped path if that exists. This is because remapped paths to // sysroot crates (/rust/$hash or /rust/$version) remain stable even // if the corresponding local path changes. - if !self.scopes.is_all() { + if !self.was_fully_remapped() { self.local.hash(state); } self.maybe_remapped.hash(state); @@ -327,6 +330,7 @@ impl RealFileName { /// ## Panic /// /// Only one scope components can be given to this function. + #[inline] pub fn path(&self, scope: RemapPathScopeComponents) -> &Path { assert!( scope.bits().count_ones() == 1, @@ -351,6 +355,7 @@ impl RealFileName { /// ## Panic /// /// Only one scope components can be given to this function. + #[inline] pub fn embeddable_name(&self, scope: RemapPathScopeComponents) -> (&Path, &Path) { assert!( scope.bits().count_ones() == 1, @@ -369,26 +374,58 @@ impl RealFileName { /// if this information exists. /// /// May not exists if the filename was imported from another crate. + /// + /// Avoid embedding this in build artifacts; prefer `path()` or `embeddable_name()`. + #[inline] pub fn local_path(&self) -> Option<&Path> { - self.local.as_ref().map(|lp| lp.name.as_ref()) + if self.was_not_remapped() { + Some(&self.maybe_remapped.name) + } else if let Some(local) = &self.local { + Some(&local.name) + } else { + None + } } /// Returns the path suitable for reading from the file system on the local host, /// if this information exists. /// /// May not exists if the filename was imported from another crate. + /// + /// Avoid embedding this in build artifacts; prefer `path()` or `embeddable_name()`. + #[inline] pub fn into_local_path(self) -> Option { - self.local.map(|lp| lp.name) + if self.was_not_remapped() { + Some(self.maybe_remapped.name) + } else if let Some(local) = self.local { + Some(local.name) + } else { + None + } } /// Returns whenever the filename was remapped. + #[inline] pub(crate) fn was_remapped(&self) -> bool { !self.scopes.is_empty() } + /// Returns whenever the filename was fully remapped. + #[inline] + fn was_fully_remapped(&self) -> bool { + self.scopes.is_all() + } + + /// Returns whenever the filename was not remapped. + #[inline] + fn was_not_remapped(&self) -> bool { + self.scopes.is_empty() + } + /// Returns an empty `RealFileName` /// /// Useful as the working directory input to `SourceMap::to_real_filename`. + #[inline] pub fn empty() -> RealFileName { RealFileName { local: Some(InnerRealFileName { @@ -420,9 +457,14 @@ impl RealFileName { /// Update the filename for encoding in the crate metadata. /// /// Currently it's about removing the local part when the filename - /// is fully remapped. + /// is either fully remapped or not remapped at all. + #[inline] pub fn update_for_crate_metadata(&mut self) { - if self.scopes.is_all() { + if self.was_fully_remapped() || self.was_not_remapped() { + // NOTE: This works because when the filename is fully + // remapped, we don't care about the `local` part, + // and when the filename is not remapped at all, + // `maybe_remapped` and `local` are equal. self.local = None; } } @@ -529,6 +571,7 @@ impl FileName { /// if this information exists. /// /// Avoid embedding this in build artifacts. Prefer using the `display` method. + #[inline] pub fn prefer_remapped_unconditionally(&self) -> FileNameDisplay<'_> { FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Remapped } } @@ -537,16 +580,19 @@ impl FileName { /// if this information exists. /// /// Avoid embedding this in build artifacts. Prefer using the `display` method. + #[inline] pub fn prefer_local_unconditionally(&self) -> FileNameDisplay<'_> { FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Local } } /// Returns a short (either the filename or an empty string). + #[inline] pub fn short(&self) -> FileNameDisplay<'_> { FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Short } } /// Returns a `Display`-able path for the given scope. + #[inline] pub fn display(&self, scope: RemapPathScopeComponents) -> FileNameDisplay<'_> { FileNameDisplay { inner: self, display_pref: FileNameDisplayPreference::Scope(scope) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8e464b55d6c2..0e30abccb62b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1998,6 +1998,7 @@ symbols! { rustc_reallocator, rustc_regions, rustc_reservation_impl, + rustc_scalable_vector, rustc_serialize, rustc_should_not_be_called_on_const_items, rustc_simd_monomorphize_lane_limit, @@ -2473,6 +2474,7 @@ symbols! { vsreg, vsx, vtable_align, + vtable_for, vtable_size, warn, wasip2, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 05b24d710948..0078866ab950 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -997,8 +997,8 @@ impl InlineAsmClobberAbi { _ => Err(&["C", "system"]), }, InlineAsmArch::LoongArch32 | InlineAsmArch::LoongArch64 => match name { - "C" | "system" => Ok(InlineAsmClobberAbi::LoongArch), - _ => Err(&["C", "system"]), + "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::LoongArch), + _ => Err(&["C", "system", "efiapi"]), }, InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => match name { "C" | "system" => Ok(if target.abi == Abi::Spe { diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index cb2b7f4455c6..13be3888611f 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -9,13 +9,14 @@ use crate::spec::{Abi, HasTargetSpec, Target}; /// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI. /// /// Corresponds to Clang's `AArch64ABIInfo::ABIKind`. -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub(crate) enum AbiKind { AAPCS, DarwinPCS, Win64, } +#[tracing::instrument(skip(cx), level = "debug")] fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option where Ty: TyAbiInterface<'a, C> + Copy, @@ -73,12 +74,13 @@ fn softfloat_float_abi(target: &Target, arg: &mut ArgAbi<'_, Ty>) { } } +#[tracing::instrument(skip(cx), level = "debug")] fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, kind: AbiKind) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if !ret.layout.is_sized() { + if !ret.layout.is_sized() || ret.layout.is_scalable_vector() { // Not touching this... return; } @@ -105,12 +107,13 @@ where ret.make_indirect(); } +#[tracing::instrument(skip(cx), level = "debug")] fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, kind: AbiKind) where Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - if !arg.layout.is_sized() { + if !arg.layout.is_sized() || arg.layout.is_scalable_vector() { // Not touching this... return; } diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index bbea841b4106..d74d15446eaf 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -85,7 +85,10 @@ where } } }, - BackendRepr::SimdVector { .. } => return Err(CannotUseFpConv), + BackendRepr::SimdVector { .. } => { + return Err(CannotUseFpConv); + } + BackendRepr::ScalableVector { .. } => panic!("scalable vectors are unsupported"), BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 878340143582..092d99e91118 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -393,6 +393,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { ), BackendRepr::SimdVector { .. } => PassMode::Direct(ArgAttributes::new()), BackendRepr::Memory { .. } => Self::indirect_pass_mode(&layout), + BackendRepr::ScalableVector { .. } => PassMode::Direct(ArgAttributes::new()), }; ArgAbi { layout, mode } } diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index c4310bf6ccfb..f166b8330516 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -91,7 +91,9 @@ where } } }, - BackendRepr::SimdVector { .. } => return Err(CannotUseFpConv), + BackendRepr::SimdVector { .. } | BackendRepr::ScalableVector { .. } => { + return Err(CannotUseFpConv); + } BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 93dc60dc1593..5e8cab0ee987 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -103,6 +103,9 @@ where } false } + BackendRepr::ScalableVector { .. } => { + panic!("scalable vectors are unsupported") + } } } diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 04eecbfbc8dd..8fab4e444228 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -59,6 +59,8 @@ where BackendRepr::SimdVector { .. } => Class::Sse, + BackendRepr::ScalableVector { .. } => panic!("scalable vectors are unsupported"), + BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => { for i in 0..layout.fields.count() { let field_off = off + layout.fields.offset(i); diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index 85ee59eabc7c..48f7700a86d5 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -25,6 +25,7 @@ where // FIXME(eddyb) there should be a size cap here // (probably what clang calls "illegal vectors"). } + BackendRepr::ScalableVector { .. } => panic!("scalable vectors are unsupported"), BackendRepr::Scalar(scalar) => { if is_ret && matches!(scalar.primitive(), Primitive::Int(Integer::I128, _)) { if cx.target_spec().rustc_abi == Some(RustcAbi::X86Softfloat) { diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 1210169b3a5c..dfa1b2320718 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -8,7 +8,6 @@ //! LLVM. // tidy-alphabetical-start -#![cfg_attr(bootstrap, feature(debug_closure_helpers))] #![expect(internal_features)] #![feature(iter_intersperse)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs index 8126227d80e4..d7fc18cd3761 100644 --- a/compiler/rustc_target/src/spec/abi_map.rs +++ b/compiler/rustc_target/src/spec/abi_map.rs @@ -56,6 +56,7 @@ impl AbiMap { ArmVer::Other }), Arch::Avr => ArchKind::Avr, + Arch::LoongArch32 | Arch::LoongArch64 => ArchKind::LoongArch, Arch::Msp430 => ArchKind::Msp430, Arch::Nvptx64 => ArchKind::Nvptx, Arch::RiscV32 | Arch::RiscV64 => ArchKind::Riscv, @@ -108,7 +109,10 @@ impl AbiMap { (ExternAbi::EfiApi, ArchKind::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs), (ExternAbi::EfiApi, ArchKind::X86_64) => CanonAbi::X86(X86Call::Win64), - (ExternAbi::EfiApi, ArchKind::Aarch64 | ArchKind::Riscv | ArchKind::X86) => CanonAbi::C, + ( + ExternAbi::EfiApi, + ArchKind::Aarch64 | ArchKind::LoongArch | ArchKind::Riscv | ArchKind::X86, + ) => CanonAbi::C, (ExternAbi::EfiApi, _) => return AbiMapping::Invalid, /* arm */ @@ -196,6 +200,7 @@ enum ArchKind { Amdgpu, Arm(ArmVer), Avr, + LoongArch, Msp430, Nvptx, Riscv, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index c76e345bb7b6..b06339f59425 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1686,6 +1686,7 @@ supported_targets! { ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf), ("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu), ("riscv32gc-unknown-linux-musl", riscv32gc_unknown_linux_musl), + ("riscv64im-unknown-none-elf", riscv64im_unknown_none_elf), ("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf), ("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf), ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu), @@ -2955,11 +2956,6 @@ impl Target { matches!(self.linker_flavor, LinkerFlavor::Bpf), "`linker_flavor` must be `bpf` if and only if `arch` is `bpf`" ); - check_eq!( - self.arch == Arch::Nvptx64, - matches!(self.linker_flavor, LinkerFlavor::Ptx), - "`linker_flavor` must be `ptc` if and only if `arch` is `nvptx64`" - ); for args in [ &self.pre_link_args, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs index 69a65e6b0f02..47775f968400 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { - features: "+v8a".into(), + features: "+v8a,+outline-atomics".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, supported_sanitizers: SanitizerSet::ADDRESS diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index 35003c4d4dd6..be09681b1f35 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -19,9 +19,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { os: Os::Cuda, vendor: "nvidia".into(), - linker_flavor: LinkerFlavor::Ptx, - // The linker can be installed from `crates.io`. - linker: Some("rust-ptx-linker".into()), + linker_flavor: LinkerFlavor::Llbc, // With `ptx-linker` approach, it can be later overridden via link flags. cpu: "sm_30".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64im_unknown_none_elf.rs new file mode 100644 index 000000000000..6aae40a3f90d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv64im_unknown_none_elf.rs @@ -0,0 +1,35 @@ +use crate::spec::{ + Arch, Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, +}; + +pub(crate) fn target() -> Target { + Target { + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + llvm_target: "riscv64".into(), + metadata: TargetMetadata { + description: Some("Bare RISC-V (RV64IM ISA)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + arch: Arch::RiscV64, + + options: TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv64".into(), + max_atomic_width: Some(64), + atomic_cas: false, + features: "+m,+forced-atomics".into(), + llvm_abiname: "lp64".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + code_model: Some(CodeModel::Medium), + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 990928774731..35cfeba2bffd 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -745,6 +745,7 @@ static WASM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("bulk-memory", Stable, &[]), ("exception-handling", Unstable(sym::wasm_target_feature), &[]), ("extended-const", Stable, &[]), + ("gc", Unstable(sym::wasm_target_feature), &["reference-types"]), ("multivalue", Stable, &[]), ("mutable-globals", Stable, &[]), ("nontrapping-fptoint", Stable, &[]), @@ -911,17 +912,24 @@ pub fn all_rust_features() -> impl Iterator { // These arrays represent the least-constraining feature that is required for vector types up to a // certain size to have their "proper" ABI on each architecture. // Note that they must be kept sorted by vector size. -const X86_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = +const X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "sse"), (256, "avx"), (512, "avx512f")]; // FIXME: might need changes for AVX10. -const AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")]; +const AARCH64_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "neon")]; // We might want to add "helium" too. -const ARM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")]; +const ARM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "neon")]; -const POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "altivec")]; -const WASM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "simd128")]; -const S390X_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vector")]; -const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[ +const AMDGPU_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(1024, "")]; +const POWERPC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "altivec")]; +const WASM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "simd128")]; +const S390X_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "vector")]; +const RISCV_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[ (32, "zvl32b"), (64, "zvl64b"), (128, "zvl128b"), @@ -936,13 +944,16 @@ const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[ (65536, "zvl65536b"), ]; // Always error on SPARC, as the necessary target features cannot be enabled in Rust at the moment. -const SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(64, "vis")*/]; +const SPARC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[/*(64, "vis")*/]; -const HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = +const HEXAGON_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(512, "hvx-length64b"),*/ (1024, "hvx-length128b")]; -const MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "msa")]; -const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vdspv1")]; -const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = +const MIPS_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "msa")]; +const CSKY_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "vdspv1")]; +const LOONGARCH_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "lsx"), (256, "lasx")]; #[derive(Copy, Clone, Debug)] @@ -981,27 +992,29 @@ impl Target { } } - pub fn features_for_correct_vector_abi(&self) -> &'static [(u64, &'static str)] { + pub fn features_for_correct_fixed_length_vector_abi(&self) -> &'static [(u64, &'static str)] { match &self.arch { - Arch::X86 | Arch::X86_64 => X86_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::AArch64 | Arch::Arm64EC => AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::Arm => ARM_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::PowerPC | Arch::PowerPC64 => POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::LoongArch32 | Arch::LoongArch64 => LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::RiscV32 | Arch::RiscV64 => RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::Wasm32 | Arch::Wasm64 => WASM_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::S390x => S390X_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::Sparc | Arch::Sparc64 => SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::Hexagon => HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI, - Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => { - MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI + Arch::X86 | Arch::X86_64 => X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::AArch64 | Arch::Arm64EC => AARCH64_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::Arm => ARM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::PowerPC | Arch::PowerPC64 => POWERPC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::LoongArch32 | Arch::LoongArch64 => { + LOONGARCH_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI } + Arch::RiscV32 | Arch::RiscV64 => RISCV_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::Wasm32 | Arch::Wasm64 => WASM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::S390x => S390X_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::Sparc | Arch::Sparc64 => SPARC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::Hexagon => HEXAGON_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, + Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => { + MIPS_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI + } + Arch::AmdGpu => AMDGPU_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, Arch::Nvptx64 | Arch::Bpf | Arch::M68k => &[], // no vector ABI - Arch::CSky => CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI, + Arch::CSky => CSKY_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI, // FIXME: for some tier3 targets, we are overly cautious and always give warnings // when passing args in vector registers. - Arch::AmdGpu - | Arch::Avr + Arch::Avr | Arch::Msp430 | Arch::PowerPC64LE | Arch::SpirV @@ -1010,6 +1023,14 @@ impl Target { } } + pub fn features_for_correct_scalable_vector_abi(&self) -> Option<&'static str> { + match &self.arch { + Arch::AArch64 | Arch::Arm64EC => Some("sve"), + // Other targets have no scalable vectors or they are unimplemented. + _ => None, + } + } + pub fn tied_target_features(&self) -> &'static [&'static [&'static str]] { match &self.arch { Arch::AArch64 | Arch::Arm64EC => AARCH64_TIED_FEATURES, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 32be23eed765..4de3ba3e2781 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1758,8 +1758,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // specify a byte literal (ty::Uint(ty::UintTy::U8), ty::Char) => { if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - && let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) + && let Some(code) = code.strip_circumfix('\'', '\'') // forbid all Unicode escapes && !code.starts_with("\\u") // forbids literal Unicode characters beyond ASCII @@ -1776,7 +1775,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // specify a character literal (issue #92479) (ty::Char, ty::Ref(_, r, _)) if r.is_str() => { if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) + && let Some(code) = code.strip_circumfix('"', '"') && code.chars().count() == 1 { suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral { 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 8c867f842b89..d96a1b0a8c0e 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 @@ -1229,7 +1229,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { chain.push((expr.span, prev_ty)); let mut prev = None; - for (span, err_ty) in chain.into_iter().rev() { + let mut iter = chain.into_iter().rev().peekable(); + while let Some((span, err_ty)) = iter.next() { + let is_last = iter.peek().is_none(); let err_ty = get_e_type(err_ty); let err_ty = match (err_ty, prev) { (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => { @@ -1241,27 +1243,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { continue; } }; - if self + + let implements_from = self .infcx .type_implements_trait( self.tcx.get_diagnostic_item(sym::From).unwrap(), [self_ty, err_ty], obligation.param_env, ) - .must_apply_modulo_regions() - { - if !suggested { - let err_ty = self.tcx.short_string(err_ty, err.long_ty_path()); - err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); - } + .must_apply_modulo_regions(); + + let err_ty_str = self.tcx.short_string(err_ty, err.long_ty_path()); + let label = if !implements_from && is_last { + format!( + "this can't be annotated with `?` because it has type `Result<_, {err_ty_str}>`" + ) } else { - let err_ty = self.tcx.short_string(err_ty, err.long_ty_path()); - err.span_label( - span, - format!( - "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", - ), - ); + format!("this has type `Result<_, {err_ty_str}>`") + }; + + if !suggested || !implements_from { + err.span_label(span, label); } prev = Some(err_ty); } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 0184b2ccde78..6705d271bc7d 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -22,6 +22,7 @@ #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] #![feature(never_type)] +#![feature(strip_circumfix)] #![feature(try_blocks)] #![feature(unwrap_infallible)] #![feature(yeet_expr)] diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index db7bd635f32e..394095508393 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -771,9 +771,25 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { } ty::Tuple(tys) => { - if let Some((_last, rest)) = tys.split_last() { + if let Some((last, rest)) = tys.split_last() { for &elem in rest { self.require_sized(elem, ObligationCauseCode::TupleElem); + if elem.is_scalable_vector() && !self.span.is_dummy() { + self.tcx() + .dcx() + .struct_span_err( + self.span, + "scalable vectors cannot be tuple fields", + ) + .emit(); + } + } + + if last.is_scalable_vector() && !self.span.is_dummy() { + self.tcx() + .dcx() + .struct_span_err(self.span, "scalable vectors cannot be tuple fields") + .emit(); } } } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index d9ebdadf3b79..1202ed238431 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -491,7 +491,7 @@ pub(crate) mod rustc { ) -> Result { // This constructor does not support non-`FieldsShape::Arbitrary` // layouts. - let FieldsShape::Arbitrary { offsets, memory_index } = layout.fields() else { + let FieldsShape::Arbitrary { offsets, in_memory_order } = layout.fields() else { return Err(Err::NotYetSupported); }; @@ -519,8 +519,7 @@ pub(crate) mod rustc { } // Append the fields, in memory order, to the layout. - let inverse_memory_index = memory_index.invert_bijective_mapping(); - for &field_idx in inverse_memory_index.iter() { + for &field_idx in in_memory_order.iter() { // Add interfield padding. let padding_needed = offsets[field_idx] - size; let padding = Self::padding(padding_needed.bytes_usize()); diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index f98f161e8093..0676fa99d826 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -315,13 +315,18 @@ fn arg_attrs_for_rust_scalar<'tcx>( attrs.pointee_align = Some(pointee.align.min(cx.tcx().sess.target.max_reliable_alignment())); - // `Box` are not necessarily dereferenceable for the entire duration of the function as - // they can be deallocated at any time. Same for non-frozen shared references (see - // ), and for mutable references to - // potentially self-referential types (see - // ). If LLVM had a way - // to say "dereferenceable on entry" we could use it here. attrs.pointee_size = match kind { + // LLVM dereferenceable attribute has unclear semantics on the return type, + // they seem to be "dereferenceable until the end of the program", which is + // generally, not valid for references. See + // + _ if is_return => Size::ZERO, + // `Box` are not necessarily dereferenceable for the entire duration of the function as + // they can be deallocated at any time. Same for non-frozen shared references (see + // ), and for mutable references to + // potentially self-referential types (see + // ). If LLVM had a way + // to say "dereferenceable on entry" we could use it here. PointerKind::Box { .. } | PointerKind::SharedRef { frozen: false } | PointerKind::MutableRef { unpin: false } => Size::ZERO, @@ -407,7 +412,9 @@ fn fn_abi_sanity_check<'tcx>( // `layout.backend_repr` and ignore everything else. We should just reject //`Aggregate` entirely here, but some targets need to be fixed first. match arg.layout.backend_repr { - BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => {} + BackendRepr::Scalar(_) + | BackendRepr::SimdVector { .. } + | BackendRepr::ScalableVector { .. } => {} BackendRepr::ScalarPair(..) => { panic!("`PassMode::Direct` used for ScalarPair type {}", arg.layout.ty) } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 3a935b917ea2..62f3667ad7f4 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -3,13 +3,13 @@ use rustc_abi::Integer::{I8, I32}; use rustc_abi::Primitive::{self, Float, Int, Pointer}; use rustc_abi::{ AddressSpace, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, HasDataLayout, Layout, - LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, - VariantIdx, Variants, WrappingRange, + LayoutCalculatorError, LayoutData, Niche, ReprOptions, ScalableElt, Scalar, Size, StructKind, + TagEncoding, VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; -use rustc_index::IndexVec; +use rustc_index::{Idx as _, IndexVec}; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::ObligationCause; @@ -374,7 +374,7 @@ fn layout_of_uncached<'tcx>( // specifically care about pattern types will have to handle it. layout.fields = FieldsShape::Arbitrary { offsets: [Size::ZERO].into_iter().collect(), - memory_index: [0].into_iter().collect(), + in_memory_order: [FieldIdx::new(0)].into_iter().collect(), }; tcx.mk_layout(layout) } @@ -567,6 +567,37 @@ fn layout_of_uncached<'tcx>( univariant(tys, kind)? } + // Scalable vector types + // + // ```rust (ignore, example) + // #[rustc_scalable_vector(3)] + // struct svuint32_t(u32); + // ``` + ty::Adt(def, args) + if matches!(def.repr().scalable, Some(ScalableElt::ElementCount(..))) => + { + let Some(element_ty) = def + .is_struct() + .then(|| &def.variant(FIRST_VARIANT).fields) + .filter(|fields| fields.len() == 1) + .map(|fields| fields[FieldIdx::ZERO].ty(tcx, args)) + else { + let guar = tcx + .dcx() + .delayed_bug("#[rustc_scalable_vector] was applied to an invalid type"); + return Err(error(cx, LayoutError::ReferencesError(guar))); + }; + let Some(ScalableElt::ElementCount(element_count)) = def.repr().scalable else { + let guar = tcx + .dcx() + .delayed_bug("#[rustc_scalable_vector] was applied to an invalid type"); + return Err(error(cx, LayoutError::ReferencesError(guar))); + }; + + let element_layout = cx.layout_of(element_ty)?; + map_layout(cx.calc.scalable_vector_type(element_layout, element_count as u64))? + } + // SIMD vector types. ty::Adt(def, args) if def.repr().simd() => { // Supported SIMD vectors are ADTs with a single array field: diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 01435f7e67a4..b013902f3fe3 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -250,7 +250,7 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou // And the size has to be element * count plus alignment padding, of course assert!(size == (element_size * count).align_to(align)); } - BackendRepr::Memory { .. } => {} // Nothing to check. + BackendRepr::Memory { .. } | BackendRepr::ScalableVector { .. } => {} // Nothing to check. } } diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 6ccddb17ff22..315ab8b97e9b 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -35,6 +35,7 @@ nightly = [ "dep:rustc_span", "rustc_ast_ir/nightly", "rustc_index/nightly", + "rustc_type_ir_macros/nightly", "smallvec/may_dangle", "smallvec/union", ] diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index d005ae95d6f1..b5c9080154ce 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -5,7 +5,7 @@ use std::ops::{ControlFlow, Deref}; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::instrument; use crate::data_structures::SsoHashSet; @@ -25,6 +25,7 @@ use crate::{self as ty, DebruijnIndex, Interner, UniverseIndex}; /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, T)] #[derive_where(Copy; I: Interner, T: Copy)] +#[derive(GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct Binder { value: T, @@ -361,6 +362,7 @@ impl TypeVisitor for ValidateBoundVars { #[derive_where(Clone, PartialEq, Ord, Hash, Debug; I: Interner, T)] #[derive_where(PartialOrd; I: Interner, T: Ord)] #[derive_where(Copy; I: Interner, T: Copy)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -944,7 +946,7 @@ impl<'a, I: Interner> ArgFolder<'a, I> { feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] pub enum BoundVarIndexKind { Bound(DebruijnIndex), Canonical, diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 7b4b953b2cf6..47e753d78731 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -5,7 +5,9 @@ use arrayvec::ArrayVec; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use crate::data_structures::HashMap; use crate::inherent::*; @@ -86,6 +88,7 @@ impl fmt::Display for Canonical { /// a copy of the canonical value in some other inference context, /// with fresh inference variables replacing the canonical values. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -219,7 +222,7 @@ impl CanonicalVarKind { feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct CanonicalVarValues { pub var_values: I::GenericArgs, } diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 8393bbe5caf8..f315e8b3e11c 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -5,12 +5,15 @@ use derive_where::derive_where; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use crate::{self as ty, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -66,7 +69,7 @@ impl fmt::Debug for ConstKind { /// An unevaluated (potentially generic) constant used in the type-system. #[derive_where(Clone, Copy, Debug, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs index eb5b6b4a1b45..502622aa50ca 100644 --- a/compiler/rustc_type_ir/src/error.rs +++ b/compiler/rustc_type_ir/src/error.rs @@ -1,11 +1,11 @@ use derive_where::derive_where; -use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::solve::NoSolution; use crate::{self as ty, Interner}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[derive(TypeFoldable_Generic, TypeVisitable_Generic)] +#[derive(TypeFoldable_Generic, TypeVisitable_Generic, GenericTypeVisitable)] pub struct ExpectedFound { pub expected: T, pub found: T, @@ -19,7 +19,7 @@ impl ExpectedFound { // Data structures used in type unification #[derive_where(Clone, Copy, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable)] #[cfg_attr(feature = "nightly", rustc_pass_by_value)] pub enum TypeError { Mismatch, diff --git a/compiler/rustc_type_ir/src/generic_arg.rs b/compiler/rustc_type_ir/src/generic_arg.rs index d6e33f724d0b..5d612740fdd8 100644 --- a/compiler/rustc_type_ir/src/generic_arg.rs +++ b/compiler/rustc_type_ir/src/generic_arg.rs @@ -1,10 +1,12 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_type_ir_macros::GenericTypeVisitable; use crate::Interner; #[derive_where(Clone, Copy, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -18,6 +20,7 @@ pub enum GenericArgKind { impl Eq for GenericArgKind {} #[derive_where(Clone, Copy, PartialEq, Debug; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/generic_visit.rs b/compiler/rustc_type_ir/src/generic_visit.rs new file mode 100644 index 000000000000..ee55c63ab477 --- /dev/null +++ b/compiler/rustc_type_ir/src/generic_visit.rs @@ -0,0 +1,251 @@ +//! A visiting traversal mechanism for complex data structures that contain type +//! information. +//! +//! This is a read-only traversal of the data structure. +//! +//! This traversal has limited flexibility. Only a small number of "types of +//! interest" within the complex data structures can receive custom +//! visitation. These are the ones containing the most important type-related +//! information, such as `Ty`, `Predicate`, `Region`, and `Const`. +//! +//! There are three traits involved in each traversal. +//! - `GenericTypeVisitable`. This is implemented once for many types, including: +//! - Types of interest, for which the methods delegate to the visitor. +//! - All other types, including generic containers like `Vec` and `Option`. +//! It defines a "skeleton" of how they should be visited. +//! - `TypeSuperVisitable`. This is implemented only for recursive types of +//! interest, and defines the visiting "skeleton" for these types. (This +//! excludes `Region` because it is non-recursive, i.e. it never contains +//! other types of interest.) +//! - `CustomizableTypeVisitor`. This is implemented for each visitor. This defines how +//! types of interest are visited. +//! +//! This means each visit is a mixture of (a) generic visiting operations, and (b) +//! custom visit operations that are specific to the visitor. +//! - The `GenericTypeVisitable` impls handle most of the traversal, and call into +//! `CustomizableTypeVisitor` when they encounter a type of interest. +//! - A `CustomizableTypeVisitor` may call into another `GenericTypeVisitable` impl, because some of +//! the types of interest are recursive and can contain other types of interest. +//! - A `CustomizableTypeVisitor` may also call into a `TypeSuperVisitable` impl, because each +//! visitor might provide custom handling only for some types of interest, or +//! only for some variants of each type of interest, and then use default +//! traversal for the remaining cases. +//! +//! For example, if you have `struct S(Ty, U)` where `S: GenericTypeVisitable` and `U: +//! GenericTypeVisitable`, and an instance `s = S(ty, u)`, it would be visited like so: +//! ```text +//! s.generic_visit_with(visitor) calls +//! - ty.generic_visit_with(visitor) calls +//! - visitor.visit_ty(ty) may call +//! - ty.super_generic_visit_with(visitor) +//! - u.generic_visit_with(visitor) +//! ``` + +use std::sync::Arc; + +use rustc_index::{Idx, IndexVec}; +use smallvec::SmallVec; +use thin_vec::ThinVec; + +/// This trait is implemented for every type that can be visited, +/// providing the skeleton of the traversal. +/// +/// To implement this conveniently, use the derive macro located in +/// `rustc_macros`. +pub trait GenericTypeVisitable { + /// The entry point for visiting. To visit a value `t` with a visitor `v` + /// call: `t.generic_visit_with(v)`. + /// + /// For most types, this just traverses the value, calling `generic_visit_with` on + /// each field/element. + /// + /// For types of interest (such as `Ty`), the implementation of this method + /// that calls a visitor method specifically for that type (such as + /// `V::visit_ty`). This is where control transfers from `GenericTypeVisitable` to + /// `CustomizableTypeVisitor`. + fn generic_visit_with(&self, visitor: &mut V); +} + +/////////////////////////////////////////////////////////////////////////// +// Traversal implementations. + +impl> GenericTypeVisitable for &T { + fn generic_visit_with(&self, visitor: &mut V) { + T::generic_visit_with(*self, visitor) + } +} + +impl, U: GenericTypeVisitable> GenericTypeVisitable for (T, U) { + fn generic_visit_with(&self, visitor: &mut V) { + self.0.generic_visit_with(visitor); + self.1.generic_visit_with(visitor); + } +} + +impl, B: GenericTypeVisitable, C: GenericTypeVisitable> + GenericTypeVisitable for (A, B, C) +{ + fn generic_visit_with(&self, visitor: &mut V) { + self.0.generic_visit_with(visitor); + self.1.generic_visit_with(visitor); + self.2.generic_visit_with(visitor); + } +} + +impl> GenericTypeVisitable for Option { + fn generic_visit_with(&self, visitor: &mut V) { + match self { + Some(v) => v.generic_visit_with(visitor), + None => {} + } + } +} + +impl, E: GenericTypeVisitable> GenericTypeVisitable + for Result +{ + fn generic_visit_with(&self, visitor: &mut V) { + match self { + Ok(v) => v.generic_visit_with(visitor), + Err(e) => e.generic_visit_with(visitor), + } + } +} + +impl> GenericTypeVisitable for Arc { + fn generic_visit_with(&self, visitor: &mut V) { + (**self).generic_visit_with(visitor) + } +} + +impl> GenericTypeVisitable for Box { + fn generic_visit_with(&self, visitor: &mut V) { + (**self).generic_visit_with(visitor) + } +} + +impl> GenericTypeVisitable for Vec { + fn generic_visit_with(&self, visitor: &mut V) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + } +} + +impl> GenericTypeVisitable for ThinVec { + fn generic_visit_with(&self, visitor: &mut V) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + } +} + +impl, const N: usize> GenericTypeVisitable for SmallVec<[T; N]> { + fn generic_visit_with(&self, visitor: &mut V) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + } +} + +impl> GenericTypeVisitable for [T] { + fn generic_visit_with(&self, visitor: &mut V) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + } +} + +impl, Ix: Idx> GenericTypeVisitable for IndexVec { + fn generic_visit_with(&self, visitor: &mut V) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + } +} + +impl GenericTypeVisitable for std::hash::BuildHasherDefault { + fn generic_visit_with(&self, _visitor: &mut V) {} +} + +#[expect(rustc::default_hash_types, rustc::potential_query_instability)] +impl< + Visitor, + Key: GenericTypeVisitable, + Value: GenericTypeVisitable, + S: GenericTypeVisitable, +> GenericTypeVisitable for std::collections::HashMap +{ + fn generic_visit_with(&self, visitor: &mut Visitor) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + self.hasher().generic_visit_with(visitor); + } +} + +#[expect(rustc::default_hash_types, rustc::potential_query_instability)] +impl, S: GenericTypeVisitable> GenericTypeVisitable + for std::collections::HashSet +{ + fn generic_visit_with(&self, visitor: &mut V) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + self.hasher().generic_visit_with(visitor); + } +} + +impl< + Visitor, + Key: GenericTypeVisitable, + Value: GenericTypeVisitable, + S: GenericTypeVisitable, +> GenericTypeVisitable for indexmap::IndexMap +{ + fn generic_visit_with(&self, visitor: &mut Visitor) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + self.hasher().generic_visit_with(visitor); + } +} + +impl, S: GenericTypeVisitable> GenericTypeVisitable + for indexmap::IndexSet +{ + fn generic_visit_with(&self, visitor: &mut V) { + self.iter().for_each(|it| it.generic_visit_with(visitor)); + self.hasher().generic_visit_with(visitor); + } +} + +macro_rules! trivial_impls { + ( $($ty:ty),* $(,)? ) => { + $( + impl + GenericTypeVisitable for $ty + { + fn generic_visit_with(&self, _visitor: &mut V) {} + } + )* + }; +} + +trivial_impls!( + (), + rustc_ast_ir::Mutability, + bool, + i8, + i16, + i32, + i64, + i128, + isize, + u8, + u16, + u32, + u64, + u128, + usize, + crate::PredicatePolarity, + crate::BoundConstness, + crate::AliasRelationDirection, + crate::DebruijnIndex, + crate::solve::Certainty, + crate::UniverseIndex, + crate::BoundVar, + crate::InferTy, + crate::IntTy, + crate::UintTy, + crate::FloatTy, + crate::InferConst, + crate::RegionVid, + rustc_hash::FxBuildHasher, + crate::TypeFlags, + crate::solve::GoalSource, +); diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 31fe0aac1de4..dc286ee7f625 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -44,6 +44,8 @@ mod const_kind; mod flags; mod fold; mod generic_arg; +#[cfg(not(feature = "nightly"))] +mod generic_visit; mod infer_ctxt; mod interner; mod opaque_ty; @@ -67,6 +69,8 @@ pub use const_kind::*; pub use flags::*; pub use fold::*; pub use generic_arg::*; +#[cfg(not(feature = "nightly"))] +pub use generic_visit::*; pub use infer_ctxt::*; pub use interner::*; pub use opaque_ty::*; @@ -75,6 +79,7 @@ pub use predicate::*; pub use predicate_kind::*; pub use region_kind::*; pub use rustc_ast_ir::{FloatTy, IntTy, Movability, Mutability, Pinnedness, UintTy}; +use rustc_type_ir_macros::GenericTypeVisitable; pub use ty_info::*; pub use ty_kind::*; pub use upcast::*; @@ -213,7 +218,7 @@ pub fn debug_bound_var( } } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(Decodable, Encodable, HashStable_NoContext))] #[cfg_attr(feature = "nightly", rustc_pass_by_value)] pub enum Variance { diff --git a/compiler/rustc_type_ir/src/opaque_ty.rs b/compiler/rustc_type_ir/src/opaque_ty.rs index b6fb9f28930f..870e15af519d 100644 --- a/compiler/rustc_type_ir/src/opaque_ty.rs +++ b/compiler/rustc_type_ir/src/opaque_ty.rs @@ -1,13 +1,13 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::inherent::*; use crate::{self as ty, Interner}; #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/pattern.rs b/compiler/rustc_type_ir/src/pattern.rs index 1f8b7158cb11..d0febc739db4 100644 --- a/compiler/rustc_type_ir/src/pattern.rs +++ b/compiler/rustc_type_ir/src/pattern.rs @@ -1,12 +1,14 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use crate::Interner; #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 3e32a7788546..42d204a8b094 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -6,7 +6,9 @@ use derive_where::derive_where; use rustc_macros::{ Decodable, Decodable_NoContext, Encodable, Encodable_NoContext, HashStable_NoContext, }; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use crate::inherent::*; use crate::lift::Lift; @@ -17,7 +19,7 @@ use crate::{self as ty, Interner}; /// `A: 'region` #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, A)] #[derive_where(Copy; I: Interner, A: Copy)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -52,7 +54,7 @@ where /// Trait references also appear in object types like `Foo`, but in /// that case the `Self` parameter is absent from the generic parameters. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -129,7 +131,7 @@ impl ty::Binder> { } #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -274,7 +276,7 @@ impl fmt::Display for PredicatePolarity { } #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -291,6 +293,13 @@ pub enum ExistentialPredicate { impl Eq for ExistentialPredicate {} impl ty::Binder> { + pub fn def_id(&self) -> I::DefId { + match self.skip_binder() { + ExistentialPredicate::Trait(tr) => tr.def_id.into(), + ExistentialPredicate::Projection(p) => p.def_id.into(), + ExistentialPredicate::AutoTrait(did) => did.into(), + } + } /// Given an existential predicate like `?Self: PartialEq` (e.g., derived from `dyn PartialEq`), /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self` /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq`, in our example). @@ -325,7 +334,7 @@ impl ty::Binder> { /// The generic parameters don't include the erased `Self`, only trait /// type and lifetime parameters (`[X, Y]` and `['a, 'b]` above). #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -394,7 +403,7 @@ impl ty::Binder> { /// A `ProjectionPredicate` for an `ExistentialTraitRef`. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -552,7 +561,7 @@ impl From for AliasTermKind { /// * For an inherent projection, this would be `Ty::N<...>`. /// * For an opaque type, there is no explicit syntax. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -764,7 +773,7 @@ impl From> for AliasTerm { /// Form #2 eventually yields one of these `ProjectionPredicate` /// instances to normalize the LHS. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -827,7 +836,7 @@ impl fmt::Debug for ProjectionPredicate { /// Used by the new solver to normalize an alias. This always expects the `term` to /// be an unconstrained inference variable which is used as the output. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -864,7 +873,7 @@ impl fmt::Debug for NormalizesTo { } #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -910,7 +919,7 @@ impl ty::Binder> { /// whether the `a` type is the type that we should label as "expected" when /// presenting user diagnostics. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -925,7 +934,7 @@ impl Eq for SubtypePredicate {} /// Encodes that we have to coerce *from* the `a` type to the `b` type. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 785d41929cfc..445e85a888fc 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -3,14 +3,14 @@ use std::fmt; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::{self as ty, Interner}; /// A clause is something that can appear in where bounds or be inferred /// by implied bounds. #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -58,7 +58,7 @@ pub enum ClauseKind { impl Eq for ClauseKind {} #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 1e8585cf52ce..9acf9ff8557e 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -5,6 +5,7 @@ use derive_where::derive_where; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; +use rustc_type_ir_macros::GenericTypeVisitable; use self::RegionKind::*; use crate::{BoundVarIndexKind, Interner}; @@ -126,6 +127,7 @@ rustc_index::newtype_index! { /// [2]: https://smallcultfollowing.com/babysteps/blog/2013/11/04/intermingled-parameter-lists/ /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext))] pub enum RegionKind { /// A region parameter; for example `'a` in `impl<'a> Trait for &'a ()`. diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs index 3af2f8dbaf27..20432687e5f6 100644 --- a/compiler/rustc_type_ir/src/solve/inspect.rs +++ b/compiler/rustc_type_ir/src/solve/inspect.rs @@ -18,7 +18,7 @@ //! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use derive_where::derive_where; -use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{GenericTypeVisitable, TypeFoldable_Generic, TypeVisitable_Generic}; use crate::solve::{CandidateSource, Certainty, Goal, GoalSource, QueryResult}; use crate::{Canonical, CanonicalVarValues, Interner}; @@ -31,7 +31,7 @@ use crate::{Canonical, CanonicalVarValues, Interner}; /// inference variables from a nested `InferCtxt`. #[derive_where(Clone, PartialEq, Hash, Debug; I: Interner, T)] #[derive_where(Copy; I: Interner, T: Copy)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] pub struct State { pub var_values: CanonicalVarValues, pub data: T, @@ -87,7 +87,7 @@ pub enum ProbeStep { /// the final result of the current goal - via [ProbeKind::Root] - we also /// store the [QueryResult]. #[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] pub enum ProbeKind { /// The root inference context while proving a goal. Root { result: QueryResult }, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index ede23d599832..72b7df22b30d 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -5,7 +5,9 @@ use std::hash::Hash; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use crate::lang_items::SolverTraitLangItem; use crate::search_graph::PathKind; @@ -33,7 +35,7 @@ pub struct NoSolution; /// we're currently typechecking while the `predicate` is some trait bound. #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, P)] #[derive_where(Copy; I: Interner, P: Copy)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -95,7 +97,7 @@ pub enum GoalSource { #[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, Goal)] #[derive_where(Copy; I: Interner, Goal: Copy)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -206,7 +208,7 @@ pub enum ParamEnvSource { } #[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] pub enum AliasBoundKind { /// Alias bound from the self type of a projection SelfBounds, @@ -235,7 +237,7 @@ pub enum BuiltinImplSource { } #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct Response { pub certainty: Certainty, @@ -248,7 +250,7 @@ impl Eq for Response {} /// Additional constraints returned on success. #[derive_where(Clone, Hash, PartialEq, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct ExternalConstraintsData { pub region_constraints: Vec>, @@ -267,7 +269,7 @@ impl ExternalConstraintsData { } #[derive_where(Clone, Hash, PartialEq, Debug, Default; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct NestedNormalizationGoals(pub Vec<(GoalSource, Goal)>); diff --git a/compiler/rustc_type_ir/src/ty_info.rs b/compiler/rustc_type_ir/src/ty_info.rs index a99956674507..53994b5dbf63 100644 --- a/compiler/rustc_type_ir/src/ty_info.rs +++ b/compiler/rustc_type_ir/src/ty_info.rs @@ -6,6 +6,7 @@ use std::ops::Deref; use rustc_data_structures::fingerprint::Fingerprint; #[cfg(feature = "nightly")] use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_type_ir_macros::GenericTypeVisitable; use crate::{DebruijnIndex, TypeFlags}; @@ -16,7 +17,7 @@ use crate::{DebruijnIndex, TypeFlags}; /// StableHash::ZERO for the hash, in which case the hash gets computed each time. /// This is useful if you have values that you intern but never (can?) use for stable /// hashing. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, GenericTypeVisitable)] pub struct WithCachedTypeInfo { pub internee: T, diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index bb80e2cf46d4..34bb3cd8a6c3 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -8,7 +8,9 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir::data_structures::{NoError, UnifyKey, UnifyValue}; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use self::TyKind::*; pub use self::closure::*; @@ -20,6 +22,7 @@ use crate::{self as ty, BoundVarIndexKind, FloatTy, IntTy, Interner, UintTy}; mod closure; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -56,6 +59,7 @@ impl AliasTyKind { /// converted to this representation using `::lower_ty`. #[cfg_attr(feature = "nightly", rustc_diagnostic_item = "IrTyKind")] #[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)] +#[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) @@ -391,7 +395,7 @@ impl fmt::Debug for TyKind { /// * For an inherent projection, this would be `Ty::N<...>`. /// * For an opaque type, there is no explicit syntax. #[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] #[cfg_attr( feature = "nightly", derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) @@ -713,7 +717,7 @@ impl fmt::Debug for InferTy { feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] pub struct TypeAndMut { pub ty: I::Ty, pub mutbl: Mutability, @@ -726,7 +730,7 @@ impl Eq for TypeAndMut {} feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct FnSig { pub inputs_and_output: I::Tys, pub c_variadic: bool, @@ -839,7 +843,7 @@ impl fmt::Debug for FnSig { // impls in this crate for `Binder`. #[derive_where(Clone, Copy, PartialEq, Hash; I: Interner)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct UnsafeBinderInner(ty::Binder); impl Eq for UnsafeBinderInner {} @@ -905,7 +909,7 @@ where feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct FnSigTys { pub inputs_and_output: I::Tys, } @@ -959,7 +963,7 @@ impl ty::Binder> { feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct FnHeader { pub c_variadic: bool, pub safety: I::Safety, @@ -973,7 +977,7 @@ impl Eq for FnHeader {} feature = "nightly", derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) )] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct CoroutineWitnessTypes { pub types: I::Tys, pub assumptions: I::RegionAssumptions, diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 4d9fd6040d8b..e8f94c8e7cc9 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -1,7 +1,9 @@ use std::ops::ControlFlow; use derive_where::derive_where; -use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use rustc_type_ir_macros::{ + GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, +}; use crate::data_structures::DelayedMap; use crate::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region}; @@ -102,7 +104,7 @@ use crate::{self as ty, Interner}; /// * `GR`: The "return type", which is the type of value returned upon /// completion of the coroutine. #[derive_where(Clone, Copy, PartialEq, Hash, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct ClosureArgs { /// Lifetime and type parameters from the enclosing function, /// concatenated with a tuple containing the types of the upvars. @@ -206,7 +208,7 @@ impl ClosureArgs { } #[derive_where(Clone, Copy, PartialEq, Hash, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct CoroutineClosureArgs { pub args: I::GenericArgs, } @@ -354,7 +356,7 @@ impl TypeVisitor for HasRegionsBoundAt { } #[derive_where(Clone, Copy, PartialEq, Hash, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] pub struct CoroutineClosureSignature { pub tupled_inputs_ty: I::Ty, pub resume_ty: I::Ty, @@ -549,7 +551,7 @@ impl TypeFolder for FoldEscapingRegions { } #[derive_where(Clone, Copy, PartialEq, Hash, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic)] pub struct GenSig { pub resume_ty: I::Ty, pub yield_ty: I::Ty, @@ -559,7 +561,7 @@ pub struct GenSig { impl Eq for GenSig {} /// Similar to `ClosureArgs`; see the above documentation for more. #[derive_where(Clone, Copy, PartialEq, Hash, Debug; I: Interner)] -#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)] +#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)] pub struct CoroutineArgs { pub args: I::GenericArgs, } diff --git a/compiler/rustc_type_ir_macros/Cargo.toml b/compiler/rustc_type_ir_macros/Cargo.toml index 14bffa403a86..910eef5e21f4 100644 --- a/compiler/rustc_type_ir_macros/Cargo.toml +++ b/compiler/rustc_type_ir_macros/Cargo.toml @@ -6,6 +6,9 @@ edition = "2024" [lib] proc-macro = true +[features] +nightly = [] + [dependencies] # tidy-alphabetical-start proc-macro2 = "1" diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs index 37617b789dba..8df10b6a9ecc 100644 --- a/compiler/rustc_type_ir_macros/src/lib.rs +++ b/compiler/rustc_type_ir_macros/src/lib.rs @@ -12,6 +12,10 @@ decl_derive!( decl_derive!( [Lift_Generic] => lift_derive ); +#[cfg(not(feature = "nightly"))] +decl_derive!( + [GenericTypeVisitable] => customizable_type_visitable_derive +); fn has_ignore_attr(attrs: &[Attribute], name: &'static str, meta: &'static str) -> bool { let mut ignored = false; @@ -211,3 +215,39 @@ fn lift(mut ty: syn::Type) -> syn::Type { ty } + +#[cfg(not(feature = "nightly"))] +fn customizable_type_visitable_derive( + mut s: synstructure::Structure<'_>, +) -> proc_macro2::TokenStream { + if let syn::Data::Union(_) = s.ast().data { + panic!("cannot derive on union") + } + + s.add_impl_generic(parse_quote!(__V)); + s.add_bounds(synstructure::AddBounds::Fields); + let body_visit = s.each(|bind| { + quote! { + ::rustc_type_ir::GenericTypeVisitable::<__V>::generic_visit_with(#bind, __visitor); + } + }); + s.bind_with(|_| synstructure::BindStyle::Move); + + s.bound_impl( + quote!(::rustc_type_ir::GenericTypeVisitable<__V>), + quote! { + fn generic_visit_with( + &self, + __visitor: &mut __V + ) { + match *self { #body_visit } + } + }, + ) +} + +#[cfg(feature = "nightly")] +#[proc_macro_derive(GenericTypeVisitable)] +pub fn customizable_type_visitable_derive(_: proc_macro::TokenStream) -> proc_macro::TokenStream { + proc_macro::TokenStream::new() +} diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index f714a87c1868..60758551cc04 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2253,7 +2253,7 @@ impl + ?Sized, A: Allocator> AsyncFn for Box #[unstable(feature = "coerce_unsized", issue = "18598")] impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Box {} // It is quite crucial that we only allow the `Global` allocator here. diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index ea9d5b128ebe..0e01d0d19e7a 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -2053,7 +2053,7 @@ impl VecDeque { /// assert_eq!(deque, [1, 2, 3, 4]); /// assert_eq!(deque.pop_front_if(pred), None); /// ``` - #[stable(feature = "vec_deque_pop_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_deque_pop_if", since = "1.93.0")] pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let first = self.front_mut()?; if predicate(first) { self.pop_front() } else { None } @@ -2075,7 +2075,7 @@ impl VecDeque { /// assert_eq!(deque, [0, 1, 2, 3]); /// assert_eq!(deque.pop_back_if(pred), None); /// ``` - #[stable(feature = "vec_deque_pop_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_deque_pop_if", since = "1.93.0")] pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let last = self.back_mut()?; if predicate(last) { self.pop_back() } else { None } diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index 4d6fe220a09a..3d7c580be8c9 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -602,7 +602,7 @@ pub use core::fmt::{DebugAsHex, FormattingOptions, Sign}; pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{Formatter, Result, Write}; -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] pub use core::fmt::{FromFn, from_fn}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{LowerExp, UpperExp}; diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index a3ebbbbaae05..8a72748668cc 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2417,14 +2417,14 @@ impl Deref for Rc { } } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Rc {} //#[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UniqueRc {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Weak {} #[unstable(feature = "deref_pure_trait", issue = "87121")] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 64abdca32d21..e400fc9e00ed 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -934,7 +934,7 @@ impl String { /// assert_eq!(rebuilt, "hello"); /// ``` #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_into_raw_parts", since = "1.93.0")] pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { self.vec.into_raw_parts() } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 6a49017a8276..4180fe91cb55 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2423,10 +2423,10 @@ impl Deref for Arc { } } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Arc {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Weak {} #[unstable(feature = "deref_pure_trait", issue = "87121")] @@ -4852,7 +4852,7 @@ impl Deref for UniqueArc { } // #[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UniqueArc {} #[unstable(feature = "unique_rc_arc", issue = "112566")] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index d2bc5d9bce39..f8a96e358d6d 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -834,7 +834,7 @@ impl Vec { /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_into_raw_parts", since = "1.93.0")] pub fn into_raw_parts(self) -> (*mut T, usize, usize) { let mut me = ManuallyDrop::new(self); (me.as_mut_ptr(), me.len(), me.capacity()) diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index c8faecfcb2cc..38995cf0f0ff 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -1,6 +1,6 @@ name: CI on: - push: { branches: [master] } + push: { branches: [main] } pull_request: concurrency: @@ -89,7 +89,7 @@ jobs: - target: x86_64-unknown-linux-gnu os: ubuntu-24.04 - target: x86_64-apple-darwin - os: macos-13 + os: macos-15-intel - target: i686-pc-windows-msvc os: windows-2025 - target: x86_64-pc-windows-msvc @@ -239,6 +239,8 @@ jobs: include: - target: x86_64-unknown-linux-gnu os: ubuntu-24.04 + - target: aarch64-unknown-linux-gnu + os: ubuntu-24.04-arm runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@master @@ -247,13 +249,13 @@ jobs: - name: Set up dependencies run: | sudo apt-get update - sudo apt-get install -y valgrind gdb libc6-dbg # Needed for iai-callgrind + sudo apt-get install -y valgrind gdb libc6-dbg # Needed for gungraun rustup update "$BENCHMARK_RUSTC" --no-self-update rustup default "$BENCHMARK_RUSTC" - # Install the version of iai-callgrind-runner that is specified in Cargo.toml - iai_version="$(cargo metadata --format-version=1 --features icount | - jq -r '.packages[] | select(.name == "iai-callgrind").version')" - cargo binstall -y iai-callgrind-runner --version "$iai_version" + # Install the version of gungraun-runner that is specified in Cargo.toml + gungraun_version="$(cargo metadata --format-version=1 --features icount | + jq -r '.packages[] | select(.name == "gungraun").version')" + cargo binstall -y gungraun-runner --version "$gungraun_version" sudo apt-get install valgrind - uses: Swatinem/rust-cache@v2 with: diff --git a/library/compiler-builtins/.github/workflows/publish.yaml b/library/compiler-builtins/.github/workflows/publish.yaml index 85a33c039d2a..d6f1dc398e8e 100644 --- a/library/compiler-builtins/.github/workflows/publish.yaml +++ b/library/compiler-builtins/.github/workflows/publish.yaml @@ -5,7 +5,7 @@ permissions: contents: write on: - push: { branches: [master] } + push: { branches: [main] } jobs: release-plz: diff --git a/library/compiler-builtins/.github/workflows/rustc-pull.yml b/library/compiler-builtins/.github/workflows/rustc-pull.yml index ad7693e17b0e..617db14f46ee 100644 --- a/library/compiler-builtins/.github/workflows/rustc-pull.yml +++ b/library/compiler-builtins/.github/workflows/rustc-pull.yml @@ -17,7 +17,7 @@ jobs: zulip-stream-id: 219381 zulip-topic: 'compiler-builtins subtree sync automation' zulip-bot-email: "compiler-builtins-ci-bot@rust-lang.zulipchat.com" - pr-base-branch: master + pr-base-branch: main branch-name: rustc-pull secrets: zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }} diff --git a/library/compiler-builtins/.gitignore b/library/compiler-builtins/.gitignore index f12b871c2f78..abe346659d4c 100644 --- a/library/compiler-builtins/.gitignore +++ b/library/compiler-builtins/.gitignore @@ -9,6 +9,7 @@ compiler-rt # Benchmark cache baseline-* iai-home +gungraun-home # Temporary files *.bk diff --git a/library/compiler-builtins/CONTRIBUTING.md b/library/compiler-builtins/CONTRIBUTING.md index 9ae4f893c60d..f74d3f8ba127 100644 --- a/library/compiler-builtins/CONTRIBUTING.md +++ b/library/compiler-builtins/CONTRIBUTING.md @@ -150,8 +150,8 @@ cargo bench --no-default-features \ ``` There are also benchmarks that check instruction count behind the `icount` -feature. These require [`iai-callgrind-runner`] (via Cargo) and [Valgrind] -to be installed, which means these only run on limited platforms. +feature. These require [`gungraun-runner`] (via Cargo) and [Valgrind] to be +installed, which means these only run on limited platforms. Instruction count benchmarks are run as part of CI to flag performance regresions. @@ -163,7 +163,7 @@ cargo bench --no-default-features \ --bench icount --bench mem_icount ``` -[`iai-callgrind-runner`]: https://crates.io/crates/iai-callgrind-runner +[`gungraun-runner`]: https://crates.io/crates/gungraun-runner [Valgrind]: https://valgrind.org/ ## Subtree synchronization diff --git a/library/compiler-builtins/Cargo.toml b/library/compiler-builtins/Cargo.toml index 956d738f3b1f..8501f4e630b5 100644 --- a/library/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/Cargo.toml @@ -51,5 +51,6 @@ codegen-units = 1 lto = "fat" [profile.bench] -# Required for iai-callgrind +# Required for gungraun debug = true +strip = false diff --git a/library/compiler-builtins/PUBLISHING.md b/library/compiler-builtins/PUBLISHING.md index 3df682ab04a4..c521910641f5 100644 --- a/library/compiler-builtins/PUBLISHING.md +++ b/library/compiler-builtins/PUBLISHING.md @@ -5,7 +5,7 @@ It's not great, but it works for now. PRs to improve this process would be greatly appreciated! 1. Make sure you've got a clean working tree and it's updated with the latest - changes on `master` + changes on `main` 2. Edit `Cargo.toml` to bump the version number 3. Commit this change 4. Run `git tag` to create a tag for this version diff --git a/library/compiler-builtins/builtins-shim/Cargo.toml b/library/compiler-builtins/builtins-shim/Cargo.toml index 707ebdbc77b2..ac77224f5ce1 100644 --- a/library/compiler-builtins/builtins-shim/Cargo.toml +++ b/library/compiler-builtins/builtins-shim/Cargo.toml @@ -11,7 +11,12 @@ [package] name = "compiler_builtins" version = "0.1.160" -authors = ["Jorge Aparicio "] +authors = [ + "Alex Crichton ", + "Amanieu d'Antras ", + "Jorge Aparicio ", + "Trevor Gross ", +] description = "Compiler intrinsics used by the Rust compiler." repository = "https://github.com/rust-lang/compiler-builtins" license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml index 00a9d8579d11..9346ea65420b 100644 --- a/library/compiler-builtins/builtins-test/Cargo.toml +++ b/library/compiler-builtins/builtins-test/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "builtins-test" version = "0.1.0" -authors = ["Alex Crichton "] edition = "2024" publish = false license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" @@ -14,7 +13,7 @@ rand_xoshiro = "0.7" # To compare float builtins against rustc_apfloat = "0.2.3" # Really a dev dependency, but dev dependencies can't be optional -iai-callgrind = { version = "0.15.2", optional = true } +gungraun = { version = "0.17.0", optional = true } [dependencies.compiler_builtins] path = "../builtins-shim" @@ -46,8 +45,8 @@ no-sys-f16-f64-convert = [] # Skip tests that rely on f16 symbols being available on the system no-sys-f16 = ["no-sys-f16-f64-convert"] -# Enable icount benchmarks (requires iai-callgrind and valgrind) -icount = ["dep:iai-callgrind"] +# Enable icount benchmarks (requires gungraun-runner and valgrind locally) +icount = ["dep:gungraun"] # Enable report generation without bringing in more dependencies by default benchmarking-reports = ["criterion/plotters", "criterion/html_reports"] diff --git a/library/compiler-builtins/builtins-test/benches/mem_icount.rs b/library/compiler-builtins/builtins-test/benches/mem_icount.rs index bd88cf80c7de..37595e825843 100644 --- a/library/compiler-builtins/builtins-test/benches/mem_icount.rs +++ b/library/compiler-builtins/builtins-test/benches/mem_icount.rs @@ -1,11 +1,11 @@ -//! Benchmarks that use Callgrind (via `iai_callgrind`) to report instruction count metrics. This +//! Benchmarks that use Callgrind (via `gungraun`) to report instruction count metrics. This //! is stable enough to be tested in CI. use std::hint::black_box; use std::{ops, slice}; use compiler_builtins::mem::{memcmp, memcpy, memmove, memset}; -use iai_callgrind::{library_benchmark, library_benchmark_group, main}; +use gungraun::{library_benchmark, library_benchmark_group, main}; const PAGE_SIZE: usize = 0x1000; // 4 kiB const MAX_ALIGN: usize = 512; // assume we may use avx512 operations one day @@ -108,7 +108,7 @@ mod mcpy { ], setup = setup, )] - fn bench((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { + fn bench_cpy((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { unsafe { black_box(memcpy( black_box(dst.as_mut_ptr()), @@ -118,7 +118,7 @@ mod mcpy { } } - library_benchmark_group!(name = memcpy; benchmarks = bench); + library_benchmark_group!(name = memcpy; benchmarks = bench_cpy); } mod mset { @@ -157,7 +157,7 @@ mod mset { ], setup = setup, )] - fn bench((len, mut dst): (usize, AlignedSlice)) { + fn bench_set((len, mut dst): (usize, AlignedSlice)) { unsafe { black_box(memset( black_box(dst.as_mut_ptr()), @@ -167,7 +167,7 @@ mod mset { } } - library_benchmark_group!(name = memset; benchmarks = bench); + library_benchmark_group!(name = memset; benchmarks = bench_set); } mod mcmp { @@ -225,7 +225,7 @@ mod mcmp { ], setup = setup )] - fn bench((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { + fn bench_cmp((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { unsafe { black_box(memcmp( black_box(dst.as_mut_ptr()), @@ -235,7 +235,7 @@ mod mcmp { } } - library_benchmark_group!(name = memcmp; benchmarks = bench); + library_benchmark_group!(name = memcmp; benchmarks = bench_cmp); } mod mmove { @@ -384,7 +384,7 @@ mod mmove { ], setup = setup_forward )] - fn forward((len, spread, mut buf): (usize, usize, AlignedSlice)) { + fn forward_move((len, spread, mut buf): (usize, usize, AlignedSlice)) { // Test moving from the start of the buffer toward the end unsafe { black_box(memmove( @@ -478,7 +478,7 @@ mod mmove { ], setup = setup_backward )] - fn backward((len, spread, mut buf): (usize, usize, AlignedSlice)) { + fn backward_move((len, spread, mut buf): (usize, usize, AlignedSlice)) { // Test moving from the end of the buffer toward the start unsafe { black_box(memmove( @@ -489,7 +489,7 @@ mod mmove { } } - library_benchmark_group!(name = memmove; benchmarks = forward, backward); + library_benchmark_group!(name = memmove; benchmarks = forward_move, backward_move); } use mcmp::memcmp; diff --git a/library/compiler-builtins/builtins-test/tests/lse.rs b/library/compiler-builtins/builtins-test/tests/lse.rs index 5d59fbb7f44d..56891be8a8ac 100644 --- a/library/compiler-builtins/builtins-test/tests/lse.rs +++ b/library/compiler-builtins/builtins-test/tests/lse.rs @@ -19,7 +19,11 @@ mod cas { let mut target = expected.wrapping_add(10); assert_eq!( unsafe { - compiler_builtins::aarch64_linux::$name::$name(expected, new, &mut target) + compiler_builtins::aarch64_outline_atomics::$name::$name( + expected, + new, + &mut target, + ) }, expected.wrapping_add(10), "return value should always be the previous value", @@ -33,7 +37,11 @@ mod cas { target = expected; assert_eq!( unsafe { - compiler_builtins::aarch64_linux::$name::$name(expected, new, &mut target) + compiler_builtins::aarch64_outline_atomics::$name::$name( + expected, + new, + &mut target, + ) }, expected ); @@ -54,7 +62,9 @@ mod swap { builtins_test::fuzz_2(10000, |left: super::int_ty!($bytes), mut right| { let orig_right = right; assert_eq!( - unsafe { compiler_builtins::aarch64_linux::$name::$name(left, &mut right) }, + unsafe { + compiler_builtins::aarch64_outline_atomics::$name::$name(left, &mut right) + }, orig_right ); assert_eq!(left, right); @@ -74,7 +84,7 @@ macro_rules! test_op { let mut target = old; let op: fn(super::int_ty!($bytes), super::int_ty!($bytes)) -> _ = $($op)*; let expected = op(old, val); - assert_eq!(old, unsafe { compiler_builtins::aarch64_linux::$name::$name(val, &mut target) }, "{} should return original value", stringify!($name)); + assert_eq!(old, unsafe { compiler_builtins::aarch64_outline_atomics::$name::$name(val, &mut target) }, "{} should return original value", stringify!($name)); assert_eq!(expected, target, "{} should store to target", stringify!($name)); }); } diff --git a/library/compiler-builtins/ci/bench-icount.sh b/library/compiler-builtins/ci/bench-icount.sh index 12228b9da971..6d92b50a6dae 100755 --- a/library/compiler-builtins/ci/bench-icount.sh +++ b/library/compiler-builtins/ci/bench-icount.sh @@ -10,35 +10,43 @@ if [ -z "$target" ]; then target="$host_target" fi -iai_home="iai-home" +# Print machine information +uname -a +lscpu || true + +gungraun_home="gungraun-home" # Use the arch as a tag to disambiguate artifacts tag="$(echo "$target" | cut -d'-' -f1)" -# Download the baseline from master +# Download the baseline from main ./ci/ci-util.py locate-baseline --download --extract --tag "$tag" +# FIXME: migration from iai-named baselines to gungraun, can be dropped +# after the first run with gungraun. +[ -d "iai-home" ] && mv "iai-home" "$gungraun_home" + # Run benchmarks once function run_icount_benchmarks() { cargo_args=( - "--bench" "icount" + "--bench" "*icount*" "--no-default-features" "--features" "unstable,unstable-float,icount" ) - iai_args=( - "--home" "$(pwd)/$iai_home" - "--callgrind-limits=ir=5.0" + gungraun_args=( + "--home" "$(pwd)/$gungraun_home" + "--callgrind-limits=ir=5.0%" "--save-summary" ) - # Parse `cargo_arg0 cargo_arg1 -- iai_arg0 iai_arg1` syntax - parsing_iai_args=0 + # Parse `cargo_arg0 cargo_arg1 -- gungraun_arg0 gungraun_arg1` syntax + parsing_gungraun_args=0 while [ "$#" -gt 0 ]; do - if [ "$parsing_iai_args" == "1" ]; then - iai_args+=("$1") + if [ "$parsing_gungraun_args" == "1" ]; then + gungraun_args+=("$1") elif [ "$1" == "--" ]; then - parsing_iai_args=1 + parsing_gungraun_args=1 else cargo_args+=("$1") fi @@ -46,9 +54,9 @@ function run_icount_benchmarks() { shift done - # Run iai-callgrind benchmarks. Do this in a subshell with `&& true` to - # capture rather than exit on error. - (cargo bench "${cargo_args[@]}" -- "${iai_args[@]}") && true + # Run gungraun benchmarks. Do this in a subshell with `&& true` to capture + # rather than exit on error. + (cargo bench "${cargo_args[@]}" -- "${gungraun_args[@]}") && true exit_code="$?" if [ "$exit_code" -eq 0 ]; then @@ -68,4 +76,4 @@ run_icount_benchmarks -- --save-baseline=hardfloat # Name and tar the new baseline name="baseline-icount-$tag-$(date -u +'%Y%m%d%H%M')-${GITHUB_SHA:0:12}" echo "BASELINE_NAME=$name" >>"$GITHUB_ENV" -tar cJf "$name.tar.xz" "$iai_home" +tar cJf "$name.tar.xz" "$gungraun_home" diff --git a/library/compiler-builtins/ci/ci-util.py b/library/compiler-builtins/ci/ci-util.py index c1db17c6c901..ef9ce455178e 100755 --- a/library/compiler-builtins/ci/ci-util.py +++ b/library/compiler-builtins/ci/ci-util.py @@ -38,7 +38,7 @@ USAGE = cleandoc( `--tag` can be specified to look for artifacts with a specific tag, such as for a specific architecture. - Note that `--extract` will overwrite files in `iai-home`. + Note that `--extract` will overwrite files in `gungraun-home`. handle-bench-regressions PR_NUMBER Exit with success if the pull request contains a line starting with @@ -49,7 +49,7 @@ USAGE = cleandoc( REPO_ROOT = Path(__file__).parent.parent GIT = ["git", "-C", REPO_ROOT] -DEFAULT_BRANCH = "master" +DEFAULT_BRANCH = "main" WORKFLOW_NAME = "CI" # Workflow that generates the benchmark artifacts ARTIFACT_PREFIX = "baseline-icount*" @@ -186,7 +186,7 @@ class Context: def _init_change_list(self): """Create a list of files that have been changed. This uses GITHUB_REF if - available, otherwise a diff between `HEAD` and `master`. + available, otherwise a diff between `HEAD` and `main`. """ # For pull requests, GitHub creates a ref `refs/pull/1234/merge` (1234 being @@ -390,6 +390,7 @@ def locate_baseline(flags: list[str]) -> None: artifact_glob = f"{ARTIFACT_PREFIX}{f"-{tag}" if tag else ""}*" + # Skip checking because this will fail if the file already exists, which is fine. sp.run( ["gh", "run", "download", str(job_id), f"--pattern={artifact_glob}"], check=False, @@ -409,7 +410,17 @@ def locate_baseline(flags: list[str]) -> None: candidate_baselines.sort(reverse=True) baseline_archive = candidate_baselines[0] eprint(f"extracting {baseline_archive}") - sp.run(["tar", "xJvf", baseline_archive], check=True) + + all_paths = sp.check_output(["tar", "tJf", baseline_archive], encoding="utf8") + sp.run(["tar", "xJf", baseline_archive], check=True) + + # Print a short summary of paths, we don't use `tar v` since the list is huge + short_paths = re.findall(r"^(?:[^/\n]+/?){1,3}", all_paths, re.MULTILINE) + + print("Extracted:") + for path in sorted(set(short_paths)): + print(f"* {path}") + eprint("baseline extracted successfully") diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 8bbe136ce33e..0845861dcfe3 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -7,7 +7,12 @@ [package] name = "compiler_builtins" version = "0.1.160" -authors = ["Jorge Aparicio "] +authors = [ + "Alex Crichton ", + "Amanieu d'Antras ", + "Jorge Aparicio ", + "Trevor Gross ", +] description = "Compiler intrinsics used by the Rust compiler." repository = "https://github.com/rust-lang/compiler-builtins" license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" diff --git a/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs b/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs index 7841e4f33cd6..5ffe1f59b4db 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs @@ -196,13 +196,12 @@ unsafe fn u128_by_u64_div_rem(duo: u128, div: u64) -> (u64, u64) { unsafe { // divides the combined registers rdx:rax (`duo` is split into two 64 bit parts to do this) // by `div`. The quotient is stored in rax and the remainder in rdx. - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. core::arch::asm!( "div {0}", in(reg) div, inlateout("rax") duo_lo => quo, inlateout("rdx") duo_hi => rem, - options(att_syntax, pure, nomem, nostack) + options(pure, nomem, nostack), ); } (quo, rem) @@ -283,13 +282,12 @@ unsafe fn u64_by_u32_div_rem(duo: u64, div: u32) -> (u32, u32) { unsafe { // divides the combined registers rdx:rax (`duo` is split into two 32 bit parts to do this) // by `div`. The quotient is stored in rax and the remainder in rdx. - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. core::arch::asm!( "div {0}", in(reg) div, inlateout("rax") duo_lo => quo, inlateout("rdx") duo_hi => rem, - options(att_syntax, pure, nomem, nostack) + options(pure, nomem, nostack), ); } (quo, rem) diff --git a/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs b/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs index fb29eb11b231..bf36a286ac95 100644 --- a/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs +++ b/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs @@ -22,13 +22,12 @@ use core::{intrinsics, mem}; #[inline(always)] #[cfg(target_feature = "ermsb")] pub unsafe fn copy_forward(dest: *mut u8, src: *const u8, count: usize) { - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. - core::arch::asm!( - "repe movsb (%rsi), (%rdi)", + asm!( + "rep movsb [rdi], [rsi]", inout("rcx") count => _, inout("rdi") dest => _, inout("rsi") src => _, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); } @@ -42,21 +41,21 @@ pub unsafe fn copy_forward(mut dest: *mut u8, mut src: *const u8, count: usize) inout("ecx") pre_byte_count => _, inout("rdi") dest => dest, inout("rsi") src => src, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep movsq", inout("rcx") qword_count => _, inout("rdi") dest => dest, inout("rsi") src => src, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep movsb", inout("ecx") byte_count => _, inout("rdi") dest => _, inout("rsi") src => _, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); } @@ -67,14 +66,13 @@ pub unsafe fn copy_backward(dest: *mut u8, src: *const u8, count: usize) { asm!( "std", "rep movsb", - "sub $7, %rsi", - "sub $7, %rdi", - "mov {qword_count:r}, %rcx", + "sub rsi, 7", + "sub rdi, 7", + "mov rcx, {qword_count:r}", "rep movsq", - "test {pre_byte_count:e}, {pre_byte_count:e}", - "add $7, %rsi", - "add $7, %rdi", - "mov {pre_byte_count:e}, %ecx", + "add rsi, 7", + "add rdi, 7", + "mov ecx, {pre_byte_count:e}", "rep movsb", "cld", pre_byte_count = in(reg) pre_byte_count, @@ -82,21 +80,19 @@ pub unsafe fn copy_backward(dest: *mut u8, src: *const u8, count: usize) { inout("ecx") byte_count => _, inout("rdi") dest.add(count - 1) => _, inout("rsi") src.add(count - 1) => _, - // We modify flags, but we restore it afterwards - options(att_syntax, nostack, preserves_flags) + options(nostack) ); } #[inline(always)] #[cfg(target_feature = "ermsb")] pub unsafe fn set_bytes(dest: *mut u8, c: u8, count: usize) { - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. - core::arch::asm!( - "repe stosb %al, (%rdi)", + asm!( + "rep stosb [rdi], al", inout("rcx") count => _, inout("rdi") dest => _, inout("al") c => _, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ) } @@ -111,21 +107,21 @@ pub unsafe fn set_bytes(mut dest: *mut u8, c: u8, count: usize) { inout("ecx") pre_byte_count => _, inout("rdi") dest => dest, in("rax") c, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep stosq", inout("rcx") qword_count => _, inout("rdi") dest => dest, in("rax") c, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep stosb", inout("ecx") byte_count => _, inout("rdi") dest => _, in("rax") c, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); } @@ -212,10 +208,10 @@ pub unsafe fn c_string_length(mut s: *const core::ffi::c_char) -> usize { let x = { let r; asm!( - "movdqa ({addr:r}), {dest}", + "movdqa {dest}, [{addr:r}]", addr = in(reg) s, dest = out(xmm_reg) r, - options(att_syntax, nostack), + options(nostack, preserves_flags), ); r }; @@ -232,10 +228,10 @@ pub unsafe fn c_string_length(mut s: *const core::ffi::c_char) -> usize { let x = { let r; asm!( - "movdqa ({addr:r}), {dest}", + "movdqa {dest}, [{addr:r}]", addr = in(reg) s, dest = out(xmm_reg) r, - options(att_syntax, nostack), + options(nostack, preserves_flags), ); r }; @@ -277,10 +273,10 @@ pub unsafe fn c_string_length(mut s: *const core::ffi::c_char) -> usize { let mut cs = { let r: u64; asm!( - "mov ({addr}), {dest}", + "mov {dest}, [{addr}]", addr = in(reg) s, dest = out(reg) r, - options(att_syntax, nostack), + options(nostack, preserves_flags), ); r }; diff --git a/library/compiler-builtins/compiler-builtins/src/probestack.rs b/library/compiler-builtins/compiler-builtins/src/probestack.rs index 72975485a776..1cab64ea113c 100644 --- a/library/compiler-builtins/compiler-builtins/src/probestack.rs +++ b/library/compiler-builtins/compiler-builtins/src/probestack.rs @@ -47,11 +47,11 @@ // We only define stack probing for these architectures today. #![cfg(any(target_arch = "x86_64", target_arch = "x86"))] -// Our goal here is to touch each page between %rsp+8 and %rsp+8-%rax, +// Our goal here is to touch each page between `rsp+8` and `rsp+8-rax`, // ensuring that if any pages are unmapped we'll make a page fault. // -// The ABI here is that the stack frame size is located in `%rax`. Upon -// return we're not supposed to modify `%rsp` or `%rax`. +// The ABI here is that the stack frame size is located in `rax`. Upon +// return we're not supposed to modify `rsp` or `rax`. #[cfg(target_arch = "x86_64")] #[unsafe(naked)] #[rustc_std_internal_symbol] @@ -59,50 +59,50 @@ pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc - pushq %rbp + push rbp .cfi_adjust_cfa_offset 8 - .cfi_offset %rbp, -16 - movq %rsp, %rbp - .cfi_def_cfa_register %rbp + .cfi_offset rbp, -16 + mov rbp, rsp + .cfi_def_cfa_register rbp - mov %rax,%r11 // duplicate %rax as we're clobbering %r11 + mov r11, rax // duplicate rax as we're clobbering r11 // Main loop, taken in one page increments. We're decrementing rsp by // a page each time until there's less than a page remaining. We're // guaranteed that this function isn't called unless there's more than a // page needed. // - // Note that we're also testing against `8(%rsp)` to account for the 8 + // Note that we're also testing against `[rsp + 8]` to account for the 8 // bytes pushed on the stack originally with our return address. Using - // `8(%rsp)` simulates us testing the stack pointer in the caller's + // `[rsp + 8]` simulates us testing the stack pointer in the caller's // context. - // It's usually called when %rax >= 0x1000, but that's not always true. + // It's usually called when rax >= 0x1000, but that's not always true. // Dynamic stack allocation, which is needed to implement unsized - // rvalues, triggers stackprobe even if %rax < 0x1000. - // Thus we have to check %r11 first to avoid segfault. - cmp $0x1000,%r11 + // rvalues, triggers stackprobe even if rax < 0x1000. + // Thus we have to check r11 first to avoid segfault. + cmp r11, 0x1000 jna 3f 2: - sub $0x1000,%rsp - test %rsp,8(%rsp) - sub $0x1000,%r11 - cmp $0x1000,%r11 + sub rsp, 0x1000 + test qword ptr [rsp + 8], rsp + sub r11, 0x1000 + cmp r11, 0x1000 ja 2b 3: // Finish up the last remaining stack space requested, getting the last // bits out of r11 - sub %r11,%rsp - test %rsp,8(%rsp) + sub rsp, r11 + test qword ptr [rsp + 8], rsp // Restore the stack pointer to what it previously was when entering // this function. The caller will readjust the stack pointer after we // return. - add %rax,%rsp + add rsp, rax leave - .cfi_def_cfa_register %rsp + .cfi_def_cfa_register rsp .cfi_adjust_cfa_offset -8 ", #[cfg(not(all(target_env = "sgx", target_vendor = "fortanix")))] @@ -112,14 +112,13 @@ pub unsafe extern "custom" fn __rust_probestack() { // for this target, [manually patch for LVI]. // // [manually patch for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions - pop %r11 + pop r11 lfence - jmp *%r11 + jmp r11 ", " .cfi_endproc ", - options(att_syntax) ) } @@ -135,36 +134,35 @@ pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc - push %ebp + push ebp .cfi_adjust_cfa_offset 4 - .cfi_offset %ebp, -8 - mov %esp, %ebp - .cfi_def_cfa_register %ebp - push %ecx - mov %eax,%ecx + .cfi_offset ebp, -8 + mov ebp, esp + .cfi_def_cfa_register ebp + push ecx + mov ecx, eax - cmp $0x1000,%ecx + cmp ecx, 0x1000 jna 3f 2: - sub $0x1000,%esp - test %esp,8(%esp) - sub $0x1000,%ecx - cmp $0x1000,%ecx + sub esp, 0x1000 + test dword ptr [esp + 8], esp + sub ecx, 0x1000 + cmp ecx, 0x1000 ja 2b 3: - sub %ecx,%esp - test %esp,8(%esp) + sub esp, ecx + test dword ptr [esp + 8], esp - add %eax,%esp - pop %ecx + add esp, eax + pop ecx leave - .cfi_def_cfa_register %esp + .cfi_def_cfa_register esp .cfi_adjust_cfa_offset -4 ret .cfi_endproc - ", - options(att_syntax) + ", ) } @@ -176,8 +174,8 @@ pub unsafe extern "custom" fn __rust_probestack() { // REF: Rust commit(74e80468347) // rust\src\llvm-project\llvm\lib\Target\X86\X86FrameLowering.cpp: 805 // Comments in LLVM: -// MSVC x32's _chkstk and cygwin/mingw's _alloca adjust %esp themselves. -// MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust %rsp +// MSVC x32's _chkstk and cygwin/mingw's _alloca adjust esp themselves. +// MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust `rsp` // themselves. #[unsafe(naked)] #[rustc_std_internal_symbol] @@ -185,40 +183,39 @@ pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc - push %ebp + push ebp .cfi_adjust_cfa_offset 4 - .cfi_offset %ebp, -8 - mov %esp, %ebp - .cfi_def_cfa_register %ebp - push %ecx - push %edx - mov %eax,%ecx + .cfi_offset ebp, -8 + mov ebp, esp + .cfi_def_cfa_register ebp + push ecx + push edx + mov ecx, eax - cmp $0x1000,%ecx + cmp ecx, 0x1000 jna 3f 2: - sub $0x1000,%esp - test %esp,8(%esp) - sub $0x1000,%ecx - cmp $0x1000,%ecx + sub esp, 0x1000 + test dword ptr [esp + 8], esp + sub ecx, 0x1000 + cmp ecx, 0x1000 ja 2b 3: - sub %ecx,%esp - test %esp,8(%esp) - mov 4(%ebp),%edx - mov %edx, 12(%esp) - add %eax,%esp - pop %edx - pop %ecx + sub esp, ecx + test dword ptr [esp + 8], esp + mov edx, dword ptr [ebp + 4] + mov dword ptr [esp + 12], edx + add esp, eax + pop edx + pop ecx leave - sub %eax, %esp - .cfi_def_cfa_register %esp + sub esp, eax + .cfi_def_cfa_register esp .cfi_adjust_cfa_offset -4 ret .cfi_endproc - ", - options(att_syntax) + ", ) } diff --git a/library/compiler-builtins/compiler-builtins/src/x86.rs b/library/compiler-builtins/compiler-builtins/src/x86.rs index 51940b3b338a..1a3c41860945 100644 --- a/library/compiler-builtins/compiler-builtins/src/x86.rs +++ b/library/compiler-builtins/compiler-builtins/src/x86.rs @@ -22,26 +22,25 @@ intrinsics! { pub unsafe extern "custom" fn _alloca() { // __chkstk and _alloca are the same function core::arch::naked_asm!( - "push %ecx", - "cmp $0x1000,%eax", - "lea 8(%esp),%ecx", // esp before calling this routine -> ecx - "jb 1f", + "push ecx", + "cmp eax, 0x1000", + "lea ecx, [esp + 8]", // esp before calling this routine -> ecx + "jb 3f", "2:", - "sub $0x1000,%ecx", - "test %ecx,(%ecx)", - "sub $0x1000,%eax", - "cmp $0x1000,%eax", + "sub ecx, 0x1000", + "test [ecx], ecx", + "sub eax, 0x1000", + "cmp eax, 0x1000", "ja 2b", - "1:", - "sub %eax,%ecx", - "test %ecx,(%ecx)", - "lea 4(%esp),%eax", // load pointer to the return address into eax - "mov %ecx,%esp", // install the new top of stack pointer into esp - "mov -4(%eax),%ecx", // restore ecx - "push (%eax)", // push return address onto the stack - "sub %esp,%eax", // restore the original value in eax + "3:", + "sub ecx, eax", + "test [ecx], ecx", + "lea eax, [esp + 4]", // load pointer to the return address into eax + "mov esp, ecx", // install the new top of stack pointer into esp + "mov ecx, [eax - 4]", // restore ecx + "push [eax]", // push return address onto the stack + "sub eax, esp", // restore the original value in eax "ret", - options(att_syntax) ); } } diff --git a/library/compiler-builtins/compiler-builtins/src/x86_64.rs b/library/compiler-builtins/compiler-builtins/src/x86_64.rs index f9ae784d5752..99a527ee9ac5 100644 --- a/library/compiler-builtins/compiler-builtins/src/x86_64.rs +++ b/library/compiler-builtins/compiler-builtins/src/x86_64.rs @@ -12,24 +12,23 @@ intrinsics! { #[cfg(any(all(windows, target_env = "gnu"), target_os = "cygwin", target_os = "uefi"))] pub unsafe extern "custom" fn ___chkstk_ms() { core::arch::naked_asm!( - "push %rcx", - "push %rax", - "cmp $0x1000,%rax", - "lea 24(%rsp),%rcx", - "jb 1f", + "push rcx", + "push rax", + "cmp rax, 0x1000", + "lea rcx, [rsp + 24]", + "jb 3f", "2:", - "sub $0x1000,%rcx", - "test %rcx,(%rcx)", - "sub $0x1000,%rax", - "cmp $0x1000,%rax", + "sub rcx, 0x1000", + "test [rcx], rcx", + "sub rax, 0x1000", + "cmp rax, 0x1000", "ja 2b", - "1:", - "sub %rax,%rcx", - "test %rcx,(%rcx)", - "pop %rax", - "pop %rcx", + "3:", + "sub rcx, rax", + "test [rcx], rcx", + "pop rax", + "pop rcx", "ret", - options(att_syntax) ); } } diff --git a/library/compiler-builtins/crates/panic-handler/Cargo.toml b/library/compiler-builtins/crates/panic-handler/Cargo.toml index a6764fc481b6..70898368d8e7 100644 --- a/library/compiler-builtins/crates/panic-handler/Cargo.toml +++ b/library/compiler-builtins/crates/panic-handler/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "panic-handler" version = "0.1.0" -authors = ["Alex Crichton "] edition = "2024" publish = false diff --git a/library/compiler-builtins/libm-test/Cargo.toml b/library/compiler-builtins/libm-test/Cargo.toml index 0af6b0c1da5c..adecfc1af6b8 100644 --- a/library/compiler-builtins/libm-test/Cargo.toml +++ b/library/compiler-builtins/libm-test/Cargo.toml @@ -21,8 +21,8 @@ build-musl = ["dep:musl-math-sys"] # Enable report generation without bringing in more dependencies by default benchmarking-reports = ["criterion/plotters", "criterion/html_reports"] -# Enable icount benchmarks (requires iai-callgrind and valgrind) -icount = ["dep:iai-callgrind"] +# Enable icount benchmarks (requires gungraun-runner and valgrind locally) +icount = ["dep:gungraun"] # Run with a reduced set of benchmarks, such as for CI short-benchmarks = [] @@ -31,7 +31,7 @@ short-benchmarks = [] anyhow = "1.0.98" # This is not directly used but is required so we can enable `gmp-mpfr-sys/force-cross`. gmp-mpfr-sys = { version = "1.6.5", optional = true, default-features = false } -iai-callgrind = { version = "0.15.2", optional = true } +gungraun = { version = "0.17.0", optional = true } indicatif = { version = "0.18.0", default-features = false } libm = { path = "../libm", features = ["unstable-public-internals"] } libm-macros = { path = "../crates/libm-macros" } diff --git a/library/compiler-builtins/libm-test/benches/icount.rs b/library/compiler-builtins/libm-test/benches/icount.rs index 0b85771225dd..fb856d9be451 100644 --- a/library/compiler-builtins/libm-test/benches/icount.rs +++ b/library/compiler-builtins/libm-test/benches/icount.rs @@ -1,10 +1,10 @@ -//! Benchmarks that use `iai-cachegrind` to be reasonably CI-stable. +//! Benchmarks that use `gungraun` to be reasonably CI-stable. #![feature(f16)] #![feature(f128)] use std::hint::black_box; -use iai_callgrind::{library_benchmark, library_benchmark_group, main}; +use gungraun::{library_benchmark, library_benchmark_group, main}; use libm::support::{HInt, Hexf, hf16, hf32, hf64, hf128, u256}; use libm_test::generate::spaced; use libm_test::{CheckBasis, CheckCtx, GeneratorKind, MathOp, OpRustArgs, TupleCall, op}; @@ -156,7 +156,13 @@ fn icount_bench_u256_shr(cases: Vec<(u256, u32)>) { library_benchmark_group!( name = icount_bench_u128_group; - benchmarks = icount_bench_u128_widen_mul, icount_bench_u256_narrowing_div, icount_bench_u256_add, icount_bench_u256_sub, icount_bench_u256_shl, icount_bench_u256_shr + benchmarks = + icount_bench_u128_widen_mul, + icount_bench_u256_narrowing_div, + icount_bench_u256_add, + icount_bench_u256_sub, + icount_bench_u256_shl, + icount_bench_u256_shr ); #[library_benchmark] diff --git a/library/compiler-builtins/libm-test/src/precision.rs b/library/compiler-builtins/libm-test/src/precision.rs index c441922d302b..7887c032394b 100644 --- a/library/compiler-builtins/libm-test/src/precision.rs +++ b/library/compiler-builtins/libm-test/src/precision.rs @@ -83,6 +83,19 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { Bn::Tgamma => 20, }; + // These have a separate implementation on i586 + if cfg!(x86_no_sse) { + match ctx.fn_ident { + Id::Exp => ulp = 1, + Id::Exp2 => ulp = 1, + Id::Exp10 => ulp = 1, + Id::Expf => ulp = 0, + Id::Exp2f => ulp = 0, + Id::Exp10f => ulp = 0, + _ => (), + } + } + // There are some cases where musl's approximation is less accurate than ours. For these // cases, increase the ULP. if ctx.basis == Musl { @@ -98,6 +111,8 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { Id::Cbrt => ulp = 2, // FIXME(#401): musl has an incorrect result here. Id::Fdim => ulp = 2, + Id::Exp2f => ulp = 1, + Id::Expf => ulp = 1, Id::Sincosf => ulp = 500, Id::Tgamma => ulp = 20, _ => (), @@ -124,8 +139,6 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { Id::Asinh => ulp = 3, Id::Asinhf => ulp = 3, Id::Cbrt => ulp = 1, - Id::Exp10 | Id::Exp10f => ulp = 1_000_000, - Id::Exp2 | Id::Exp2f => ulp = 10_000_000, Id::Log1p | Id::Log1pf => ulp = 2, Id::Tan => ulp = 2, _ => (), @@ -205,36 +218,6 @@ impl MaybeOverride<(f16,)> for SpecialCase {} impl MaybeOverride<(f32,)> for SpecialCase { fn check_float(input: (f32,), actual: F, expected: F, ctx: &CheckCtx) -> CheckAction { - if ctx.base_name == BaseName::Expm1 - && !input.0.is_infinite() - && input.0 > 80.0 - && actual.is_infinite() - && !expected.is_infinite() - { - // we return infinity but the number is representable - if ctx.basis == CheckBasis::Musl { - return XFAIL_NOCHECK; - } - return XFAIL("expm1 representable numbers"); - } - - if cfg!(x86_no_sse) - && ctx.base_name == BaseName::Exp2 - && !expected.is_infinite() - && actual.is_infinite() - { - // We return infinity when there is a representable value. Test input: 127.97238 - return XFAIL("586 exp2 representable numbers"); - } - - if ctx.base_name == BaseName::Sinh && input.0.abs() > 80.0 && actual.is_nan() { - // we return some NaN that should be real values or infinite - if ctx.basis == CheckBasis::Musl { - return XFAIL_NOCHECK; - } - return XFAIL("sinh unexpected NaN"); - } - if (ctx.base_name == BaseName::Lgamma || ctx.base_name == BaseName::LgammaR) && input.0 > 4e36 && expected.is_infinite() @@ -278,14 +261,6 @@ impl MaybeOverride<(f64,)> for SpecialCase { return XFAIL("i586 rint rounding mode"); } - if cfg!(x86_no_sse) - && (ctx.fn_ident == Identifier::Exp10 || ctx.fn_ident == Identifier::Exp2) - { - // FIXME: i586 has very imprecise results with ULP > u32::MAX for these - // operations so we can't reasonably provide a limit. - return XFAIL_NOCHECK; - } - if ctx.base_name == BaseName::J0 && input.0 < -1e300 { // Errors get huge close to -inf return XFAIL_NOCHECK; diff --git a/library/compiler-builtins/libm/Cargo.toml b/library/compiler-builtins/libm/Cargo.toml index 63b4d3c27798..5b5ca34fd2c9 100644 --- a/library/compiler-builtins/libm/Cargo.toml +++ b/library/compiler-builtins/libm/Cargo.toml @@ -1,7 +1,12 @@ [package] name = "libm" version = "0.2.15" -authors = ["Jorge Aparicio "] +authors = [ + "Alex Crichton ", + "Amanieu d'Antras ", + "Jorge Aparicio ", + "Trevor Gross ", +] description = "libm in pure Rust" categories = ["no-std"] keywords = ["libm", "math"] diff --git a/library/compiler-builtins/libm/src/math/arch/i586.rs b/library/compiler-builtins/libm/src/math/arch/i586.rs index b9a66762063d..d9bb93fbf585 100644 --- a/library/compiler-builtins/libm/src/math/arch/i586.rs +++ b/library/compiler-builtins/libm/src/math/arch/i586.rs @@ -60,3 +60,62 @@ pub fn floor(mut x: f64) -> f64 { } x } +/// Implements the exponential functions with `x87` assembly. +/// +/// This relies on the instruction `f2xm1`, which computes `2^x - 1` (for +/// |x| < 1). This transcendental instruction is documented to produce results +/// with error below 1ulp (in the native double-extended precision format). This +/// translates to correctly rounded results for f32, but results in f64 may have +/// 1ulp error, which may depend on the hardware. +macro_rules! x87exp { + ($float_ty:ident, $word_size:literal, $fn_name:ident, $load_op:literal) => { + pub fn $fn_name(mut x: $float_ty) -> $float_ty { unsafe { + core::arch::asm!( + // Prepare the register stack as + // ``` + // st(0) = y = x*log2(base) + // st(1) = 1.0 + // st(2) = round(y) + // ``` + concat!($load_op, " ", $word_size, " ptr [{x}]"), + "fld1", + "fld st(1)", + "frndint", + "fxch st(2)", + + // Compare y with round(y) to determine if y is finite and + // not an integer. If so, compute `exp2(y - round(y))` into + // st(1). Otherwise skip ahead with `st(1) = 1.0` + "fucom st(2)", + "fstsw ax", + "test ax, 0x4000", + "jnz 2f", + "fsub st(0), st(2)", // st(0) = y - round(y) + "f2xm1", // st(0) = 2^st(0) - 1.0 + "fadd st(1), st(0)", // st(1) = 1 + st(0) = exp2(y - round(y)) + "2:", + + // Finally, scale by `exp2(round(y))` and clear the stack. + "fstp st(0)", + "fscale", + concat!("fstp ", $word_size, " ptr [{x}]"), + "fstp st(0)", + x = in(reg) &mut x, + out("ax") _, + out("st(0)") _, out("st(1)") _, + out("st(2)") _, out("st(3)") _, + out("st(4)") _, out("st(5)") _, + out("st(6)") _, out("st(7)") _, + options(nostack), + ); + x + }} + }; +} + +x87exp!(f32, "dword", x87_exp2f, "fld"); +x87exp!(f64, "qword", x87_exp2, "fld"); +x87exp!(f32, "dword", x87_exp10f, "fldl2t\nfmul"); +x87exp!(f64, "qword", x87_exp10, "fldl2t\nfmul"); +x87exp!(f32, "dword", x87_expf, "fldl2e\nfmul"); +x87exp!(f64, "qword", x87_exp, "fldl2e\nfmul"); diff --git a/library/compiler-builtins/libm/src/math/arch/mod.rs b/library/compiler-builtins/libm/src/math/arch/mod.rs index 984ae7f3129f..ba859c679d0d 100644 --- a/library/compiler-builtins/libm/src/math/arch/mod.rs +++ b/library/compiler-builtins/libm/src/math/arch/mod.rs @@ -48,3 +48,8 @@ cfg_if! { pub use i586::{ceil, floor}; } } +cfg_if! { + if #[cfg(x86_no_sse)] { + pub use i586::{x87_exp10f, x87_exp10, x87_expf, x87_exp, x87_exp2f, x87_exp2}; + } +} diff --git a/library/compiler-builtins/libm/src/math/exp.rs b/library/compiler-builtins/libm/src/math/exp.rs index 78ce5dd134ac..cb939ad5d8bf 100644 --- a/library/compiler-builtins/libm/src/math/exp.rs +++ b/library/compiler-builtins/libm/src/math/exp.rs @@ -83,6 +83,12 @@ const P5: f64 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp(mut x: f64) -> f64 { + select_implementation! { + name: x87_exp, + use_arch_required: x86_no_sse, + args: x, + } + let x1p1023 = f64::from_bits(0x7fe0000000000000); // 0x1p1023 === 2 ^ 1023 let x1p_149 = f64::from_bits(0x36a0000000000000); // 0x1p-149 === 2 ^ -149 diff --git a/library/compiler-builtins/libm/src/math/exp10.rs b/library/compiler-builtins/libm/src/math/exp10.rs index 1f49f5e96979..e0af1945b922 100644 --- a/library/compiler-builtins/libm/src/math/exp10.rs +++ b/library/compiler-builtins/libm/src/math/exp10.rs @@ -9,6 +9,12 @@ const P10: &[f64] = &[ /// Calculates 10 raised to the power of `x` (f64). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp10(x: f64) -> f64 { + select_implementation! { + name: x87_exp10, + use_arch_required: x86_no_sse, + args: x, + } + let (mut y, n) = modf(x); let u: u64 = n.to_bits(); /* fabs(n) < 16 without raising invalid on nan */ diff --git a/library/compiler-builtins/libm/src/math/exp10f.rs b/library/compiler-builtins/libm/src/math/exp10f.rs index 22a264211d03..f0a311c2d191 100644 --- a/library/compiler-builtins/libm/src/math/exp10f.rs +++ b/library/compiler-builtins/libm/src/math/exp10f.rs @@ -9,6 +9,12 @@ const P10: &[f32] = &[ /// Calculates 10 raised to the power of `x` (f32). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp10f(x: f32) -> f32 { + select_implementation! { + name: x87_exp10f, + use_arch_required: x86_no_sse, + args: x, + } + let (mut y, n) = modff(x); let u = n.to_bits(); /* fabsf(n) < 8 without raising invalid on nan */ diff --git a/library/compiler-builtins/libm/src/math/exp2.rs b/library/compiler-builtins/libm/src/math/exp2.rs index 6e4cbc29dcc9..08b71587f6de 100644 --- a/library/compiler-builtins/libm/src/math/exp2.rs +++ b/library/compiler-builtins/libm/src/math/exp2.rs @@ -324,6 +324,12 @@ static TBL: [u64; TBLSIZE * 2] = [ /// Calculate `2^x`, that is, 2 raised to the power `x`. #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp2(mut x: f64) -> f64 { + select_implementation! { + name: x87_exp2, + use_arch_required: x86_no_sse, + args: x, + } + let redux = f64::from_bits(0x4338000000000000) / TBLSIZE as f64; let p1 = f64::from_bits(0x3fe62e42fefa39ef); let p2 = f64::from_bits(0x3fcebfbdff82c575); diff --git a/library/compiler-builtins/libm/src/math/exp2f.rs b/library/compiler-builtins/libm/src/math/exp2f.rs index 733d2f1a8473..ceff6822c596 100644 --- a/library/compiler-builtins/libm/src/math/exp2f.rs +++ b/library/compiler-builtins/libm/src/math/exp2f.rs @@ -75,6 +75,12 @@ static EXP2FT: [u64; TBLSIZE] = [ /// Calculate `2^x`, that is, 2 raised to the power `x`. #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp2f(mut x: f32) -> f32 { + select_implementation! { + name: x87_exp2f, + use_arch_required: x86_no_sse, + args: x, + } + let redux = f32::from_bits(0x4b400000) / TBLSIZE as f32; let p1 = f32::from_bits(0x3f317218); let p2 = f32::from_bits(0x3e75fdf0); diff --git a/library/compiler-builtins/libm/src/math/expf.rs b/library/compiler-builtins/libm/src/math/expf.rs index dbbfdbba9253..5541ab79a9c1 100644 --- a/library/compiler-builtins/libm/src/math/expf.rs +++ b/library/compiler-builtins/libm/src/math/expf.rs @@ -32,6 +32,12 @@ const P2: f32 = -2.7667332906e-3; /* -0xb55215.0p-32 */ /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn expf(mut x: f32) -> f32 { + select_implementation! { + name: x87_expf, + use_arch_required: x86_no_sse, + args: x, + } + let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 let x1p_126 = f32::from_bits(0x800000); // 0x1p-126f === 2 ^ -126 /*original 0x1p-149f ??????????? */ let mut hx = x.to_bits(); diff --git a/library/compiler-builtins/libm/src/math/expm1f.rs b/library/compiler-builtins/libm/src/math/expm1f.rs index f77515a4b99b..388da3f30173 100644 --- a/library/compiler-builtins/libm/src/math/expm1f.rs +++ b/library/compiler-builtins/libm/src/math/expm1f.rs @@ -13,7 +13,6 @@ * ==================================================== */ -const O_THRESHOLD: f32 = 8.8721679688e+01; /* 0x42b17180 */ const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ const INV_LN2: f32 = 1.4426950216e+00; /* 0x3fb8aa3b */ @@ -50,7 +49,8 @@ pub fn expm1f(mut x: f32) -> f32 { if sign { return -1.; } - if x > O_THRESHOLD { + if hx > 0x42b17217 { + /* x > log(FLT_MAX) */ x *= x1p127; return x; } diff --git a/library/compiler-builtins/libm/src/math/generic/fmod.rs b/library/compiler-builtins/libm/src/math/generic/fmod.rs index 29acc8a4d5df..3c3fd44b27cc 100644 --- a/library/compiler-builtins/libm/src/math/generic/fmod.rs +++ b/library/compiler-builtins/libm/src/math/generic/fmod.rs @@ -1,8 +1,12 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ -use crate::support::{CastFrom, Float, Int, MinInt}; +use crate::support::{CastFrom, CastInto, Float, HInt, Int, MinInt, NarrowingDiv}; #[inline] -pub fn fmod(x: F, y: F) -> F { +pub fn fmod(x: F, y: F) -> F +where + F::Int: HInt, + ::D: NarrowingDiv, +{ let _1 = F::Int::ONE; let sx = x.to_bits() & F::SIGN_MASK; let ux = x.to_bits() & !F::SIGN_MASK; @@ -29,7 +33,7 @@ pub fn fmod(x: F, y: F) -> F { // To compute `(num << ex) % (div << ey)`, first // evaluate `rem = (num << (ex - ey)) % div` ... - let rem = reduction(num, ex - ey, div); + let rem = reduction::(num, ex - ey, div); // ... so the result will be `rem << ey` if rem.is_zero() { @@ -58,11 +62,55 @@ fn into_sig_exp(mut bits: F::Int) -> (F::Int, u32) { } /// Compute the remainder `(x * 2.pow(e)) % y` without overflow. -fn reduction(mut x: I, e: u32, y: I) -> I { - x %= y; - for _ in 0..e { - x <<= 1; - x = x.checked_sub(y).unwrap_or(x); +fn reduction(mut x: F::Int, e: u32, y: F::Int) -> F::Int +where + F: Float, + F::Int: HInt, + <::Int as HInt>::D: NarrowingDiv, +{ + // `f16` only has 5 exponent bits, so even `f16::MAX = 65504.0` is only + // a 40-bit integer multiple of the smallest subnormal. + if F::BITS == 16 { + debug_assert!(F::EXP_MAX - F::EXP_MIN == 29); + debug_assert!(e <= 29); + let u: u16 = x.cast(); + let v: u16 = y.cast(); + let u = (u as u64) << e; + let v = v as u64; + return F::Int::cast_from((u % v) as u16); } - x + + // Ensure `x < 2y` for later steps + if x >= (y << 1) { + // This case is only reached with subnormal divisors, + // but it might be better to just normalize all significands + // to make this unnecessary. The further calls could potentially + // benefit from assuming a specific fixed leading bit position. + x %= y; + } + + // The simple implementation seems to be fastest for a short reduction + // at this size. The limit here was chosen empirically on an Intel Nehalem. + // Less old CPUs that have faster `u64 * u64 -> u128` might not benefit, + // and 32-bit systems or architectures without hardware multipliers might + // want to do this in more cases. + if F::BITS == 64 && e < 32 { + // Assumes `x < 2y` + for _ in 0..e { + x = x.checked_sub(y).unwrap_or(x); + x <<= 1; + } + return x.checked_sub(y).unwrap_or(x); + } + + // Fast path for short reductions + if e < F::BITS { + let w = x.widen() << e; + if let Some((_, r)) = w.checked_narrowing_div_rem(y) { + return r; + } + } + + // Assumes `x < 2y` + crate::support::linear_mul_reduction(x, e, y) } diff --git a/library/compiler-builtins/libm/src/math/generic/scalbn.rs b/library/compiler-builtins/libm/src/math/generic/scalbn.rs index 6dd9b1a9b84a..68de41757913 100644 --- a/library/compiler-builtins/libm/src/math/generic/scalbn.rs +++ b/library/compiler-builtins/libm/src/math/generic/scalbn.rs @@ -96,14 +96,14 @@ where // Work aroudn this by using a different algorithm that calculates the prescale // dynamically based on the maximum possible value. This adds more operations per round // since it needs to construct the scale, but works better in the general case. - let add = -(n + sig_total_bits as i32).clamp(exp_min, sig_total_bits as i32); + let add = -(n + sig_total_bits as i32).max(exp_min); let mul = F::from_parts(false, (F::EXP_BIAS as i32 - add) as u32, zero); x *= mul; n += add; if n < exp_min { - let add = -(n + sig_total_bits as i32).clamp(exp_min, sig_total_bits as i32); + let add = -(n + sig_total_bits as i32).max(exp_min); let mul = F::from_parts(false, (F::EXP_BIAS as i32 - add) as u32, zero); x *= mul; diff --git a/library/compiler-builtins/libm/src/math/support/int_traits.rs b/library/compiler-builtins/libm/src/math/support/int_traits.rs index f1aa1e5b9b4d..55b609affd2e 100644 --- a/library/compiler-builtins/libm/src/math/support/int_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/int_traits.rs @@ -296,7 +296,14 @@ int_impl!(i128, u128); /// Trait for integers twice the bit width of another integer. This is implemented for all /// primitives except for `u8`, because there is not a smaller primitive. -pub trait DInt: MinInt { +pub trait DInt: + MinInt + + ops::Add + + ops::Sub + + ops::Shl + + ops::Shr + + Ord +{ /// Integer that is half the bit width of the integer this trait is implemented for type H: HInt; diff --git a/library/compiler-builtins/libm/src/math/support/int_traits/narrowing_div.rs b/library/compiler-builtins/libm/src/math/support/int_traits/narrowing_div.rs index 3da0843cc540..e76fc5ae9f4c 100644 --- a/library/compiler-builtins/libm/src/math/support/int_traits/narrowing_div.rs +++ b/library/compiler-builtins/libm/src/math/support/int_traits/narrowing_div.rs @@ -7,7 +7,6 @@ use crate::support::{CastInto, DInt, HInt, Int, MinInt, u256}; /// This is the inverse of widening multiplication: /// - for any `x` and nonzero `y`: `x.widen_mul(y).checked_narrowing_div_rem(y) == Some((x, 0))`, /// - and for any `r in 0..y`: `x.carrying_mul(y, r).checked_narrowing_div_rem(y) == Some((x, r))`, -#[allow(dead_code)] pub trait NarrowingDiv: DInt + MinInt { /// Computes `(self / n, self % n))` /// diff --git a/library/compiler-builtins/libm/src/math/support/mod.rs b/library/compiler-builtins/libm/src/math/support/mod.rs index 7b529eb760b7..15ab010dc8d5 100644 --- a/library/compiler-builtins/libm/src/math/support/mod.rs +++ b/library/compiler-builtins/libm/src/math/support/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod feature_detect; mod float_traits; pub mod hex_float; mod int_traits; +mod modular; #[allow(unused_imports)] pub use big::{i256, u256}; @@ -28,8 +29,8 @@ pub use hex_float::hf16; pub use hex_float::hf128; #[allow(unused_imports)] pub use hex_float::{hf32, hf64}; -#[allow(unused_imports)] pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt, NarrowingDiv}; +pub use modular::linear_mul_reduction; /// Hint to the compiler that the current path is cold. pub fn cold_path() { diff --git a/library/compiler-builtins/libm/src/math/support/modular.rs b/library/compiler-builtins/libm/src/math/support/modular.rs new file mode 100644 index 000000000000..cc0edf2f2bc0 --- /dev/null +++ b/library/compiler-builtins/libm/src/math/support/modular.rs @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: MIT OR Apache-2.0 */ + +//! This module provides accelerated modular multiplication by large powers +//! of two, which is needed for computing floating point remainders in `fmod` +//! and similar functions. +//! +//! To keep the equations somewhat concise, the following conventions are used: +//! - all integer operations are in the mathematical sense, without overflow +//! - concatenation means multiplication: `2xq = 2 * x * q` +//! - `R = (1 << U::BITS)` is the modulus of wrapping arithmetic in `U` + +use crate::support::int_traits::NarrowingDiv; +use crate::support::{DInt, HInt, Int}; + +/// Compute the remainder `(x << e) % y` with unbounded integers. +/// Requires `x < 2y` and `y.leading_zeros() >= 2` +pub fn linear_mul_reduction(x: U, mut e: u32, mut y: U) -> U +where + U: HInt + Int, + U::D: NarrowingDiv, +{ + assert!(y <= U::MAX >> 2); + assert!(x < (y << 1)); + let _0 = U::ZERO; + let _1 = U::ONE; + + // power of two divisors + if (y & (y - _1)).is_zero() { + if e < U::BITS { + // shift and only keep low bits + return (x << e) & (y - _1); + } else { + // would shift out all the bits + return _0; + } + } + + // Use the identity `(x << e) % y == ((x << (e + s)) % (y << s)) >> s` + // to shift the divisor so it has exactly two leading zeros to satisfy + // the precondition of `Reducer::new` + let s = y.leading_zeros() - 2; + e += s; + y <<= s; + + // `m: Reducer` keeps track of the remainder `x` in a form that makes it + // very efficient to do `x <<= k` modulo `y` for integers `k < U::BITS` + let mut m = Reducer::new(x, y); + + // Use the faster special case with constant `k == U::BITS - 1` while we can + while e >= U::BITS - 1 { + m.word_reduce(); + e -= U::BITS - 1; + } + // Finish with the variable shift operation + m.shift_reduce(e); + + // The partial remainder is in `[0, 2y)` ... + let r = m.partial_remainder(); + // ... so check and correct, and compensate for the earlier shift. + r.checked_sub(y).unwrap_or(r) >> s +} + +/// Helper type for computing the reductions. The implementation has a number +/// of seemingly weird choices, but everything is aimed at streamlining +/// `Reducer::word_reduce` into its current form. +/// +/// Implicitly contains: +/// n in (R/8, R/4) +/// x in [0, 2n) +/// The value of `n` is fixed for a given `Reducer`, +/// but the value of `x` is modified by the methods. +#[derive(Debug, Clone, PartialEq, Eq)] +struct Reducer { + // m = 2n + m: U, + // q = (RR/2) / m + // r = (RR/2) % m + // Then RR/2 = qm + r, where `0 <= r < m` + // The value `q` is only needed during construction, so isn't saved. + r: U, + // The value `x` is implicitly stored as `2 * q * x`: + _2xq: U::D, +} + +impl Reducer +where + U: HInt, + U: Int, +{ + /// Construct a reducer for `(x << _) mod n`. + /// + /// Requires `R/8 < n < R/4` and `x < 2n`. + fn new(x: U, n: U) -> Self + where + U::D: NarrowingDiv, + { + let _1 = U::ONE; + assert!(n > (_1 << (U::BITS - 3))); + assert!(n < (_1 << (U::BITS - 2))); + let m = n << 1; + assert!(x < m); + + // We need to compute the parameters + // `q = (RR/2) / m` + // `r = (RR/2) % m` + + // Since `m` is in `(R/4, R/2)`, the quotient `q` is in `[R, 2R)`, and + // it would overflow in `U` if computed directly. Instead, we compute + // `f = q - R`, which is in `[0, R)`. To do so, we simply subtract `Rm` + // from the dividend, which doesn't change the remainder: + // `f = R(R/2 - m) / m` + // `r = R(R/2 - m) % m` + let dividend = ((_1 << (U::BITS - 1)) - m).widen_hi(); + let (f, r) = dividend.checked_narrowing_div_rem(m).unwrap(); + + // As `x < m`, `xq < qm <= RR/2` + // Thus `2xq = 2xR + 2xf` does not overflow in `U::D`. + let _2x = x + x; + let _2xq = _2x.widen_hi() + _2x.widen_mul(f); + Self { m, r, _2xq } + } + + /// Extract the current remainder `x` in the range `[0, 2n)` + fn partial_remainder(&self) -> U { + // `RR/2 = qm + r`, where `0 <= r < m` + // `2xq = uR + v`, where `0 <= v < R` + + // The goal is to extract the current value of `x` from the value `2xq` + // that we actually have. A bit simplified, we could multiply it by `m` + // to obtain `2xqm == 2x(RR/2 - r) == xRR - 2xr`, where `2xr < RR`. + // We could just round that up to the next multiple of `RR` to get `x`, + // but we can avoid having to multiply the full double-wide `2xq` by + // making a couple of adjustments: + + // First, let's only use the high half `u` for the product, and + // include an additional error term due to the truncation: + // `mu = xR - (2xr + mv)/R` + + // Next, show bounds for the error term + // `0 <= mv < mR` follows from `0 <= v < R` + // `0 <= 2xr < mR` follows from `0 <= x < m < R/2` and `0 <= r < m` + // Adding those together, we have: + // `0 <= (mv + 2xr)/R < 2m` + // Which also implies: + // `0 < 2m - (mv + 2xr)/R <= 2m < R` + + // For that reason, we can use `u + 2` as the factor to obtain + // `m(u + 2) = xR + (2m - (mv + 2xr)/R)` + // By the previous inequality, the second term fits neatly in the lower + // half, so we get exactly `x` as the high half. + let u = self._2xq.hi(); + let _2 = U::ONE + U::ONE; + self.m.widen_mul(u + _2).hi() + + // Additionally, we should ensure that `u + 2` cannot overflow: + // Since `x < m` and `2qm <= RR`, + // `2xq <= 2q(m-1) <= RR - 2q` + // As we also have `q > R`, + // `2xq < RR - 2R` + // which is sufficient. + } + + /// Replace the remainder `x` with `(x << k) - un`, + /// for a suitable quotient `u`, which is returned. + /// + /// Requires that `k < U::BITS`. + fn shift_reduce(&mut self, k: u32) -> U { + assert!(k < U::BITS); + + // First, split the shifted value: + // `2xq << k = aRR/2 + b`, where `0 <= b < RR/2` + let a = self._2xq.hi() >> (U::BITS - 1 - k); + let (low, high) = (self._2xq << k).lo_hi(); + let b = U::D::from_lo_hi(low, high & (U::MAX >> 1)); + + // Then, subtract `2anq = aqm`: + // ``` + // (2xq << k) - aqm + // = aRR/2 + b - aqm + // = a(RR/2 - qm) + b + // = ar + b + // ``` + self._2xq = a.widen_mul(self.r) + b; + a + + // Since `a` is at most the high half of `2xq`, we have + // `a + 2 < R` (shown above, in `partial_remainder`) + // Using that together with `b < RR/2` and `r < m < R/2`, + // we get `(a + 2)r + b < RR`, so + // `ar + b < RR - 2r = 2mq` + // which shows that the new remainder still satisfies `x < m`. + } + + // NB: `word_reduce()` is just the special case `shift_reduce(U::BITS - 1)` + // that optimizes especially well. The correspondence is that `a == u` and + // `b == (v >> 1).widen_hi()` + // + /// Replace the remainder `x` with `x(R/2) - un`, + /// for a suitable quotient `u`, which is returned. + fn word_reduce(&mut self) -> U { + // To do so, we replace `2xq = uR + v` with + // ``` + // 2 * (x(R/2) - un) * q + // = xqR - 2unq + // = xqR - uqm + // = uRR/2 + vR/2 - uRR/2 + ur + // = ur + (v/2)R + // ``` + let (v, u) = self._2xq.lo_hi(); + self._2xq = u.widen_mul(self.r) + U::widen_hi(v >> 1); + u + + // Additional notes: + // 1. As `v` is the low bits of `2xq`, it is even and can be halved. + // 2. The new remainder is `(xr + mv/2) / R` (see below) + // and since `v < R`, `r < m`, `x < m < R/2`, + // that is also strictly less than `m`. + // ``` + // (x(R/2) - un)R + // = xRR/2 - (m/2)uR + // = x(qm + r) - (m/2)(2xq - v) + // = xqm + xr - xqm + mv/2 + // = xr + mv/2 + // ``` + } +} + +#[cfg(test)] +mod test { + use crate::support::linear_mul_reduction; + use crate::support::modular::Reducer; + + #[test] + fn reducer_ops() { + for n in 33..=63_u8 { + for x in 0..2 * n { + let temp = Reducer::new(x, n); + let n = n as u32; + let x0 = temp.partial_remainder() as u32; + assert_eq!(x as u32, x0); + for k in 0..=7 { + let mut red = temp.clone(); + let u = red.shift_reduce(k) as u32; + let x1 = red.partial_remainder() as u32; + assert_eq!(x1, (x0 << k) - u * n); + assert!(x1 < 2 * n); + assert!((red._2xq as u32).is_multiple_of(2 * x1)); + + // `word_reduce` is equivalent to + // `shift_reduce(U::BITS - 1)` + if k == 7 { + let mut alt = temp.clone(); + let w = alt.word_reduce(); + assert_eq!(u, w as u32); + assert_eq!(alt, red); + } + } + } + } + } + #[test] + fn reduction_u8() { + for y in 1..64u8 { + for x in 0..2 * y { + let mut r = x % y; + for e in 0..100 { + assert_eq!(r, linear_mul_reduction(x, e, y)); + // maintain the correct expected remainder + r <<= 1; + if r >= y { + r -= y; + } + } + } + } + } + #[test] + fn reduction_u128() { + assert_eq!( + linear_mul_reduction::(17, 100, 123456789), + (17 << 100) % 123456789 + ); + + // power-of-two divisor + assert_eq!( + linear_mul_reduction(0xdead_beef, 100, 1_u128 << 116), + 0xbeef << 100 + ); + + let x = 10_u128.pow(37); + let y = 11_u128.pow(36); + assert!(x < y); + let mut r = x; + for e in 0..1000 { + assert_eq!(r, linear_mul_reduction(x, e, y)); + // maintain the correct expected remainder + r <<= 1; + if r >= y { + r -= y; + } + assert!(r != 0); + } + } +} diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version index 71fbbbaa984f..7345c25066a8 100644 --- a/library/compiler-builtins/rust-version +++ b/library/compiler-builtins/rust-version @@ -1 +1 @@ -47cd7120d9b4e1b64eb27c87522a07888197fae8 +2dc30247c5d8293aaa31e1d7dae2ed2fde908ada diff --git a/library/compiler-builtins/triagebot.toml b/library/compiler-builtins/triagebot.toml index eba5cdd88b94..b210a5fb5256 100644 --- a/library/compiler-builtins/triagebot.toml +++ b/library/compiler-builtins/triagebot.toml @@ -12,10 +12,11 @@ exclude_titles = ["Rustc pull update"] [issue-links] check-commits = false -# Prevents mentions in commits to avoid users being spammed -# Documentation at: https://forge.rust-lang.org/triagebot/no-mentions.html -[no-mentions] - # Enable issue transfers within the org # Documentation at: https://forge.rust-lang.org/triagebot/transfer.html [transfer] + +# Enable comments linking to triagebot range-diff when a PR is rebased +# onto a different base commit +# Documentation at: https://forge.rust-lang.org/triagebot/range-diff.html +[range-diff] diff --git a/library/core/src/any.rs b/library/core/src/any.rs index ff55793340bd..42f332f7d8ba 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,7 +86,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::{fmt, hash, intrinsics}; +use crate::{fmt, hash, intrinsics, ptr}; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -906,3 +906,109 @@ pub const fn type_name() -> &'static str { pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() } + +/// Returns `Some(&U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. +/// +/// # Compile-time failures +/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. +/// In some cases, that resolution can exceed the recursion limit, +/// and compilation will fail instead of this function returning `None`. +/// # Examples +/// +/// ```rust +/// #![feature(try_as_dyn)] +/// +/// use core::any::try_as_dyn; +/// +/// trait Animal { +/// fn speak(&self) -> &'static str; +/// } +/// +/// struct Dog; +/// impl Animal for Dog { +/// fn speak(&self) -> &'static str { "woof" } +/// } +/// +/// struct Rock; // does not implement Animal +/// +/// let dog = Dog; +/// let rock = Rock; +/// +/// let as_animal: Option<&dyn Animal> = try_as_dyn::(&dog); +/// assert_eq!(as_animal.unwrap().speak(), "woof"); +/// +/// let not_an_animal: Option<&dyn Animal> = try_as_dyn::(&rock); +/// assert!(not_an_animal.is_none()); +/// ``` +#[must_use] +#[unstable(feature = "try_as_dyn", issue = "144361")] +pub const fn try_as_dyn< + T: Any + 'static, + U: ptr::Pointee> + ?Sized + 'static, +>( + t: &T, +) -> Option<&U> { + let vtable: Option> = const { intrinsics::vtable_for::() }; + match vtable { + Some(dyn_metadata) => { + let pointer = ptr::from_raw_parts(t, dyn_metadata); + // SAFETY: `t` is a reference to a type, so we know it is valid. + // `dyn_metadata` is a vtable for T, implementing the trait of `U`. + Some(unsafe { &*pointer }) + } + None => None, + } +} + +/// Returns `Some(&mut U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. +/// +/// # Compile-time failures +/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. +/// In some cases, that resolution can exceed the recursion limit, +/// and compilation will fail instead of this function returning `None`. +/// # Examples +/// +/// ```rust +/// #![feature(try_as_dyn)] +/// +/// use core::any::try_as_dyn_mut; +/// +/// trait Animal { +/// fn speak(&self) -> &'static str; +/// } +/// +/// struct Dog; +/// impl Animal for Dog { +/// fn speak(&self) -> &'static str { "woof" } +/// } +/// +/// struct Rock; // does not implement Animal +/// +/// let mut dog = Dog; +/// let mut rock = Rock; +/// +/// let as_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut dog); +/// assert_eq!(as_animal.unwrap().speak(), "woof"); +/// +/// let not_an_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut rock); +/// assert!(not_an_animal.is_none()); +/// ``` +#[must_use] +#[unstable(feature = "try_as_dyn", issue = "144361")] +pub const fn try_as_dyn_mut< + T: Any + 'static, + U: ptr::Pointee> + ?Sized + 'static, +>( + t: &mut T, +) -> Option<&mut U> { + let vtable: Option> = const { intrinsics::vtable_for::() }; + match vtable { + Some(dyn_metadata) => { + let pointer = ptr::from_raw_parts_mut(t, dyn_metadata); + // SAFETY: `t` is a reference to a type, so we know it is valid. + // `dyn_metadata` is a vtable for T, implementing the trait of `U`. + Some(unsafe { &mut *pointer }) + } + None => None, + } +} diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index 78ba69fec142..eb4562bda4ce 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -187,7 +187,7 @@ pub const trait Borrow { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "BorrowMut"] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] -pub const trait BorrowMut: Borrow { +pub const trait BorrowMut: [const] Borrow { /// Mutably borrows from an owned value. /// /// # Examples diff --git a/library/core/src/bstr/mod.rs b/library/core/src/bstr/mod.rs index e13dc5cd44d5..34e1ea66c99a 100644 --- a/library/core/src/bstr/mod.rs +++ b/library/core/src/bstr/mod.rs @@ -68,6 +68,30 @@ impl ByteStr { ByteStr::from_bytes(bytes.as_ref()) } + /// Returns the same string as `&ByteStr`. + /// + /// This method is redundant when used directly on `&ByteStr`, but + /// it helps dereferencing other "container" types, + /// for example `Box` or `Arc`. + #[inline] + // #[unstable(feature = "str_as_str", issue = "130366")] + #[unstable(feature = "bstr", issue = "134915")] + pub const fn as_byte_str(&self) -> &ByteStr { + self + } + + /// Returns the same string as `&mut ByteStr`. + /// + /// This method is redundant when used directly on `&mut ByteStr`, but + /// it helps dereferencing other "container" types, + /// for example `Box` or `MutexGuard`. + #[inline] + // #[unstable(feature = "str_as_str", issue = "130366")] + #[unstable(feature = "bstr", issue = "134915")] + pub const fn as_mut_byte_str(&mut self) -> &mut ByteStr { + self + } + #[doc(hidden)] #[unstable(feature = "bstr_internals", issue = "none")] #[inline] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c8340c328be1..9a3f7166f1fc 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -2696,20 +2696,20 @@ fn assert_coerce_unsized( let _: RefCell<&dyn Send> = d; } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UnsafeCell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for SyncUnsafeCell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Cell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for RefCell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl<'b, T: ?Sized> PinCoerceUnsized for Ref<'b, T> {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl<'b, T: ?Sized> PinCoerceUnsized for RefMut<'b, T> {} diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index d1de2c560615..89cb06972392 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -74,12 +74,12 @@ impl char { /// The maximum number of bytes required to [encode](char::encode_utf8) a `char` to /// UTF-8 encoding. - #[stable(feature = "char_max_len_assoc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "char_max_len_assoc", since = "1.93.0")] pub const MAX_LEN_UTF8: usize = 4; /// The maximum number of two-byte units required to [encode](char::encode_utf16) a `char` /// to UTF-16 encoding. - #[stable(feature = "char_max_len_assoc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "char_max_len_assoc", since = "1.93.0")] pub const MAX_LEN_UTF16: usize = 2; /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 9a35ed07b89a..621277179bb3 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -648,6 +648,17 @@ impl CStr { pub fn display(&self) -> impl fmt::Display { crate::bstr::ByteStr::from_bytes(self.to_bytes()) } + + /// Returns the same string as a string slice `&CStr`. + /// + /// This method is redundant when used directly on `&CStr`, but + /// it helps dereferencing other string-like types to string slices, + /// for example references to `Box` or `Arc`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_c_str(&self) -> &CStr { + self + } } #[stable(feature = "c_string_eq_c_str", since = "1.90.0")] diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 4c59ea0cc532..d5166baf0c7c 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -31,6 +31,9 @@ use crate::marker::PhantomCovariantLifetime; // the pointer decay behavior in Rust, while otherwise matching Rust semantics. // This attribute ensures that the compiler uses the correct ABI for functions // like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly. +// +// The Clang `BuiltinVaListKind` enumerates the `va_list` variations that Clang supports, +// and we mirror these here. crate::cfg_select! { all( target_arch = "aarch64", @@ -124,6 +127,23 @@ crate::cfg_select! { } } + all(target_arch = "hexagon", target_env = "musl") => { + /// Hexagon Musl implementation of a `va_list`. + /// + /// See the [LLVM source] for more details. On bare metal Hexagon uses an opaque pointer. + /// + /// [LLVM source]: + /// https://github.com/llvm/llvm-project/blob/0cdc1b6dd4a870fc41d4b15ad97e0001882aba58/clang/lib/CodeGen/Targets/Hexagon.cpp#L407-L417 + #[repr(C)] + #[derive(Debug)] + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { + __current_saved_reg_area_pointer: *const c_void, + __saved_reg_area_end_pointer: *const c_void, + __overflow_area_pointer: *const c_void, + } + } + // The fallback implementation, used for: // // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 4ea6c6ba8fb9..197cddd3fa9d 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1226,7 +1226,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{}", wrapped), "'a'"); /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] #[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) @@ -1235,10 +1235,10 @@ pub fn from_fn) -> fmt::Result>(f: F) -> FromFn /// Implements [`fmt::Debug`] and [`fmt::Display`] via the provided closure. /// /// Created with [`from_fn`]. -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] pub struct FromFn(F); -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] impl fmt::Debug for FromFn where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, @@ -1248,7 +1248,7 @@ where } } -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] impl fmt::Display for FromFn where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 7a80023ce64e..e20109c3cc9a 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -41,7 +41,7 @@ pub use num_buffer::{NumBuffer, NumBufferTrait}; #[stable(feature = "debug_builders", since = "1.2.0")] pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] pub use self::builders::{FromFn, from_fn}; /// The type returned by formatter methods. diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index e2ac746d3149..4c050b49bf7e 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -269,6 +269,15 @@ pub const unsafe fn assert_unchecked(cond: bool) { #[stable(feature = "renamed_spin_loop", since = "1.49.0")] pub fn spin_loop() { crate::cfg_select! { + miri => { + unsafe extern "Rust" { + safe fn miri_spin_loop(); + } + + // Miri does support some of the intrinsics that are called below, but to guarantee + // consistent behavior across targets, this custom function is used. + miri_spin_loop(); + } target_arch = "x86" => { // SAFETY: the `cfg` attr ensures that we only execute this on x86 targets. crate::arch::x86::_mm_pause() diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 722e16ab0fa4..2179b451c375 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2754,6 +2754,22 @@ pub unsafe fn vtable_size(ptr: *const ()) -> usize; #[rustc_intrinsic] pub unsafe fn vtable_align(ptr: *const ()) -> usize; +/// The intrinsic returns the `U` vtable for `T` if `T` can be coerced to the trait object type `U`. +/// +/// # Compile-time failures +/// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler. +/// In some cases, that resolution can exceed the recursion limit, +/// and compilation will fail instead of this function returning `None`. +/// +/// # Safety +/// +/// `ptr` must point to a vtable. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +pub const fn vtable_for> + ?Sized>() +-> Option>; + /// The size of a type in bytes. /// /// Note that, unlike most intrinsics, this is safe to call; diff --git a/library/core/src/io/borrowed_buf.rs b/library/core/src/io/borrowed_buf.rs index b765b96fd00a..088dea781294 100644 --- a/library/core/src/io/borrowed_buf.rs +++ b/library/core/src/io/borrowed_buf.rs @@ -2,26 +2,27 @@ use crate::fmt::{self, Debug, Formatter}; use crate::mem::{self, MaybeUninit}; +use crate::{cmp, ptr}; -/// A borrowed buffer of initially uninitialized bytes, which is incrementally filled. +/// A borrowed byte buffer which is incrementally filled and initialized. /// -/// This type makes it safer to work with `MaybeUninit` buffers, such as to read into a buffer -/// without having to initialize it first. It tracks the region of bytes that have been filled and -/// the region that remains uninitialized. +/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the +/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet +/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a +/// subset of the initialized region. /// -/// The contents of the buffer can be visualized as: +/// In summary, the contents of the buffer can be visualized as: /// ```not_rust -/// [ capacity ] -/// [ len: filled and initialized | capacity - len: uninitialized ] +/// [ capacity ] +/// [ filled | unfilled ] +/// [ initialized | uninitialized ] /// ``` /// -/// Note that `BorrowedBuf` does not distinguish between uninitialized data and data that was -/// previously initialized but no longer contains valid data. -/// -/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique -/// reference (`&mut`). The `BorrowedBuf` can be configured (e.g., using `clear` or `set_len`), but -/// cannot be directly written. To write into the buffer, use `unfilled` to create a -/// `BorrowedCursor`. The cursor has write-only access to the unfilled portion of the buffer. +/// A `BorrowedBuf` is created around some existing data (or capacity for data) via a unique reference +/// (`&mut`). The `BorrowedBuf` can be configured (e.g., using `clear` or `set_init`), but cannot be +/// directly written. To write into the buffer, use `unfilled` to create a `BorrowedCursor`. The cursor +/// has write-only access to the unfilled portion of the buffer (you can think of it as a +/// write-only iterator). /// /// The lifetime `'data` is a bound on the lifetime of the underlying data. pub struct BorrowedBuf<'data> { @@ -29,11 +30,14 @@ pub struct BorrowedBuf<'data> { buf: &'data mut [MaybeUninit], /// The length of `self.buf` which is known to be filled. filled: usize, + /// The length of `self.buf` which is known to be initialized. + init: usize, } impl Debug for BorrowedBuf<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("BorrowedBuf") + .field("init", &self.init) .field("filled", &self.filled) .field("capacity", &self.capacity()) .finish() @@ -44,22 +48,24 @@ impl Debug for BorrowedBuf<'_> { impl<'data> From<&'data mut [u8]> for BorrowedBuf<'data> { #[inline] fn from(slice: &'data mut [u8]) -> BorrowedBuf<'data> { + let len = slice.len(); + BorrowedBuf { - // SAFETY: Always in bounds. We treat the buffer as uninitialized, even though it's - // already initialized. + // SAFETY: initialized data never becoming uninitialized is an invariant of BorrowedBuf buf: unsafe { (slice as *mut [u8]).as_uninit_slice_mut().unwrap() }, filled: 0, + init: len, } } } /// Creates a new `BorrowedBuf` from an uninitialized buffer. /// -/// Use `set_filled` if part of the buffer is known to be already filled. +/// Use `set_init` if part of the buffer is known to be already initialized. impl<'data> From<&'data mut [MaybeUninit]> for BorrowedBuf<'data> { #[inline] fn from(buf: &'data mut [MaybeUninit]) -> BorrowedBuf<'data> { - BorrowedBuf { buf, filled: 0 } + BorrowedBuf { buf, filled: 0, init: 0 } } } @@ -68,11 +74,14 @@ impl<'data> From<&'data mut [MaybeUninit]> for BorrowedBuf<'data> { /// Use `BorrowedCursor::with_unfilled_buf` instead for a safer alternative. impl<'data> From> for BorrowedBuf<'data> { #[inline] - fn from(buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> { + fn from(mut buf: BorrowedCursor<'data>) -> BorrowedBuf<'data> { + let init = buf.init_mut().len(); BorrowedBuf { - // SAFETY: Always in bounds. We treat the buffer as uninitialized. + // SAFETY: no initialized byte is ever uninitialized as per + // `BorrowedBuf`'s invariant buf: unsafe { buf.buf.buf.get_unchecked_mut(buf.buf.filled..) }, filled: 0, + init, } } } @@ -90,6 +99,12 @@ impl<'data> BorrowedBuf<'data> { self.filled } + /// Returns the length of the initialized part of the buffer. + #[inline] + pub fn init_len(&self) -> usize { + self.init + } + /// Returns a shared reference to the filled portion of the buffer. #[inline] pub fn filled(&self) -> &[u8] { @@ -144,16 +159,33 @@ impl<'data> BorrowedBuf<'data> { /// Clears the buffer, resetting the filled region to empty. /// - /// The contents of the buffer are not modified. + /// The number of initialized bytes is not changed, and the contents of the buffer are not modified. #[inline] pub fn clear(&mut self) -> &mut Self { self.filled = 0; self } + + /// Asserts that the first `n` bytes of the buffer are initialized. + /// + /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer + /// bytes than are already known to be initialized. + /// + /// # Safety + /// + /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized. + #[inline] + pub unsafe fn set_init(&mut self, n: usize) -> &mut Self { + self.init = cmp::max(self.init, n); + self + } } /// A writeable view of the unfilled portion of a [`BorrowedBuf`]. /// +/// The unfilled portion consists of an initialized and an uninitialized part; see [`BorrowedBuf`] +/// for details. +/// /// Data can be written directly to the cursor by using [`append`](BorrowedCursor::append) or /// indirectly by getting a slice of part or all of the cursor and writing into the slice. In the /// indirect case, the caller must call [`advance`](BorrowedCursor::advance) after writing to inform @@ -206,17 +238,48 @@ impl<'a> BorrowedCursor<'a> { self.buf.filled } + /// Returns a mutable reference to the initialized portion of the cursor. + #[inline] + pub fn init_mut(&mut self) -> &mut [u8] { + // SAFETY: We only slice the initialized part of the buffer, which is always valid + unsafe { + let buf = self.buf.buf.get_unchecked_mut(self.buf.filled..self.buf.init); + buf.assume_init_mut() + } + } + /// Returns a mutable reference to the whole cursor. /// /// # Safety /// - /// The caller must not uninitialize any previously initialized bytes. + /// The caller must not uninitialize any bytes in the initialized portion of the cursor. #[inline] pub unsafe fn as_mut(&mut self) -> &mut [MaybeUninit] { // SAFETY: always in bounds unsafe { self.buf.buf.get_unchecked_mut(self.buf.filled..) } } + /// Advances the cursor by asserting that `n` bytes have been filled. + /// + /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be + /// accessed via the underlying buffer. I.e., the buffer's filled portion grows by `n` elements + /// and its unfilled portion (and the capacity of this cursor) shrinks by `n` elements. + /// + /// If less than `n` bytes initialized (by the cursor's point of view), `set_init` should be + /// called first. + /// + /// # Panics + /// + /// Panics if there are less than `n` bytes initialized. + #[inline] + pub fn advance(&mut self, n: usize) -> &mut Self { + // The subtraction cannot underflow by invariant of this type. + assert!(n <= self.buf.init - self.buf.filled); + + self.buf.filled += n; + self + } + /// Advances the cursor by asserting that `n` bytes have been filled. /// /// After advancing, the `n` bytes are no longer accessible via the cursor and can only be @@ -225,11 +288,42 @@ impl<'a> BorrowedCursor<'a> { /// /// # Safety /// - /// The caller must ensure that the first `n` bytes of the cursor have been initialized. `n` - /// must not exceed the remaining capacity of this cursor. + /// The caller must ensure that the first `n` bytes of the cursor have been properly + /// initialised. #[inline] - pub unsafe fn advance(&mut self, n: usize) -> &mut Self { + pub unsafe fn advance_unchecked(&mut self, n: usize) -> &mut Self { self.buf.filled += n; + self.buf.init = cmp::max(self.buf.init, self.buf.filled); + self + } + + /// Initializes all bytes in the cursor. + #[inline] + pub fn ensure_init(&mut self) -> &mut Self { + // SAFETY: always in bounds and we never uninitialize these bytes. + let uninit = unsafe { self.buf.buf.get_unchecked_mut(self.buf.init..) }; + + // SAFETY: 0 is a valid value for MaybeUninit and the length matches the allocation + // since it is comes from a slice reference. + unsafe { + ptr::write_bytes(uninit.as_mut_ptr(), 0, uninit.len()); + } + self.buf.init = self.buf.capacity(); + + self + } + + /// Asserts that the first `n` unfilled bytes of the cursor are initialized. + /// + /// `BorrowedBuf` assumes that bytes are never de-initialized, so this method does nothing when + /// called with fewer bytes than are already known to be initialized. + /// + /// # Safety + /// + /// The caller must ensure that the first `n` bytes of the buffer have already been initialized. + #[inline] + pub unsafe fn set_init(&mut self, n: usize) -> &mut Self { + self.buf.init = cmp::max(self.buf.init, self.buf.filled + n); self } @@ -247,6 +341,10 @@ impl<'a> BorrowedCursor<'a> { self.as_mut()[..buf.len()].write_copy_of_slice(buf); } + // SAFETY: We just added the entire contents of buf to the filled section. + unsafe { + self.set_init(buf.len()); + } self.buf.filled += buf.len(); } @@ -269,9 +367,17 @@ impl<'a> BorrowedCursor<'a> { // there, one could mark some bytes as initialized even though there aren't. assert!(core::ptr::addr_eq(prev_ptr, buf.buf)); - // SAFETY: These bytes were filled in the `BorrowedBuf`, so they're filled in the cursor - // too, because the buffer wasn't replaced. - self.buf.filled += buf.filled; + let filled = buf.filled; + let init = buf.init; + + // Update `init` and `filled` fields with what was written to the buffer. + // `self.buf.filled` was the starting length of the `BorrowedBuf`. + // + // SAFETY: These amounts of bytes were initialized/filled in the `BorrowedBuf`, + // and therefore they are initialized/filled in the cursor too, because the + // buffer wasn't replaced. + self.buf.init = self.buf.filled + init; + self.buf.filled += filled; res } diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index e00cf45fcab2..e0001153a6e7 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -160,11 +160,10 @@ use crate::{fmt, intrinsics, ptr, slice}; /// /// ## Initializing a struct field-by-field /// -/// You can use `MaybeUninit`, and the [`std::ptr::addr_of_mut`] macro, to initialize structs field by field: +/// You can use `MaybeUninit` and the [`&raw mut`] syntax to initialize structs field by field: /// /// ```rust /// use std::mem::MaybeUninit; -/// use std::ptr::addr_of_mut; /// /// #[derive(Debug, PartialEq)] /// pub struct Foo { @@ -179,11 +178,11 @@ use crate::{fmt, intrinsics, ptr, slice}; /// // Initializing the `name` field /// // Using `write` instead of assignment via `=` to not call `drop` on the /// // old, uninitialized value. -/// unsafe { addr_of_mut!((*ptr).name).write("Bob".to_string()); } +/// unsafe { (&raw mut (*ptr).name).write("Bob".to_string()); } /// /// // Initializing the `list` field /// // If there is a panic here, then the `String` in the `name` field leaks. -/// unsafe { addr_of_mut!((*ptr).list).write(vec![0, 1, 2]); } +/// unsafe { (&raw mut (*ptr).list).write(vec![0, 1, 2]); } /// /// // All the fields are initialized, so we call `assume_init` to get an initialized Foo. /// unsafe { uninit.assume_init() } @@ -197,7 +196,7 @@ use crate::{fmt, intrinsics, ptr, slice}; /// } /// ); /// ``` -/// [`std::ptr::addr_of_mut`]: crate::ptr::addr_of_mut +/// [`&raw mut`]: https://doc.rust-lang.org/reference/types/pointer.html#r-type.pointer.raw.constructor /// [ub]: ../../reference/behavior-considered-undefined.html /// /// # Layout @@ -1140,8 +1139,8 @@ impl [MaybeUninit] { /// ``` /// /// [`write_clone_of_slice`]: slice::write_clone_of_slice - #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "maybe_uninit_write_slice", since = "1.93.0")] + #[rustc_const_stable(feature = "maybe_uninit_write_slice", since = "1.93.0")] pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Copy, @@ -1201,7 +1200,7 @@ impl [MaybeUninit] { /// ``` /// /// [`write_copy_of_slice`]: slice::write_copy_of_slice - #[stable(feature = "maybe_uninit_write_slice", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "maybe_uninit_write_slice", since = "1.93.0")] pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Clone, @@ -1463,7 +1462,7 @@ impl [MaybeUninit] { /// requirement the compiler knows about it is that the data pointer must be /// non-null. Dropping such a `Vec` however will cause undefined /// behaviour. - #[stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "maybe_uninit_slice", since = "1.93.0")] #[inline(always)] #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] pub const unsafe fn assume_init_drop(&mut self) @@ -1485,8 +1484,8 @@ impl [MaybeUninit] { /// Calling this when the content is not yet fully initialized causes undefined /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in /// the slice really is in an initialized state. - #[stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "maybe_uninit_slice", since = "1.93.0")] + #[rustc_const_stable(feature = "maybe_uninit_slice", since = "1.93.0")] #[inline(always)] pub const unsafe fn assume_init_ref(&self) -> &[T] { // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that @@ -1504,8 +1503,8 @@ impl [MaybeUninit] { /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in the /// slice really is in an initialized state. For instance, `.assume_init_mut()` cannot /// be used to initialize a `MaybeUninit` slice. - #[stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "maybe_uninit_slice", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "maybe_uninit_slice", since = "1.93.0")] + #[rustc_const_stable(feature = "maybe_uninit_slice", since = "1.93.0")] #[inline(always)] pub const unsafe fn assume_init_mut(&mut self) -> &mut [T] { // SAFETY: similar to safety notes for `slice_get_ref`, but we have a diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 4a3ceb3afa38..f49f5f3c3afb 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1275,8 +1275,8 @@ macro_rules! int_impl { /// i.e. when [`checked_neg`] would return `None`. /// #[doc = concat!("[`checked_neg`]: ", stringify!($SelfT), "::checked_neg")] - #[stable(feature = "unchecked_neg", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unchecked_neg", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unchecked_neg", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_neg", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1392,8 +1392,8 @@ macro_rules! int_impl { /// i.e. when [`checked_shl`] would return `None`. /// #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")] - #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1564,8 +1564,8 @@ macro_rules! int_impl { /// i.e. when [`checked_shr`] would return `None`. /// #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")] - #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -2288,6 +2288,13 @@ macro_rules! int_impl { /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes /// any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `<<` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shl`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to /// the range of the type, rather than the bits shifted out of the LHS being returned to the other end. /// The primitive integer types all implement a [`rotate_left`](Self::rotate_left) function, @@ -2296,8 +2303,11 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128);")] - #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);")] + #[doc = concat!("assert_eq!((-1_", stringify!($SelfT), ").wrapping_shl(7), -128);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(1).wrapping_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!((-1_", stringify!($SelfT), ").wrapping_shl(128), -1);")] + #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".wrapping_shl(1025), 10);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] @@ -2315,6 +2325,13 @@ macro_rules! int_impl { /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` /// removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `>>` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shr`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted /// to the range of the type, rather than the bits shifted out of the LHS being returned to the other /// end. The primitive integer types all implement a [`rotate_right`](Self::rotate_right) function, @@ -2323,8 +2340,11 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1);")] - /// assert_eq!((-128i16).wrapping_shr(64), -128); + #[doc = concat!("assert_eq!((-128_", stringify!($SelfT), ").wrapping_shr(7), -1);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(1).wrapping_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + /// assert_eq!((-128_i16).wrapping_shr(64), -128); + #[doc = concat!("assert_eq!(10_", stringify!($SelfT), ".wrapping_shr(1025), 5);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 34f377661bb7..bf901c9b4ad3 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1851,8 +1851,8 @@ macro_rules! uint_impl { /// i.e. when [`checked_shl`] would return `None`. /// #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")] - #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -2020,8 +2020,8 @@ macro_rules! uint_impl { /// i.e. when [`checked_shr`] would return `None`. /// #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")] - #[stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "unchecked_shifts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -2593,6 +2593,13 @@ macro_rules! uint_impl { /// where `mask` removes any high-order bits of `rhs` that /// would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `<<` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shl`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-left; the /// RHS of a wrapping shift-left is restricted to the range /// of the type, rather than the bits shifted out of the LHS @@ -2603,8 +2610,15 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128);")] - #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);")] + #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(7), 128);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(0), 0b101);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(1), 0b1010);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(2), 0b10100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_shl(2), ", stringify!($SelfT), "::MAX - 3);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(1).wrapping_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(128), 1);")] + #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".wrapping_shl(1025), 10);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] @@ -2623,6 +2637,13 @@ macro_rules! uint_impl { /// where `mask` removes any high-order bits of `rhs` that /// would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `>>` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shr`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-right; the /// RHS of a wrapping shift-right is restricted to the range /// of the type, rather than the bits shifted out of the LHS @@ -2633,8 +2654,15 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1);")] - #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);")] + #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(7), 1);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(0), 0b1010);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(1), 0b101);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(2), 0b10);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_shr(1), ", stringify!($SignedT), "::MAX.cast_unsigned());")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(1).wrapping_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(128), 128);")] + #[doc = concat!("assert_eq!(10_", stringify!($SelfT), ".wrapping_shr(1025), 5);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 479368ba8f80..efe5c0871fb8 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -73,7 +73,7 @@ use crate::marker::Tuple; #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] -pub const trait Fn: FnMut { +pub const trait Fn: [const] FnMut { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call(&self, args: Args) -> Self::Output; diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 81c2dabf0d1d..74ecb5ee4946 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -1825,7 +1825,7 @@ where { } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] /// Trait that indicates that this is a pointer or a wrapper for one, where /// unsizing can be performed on the pointee when it is pinned. /// diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 2860bf0a6e69..00b71f9a997c 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1462,8 +1462,8 @@ impl *const [T] { /// Gets a raw pointer to the underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_array(self) -> Option<*const [T; N]> { diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index f8dc6ef7ed71..8976154c61db 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1712,8 +1712,8 @@ impl *mut [T] { /// Gets a raw, mutable pointer to the underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_mut_array(self) -> Option<*mut [T; N]> { diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index 1bbe3ea242f6..3160c9de4b7e 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -176,7 +176,7 @@ impl CoerceUnsized> for Unique wh #[unstable(feature = "ptr_internals", issue = "none")] impl DispatchFromDyn> for Unique where T: Unsize {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Unique {} #[unstable(feature = "ptr_internals", issue = "none")] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index dc34d2a21ee9..889fd4cd65df 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -842,8 +842,8 @@ impl [T] { /// Gets a reference to the underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_array(&self) -> Option<&[T; N]> { @@ -861,8 +861,8 @@ impl [T] { /// Gets a mutable reference to the slice's underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_mut_array(&mut self) -> Option<&mut [T; N]> { @@ -4908,6 +4908,28 @@ impl [T] { if start <= self.len() && end <= self.len() { Some(start..end) } else { None } } + + /// Returns the same slice `&[T]`. + /// + /// This method is redundant when used directly on `&[T]`, but + /// it helps dereferencing other "container" types to slices, + /// for example `Box<[T]>` or `Arc<[T]>`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_slice(&self) -> &[T] { + self + } + + /// Returns the same slice `&mut [T]`. + /// + /// This method is redundant when used directly on `&mut [T]`, but + /// it helps dereferencing other "container" types to slices, + /// for example `Box<[T]>` or `MutexGuard<[T]>`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_mut_slice(&mut self) -> &mut [T] { + self + } } impl [MaybeUninit] { diff --git a/library/core/src/time.rs b/library/core/src/time.rs index b85179e925d1..940129d68816 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -325,8 +325,8 @@ impl Duration { /// assert_eq!(10_u64.pow(15), duration.as_secs()); /// assert_eq!(321, duration.subsec_nanos()); /// ``` - #[stable(feature = "duration_from_nanos_u128", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "duration_from_nanos_u128", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "duration_from_nanos_u128", since = "1.93.0")] + #[rustc_const_stable(feature = "duration_from_nanos_u128", since = "1.93.0")] #[must_use] #[inline] #[track_caller] diff --git a/library/coretests/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs index 744a6a0d2dd8..c6d841b8383a 100644 --- a/library/coretests/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs @@ -1,5 +1,8 @@ use core::any::TypeId; -use core::intrinsics::assume; +use core::intrinsics::{assume, vtable_for}; +use std::fmt::Debug; +use std::option::Option; +use std::ptr::DynMetadata; #[test] fn test_typeid_sized_types() { @@ -193,3 +196,17 @@ fn carrying_mul_add_fallback_i128() { (u128::MAX - 1, -(i128::MIN / 2)), ); } + +#[test] +fn test_vtable_for() { + #[derive(Debug)] + struct A {} + + struct B {} + + const A_VTABLE: Option> = vtable_for::(); + assert!(A_VTABLE.is_some()); + + const B_VTABLE: Option> = vtable_for::(); + assert!(B_VTABLE.is_none()); +} diff --git a/library/coretests/tests/io/borrowed_buf.rs b/library/coretests/tests/io/borrowed_buf.rs index 730ba04465a1..aaa98d26ff8b 100644 --- a/library/coretests/tests/io/borrowed_buf.rs +++ b/library/coretests/tests/io/borrowed_buf.rs @@ -8,6 +8,7 @@ fn new() { let mut rbuf: BorrowedBuf<'_> = buf.into(); assert_eq!(rbuf.filled().len(), 0); + assert_eq!(rbuf.init_len(), 16); assert_eq!(rbuf.capacity(), 16); assert_eq!(rbuf.unfilled().capacity(), 16); } @@ -19,16 +20,27 @@ fn uninit() { let mut rbuf: BorrowedBuf<'_> = buf.into(); assert_eq!(rbuf.filled().len(), 0); + assert_eq!(rbuf.init_len(), 0); assert_eq!(rbuf.capacity(), 16); assert_eq!(rbuf.unfilled().capacity(), 16); } +#[test] +fn initialize_unfilled() { + let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; + let mut rbuf: BorrowedBuf<'_> = buf.into(); + + rbuf.unfilled().ensure_init(); + + assert_eq!(rbuf.init_len(), 16); +} + #[test] fn advance_filled() { let buf: &mut [_] = &mut [0; 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); - unsafe { rbuf.unfilled().advance(1) }; + rbuf.unfilled().advance(1); assert_eq!(rbuf.filled().len(), 1); assert_eq!(rbuf.unfilled().capacity(), 15); @@ -39,7 +51,7 @@ fn clear() { let buf: &mut [_] = &mut [255; 16]; let mut rbuf: BorrowedBuf<'_> = buf.into(); - unsafe { rbuf.unfilled().advance(16) }; + rbuf.unfilled().advance(16); assert_eq!(rbuf.filled().len(), 16); assert_eq!(rbuf.unfilled().capacity(), 0); @@ -49,9 +61,33 @@ fn clear() { assert_eq!(rbuf.filled().len(), 0); assert_eq!(rbuf.unfilled().capacity(), 16); - unsafe { rbuf.unfilled().advance(16) }; + assert_eq!(rbuf.unfilled().init_mut(), [255; 16]); +} - assert_eq!(rbuf.filled(), [255; 16]); +#[test] +fn set_init() { + let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16]; + let mut rbuf: BorrowedBuf<'_> = buf.into(); + + unsafe { + rbuf.set_init(8); + } + + assert_eq!(rbuf.init_len(), 8); + + rbuf.unfilled().advance(4); + + unsafe { + rbuf.set_init(2); + } + + assert_eq!(rbuf.init_len(), 8); + + unsafe { + rbuf.set_init(8); + } + + assert_eq!(rbuf.init_len(), 8); } #[test] @@ -61,6 +97,7 @@ fn append() { rbuf.unfilled().append(&[0; 8]); + assert_eq!(rbuf.init_len(), 8); assert_eq!(rbuf.filled().len(), 8); assert_eq!(rbuf.filled(), [0; 8]); @@ -68,6 +105,7 @@ fn append() { rbuf.unfilled().append(&[1; 16]); + assert_eq!(rbuf.init_len(), 16); assert_eq!(rbuf.filled().len(), 16); assert_eq!(rbuf.filled(), [1; 16]); } @@ -87,12 +125,43 @@ fn reborrow_written() { assert_eq!(cursor.written(), 32); assert_eq!(buf.unfilled().written(), 32); + assert_eq!(buf.init_len(), 32); assert_eq!(buf.filled().len(), 32); let filled = buf.filled(); assert_eq!(&filled[..16], [1; 16]); assert_eq!(&filled[16..], [2; 16]); } +#[test] +fn cursor_set_init() { + let buf: &mut [_] = &mut [MaybeUninit::zeroed(); 16]; + let mut rbuf: BorrowedBuf<'_> = buf.into(); + + unsafe { + rbuf.unfilled().set_init(8); + } + + assert_eq!(rbuf.init_len(), 8); + assert_eq!(rbuf.unfilled().init_mut().len(), 8); + assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 16); + + rbuf.unfilled().advance(4); + + unsafe { + rbuf.unfilled().set_init(2); + } + + assert_eq!(rbuf.init_len(), 8); + + unsafe { + rbuf.unfilled().set_init(8); + } + + assert_eq!(rbuf.init_len(), 12); + assert_eq!(rbuf.unfilled().init_mut().len(), 8); + assert_eq!(unsafe { rbuf.unfilled().as_mut().len() }, 12); +} + #[test] fn cursor_with_unfilled_buf() { let buf: &mut [_] = &mut [MaybeUninit::uninit(); 16]; @@ -100,30 +169,31 @@ fn cursor_with_unfilled_buf() { let mut cursor = rbuf.unfilled(); cursor.with_unfilled_buf(|buf| { - assert_eq!(buf.capacity(), 16); buf.unfilled().append(&[1, 2, 3]); assert_eq!(buf.filled(), &[1, 2, 3]); }); + assert_eq!(cursor.init_mut().len(), 0); assert_eq!(cursor.written(), 3); cursor.with_unfilled_buf(|buf| { assert_eq!(buf.capacity(), 13); + assert_eq!(buf.init_len(), 0); - unsafe { - buf.unfilled().as_mut().write_filled(0); - buf.unfilled().advance(4) - }; + buf.unfilled().ensure_init(); + buf.unfilled().advance(4); }); + assert_eq!(cursor.init_mut().len(), 9); assert_eq!(cursor.written(), 7); cursor.with_unfilled_buf(|buf| { assert_eq!(buf.capacity(), 9); + assert_eq!(buf.init_len(), 9); }); + assert_eq!(cursor.init_mut().len(), 9); assert_eq!(cursor.written(), 7); - assert_eq!(rbuf.len(), 7); assert_eq!(rbuf.filled(), &[1, 2, 3, 0, 0, 0, 0]); } diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index dccb6b5528d4..65f1289d531b 100644 --- a/library/coretests/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs @@ -204,8 +204,8 @@ fn many_digits() { "1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38" ); assert_float_result_bits_eq!( - 0x7ffffe, - f32, - "1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38" + 0x7ffffffffffffe, + f64, + "2.8480945388892171379514712013899006561925415836684281158317117472799232118121416898288331376688117187382156519616555919469923055562697333532151065976805407006156569379201589813489881051279092585294136284133893120582268661680741374638184352784785121471797815387841323043061183721896440504100014432145713413240639315126126485370149254502658324386101932795656342787961697168715161403422599767855490566539165801964388919848888980308652852766053138934233069066651644954964960514065582826296567812754916294792554028205611526494813491373571799099361175786448799007387647056059512705071170383000860694587462575533337769237800154235626508997957207085308694919708914563870082480025309479610576861709891209343110514894825624524442569257542956506767950486391782760620117187500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-306" ); } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 9287807c43e3..a0b6656c8ded 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -22,7 +22,6 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![cfg_attr(bootstrap, feature(maybe_uninit_write_slice))] #![feature(negative_impls)] #![feature(panic_can_unwind)] #![feature(restricted_std)] diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index a1dd8e5f86f4..4e4d377ae270 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -13,7 +13,7 @@ use crate::rc::Rc; use crate::str::FromStr; use crate::sync::Arc; use crate::sys::os_str::{Buf, Slice}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{cmp, fmt, slice}; /// A type that can represent owned, mutable platform-native strings, but is @@ -1278,6 +1278,17 @@ impl OsStr { pub fn display(&self) -> Display<'_> { Display { os_str: self } } + + /// Returns the same string as a string slice `&OsStr`. + /// + /// This method is redundant when used directly on `&OsStr`, but + /// it helps dereferencing other string-like types to string slices, + /// for example references to `Box` or `Arc`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_os_str(&self) -> &OsStr { + self + } } #[stable(feature = "box_from_os_str", since = "1.17.0")] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index d14d0ad2f0ea..f078dec21d0a 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -46,8 +46,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, use crate::path::{Path, PathBuf}; use crate::sealed::Sealed; use crate::sync::Arc; -use crate::sys::fs as fs_imp; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp}; use crate::time::SystemTime; use crate::{error, fmt}; diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index bcaafcfee787..0a5d1153d860 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -709,6 +709,8 @@ fn file_test_read_buf() { let mut file = check!(File::open(filename)); check!(file.read_buf(buf.unfilled())); assert_eq!(buf.filled(), &[1, 2, 3, 4]); + // File::read_buf should omit buffer initialization. + assert_eq!(buf.init_len(), 4); check!(fs::remove_file(filename)); } diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index 69c260b5410a..40441dc057d0 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -284,6 +284,15 @@ impl BufReader { } } +// This is only used by a test which asserts that the initialization-tracking is correct. +#[cfg(test)] +impl BufReader { + #[allow(missing_docs)] + pub fn initialized(&self) -> usize { + self.buf.initialized() + } +} + impl BufReader { /// Seeks relative to the current position. If the new position lies within the buffer, /// the buffer will not be flushed, allowing for more efficient seeks. diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 2694726b3f44..9b600cd55758 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -21,19 +21,25 @@ pub struct Buffer { // Each call to `fill_buf` sets `filled` to indicate how many bytes at the start of `buf` are // initialized with bytes from a read. filled: usize, + // This is the max number of bytes returned across all `fill_buf` calls. We track this so that we + // can accurately tell `read_buf` how many bytes of buf are initialized, to bypass as much of its + // defensive initialization as possible. Note that while this often the same as `filled`, it + // doesn't need to be. Calls to `fill_buf` are not required to actually fill the buffer, and + // omitting this is a huge perf regression for `Read` impls that do not. + initialized: usize, } impl Buffer { #[inline] pub fn with_capacity(capacity: usize) -> Self { let buf = Box::new_uninit_slice(capacity); - Self { buf, pos: 0, filled: 0 } + Self { buf, pos: 0, filled: 0, initialized: 0 } } #[inline] pub fn try_with_capacity(capacity: usize) -> io::Result { match Box::try_new_uninit_slice(capacity) { - Ok(buf) => Ok(Self { buf, pos: 0, filled: 0 }), + Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }), Err(_) => { Err(io::const_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) } @@ -62,6 +68,12 @@ impl Buffer { self.pos } + // This is only used by a test which asserts that the initialization-tracking is correct. + #[cfg(test)] + pub fn initialized(&self) -> usize { + self.initialized + } + #[inline] pub fn discard_buffer(&mut self) { self.pos = 0; @@ -98,8 +110,13 @@ impl Buffer { /// Read more bytes into the buffer without discarding any of its contents pub fn read_more(&mut self, mut reader: impl Read) -> io::Result { let mut buf = BorrowedBuf::from(&mut self.buf[self.filled..]); + let old_init = self.initialized - self.filled; + unsafe { + buf.set_init(old_init); + } reader.read_buf(buf.unfilled())?; self.filled += buf.len(); + self.initialized += buf.init_len() - old_init; Ok(buf.len()) } @@ -120,10 +137,16 @@ impl Buffer { debug_assert!(self.pos == self.filled); let mut buf = BorrowedBuf::from(&mut *self.buf); + // SAFETY: `self.filled` bytes will always have been initialized. + unsafe { + buf.set_init(self.initialized); + } + let result = reader.read_buf(buf.unfilled()); self.pos = 0; self.filled = buf.len(); + self.initialized = buf.init_len(); result?; } diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index 068dca819775..6ad4158b9290 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -172,7 +172,7 @@ fn test_buffered_reader_stream_position_panic() { // cause internal buffer to be filled but read only partially let mut buffer = [0, 0]; assert!(reader.read_exact(&mut buffer).is_ok()); - // rewinding the internal reader will cause buffer to loose sync + // rewinding the internal reader will cause buffer to lose sync let inner = reader.get_mut(); assert!(inner.seek(SeekFrom::Start(0)).is_ok()); // overflow when subtracting the remaining buffer size from current position @@ -1052,6 +1052,30 @@ fn single_formatted_write() { assert_eq!(writer.get_ref().events, [RecordedEvent::Write("hello, world!\n".to_string())]); } +#[test] +fn bufreader_full_initialize() { + struct OneByteReader; + impl Read for OneByteReader { + fn read(&mut self, buf: &mut [u8]) -> crate::io::Result { + if buf.len() > 0 { + buf[0] = 0; + Ok(1) + } else { + Ok(0) + } + } + } + let mut reader = BufReader::new(OneByteReader); + // Nothing is initialized yet. + assert_eq!(reader.initialized(), 0); + + let buf = reader.fill_buf().unwrap(); + // We read one byte... + assert_eq!(buf.len(), 1); + // But we initialized the whole buffer! + assert_eq!(reader.initialized(), reader.capacity()); +} + /// This is a regression test for https://github.com/rust-lang/rust/issues/127584. #[test] fn bufwriter_aliasing() { diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 8b5e7c4df4e0..2b558efb8885 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -214,19 +214,28 @@ impl BufferedWriterSpec for BufWriter { } let mut len = 0; + let mut init = 0; loop { let buf = self.buffer_mut(); let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into(); + unsafe { + // SAFETY: init is either 0 or the init_len from the previous iteration. + read_buf.set_init(init); + } + if read_buf.capacity() >= DEFAULT_BUF_SIZE { let mut cursor = read_buf.unfilled(); match reader.read_buf(cursor.reborrow()) { Ok(()) => { let bytes_read = cursor.written(); + if bytes_read == 0 { return Ok(len); } + + init = read_buf.init_len() - bytes_read; len += bytes_read as u64; // SAFETY: BorrowedBuf guarantees all of its filled bytes are init @@ -239,6 +248,10 @@ impl BufferedWriterSpec for BufWriter { Err(e) => return Err(e), } } else { + // All the bytes that were already in the buffer are initialized, + // treat them as such when the buffer is flushed. + init += buf.len(); + self.flush_buf()?; } } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 0167be4d514c..528eb185df08 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,6 +1,13 @@ #[cfg(test)] mod tests; +// On 64-bit platforms, `io::Error` may use a bit-packed representation to +// reduce size. However, this representation assumes that error codes are +// always 32-bit wide. +// +// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. +// Therefore, the packed representation is explicitly disabled for UEFI +// targets, and the unpacked representation must be used instead. #[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] mod repr_bitpacked; #[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] @@ -139,7 +146,7 @@ enum ErrorData { /// /// [`into`]: Into::into #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub type RawOsError = sys::RawOsError; +pub type RawOsError = sys::io::RawOsError; // `#[repr(align(4))]` is probably redundant, it should have that value or // higher already. We include it just because repr_bitpacked.rs's encoding diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 4c064c435e5b..b7756befa11e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -419,6 +419,8 @@ pub(crate) fn default_read_to_end( .and_then(|s| s.checked_add(1024)?.checked_next_multiple_of(DEFAULT_BUF_SIZE)) .unwrap_or(DEFAULT_BUF_SIZE); + let mut initialized = 0; // Extra initialized bytes from previous loop iteration + const PROBE_SIZE: usize = 32; fn small_probe_read(r: &mut R, buf: &mut Vec) -> Result { @@ -447,6 +449,8 @@ pub(crate) fn default_read_to_end( } } + let mut consecutive_short_reads = 0; + loop { if buf.len() == buf.capacity() && buf.capacity() == start_cap { // The buffer might be an exact fit. Let's read into a probe buffer @@ -470,6 +474,11 @@ pub(crate) fn default_read_to_end( spare = &mut spare[..buf_len]; let mut read_buf: BorrowedBuf<'_> = spare.into(); + // SAFETY: These bytes were initialized but not filled in the previous loop + unsafe { + read_buf.set_init(initialized); + } + let mut cursor = read_buf.unfilled(); let result = loop { match r.read_buf(cursor.reborrow()) { @@ -480,7 +489,9 @@ pub(crate) fn default_read_to_end( } }; + let unfilled_but_initialized = cursor.init_mut().len(); let bytes_read = cursor.written(); + let was_fully_initialized = read_buf.init_len() == buf_len; // SAFETY: BorrowedBuf's invariants mean this much memory is initialized. unsafe { @@ -495,8 +506,27 @@ pub(crate) fn default_read_to_end( return Ok(buf.len() - start_len); } + if bytes_read < buf_len { + consecutive_short_reads += 1; + } else { + consecutive_short_reads = 0; + } + + // store how much was initialized but not filled + initialized = unfilled_but_initialized; + // Use heuristics to determine the max read size if no initial size hint was provided if size_hint.is_none() { + // The reader is returning short reads but it doesn't call ensure_init(). + // In that case we no longer need to restrict read sizes to avoid + // initialization costs. + // When reading from disk we usually don't get any short reads except at EOF. + // So we wait for at least 2 short reads before uncapping the read buffer; + // this helps with the Windows issue. + if !was_fully_initialized && consecutive_short_reads > 1 { + max_read_size = usize::MAX; + } + // we have passed a larger buffer than previously and the // reader still hasn't returned a short read if buf_len >= max_read_size && bytes_read == buf_len { @@ -557,13 +587,8 @@ pub(crate) fn default_read_buf(read: F, mut cursor: BorrowedCursor<'_>) -> Re where F: FnOnce(&mut [u8]) -> Result, { - // SAFETY: We do not uninitialize any part of the buffer. - let n = read(unsafe { cursor.as_mut().write_filled(0) })?; - assert!(n <= cursor.capacity()); - // SAFETY: We've initialized the entire buffer, and `read` can't make it uninitialized. - unsafe { - cursor.advance(n); - } + let n = read(cursor.ensure_init().init_mut())?; + cursor.advance(n); Ok(()) } @@ -3073,21 +3098,31 @@ impl Read for Take { // The condition above guarantees that `self.limit` fits in `usize`. let limit = self.limit as usize; + let extra_init = cmp::min(limit, buf.init_mut().len()); + // SAFETY: no uninit data is written to ibuf let ibuf = unsafe { &mut buf.as_mut()[..limit] }; let mut sliced_buf: BorrowedBuf<'_> = ibuf.into(); + // SAFETY: extra_init bytes of ibuf are known to be initialized + unsafe { + sliced_buf.set_init(extra_init); + } + let mut cursor = sliced_buf.unfilled(); let result = self.inner.read_buf(cursor.reborrow()); + let new_init = cursor.init_mut().len(); let filled = sliced_buf.len(); // cursor / sliced_buf / ibuf must drop here - // SAFETY: filled bytes have been filled and therefore initialized unsafe { - buf.advance(filled); + // SAFETY: filled bytes have been filled and therefore initialized + buf.advance_unchecked(filled); + // SAFETY: new_init bytes of buf's unfilled buffer have been initialized + buf.set_init(new_init); } self.limit -= filled as u64; diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs index 16727d445416..61b81cf074a6 100644 --- a/library/std/src/io/pipe.rs +++ b/library/std/src/io/pipe.rs @@ -1,6 +1,5 @@ use crate::io; -use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; -use crate::sys_common::{FromInner, IntoInner}; +use crate::sys::{FromInner, IntoInner, pipe as imp}; /// Creates an anonymous pipe. /// @@ -84,39 +83,39 @@ use crate::sys_common::{FromInner, IntoInner}; #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[inline] pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { - pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) + imp::pipe().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) } /// Read end of an anonymous pipe. #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] -pub struct PipeReader(pub(crate) AnonPipe); +pub struct PipeReader(pub(crate) imp::Pipe); /// Write end of an anonymous pipe. #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] -pub struct PipeWriter(pub(crate) AnonPipe); +pub struct PipeWriter(pub(crate) imp::Pipe); -impl FromInner for PipeReader { - fn from_inner(inner: AnonPipe) -> Self { +impl FromInner for PipeReader { + fn from_inner(inner: imp::Pipe) -> Self { Self(inner) } } -impl IntoInner for PipeReader { - fn into_inner(self) -> AnonPipe { +impl IntoInner for PipeReader { + fn into_inner(self) -> imp::Pipe { self.0 } } -impl FromInner for PipeWriter { - fn from_inner(inner: AnonPipe) -> Self { +impl FromInner for PipeWriter { + fn from_inner(inner: imp::Pipe) -> Self { Self(inner) } } -impl IntoInner for PipeWriter { - fn into_inner(self) -> AnonPipe { +impl IntoInner for PipeWriter { + fn into_inner(self) -> imp::Pipe { self.0 } } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index e14e6432eafa..b22988d4a8a9 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -209,6 +209,15 @@ fn read_buf_exact() { assert_eq!(c.read_buf_exact(buf.unfilled()).unwrap_err().kind(), io::ErrorKind::UnexpectedEof); } +#[test] +#[should_panic] +fn borrowed_cursor_advance_overflow() { + let mut buf = [0; 512]; + let mut buf = BorrowedBuf::from(&mut buf[..]); + buf.unfilled().advance(1); + buf.unfilled().advance(usize::MAX); +} + #[test] fn take_eof() { struct R; diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index a09c8bc06930..0410df3ef1a3 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -283,7 +283,7 @@ impl Read for Repeat { // SAFETY: No uninit bytes are being written. unsafe { buf.as_mut() }.write_filled(self.byte); // SAFETY: the entire unfilled portion of buf has been initialized. - unsafe { buf.advance(buf.capacity()) }; + unsafe { buf.advance_unchecked(buf.capacity()) }; Ok(()) } diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index 92dbc3919bea..d0f106d7af41 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -75,36 +75,43 @@ fn empty_reads() { let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let buf: &mut [_] = &mut [MaybeUninit::uninit()]; let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let buf: &mut [MaybeUninit<_>] = &mut []; let mut buf: BorrowedBuf<'_> = buf.into(); e.read_buf_exact(buf.unfilled()).unwrap(); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let buf: &mut [_] = &mut [MaybeUninit::uninit()]; let mut buf: BorrowedBuf<'_> = buf.into(); assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024]; let mut buf: BorrowedBuf<'_> = buf.into(); @@ -113,6 +120,7 @@ fn empty_reads() { ErrorKind::UnexpectedEof, ); assert_eq!(buf.len(), 0); + assert_eq!(buf.init_len(), 0); let mut buf = Vec::new(); assert_eq!(e.read_to_end(&mut buf).unwrap(), 0); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 8fb1b1b05d20..508a6ed85261 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -667,7 +667,7 @@ pub mod arch { pub use std_detect::is_loongarch_feature_detected; #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] pub use std_detect::is_riscv_feature_detected; - #[stable(feature = "stdarch_s390x_feature_detection", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] pub use std_detect::is_s390x_feature_detected; #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index ae50f531a716..dac568e419f3 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -16,8 +16,7 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 4787f8a1040b..7c7ef7b2f701 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -315,6 +315,8 @@ fn read_buf() { let mut buf = BorrowedBuf::from(buf.as_mut_slice()); t!(s.read_buf(buf.unfilled())); assert_eq!(buf.filled(), &[1, 2, 3, 4]); + // TcpStream::read_buf should omit buffer initialization. + assert_eq!(buf.init_len(), 4); t.join().ok().expect("thread panicked"); }) diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 72e292e3d157..136803ab16f1 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -13,8 +13,7 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/os/aix/fs.rs b/library/std/src/os/aix/fs.rs index ac9dd45f0542..2a940c6b9c97 100644 --- a/library/std/src/os/aix/fs.rs +++ b/library/std/src/os/aix/fs.rs @@ -5,7 +5,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/android/fs.rs b/library/std/src/os/android/fs.rs index 6b931e381695..d67c5582fd91 100644 --- a/library/std/src/os/android/fs.rs +++ b/library/std/src/os/android/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::android::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/cygwin/fs.rs b/library/std/src/os/cygwin/fs.rs index 5533264fd515..42623e6d47ad 100644 --- a/library/std/src/os/cygwin/fs.rs +++ b/library/std/src/os/cygwin/fs.rs @@ -1,6 +1,6 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: crate::fs::Metadata diff --git a/library/std/src/os/darwin/fs.rs b/library/std/src/os/darwin/fs.rs index 5740c86e6218..af98e069b41a 100644 --- a/library/std/src/os/darwin/fs.rs +++ b/library/std/src/os/darwin/fs.rs @@ -5,7 +5,7 @@ use crate::fs::{self, Metadata}; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; /// OS-specific extensions to [`fs::Metadata`]. diff --git a/library/std/src/os/dragonfly/fs.rs b/library/std/src/os/dragonfly/fs.rs index 0cd543b2ab97..b47b5108c70e 100644 --- a/library/std/src/os/dragonfly/fs.rs +++ b/library/std/src/os/dragonfly/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::dragonfly::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/emscripten/fs.rs b/library/std/src/os/emscripten/fs.rs index 81f9ef331a5f..a5778812ad94 100644 --- a/library/std/src/os/emscripten/fs.rs +++ b/library/std/src/os/emscripten/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::emscripten::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/espidf/fs.rs b/library/std/src/os/espidf/fs.rs index ffff584cda02..cd09efca2db5 100644 --- a/library/std/src/os/espidf/fs.rs +++ b/library/std/src/os/espidf/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::espidf::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 34479ca0e190..98faf0709153 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -1,6 +1,6 @@ use crate::os::fd::owned::OwnedFd; use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{net, sys}; macro_rules! impl_as_raw_fd { diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 6a0e7a640028..846ad37aad22 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -20,7 +20,7 @@ use crate::mem::ManuallyDrop; )))] use crate::sys::cvt; #[cfg(not(target_os = "trusty"))] -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, io}; type ValidRawFd = core::num::niche_types::NotAllOnes; diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index c01e6b83cd36..39e374174646 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -23,7 +23,7 @@ use crate::os::unix::io::OwnedFd; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; #[cfg(not(target_os = "trusty"))] -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/freebsd/fs.rs b/library/std/src/os/freebsd/fs.rs index 34384a4bcb50..682a32a94c19 100644 --- a/library/std/src/os/freebsd/fs.rs +++ b/library/std/src/os/freebsd/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::freebsd::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index fcfc5c1c8393..f759b91921b5 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -6,7 +6,7 @@ use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// FreeBSD-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/fuchsia/fs.rs b/library/std/src/os/fuchsia/fs.rs index b48a46f9124a..8cded1db37d7 100644 --- a/library/std/src/os/fuchsia/fs.rs +++ b/library/std/src/os/fuchsia/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/haiku/fs.rs b/library/std/src/os/haiku/fs.rs index 23f6493180b7..86624edcaef9 100644 --- a/library/std/src/os/haiku/fs.rs +++ b/library/std/src/os/haiku/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::haiku::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 233bc885fc73..d5baeafec649 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -1,6 +1,6 @@ +use crate::net; use crate::os::hermit::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; -use crate::{net, sys}; +use crate::sys::{self, AsInner, FromInner, IntoInner}; macro_rules! impl_as_raw_fd { ($($t:ident)*) => {$( diff --git a/library/std/src/os/horizon/fs.rs b/library/std/src/os/horizon/fs.rs index 1325522105dc..a5f3f791e6a1 100644 --- a/library/std/src/os/horizon/fs.rs +++ b/library/std/src/os/horizon/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/hurd/fs.rs b/library/std/src/os/hurd/fs.rs index e3087fa8af1c..e0fc544fed1d 100644 --- a/library/std/src/os/hurd/fs.rs +++ b/library/std/src/os/hurd/fs.rs @@ -5,7 +5,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/illumos/fs.rs b/library/std/src/os/illumos/fs.rs index 75dbe167785d..76a5871638a1 100644 --- a/library/std/src/os/illumos/fs.rs +++ b/library/std/src/os/illumos/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::illumos::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/illumos/net.rs b/library/std/src/os/illumos/net.rs index 5ef4e1ec89e3..c21c887edbdf 100644 --- a/library/std/src/os/illumos/net.rs +++ b/library/std/src/os/illumos/net.rs @@ -5,7 +5,7 @@ use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// illumos-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/l4re/fs.rs b/library/std/src/os/l4re/fs.rs index 0511ddcf19af..1f0bacae1ca6 100644 --- a/library/std/src/os/l4re/fs.rs +++ b/library/std/src/os/l4re/fs.rs @@ -7,7 +7,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::l4re::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index 20a7a161a262..e52a63bc798e 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -7,7 +7,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::linux::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 919590947972..fef321436f6a 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -8,9 +8,9 @@ use crate::io::Result; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::process::{self, ExitStatus}; use crate::sealed::Sealed; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(not(doc))] use crate::sys::{fd::FileDesc, linux::pidfd::PidFd as InnerPidFd}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(doc)] struct InnerPidFd; diff --git a/library/std/src/os/motor/ffi.rs b/library/std/src/os/motor/ffi.rs index 10e8da392dcc..e65a7736d3fa 100644 --- a/library/std/src/os/motor/ffi.rs +++ b/library/std/src/os/motor/ffi.rs @@ -3,7 +3,7 @@ use crate::ffi::{OsStr, OsString}; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner}; /// Motor OS–specific extensions to [`OsString`]. /// diff --git a/library/std/src/os/motor/process.rs b/library/std/src/os/motor/process.rs index 015fbcb97f97..09f38a4721bb 100644 --- a/library/std/src/os/motor/process.rs +++ b/library/std/src/os/motor/process.rs @@ -1,7 +1,7 @@ #![unstable(feature = "motor_ext", issue = "147456")] use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; pub trait ChildExt: Sealed { /// Extracts the main thread raw handle, without taking ownership diff --git a/library/std/src/os/net/linux_ext/socket.rs b/library/std/src/os/net/linux_ext/socket.rs index a15feb6bd9f2..ecc2758961e1 100644 --- a/library/std/src/os/net/linux_ext/socket.rs +++ b/library/std/src/os/net/linux_ext/socket.rs @@ -3,7 +3,7 @@ use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// Linux-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index 3f9b2bd3f4b4..c97063dd1650 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -3,7 +3,7 @@ //! [`std::net`]: crate::net use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; #[cfg(target_os = "linux")] use crate::time::Duration; use crate::{io, net}; diff --git a/library/std/src/os/netbsd/fs.rs b/library/std/src/os/netbsd/fs.rs index 74fbbabb17a5..e61afd895deb 100644 --- a/library/std/src/os/netbsd/fs.rs +++ b/library/std/src/os/netbsd/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::netbsd::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index e1950d349bf7..9174fee2f996 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -6,7 +6,7 @@ use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// NetBSD-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/nto/fs.rs b/library/std/src/os/nto/fs.rs index 8f915b08c9e2..d371936c4336 100644 --- a/library/std/src/os/nto/fs.rs +++ b/library/std/src/os/nto/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; #[stable(feature = "metadata_ext", since = "1.1.0")] pub trait MetadataExt { diff --git a/library/std/src/os/nuttx/fs.rs b/library/std/src/os/nuttx/fs.rs index 7d6d8d16eca7..f6c74bab9b45 100644 --- a/library/std/src/os/nuttx/fs.rs +++ b/library/std/src/os/nuttx/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; #[stable(feature = "metadata_ext", since = "1.1.0")] pub trait MetadataExt { diff --git a/library/std/src/os/openbsd/fs.rs b/library/std/src/os/openbsd/fs.rs index e584098476a7..d3ca2426507a 100644 --- a/library/std/src/os/openbsd/fs.rs +++ b/library/std/src/os/openbsd/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::openbsd::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/redox/fs.rs b/library/std/src/os/redox/fs.rs index c6b813f0cc6c..0451e91071bc 100644 --- a/library/std/src/os/redox/fs.rs +++ b/library/std/src/os/redox/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::redox::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/rtems/fs.rs b/library/std/src/os/rtems/fs.rs index bec0d41e42d8..97a0c9004c00 100644 --- a/library/std/src/os/rtems/fs.rs +++ b/library/std/src/os/rtems/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/solaris/fs.rs b/library/std/src/os/solaris/fs.rs index 9b0527d71389..d32f57522860 100644 --- a/library/std/src/os/solaris/fs.rs +++ b/library/std/src/os/solaris/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::solaris::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/solaris/net.rs b/library/std/src/os/solaris/net.rs index ca841f15b0e7..cea65f6ee701 100644 --- a/library/std/src/os/solaris/net.rs +++ b/library/std/src/os/solaris/net.rs @@ -5,7 +5,7 @@ use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// solaris-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index c23d842b238b..a1c8a86c9861 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -48,7 +48,7 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, net, sys}; /// Raw file descriptors. diff --git a/library/std/src/os/unix/ffi/os_str.rs b/library/std/src/os/unix/ffi/os_str.rs index 650f712bc6ee..da47112f7cb4 100644 --- a/library/std/src/os/unix/ffi/os_str.rs +++ b/library/std/src/os/unix/ffi/os_str.rs @@ -2,7 +2,7 @@ use crate::ffi::{OsStr, OsString}; use crate::mem; use crate::sealed::Sealed; use crate::sys::os_str::Buf; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; // Note: this file is currently reused in other `std::os::{platform}::ffi` modules to reduce duplication. // Keep this in mind when applying changes to this file that only apply to `unix`. diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 1d1a138b3025..219b340b9246 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -15,7 +15,7 @@ use crate::io::BorrowedCursor; use crate::os::unix::io::{AsFd, AsRawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner}; use crate::{io, sys}; // Tests for this module diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 163267be1e5c..beda370006f7 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -22,9 +22,8 @@ use crate::net::Shutdown; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; use crate::time::Duration; use crate::{fmt, io}; #[cfg(not(any( diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 5b4659e26188..99eef7f4013d 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -1,9 +1,8 @@ use super::{SocketAddr, UnixStream, sockaddr_un}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; -use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; use crate::{fmt, io, mem}; /// A structure representing a Unix domain socket server. diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 851ff7f08795..1fed1229d5ae 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -36,9 +36,8 @@ use crate::net::Shutdown; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys::{AsInner, FromInner, cvt}; use crate::time::Duration; /// A Unix stream socket. diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index ee0c460f7dfa..fab1b20b8c0e 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -8,7 +8,8 @@ use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::process::ChildPipe; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; cfg_select! { @@ -511,7 +512,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor. #[inline] fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { - child_stdin.into_inner().into_inner().into_inner() + child_stdin.into_inner().into_inner() } } @@ -523,8 +524,7 @@ impl From for OwnedFd { impl From for process::ChildStdin { #[inline] fn from(fd: OwnedFd) -> process::ChildStdin { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStdin::from_inner(pipe) } } @@ -542,7 +542,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor. #[inline] fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { - child_stdout.into_inner().into_inner().into_inner() + child_stdout.into_inner().into_inner() } } @@ -554,8 +554,7 @@ impl From for OwnedFd { impl From for process::ChildStdout { #[inline] fn from(fd: OwnedFd) -> process::ChildStdout { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStdout::from_inner(pipe) } } @@ -573,7 +572,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor. #[inline] fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { - child_stderr.into_inner().into_inner().into_inner() + child_stderr.into_inner().into_inner() } } @@ -585,8 +584,7 @@ impl From for OwnedFd { impl From for process::ChildStderr { #[inline] fn from(fd: OwnedFd) -> process::ChildStderr { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStderr::from_inner(pipe) } } diff --git a/library/std/src/os/unix/thread.rs b/library/std/src/os/unix/thread.rs index 03dcc3a4f9ba..32085e525942 100644 --- a/library/std/src/os/unix/thread.rs +++ b/library/std/src/os/unix/thread.rs @@ -6,7 +6,7 @@ #[allow(deprecated)] use crate::os::unix::raw::pthread_t; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner}; use crate::thread::JoinHandle; #[stable(feature = "thread_extensions", since = "1.9.0")] diff --git a/library/std/src/os/vita/fs.rs b/library/std/src/os/vita/fs.rs index a5a06764a4dd..d796f02ba6a3 100644 --- a/library/std/src/os/vita/fs.rs +++ b/library/std/src/os/vita/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/vxworks/fs.rs b/library/std/src/os/vxworks/fs.rs index 77e6238ca1f5..b88ed19b067a 100644 --- a/library/std/src/os/vxworks/fs.rs +++ b/library/std/src/os/vxworks/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// /// [`fs::Metadata`]: crate::fs::Metadata diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 9145283dae6e..248112cb369d 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -17,7 +17,7 @@ use crate::os::fd::AsRawFd; use crate::path::Path; #[cfg(target_env = "p1")] use crate::sys::err2io; -use crate::sys_common::{AsInner, AsInnerMut}; +use crate::sys::{AsInner, AsInnerMut}; /// WASI-specific extensions to [`File`]. pub trait FileExt { diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index 20e5383dc09e..fa115385c5d4 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -60,7 +60,7 @@ use crate::fmt; use crate::iter::FusedIterator; use crate::sealed::Sealed; use crate::sys::os_str::Buf; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys::{AsInner, FromInner}; /// Windows-specific extensions to [`OsString`]. /// diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index b445f368aeb1..c544bf485a25 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -8,7 +8,7 @@ use crate::fs::{self, Metadata, OpenOptions}; use crate::io::BorrowedCursor; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; use crate::{io, sys}; diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 4fc04b79315f..afc58ca59cfa 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -5,8 +5,7 @@ use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys::cvt; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; use crate::{fmt, fs, io, ptr, sys}; /// A borrowed handle. diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index a3ec7440338d..0d3f24005f36 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index f21ed51606f6..b32c6cd442ff 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -10,7 +10,7 @@ use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, marker, process, ptr, sys}; #[stable(feature = "process_extensions", since = "1.2.0")] @@ -117,7 +117,7 @@ impl IntoRawHandle for process::ChildStderr { impl From for process::ChildStdin { fn from(handle: OwnedHandle) -> process::ChildStdin { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStdin::from_inner(pipe) } } @@ -130,7 +130,7 @@ impl From for process::ChildStdin { impl From for process::ChildStdout { fn from(handle: OwnedHandle) -> process::ChildStdout { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStdout::from_inner(pipe) } } @@ -143,7 +143,7 @@ impl From for process::ChildStdout { impl From for process::ChildStderr { fn from(handle: OwnedHandle) -> process::ChildStderr { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStderr::from_inner(pipe) } } diff --git a/library/std/src/os/windows/thread.rs b/library/std/src/os/windows/thread.rs index d81d6d0ac28a..1cdee44de4b7 100644 --- a/library/std/src/os/windows/thread.rs +++ b/library/std/src/os/windows/thread.rs @@ -5,7 +5,7 @@ #![stable(feature = "thread_extensions", since = "1.9.0")] use crate::os::windows::io::{AsRawHandle, IntoRawHandle, RawHandle}; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner}; use crate::thread; #[stable(feature = "thread_extensions", since = "1.9.0")] diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 1997785885d3..beddc328582c 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -523,7 +523,7 @@ pub fn get_backtrace_style() -> Option { Some(x) if &x == "0" => BacktraceStyle::Off, Some(x) if &x == "full" => BacktraceStyle::Full, Some(_) => BacktraceStyle::Short, - None if crate::sys::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full, + None if crate::sys::backtrace::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full, None => BacktraceStyle::Off, }; diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 2a57a1ed5960..e8eda3c5f76b 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -3206,6 +3206,17 @@ impl Path { Display { inner: self.inner.display() } } + /// Returns the same path as `&Path`. + /// + /// This method is redundant when used directly on `&Path`, but + /// it helps dereferencing other `PathBuf`-like types to `Path`s, + /// for example references to `Box` or `Arc`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_path(&self) -> &Path { + self + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the diff --git a/library/std/src/process.rs b/library/std/src/process.rs index dbcf2684c6fb..720ba0ad73e2 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -166,9 +166,7 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::num::NonZero; use crate::path::Path; -use crate::sys::pipe::{AnonPipe, read2}; -use crate::sys::process as imp; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, process as imp}; use crate::{fmt, fs, str}; /// Representation of a running or exited child process. @@ -300,9 +298,9 @@ impl fmt::Debug for Child { /// /// Used to pass pipe handles between this module and [`imp`]. pub(crate) struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, } /// A handle to a child process's standard input (stdin). @@ -317,7 +315,7 @@ pub(crate) struct StdioPipes { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdin { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStdin` also has `impl`s for @@ -366,21 +364,21 @@ impl Write for &ChildStdin { } } -impl AsInner for ChildStdin { +impl AsInner for ChildStdin { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStdin { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStdin { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStdin { - fn from_inner(pipe: AnonPipe) -> ChildStdin { +impl FromInner for ChildStdin { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdin { ChildStdin { inner: pipe } } } @@ -403,7 +401,7 @@ impl fmt::Debug for ChildStdin { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdout { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStdout` also has `impl`s for @@ -436,21 +434,21 @@ impl Read for ChildStdout { } } -impl AsInner for ChildStdout { +impl AsInner for ChildStdout { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStdout { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStdout { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStdout { - fn from_inner(pipe: AnonPipe) -> ChildStdout { +impl FromInner for ChildStdout { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdout { ChildStdout { inner: pipe } } } @@ -473,7 +471,7 @@ impl fmt::Debug for ChildStdout { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStderr { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStderr` also has `impl`s for @@ -506,21 +504,21 @@ impl Read for ChildStderr { } } -impl AsInner for ChildStderr { +impl AsInner for ChildStderr { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStderr { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStderr { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStderr { - fn from_inner(pipe: AnonPipe) -> ChildStderr { +impl FromInner for ChildStderr { + fn from_inner(pipe: imp::ChildPipe) -> ChildStderr { ChildStderr { inner: pipe } } } @@ -2380,7 +2378,7 @@ impl Child { res.unwrap(); } (Some(out), Some(err)) => { - let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); + let res = imp::read_output(out.inner, &mut stdout, err.inner, &mut stderr); res.unwrap(); } } diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index c8a83edffe42..12c5130defe5 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -188,8 +188,10 @@ fn child_stdout_read_buf() { // ChildStdout::read_buf should omit buffer initialization. if cfg!(target_os = "windows") { assert_eq!(buf.filled(), b"abc\r\n"); + assert_eq!(buf.init_len(), 5); } else { assert_eq!(buf.filled(), b"abc\n"); + assert_eq!(buf.init_len(), 4); }; } diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 673033034eff..ee9795a52812 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -6,9 +6,10 @@ //! * [`Sender`] //! * [`Receiver`] //! -//! [`Sender`]s are used to send data to a set of [`Receiver`]s. Both -//! sender and receiver are cloneable (multi-producer) such that many threads can send -//! simultaneously to receivers (multi-consumer). +//! [`Sender`]s are used to send data to a set of [`Receiver`]s where each item +//! sent is delivered to (at most) one receiver. Both sender and receiver are +//! cloneable (multi-producer) such that many threads can send simultaneously +//! to receivers (multi-consumer). //! //! These channels come in two flavors: //! diff --git a/library/std/src/sys/anonymous_pipe/motor.rs b/library/std/src/sys/anonymous_pipe/motor.rs deleted file mode 100644 index dfe10f7fafe4..000000000000 --- a/library/std/src/sys/anonymous_pipe/motor.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; -use crate::sys::pipe::anon_pipe; -use crate::sys_common::IntoInner; - -pub type AnonPipe = FileDesc; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) -} diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs deleted file mode 100644 index dfe10f7fafe4..000000000000 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; -use crate::sys::pipe::anon_pipe; -use crate::sys_common::IntoInner; - -pub type AnonPipe = FileDesc; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) -} diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs deleted file mode 100644 index a0805ba9540e..000000000000 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::io; -pub use crate::sys::pipe::AnonPipe; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - Err(io::Error::UNSUPPORTED_PLATFORM) -} diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index f800500c22a8..6ff94f5681b6 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -4,10 +4,10 @@ use crate::ffi::OsString; use crate::num::NonZero; use crate::ops::Try; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::sys::FromInner; use crate::sys::os_str::Buf; use crate::sys::pal::abi::usercalls::alloc; use crate::sys::pal::abi::usercalls::raw::ByteBuffer; -use crate::sys_common::FromInner; use crate::{fmt, slice}; // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests diff --git a/library/std/src/sys/args/windows.rs b/library/std/src/sys/args/windows.rs index 81c44fabdcc6..b4de759e1a4c 100644 --- a/library/std/src/sys/args/windows.rs +++ b/library/std/src/sys/args/windows.rs @@ -14,8 +14,7 @@ use crate::path::{Path, PathBuf}; use crate::sys::pal::os::current_exe; use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf}; use crate::sys::path::get_long_path; -use crate::sys::{c, to_u16s}; -use crate::sys_common::AsInner; +use crate::sys::{AsInner, c, to_u16s}; use crate::sys_common::wstr::WStrUnits; use crate::{io, iter, ptr}; diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index 8e4e6aab0e49..858a95882b39 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -11,6 +11,12 @@ use crate::{env, fmt, io}; /// Max number of frames to print. const MAX_NB_FRAMES: usize = 100; +pub(crate) const FULL_BACKTRACE_DEFAULT: bool = cfg_select! { + // Fuchsia components default to full backtrace. + target_os = "fuchsia" => true, + _ => false, +}; + pub(crate) struct BacktraceLock<'a>(#[allow(dead_code)] MutexGuard<'a, ()>); pub(crate) fn lock<'a>() -> BacktraceLock<'a> { diff --git a/library/std/src/sys/env/zkvm.rs b/library/std/src/sys/env/zkvm.rs index 2eb7005ba128..b672a03bf0ba 100644 --- a/library/std/src/sys/env/zkvm.rs +++ b/library/std/src/sys/env/zkvm.rs @@ -4,9 +4,8 @@ mod unsupported_env; pub use unsupported_env::{Env, env, setenv, unsetenv}; use crate::ffi::{OsStr, OsString}; -use crate::sys::os_str; use crate::sys::pal::{WORD_SIZE, abi}; -use crate::sys_common::FromInner; +use crate::sys::{FromInner, os_str}; pub fn getenv(varname: &OsStr) -> Option { let varname = varname.as_encoded_bytes(); diff --git a/library/std/src/sys/fd/hermit.rs b/library/std/src/sys/fd/hermit.rs index afcd8c635541..2666da16420c 100644 --- a/library/std/src/sys/fd/hermit.rs +++ b/library/std/src/sys/fd/hermit.rs @@ -4,8 +4,7 @@ use crate::cmp; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, SeekFrom}; use crate::os::hermit::hermit_abi; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::{cvt, unsupported}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt, unsupported}; const fn max_iov() -> usize { hermit_abi::IOV_MAX @@ -34,7 +33,7 @@ impl FileDesc { ) })?; // SAFETY: Exactly `result` bytes have been filled. - unsafe { buf.advance(result as usize) }; + unsafe { buf.advance_unchecked(result as usize) }; Ok(()) } diff --git a/library/std/src/sys/fd/motor.rs b/library/std/src/sys/fd/motor.rs index 4211fef8007a..edeb99ccf8ef 100644 --- a/library/std/src/sys/fd/motor.rs +++ b/library/std/src/sys/fd/motor.rs @@ -2,8 +2,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::map_motor_error; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; #[derive(Debug)] pub struct FileDesc(OwnedFd); diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index 33fff36b35b0..bb6c0ac9e18e 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -36,12 +36,11 @@ cfg_select! { use crate::cmp; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::cvt; #[cfg(all(target_os = "android", target_pointer_width = "64"))] use crate::sys::pal::weak::syscall; #[cfg(any(all(target_os = "android", target_pointer_width = "32"), target_vendor = "apple"))] use crate::sys::pal::weak::weak; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; #[derive(Debug)] pub struct FileDesc(OwnedFd); @@ -186,7 +185,7 @@ impl FileDesc { // SAFETY: `ret` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance(ret as usize); + cursor.advance_unchecked(ret as usize); } Ok(()) } @@ -204,7 +203,7 @@ impl FileDesc { // SAFETY: `ret` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance(ret as usize); + cursor.advance_unchecked(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 21235bcfbd8c..0914e4b6c907 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -13,8 +13,7 @@ use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::{copy, exists}; use crate::sys::time::SystemTime; -use crate::sys::{cvt, unsupported, unsupported_err}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, cvt, unsupported, unsupported_err}; use crate::{fmt, mem}; #[derive(Debug)] diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index f723a23bd5df..8f3336e2fa96 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -6,8 +6,7 @@ use crate::path::{Path, PathBuf}; use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::exists; use crate::sys::time::SystemTime; -use crate::sys::{map_motor_error, unsupported}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, map_motor_error, unsupported}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct FileType { diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index ec1db262855a..f6d5d3b784d3 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -401,7 +401,7 @@ impl File { // Safety: `num_bytes_read` bytes were written to the unfilled // portion of the buffer - cursor.advance(num_bytes_read); + cursor.advance_unchecked(num_bytes_read); Ok(()) } diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 44f8b6e80f90..21ef70b43dcd 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -9,7 +9,6 @@ use crate::path::{Path, PathBuf}; use crate::sys::time::SystemTime; use crate::sys::{helpers, unsupported}; -#[expect(dead_code)] const FILE_PERMISSIONS_MASK: u64 = r_efi::protocols::file::READ_ONLY; pub struct File(!); @@ -109,7 +108,6 @@ impl FilePermissions { Self(attr & r_efi::protocols::file::READ_ONLY != 0) } - #[expect(dead_code)] const fn to_attr(&self) -> u64 { if self.0 { r_efi::protocols::file::READ_ONLY } else { 0 } } @@ -366,16 +364,40 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } -pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { - unsupported() +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + let mut file_info = f.file_info()?; + + unsafe { + (*file_info.as_mut_ptr()).attribute = + ((*file_info.as_ptr()).attribute & !FILE_PERMISSIONS_MASK) | perm.to_attr() + }; + + f.set_file_info(file_info) } -pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { - unsupported() +pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { + // UEFI does not support symlinks + set_times_nofollow(p, times) } -pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { - unsupported() +pub fn set_times_nofollow(p: &Path, times: FileTimes) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + let mut file_info = f.file_info()?; + + if let Some(x) = times.accessed { + unsafe { + (*file_info.as_mut_ptr()).last_access_time = uefi_fs::systemtime_to_uefi(x); + } + } + + if let Some(x) = times.modified { + unsafe { + (*file_info.as_mut_ptr()).modification_time = uefi_fs::systemtime_to_uefi(x); + } + } + + f.set_file_info(file_info) } pub fn rmdir(p: &Path) -> io::Result<()> { @@ -560,6 +582,17 @@ mod uefi_fs { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(info) } } + pub(crate) fn set_file_info(&self, mut info: UefiBox) -> io::Result<()> { + let file_ptr = self.0.as_ptr(); + let mut info_id = file::INFO_ID; + + let r = unsafe { + ((*file_ptr).set_info)(file_ptr, &mut info_id, info.len(), info.as_mut_ptr().cast()) + }; + + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + pub(crate) fn delete(self) -> io::Result<()> { let file_ptr = self.0.as_ptr(); let r = unsafe { ((*file_ptr).delete)(file_ptr) }; @@ -643,8 +676,7 @@ mod uefi_fs { } /// Convert to UEFI Time with the current timezone. - #[expect(dead_code)] - fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time { + pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time { let now = time::system_time_internal::now(); time.to_uefi_loose(now.timezone, now.daylight) } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 507d093d2b2f..ee3cb54a5ee6 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -97,8 +97,7 @@ use crate::sys::time::SystemTime; use crate::sys::weak::syscall; #[cfg(target_os = "android")] use crate::sys::weak::weak; -use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, cvt, cvt_r}; use crate::{mem, ptr}; pub struct File(FileDesc); @@ -2077,12 +2076,25 @@ pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> { pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { cfg_select! { - any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70") => { - // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves - // it implementation-defined whether `link` follows symlinks, so rely on the - // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. - // Android has `linkat` on newer versions, but we happen to know `link` - // always has the correct behavior, so it's here as well. + any( + // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. + // POSIX leaves it implementation-defined whether `link` follows + // symlinks, so rely on the `symlink_hard_link` test in + // library/std/src/fs/tests.rs to check the behavior. + target_os = "vxworks", + target_os = "redox", + target_os = "espidf", + // Android has `linkat` on newer versions, but we happen to know + // `link` always has the correct behavior, so it's here as well. + target_os = "android", + // wasi-sdk-29-and-prior have a buggy `linkat` so use `link` instead + // until wasi-sdk is updated (see WebAssembly/wasi-libc#690) + target_os = "wasi", + // Other misc platforms + target_os = "horizon", + target_os = "vita", + target_env = "nto70", + ) => { cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; } _ => { diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index f2d325da35c7..b588b91c753b 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -15,8 +15,7 @@ use crate::sys::pal::api::{self, WinError, set_file_information_by_handle}; use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul}; use crate::sys::path::{WCStr, maybe_verbatim}; use crate::sys::time::SystemTime; -use crate::sys::{Align8, c, cvt}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{Align8, AsInner, FromInner, IntoInner, c, cvt}; use crate::{fmt, ptr, slice}; mod remove_dir_all; diff --git a/library/std/src/sys/io/kernel_copy/linux/tests.rs b/library/std/src/sys/io/kernel_copy/linux/tests.rs index 15dee768d928..9b2d9cbef990 100644 --- a/library/std/src/sys/io/kernel_copy/linux/tests.rs +++ b/library/std/src/sys/io/kernel_copy/linux/tests.rs @@ -88,13 +88,8 @@ fn dont_splice_pipes_from_files() -> Result<()> { use crate::io::SeekFrom; use crate::os::unix::fs::FileExt; - use crate::process::{ChildStdin, ChildStdout}; - use crate::sys_common::FromInner; - let (read_end, write_end) = crate::sys::pipe::anon_pipe()?; - - let mut read_end = ChildStdout::from_inner(read_end); - let mut write_end = ChildStdin::from_inner(write_end); + let (mut read_end, mut write_end) = crate::io::pipe()?; let tmp_path = tmpdir(); let file = tmp_path.join("to_be_modified"); @@ -220,13 +215,8 @@ fn bench_file_to_uds_copy(b: &mut test::Bencher) { fn bench_socket_pipe_socket_copy(b: &mut test::Bencher) { use super::CopyResult; use crate::io::ErrorKind; - use crate::process::{ChildStdin, ChildStdout}; - use crate::sys_common::FromInner; - let (read_end, write_end) = crate::sys::pipe::anon_pipe().unwrap(); - - let mut read_end = ChildStdout::from_inner(read_end); - let write_end = ChildStdin::from_inner(write_end); + let (mut read_end, write_end) = crate::io::pipe().unwrap(); let acceptor = crate::net::TcpListener::bind("localhost:0").unwrap(); let mut remote_end = crate::net::TcpStream::connect(acceptor.local_addr().unwrap()).unwrap(); diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index b4e4e6ec7312..4a5d6f8e27f2 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -55,3 +55,8 @@ pub use kernel_copy::{CopyState, kernel_copy}; // Bare metal platforms usually have very small amounts of RAM // (in the order of hundreds of KB) pub const DEFAULT_BUF_SIZE: usize = if cfg!(target_os = "espidf") { 512 } else { 8 * 1024 }; + +pub type RawOsError = cfg_select! { + target_os = "uefi" => usize, + _ => i32, +}; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 2dbdc8a4e026..0c72f6a57fe8 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -13,7 +13,6 @@ mod pal; mod alloc; mod personality; -pub mod anonymous_pipe; pub mod args; pub mod backtrace; pub mod cmath; @@ -26,6 +25,7 @@ pub mod io; pub mod net; pub mod os_str; pub mod path; +pub mod pipe; pub mod platform_version; pub mod process; pub mod random; @@ -37,3 +37,25 @@ pub mod thread_local; // FIXME(117276): remove this, move feature implementations into individual // submodules. pub use pal::*; + +/// A trait for viewing representations from std types. +#[cfg_attr(not(target_os = "linux"), allow(unused))] +pub(crate) trait AsInner { + fn as_inner(&self) -> &Inner; +} + +/// A trait for viewing representations from std types. +#[cfg_attr(not(target_os = "linux"), allow(unused))] +pub(crate) trait AsInnerMut { + fn as_inner_mut(&mut self) -> &mut Inner; +} + +/// A trait for extracting representations from std types. +pub(crate) trait IntoInner { + fn into_inner(self) -> Inner; +} + +/// A trait for creating std types from internal representations. +pub(crate) trait FromInner { + fn from_inner(inner: Inner) -> Self; +} diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs index e9bf29e34f90..9beed0779752 100644 --- a/library/std/src/sys/net/connection/motor.rs +++ b/library/std/src/sys/net/connection/motor.rs @@ -5,8 +5,7 @@ use crate::net::SocketAddr::{V4, V6}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::map_motor_error; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; use crate::time::Duration; // We want to re-use as much of Rust's stdlib code as possible, diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index f044bf8dfae0..c32f8dcc8de8 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -10,8 +10,8 @@ use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::time::Instant; +use crate::sys::{AsInner, FromInner, IntoInner}; pub use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem}; @@ -143,7 +143,7 @@ impl Socket { ) })?; unsafe { - buf.advance(ret as usize); + buf.advance_unchecked(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 63d5c1d23124..c6df9c6b8ae1 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -9,7 +9,7 @@ use crate::net::{ }; use crate::sys::common::small_c_string::run_with_cstr; use crate::sys::net::connection::each_addr; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys::{AsInner, FromInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 731157ec319c..673d75046d3f 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -6,8 +6,7 @@ use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys::abi; -use crate::sys_common::{FromInner, IntoInner}; +use crate::sys::{FromInner, IntoInner, abi}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; @@ -191,7 +190,7 @@ impl Socket { netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) })?; unsafe { - buf.advance(ret as usize); + buf.advance_unchecked(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index d30f6054077d..6d06a8d86bf1 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -8,7 +8,7 @@ use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::net::{getsockopt, setsockopt}; use crate::sys::pal::IsMinusOne; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; @@ -294,7 +294,7 @@ impl Socket { ) })?; unsafe { - buf.advance(ret as usize); + buf.advance_unchecked(ret as usize); } Ok(()) } diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 08196d61aa30..7355f0ce6bf5 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -8,9 +8,8 @@ use crate::net::{Shutdown, SocketAddr}; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; -use crate::sys::c; use crate::sys::pal::winsock::last_error; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, c}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -244,7 +243,7 @@ impl Socket { } } _ => { - unsafe { buf.advance(result as usize) }; + unsafe { buf.advance_unchecked(result as usize) }; Ok(()) } } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index 3a0ac5552a64..95a4ab2fbf0f 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -5,8 +5,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::{err2io, unsupported}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, err2io, unsupported}; use crate::time::Duration; pub struct Socket(FileDesc); diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 258279bd4a60..7ba6c46eaef4 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -8,7 +8,7 @@ use crate::collections::TryReserveError; use crate::fmt::Write; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem, str}; #[cfg(test)] diff --git a/library/std/src/sys/os_str/utf8.rs b/library/std/src/sys/os_str/utf8.rs index 5dd24f67d303..a324a478325e 100644 --- a/library/std/src/sys/os_str/utf8.rs +++ b/library/std/src/sys/os_str/utf8.rs @@ -6,7 +6,7 @@ use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem}; #[derive(Hash)] diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 5050fd279433..1f130d91cf39 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -8,7 +8,7 @@ use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem}; #[derive(Hash)] diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 3ddf6e5acb02..52bcd8da2420 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -23,8 +23,6 @@ use crate::sys::env; pub mod futex; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod time; pub fn unsupported() -> crate::io::Result { diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index 2bd3ec8188a7..93ad4536e18f 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -86,12 +86,3 @@ cfg_select! { pub use self::unsupported::*; } } - -pub const FULL_BACKTRACE_DEFAULT: bool = cfg_select! { - // Fuchsia components default to full backtrace. - target_os = "fuchsia" => true, - _ => false, -}; - -#[cfg(not(target_os = "uefi"))] -pub type RawOsError = i32; diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index 32f95df6ad08..9bca264d78ea 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -1,7 +1,6 @@ #![allow(unsafe_op_in_unsafe_fn)] pub mod os; -pub mod pipe; pub mod time; pub use moto_rt::futex; diff --git a/library/std/src/sys/pal/motor/pipe.rs b/library/std/src/sys/pal/motor/pipe.rs deleted file mode 100644 index d3be6ddf1573..000000000000 --- a/library/std/src/sys/pal/motor/pipe.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::fd::FileDesc; -use crate::sys::map_motor_error; -use crate::sys_common::{FromInner, IntoInner}; - -#[derive(Debug)] -pub struct AnonPipe(FileDesc); - -impl From for AnonPipe { - fn from(rt_fd: moto_rt::RtFd) -> AnonPipe { - unsafe { AnonPipe::from_raw_fd(rt_fd) } - } -} - -impl AnonPipe { - pub fn read(&self, buf: &mut [u8]) -> io::Result { - moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|buf| self.read(buf), cursor) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|b| self.read(b), bufs) - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|b| self.write(b), bufs) - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut temp_vec = Vec::new(); - let mut size = 0_usize; - loop { - temp_vec.resize(256, 0_u8); - match self.read(&mut temp_vec[..]) { - Ok(sz) => { - if sz == 0 { - return Ok(size); - } - size += sz; - temp_vec.truncate(sz); - buf.append(&mut temp_vec); - } - Err(err) => { - if size != 0 { - return Ok(size); - } else { - return Err(err); - } - } - } - } - } -} - -impl AsRawFd for AnonPipe { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl FromRawFd for AnonPipe { - unsafe fn from_raw_fd(fd: RawFd) -> Self { - let desc = FileDesc::from_raw_fd(fd); - Self(desc) - } -} - -impl IntoRawFd for AnonPipe { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl AsFd for AnonPipe { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> OwnedFd { - self.0.into_inner() - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for AnonPipe { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(FileDesc::from_inner(owned_fd)) - } -} - -pub fn read2(_p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) -} - -#[inline] -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - Err(io::Error::UNSUPPORTED_PLATFORM) -} diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index fb410c285160..aaf380d1bef4 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -773,7 +773,7 @@ where #[unstable(feature = "sgx_platform", issue = "56975")] impl, U> CoerceUnsized> for UserRef {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UserRef {} #[unstable(feature = "sgx_platform", issue = "56975")] diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index f1e4a5a42577..5041770faf66 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -46,7 +46,7 @@ pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> IoResult<()> { let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.capacity()); let len = raw::read(fd, userbuf.as_mut_ptr().cast(), userbuf.len()).from_sgx_result()?; userbuf[..len].copy_to_enclave(&mut buf.as_mut()[..len]); - buf.advance(len); + buf.advance_unchecked(len); Ok(()) } } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 9a33873af581..7a207ceb329e 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -11,8 +11,6 @@ use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; pub mod abi; mod libunwind_integration; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod thread_parking; pub mod time; pub mod waitqueue; @@ -122,8 +120,6 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -pub use crate::sys_common::{AsInner, FromInner, IntoInner}; - pub trait TryIntoInner: Sized { fn try_into_inner(self) -> Result; } diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 9ca6dc581183..33df9116f5c2 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -18,8 +18,6 @@ pub mod itron { // `crate::sys::error` pub(crate) mod error; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub use self::itron::thread_parking; pub mod time; diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index dd0155265da6..fe2dd9c6c493 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -7,8 +7,6 @@ #![allow(dead_code)] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs index cf0c098f8a2f..76a3a75b10c1 100644 --- a/library/std/src/sys/pal/trusty/mod.rs +++ b/library/std/src/sys/pal/trusty/mod.rs @@ -5,8 +5,6 @@ mod common; #[path = "../unsupported/os.rs"] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index bfad6491e321..d059be010e98 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -802,6 +802,10 @@ impl UefiBox { pub(crate) fn as_ptr(&self) -> *const T { self.inner.as_ptr().cast() } + + pub(crate) const fn len(&self) -> usize { + self.size + } } impl Drop for UefiBox { diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index ebd311db1e12..61725b2ed048 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -13,17 +13,15 @@ //! [`OsString`]: crate::ffi::OsString #![forbid(unsafe_op_in_unsafe_fn)] +use crate::io::RawOsError; + pub mod helpers; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod time; #[cfg(test)] mod tests; -pub type RawOsError = usize; - use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs index 47e9a616bfda..7859854e96b4 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -1,9 +1,8 @@ use crate::io; use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; -use crate::sys::cvt; use crate::sys::fd::FileDesc; use crate::sys::process::ExitStatus; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; #[cfg(test)] mod tests; diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 81533d593131..a6c5decf2d34 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -8,7 +8,6 @@ pub mod futex; #[cfg(target_os = "linux")] pub mod linux; pub mod os; -pub mod pipe; pub mod stack_overflow; pub mod sync; pub mod thread_parking; diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs deleted file mode 100644 index 4798acf9dad6..000000000000 --- a/library/std/src/sys/pal/unix/pipe.rs +++ /dev/null @@ -1,184 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::mem; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::fd::FileDesc; -use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{FromInner, IntoInner}; - -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -#[derive(Debug)] -pub struct AnonPipe(FileDesc); - -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - let mut fds = [0; 2]; - - // The only known way right now to create atomically set the CLOEXEC flag is - // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 - // and musl 0.9.3, and some other targets also have it. - cfg_select! { - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - target_os = "redox" - ) => { - unsafe { - cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; - Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1])))) - } - } - _ => { - unsafe { - cvt(libc::pipe(fds.as_mut_ptr()))?; - - let fd0 = FileDesc::from_raw_fd(fds[0]); - let fd1 = FileDesc::from_raw_fd(fds[1]); - fd0.set_cloexec()?; - fd1.set_cloexec()?; - Ok((AnonPipe(fd0), AnonPipe(fd1))) - } - } - } -} - -impl AnonPipe { - #[allow(dead_code)] - // FIXME: This function seems legitimately unused. - pub fn try_clone(&self) -> io::Result { - self.0.duplicate().map(Self) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0.read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - self.0.read_to_end(buf) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - #[allow(dead_code)] - // FIXME: This function seems legitimately unused. - pub fn as_file_desc(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { - // Set both pipes into nonblocking mode as we're gonna be reading from both - // in the `select` loop below, and we wouldn't want one to block the other! - let p1 = p1.into_inner(); - let p2 = p2.into_inner(); - p1.set_nonblocking(true)?; - p2.set_nonblocking(true)?; - - let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; - fds[0].fd = p1.as_raw_fd(); - fds[0].events = libc::POLLIN; - fds[1].fd = p2.as_raw_fd(); - fds[1].events = libc::POLLIN; - loop { - // wait for either pipe to become readable using `poll` - cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; - - if fds[0].revents != 0 && read(&p1, v1)? { - p2.set_nonblocking(false)?; - return p2.read_to_end(v2).map(drop); - } - if fds[1].revents != 0 && read(&p2, v2)? { - p1.set_nonblocking(false)?; - return p1.read_to_end(v1).map(drop); - } - } - - // Read as much as we can from each pipe, ignoring EWOULDBLOCK or - // EAGAIN. If we hit EOF, then this will happen because the underlying - // reader will return Ok(0), in which case we'll see `Ok` ourselves. In - // this case we flip the other fd back into blocking mode and read - // whatever's leftover on that file descriptor. - fn read(fd: &FileDesc, dst: &mut Vec) -> Result { - match fd.read_to_end(dst) { - Ok(_) => Ok(true), - Err(e) => { - if e.raw_os_error() == Some(libc::EWOULDBLOCK) - || e.raw_os_error() == Some(libc::EAGAIN) - { - Ok(false) - } else { - Err(e) - } - } - } - } -} - -impl AsRawFd for AnonPipe { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl AsFd for AnonPipe { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl IntoRawFd for AnonPipe { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for AnonPipe { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } -} - -impl FromInner for AnonPipe { - fn from_inner(fd: FileDesc) -> Self { - Self(fd) - } -} diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 1b3fbeee4d90..50b690256a20 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,6 +1,6 @@ use core::num::niche_types::Nanoseconds; -use crate::sys_common::AsInner; +use crate::sys::AsInner; use crate::time::Duration; use crate::{fmt, io}; @@ -275,7 +275,7 @@ impl Instant { #[cfg(not(target_vendor = "apple"))] pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC; pub fn now() -> Instant { - // https://www.manpagez.com/man/3/clock_gettime/ + // https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html // // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man- // ner as CLOCK_MONOTONIC_RAW, but that does not incre- diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index e64bbc7c6169..c33d2e5fb02a 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -1,7 +1,6 @@ #![deny(unsafe_op_in_unsafe_fn)] pub mod os; -pub mod pipe; pub mod time; mod common; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 42629a91e1af..5d36687df097 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -9,8 +9,6 @@ pub mod futex; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod stack_overflow; #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index a20cd0e9ac77..80429a9aae18 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -18,8 +18,6 @@ #[path = "../unsupported/os.rs"] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index 712f41ba8030..90e243e1aa03 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -1,8 +1,5 @@ #![unstable(issue = "none", feature = "windows_handle")] -#[cfg(test)] -mod tests; - use core::ffi::c_void; use core::{cmp, mem, ptr}; @@ -10,8 +7,7 @@ use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut, Read}; use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; -use crate::sys::{c, cvt}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, c, cvt}; /// An owned container for `HANDLE` object, closing them on Drop. /// @@ -121,7 +117,7 @@ impl Handle { Ok(read) => { // Safety: `read` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance(read); + cursor.advance_unchecked(read); } Ok(()) } @@ -144,7 +140,7 @@ impl Handle { // SAFETY: `read` bytes were written to the initialized portion of the buffer unsafe { - cursor.advance(read); + cursor.advance_unchecked(read); } Ok(()) } diff --git a/library/std/src/sys/pal/windows/handle/tests.rs b/library/std/src/sys/pal/windows/handle/tests.rs deleted file mode 100644 index 0c976ed84e6b..000000000000 --- a/library/std/src/sys/pal/windows/handle/tests.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::sys::pipe::{Pipes, anon_pipe}; -use crate::{thread, time}; - -/// Test the synchronous fallback for overlapped I/O. -#[test] -fn overlapped_handle_fallback() { - // Create some pipes. `ours` will be asynchronous. - let Pipes { ours, theirs } = anon_pipe(true, false).unwrap(); - - let async_readable = ours.into_handle(); - let sync_writeable = theirs.into_handle(); - - thread::scope(|_| { - thread::sleep(time::Duration::from_millis(100)); - sync_writeable.write(b"hello world!").unwrap(); - }); - - // The pipe buffer starts empty so reading won't complete synchronously unless - // our fallback path works. - let mut buffer = [0u8; 1024]; - async_readable.read(&mut buffer).unwrap(); -} diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index a5f060080130..32bd6ea3a4f6 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -19,7 +19,6 @@ pub mod c; pub mod futex; pub mod handle; pub mod os; -pub mod pipe; pub mod time; cfg_select! { // We don't care about printing nice error messages for panic=immediate-abort diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index 6cccf090a3fc..ecd46c950b5e 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -3,8 +3,7 @@ use core::ops::Neg; use crate::cmp::Ordering; use crate::ptr::null; -use crate::sys::c; -use crate::sys_common::IntoInner; +use crate::sys::{IntoInner, c}; use crate::time::Duration; use crate::{fmt, mem}; diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index e673157e0eb5..077cff1ee0f2 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -3,8 +3,6 @@ use crate::os::xous::ffi::exit; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 9069c8d12fa1..6dece1055a08 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -12,8 +12,6 @@ pub const WORD_SIZE: usize = size_of::(); pub mod abi; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/pipe/mod.rs similarity index 57% rename from library/std/src/sys/anonymous_pipe/mod.rs rename to library/std/src/sys/pipe/mod.rs index 64b2c014b54f..85228963b4ad 100644 --- a/library/std/src/sys/anonymous_pipe/mod.rs +++ b/library/std/src/sys/pipe/mod.rs @@ -3,18 +3,18 @@ cfg_select! { unix => { mod unix; - pub use unix::{AnonPipe, pipe}; + pub use unix::{Pipe, pipe}; } windows => { mod windows; - pub use windows::{AnonPipe, pipe}; + pub use windows::{Pipe, pipe}; } target_os = "motor" => { mod motor; - pub use motor::{AnonPipe, pipe}; + pub use motor::{Pipe, pipe}; } _ => { mod unsupported; - pub use unsupported::{AnonPipe, pipe}; + pub use unsupported::{Pipe, pipe}; } } diff --git a/library/std/src/sys/pipe/motor.rs b/library/std/src/sys/pipe/motor.rs new file mode 100644 index 000000000000..50787c580133 --- /dev/null +++ b/library/std/src/sys/pipe/motor.rs @@ -0,0 +1,9 @@ +use crate::io; +use crate::sys::fd::FileDesc; + +pub type Pipe = FileDesc; + +#[inline] +pub fn pipe() -> io::Result<(Pipe, Pipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) +} diff --git a/library/std/src/sys/pipe/unix.rs b/library/std/src/sys/pipe/unix.rs new file mode 100644 index 000000000000..bb97e806a040 --- /dev/null +++ b/library/std/src/sys/pipe/unix.rs @@ -0,0 +1,44 @@ +use crate::io; +use crate::os::fd::FromRawFd; +use crate::sys::fd::FileDesc; +use crate::sys::pal::cvt; + +pub type Pipe = FileDesc; + +pub fn pipe() -> io::Result<(Pipe, Pipe)> { + let mut fds = [0; 2]; + + // The only known way right now to create atomically set the CLOEXEC flag is + // to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9 + // and musl 0.9.3, and some other targets also have it. + cfg_select! { + any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "redox" + ) => { + unsafe { + cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; + Ok((Pipe::from_raw_fd(fds[0]), Pipe::from_raw_fd(fds[1]))) + } + } + _ => { + unsafe { + cvt(libc::pipe(fds.as_mut_ptr()))?; + + let fd0 = Pipe::from_raw_fd(fds[0]); + let fd1 = Pipe::from_raw_fd(fds[1]); + fd0.set_cloexec()?; + fd1.set_cloexec()?; + Ok((fd0, fd1)) + } + } + } +} diff --git a/library/std/src/sys/pal/unsupported/pipe.rs b/library/std/src/sys/pipe/unsupported.rs similarity index 72% rename from library/std/src/sys/pal/unsupported/pipe.rs rename to library/std/src/sys/pipe/unsupported.rs index 988e551de522..1c5df47c9581 100644 --- a/library/std/src/sys/pal/unsupported/pipe.rs +++ b/library/std/src/sys/pipe/unsupported.rs @@ -1,16 +1,14 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::sys_common::{FromInner, IntoInner}; -pub struct AnonPipe(!); +pub struct Pipe(!); -impl fmt::Debug for AnonPipe { - fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } +#[inline] +pub fn pipe() -> io::Result<(Pipe, Pipe)> { + Err(io::Error::UNSUPPORTED_PLATFORM) } -impl AnonPipe { +impl Pipe { pub fn try_clone(&self) -> io::Result { self.0 } @@ -52,56 +50,52 @@ impl AnonPipe { } } -pub fn read2(p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - match p1.0 {} -} - -impl FromInner for AnonPipe { - fn from_inner(inner: !) -> Self { - inner - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> ! { +impl fmt::Debug for Pipe { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { self.0 } } #[cfg(any(unix, target_os = "hermit", target_os = "wasi"))] mod unix_traits { - use super::AnonPipe; + use super::Pipe; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; - use crate::sys_common::FromInner; + use crate::sys::{FromInner, IntoInner}; - impl AsRawFd for AnonPipe { + impl AsRawFd for Pipe { #[inline] fn as_raw_fd(&self) -> RawFd { self.0 } } - impl AsFd for AnonPipe { + impl AsFd for Pipe { fn as_fd(&self) -> BorrowedFd<'_> { self.0 } } - impl IntoRawFd for AnonPipe { + impl IntoRawFd for Pipe { fn into_raw_fd(self) -> RawFd { self.0 } } - impl FromRawFd for AnonPipe { + impl FromRawFd for Pipe { unsafe fn from_raw_fd(_: RawFd) -> Self { panic!("creating pipe on this platform is unsupported!") } } - impl FromInner for AnonPipe { + impl FromInner for Pipe { fn from_inner(_: OwnedFd) -> Self { panic!("creating pipe on this platform is unsupported!") } } + + impl IntoInner for Pipe { + fn into_inner(self) -> OwnedFd { + self.0 + } + } } diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/pipe/windows.rs similarity index 85% rename from library/std/src/sys/anonymous_pipe/windows.rs rename to library/std/src/sys/pipe/windows.rs index bdda7ffc5d25..5bd90b43d742 100644 --- a/library/std/src/sys/anonymous_pipe/windows.rs +++ b/library/std/src/sys/pipe/windows.rs @@ -3,9 +3,9 @@ use crate::sys::c; use crate::sys::handle::Handle; use crate::{io, ptr}; -pub type AnonPipe = Handle; +pub type Pipe = Handle; -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { +pub fn pipe() -> io::Result<(Pipe, Pipe)> { let mut read_pipe = c::INVALID_HANDLE_VALUE; let mut write_pipe = c::INVALID_HANDLE_VALUE; diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index 92e459298fc4..121d3bc9d5c3 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -28,7 +28,8 @@ mod env; pub use env::CommandEnvs; pub use imp::{ - Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, + ChildPipe, Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, + read_output, }; #[cfg(any( @@ -45,8 +46,6 @@ pub use imp::{ target_os = "motor" ))] pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec)> { - use crate::sys::pipe::read2; - let (mut process, mut pipes) = cmd.spawn(Stdio::MakePipe, false)?; drop(pipes.stdin.take()); @@ -62,7 +61,7 @@ pub fn output(cmd: &mut Command) -> crate::io::Result<(ExitStatus, Vec, Vec< res.unwrap(); } (Some(out), Some(err)) => { - let res = read2(out, &mut stdout, err, &mut stderr); + let res = read_output(out, &mut stdout, err, &mut stderr); res.unwrap(); } } diff --git a/library/std/src/sys/process/motor.rs b/library/std/src/sys/process/motor.rs index 949a9d494290..97b5a0fce2e4 100644 --- a/library/std/src/sys/process/motor.rs +++ b/library/std/src/sys/process/motor.rs @@ -8,9 +8,7 @@ use crate::os::motor::ffi::OsStrExt; use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fs::File; -use crate::sys::map_motor_error; -use crate::sys::pipe::AnonPipe; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys::{AsInner, FromInner, map_motor_error}; use crate::{fmt, io}; pub enum Stdio { @@ -150,20 +148,26 @@ impl Command { Ok(( Process { handle }, StdioPipes { - stdin: if stdin >= 0 { Some(stdin.into()) } else { None }, - stdout: if stdout >= 0 { Some(stdout.into()) } else { None }, - stderr: if stderr >= 0 { Some(stderr.into()) } else { None }, + stdin: if stdin >= 0 { + Some(unsafe { ChildPipe::from_raw_fd(stdin) }) + } else { + None + }, + stdout: if stdout >= 0 { + Some(unsafe { ChildPipe::from_raw_fd(stdout) }) + } else { + None + }, + stderr: if stderr >= 0 { + Some(unsafe { ChildPipe::from_raw_fd(stderr) }) + } else { + None + }, }, )) } } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - unsafe { Stdio::Fd(crate::sys::fd::FileDesc::from_raw_fd(pipe.into_raw_fd())) } - } -} - impl From for Stdio { fn from(fd: crate::sys::fd::FileDesc) -> Stdio { Stdio::Fd(fd) @@ -315,3 +319,14 @@ impl<'a> fmt::Debug for CommandArgs<'a> { f.debug_list().entries(self.iter.clone()).finish() } } + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + _out: ChildPipe, + _stdout: &mut Vec, + _err: ChildPipe, + _stderr: &mut Vec, +) -> io::Result<()> { + Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) +} diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 8d44292611bc..0e10bc02a0b9 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -10,7 +10,6 @@ use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::pal::helpers; use crate::sys::pal::os::error_string; -use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; use crate::{fmt, io}; @@ -204,8 +203,8 @@ pub fn output(command: &mut Command) -> io::Result<(ExitStatus, Vec, Vec Ok((ExitStatus(stat), stdout, stderr)) } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { +impl From for Stdio { + fn from(pipe: ChildPipe) -> Stdio { pipe.diverge() } } @@ -356,6 +355,17 @@ impl<'a> fmt::Debug for CommandArgs<'a> { } } +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + _stdout: &mut Vec, + _err: ChildPipe, + _stderr: &mut Vec, +) -> io::Result<()> { + match out.diverge() {} +} + #[allow(dead_code)] mod uefi_command_internal { use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output}; diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index 44d54aaf5151..2e1cd7068d7f 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -14,10 +14,10 @@ use crate::sys::fd::FileDesc; use crate::sys::fs::File; #[cfg(not(target_os = "fuchsia"))] use crate::sys::fs::OpenOptions; -use crate::sys::pipe::{self, AnonPipe}; +use crate::sys::pipe::pipe; use crate::sys::process::env::{CommandEnv, CommandEnvs}; -use crate::sys_common::{FromInner, IntoInner}; -use crate::{fmt, io}; +use crate::sys::{FromInner, IntoInner, cvt_r}; +use crate::{fmt, io, mem}; mod cstring_array; @@ -393,7 +393,7 @@ fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStr } impl Stdio { - pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { + pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { match *self { Stdio::Inherit => Ok((ChildStdio::Inherit, None)), @@ -418,9 +418,9 @@ impl Stdio { } Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; + let (reader, writer) = pipe()?; let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours))) + Ok((ChildStdio::Owned(theirs), Some(ours))) } #[cfg(not(target_os = "fuchsia"))] @@ -438,12 +438,6 @@ impl Stdio { } } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_inner()) - } -} - impl From for Stdio { fn from(fd: FileDesc) -> Stdio { Stdio::Fd(fd) @@ -632,3 +626,56 @@ impl<'a> fmt::Debug for CommandArgs<'a> { f.debug_list().entries(self.iter.clone()).finish() } } + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + stdout: &mut Vec, + err: ChildPipe, + stderr: &mut Vec, +) -> io::Result<()> { + // Set both pipes into nonblocking mode as we're gonna be reading from both + // in the `select` loop below, and we wouldn't want one to block the other! + out.set_nonblocking(true)?; + err.set_nonblocking(true)?; + + let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; + fds[0].fd = out.as_raw_fd(); + fds[0].events = libc::POLLIN; + fds[1].fd = err.as_raw_fd(); + fds[1].events = libc::POLLIN; + loop { + // wait for either pipe to become readable using `poll` + cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; + + if fds[0].revents != 0 && read(&out, stdout)? { + err.set_nonblocking(false)?; + return err.read_to_end(stderr).map(drop); + } + if fds[1].revents != 0 && read(&err, stderr)? { + out.set_nonblocking(false)?; + return out.read_to_end(stdout).map(drop); + } + } + + // Read as much as we can from each pipe, ignoring EWOULDBLOCK or + // EAGAIN. If we hit EOF, then this will happen because the underlying + // reader will return Ok(0), in which case we'll see `Ok` ourselves. In + // this case we flip the other fd back into blocking mode and read + // whatever's leftover on that file descriptor. + fn read(fd: &FileDesc, dst: &mut Vec) -> Result { + match fd.read_to_end(dst) { + Ok(_) => Ok(true), + Err(e) => { + if e.raw_os_error() == Some(libc::EWOULDBLOCK) + || e.raw_os_error() == Some(libc::EAGAIN) + { + Ok(false) + } else { + Err(e) + } + } + } + } +} diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs index cda1bf74f1ca..1938e8f4b737 100644 --- a/library/std/src/sys/process/unix/mod.rs +++ b/library/std/src/sys/process/unix/mod.rs @@ -23,5 +23,5 @@ cfg_select! { pub use imp::{ExitStatus, ExitStatusError, Process}; -pub use self::common::{Command, CommandArgs, ExitCode, Stdio}; +pub use self::common::{ChildPipe, Command, CommandArgs, ExitCode, Stdio, read_output}; pub use crate::ffi::OsString as EnvKey; diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 7d944f2f7eef..5ba57e11679c 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -78,7 +78,7 @@ impl Command { let (input, output) = sys::net::Socket::new_pair(libc::AF_UNIX, libc::SOCK_SEQPACKET)?; #[cfg(not(target_os = "linux"))] - let (input, output) = sys::pipe::anon_pipe()?; + let (input, output) = sys::pipe::pipe()?; // Whatever happens after the fork is almost for sure going to touch or // look at the environment in one way or another (PATH in `execvp` or @@ -966,7 +966,7 @@ impl Process { /// [I/O Safety]: crate::io#io-safety unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self { use crate::os::unix::io::FromRawFd; - use crate::sys_common::FromInner; + use crate::sys::FromInner; // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned. let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd))); Process { pid, status: None, pidfd } @@ -1264,8 +1264,8 @@ impl ExitStatusError { mod linux_child_ext { use crate::io::ErrorKind; use crate::os::linux::process as os; + use crate::sys::FromInner; use crate::sys::pal::linux::pidfd as imp; - use crate::sys_common::FromInner; use crate::{io, mem}; #[unstable(feature = "linux_pidfd", issue = "82971")] diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index 2dfc676ec005..455c38e55b7b 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -5,7 +5,6 @@ use crate::num::NonZero; use crate::path::Path; use crate::process::StdioPipes; use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; use crate::{fmt, io}; @@ -107,8 +106,8 @@ pub fn output(_cmd: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> unsupported() } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { +impl From for Stdio { + fn from(pipe: ChildPipe) -> Stdio { pipe.diverge() } } @@ -317,3 +316,14 @@ impl<'a> fmt::Debug for CommandArgs<'a> { f.debug_list().entries(self.iter.clone()).finish() } } + +pub type ChildPipe = crate::sys::pipe::Pipe; + +pub fn read_output( + out: ChildPipe, + _stdout: &mut Vec, + _err: ChildPipe, + _stderr: &mut Vec, +) -> io::Result<()> { + match out.diverge() {} +} diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index 6e8be21a1fa6..dba647c502d1 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -23,11 +23,13 @@ use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::pal::api::{self, WinError, utf16}; use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf}; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys::{cvt, path, stdio}; -use crate::sys_common::IntoInner; +use crate::sys::{IntoInner, cvt, path, stdio}; use crate::{cmp, env, fmt, ptr}; +mod child_pipe; + +pub use self::child_pipe::{ChildPipe, read_output}; + //////////////////////////////////////////////////////////////////////////////// // Command //////////////////////////////////////////////////////////////////////////////// @@ -167,7 +169,7 @@ pub enum Stdio { InheritSpecific { from_stdio_id: u32 }, Null, MakePipe, - Pipe(AnonPipe), + Pipe(ChildPipe), Handle(Handle), } @@ -596,7 +598,7 @@ fn program_exists(path: &Path) -> Option> { } impl Stdio { - fn to_handle(&self, stdio_id: u32, pipe: &mut Option) -> io::Result { + fn to_handle(&self, stdio_id: u32, pipe: &mut Option) -> io::Result { let use_stdio_id = |stdio_id| match stdio::get_handle(stdio_id) { Ok(io) => unsafe { let io = Handle::from_raw_handle(io); @@ -613,14 +615,15 @@ impl Stdio { Stdio::MakePipe => { let ours_readable = stdio_id != c::STD_INPUT_HANDLE; - let pipes = pipe::anon_pipe(ours_readable, true)?; + let pipes = child_pipe::child_pipe(ours_readable, true)?; *pipe = Some(pipes.ours); Ok(pipes.theirs.into_handle()) } Stdio::Pipe(ref source) => { let ours_readable = stdio_id != c::STD_INPUT_HANDLE; - pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle) + child_pipe::spawn_pipe_relay(source, ours_readable, true) + .map(ChildPipe::into_handle) } Stdio::Handle(ref handle) => handle.duplicate(0, true, c::DUPLICATE_SAME_ACCESS), @@ -639,8 +642,8 @@ impl Stdio { } } -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { +impl From for Stdio { + fn from(pipe: ChildPipe) -> Stdio { Stdio::Pipe(pipe) } } diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/process/windows/child_pipe.rs similarity index 90% rename from library/std/src/sys/pal/windows/pipe.rs rename to library/std/src/sys/process/windows/child_pipe.rs index 32cf4695d4a1..da7a86ca072e 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/process/windows/child_pipe.rs @@ -2,43 +2,40 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::ops::Neg; use crate::os::windows::prelude::*; use crate::sys::handle::Handle; -use crate::sys::{api, c}; -use crate::sys_common::{FromInner, IntoInner}; +use crate::sys::{FromInner, IntoInner, api, c}; use crate::{mem, ptr}; -//////////////////////////////////////////////////////////////////////////////// -// Anonymous pipes -//////////////////////////////////////////////////////////////////////////////// - -pub struct AnonPipe { +pub struct ChildPipe { inner: Handle, } -impl IntoInner for AnonPipe { +impl IntoInner for ChildPipe { fn into_inner(self) -> Handle { self.inner } } -impl FromInner for AnonPipe { - fn from_inner(inner: Handle) -> AnonPipe { +impl FromInner for ChildPipe { + fn from_inner(inner: Handle) -> ChildPipe { Self { inner } } } -pub struct Pipes { - pub ours: AnonPipe, - pub theirs: AnonPipe, +pub(super) struct Pipes { + pub ours: ChildPipe, + pub theirs: ChildPipe, } -/// Although this looks similar to `anon_pipe` in the Unix module it's actually -/// subtly different. Here we'll return two pipes in the `Pipes` return value, -/// but one is intended for "us" where as the other is intended for "someone -/// else". +/// Creates an anonymous pipe suitable for communication with a child process. /// -/// Currently the only use case for this function is pipes for stdio on -/// processes in the standard library, so "ours" is the one that'll stay in our -/// process whereas "theirs" will be inherited to a child. +/// Windows unfortunately does not have a way of performing asynchronous operations +/// on a handle originally created for synchronous operation. As `read_output` can +/// only be correctly implemented with asynchronous reads but the pipe created by +/// `CreatePipe` is synchronous, we cannot use it (and thus [`io::pipe`]) to create +/// a pipe for communicating with a child. Instead, this function uses the NT API +/// to create a pipe where one pipe handle (`ours`) is asynchronous and one is +/// synchronous and can be inherited by a child for use as a console handle +/// (`theirs`). /// /// The ours/theirs pipes are *not* specifically readable or writable. Each /// one only supports a read or a write, but which is which depends on the @@ -50,7 +47,11 @@ pub struct Pipes { /// mode. This means that technically speaking it should only ever be used /// with `OVERLAPPED` instances, but also works out ok if it's only ever used /// once at a time (which we do indeed guarantee). -pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result { +// FIXME(joboet): No, we don't guarantee that? E.g. `&Stdout` is both `Read` +// and `Sync`, so there could be multiple operations at the same +// time. All the functions below that forward to the inner handle +// methods could abort if used concurrently. +pub(super) fn child_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result { // A 64kb pipe capacity is the same as a typical Linux default. const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024; @@ -166,7 +167,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res } }; - Ok(Pipes { ours: AnonPipe { inner: ours }, theirs: AnonPipe { inner: theirs } }) + Ok(Pipes { ours: ChildPipe { inner: ours }, theirs: ChildPipe { inner: theirs } }) } } @@ -175,16 +176,16 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res /// /// This is achieved by creating a new set of pipes and spawning a thread that /// relays messages between the source and the synchronous pipe. -pub fn spawn_pipe_relay( - source: &AnonPipe, +pub(super) fn spawn_pipe_relay( + source: &ChildPipe, ours_readable: bool, their_handle_inheritable: bool, -) -> io::Result { +) -> io::Result { // We need this handle to live for the lifetime of the thread spawned below. let source = source.try_clone()?; // create a new pair of anon pipes. - let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; + let Pipes { theirs, ours } = child_pipe(ours_readable, their_handle_inheritable)?; // Spawn a thread that passes messages from one pipe to the other. // Any errors will simply cause the thread to exit. @@ -210,7 +211,7 @@ pub fn spawn_pipe_relay( Ok(theirs) } -impl AnonPipe { +impl ChildPipe { pub fn handle(&self) -> &Handle { &self.inner } @@ -219,7 +220,7 @@ impl AnonPipe { } pub fn try_clone(&self) -> io::Result { - self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) + self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| ChildPipe { inner }) } pub fn read(&self, buf: &mut [u8]) -> io::Result { @@ -259,7 +260,7 @@ impl AnonPipe { Err(e) => Err(e), Ok(n) => { unsafe { - buf.advance(n); + buf.advance_unchecked(n); } Ok(()) } @@ -347,18 +348,17 @@ impl AnonPipe { // STEP 3: The callback. unsafe extern "system" fn callback( - dwErrorCode: u32, - dwNumberOfBytesTransferred: u32, - lpOverlapped: *mut c::OVERLAPPED, + error: u32, + transferred: u32, + overlapped: *mut c::OVERLAPPED, ) { // Set `async_result` using a pointer smuggled through `hEvent`. // SAFETY: // At this point, the OVERLAPPED struct will have been written to by the OS, // except for our `hEvent` field which we set to a valid AsyncResult pointer (see below) unsafe { - let result = - AsyncResult { error: dwErrorCode, transferred: dwNumberOfBytesTransferred }; - *(*lpOverlapped).hEvent.cast::>() = Some(result); + let result = AsyncResult { error, transferred }; + *(*overlapped).hEvent.cast::>() = Some(result); } } @@ -395,7 +395,12 @@ impl AnonPipe { } } -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { +pub fn read_output( + p1: ChildPipe, + v1: &mut Vec, + p2: ChildPipe, + v2: &mut Vec, +) -> io::Result<()> { let p1 = p1.into_handle(); let p2 = p2.into_handle(); diff --git a/library/std/src/sys/process/windows/tests.rs b/library/std/src/sys/process/windows/tests.rs index a21afe3363c5..bc5e0d5c7fc9 100644 --- a/library/std/src/sys/process/windows/tests.rs +++ b/library/std/src/sys/process/windows/tests.rs @@ -1,8 +1,10 @@ +use super::child_pipe::{Pipes, child_pipe}; use super::{Arg, make_command_line}; -use crate::env; use crate::ffi::{OsStr, OsString}; use crate::os::windows::io::AsHandle; use crate::process::{Command, Stdio}; +use crate::time::Duration; +use crate::{env, thread}; #[test] fn test_raw_args() { @@ -233,3 +235,26 @@ fn windows_exe_resolver() { ); } } + +/// Test the synchronous fallback for overlapped I/O. +/// +/// While technically testing `Handle` functionality, this is situated in this +/// module to allow easier access to `ChildPipe`. +#[test] +fn overlapped_handle_fallback() { + // Create some pipes. `ours` will be asynchronous. + let Pipes { ours, theirs } = child_pipe(true, false).unwrap(); + + let async_readable = ours.into_handle(); + let sync_writeable = theirs.into_handle(); + + thread::scope(|_| { + thread::sleep(Duration::from_millis(100)); + sync_writeable.write(b"hello world!").unwrap(); + }); + + // The pipe buffer starts empty so reading won't complete synchronously unless + // our fallback path works. + let mut buffer = [0u8; 1024]; + async_readable.read(&mut buffer).unwrap(); +} diff --git a/library/std/src/sys/stdio/motor.rs b/library/std/src/sys/stdio/motor.rs index 0a44feab723d..2b1bc15285f9 100644 --- a/library/std/src/sys/stdio/motor.rs +++ b/library/std/src/sys/stdio/motor.rs @@ -1,6 +1,5 @@ use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::map_motor_error; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; use crate::{io, process, sys}; pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE; @@ -166,7 +165,7 @@ impl From for OwnedFd { impl From for process::ChildStdin { #[inline] fn from(fd: OwnedFd) -> process::ChildStdin { - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = sys::process::ChildPipe::from_inner(fd); process::ChildStdin::from_inner(pipe) } } @@ -196,7 +195,7 @@ impl From for OwnedFd { impl From for process::ChildStdout { #[inline] fn from(fd: OwnedFd) -> process::ChildStdout { - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = sys::process::ChildPipe::from_inner(fd); process::ChildStdout::from_inner(pipe) } } @@ -226,7 +225,7 @@ impl From for OwnedFd { impl From for process::ChildStderr { #[inline] fn from(fd: OwnedFd) -> process::ChildStderr { - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = sys::process::ChildPipe::from_inner(fd); process::ChildStderr::from_inner(pipe) } } diff --git a/library/std/src/sys/stdio/zkvm.rs b/library/std/src/sys/stdio/zkvm.rs index 84496ac93736..f31c6c26e87c 100644 --- a/library/std/src/sys/stdio/zkvm.rs +++ b/library/std/src/sys/stdio/zkvm.rs @@ -19,7 +19,7 @@ impl io::Read for Stdin { fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { unsafe { let n = abi::sys_read(fileno::STDIN, buf.as_mut().as_mut_ptr().cast(), buf.capacity()); - buf.advance(n); + buf.advance_unchecked(n); } Ok(()) } diff --git a/library/std/src/sys/thread/windows.rs b/library/std/src/sys/thread/windows.rs index 1ef496a20cfe..6a21b11e0312 100644 --- a/library/std/src/sys/thread/windows.rs +++ b/library/std/src/sys/thread/windows.rs @@ -6,8 +6,7 @@ use crate::os::windows::io::{AsRawHandle, HandleOrNull}; use crate::sys::handle::Handle; use crate::sys::pal::time::WaitableTimer; use crate::sys::pal::{dur2timeout, to_u16s}; -use crate::sys::{c, stack_overflow}; -use crate::sys_common::FromInner; +use crate::sys::{FromInner, c, stack_overflow}; use crate::thread::ThreadInit; use crate::time::Duration; use crate::{io, ptr}; diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index c6cb1b006e8c..015042440b5a 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -23,32 +23,6 @@ pub mod wstr; // common error constructors -/// A trait for viewing representations from std types -#[doc(hidden)] -#[allow(dead_code)] // not used on all platforms -pub trait AsInner { - fn as_inner(&self) -> &Inner; -} - -/// A trait for viewing representations from std types -#[doc(hidden)] -#[allow(dead_code)] // not used on all platforms -pub trait AsInnerMut { - fn as_inner_mut(&mut self) -> &mut Inner; -} - -/// A trait for extracting representations from std types -#[doc(hidden)] -pub trait IntoInner { - fn into_inner(self) -> Inner; -} - -/// A trait for creating std types from internal representations -#[doc(hidden)] -pub trait FromInner { - fn from_inner(inner: Inner) -> Self; -} - // Computes (value*numerator)/denom without overflow, as long as both (numerator*denom) and the // overall result fit into i64 (which is the case for our time conversions). #[allow(dead_code)] // not used on all platforms diff --git a/library/std/src/thread/join_handle.rs b/library/std/src/thread/join_handle.rs index 8714ceeb4f46..7112fe44f907 100644 --- a/library/std/src/thread/join_handle.rs +++ b/library/std/src/thread/join_handle.rs @@ -2,8 +2,7 @@ use super::Result; use super::lifecycle::JoinInner; use super::thread::Thread; use crate::fmt; -use crate::sys::thread as imp; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner, thread as imp}; /// An owned permission to join on a thread (block on its termination). /// diff --git a/library/std/src/thread/lifecycle.rs b/library/std/src/thread/lifecycle.rs index a48594c606a3..0bb1f347ffaa 100644 --- a/library/std/src/thread/lifecycle.rs +++ b/library/std/src/thread/lifecycle.rs @@ -10,8 +10,7 @@ use crate::marker::PhantomData; use crate::mem::{ManuallyDrop, MaybeUninit}; use crate::sync::Arc; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::sys::thread as imp; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner, thread as imp}; use crate::{env, io, panic}; #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 67c144be14f6..3d09824019e3 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -39,8 +39,7 @@ pub use core::time::TryFromFloatSecsError; use crate::error::Error; use crate::fmt; use crate::ops::{Add, AddAssign, Sub, SubAssign}; -use crate::sys::time; -use crate::sys_common::{FromInner, IntoInner}; +use crate::sys::{FromInner, IntoInner, time}; /// A measurement of a monotonically nondecreasing clock. /// Opaque and useful only with [`Duration`]. @@ -124,7 +123,7 @@ use crate::sys_common::{FromInner, IntoInner}; /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode /// [__wasi_clock_time_get]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get -/// [clock_gettime]: https://linux.die.net/man/3/clock_gettime +/// [clock_gettime]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html /// /// **Disclaimer:** These system calls might change over time. /// @@ -234,7 +233,7 @@ pub struct Instant(time::Instant); /// [currently]: crate::io#platform-specific-behavior /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode -/// [clock_gettime (Realtime Clock)]: https://linux.die.net/man/3/clock_gettime +/// [clock_gettime (Realtime Clock)]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html /// [__wasi_clock_time_get (Realtime Clock)]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#clock_time_get /// [GetSystemTimePreciseAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime /// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime diff --git a/library/std_detect/src/detect/arch/mod.rs b/library/std_detect/src/detect/arch/mod.rs index 23e7a30b985b..2be7f091c285 100644 --- a/library/std_detect/src/detect/arch/mod.rs +++ b/library/std_detect/src/detect/arch/mod.rs @@ -60,7 +60,7 @@ cfg_select! { pub use loongarch::*; } target_arch = "s390x" => { - #[stable(feature = "stdarch_s390x_feature_detection", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] pub use s390x::*; } _ => { diff --git a/library/std_detect/src/detect/arch/s390x.rs b/library/std_detect/src/detect/arch/s390x.rs index 6122e8f5b837..a04283f90a02 100644 --- a/library/std_detect/src/detect/arch/s390x.rs +++ b/library/std_detect/src/detect/arch/s390x.rs @@ -9,7 +9,7 @@ features! { /// /// When the feature is known to be enabled at compile time (e.g. via `-Ctarget-feature`) /// the macro expands to `true`. - #[stable(feature = "stdarch_s390x_feature_detection", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "44839")] concurrent_functions: "concurrent-functions"; /// s390x concurrent-functions facility @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "44839")] deflate_conversion: "deflate-conversion"; @@ -32,30 +32,30 @@ features! { /// s390x message-security-assist-extension9 facility @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "44839")] message_security_assist_extension12: "message-security-assist-extension12"; /// s390x message-security-assist-extension12 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] miscellaneous_extensions_2: "miscellaneous-extensions-2"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_2: "miscellaneous-extensions-2"; /// s390x miscellaneous-extensions-2 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] miscellaneous_extensions_3: "miscellaneous-extensions-3"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_3: "miscellaneous-extensions-3"; /// s390x miscellaneous-extensions-3 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] miscellaneous_extensions_4: "miscellaneous-extensions-4"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] miscellaneous_extensions_4: "miscellaneous-extensions-4"; /// s390x miscellaneous-extensions-4 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] nnp_assist: "nnp-assist"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] nnp_assist: "nnp-assist"; /// s390x nnp-assist facility @FEATURE: #[unstable(feature = "s390x_target_feature", issue = "44839")] transactional_execution: "transactional-execution"; /// s390x transactional-execution facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector: "vector"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector: "vector"; /// s390x vector facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector_enhancements_1: "vector-enhancements-1"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_1: "vector-enhancements-1"; /// s390x vector-enhancements-1 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector_enhancements_2: "vector-enhancements-2"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_2: "vector-enhancements-2"; /// s390x vector-enhancements-2 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector_enhancements_3: "vector-enhancements-3"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_enhancements_3: "vector-enhancements-3"; /// s390x vector-enhancements-3 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector_packed_decimal: "vector-packed-decimal"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal: "vector-packed-decimal"; /// s390x vector-packed-decimal facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector_packed_decimal_enhancement: "vector-packed-decimal-enhancement"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement: "vector-packed-decimal-enhancement"; /// s390x vector-packed-decimal-enhancement facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector_packed_decimal_enhancement_2: "vector-packed-decimal-enhancement-2"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_2: "vector-packed-decimal-enhancement-2"; /// s390x vector-packed-decimal-enhancement-2 facility - @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "CURRENT_RUSTC_VERSION")] vector_packed_decimal_enhancement_3: "vector-packed-decimal-enhancement-3"; + @FEATURE: #[stable(feature = "s390x_target_feature_vector", since = "1.93.0")] vector_packed_decimal_enhancement_3: "vector-packed-decimal-enhancement-3"; /// s390x vector-packed-decimal-enhancement-3 facility } diff --git a/library/std_detect/src/detect/os/darwin/aarch64.rs b/library/std_detect/src/detect/os/darwin/aarch64.rs index 8c9fd9647b8a..a23d65a23d81 100644 --- a/library/std_detect/src/detect/os/darwin/aarch64.rs +++ b/library/std_detect/src/detect/os/darwin/aarch64.rs @@ -76,6 +76,8 @@ pub(crate) fn detect_features() -> cache::Initializer { let sme = _sysctlbyname(c"hw.optional.arm.FEAT_SME"); let sme2 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2"); let sme2p1 = _sysctlbyname(c"hw.optional.arm.FEAT_SME2p1"); + let sme_b16b16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_B16B16"); + let sme_f16f16 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F16F16"); let sme_f64f64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_F64F64"); let sme_i16i64 = _sysctlbyname(c"hw.optional.arm.FEAT_SME_I16I64"); let ssbs = _sysctlbyname(c"hw.optional.arm.FEAT_SSBS"); @@ -153,6 +155,8 @@ pub(crate) fn detect_features() -> cache::Initializer { enable_feature(Feature::sme, sme); enable_feature(Feature::sme2, sme2); enable_feature(Feature::sme2p1, sme2p1); + enable_feature(Feature::sme_b16b16, sme_b16b16); + enable_feature(Feature::sme_f16f16, sme_f16f16); enable_feature(Feature::sme_f64f64, sme_f64f64); enable_feature(Feature::sme_i16i64, sme_i16i64); enable_feature(Feature::ssbs, ssbs); diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 1566f1cb1dac..74d99e0f1270 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -189,8 +189,10 @@ impl OutputFormatter for JunitFormatter { compilation_time: f64, ) -> io::Result<()> { self.write_message(&format!( - "\n", - )) + "", + ))?; + self.out.write_all(b"\n")?; + Ok(()) } } diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index ab07a2df3047..e46fed7c426a 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -221,7 +221,10 @@ please file issues on the [Rust issue tracker][rust-issue-tracker]. ## Testing -To run bootstrap tests, execute `x test bootstrap`. If you want to bless snapshot tests, then install `cargo-insta` (`cargo install cargo-insta`) and then run `cargo insta review --manifest-path src/bootstrap/Cargo.toml`. +To run bootstrap tests, execute `x test bootstrap`. +If you want to bless snapshot tests, +then install `cargo-insta` (`cargo install cargo-insta`), +and then run `cargo insta review --manifest-path src/bootstrap/Cargo.toml`. ## Changelog diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 1915986be28c..e3895d4e0a18 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -593,7 +593,7 @@ def parse_example_config(known_args, config): top_level_keys = [] comment_lines = [] - with open(rust_dir + "/bootstrap.example.toml") as example_config: + with open(rust_dir + "/bootstrap.example.toml", encoding="utf-8") as example_config: example_lines = example_config.read().split("\n") for line in example_lines: if line.count("=") >= 1 and not line.startswith("# "): diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index f15b76fa85c9..bb974e4645ea 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -168,8 +168,11 @@ fn main() { } } - if let Ok(map) = env::var("RUSTC_DEBUGINFO_MAP") { - cmd.arg("--remap-path-prefix").arg(&map); + // The remap flags for the compiler and standard library sources. + if let Ok(maps) = env::var("RUSTC_DEBUGINFO_MAP") { + for map in maps.split('\t') { + cmd.arg("--remap-path-prefix").arg(map); + } } // The remap flags for Cargo registry sources need to be passed after the remapping for the // Rust source code directory, to handle cases when $CARGO_HOME is inside the source directory. diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 82cc25f123bc..ed154133dec2 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -7,7 +7,7 @@ //! goes along from the output of the previous stage. use std::borrow::Cow; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::ffi::OsStr; use std::io::BufReader; use std::io::prelude::*; @@ -19,7 +19,7 @@ use serde_derive::Deserialize; #[cfg(feature = "tracing")] use tracing::span; -use crate::core::build_steps::gcc::{Gcc, GccOutput, add_cg_gcc_cargo_flags}; +use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair, add_cg_gcc_cargo_flags}; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; use crate::core::build_steps::{dist, llvm}; use crate::core::builder; @@ -1232,19 +1232,6 @@ pub fn rustc_cargo( // . cargo.rustflag("-Zon-broken-pipe=kill"); - // We want to link against registerEnzyme and in the future we want to use additional - // functionality from Enzyme core. For that we need to link against Enzyme. - if builder.config.llvm_enzyme { - let arch = builder.build.host_target; - let enzyme_dir = builder.build.out.join(arch).join("enzyme").join("lib"); - cargo.rustflag("-L").rustflag(enzyme_dir.to_str().expect("Invalid path")); - - if let Some(llvm_config) = builder.llvm_config(builder.config.host_target) { - let llvm_version_major = llvm::get_llvm_version_major(builder, &llvm_config); - cargo.rustflag("-l").rustflag(&format!("Enzyme-{llvm_version_major}")); - } - } - // Building with protected visibility reduces the number of dynamic relocations needed, giving // us a faster startup time. However GNU ld < 2.40 will error if we try to link a shared object // with direct references to protected symbols, so for now we only use protected symbols if @@ -1576,17 +1563,98 @@ impl Step for RustcLink { } } +/// Set of `libgccjit` dylibs that can be used by `cg_gcc` to compile code for a set of targets. +#[derive(Clone)] +pub struct GccDylibSet { + dylibs: BTreeMap, + host_pair: GccTargetPair, +} + +impl GccDylibSet { + /// Returns the libgccjit.so dylib that corresponds to a host target on which `cg_gcc` will be + /// executed, and which will target the host. So e.g. if `cg_gcc` will be executed on + /// x86_64-unknown-linux-gnu, the host dylib will be for compilation pair + /// `(x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu)`. + fn host_dylib(&self) -> &GccOutput { + self.dylibs.get(&self.host_pair).unwrap_or_else(|| { + panic!("libgccjit.so was not built for host target {}", self.host_pair) + }) + } + + /// Install the libgccjit dylibs to the corresponding target directories of the given compiler. + /// cg_gcc know how to search for the libgccjit dylibs in these directories, according to the + /// (host, target) pair that is being compiled by rustc and cg_gcc. + pub fn install_to(&self, builder: &Builder<'_>, compiler: Compiler) { + if builder.config.dry_run() { + return; + } + + // /lib//codegen-backends + let cg_sysroot = builder.sysroot_codegen_backends(compiler); + + for (target_pair, libgccjit) in &self.dylibs { + assert_eq!( + target_pair.host(), + compiler.host, + "Trying to install libgccjit ({target_pair}) to a compiler with a different host ({})", + compiler.host + ); + let libgccjit = libgccjit.libgccjit(); + let target_filename = libgccjit.file_name().unwrap().to_str().unwrap(); + + // If we build libgccjit ourselves, then `libgccjit` can actually be a symlink. + // In that case, we have to resolve it first, otherwise we'd create a symlink to a + // symlink, which wouldn't work. + let actual_libgccjit_path = t!( + libgccjit.canonicalize(), + format!("Cannot find libgccjit at {}", libgccjit.display()) + ); + + // /lib//libgccjit.so + let dest_dir = cg_sysroot.join("lib").join(target_pair.target()); + t!(fs::create_dir_all(&dest_dir)); + let dst = dest_dir.join(target_filename); + builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary); + } + } +} + /// Output of the `compile::GccCodegenBackend` step. -/// It includes the path to the libgccjit library on which this backend depends. +/// +/// It contains paths to all built libgccjit libraries on which this backend depends here. #[derive(Clone)] pub struct GccCodegenBackendOutput { stamp: BuildStamp, - gcc: GccOutput, + dylib_set: GccDylibSet, } +/// Builds the GCC codegen backend (`cg_gcc`). +/// The `cg_gcc` backend uses `libgccjit`, which requires a separate build for each +/// `host -> target` pair. So if you are on linux-x64 and build for linux-aarch64, +/// you will need at least: +/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros) +/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code) +/// +/// We model this by having a single cg_gcc for a given host target, which contains one +/// libgccjit per (host, target) pair. +/// Note that the host target is taken from `self.compilers.target_compiler.host`. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GccCodegenBackend { compilers: RustcPrivateCompilers, + targets: Vec, +} + +impl GccCodegenBackend { + /// Build `cg_gcc` that will run on host `H` (`compilers.target_compiler.host`) and will be + /// able to produce code target pairs (`H`, `T`) for all `T` from `targets`. + pub fn for_targets( + compilers: RustcPrivateCompilers, + mut targets: Vec, + ) -> Self { + // Sort targets to improve step cache hits + targets.sort(); + Self { compilers, targets } + } } impl Step for GccCodegenBackend { @@ -1599,23 +1667,34 @@ impl Step for GccCodegenBackend { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(GccCodegenBackend { - compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), - }); + // By default, build cg_gcc that will only be able to compile native code for the given + // host target. + let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target); + run.builder.ensure(GccCodegenBackend { compilers, targets: vec![run.target] }); } fn run(self, builder: &Builder<'_>) -> Self::Output { - let target = self.compilers.target(); + let host = self.compilers.target(); let build_compiler = self.compilers.build_compiler(); let stamp = build_stamp::codegen_backend_stamp( builder, build_compiler, - target, + host, &CodegenBackendKind::Gcc, ); - let gcc = builder.ensure(Gcc { target }); + let dylib_set = GccDylibSet { + dylibs: self + .targets + .iter() + .map(|&target| { + let target_pair = GccTargetPair::for_target_pair(host, target); + (target_pair, builder.ensure(Gcc { target_pair })) + }) + .collect(), + host_pair: GccTargetPair::for_native_build(host), + }; if builder.config.keep_stage.contains(&build_compiler.stage) { trace!("`keep-stage` requested"); @@ -1625,7 +1704,7 @@ impl Step for GccCodegenBackend { ); // Codegen backends are linked separately from this step today, so we don't do // anything here. - return GccCodegenBackendOutput { stamp, gcc }; + return GccCodegenBackendOutput { stamp, dylib_set }; } let mut cargo = builder::Cargo::new( @@ -1633,21 +1712,21 @@ impl Step for GccCodegenBackend { build_compiler, Mode::Codegen, SourceType::InTree, - target, + host, Kind::Build, ); cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml")); - rustc_cargo_env(builder, &mut cargo, target); + rustc_cargo_env(builder, &mut cargo, host); - add_cg_gcc_cargo_flags(&mut cargo, &gcc); + add_cg_gcc_cargo_flags(&mut cargo, dylib_set.host_dylib()); let _guard = - builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, target); + builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, host); let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); GccCodegenBackendOutput { stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()), - gcc, + dylib_set, } } @@ -2324,12 +2403,65 @@ impl Step for Assemble { copy_codegen_backends_to_sysroot(builder, stamp, target_compiler); } CodegenBackendKind::Gcc => { - let output = - builder.ensure(GccCodegenBackend { compilers: prepare_compilers() }); + // We need to build cg_gcc for the host target of the compiler which we + // build here, which is `target_compiler`. + // But we also need to build libgccjit for some additional targets, in + // the most general case. + // 1. We need to build (target_compiler.host, stdlib target) libgccjit + // for all stdlibs that we build, so that cg_gcc can be used to build code + // for all those targets. + // 2. We need to build (target_compiler.host, target_compiler.host) + // libgccjit, so that the target compiler can compile host code (e.g. proc + // macros). + // 3. We need to build (target_compiler.host, host target) libgccjit + // for all *host targets* that we build, so that cg_gcc can be used to + // build a (possibly cross-compiled) stage 2+ rustc. + // + // Assume that we are on host T1 and we do a stage2 build of rustc for T2. + // We want the T2 rustc compiler to be able to use cg_gcc and build code + // for T2 (host) and T3 (target). We also want to build the stage2 compiler + // itself using cg_gcc. + // This could correspond to the following bootstrap invocation: + // `x build rustc --build T1 --host T2 --target T3 --set codegen-backends=['gcc', 'llvm']` + // + // For that, we will need the following GCC target pairs: + // 1. T1 -> T2 (to cross-compile a T2 rustc using cg_gcc running on T1) + // 2. T2 -> T2 (to build host code with the stage 2 rustc running on T2) + // 3. T2 -> T3 (to cross-compile code with the stage 2 rustc running on T2) + // + // FIXME: this set of targets is *maximal*, in reality we might need + // less libgccjits at this current build stage. Try to reduce the set of + // GCC dylibs built below by taking a look at the current stage and whether + // cg_gcc is used as the default codegen backend. + + let compilers = prepare_compilers(); + + // The left side of the target pairs below is implied. It has to match the + // host target on which cg_gcc will run, which is the host target of + // `target_compiler`. We only pass the right side of the target pairs to + // the `GccCodegenBackend` constructor. + let mut targets = HashSet::new(); + // Add all host targets, so that we are able to build host code in this + // bootstrap invocation using cg_gcc. + for target in &builder.hosts { + targets.insert(*target); + } + // Add all stdlib targets, so that the built rustc can produce code for them + for target in &builder.targets { + targets.insert(*target); + } + // Add the host target of the built rustc itself, so that it can build + // host code (e.g. proc macros) using cg_gcc. + targets.insert(compilers.target_compiler().host); + + let output = builder.ensure(GccCodegenBackend::for_targets( + compilers, + targets.into_iter().collect(), + )); copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler); - // Also copy libgccjit to the library sysroot, so that it is available for - // the codegen backend. - output.gcc.install_to(builder, &rustc_libdir); + // Also copy all requires libgccjit dylibs to the corresponding + // library sysroots, so that they are available for the codegen backend. + output.dylib_set.install_to(builder, target_compiler); } CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue, } diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index caf0af35e401..cbbdc7e02419 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -21,6 +21,7 @@ use tracing::instrument; use crate::core::build_steps::compile::{get_codegen_backend_file, normalize_codegen_backend_name}; use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::build_steps::gcc::GccTargetPair; use crate::core::build_steps::tool::{ self, RustcPrivateCompilers, ToolTargetBuildMode, get_tool_target_compiler, }; @@ -2856,8 +2857,9 @@ impl Step for Gcc { fn run(self, builder: &Builder<'_>) -> Self::Output { let tarball = Tarball::new(builder, "gcc", &self.target.triple); - let output = builder.ensure(super::gcc::Gcc { target: self.target }); - tarball.add_file(&output.libgccjit, "lib", FileType::NativeLibrary); + let output = builder + .ensure(super::gcc::Gcc { target_pair: GccTargetPair::for_native_build(self.target) }); + tarball.add_file(output.libgccjit(), "lib", FileType::NativeLibrary); tarball.generate() } diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index fc87c48f17b6..648a2bea4181 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -8,49 +8,69 @@ //! GCC and compiler-rt are essentially just wired up to everything else to //! ensure that they're always in place if needed. +use std::fmt::{Display, Formatter}; use std::fs; use std::path::{Path, PathBuf}; use std::sync::OnceLock; -use crate::FileType; use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{self, t}; +/// GCC cannot cross-compile from a single binary to multiple targets. +/// So we need to have a separate GCC dylib for each (host, target) pair. +/// We represent this explicitly using this struct. +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct GccTargetPair { + /// Target on which the libgccjit.so dylib will be executed. + host: TargetSelection, + /// Target for which the libgccjit.so dylib will generate assembly. + target: TargetSelection, +} + +impl GccTargetPair { + /// Create a target pair for a GCC that will run on `target` and generate assembly for `target`. + pub fn for_native_build(target: TargetSelection) -> Self { + Self { host: target, target } + } + + /// Create a target pair for a GCC that will run on `host` and generate assembly for `target`. + /// This may be cross-compilation if `host != target`. + pub fn for_target_pair(host: TargetSelection, target: TargetSelection) -> Self { + Self { host, target } + } + + pub fn host(&self) -> TargetSelection { + self.host + } + + pub fn target(&self) -> TargetSelection { + self.target + } +} + +impl Display for GccTargetPair { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{} -> {}", self.host, self.target) + } +} + #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Gcc { - pub target: TargetSelection, + pub target_pair: GccTargetPair, } #[derive(Clone)] pub struct GccOutput { - pub libgccjit: PathBuf, - target: TargetSelection, + /// Path to a built or downloaded libgccjit. + libgccjit: PathBuf, } impl GccOutput { - /// Install the required libgccjit library file(s) to the specified `path`. - pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) { - if builder.config.dry_run() { - return; - } - - let target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string(); - - // If we build libgccjit ourselves, then `self.libgccjit` can actually be a symlink. - // In that case, we have to resolve it first, otherwise we'd create a symlink to a symlink, - // which wouldn't work. - let actual_libgccjit_path = t!( - self.libgccjit.canonicalize(), - format!("Cannot find libgccjit at {}", self.libgccjit.display()) - ); - - let dest_dir = directory.join("rustlib").join(self.target).join("lib"); - t!(fs::create_dir_all(&dest_dir)); - let dst = dest_dir.join(target_filename); - builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary); + pub fn libgccjit(&self) -> &Path { + &self.libgccjit } } @@ -64,33 +84,38 @@ impl Step for Gcc { } fn make_run(run: RunConfig<'_>) { - run.builder.ensure(Gcc { target: run.target }); + // By default, we build libgccjit that can do native compilation (no cross-compilation) + // on a given target. + run.builder + .ensure(Gcc { target_pair: GccTargetPair { host: run.target, target: run.target } }); } /// Compile GCC (specifically `libgccjit`) for `target`. fn run(self, builder: &Builder<'_>) -> Self::Output { - let target = self.target; + let target_pair = self.target_pair; // If GCC has already been built, we avoid building it again. - let metadata = match get_gcc_build_status(builder, target) { - GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path, target }, + let metadata = match get_gcc_build_status(builder, target_pair) { + GccBuildStatus::AlreadyBuilt(path) => return GccOutput { libgccjit: path }, GccBuildStatus::ShouldBuild(m) => m, }; - let _guard = builder.msg_unstaged(Kind::Build, "GCC", target); + let action = Kind::Build.description(); + let msg = format!("{action} GCC for {target_pair}"); + let _guard = builder.group(&msg); t!(metadata.stamp.remove()); let _time = helpers::timeit(builder); let libgccjit_path = libgccjit_built_path(&metadata.install_dir); if builder.config.dry_run() { - return GccOutput { libgccjit: libgccjit_path, target }; + return GccOutput { libgccjit: libgccjit_path }; } - build_gcc(&metadata, builder, target); + build_gcc(&metadata, builder, target_pair); t!(metadata.stamp.write()); - GccOutput { libgccjit: libgccjit_path, target } + GccOutput { libgccjit: libgccjit_path } } } @@ -111,15 +136,27 @@ pub enum GccBuildStatus { /// are available for the given target. /// Returns a path to the libgccjit.so file. #[cfg(not(test))] -fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option { +fn try_download_gcc(builder: &Builder<'_>, target_pair: GccTargetPair) -> Option { use build_helper::git::PathFreshness; // Try to download GCC from CI if configured and available if !matches!(builder.config.gcc_ci_mode, crate::core::config::GccCiMode::DownloadFromCi) { return None; } - if target != "x86_64-unknown-linux-gnu" { - eprintln!("GCC CI download is only available for the `x86_64-unknown-linux-gnu` target"); + + // We currently do not support downloading CI GCC if the host/target pair doesn't match. + if target_pair.host != target_pair.target { + eprintln!( + "GCC CI download is not available when the host ({}) does not equal the compilation target ({}).", + target_pair.host, target_pair.target + ); + return None; + } + + if target_pair.host != "x86_64-unknown-linux-gnu" { + eprintln!( + "GCC CI download is only available for the `x86_64-unknown-linux-gnu` host/target" + ); return None; } let source = detect_gcc_freshness( @@ -132,7 +169,7 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option { // Download from upstream CI - let root = ci_gcc_root(&builder.config, target); + let root = ci_gcc_root(&builder.config, target_pair.target); let gcc_stamp = BuildStamp::new(&root).with_prefix("gcc").add_stamp(&upstream); if !gcc_stamp.is_up_to_date() && !builder.config.dry_run() { builder.config.download_ci_gcc(&upstream, &root); @@ -158,7 +195,7 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option, _target: TargetSelection) -> Option { +fn try_download_gcc(_builder: &Builder<'_>, _target_pair: GccTargetPair) -> Option { None } @@ -167,11 +204,37 @@ fn try_download_gcc(_builder: &Builder<'_>, _target: TargetSelection) -> Option< /// /// It's used to avoid busting caches during x.py check -- if we've already built /// GCC, it's fine for us to not try to avoid doing so. -pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> GccBuildStatus { - if let Some(path) = try_download_gcc(builder, target) { +pub fn get_gcc_build_status(builder: &Builder<'_>, target_pair: GccTargetPair) -> GccBuildStatus { + // Prefer taking externally provided prebuilt libgccjit dylib + if let Some(dir) = &builder.config.libgccjit_libs_dir { + // The dir structure should be ///libgccjit.so + let host_dir = dir.join(target_pair.host); + let path = host_dir.join(target_pair.target).join("libgccjit.so"); + if path.exists() { + return GccBuildStatus::AlreadyBuilt(path); + } else { + builder.info(&format!( + "libgccjit.so for `{target_pair}` was not found at `{}`", + path.display() + )); + + if target_pair.host != target_pair.target || target_pair.host != builder.host_target { + eprintln!( + "info: libgccjit.so for `{target_pair}` was not found at `{}`", + path.display() + ); + eprintln!("error: we do not support downloading or building a GCC cross-compiler"); + std::process::exit(1); + } + } + } + + // If not available, try to download from CI + if let Some(path) = try_download_gcc(builder, target_pair) { return GccBuildStatus::AlreadyBuilt(path); } + // If not available, try to build (or use already built libgccjit from disk) static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { generate_smart_stamp_hash( @@ -185,8 +248,8 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G builder.config.update_submodule("src/gcc"); let root = builder.src.join("src/gcc"); - let out_dir = builder.gcc_out(target).join("build"); - let install_dir = builder.gcc_out(target).join("install"); + let out_dir = gcc_out(builder, target_pair).join("build"); + let install_dir = gcc_out(builder, target_pair).join("install"); let stamp = BuildStamp::new(&out_dir).with_prefix("gcc").add_stamp(smart_stamp_hash); @@ -215,15 +278,20 @@ pub fn get_gcc_build_status(builder: &Builder<'_>, target: TargetSelection) -> G GccBuildStatus::ShouldBuild(Meta { stamp, out_dir, install_dir, root }) } +fn gcc_out(builder: &Builder<'_>, pair: GccTargetPair) -> PathBuf { + builder.out.join(pair.host).join("gcc").join(pair.target) +} + /// Returns the path to a libgccjit.so file in the install directory of GCC. fn libgccjit_built_path(install_dir: &Path) -> PathBuf { install_dir.join("lib/libgccjit.so") } -fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) { - if builder.build.cc_tool(target).is_like_clang() - || builder.build.cxx_tool(target).is_like_clang() - { +fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target_pair: GccTargetPair) { + // Target on which libgccjit.so will be executed. Here we will generate a dylib with + // instructions for that target. + let host = target_pair.host; + if builder.build.cc_tool(host).is_like_clang() || builder.build.cxx_tool(host).is_like_clang() { panic!( "Attempting to build GCC using Clang, which is known to misbehave. Please use GCC as the host C/C++ compiler. " ); @@ -241,7 +309,7 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) { // builds. // Therefore, we first copy the whole source directory to the build directory, and perform the // build from there. - let src_dir = builder.gcc_out(target).join("src"); + let src_dir = gcc_out(builder, target_pair).join("src"); if src_dir.exists() { builder.remove_dir(&src_dir); } @@ -259,7 +327,7 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) { .arg("--disable-multilib") .arg(format!("--prefix={}", install_dir.display())); - let cc = builder.build.cc(target).display().to_string(); + let cc = builder.build.cc(host).display().to_string(); let cc = builder .build .config @@ -268,7 +336,7 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) { .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}")); configure_cmd.env("CC", cc); - if let Ok(ref cxx) = builder.build.cxx(target) { + if let Ok(ref cxx) = builder.build.cxx(host) { let cxx = cxx.display().to_string(); let cxx = builder .build diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index db2a76c4a2df..51a791daef28 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -925,7 +925,7 @@ impl Step for Enzyme { } let target = self.target; - let LlvmResult { host_llvm_config, .. } = builder.ensure(Llvm { target: self.target }); + let LlvmResult { host_llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target }); static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { @@ -955,15 +955,20 @@ impl Step for Enzyme { return out_dir; } + if !builder.config.dry_run() && !llvm_cmake_dir.is_dir() { + builder.info(&format!( + "WARNING: {} does not exist, Enzyme build will likely fail", + llvm_cmake_dir.display() + )); + } + trace!(?target, "(re)building enzyme artifacts"); builder.info(&format!("Building Enzyme for {target}")); t!(stamp.remove()); let _time = helpers::timeit(builder); t!(fs::create_dir_all(&out_dir)); - builder - .config - .update_submodule(Path::new("src").join("tools").join("enzyme").to_str().unwrap()); + builder.config.update_submodule("src/tools/enzyme"); let mut cfg = cmake::Config::new(builder.src.join("src/tools/enzyme/enzyme/")); configure_cmake(builder, target, &mut cfg, true, LdFlags::default(), &[]); @@ -983,7 +988,7 @@ impl Step for Enzyme { .define("LLVM_ENABLE_ASSERTIONS", "ON") .define("ENZYME_EXTERNAL_SHARED_LIB", "ON") .define("ENZYME_BC_LOADER", "OFF") - .define("LLVM_DIR", builder.llvm_out(target)); + .define("LLVM_DIR", llvm_cmake_dir); cfg.build(); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 9f8bb4dea54f..a58739c9e65c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -16,7 +16,7 @@ use build_helper::exit; use crate::core::build_steps::compile::{Std, run_cargo}; use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; -use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; +use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; use crate::core::build_steps::run::{get_completion_paths, get_help_path}; use crate::core::build_steps::synthetic_targets::MirOptPanicAbortSyntheticTarget; @@ -722,7 +722,6 @@ impl Step for Miri { // miri tests need to know about the stage sysroot cargo.env("MIRI_SYSROOT", &miri_sysroot); cargo.env("MIRI_HOST_SYSROOT", &host_sysroot); - cargo.env("MIRI", &miri.tool_path); // Set the target. cargo.env("MIRI_TEST_TARGET", target.rustc_target_arg()); @@ -2181,6 +2180,10 @@ Please disable assertions with `rust.debug-assertions = false`. } if mode == CompiletestMode::Debuginfo { + if let Some(debuggers::Cdb { cdb }) = debuggers::discover_cdb(target) { + cmd.arg("--cdb").arg(cdb); + } + if let Some(debuggers::Gdb { gdb }) = debuggers::discover_gdb(builder, android.as_ref()) { cmd.arg("--gdb").arg(gdb.as_ref()); @@ -2254,6 +2257,10 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--with-std-debug-assertions"); } + if builder.config.rust_remap_debuginfo { + cmd.arg("--with-std-remap-debuginfo"); + } + let mut llvm_components_passed = false; let mut copts_passed = false; if builder.config.llvm_enabled(test_compiler.host) { @@ -3956,7 +3963,7 @@ impl Step for CodegenGCC { let compilers = self.compilers; let target = self.target; - let gcc = builder.ensure(Gcc { target }); + let gcc = builder.ensure(Gcc { target_pair: GccTargetPair::for_native_build(target) }); builder.ensure( compile::Std::new(compilers.build_compiler(), target) @@ -3997,12 +4004,13 @@ impl Step for CodegenGCC { .arg("--use-backend") .arg("gcc") .arg("--gcc-path") - .arg(gcc.libgccjit.parent().unwrap()) + .arg(gcc.libgccjit().parent().unwrap()) .arg("--out-dir") .arg(builder.stage_out(compilers.build_compiler(), Mode::Codegen).join("cg_gcc")) .arg("--release") .arg("--mini-tests") .arg("--std-tests"); + cargo.args(builder.config.test_args()); cargo.into_cmd().run(builder); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 5a6bade59a6a..b0a8ce77efe7 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1025,18 +1025,26 @@ impl Builder<'_> { if let Some(ref map_to) = self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) { + // Tell the compiler which prefix was used for remapping the standard library cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to); } if let Some(ref map_to) = self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::Compiler) { - // When building compiler sources, we want to apply the compiler remap scheme. - cargo.env( - "RUSTC_DEBUGINFO_MAP", - format!("{}={}", self.build.src.display(), map_to), - ); + // Tell the compiler which prefix was used for remapping the compiler it-self cargo.env("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR", map_to); + + // When building compiler sources, we want to apply the compiler remap scheme. + let map = [ + // Cargo use relative paths for workspace members, so let's remap those. + format!("compiler/={map_to}/compiler"), + // rustc creates absolute paths (in part bc of the `rust-src` unremap + // and for working directory) so let's remap the build directory as well. + format!("{}={map_to}", self.build.src.display()), + ] + .join("\t"); + cargo.env("RUSTC_DEBUGINFO_MAP", map); } } Mode::Std @@ -1047,10 +1055,16 @@ impl Builder<'_> { if let Some(ref map_to) = self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler) { - cargo.env( - "RUSTC_DEBUGINFO_MAP", - format!("{}={}", self.build.src.display(), map_to), - ); + // When building the standard library sources, we want to apply the std remap scheme. + let map = [ + // Cargo use relative paths for workspace members, so let's remap those. + format!("library/={map_to}/library"), + // rustc creates absolute paths (in part bc of the `rust-src` unremap + // and for working directory) so let's remap the build directory as well. + format!("{}={map_to}", self.build.src.display()), + ] + .join("\t"); + cargo.env("RUSTC_DEBUGINFO_MAP", map); } } } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 2f493658ec0e..a81b8ccb9140 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -188,6 +188,7 @@ pub struct Config { // gcc codegen options pub gcc_ci_mode: GccCiMode, + pub libgccjit_libs_dir: Option, // rust codegen options pub rust_optimize: RustOptimize, @@ -620,7 +621,10 @@ impl Config { vendor: dist_vendor, } = toml.dist.unwrap_or_default(); - let Gcc { download_ci_gcc: gcc_download_ci_gcc } = toml.gcc.unwrap_or_default(); + let Gcc { + download_ci_gcc: gcc_download_ci_gcc, + libgccjit_libs_dir: gcc_libgccjit_libs_dir, + } = toml.gcc.unwrap_or_default(); if rust_bootstrap_override_lld.is_some() && rust_bootstrap_override_lld_legacy.is_some() { panic!( @@ -1346,6 +1350,7 @@ impl Config { keep_stage: flags_keep_stage, keep_stage_std: flags_keep_stage_std, libdir: install_libdir.map(PathBuf::from), + libgccjit_libs_dir: gcc_libgccjit_libs_dir, library_docs_private_items: build_library_docs_private_items.unwrap_or(false), lld_enabled, lldb: build_lldb.map(PathBuf::from), diff --git a/src/bootstrap/src/core/config/toml/gcc.rs b/src/bootstrap/src/core/config/toml/gcc.rs index 9ea697edf159..94d15a9baaff 100644 --- a/src/bootstrap/src/core/config/toml/gcc.rs +++ b/src/bootstrap/src/core/config/toml/gcc.rs @@ -15,5 +15,6 @@ define_config! { #[derive(Default)] struct Gcc { download_ci_gcc: Option = "download-ci-gcc", + libgccjit_libs_dir: Option = "libgccjit-libs-dir", } } diff --git a/src/bootstrap/src/core/config/toml/llvm.rs b/src/bootstrap/src/core/config/toml/llvm.rs index 9523f8021484..9751837a8879 100644 --- a/src/bootstrap/src/core/config/toml/llvm.rs +++ b/src/bootstrap/src/core/config/toml/llvm.rs @@ -117,7 +117,7 @@ pub fn check_incompatible_options_for_ci_llvm( enable_warnings, download_ci_llvm: _, build_config, - enzyme: _, + enzyme, } = ci_llvm_config; err!(current_llvm_config.optimize, optimize); @@ -139,6 +139,7 @@ pub fn check_incompatible_options_for_ci_llvm( err!(current_llvm_config.clang, clang); err!(current_llvm_config.build_config, build_config); err!(current_llvm_config.plugins, plugins); + err!(current_llvm_config.enzyme, enzyme); warn!(current_llvm_config.enable_warnings, enable_warnings); diff --git a/src/bootstrap/src/core/debuggers/cdb.rs b/src/bootstrap/src/core/debuggers/cdb.rs new file mode 100644 index 000000000000..a19b70477ecf --- /dev/null +++ b/src/bootstrap/src/core/debuggers/cdb.rs @@ -0,0 +1,41 @@ +use std::env; +use std::path::PathBuf; + +use crate::core::config::TargetSelection; + +pub(crate) struct Cdb { + pub(crate) cdb: PathBuf, +} + +/// FIXME: This CDB discovery code was very questionable when it was in +/// compiletest, and it's just as questionable now that it's in bootstrap. +pub(crate) fn discover_cdb(target: TargetSelection) -> Option { + if !cfg!(windows) || !target.ends_with("-pc-windows-msvc") { + return None; + } + + let pf86 = + PathBuf::from(env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?); + let cdb_arch = if cfg!(target_arch = "x86") { + "x86" + } else if cfg!(target_arch = "x86_64") { + "x64" + } else if cfg!(target_arch = "aarch64") { + "arm64" + } else if cfg!(target_arch = "arm") { + "arm" + } else { + return None; // No compatible CDB.exe in the Windows 10 SDK + }; + + let mut path = pf86; + path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too? + path.push(cdb_arch); + path.push(r"cdb.exe"); + + if !path.exists() { + return None; + } + + Some(Cdb { cdb: path }) +} diff --git a/src/bootstrap/src/core/debuggers/mod.rs b/src/bootstrap/src/core/debuggers/mod.rs index a11eda8e686e..65d76e114189 100644 --- a/src/bootstrap/src/core/debuggers/mod.rs +++ b/src/bootstrap/src/core/debuggers/mod.rs @@ -1,8 +1,10 @@ //! Code for discovering debuggers and debugger-related configuration, so that //! it can be passed to compiletest when running debuginfo tests. +pub(crate) use self::cdb::{Cdb, discover_cdb}; pub(crate) use self::gdb::{Gdb, discover_gdb}; pub(crate) use self::lldb::{Lldb, discover_lldb}; +mod cdb; mod gdb; mod lldb; diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 50d8154014bb..932a58c954df 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -34,14 +34,8 @@ pub struct Finder { // // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap). const STAGE0_MISSING_TARGETS: &[&str] = &[ - "aarch64-unknown-helenos", - "i686-unknown-helenos", - "x86_64-unknown-helenos", - "powerpc-unknown-helenos", - "sparc64-unknown-helenos", // just a dummy comment so the list doesn't get onelined - "riscv64gc-unknown-redox", - "hexagon-unknown-qurt", + "riscv64im-unknown-none-elf", ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index a31eb0c1c801..adae9a1b8b08 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -975,10 +975,6 @@ impl Build { self.out.join(&*target.triple).join("enzyme") } - fn gcc_out(&self, target: TargetSelection) -> PathBuf { - self.out.join(&*target.triple).join("gcc") - } - fn lld_out(&self, target: TargetSelection) -> PathBuf { self.out.join(target).join("lld") } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 37ad5f09897f..d7990c2316d1 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -601,4 +601,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New options `rust.rustflags` for all targets and per-target `rustflags` that will pass specified flags to rustc for all stages. Target-specific flags override global `rust.rustflags` ones.", }, + ChangeInfo { + change_id: 149354, + severity: ChangeSeverity::Info, + summary: "New option `gcc.libgccjit-libs-dir` to specify which libgccjit.so to use per target.", + }, ]; diff --git a/src/ci/docker/scripts/build-gcc.sh b/src/ci/docker/scripts/build-gcc.sh index 11db5aa811ce..ab4cae21e025 100755 --- a/src/ci/docker/scripts/build-gcc.sh +++ b/src/ci/docker/scripts/build-gcc.sh @@ -7,7 +7,7 @@ source shared.sh # This version is specified in the Dockerfile GCC=$GCC_VERSION -curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | xzcat | tar xf - +curl https://ci-mirrors.rust-lang.org/rustc/gcc/gcc-$GCC.tar.xz | xzcat | tar xf - cd gcc-$GCC # FIXME(#49246): Remove the `sed` below. diff --git a/src/doc/nomicon b/src/doc/nomicon index 9fe8fa599ad2..5b3a9d084cbc 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 9fe8fa599ad228dda74f240cc32b54bc5c1aa3e6 +Subproject commit 5b3a9d084cbc64e54da87e3eec7c7faae0e48ba9 diff --git a/src/doc/reference b/src/doc/reference index 50c5de90487b..ec78de0ffe2f 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 50c5de90487b68d429a30cc9466dc8f5b410128f +Subproject commit ec78de0ffe2f8344bd0e222b17ac7a7d32dc7a26 diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index fe92bc876cf7..f2f2f7ed1485 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -7,7 +7,7 @@ on: pull_request: schedule: # Run multiple times a day as the successfull cached links are not checked every time. - - cron: '0 */8 * * *' + - cron: "0 */8 * * *" jobs: ci: @@ -83,16 +83,6 @@ jobs: git commit -m "Deploy ${GITHUB_SHA} to gh-pages" git push --quiet -f "https://x-token:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}" HEAD:gh-pages - - name: Cache sembr build - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - ci/sembr/target/ - key: sembr-${{ hashFiles('ci/sembr/Cargo.lock') }} - - name: Check if files comply with semantic line breaks continue-on-error: true run: | diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index fee7cf042e1f..59d6719c71cb 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -1,19 +1,15 @@ [![CI](https://github.com/rust-lang/rustc-dev-guide/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-lang/rustc-dev-guide/actions/workflows/ci.yml) - -This is a collaborative effort to build a guide that explains how rustc -works. The aim of the guide is to help new contributors get oriented -to rustc, as well as to help more experienced folks in figuring out +This is a collaborative effort to build a guide that explains how rustc works. +The aim of the guide is to help new contributors get oriented to rustc, +as well as to help more experienced folks in figuring out some new part of the compiler that they haven't worked on before. -[You can read the latest version of the guide here.](https://rustc-dev-guide.rust-lang.org/) +You may also find the [rustc API docs] useful. -You may also find the rustdocs [for the compiler itself][rustdocs] useful. Note that these are not intended as a guide; it's recommended that you search for the docs you're looking for instead of reading them top to bottom. -[rustdocs]: https://doc.rust-lang.org/nightly/nightly-rustc - For documentation on developing the standard library, see [`std-dev-guide`](https://std-dev-guide.rust-lang.org/). @@ -21,22 +17,26 @@ For documentation on developing the standard library, see The guide is useful today, but it has a lot of work still to go. -If you'd like to help improve the guide, we'd love to have you! You can find -plenty of issues on the [issue -tracker](https://github.com/rust-lang/rustc-dev-guide/issues). Just post a -comment on the issue you would like to work on to make sure that we don't -accidentally duplicate work. If you think something is missing, please open an -issue about it! +If you'd like to help improve the guide, we'd love to have you! +You can find plenty of issues on the [issue +tracker](https://github.com/rust-lang/rustc-dev-guide/issues). +Just post a comment on the issue you would like to work on to make sure that we don't +accidentally duplicate work. +If you think something is missing, please open an issue about it! **In general, if you don't know how the compiler works, that is not a problem!** In that case, what we will do is to schedule a bit of time for you to talk with someone who **does** know the code, or who wants -to pair with you and figure it out. Then you can work on writing up -what you learned. +to pair with you and figure it out. +Then you can work on writing up what you learned. In general, when writing about a particular part of the compiler's code, we -recommend that you link to the relevant parts of the [rustc -rustdocs][rustdocs]. +recommend that you link to the relevant parts of the [rustc API docs]. + +The guide has a much lower bar for what it takes for a PR to be merged. +Check out the forge documentation for [our policy][forge_policy]. + +[forge_policy]: https://forge.rust-lang.org/rustc-dev-guide/index.html#review-policy ### Build Instructions @@ -56,9 +56,9 @@ The build files are found in the `book/html` directory. ### Link Validations -We use `mdbook-linkcheck2` to validate URLs included in our documentation. Link -checking is **not** run by default locally, though it is in CI. To enable it -locally, set the environment variable `ENABLE_LINKCHECK=1` like in the +We use `mdbook-linkcheck2` to validate URLs included in our documentation. +Link checking is **not** run by default locally, though it is in CI. +To enable it locally, set the environment variable `ENABLE_LINKCHECK=1` like in the following example. ``` @@ -67,6 +67,9 @@ ENABLE_LINKCHECK=1 mdbook serve ## Synchronizing josh subtree with rustc -This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization. +This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. +You can use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization. You can find a guide on how to perform the synchronization [here](./src/external-repos.md#synchronizing-a-josh-subtree). + +[rustc API docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 7ace34aed984..2539a9eadda3 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -177,6 +177,9 @@ fn lengthen_lines(content: &str, limit: usize) -> String { let Some(next_line) = content.get(n + 1) else { continue; }; + if next_line.trim_start().starts_with("```") { + continue; + } if ignore(next_line, in_code_block) || REGEX_LIST_ENTRY.is_match(next_line) || REGEX_IGNORE_END.is_match(line) @@ -255,6 +258,12 @@ preserve next line preserve next line * three + +do not mess with code block chars +``` +leave the +text alone +``` "; let expected = "\ do not split short sentences @@ -269,6 +278,12 @@ preserve next line preserve next line * three + +do not mess with code block chars +``` +leave the +text alone +``` "; assert_eq!(expected, lengthen_lines(original, 50)); } @@ -294,40 +309,6 @@ fn test_prettify_ignore_link_targets() { assert_eq!(original, lengthen_lines(original, 100)); } -#[test] -fn test_sembr_then_prettify() { - let original = " -hi there. do -not split -short sentences. -hi again. -"; - let expected = " -hi there. -do -not split -short sentences. -hi again. -"; - let processed = comply(original); - assert_eq!(expected, processed); - let expected = " -hi there. -do not split -short sentences. -hi again. -"; - let processed = lengthen_lines(&processed, 50); - assert_eq!(expected, processed); - let expected = " -hi there. -do not split short sentences. -hi again. -"; - let processed = lengthen_lines(&processed, 50); - assert_eq!(expected, processed); -} - #[test] fn test_sembr_question_mark() { let original = " diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 7a84872f266d..71a84e2bda12 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -dfe1b8c97bcde283102f706d5dcdc3649e5e12e3 +cec70080fd441d16e9fb08a0d1d1a04c72d1ed25 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index c136d37160c5..e80ee6a5137d 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -66,6 +66,7 @@ - [ARM](notification-groups/arm.md) - [Emscripten](notification-groups/emscripten.md) - [Fuchsia](notification-groups/fuchsia.md) + - [LoongArch](notification-groups/loongarch.md) - [RISC-V](notification-groups/risc-v.md) - [Rust for Linux](notification-groups/rust-for-linux.md) - [WASI](notification-groups/wasi.md) @@ -126,8 +127,8 @@ - [Lang Items](./lang-items.md) - [The HIR (High-level IR)](./hir.md) - [Lowering AST to HIR](./hir/lowering.md) - - [Ambig/Unambig Types and Consts](./hir/ambig-unambig-ty-and-consts.md) - [Debugging](./hir/debugging.md) +- [Ambig/Unambig Types and Consts](./ambig-unambig-ty-and-consts.md) - [The THIR (Typed High-level IR)](./thir.md) - [The MIR (Mid-level IR)](./mir/index.md) - [MIR construction](./mir/construction.md) @@ -185,12 +186,14 @@ - [Proof trees](./solve/proof-trees.md) - [Opaque types](./solve/opaque-types.md) - [Significant changes and quirks](./solve/significant-changes.md) + - [Sharing the trait solver with rust-analyzer](./solve/sharing-crates-with-rust-analyzer.md) - [`Unsize` and `CoerceUnsized` traits](./traits/unsize.md) - [Variance](./variance.md) - [Coherence checking](./coherence.md) - [HIR Type checking](./hir-typeck/summary.md) - [Coercions](./hir-typeck/coercions.md) - [Method lookup](./hir-typeck/method-lookup.md) +- [Const Generics](./const-generics.md) - [Opaque types](./opaque-types-type-alias-impl-trait.md) - [Inference details](./opaque-types-impl-trait-inference.md) - [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md) @@ -230,11 +233,22 @@ - [Debugging LLVM](./backend/debugging.md) - [Backend Agnostic Codegen](./backend/backend-agnostic.md) - [Implicit caller location](./backend/implicit-caller-location.md) +- [Debug Info](./debuginfo/intro.md) + - [Rust Codegen](./debuginfo/rust-codegen.md) + - [LLVM Codegen](./debuginfo/llvm-codegen.md) + - [Debugger Internals](./debuginfo/debugger-internals.md) + - [LLDB Internals](./debuginfo/lldb-internals.md) + - [GDB Internals](./debuginfo/gdb-internals.md) + - [Debugger Visualizers](./debuginfo/debugger-visualizers.md) + - [LLDB - Python Providers](./debuginfo/lldb-visualizers.md) + - [GDB - Python Providers](./debuginfo/gdb-visualizers.md) + - [CDB - Natvis](./debuginfo/natvis-visualizers.md) + - [Testing](./debuginfo/testing.md) + - [(Lecture Notes) Debugging support in the Rust compiler](./debugging-support-in-rustc.md) - [Libraries and metadata](./backend/libs-and-metadata.md) - [Profile-guided optimization](./profile-guided-optimization.md) - [LLVM source-based code coverage](./llvm-coverage-instrumentation.md) - [Sanitizers support](./sanitizers.md) -- [Debugging support in the Rust compiler](./debugging-support-in-rustc.md) --- diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index 4f5733ae0821..841691d859f9 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -58,21 +58,24 @@ please see the corresponding [subsection on writing documentation in this guide] [subsection on writing documentation in this guide]: contributing.md#contributing-to-rustc-dev-guide -> “‘All conditioned things are impermanent’ — +> “‘All conditioned things are impermanent’ — > when one sees this with wisdom, one turns away from suffering.” > _The Dhammapada, verse 277_ ## Other places to find information +This guide, the one you are currently reading, +contains information about how various parts of the compiler work, +and how to contribute to the compiler. + You might also find the following sites useful: -- This guide contains information about how various parts of the - compiler work and how to contribute to the compiler. - [rustc API docs] -- rustdoc documentation for the compiler, devtools, and internal tools - [Forge] -- contains documentation about Rust infrastructure, team procedures, and more - [compiler-team] -- the home-base for the Rust compiler team, with description of the team procedures, active working groups, and the team calendar. - [std-dev-guide] -- a similar guide for developing the standard library. +- [rust-analyzer book] -- documentation for the rust-analyzer. - [The t-compiler Zulip][z] - The [Rust Internals forum][rif], a place to ask questions and discuss Rust's internals @@ -110,4 +113,5 @@ You might also find the following sites useful: [Forge]: https://forge.rust-lang.org/ [compiler-team]: https://github.com/rust-lang/compiler-team/ [std-dev-guide]: https://std-dev-guide.rust-lang.org/ +[rust-analyzer book]: https://rust-analyzer.github.io/book/ [z]: https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler diff --git a/src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md b/src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md new file mode 100644 index 000000000000..64a15b7f9aa8 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md @@ -0,0 +1,111 @@ +# Ambig/Unambig Types and Consts + +Types and Consts args in the AST/HIR can be in two kinds of positions ambiguous (ambig) or unambiguous (unambig). Ambig positions are where +it would be valid to parse either a type or a const, unambig positions are where only one kind would be valid to +parse. + +```rust +fn func(arg: T) { + // ^ Unambig type position + let a: _ = arg; + // ^ Unambig type position + + func::(arg); + // ^ ^ + // ^^^^ Ambig position + + let _: [u8; 10]; + // ^^ ^^ Unambig const position + // ^^ Unambig type position +} + +``` + +Most types/consts in ambig positions are able to be disambiguated as either a type or const during parsing. The only exceptions to this are paths and inferred generic arguments. + +## Paths + +```rust +struct Foo; + +fn foo(_: Foo) {} +``` + +At parse time we parse all unbraced generic arguments as *types* (ie they wind up as [`ast::GenericArg::Ty`]). In the above example this means we would parse the generic argument to `Foo` as an `ast::GenericArg::Ty` wrapping a [`ast::Ty::Path(N)`]. + +Then during name resolution: +- When encountering a single segment path with no generic arguments in generic argument position, we will first try to resolve it in the type namespace and if that fails we then attempt to resolve in the value namespace. +- All other kinds of paths we only try to resolve in the type namespace + +See [`LateResolutionVisitor::visit_generic_arg`] for where this is implemented. + +Finally during AST lowering when we attempt to lower a type argument, we first check if it is a `Ty::Path` and if it resolved to something in the value namespace. If it did then we create an *anon const* and lower to a const argument instead of a type argument. + +See [`LoweringContext::lower_generic_arg`] for where this is implemented. + +Note that the ambiguity for paths is not propgated into the HIR; there's no `hir::GenericArg::Path` which is turned into either a `Ty` or `Const` during HIR ty lowering (though we could do such a thing). + +## Inferred arguments (`_`) + +```rust +struct Foo; + +fn foo() { + let _unused: Foo<_>; +} +``` + +The only generic arguments which remain ambiguous after lowering are inferred generic arguments (`_`) in path segments. In the above example it is not clear at parse time whether the `_` argument to `Foo` is an inferred type argument, or an inferred const argument. + +In ambig AST positions, inferred argumentsd are parsed as an [`ast::GenericArg::Ty`] wrapping a [`ast::Ty::Infer`]. Then during AST lowering when lowering an `ast::GenericArg::Ty` we check if it is an inferred type and if so lower to a [`hir::GenericArg::Infer`]. + +In unambig AST positions, inferred arguments are parsed as either `ast::Ty::Infer` or [`ast::AnonConst`]. The `AnonConst` case is quite strange, we use [`ast::ExprKind::Underscore`] to represent the "body" of the "anon const" although in reality we do not actually lower this to an anon const in the HIR. + +It may be worth seeing if we can refactor the AST to have `ast::GenericArg::Infer` and then get rid of this overloaded meaning of `AnonConst`, as well as the reuse of `ast::Ty::Infer` in ambig positions. + +In unambig AST positions, during AST lowering we lower inferred arguments to [`hir::TyKind::Infer`][ty_infer] or [`hir::ConstArgKind::Infer`][const_infer] depending on whether it is a type or const position respectively. +In ambig AST positions, during AST lowering we lower inferred arguments to [`hir::GenericArg::Infer`][generic_arg_infer]. See [`LoweringContext::lower_generic_arg`] for where this is implemented. + +A naive implementation of this would result in there being potentially 5 places where you might think an inferred type/const could be found in the HIR from looking at the structure of the HIR: +1. In unambig type position as a `TyKind::Infer` +2. In unambig const arg position as a `ConstArgKind::Infer` +3. In an ambig position as a [`GenericArg::Type(TyKind::Infer)`][generic_arg_ty] +4. In an ambig position as a [`GenericArg::Const(ConstArgKind::Infer)`][generic_arg_const] +5. In an ambig position as a `GenericArg::Infer` + +Note that places 3 and 4 would never actually be possible to encounter as we always lower to `GenericArg::Infer` in generic arg position. + +This has a few failure modes: +- People may write visitors which check for `GenericArg::Infer` but forget to check for `hir::TyKind/ConstArgKind::Infer`, only handling infers in ambig positions by accident. +- People may write visitors which check for `TyKind/ConstArgKind::Infer` but forget to check for `GenericArg::Infer`, only handling infers in unambig positions by accident. +- People may write visitors which check for `GenericArg::Type/Const(TyKind/ConstArgKind::Infer)` and `GenericArg::Infer`, not realising that we never represent inferred types/consts in ambig positions as a `GenericArg::Type/Const`. +- People may write visitors which check for *only* `TyKind::Infer` and not `ConstArgKind::Infer` forgetting that there are also inferred const arguments (and vice versa). + +To make writing HIR visitors less error prone when caring about inferred types/consts we have a relatively complex system: + +1. We have different types in the compiler for when a type or const is in an unambig or ambig position, `Ty` and `Ty<()>`. [`AmbigArg`][ambig_arg] is an uninhabited type which we use in the `Infer` variant of `TyKind` and `ConstArgKind` to selectively "disable" it if we are in an ambig position. + +2. The [`visit_ty`][visit_ty] and [`visit_const_arg`][visit_const_arg] methods on HIR visitors only accept the ambig position versions of types/consts. Unambig types/consts are implicitly converted to ambig types/consts during the visiting process, with the `Infer` variant handled by a dedicated [`visit_infer`][visit_infer] method. + +This has a number of benefits: +- It's clear that `GenericArg::Type/Const` cannot represent inferred type/const arguments +- Implementors of `visit_ty` and `visit_const_arg` will never encounter inferred types/consts making it impossible to write a visitor that seems to work right but handles edge cases wrong +- The `visit_infer` method handles *all* cases of inferred type/consts in the HIR making it easy for visitors to handle inferred type/consts in one dedicated place and not forget cases + +[ty_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.TyKind.html#variant.Infer +[const_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.ConstArgKind.html#variant.Infer +[generic_arg_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Type +[generic_arg_const]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Const +[generic_arg_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Infer +[ambig_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.AmbigArg.html +[visit_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_ty +[visit_const_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_const_arg +[visit_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_infer +[`LateResolutionVisitor::visit_generic_arg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.LateResolutionVisitor.html#method.visit_generic_arg +[`LoweringContext::lower_generic_arg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/struct.LoweringContext.html#method.lower_generic_arg +[`ast::GenericArg::Ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.GenericArg.html#variant.Type +[`ast::Ty::Infer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.Infer +[`ast::AnonConst`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.AnonConst.html +[`hir::GenericArg::Infer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Infer +[`ast::ExprKind::Underscore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.ExprKind.html#variant.Underscore +[`ast::Ty::Path(N)`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.TyKind.html#variant.Path \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/appendix/bibliography.md b/src/doc/rustc-dev-guide/src/appendix/bibliography.md index 3729194f5fa1..13a9c3a0b40b 100644 --- a/src/doc/rustc-dev-guide/src/appendix/bibliography.md +++ b/src/doc/rustc-dev-guide/src/appendix/bibliography.md @@ -15,7 +15,7 @@ Rust, as well as publications about Rust. * [Safe manual memory management in Cyclone](https://www.cs.umd.edu/projects/PL/cyclone/scp.pdf) * [Skolem Normal Form](https://en.wikipedia.org/wiki/Skolem_normal_form) * [Traits: composable units of behavior](http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf) -* [Uniqueness and Reference Immutability for Safe Parallelism](https://research.microsoft.com/pubs/170528/msr-tr-2012-79.pdf) +* [Uniqueness and Reference Immutability for Safe Parallelism](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/msr-tr-2012-79.pdf) ## Concurrency @@ -25,12 +25,12 @@ Rust, as well as publications about Rust. * [Contention aware scheduling](https://www.blagodurov.net/files/a8-blagodurov.pdf) * [Dynamic circular work stealing deque](https://patents.google.com/patent/US7346753B2/en) - The Chase/Lev deque * [Epoch-based reclamation](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf). -* [Language support for fast and reliable message passing in singularity OS](https://research.microsoft.com/pubs/67482/singsharp.pdf) +* [Language support for fast and reliable message-based communication in singularity OS](https://www.microsoft.com/en-us/research/wp-content/uploads/2006/04/singsharp.pdf) * [Non-blocking steal-half work queues](https://www.cs.bgu.ac.il/%7Ehendlerd/papers/p280-hendler.pdf) * [Reagents: expressing and composing fine-grained concurrency](https://aturon.github.io/academic/reagents.pdf) * [Scheduling multithreaded computations by work stealing](https://www.lri.fr/~cecile/ENSEIGNEMENT/IPAR/Exposes/cilk1.pdf) * [Scheduling techniques for concurrent systems](https://www.stanford.edu/~ouster/cgi-bin/papers/coscheduling.pdf) -* [Singularity: rethinking the software stack](https://research.microsoft.com/pubs/69431/osr2007_rethinkingsoftwarestack.pdf) +* [Singularity: rethinking the software stack](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/osr2007_rethinkingsoftwarestack.pdf) * [The data locality of work stealing](http://www.aladdin.cs.cmu.edu/papers/pdfs/y2000/locality_spaa00.pdf) * [Thread scheduling for multiprogramming multiprocessors](https://dl.acm.org/doi/10.1145/277651.277678) * [Three layer cake for shared-memory programming](https://dl.acm.org/doi/10.1145/1953611.1953616) diff --git a/src/doc/rustc-dev-guide/src/const-generics.md b/src/doc/rustc-dev-guide/src/const-generics.md new file mode 100644 index 000000000000..cb8c7adc07f4 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/const-generics.md @@ -0,0 +1,222 @@ +# Const Generics + +## Kinds of const arguments + +Most of the kinds of `ty::Const` that exist have direct parallels to kinds of types that exist, for example `ConstKind::Param` is equivalent to `TyKind::Param`. + +The main interesting points here are: +- [`ConstKind::Unevaluated`], this is equivalent to `TyKind::Alias` and in the long term should be renamed (as well as introducing an `AliasConstKind` to parallel `ty::AliasKind`). +- [`ConstKind::Value`], this is the final value of a `ty::Const` after monomorphization. This is similar-ish to fully concrete to things like `TyKind::Str` or `TyKind::ADT`. + +For a complete list of *all* kinds of const arguments and how they are actually represented in the type system, see the [`ConstKind`] type. + +Inference Variables are quite boring and treated equivalently to type inference variables almost everywhere. Const Parameters are also similarly boring and equivalent to uses of type parameters almost everywhere. However, there are some interesting subtleties with how they are handled during parsing, name resolution, and AST lowering: [ambig-unambig-ty-and-consts]. + +## Anon Consts + +Anon Consts (short for anonymous const items) are how arbitrary expression are represented in const generics, for example an array length of `1 + 1` or `foo()` or even just `0`. These are unique to const generics and have no real type equivalent. + +### Desugaring + +```rust +struct Foo; +type Alias = [u8; 1 + 1]; +``` + +In this example we have a const argument of `1 + 1` (the array length) which is represented as an *anon const*. The desugaring would look something like: +```rust +struct Foo; + +const ANON: usize = 1 + 1; +type Alias = [u8; ANON]; +``` + +Where the array length in `[u8; ANON]` isn't itself an anon const containing a usage of `ANON`, but a kind of "direct" usage of the `ANON` const item ([`ConstKind::Unevaluated`]). + +Anon consts do not inherit any generic parameters of the item they are inside of: +```rust +struct Foo; +type Alias = [T; 1 + 1]; + +// Desugars To; + +struct Foo; + +const ANON: usize = 1 + 1; +type Alias = [T; ANON]; +``` + +Note how the `ANON` const has no generic parameters or where clauses, even though `Alias` has both a type parameter `T` and a where clauses `T: Sized`. This desugaring is part of how we enforce that anon consts can't make use of generic parameters. + +While it's useful to think of anon consts as being desugared to real const items, the compiler does not actually implement things this way. + +At AST lowering time we do not yet know the *type* of the anon const, so we can't desugar to a real HIR item with an explicitly written type. To work around this we have [`DefKind::AnonConst`] and [`hir::Node::AnonConst`] which are used to represent these anonymous const items that can't actually be desugared. + +The types of these anon consts are obtainable from the [`type_of`] query. However, the `type_of` query does not actually contain logic for computing the type (infact it just ICEs when called), instead HIR Ty lowering is responsible for *feeding* the value of the `type_of` query for any anon consts that get lowered. HIR Ty lowering can determine the type of the anon const by looking at the type of the Const Parameter that the anon const is an argument to. + +TODO: write a chapter on query feeding and link it here + +In some sense the desugarings from the previous examples are to: +```rust +struct Foo; +type Alias = [u8; 1 + 1]; + +// sort-of desugars to psuedo-rust: +struct Foo; + +const ANON = 1 + 1; +type Alias = [u8; ANON]; +``` + +Where when we go through HIR ty lowering for the array type in `Alias`, we will lower the array length too and feed `type_of(ANON) -> usize`. Effectively setting the type of the `ANON` const item during some later part of the compiler rather than when constructing the HIR. + +After all of this desugaring has taken place the final representation in the type system (ie as a `ty::Const`) is a `ConstKind::Unevaluated` with the `DefId` of the `AnonConst`. This is equivalent to how we would representa a usage of an actual const item if we were to represent them without going through an anon const (e.g. when `min_generic_const_args` is enabled). + +This allows the representation for const "aliases" to be the same as the representation of `TyKind::Alias`. Having a proper HIR body also allows for a *lot* of code re-use, e.g. we can reuse HIR typechecking and all of the lowering steps to MIR where we can then reuse const eval. + +### Enforcing lack of Generic Parameters + +There are three ways that we enforce anon consts can't use generic parameters: +1. Name Resolution will not resolve paths to generic parameters when inside of an anon const +2. HIR Ty lowering will error when a `Self` type alias to a type referencing generic parameters is encountered inside of an anon const +3. Anon Consts do not inherit where clauses or generics from their parent definition (ie [`generics_of`] does not contain a parent for anon consts) + +```rust +// *1* Errors in name resolution +type Alias = [u8; N + 1]; +//~^ ERROR: generic parameters may not be used in const operations + +// *2* Errors in HIR Ty lowering: +struct Foo(T); +impl Foo { + fn assoc() -> [u8; { let a: Self; 0 }] {} + //~^ ERROR: generic `Self` types are currently not permitted in anonymous constants +} + +// *3* Errors due to lack of where clauses on the desugared anon const +trait Trait { + const ASSOC: usize; +} +fn foo() -> [u8; <()>::ASSOC] +//~^ ERROR: no associated item named `ASSOC` found for unit type `()` +where + (): Trait {} +``` + +The second point is particularly subtle as it is very easy to get HIR Ty lowering wrong and not properly enforce that anon consts can't use generic parameters. The existing check is too conservative and accidentally permits some generic parameters to wind up in the body of the anon const [#144547](https://github.com/rust-lang/rust/issues/144547). + +Erroneously allowing generic parameters in anon consts can sometimes lead to ICEs but can also lead to accepting illformed programs. + +The third point is also somewhat subtle, by not inheriting any of the where clauses of the parent item we can't wind up with the trait solving inferring inference variables to generic parameters based off where clauses in scope that mention generic parameters. For example inferring `?x=T` from the expression `<() as Trait>::ASSOC` and an in scope where clause of `(): Trait`. + +This also makes it much more likely that the compiler will ICE or atleast incidentally emit some kind of error if we *do* accidentally allow generic parameters in an anon const, as the anon const will have none of the necessary information in its environment to properly handle the generic parameters. + +```rust +fn foo() { + let a = [1_u8; size_of::<*mut T>()]; +} +``` + +The one exception to all of the above is repeat counts of array expressions. As a *backwards compatibility hack* we allow the repeat count const argument to use generic parameters. + +However, to avoid most of the problems involved in allowing generic parameters in anon const const arguments we require that the constant be evaluated before monomorphization (e.g. during type checking). In some sense we only allow generic parameters here when they are semantically unused. + +In the previous example the anon const can be evaluated for any type parameter `T` because raw pointers to sized types always have the same size (e.g. `8` on 64bit platforms). + +When detecting that we evaluated an anon const that syntactically contained generic parameters, but did not actually depend on them for evaluation to succeed, we emit the [`const_evaluatable_unchecked` FCW][cec_fcw]. This is intended to become a hard error once we stabilize more ways of using generic parameters in const arguments, for example `min_generic_const_args` or (the now dead) `generic_const_exprs`. + +The implementation for this FCW can be found here: [`const_eval_resolve_for_typeck`] + +### Incompatibilities with `generic_const_parameter_types` + +Supporting const paramters such as `const N: [u8; M]` or `const N: Foo` does not work very nicely with the current anon consts setup. There are two reasons for this: +1. As anon consts cannot use generic parameters, their type *also* can't reference generic parameters. This means it is fundamentally not possible to use an anon const as an argument to a const parameeter whose type still references generic parameters. + + ```rust + #![feature(adt_const_params, generic_const_parameter_types)] + + fn foo() {} + + fn bar() { + // There is no way to specify the const argument to `M` + foo::(); + } + ``` + +2. We currently require knowing the type of anon consts when lowering them during HIR ty lowering. With generic const parameter types it may be the case that the currently known type contains inference variables (ie may not be fully known yet). + + ```rust + #![feature(adt_const_params, generic_const_parameter_types)] + + fn foo() {} + + fn bar() { + // The const argument to `N` must be explicitly specified + // even though it is able to be inferred + foo::<_, { [1_u8; 3] }>(); + } + ``` + +It is currently unclear what the right way to make `generic_const_parameter_types` work nicely with the rest of const generics is. + +`generic_const_exprs` would have allowed for anon consts with types referencing generic parameters, but that design wound up unworkable. + +`min_generic_const_args` will allow for some expressions (for example array construction) to be representable without an anon const and therefore without running into these issues, though whether this is *enough* has yet to be determined. + +## Checking types of Const Arguments + +In order for a const argument to be well formed it must have the same type as the const parameter it is an argument to. For example a const argument of type `bool` for an array length is not well formed, as an array's length parameter has type `usize`. + +```rust +type Alias = [u8; B]; +//~^ ERROR: +``` + +To check this we have [`ClauseKind::ConstArgHasType(ty::Const, Ty)`][const_arg_has_type], where for each Const Parameter defined on an item we also desugar an equivalent `ConstArgHasType` clause into its list of where cluases. This ensures that whenever we check wellformedness of anything by proving all of its clauses, we also check happen to check that all of the Const Arguments have the correct type. + +```rust +fn foo() {} + +// desugars to in psuedo-rust + +fn foo() +where +// ConstArgHasType(N, usize) + N: usize, {} +``` + +Proving `ConstArgHasType` goals is implemented by first computing the type of the const argument, then equating it with the provided type. A rough outline of how the type of a Const Argument may be computed: +- [`ConstKind::Param(N)`][`ConstKind::Param`] can be looked up in the [`ParamEnv`] to find a `ConstArgHasType(N, ty)` clause +- [`ConstKind::Value`] stores the type of the value inside itself so can trivially be accessed +- [`ConstKind::Unevaluated`] can have its type computed by calling the `type_of` query +- See the implementation of proving `ConstArgHasType` goals for more detailed information + +`ConstArgHasType` is *the* soundness critical way that we check Const Arguments have the correct type. However, we do *indirectly* check the types of Const Arguments a different way in some cases. + +```rust +type Alias = [u8; true]; + +// desugars to + +const ANON: usize = true; +type Alias = [u8; ANON]; +``` + +By feeding the type of an anon const with the type of the Const Parameter we guarantee that the `ConstArgHasType` goal involving the anon const will succeed. In cases where the type of the anon const doesn't match the type of the Const Parameter what actually happens is a *type checking* error when type checking the anon const's body. + +Looking at the above example, this corresponds to `[u8; ANON]` being a well formed type because `ANON` has type `usize`, but the *body* of `ANON` being illformed and resulting in a type checking error because `true` can't be returned from a const item of type `usize`. + +[ambig-unambig-ty-and-consts]: ./ambig-unambig-ty-and-consts.md +[`ConstKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html +[`ConstKind::Infer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Infer +[`ConstKind::Param`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Param +[`ConstKind::Unevaluated`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Unevaluated +[`ConstKind::Value`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Value +[const_arg_has_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ClauseKind.html#variant.ConstArgHasType +[`ParamEnv`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html +[`generics_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#impl-TyCtxt%3C'tcx%3E/method.generics_of +[`type_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.type_of +[`DefKind::AnonConst`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def/enum.DefKind.html#variant.AnonConst +[`hir::Node::AnonConst`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.Node.html#variant.AnonConst# +[cec_fcw]: https://github.com/rust-lang/rust/issues/76200 +[`const_eval_resolve_for_typeck`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.const_eval_resolve_for_typeck diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md index cce1d1850694..92ed0825c8c3 100644 --- a/src/doc/rustc-dev-guide/src/conventions.md +++ b/src/doc/rustc-dev-guide/src/conventions.md @@ -21,6 +21,16 @@ Instead, formatting should be done using `./x fmt`. It's a good habit to run Formatting is checked by the `tidy` script. It runs automatically when you do `./x test` and can be run in isolation with `./x fmt --check`. +> **Note: Formatting and test suites** +> +> Most Rust source files under `tests/` directory are not formatted for reasons +> such as whitespace sensitivity, nature of snapshot tests, location-sensitive +> comments and more. +> +> Consult the `ignore` entries in +> for which test +> files are not formatted. + If you want to use format-on-save in your editor, the pinned version of `rustfmt` is built under `build//stage0/bin/rustfmt`. diff --git a/src/doc/rustc-dev-guide/src/debuginfo/CodeView.pdf b/src/doc/rustc-dev-guide/src/debuginfo/CodeView.pdf new file mode 100644 index 000000000000..f899d2517845 Binary files /dev/null and b/src/doc/rustc-dev-guide/src/debuginfo/CodeView.pdf differ diff --git a/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md b/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md new file mode 100644 index 000000000000..114ce8a998c5 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/debugger-internals.md @@ -0,0 +1,14 @@ +# Debugger Internals + +It is the debugger's job to convert the debug info into an in-memory representation. Both the +interpretation of the debug info and the in-memory representation are arbitrary; anything will do +so long as meaningful information can be reconstructed while the program is running. The pipeline +from raw debug info to usable types can be quite complicated. + +Once the information is in a workable format, the debugger front-end then must provide a way to +interpret and display the data, a way for users to interact with it, and an API for extensibility. + +Debuggers are vast systems and cannot be covered completely here. This section will provide a brief +overview of the subsystems directly relevant to the Rust debugging experience. + +Microsoft's debugging engine is closed source, so it will not be covered here. \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md new file mode 100644 index 000000000000..831acbd2f8f8 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/debugger-visualizers.md @@ -0,0 +1,111 @@ +# Debugger Visualizers + +These are typically the last step before the debugger displays the information, but the results may +be piped through a debug adapter such as an IDE's debugger API. + +The term "Visualizer" is a bit of a misnomer. The real goal isn't just to prettify the output, but +to provide an interface for the user to interact with that is as useful as possible. In many cases +this means reconstructing the original type as closely as possible to its Rust representation, but +not always. + +The visualizer interface allows generating "synthetic children" - fields that don't exist in the +debug info, but can be derived from invariants about the language and the type itself. A simple +example is allowing one to interact with the elements of a `Vec` instead of just it's `*mut u8` +heap pointer, length, and capacity. + +## `rust-lldb`, `rust-gdb`, and `rust-windbg.cmd` + +These support scripts are distributed with Rust toolchains. They locate the appropriate debugger and +the toolchain's visualizer scripts, then launch the debugger with the appropriate arguments to load +the visualizer scripts before a debugee is launched/attached to. + +## `#![debugger_visualizer]` + +[This attribute][dbg_vis_attr] allows Rust library authors to include pretty printers for their +types within the library itself. These pretty printers are of the same format as typical +visualizers, but are embedded directly into the compiled binary. These scripts are loaded +automatically by the debugger, allowing a seamless experience for users. This attribute currently +works for GDB and natvis scripts. + +[dbg_vis_attr]: https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute + +GDB python scripts are embedded in the `.debug_gdb_scripts` section of the binary. More information +can be found [here](https://sourceware.org/gdb/current/onlinedocs/gdb.html/dotdebug_005fgdb_005fscripts-section.html). Rustc accomplishes this in [`rustc_codegen_llvm/src/debuginfo/gdb.rs`][gdb_rs] + +[gdb_rs]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs + +Natvis files can be embedded in the PDB debug info using the [`/NATVIS` linker option][linker_opt], +and have the [highest priority][priority] when a type is resolving which visualizer to use. The +files specified by the attribute are collected into +[`CrateInfo::natvis_debugger_visualizers`][natvis] which are then added as linker arguments in +[`rustc_codegen_ssa/src/back/linker.rs`][linker_rs] + +[linker_opt]: https://learn.microsoft.com/en-us/cpp/build/reference/natvis-add-natvis-to-pdb?view=msvc-170 +[priority]: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=visualstudio#BKMK_natvis_location +[natvis]: https://github.com/rust-lang/rust/blob/e0e204f3e97ad5f79524b9c259dc38df606ed82c/compiler/rustc_codegen_ssa/src/lib.rs#L212 +[linker_rs]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_ssa/src/back/linker.rs#L1106 + +LLDB is not currently supported, but there are a few methods that could potentially allow support in +the future. Officially, the intended method is via a [formatter bytecode][bytecode]. This was +created to offer a comparable experience to GDB's, but without the safety concerns associated with +embedding an entire python script. The opcodes are limited, but it works with `SBValue` and `SBType` +in roughly the same way as python visualizer scripts. Implementing this would require writing some +sort of DSL/mini compiler. + +[bytecode]: https://lldb.llvm.org/resources/formatterbytecode.html + +Alternatively, it might be possible to copy GDB's strategy entirely: create a bespoke section in the +binary and embed a python script in it. LLDB will not load it automatically, but the python API does +allow one to access the [raw sections of the debug info][SBSection]. With this, it may be possible +to extract the python script from our bespoke section and then load it in during the startup of +Rust's visualizer scripts. + +[SBSection]: https://lldb.llvm.org/python_api/lldb.SBSection.html#sbsection + +## Performance + +Before tackling the visualizers themselves, it's important to note that these are part of a +performance-sensitive system. Please excuse the break in formality, but: if I have to spend +significant time debugging, I'm annoyed. If I have to *wait on my debugger*, I'm pissed. + +Every millisecond spent in these visualizers is a millisecond longer for the user to see output. +This can be especially painful for large stackframes that contain many/large container types. +Debugger GUI's such as VSCode will request the whole stack frame at once, and this can result in +delays of tens of seconds (or even minutes) before being able to interact with any variables in the +frame. + +There is a tendancy to balk at the idea of optimizing Python code, but it really can have a +substantial impact. Remember, there is no compiler to help keep the code fast. Even simple +transformations are not done for you. It can be difficult to find Python performance tips through +all the noise of people suggesting you don't bother optimizing Python, so here are some things to +keep in mind that are relevant to these scripts: + +* Everything allocates, even `int` +* Use tuples when possible. `list` is effectively `Vec>`, whereas tuples are equivalent +to `Box<[Any]>`. They have one less layer of indirection, don't carry extra capacity and can't +grow/shrink which can be advantageous in many cases. An additional benefit is that Python caches and +recycles the underlying allocations of all tuples up to size 20. +* Regexes are slow and should be avoided when simple string manipulation will do +* Strings are immutable, thus many string operations implictly copy the contents. +* When concatenating large lists of strings, `"".join(iterable_of_strings)` is typically the fastest +way to do it. +* f-strings are generally the fastest way to do small, simple string transformations such as +surrounding a string with parentheses. +* The act of calling a function is somewhat slow (even if the function is completely empty). If the +code section is very hot, consider inlining the function manually. +* Local variable access is significantly faster than global and built-in function access +* Member/method access via the `.` operator is also slow, consider reassigning deeply nested values +to local variables to avoid this cost (e.g. `h = a.b.c.d.e.f.g.h`). +* Accessing inherited methods and fields is about 2x slower than base-class methods and fields. +Avoid inheritance whenever possible. +* Use [`__slots__`](https://wiki.python.org/moin/UsingSlots) wherever possible. `__slots__` is a way +to indicate to Python that your class's fields won't change and speeds up field access by a +noticable amount. This does require you to name your fields in advance and initialize them in +`__init__`, but it's a small price to pay for the benefits. +* Match statements/if..elif..else are not optimized in any way. The conditions are checked in order, +1 by 1. If possible, use an alternative such as dictionary dispatch or a table of values +* Compute lazily when possible +* List comprehensions are typically faster than loops, generator comprehensions are a bit slower +than list comprehensions, but use less memory. You can think of comprehensions as equivalent to +Rust's `iter.map()`. List comprehensions effectively call `collect::>` at the end, whereas +generator comprehensions do not. \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/gdb-internals.md b/src/doc/rustc-dev-guide/src/debuginfo/gdb-internals.md new file mode 100644 index 000000000000..95f543c8e94a --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/gdb-internals.md @@ -0,0 +1,4 @@ +# (WIP) GDB Internals + +GDB's Rust support lives at `gdb/rust-lang.h` and `gdb/rust-lang.c`. The expression parsing support +can be found in `gdb/rust-exp.h` and `gdb/rust-parse.c` \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/gdb-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/gdb-visualizers.md new file mode 100644 index 000000000000..4027ef897f28 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/gdb-visualizers.md @@ -0,0 +1,9 @@ +# (WIP) GDB - Python Providers + +Below are links to relevant parts of the GDB documentation + +* [Overview on writing a pretty printer](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Writing-a-Pretty_002dPrinter.html#Writing-a-Pretty_002dPrinter) +* [Pretty Printer API](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API) (equivalent to LLDB's `SyntheticProvider`) +* [Value API](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Values-From-Inferior.html#Values-From-Inferior) (equivalent to LLDB's `SBValue`) +* [Type API](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Types-In-Python.html#Types-In-Python) (equivalent to LLDB's `SBType`) +* [Type Printing API](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Type-Printing-API.html#Type-Printing-API) (equivalent to LLDB's `SyntheticProvider.get_type_name`) diff --git a/src/doc/rustc-dev-guide/src/debuginfo/intro.md b/src/doc/rustc-dev-guide/src/debuginfo/intro.md new file mode 100644 index 000000000000..0f1e59fa65e2 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/intro.md @@ -0,0 +1,114 @@ +# Debug Info + +Debug info is a collection of information generated by the compiler that allows debuggers to +correctly interpret the state of a program while it is running. That includes things like mapping +instruction addresses to lines of code in the source file, and type layout information so that +bytes in memory can be read and displayed in a meaningful way. + +Debug info can be a slightly overloaded term, covering all the layers between Rust MIR, and the +end-user seeing the output of their debugger onscreen. In brief, the stack from beginning to end is +as follows: + +1. Rustc inspects the MIR and communicates the relevant source, symbol, and type information to LLVM +2. LLVM translates this information into a target-specific debug info format during compilation +3. A debugger reads and interprets the debug info, mapping source-lines and allowing the debugee's +variables in memory to be located and read with the correct layout +4. Built-in debugger formatting and styling is applied to variables +5. User-defined scripts are run, formatting and styling the variables further +6. The debugger frontend displays the variable to the user, possibly through the means of additional +API layers (e.g. VSCode extension by way of the +[Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)) + + +> NOTE: This subsection of the dev guide is perhaps more detailed than necessary. It aims to collect +> a large amount of scattered information into one place and equip the reader with as firm a grasp of +> the entire debug stack as possible. +> +> If you are only interested in working on the visualizer +> scripts, the information in the [debugger-visualizers](./debugger-visualizers.md) and +> [testing](./testing.md) will suffice. If you need to make changes to Rust's debug node generation, +> please see [rust-codegen](./rust-codegen.md). All other sections are supplementary, but can be +> vital to understanding some of the compromises the visualizers or codegen need to make. It can +> also be valuable to know when a problem might be better solved in LLVM or the debugger itself. + +# DWARF + +The is the primary debug info format for `*-gnu` targets. It is typically bundled in with the +binary, but it [can be generated as a separate file](https://gcc.gnu.org/wiki/DebugFission). The +DWARF standard is available [here](https://dwarfstd.org/). + +> NOTE: To inspect DWARF debug info, [gimli](https://crates.io/crates/gimli) can be used +> programatically. If you prefer a GUI, the author recommends [DWEX](https://github.com/sevaa/dwex) + +# PDB/CodeView + +The primary debug info format for `*-msvc` targets. PDB is a proprietary container format created by +Microsoft that, unfortunately, +[has multiple meanings](https://docs.rs/ms-pdb/0.1.10/ms_pdb/taster/enum.Flavor.html). +We are concerned with ordinary PDB files, as Portable PDB is used mainly for .Net applications. PDB +files are separate from the compiled binary and use the `.pdb` extension. + +PDB files contain CodeView objects, equivalent to DWARF's tags. CodeView, the debugger that +consumed CodeView objects, was originally released in 1985. Its original intent was for C debugging, +and was later extended to support Visual C++. There are still minor alterations to the format to +support modern architectures and languages, but many of these changes are undocumented and/or +sparsely used. + +It is important to keep this context in mind when working with CodeView objects. Due to its origins, +the "feature-set" of these objects is very limited, and focused around the core features of C. It +does not have many of the convenience or features of modern DWARF standards. A fair number of +workarounds exist within the debug info stack to compensate for CodeView's shortcomings. + +Due to its proprietary nature, it is very difficult to find information about PDB and CodeView. Many +of the sources were made at vastly different times and contain incomplete or somewhat contradictory +information. As such this page will aim to collect as many sources as possible. + +* [CodeView 1.0 specification](./CodeView.pdf) +* LLVM + * [CodeView Overview](https://llvm.org/docs/SourceLevelDebugging.html#codeview-debug-info-format) + * [PDB Overview and technical details](https://llvm.org/docs/PDB/index.html) +* Microsoft + * [microsoft-pdb](https://github.com/microsoft/microsoft-pdb) - A C/C++ implementation of a PDB + reader. The implementation does not contain the full PDB or CodeView specification, but does + contain enough information for other PDB consumers to be written. At time of writing (Nov 2025), + this repo has been archived for several years. + * [pdb-rs](https://github.com/microsoft/pdb-rs/) - A Rust-based PDB reader and writer based on + other publicly-available information. Does not guarantee stability or spec compliance. Also + contains `pdbtool`, which can dump PDB files (`cargo install pdbtool`) + * [Debug Interface Access SDK](https://learn.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/getting-started-debug-interface-access-sdk). + While it does not document the PDB format directly, details can be gleaned from the interface + itself. + +# Debuggers + +Rust supports 3 major debuggers: GDB, LLDB, and CDB. Each has its own set of requirements, +limitations, and quirks. This unfortunately creates a large surface area to account for. + +> NOTE: CDB is a proprietary debugger created by Microsoft. The underlying engine also powers +>WinDbg, KD, the Microsoft C/C++ extension for VSCode, and part of the Visual Studio Debugger. In +>these docs, it will be referred to as CDB for consistency + +While GDB and LLDB do offer facilities to natively support Rust's value layout, this isn't +completely necessary. Rust currently outputs debug info very similar to that of C++, allowing +debuggers without Rust support to work with a slightly degraded experience. More detail will be +included in later sections, but here is a quick reference for the capabilities of each debugger: + +| Debugger | Debug Info Format | Native Rust support | Expression Style | Visualizer Scripts | +| --- | --- | --- | --- | --- | +| GDB | DWARF | Full | Rust | Python | +| LLDB | DWARF and PDB | Partial | C/C++ | Python | +| CDB | PDB | None | C/C++ | Natvis | + +> IMPORTANT: CDB can be assumed to run only on Windows. No assumptions can be made about the OS +>running GDB or LLDB. + +## Unsupported + +Below, are several unsupported debuggers that are of particular note due to their potential impact +in the future. + +* [Bugstalker](https://github.com/godzie44/BugStalker) is an x86-64 Linux debugger written in Rust, +specifically to debug Rust programs. While promising, it is still in early development. +* [RAD Debugger](https://github.com/EpicGamesExt/raddebugger) is a Windows-only GUI debugger. It has +a custom debug info format that PDB is translated into. The project also includes a linker that can +generate their new debug info format during the linking phase. \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/lldb-internals.md b/src/doc/rustc-dev-guide/src/debuginfo/lldb-internals.md new file mode 100644 index 000000000000..e104f1d24532 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/lldb-internals.md @@ -0,0 +1,208 @@ +# LLDB Internals + +LLDB's debug info processing relies on a set of extensible interfaces largely defined in +[lldb/src/Plugins][lldb_plugins]. These are meant to allow third-party compiler developers to add +language support that is loaded at run-time by LLDB, but at time of writing (Nov 2025) the public +API has not been settled on, so plugins exist either in LLDB itself or in standalone forks of LLDB. + +[lldb_plugins]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins + +Typically, language support will be written as a pipeline of these plugins: `*ASTParser` -> +`TypeSystem` -> `ExpressionParser`/`Language`. + +Here are some existing implementations of LLDB's plugin API: + +* [Apple's fork with support for Swift](https://github.com/swiftlang/llvm-project) +* [CodeLLDB's former fork with support for Rust](https://archive.softwareheritage.org/browse/origin/directory/?branch=refs/heads/codelldb/16.x&origin_url=https://github.com/vadimcn/llvm-project&path=lldb/source/Plugins/TypeSystem/Rust×tamp=2023-09-11T04:55:10Z) +* [A work in progress reimplementation of Rust support](https://github.com/Walnut356/llvm-project/tree/lldbrust/19.x) +* [A Rust expression parser plugin](https://github.com/tromey/lldb/tree/a0fc10ce0dacb3038b7302fff9f6cb8cb34b37c6/source/Plugins/ExpressionParser/Rust). +This was written before the `TypeSystem` API was created. Due to the freeform nature of expression parsing, the +underlyng lexing, parsing, function calling, etc. should still offer valuable insights. + +## Rust Support and TypeSystemClang + +As mentioned in the debug info overview, LLDB has partial Rust support. To further clarify, Rust +uses the plugin-pipeline that was built for C/C++ (though it contains some helpers for Rust enum +types), which relies directly on the `clang` compiler's representation of types. This imposes heavy +restrictions on how much we can change when LLDB's output doesn't match what we want. Some +workarounds can help, but at the end of the day Rust's needs are secondary compared to making sure +C and C++ compilation and debugging work correctly. + +LLDB is receptive to adding a `TypeSystemRust`, but it is a massive undertaking. This section serves +to not only document how we currently interact with [`TypeSystemClang`][ts_clang], but also as light +guidance on implementing a `TypeSystemRust` in the future. + +[ts_clang]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/TypeSystem/Clang + +It is worth noting that a `TypeSystem` directly interacting with the target language's compiler is +the intention, but it is not a requirement. One can create all the necessary supporting types within +their plugin implementation. + +> Note: LLDB's documentation, including comments in the source code, is pretty sparse. Trying to +> understand how language support works by reading `TypeSystemClang`'s implementation is somewhat +> difficult due to the added requirement of understanding the `clang` compiler's internals. It is +> recommended to look at the 2 `TypeSystemRust` implementations listed above, as they are written +> "from scratch" without leveraging a compiler's type representation. They are relatively close to +> the minimum necessary to implement language support. + +## DWARF vs PDB + +LLDB is unique in being able to handle both DWARF and PDB debug information. This does come with +some added complexity. To complicate matters further, PDB support is split between `dia`, which +relies on the `msdia140.dll` library distributed with Visual Studio, and `native`, which is written +from scratch using publicly available information about the PDB format. + +> Note: `dia` was the default up to LLDB version 21. `native` is the new default as of +> LLDB 22's release. There are plans to deprecate and completely remove the `dia`-based plugins. As +> such, only `native` parsing will be discussed below. For progress, please see +> [this discourse thread][dia_discourse] and the relevant [tracking issue][dia_tracking]. +> +> `native` can be toggled via the `plugin.symbol-file.pdb.reader` setting added in LLDB 22 or using +> the environment variable `LLDB_USE_NATIVE_PDB_READER=0/1` + +[dia_discourse]: https://discourse.llvm.org/t/rfc-removing-the-dia-pdb-plugin-from-lldb/87827 +[dia_tracking]: https://github.com/llvm/llvm-project/issues/114906 + +## Debug Node Parsing + +The first step is to process the raw debug nodes into something usable. This primarily occurs in +the [`DWARFASTParser`][dwarf_ast] and [`PdbAstBuilder`][pdb_ast] classes. These classes are fed a +deserialized form of the debug info generated from [`SymbolFileDWARF`][sf_dwarf] and +[`SymbolFileNativePDB`][sf_pdb] respectively. The `SymbolFile` implementers make almost no +transformations to the underlying debug info before passing it to the parsers. For both PDB and +DWARF, the debug info is read using LLVM's debug info handlers. + +[dwarf_ast]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/SymbolFile/DWARF +[pdb_ast]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/SymbolFile/NativePDB +[sf_dwarf]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +[sf_pdb]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h + +The parsers translate the nodes into more convenient formats for LLDB's purposes. For `clang`, these +formats are `clang::QualType`, `clang::Decl`, and `clang::DeclContext`, which are the types `clang` +uses internally when compiling C and C++. Again, using the compiler's representation of types is not a +requirement, but the plugin system was built with it as a possibility. + +> Note: The above types will be referred to language-agnostically as `LangType`, `Decl`, and +`DeclContext` when the specific implementation details of `TypeSystemClang` are not relevant. + +`LangType` represents a type. This includes information such as the name of the type, the size and +alignment, its classification (e.g. struct, primitive, pointer), its qualifiers (e.g. +`const`, `volatile`), template arguments, function argument and return types, etc. [Here][rust_type] +is an example of what a `RustType` might look like. + +[rust_type]: https://github.com/Walnut356/llvm-project/blob/13bcfd502452606d69faeea76aec3a06db554af9/lldb/source/Plugins/TypeSystem/Rust/TypeSystemRust.h#L618 + +`Decl` represents any kind of declaration. It could be a type, a variable, a static field of a +struct, the value that a static or const is initialized with, etc. + +`DeclContext` more or less represents a scope. `DeclContext`s typically contain `Decl`s and other +`DeclContexts`, though the relationship isn't that straight forward. For example, a function can be +both a `Decl` (because function signatures are types), **and** a `DeclContext` (because functions +contain variable declarations, nested functions declarations, etc.). + +The translation process can be quite verbose, but is usually straightforward. Much of the work here +is dependant on the exact information needed to fill out `LangType`, `Decl`, and `DeclContext`. + +Once a node is translated, a pointer to it is type-erased (`void*`) and wrapped in `CompilerType`, +`CompilerDecl`, or `CompilerDeclContext`. These wrappers associate the them with the `TypeSystem` +that owns them. Methods on these objects delegates to the `TypeSystem`, which casts the `void*` back +to the appropriate `LangType*`/`Decl*`/`DeclContext*` and operates on the internals. In Rust terms, +the relationship looks something like this: + +```Rust +struct CompilerType { + inner_type: *mut c_void, + type_system: Arc, +} + +impl CompilerType { + pub fn get_byte_size(&self) -> usize { + self.type_system.get_byte_size(self.lang_type) + } + +} + +... + +impl TypeSystem for TypeSystemLang { + pub fn get_byte_size(lang_type: *mut c_void) -> usize { + let lang_type = lang_type as *mut LangType; + + // Operate on the internals of the LangType to + // determine its size + ... + } +} +``` + +## Type Systems + +The [`TypeSystem` interface][ts_interface] has 3 major purposes: + +[ts_interface]: https://github.com/llvm/llvm-project/blob/main/lldb/include/lldb/Symbol/TypeSystem.h#L69 + +1. Act as the "sole authority" of a language's types. This allows the type system to be added to +LLDB's "pool" of type systems. When an executable is loaded, the target language is determined, and +the pool is queried to find a `TypeSystem` that claims it can handle the language. One can also use +the `TypeSystem` to retrieve the backing `SymbolFile`, search for types, and synthesize basic types +that might not exist in the debug info (e.g. primitives, arrays-of-`T`, pointers-to-`T`). +2. Manage the lifetimes of the `LangType`, `Decl`, and `DeclContext` objects +3. Customize the "defaults" of how those types appear and how they can be interacted with. + +The first two functions are pretty straightforward so we will focus on the third. + +Many of the functions in the `TypeSystem` interface will look familiar if you have worked with the +visualizer scripts. These functions underpin `SBType` the `SBValue` functions with matching names. +For example, `TypeSystem::GetFormat` returns the default format for the type if no custom formatter +has been applied to it. + +Of particular note are `GetIndexOfChildWithName` and `GetNumChildren`. The `TypeSystem` versions of +these functions operate on a *type*, not a value like the `SBValue` versions. The values returned +from the `TypeSystem` functions dictate what parts of the struct can be interacted with *at all* by +the rest of LLDB. If a field is ommitted, that field effectively no longer exists to LLDB. + +Additionally, since they do not work with objects, there is no underlying memory to inspect or +interpret. Essentially, this means these functions do not have the same purpose as their equivalent +`SyntheticProvider` functions. There is no way to determine how many elements a `Vec` has or what +address those elements live at. It is also not possible to determine the value of the discriminant +of a sum-type. + +Ideally, the `TypeSystem` should expose types as they appear in the debug info with as few +alterations as possible. LLDB's synthetics and frontend can handle making the type pretty. If some +piece of information is useless, the Rust compiler should be altered to not output that debug info +in the first place. + +## Expression Parsing + +The `TypeSystem` is typically written to have a counterpart that can handle expression parsing. It +requires implementing a few extra functions in the `TypeSystem` interface. The bulk of the +expression parsing code should live in [lldb/source/Plugins/ExpressionParser][expr]. + +[expr]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/ExpressionParser + +There isn't too much of note about the parser. It requires implementing a simple interpreter that +can handle (possibly simplified) Rust syntax. They operate on `lldb::ValueObject`s, which are the +objects that underpin `SBValue`. + +## Language + +The [`Language` plugins][lang_plugin] are the C++ equivalent to the Python visualizer scripts. They +operate on `SBValue` objects for the same purpose: creating synthetic children and pretty-printing. +The [CPlusPlusLanguage's implementations][cpp_lang] for the LibCxx types are great resources to +learn how visualizers should be written. + +[lang_plugin]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/Language +[cpp_lang]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/Language/CPlusPlus + +These plugins can access LLDB's private internals (including the underlying `TypeSystem`), so +synthetic/summary providers written as a `Language` plugin can provide higher quality output than +their python equivalent. + +While debug node parsing, type systems, and expression parsing are all closely tied to eachother, +the `Language` plugin is encapsulated more and thus can be written "standalone" for any language +that an existing type system supports. Due to the lower barrier of entry, a `RustLanguage` plugin +may be a good stepping stone towards full language support in LLDB. + +## Visualizers + +WIP \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md new file mode 100644 index 000000000000..bb27c0e97369 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/lldb-visualizers.md @@ -0,0 +1,662 @@ +# LLDB - Python Providers + +> NOTE: LLDB's C++<->Python FFI expects a version of python designated at the time LLDB was +>compiled. LLDB is careful to correspond this version to the minimum in typical Linux and macOS +>distributions, but on Windows there is no easy solution. If you recieve an import error regarding +>`_lldb` not existing, a mismatched Python version is likely the cause. +> +> LLDB is considering solutions this issue. For updates, see +>[this discussion][minimal_python_install] and [this github issue][issue_167001] + +[minimal_python_install]: https://discourse.llvm.org/t/a-minimal-python-install-for-lldb/88658 +[issue_167001]: https://github.com/llvm/llvm-project/issues/167001 + +> NOTE: Currently (Nov 2025), LLDB's minimum supported Python version is 3.8 with plans to update it to +>3.9 or 3.10 depending on several outside factors. Scripts should ideally be written with only the +>features available in the minimum supported Python version. Please see [this discussion][mrpv] for +>more info. + +[mrpv]: https://discourse.llvm.org/t/rfc-upgrading-llvm-s-minimum-required-python-version/88605/ + +> NOTE: The path to LLDB's python package can be located via the CLI command `lldb -P` + +LLDB provides 3 mechanisms for customizing output: + +* Formats +* Synthetic providers +* Summary providers + +## Formats + +The official documentation is [here](https://lldb.llvm.org/use/variable.html#type-format). In short, +formats allow one to set the default print format for primitive types (e.g. print `25u8` as decimal +`25`, hex `0x19`, or binary `00011001`). + +Rust will almost always need to override `unsigned char`, `signed char`, `char`, `u8`, and `i8`, to +(unsigned) decimal format. + +## Synthetic Providers + +The official documentation is [here](https://lldb.llvm.org/use/variable.html#synthetic-children), +but some information is vague, outdated, or entirely missing. + +Nearly all interaction the user has with variables will be through LLDB's +[`SBValue` objects][sbvalue] which are used both in the Python API, and internally via LLDB's +plugins and CLI. + +[sbvalue]: https://lldb.llvm.org/python_api/lldb.SBValue.html + +A Synthetic Provider is a Python class, written with a specific interface, that is associated with +one or more Rust types. The Synthetic Provider wraps `SBValue` objects and LLDB will call our +class's functions when inspecting the variable. + +The wrapped value is still an `SBValue`, but when calling e.g. `SBValue.GetChildAtIndex`, it will +internally call `SyntheticProvider.get_child_at_index`. You can check if a value has a synthetic +provider via `SBValue.IsSynthetic()`, and which synthetic it is via `SBValue.GetTypeSynthetic()`. If +you want to interact with the underlying non-synthetic value, you can call +`SBValue.GetNonSyntheticValue()`. + + +The expected interface is as follows: + +```python +class SyntheticProvider: + def __init__(self, valobj: SBValue, _lldb_internal): ... + + # optional + def update(self) -> bool: ... + + # optional + def has_children(self) -> bool: ... + + # optional + def num_children(self, max_children: int) -> int: ... + + def get_child_index(self, name: str) -> int: ... + + def get_child_at_index(self, index: int) -> SBValue: ... + + # optional + def get_type_name(self) -> str: ... + + # optional + def get_value(self) -> SBValue: ... +``` + +Below are explanations of the methods, their quirks, and how they should generally be used. If a +method overrides an `SBValue` method, that method will be listed. + +### `__init__` + +This function is called once per object, and must store the `valobj` in the python class so that it +is accessible elsewhere. Very little else should be done here. + +### (optional) `update` + +This function is called prior to LLDB interacting with a variable, but after `__init__`. LLDB tracks +whether `update` has already been called. If it has been, and if it is not possible for the variable +to have changed (e.g. inspecting the same variable a second time without stepping), it will omit the +call to `update`. + +This function has 2 purposes: + +* Store/update any information that may have changed since the last time `update` was run +* Inform LLDB if there were changes to the children such that it should flush the child cache. + +Typical operations include storing the heap pointer, length, capacity, and element type of a `Vec`, +determining an enum variable's variant, or checking which slots of a `HashMap` are occupied. + +The bool returned from this function is somewhat complicated, see: +[`update` caching](#update-caching) below for more info. When in doubt, return `False`/`None`. +Currently (Nov 2025), none of the visualizers return `True`, but that may change as the debug info +test suite is improved. + +#### `update` caching + +LLDB attempts to cache values when possible, including child values. This cache is effectively the +number of child objects, and the addresses of the underlying debugee memory that the child object +represents. By returning `True`, you indicate to LLDB that the number of children and the addresses +of those children have not changed since the last time `update` was run, meaning it can reuse the +cached children. + +**Returning `True` in the wrong circumstances will result in the debugger outputting incorrect +information**. + +Returning `False` indicates that there have been changes, the cache will be flushed, and the +children will be fetched from scratch. It is the safer option if you are unsure. + +The only relationship that matters is parent-to-child. Grandchildren depend on the `update` function +of their direct parent, not that of the grandparent. + +It is important to view the child cache as pointers-to-memory. For example, if a slice's `data_ptr` +value and `length` have not changed, returning `True` is appropriate. Even if the slice is mutable +and elements of it are overwritten (e.g. `slice[0] = 15`), because the child cache consists of +*pointers*, they will reflect the new data at that memory location. + +Conversely, if `data_ptr` has changed, that means it is pointing to a new location in memory, the +child pointers are invalid, and the cache must be flushed. If the `length` has changed, we need to +flush the cache to reflect the new number of children. If `length` has changed but `data_ptr` has +not, it is possible to store the old children in the `SyntheticProvider` itself (e.g. +`list[SBValue]`) and dole those out rather than generating them from scratch, only creating new +children if they do not already exist in the `SyntheticProvider`'s list. + +For further clarification, see [this discussion](https://discourse.llvm.org/t/when-is-it-safe-to-cache-syntheticprovider-update/88608) + +> NOTE: when testing the caching behavior, do not rely on LLDB's heuristic to persist variables when +> stepping. Instead, store the variable in a python object (e.g. `v = lldb.frame.var("var_name")`), +> step forward, and then inspect the stored variable. + +### (optional) `has_children` + +> Overrides `SBValue.MightHaveChildren` + +This is a shortcut used by LLDB to check if the value has children *at all*, without doing +potentially expensive computations to determine how many children there are (e.g. linked list). +Often, this will be a one-liner of `return True`/`return False` or +`return self.valobj.MightHaveChildren()`. + +### (optional) `num_children` + +> Overrides `SBValue.GetNumChildren` + +Returns the total number of children that LLDB should try to access when printing the type. This +number **does not** need to match to total number of synthetic children. + +The `max_children` argument can be returned if calculating the number of children can be expensive +(e.g. linked list). If this is not a consideration, `max_children` can be omitted from the function +signature. + +Additionally, fields can be intentionally "hidden" from LLDB while still being accessible to the +user. For example, one might want a `vec![1, 2, 3]` to display only its elements, but still have the +`len` and `capacity` values accessible on request. By returning `3` from `num_children`, one can +restrict LLDB to only displaying `[1, 2, 3]`, while users can still directly access `v.len` and +`v.capacity`. See: [Example Provider: Vec\](#example-provider-vect) to see an implementation of +this. + +### `get_child_index` + +> Overrides `SBValue.GetIndexOfChildWithName` +> +> Affects `SBValue.GetChildMemberWithName` + +Given a name, returns the index that the child should be accessed at. It is expected that the return +value of this function is passed directly to `get_child_at_index`. As with `num_children`, the +values returned here *can* be arbitrary, so long as they are properly coordinated with +`get_child_at_index`. + +One special value is `$$dereference$$`. Accounting for this pseudo-field will allow LLDB to use the +`SBValue` returned from `get_child_at_index` as the result of a dereference via LLDB's expression +parser (e.g. `*val` and `val->field`) + +### `get_child_at_index` + +> Overrides `SBValue.GetChildAtIndex` + +Given an index, returns a child `SBValue`. Often these are generated via +`SBValue.CreateValueFromAddress`, but less commonly `SBValue.CreateChildAtOffset`, +`SBValue.CreateValueFromExpression`, and `SBValue.CreateValueFromData`. These functions can be a +little finicky, so you may need to fiddle with them to get the output you want. + +In some cases, `SBValue.Clone` is appropriate. It creates a new child that is an exact copy of an +existing child, but with a new name. This is useful for cases like tuples, which have field names of +the style `__0`, `__1`, ... when we would prefer they were named `0`, `1`, ... + +Small alterations can be made to the resulting child before it is returned. This is useful for +`&str`/`String`, where we would prefer if the children were displayed as +`lldb.eFormatBytesWithASCII` rather than just as a decimal value. + +### (optional) `get_type_name` + +> Overrides `SBValue.GetDisplayTypeName` + +Overrides the displayed name of a type. For a synthetic `SBValue` whose type name is overridden, the +original type name can still be retrieved via `SBValue.GetTypeName()` and +`SBValue.GetType().GetName()` + +This can be helpful in shortening the name of common standard library types (e.g. +`std::collections::hash::map::HashMap` -> `HashMap`), or +in normalizing MSVC type names (e.g. `ref$` -> `&str`). + +The string manipulation can be a little tricky, especially on MSVC where we cannot conveniently +access the generic parameters of the type. + +### (optional) `get_value` + +> Overrides `SBValue.GetValue()`, `SBValue.GetValueAsUnsigned()`, `SBValue.GetValueAsSigned()`, +>`SBValue.GetValueAsAddress()`, + +The `SBValue` returned is expected to be a primitive type or pointer, and is treated as the value of +the variable in expressions. + +> IMPORTANT: The `SBValue` returned **must be stored in the `SyntheticProvider`**. There is +>currently (Nov 2025) a bug where if the `SBValue` is acquired within `get_value` and not stored +>anywhere, Python will segfault when LLDB attempts to access the value. + +## Summary Providers + +Summary providers are python functions of the following form: + +```python +def SummaryProvider(valobj: SBValue, _lldb_internal) -> str: ... +``` + +Where the returned string is passed verbatim to the user. If the returned value isn't a string, it +is naively convered to a string (e.g. `return None` prints `"None"`, not an empty string). + +If the `SBValue` passed in is of a type that has a Synthetic Provider, `valobj.IsSynthetic()` will +return `True`, and the synthetic's corresponding functions will be used. If this is undesirable, the +original value can be retrieved via `valobj.GetNonSyntheticValue()`. This can be helpful in cases +like `String`, where individually calling `GetChildAtIndex` in a loop is much slower than accessing +the heap pointer, reading the whole byte array directly from the debugee's memory, and using +Python's `bytes.decode()`. + +### Instance Summaries + +Regular `SummaryProvider` functions take an opaque `SBValue`. That `SBValue` will reflect the type's +`SyntheticProvider` if one exists, but we cannot access the `SyntheticProvider` instance itself, or +any of its internal implementation details. This is deterimental in cases where we need some of +those internal details to help complete the summary. Currently (Nov 2025), in the synthetic we just +run the non-synthetic value through the synthetic provider +(`synth = SyntheticProvider(valobj.GetNonSyntheticValue(), _dict)`), but this is obviously +suboptimal and there are plans to use the method outlined below. + +Instead, we can leverage the Python module's state to allow for instance summaries. Prior art for +this technique exists in the [old CodeLLDB Rust visualizer scripts](https://github.com/vadimcn/codelldb/blob/cf9574977b80e29c6de2c44d12f1071a53a54caf/formatters/rust.py#L110). + +In short: every Synthetic Provider's `__init__` function stores a unique ID and a weak reference to +`self` in a global dictionary. The Synthetic Provider class also implements a `get_summary` +function. The type's `SummaryProvider` is a function that looks up the unique ID in this dictionary, +then calls a `get_summary` on the instance it retrieves. + +```python +import weakref + +SYNTH_BY_ID = weakref.WeakValueDictionary() + +class SyntheticProvider: + valobj: SBValue + + # slots requires opting-in to __weakref__ + __slots__ = ("valobj", "__weakref__") + + def __init__(valobj: SBValue, _dict): + SYNTH_BY_ID[valobj.GetID()] = self + self.valobj = valobj + + def get_summary(self) -> str: + ... + +def InstanceSummaryProvider(valobj: SBValue, _dict) -> str: + # GetNonSyntheticValue should never fail as InstanceSummaryProvider implies an instance of a + # `SyntheticProvider`. No non-synthetic types should ever have this summary assigned to them + # We use GetNonSyntheticValue because the synthetic vaobj has its own unique ID + return SYNTH_BY_ID[valobj.GetNonSyntheticValue().GetID()].get_summary() +``` + +For example, one might use this for the Enum synthetic provider. The summary would like to access +the variant name, but there isn't a convenient way to reflect this via the type name or child-values +of the synthetic. By implementing an instance summary, we can retrieve the variant name via +`self.variant.GetTypeName()` and some string manipulation. + +# Writing Visualizer Scripts + +> IMPORTANT: Unlike GDB and CDB, LLDB can debug executables with either DWARF or PDB debug info. +>Visualizers must be written to account for both formats whenever possible. See: +>[rust-codegen](./rust-codegen.md#dwarf-vs-pdb) for an overview of the differences + +Scripts are injected into LLDB via the CLI command `command script import .py`. Once +injected, classes and functions can be added to the synthetic/summary pool with `type synthetic add` +and `type summary add` respectively. The summaries and synthetics can be associated with a +"category", which is typically named after the language the providers are intended for. The category +we use will be called `Rust`. + +> TIP: all LLDB commands can be prefixed with `help` (e.g. `help type synthetic add`) for a brief +description, list of arguments, and examples. + +Currently (Nov 2025) we use `command source ...`, which executes a series of CLI commands from the +file [`lldb_commands`](https://github.com/rust-lang/rust/blob/main/src/etc/lldb_commands) to add +providers. This file is somewhat unwieldy, and will soon be supplanted by the Python API equivalent +outlined below. + +## `__lldb_init_module` + +This is an optional function of the form: + +```python +def __lldb_init_module(debugger: SBDebugger, _lldb_internal) -> None: ... +``` + +This function is called at the end of `command script import ...`, but before control returns back +to the CLI. It allows the script to initialize its own state. + +Crucially, it is passed a reference to the debugger itself. This allows us to create the `Rust` +category and add providers to it. It can also allow us to conditionally change which providers we +use depending on what version of LLDB the script detects. This is vital for backwards compatibility +once we begin using recognizer functions, as recognizers were added in lldb 19.0. + +## Visualizer Resolution + +The order that visualizers resolve in is listed [here][formatters_101]. In short: + +[formatters_101]: https://lldb.llvm.org/use/variable.html#finding-formatters-101 + +* If there is an exact match (non-regex name, recognizer function, or type already matched to +provider), use that +* If the object is a pointer/reference, try to use the dereferenced type's formatter +* If the object is a typedef, check the underlying type for a formatter +* If none of the above work, iterate through the regex type matchers + +Within each of those steps, **iteration is done backwards** to allow new commands to "override" old +commands. This is important for cases like `Box` vs `Box`, were we want a specialized +synthetic for the former, but a more generalized synthetic for the latter. + +## Minutiae + +LLDB's API is very powerful, but there are some "gotchas" and unintuitive behavior, some of which +will be outlined below. The python implementation can be viewed at the path returned by the CLI +command `lldb -P` in `lldb\__init__.py`. In addition to the +[examples in the lldb repo][synth_examples], there are also [C++ visualizers][plugin_cpp] that can +be used as a reference (e.g. [LibCxxVector, the equivalent to `Vec`][cxx_vector]). While C++'s +visualizers are written in C++ and have access to LLDB's internals, the API and general practices +are very similar. + +[synth_examples]:https://github.com/llvm/llvm-project/tree/main/lldb/examples/synthetic +[plugin_cpp]: https://github.com/llvm/llvm-project/tree/main/lldb/source/Plugins/Language/CPlusPlus +[cxx_vector]: https://github.com/llvm/llvm-project/blob/main/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp + +### `SBValue` + +* Pointer/reference `SBValue`s will effectively "auto-deref" in some cases, acting as if the +children of the pointed-to-object are its own children. +* The non-function fields are typically [`property()`][property] fields that point directly to the +function anyway (e.g. `SBValue.type = property(GetType, None)`). Accessing through these shorthands +is a bit slower to access than just calling the function directly, so they should be avoided. Some +of the properties return special objects with special properties (e.g. `SBValue.member` returns an +object that acts like `dict[str, SBValue]` to access children). Internally, many of these special +objects just allocate a new class instance and call the function on the `SBValue` anyway, resulting +in additional performance loss (e.g. `SBValue.member` internally just implements `__getitem__` which +is the one-liner `return self.valobj.GetChildMemberWithName(name)`) +* `SBValue.GetID` returns a unique `int` for each value for the duration of the debug session. +Synthetic `SBValue`'s have a different ID than their underlying `SBValue`. The underlying ID can be +retrieved via `SBValue.GetNonSyntheticValue().GetID()`. +* When manually calculating an address, `SBValue.GetValueAsAddress` should be preferred over +`SBValue.GetValueAsUnsigned` due to [target-specific behavior][get_address] +* Getting a string representation of an `SBValue` can be tricky because `GetSummary` requires a +summary provider and `GetValue` requires the type be representable by a primitive. In almost all +cases where neither of those conditions are met, the type is a user defined struct that can be +passed through `StructSummaryProvider`. + +[property]: https://docs.python.org/3/library/functions.html#property +[get_address]: https://lldb.llvm.org/python_api/lldb.SBValue.html#lldb.SBValue.GetValueAsAddress + +### `SBType` + +* "Aggregate type" means a non-primitive struct/class/union +* "Template" is equivalent to "Generic" +* Types can be looked up by their name via `SBTarget.FindFirstType(type_name)`. `SBTarget` can be +acquired via `SBValue.GetTarget` +* `SBType.template_args` returns `None` instead of an empty list if the type has no generics +* It is sometimes necessary to transform a type into the type you want via functions like +`SBType.GetArrayType` and `SBType.GetPointerType`. These functions cannot fail. They ask the +underlying LLDB `TypeSystem` plugin for the type, bypassing the debug info completely. Even if the +type does not exist in the debug info at all, these functions can create the appropriate type. +* `SBType.GetCanonicalType` is effectively `SBType.GetTypedefedType` + `SBType.GetUnqualifiedType`. +Unlike `SBType.GetTypedefedType`, it will always return a valid `SBType` regardless of whether or +not the original `SBType` is a typedef. +* `SBType.GetStaticFieldWithName` was added in LLDB 18. Unfortunately, backwards compatibility isn't +always possible since the static fields are otherwise completely inaccessible. + + +# Example Provider: `Vec` + +## SyntheticProvider + +We start with the typical prelude, using `__slots__` since we have known fields. In addition to the +object itself, we also need to store the type of the elements because `Vec`'s heap pointer is a +`*mut u8`, not a `*mut T`. Rust is a statically typed language, so the type of `T` will never +change. That means we can store it during initialization. The heap pointer, length, and capacity +*can* change though, and thus are default initialized here. + +```python +import lldb + +class VecSyntheticProvider: + valobj: SBValue + data_ptr: SBValue + len: int + cap: int + element_type: SBType + + __slots__ = ( + "valobj", + "data_ptr", + "len", + "cap", + "element_type", + "__weakref__", + ) + + def __init__(valobj: SBValue, _dict) -> None: + self.valobj = valobj + # invalid type is a better default than `None` + self.element_type = SBType() + + # special handling to account for DWARF/PDB differences + if (arg := valobj.GetType().GetTemplateArgumentType(0)): + self.element_type = arg + else: + arg_name = next(get_template_args(valobj.GetTypeName())) + self.element_type = resolve_msvc_template_arg(arg_name, valobj.GetTarget()) +``` + +For the implementation of `get_template_args` and `resolve_msvc_template_arg`, please see: +[`lldb_providers.py`](https://github.com/rust-lang/rust/blob/main/src/etc/lldb_providers.py#L136). + +Next, the update function. We check if the pointer or length have changed. We can ommit checking the +capacity, as the number of children will remain the same unless `len` changes. If changing the +capacity resulted in a reallocation, `data_ptr`'s address would be different. + +If `data_ptr` and `length` haven't changed, we can take advantage of LLDB's caching and return +early. If they have changed, we store the new values and tell LLDB to flush the cache. + +```python +def update(self): + ptr = self.valobj.GetChildMemberWithName("data_ptr") + len = self.valobj.GetChildMemberWithName("length").GetValueAsUnsigned() + + if ( + self.data_ptr.GetValueAsAddress() == ptr.GetValueAsAddress() + and self.len == len + ): + # Our child address offsets and child count are still valid + # so we can reuse cached children + return True + + self.data_ptr = ptr + self.len = len + + return False +``` + +`has_children` and `num_children` are both straightforward: + +```python +def has_children(self) -> bool: + return True + +def num_children(self) -> int: + return self.len +``` + +When accessing elements, we expect values of the format `[0]`, `[1]`, etc. to mimic indexing. +Additionally, we still want the user to be able to quickly access the length and capacity, as they +can be very useful when debugging. We assign these values `u32::MAX - 1` and `u32::MAX - 2` +respectively, as we can almost surely guarantee that they will not overlap with element values. Note +that we can account for both the full and shorthand `capacity` name. + +```python + def get_child_index(self, name: str) -> int: + index = name.lstrip("[").rstrip("]") + if index.isdigit(): + return int(index) + if name == "len": + return lldb.UINT32_MAX - 1 + if name == "cap" or name == "capacity": + return lldb.UINT32_MAX - 2 + + return -1 +``` + +We now have to properly coordinate `get_child_at_index` so that the elements, length, and capacity +are all accessible. + +```python +def get_child_at_index(self, index: int) -> SBValue: + if index == UINT32_MAX - 1: + return self.valobj.GetChildMemberWithName("len") + if index == UINT32_MAX - 2: + return ( + self.valobj.GetChildMemberWithName("buf") + .GetChildMemberWithName("inner") + .GetChildMemberWithName("cap") + .GetChildAtIndex(0) + .Clone("capacity") + ) + addr = self.data_ptr.GetValueAsAddress() + addr += index * self.element_type.GetByteSize() + return self.valobj.CreateValueFromAddress(f"[{index}]", addr, self.element_type) +``` + +For the type's display name, we can strip the path qualifier. User defined types named +`Vec` will end up fully qualified, so there shouldn't be any ambiguity. We can also remove the +allocator generic, as it's very very rarely useful. We use `get_template_args` instead of +`self.element_type.GetName()` for 3 reasons: + +1. If we fail to resolve the element type for any reason, `self.valobj`'s type name can still let +the user know what the real type of the element is +2. Type names are not subject to the limitations of DWARF and PDB nodes, so the template type in +the name will reflect things like `*const`/`*mut` and `&`/`&mut`. +3. We do not currently (Nov 2025) normalize MSVC type names, but once we do, we will need to work with the +string-names of types anyway. It's also much easier to cache a string-to-string conversion compared +to an `SBType`-to-string conversion. + +```python +def get_type_name(self) -> str: + return f"Vec<{next(get_template_args(self.valobj))}>" +``` + +There isn't an appropriate primitive value with which to represent a `Vec`, so we simply ommit +the `get_value` function. + +## SummaryProvider + +The summary provider is very simple thanks to our synthetic provider. The only real hiccup is that +`GetSummary` only returns a value if the object's type has a `SummaryProvider`. If it doesn't, it +will return an empty string which is not ideal. In a full set of visualizer scripts, we can ensure +that every type that doesn't have a `GetSummary()` or a `GetValue()` is a struct, and then delegate +to a generic `StructSummaryProvider`. For this demonstration, I will gloss over that detail. + +```python +def VecSummaryProvider(valobj: SBValue, _lldb_internal) -> str: + children = [] + for i in range(valobj.GetNumChildren()): + child = valobj.GetChildAtIndex(i) + summary = child.GetSummary() + if summary is None: + summary = child.GetValue() + if summary is None: + summary = "{...}" + + children.append(summary) + + return f"vec![{", ".join(children)}]" +``` + +## Enabling the providers + +Assume this synthetic is imported into `lldb_lookup.py` + +With CLI commands: + +```txt +type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust +``` + +With `__lldb_init_module`: + +```python +def __lldb_init_module(debugger: SBDebugger, _dict: LLDBOpaque): + # Ensure the category exists and is enabled + rust_cat = debugger.GetCategory("Rust") + + if not rust_cat.IsValid(): + rust_cat = debugger.CreateCategory("Rust") + + rust_cat.SetEnabled(True) + + # Register Vec providers + vec_regex = r"^(alloc::([a-z_]+::)+)Vec<.+>$" + sb_name = lldb.SBTypeNameSpecifier(vec_regex, is_regex=True) + + sb_synth = lldb.SBTypeSynthetic.CreateWithClassName("lldb_lookup.VecSyntheticProvider") + sb_synth.SetOptions(lldb.eTypeOptionCascade) + + sb_summary = lldb.SBTypeSummary.CreateWithFunctionName("lldb_lookup.VecSummaryProvider") + sb_summary.SetOptions(lldb.eTypeOptionCascade) + + rust_cat.AddTypeSynthetic(sb_name, sb_synth) + rust_cat.AddSummary(sb_name, sb_summary) +``` + +## Output + +Without providers: + +```text +(lldb) v vec_v +(alloc::vec::Vec) vec_v = { + buf = { + inner = { + ptr = { + pointer = (pointer = "\n") + _marker = {} + } + cap = (__0 = 5) + alloc = {} + } + _marker = {} + } + len = 5 +} +(lldb) v vec_v[0] +error: :1:6: subscripted value is not an array or pointer + 1 | vec_v[0] + | ^ +``` + +With providers (`v ` prints the summary and then a list of all children): + +```text +(lldb) v vec_v +(Vec) vec_v = vec![10, 20, 30, 40, 50] { + [0] = 10 + [1] = 20 + [2] = 30 + [3] = 40 + [4] = 50 +} +(lldb) v vec_v[0] +(int) vec_v[0] = 10 +``` + +We can also confirm that the "hidden" length and capacity are still accessible: + +```text +(lldb) v vec_v.len +(unsigned long long) vec_v.len = 5 +(lldb) v vec_v.capacity +(unsigned long long) vec_v.capacity = 5 +(lldb) v vec_v.cap +(unsigned long long) vec_v.cap = 5 +``` \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md b/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md new file mode 100644 index 000000000000..cc052b6b965f --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/llvm-codegen.md @@ -0,0 +1,12 @@ +# (WIP) LLVM Codegen + +When Rust calls an LLVM `DIBuilder` function, LLVM translates the given information to a +["debug record"][dbg_record] that is format-agnostic. These records can be inspected in the LLVM-IR. + +[dbg_record]: https://llvm.org/docs/SourceLevelDebugging.html#debug-records + +It is important to note that tags within the debug records are **always stored as DWARF tags**. If +the target calls for PDB debug info, during codegen the debug records will then be passed through +[a module that translates the DWARF tags to their CodeView counterparts][cv]. + +[cv]:https://github.com/llvm/llvm-project/blob/main/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/natvis-visualizers.md b/src/doc/rustc-dev-guide/src/debuginfo/natvis-visualizers.md new file mode 100644 index 000000000000..653e7d5d6555 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/natvis-visualizers.md @@ -0,0 +1,3 @@ +# (WIP) CDB - Natvis + +Official documentation for Natvis can be found [here](https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects) and [here](https://code.visualstudio.com/docs/cpp/natvis) diff --git a/src/doc/rustc-dev-guide/src/debuginfo/rust-codegen.md b/src/doc/rustc-dev-guide/src/debuginfo/rust-codegen.md new file mode 100644 index 000000000000..73fa68d36076 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/rust-codegen.md @@ -0,0 +1,183 @@ +# Rust Codegen + +The first phase in debug info generation requires Rust to inspect the MIR of the program and +communicate it to LLVM. This is primarily done in [`rustc_codegen_llvm/debuginfo`][llvm_di], though +some type-name processing exists in [`rustc_codegen_ssa/debuginfo`][ssa_di]. Rust communicates to +LLVM via the `DIBuilder` API - a thin wrapper around LLVM's internals that exists in +[rustc_llvm][rustc_llvm]. + +[llvm_di]: https://github.com/rust-lang/rust/tree/main/compiler/rustc_codegen_llvm/src/debuginfo +[ssa_di]: https://github.com/rust-lang/rust/tree/main/compiler/rustc_codegen_ssa/src/debuginfo +[rustc_llvm]: https://github.com/rust-lang/rust/tree/main/compiler/rustc_llvm + +# Type Information + +Type information typically consists of the type name, size, alignment, as well as things like +fields, generic parameters, and storage modifiers if they are relevant. Much of this work happens in +[rustc_codegen_llvm/src/debuginfo/metadata][di_metadata]. + +[di_metadata]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs + +It is important to keep in mind that the goal is not necessarily "represent types exactly how they +appear in Rust", rather it is to represent them in a way that allows debuggers to most accurately +reconstruct the data during debugging. This distinction is vital to understanding the core work that +occurs on this layer; many changes made here will be for the purpose of working around debugger +limitations when no other option will work. + +## Quirks + +Rust's generated DI nodes "pretend" to be C/C++ for both CDB and LLDB's sake. This can result in +some unintuitive and non-idiomatic debug info. + +### Pointers and Reference + +Wide pointers/references/`Box` are treated as a struct with 2 fields: `data_ptr` and `length`. + +All non-wide pointers, references, and `Box` pointers are output as pointer nodes, and no +distinction is made between `mut` and non-`mut`. Several attempts have been made to rectify this, +but unfortunately there is not a straightforward solution. Using the `reference` DI nodes of the +respective formats has pitfalls. There is a semantic difference between C++ references and Rust +references that is unreconcilable. + +>From [cppreference](https://en.cppreference.com/w/cpp/language/reference.html): +> +>References are not objects; **they do not necessarily occupy storage**, although the compiler may +>allocate storage if it is necessary to implement the desired semantics (e.g. a non-static data +>member of reference type usually increases the size of the class by the amount necessary to store +>a memory address). +> +>Because references are not objects, **there are no arrays of references, no pointers to references, and no references to references** + +The current proposed solution is to simply [typedef the pointer nodes][issue_144394]. + +[issue_144394]: https://github.com/rust-lang/rust/pull/144394 + +Using the `const` qualifier to denote non-`mut` poses potential issues due to LLDB's internal +optimizations. In short, LLDB attempts to cache the child-values of variables (e.g. struct fields, +array elements) when stepping through code. A heuristic is used to determine which values are safely +cache-able, and `const` is part of that heuristic. Research has not been done into how this would +interact with things like Rust's interior mutability constructs. + +### DWARF vs PDB + +While most of the type information is fairly straight forward, one notable issue is the debug info +format of the target. Each format has different semantics and limitations, as such they require +slightly different debug info in some cases. This is gated by calls to +[`cpp_like_debuginfo`][cpp_like]. + +[cpp_like]: https://github.com/rust-lang/rust/blob/main/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs#L813 + +### Naming + +Rust attempts to communicate type names as accurately as possible, but debuggers and debug info +formats do not always respect that. + +Due to limitations in MSVC's expression parser, the following name transformations are made for PDB +debug info: + +| Rust name | MSVC name | +| --- | --- | +| `&str`/`&mut str` | `ref$`/`ref_mut$` | +| `&[T]`/`&mut [T]` | `ref$ >`/`ref_mut$ >`[^1] | +| `[T; N]` | `array$` | +| `RustEnum` | `enum2$` | +| `(T1, T2)` | `tuple$`| +| `*const T` | `ptr_const$` | +| `*mut T` | `ptr_mut$` | +| `usize` | `size_t`[^2] | +| `isize` | `ptrdiff_t`[^2] | +| `uN` | `unsigned __intN`[^2] | +| `iN` | `__intN`[^2] | +| `f32` | `float`[^2] | +| `f64` | `double`[^2] | +| `f128` | `fp128`[^2] | + +[^1]: MSVC's expression parser will treat `>>` as a right shift. It is necessary to separate +consecutive `>`'s with a space (`> >`) in type names. + +[^2]: While these type names are generated as part of the debug info node (which is then wrapped in +a typedef node with the Rust name), once the LLVM-IR node is converted to a CodeView node, the type +name information is lost. This is because CodeView has special shorthand nodes for primitive types, +and those shorthand nodes to not have a "name" field. + +### Generics + +Rust outputs generic *type* information (`T` in `ArrayVec`), but not generic *value* +information (`N` in `ArrayVec`). + +CodeView does not have a leaf node for generics/C++ templates, so all generic information is lost +when generating PDB debug info. There are workarounds that allow the debugger to retrieve the +generic arguments via the type name, but it is fragile solution at best. Efforts are being made to +contact Microsoft to correct this deficiency, and/or to use one of the unused CodeView node types as +a suitable equivalent. + +### Type aliases + +Rust outputs typedef nodes in several cases to help account for debugger limitiations, but it does +not currently output nodes for [type aliases in the source code][type_aliases]. + +[type_aliases]: https://doc.rust-lang.org/reference/items/type-aliases.html + +### Enums + +Enum DI nodes are generated in [rustc_codegen_llvm/src/debuginfo/metadata/enums][di_metadata_enums] + +[di_metadata_enums]: https://github.com/rust-lang/rust/tree/main/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums + +#### DWARF + +DWARF has a dedicated node for discriminated unions: `DW_TAG_variant`. It is a container that +references `DW_TAG_variant_part` nodes that may or may not contain a discriminant value. The +hierarchy looks as follows: + +```txt +DW_TAG_structure_type (top-level type for the coroutine) + DW_TAG_variant_part (variant part) + DW_AT_discr (reference to discriminant DW_TAG_member) + DW_TAG_member (discriminant member) + DW_TAG_variant (variant 1) + DW_TAG_variant (variant 2) + DW_TAG_variant (variant 3) + DW_TAG_structure_type (type of variant 1) + DW_TAG_structure_type (type of variant 2) + DW_TAG_structure_type (type of variant 3) +``` + +#### PDB +PDB does not have a dedicated node, so it generates the C equivalent of a discriminated union: + +```c +union enum2$ { + enum VariantNames { + First, + Second + }; + struct Variant0 { + struct First { + // fields + }; + static const enum2$::VariantNames NAME; + static const unsigned long DISCR_EXACT; + enum2$::Variant0::First value; + }; + struct Variant1 { + struct Second { + // fields + }; + static enum2$::VariantNames NAME; + static unsigned long DISCR_EXACT; + enum2$::Variant1::Second value; + }; + enum2$::Variant0 variant0; + enum2$::Variant1 variant1; + unsigned long tag; +} +``` + +An important note is that due to limitations in LLDB, the `DISCR_*` value generated is always a +`u64` even if the value is not `#[repr(u64)]`. This is largely a non-issue for LLDB because the +`DISCR_*` value and the `tag` are read into `uint64_t` values regardless of their type. + +# Source Information + +TODO \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/debuginfo/testing.md b/src/doc/rustc-dev-guide/src/debuginfo/testing.md new file mode 100644 index 000000000000..f58050875e1a --- /dev/null +++ b/src/doc/rustc-dev-guide/src/debuginfo/testing.md @@ -0,0 +1,8 @@ +# (WIP) Testing + +The debug info test suite is undergoing a substantial rewrite. This section will be filled out as +the rewrite makes progress. + +Please see [this tracking issue][148483] for more information. + +[148483]: https://github.com/rust-lang/rust/issues/148483 \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md b/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md index 1693432b90de..c7bb6669dce4 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/error-codes.md @@ -33,40 +33,22 @@ written, as always: ask your reviewer or ask around on the Rust Zulip. Error codes are stored in `compiler/rustc_error_codes`. -To create a new error, you first need to find the next available -code. You can find it with `tidy`: +To create a new error, you first need to find the next available code. +You can find it by opening `rustc_error_codes/src/lib.rs` and scrolling down +to the end of the `error_codes!` macro declaration. -``` -./x test tidy -``` - -This will invoke the tidy script, which generally checks that your code obeys -our coding conventions. Some of these jobs check error codes and ensure that -there aren't duplicates, etc (the tidy check is defined in -`src/tools/tidy/src/error_codes.rs`). Once it is finished with that, tidy will -print out the highest used error code: - -``` -... -tidy check -Found 505 error codes -Highest error code: `E0591` -... -``` - -Here we see the highest error code in use is `E0591`, so we _probably_ want -`E0592`. To be sure, run `rg E0592` and check, you should see no references. +Here we might see the highest error code in use is `E0805`, so we _probably_ want +`E0806`. To be sure, run `rg E0806` and check, you should see no references. You will have to write an extended description for your error, -which will go in `rustc_error_codes/src/error_codes/E0592.md`. -To register the error, open `rustc_error_codes/src/error_codes.rs` and add the -code (in its proper numerical order) into` register_diagnostics!` macro, like -this: +which will go in `rustc_error_codes/src/error_codes/E0806.md`. +To register the error, add the code (in its proper numerical order) to +the `error_codes!` macro, like this: ```rust -register_diagnostics! { - ... - E0592: include_str!("./error_codes/E0592.md"), +macro_rules! error_codes { +... +0806, } ``` @@ -75,7 +57,7 @@ To actually issue the error, you can use the `struct_span_code_err!` macro: ```rust struct_span_code_err!(self.dcx(), // some path to the `DiagCtxt` here span, // whatever span in the source you want - E0592, // your new error code + E0806, // your new error code fluent::example::an_error_message) .emit() // actually issue the error ``` diff --git a/src/doc/rustc-dev-guide/src/fuzzing.md b/src/doc/rustc-dev-guide/src/fuzzing.md index cc98b49a97c6..635c01d21aee 100644 --- a/src/doc/rustc-dev-guide/src/fuzzing.md +++ b/src/doc/rustc-dev-guide/src/fuzzing.md @@ -3,12 +3,13 @@ For the purposes of this guide, *fuzzing* is any testing methodology that -involves compiling a wide variety of programs in an attempt to uncover bugs in -rustc. Fuzzing is often used to find internal compiler errors (ICEs). Fuzzing -can be beneficial, because it can find bugs before users run into them and -provide small, self-contained programs that make the bug easier to track down. +involves compiling a wide variety of programs in an attempt to uncover bugs in rustc. +Fuzzing is often used to find internal compiler errors (ICEs). +Fuzzing can be beneficial, because it can find bugs before users run into them. +It also provides small, self-contained programs that make the bug easier to track down. However, some common mistakes can reduce the helpfulness of fuzzing and end up -making contributors' lives harder. To maximize your positive impact on the Rust +making contributors' lives harder. +To maximize your positive impact on the Rust project, please read this guide before reporting fuzzer-generated bugs! ## Guidelines @@ -21,31 +22,31 @@ project, please read this guide before reporting fuzzer-generated bugs! - Include a reasonably minimal, standalone example along with any bug report - Include all of the information requested in the bug report template - Search for existing reports with the same message and query stack -- Format the test case with `rustfmt`, if it maintains the bug +- Format the test case with `rustfmt` - Indicate that the bug was found by fuzzing *Please don't:* - Don't report lots of bugs that use internal features, including but not limited to `custom_mir`, `lang_items`, `no_core`, and `rustc_attrs`. -- Don't seed your fuzzer with inputs that are known to crash rustc (details - below). +- Don't seed your fuzzer with inputs that are known to crash rustc (details below). ### Discussion If you're not sure whether or not an ICE is a duplicate of one that's already -been reported, please go ahead and report it and link to issues you think might -be related. In general, ICEs on the same line but with different *query stacks* -are usually distinct bugs. For example, [#109020][#109020] and [#109129][#109129] -had similar error messages: +been reported, please go ahead and report it and link to issues you think might be related. +In general, ICEs on the same line but with different *query stacks* are usually distinct bugs. +For example, [#109020] and [#109129] had similar error messages: ``` error: internal compiler error: compiler/rustc_middle/src/ty/normalize_erasing_regions.rs:195:90: Failed to normalize <[closure@src/main.rs:36:25: 36:28] as std::ops::FnOnce<(Emplacable<()>,)>>::Output, maybe try to call `try_normalize_erasing_regions` instead ``` + ``` error: internal compiler error: compiler/rustc_middle/src/ty/normalize_erasing_regions.rs:195:90: Failed to normalize <() as Project>::Assoc, maybe try to call `try_normalize_erasing_regions` instead ``` -but different query stacks: + +However, they have different query stacks: ``` query stack during panic: #0 [fn_abi_of_instance] computing call ABI of `<[closure@src/main.rs:36:25: 36:28] as core::ops::function::FnOnce<(Emplacable<()>,)>>::call_once - shim(vtable)` @@ -63,10 +64,10 @@ end of query stack ## Building a corpus -When building a corpus, be sure to avoid collecting tests that are already -known to crash rustc. A fuzzer that is seeded with such tests is more likely to -generate bugs with the same root cause, wasting everyone's time. The simplest -way to avoid this is to loop over each file in the corpus, see if it causes an +When building a corpus, be sure to avoid collecting tests that are already known to crash rustc. +A fuzzer that is seeded with such tests is more likely to +generate bugs with the same root cause. +The simplest way to avoid this is to loop over each file in the corpus, see if it causes an ICE, and remove it if so. To build a corpus, you may want to use: @@ -74,26 +75,26 @@ To build a corpus, you may want to use: - The rustc/rust-analyzer/clippy test suites (or even source code) --- though avoid tests that are already known to cause failures, which often begin with comments like `//@ failure-status: 101` or `//@ known-bug: #NNN`. -- The already-fixed ICEs in the archived [Glacier][glacier] repository --- though +- The already-fixed ICEs in the archived [Glacier] repository --- though avoid the unfixed ones in `ices/`! [glacier]: https://github.com/rust-lang/glacier ## Extra credit -Here are a few things you can do to help the Rust project after filing an ICE. +Here are a few things you can do to help the Rust project after filing an ICE: - [Bisect][bisect] the bug to figure out when it was introduced. - If you find the regressing PR / commit, you can mark the issue with the label - `S-has-bisection`. If not, consider applying `E-needs-bisection` instead. + If you find the regressing PR / commit, you can mark the issue with the label `S-has-bisection`. + If not, consider applying `E-needs-bisection` instead. - Fix "distractions": problems with the test case that don't contribute to triggering the ICE, such as syntax errors or borrow-checking errors -- Minimize the test case (see below). If successful, you can label the - issue with `S-has-mcve`. Otherwise, you can apply `E-needs-mcve`. +- Minimize the test case (see below). + If successful, you can label the issue with `S-has-mcve`. + Otherwise, you can apply `E-needs-mcve`. - Add the minimal test case to the rust-lang/rust repo as a [crash test]. While you're at it, consider including other "untracked" crashes in your PR. - Please don't forget to mark all relevant issues with `S-bug-has-test` once - your PR is merged. + Please don't forget to mark all relevant issues with `S-bug-has-test` once your PR is merged. See also [applying and removing labels][labeling]. @@ -103,13 +104,14 @@ See also [applying and removing labels][labeling]. ## Minimization -It is helpful to carefully *minimize* the fuzzer-generated input. When -minimizing, be careful to preserve the original error, and avoid introducing +It is helpful to carefully *minimize* the fuzzer-generated input. +When minimizing, be careful to preserve the original error, and avoid introducing distracting problems such as syntax, type-checking, or borrow-checking errors. -There are some tools that can help with minimization. If you're not sure how -to avoid introducing syntax, type-, and borrow-checking errors while using -these tools, post both the complete and minimized test cases. Generally, +There are some tools that can help with minimization. +If you're not sure how to avoid introducing syntax, type-, and borrow-checking errors while using +these tools, post both the complete and minimized test cases. +Generally, *syntax-aware* tools give the best results in the least amount of time. [`treereduce-rust`][treereduce] and [picireny][picireny] are syntax-aware. [`halfempty`][halfempty] is not, but is generally a high-quality tool. @@ -121,31 +123,32 @@ these tools, post both the complete and minimized test cases. Generally, ## Effective fuzzing When fuzzing rustc, you may want to avoid generating machine code, since this -is mostly done by LLVM. Try `--emit=mir` instead. +is mostly done by LLVM. +Try `--emit=mir` instead. -A variety of compiler flags can uncover different issues. `-Zmir-opt-level=4` -will turn on MIR optimization passes that are not run by default, potentially -uncovering interesting bugs. `-Zvalidate-mir` can help uncover such bugs. +A variety of compiler flags can uncover different issues. +`-Zmir-opt-level=4` will turn on MIR optimization passes that are not run by default, potentially +uncovering interesting bugs. +`-Zvalidate-mir` can help uncover such bugs. If you're fuzzing a compiler you built, you may want to build it with `-C -target-cpu=native` or even PGO/BOLT to squeeze out a few more executions per -second. Of course, it's best to try multiple build configurations and see +target-cpu=native` or even PGO/BOLT to squeeze out a few more executions per second. +Of course, it's best to try multiple build configurations and see what actually results in superior throughput. You may want to build rustc from source with debug assertions to find -additional bugs, though this is a trade-off: it can slow down fuzzing by -requiring extra work for every execution. To enable debug assertions, add this -to `bootstrap.toml` when compiling rustc: +additional bugs, though this can slow down fuzzing by +requiring extra work for every execution. +To enable debug assertions, add this to `bootstrap.toml` when compiling rustc: ```toml -[rust] -debug-assertions = true +rust.debug-assertions = true ``` -ICEs that require debug assertions to reproduce should be tagged -[`requires-debug-assertions`][requires-debug-assertions]. +ICEs that require debug assertions to reproduce should be tagged +[`requires-debug-assertions`]. -[requires-debug-assertions]: https://github.com/rust-lang/rust/labels/requires-debug-assertions +[`requires-debug-assertions`]: https://github.com/rust-lang/rust/labels/requires-debug-assertions ## Existing projects diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md index 34bc5b59dd03..36d19e6c570e 100644 --- a/src/doc/rustc-dev-guide/src/getting-started.md +++ b/src/doc/rustc-dev-guide/src/getting-started.md @@ -96,7 +96,7 @@ For example: Not all important or beginner work has issue labels. See below for how to find work that isn't labelled. -[help-wanted-search]: https://github.com/issues?q=is%3Aopen+is%3Aissue+org%3Arust-lang+no%3Aassignee+label%3AE-easy%2C%22good+first+issue%22%2Cgood-first-issue%2CE-medium%2CEasy%2CE-help-wanted%2CE-mentor+-label%3AS-blocked+-linked%3Apr+ +[help-wanted-search]: https://github.com/issues?q=is%3Aopen%20is%3Aissue%20org%3Arust-lang%20no%3Aassignee%20label%3AE-easy%2CE-medium%2CE-help-wanted%2CE-mentor%20-label%3AS-blocked%20-linked%3Apr [Triage]: ./contributing.md#issue-triage ### Recurring work @@ -151,6 +151,9 @@ Issues that have been resolved but do not have a regression test are marked with Writing unit tests is a low-risk, lower-priority task that offers new contributors a great opportunity to familiarize themselves with the testing infrastructure and contribution workflow. +You can see a list of needs test issues [here][needs-test-issues]. + +[needs-test-issues]: https://github.com/rust-lang/rust/issues?q=is%3Aissue%20is%3Aopen%20label%3AE-needs-test%20no%3Aassignee ### Contributing to std (standard library) diff --git a/src/doc/rustc-dev-guide/src/hir/ambig-unambig-ty-and-consts.md b/src/doc/rustc-dev-guide/src/hir/ambig-unambig-ty-and-consts.md deleted file mode 100644 index d4f504ad2a98..000000000000 --- a/src/doc/rustc-dev-guide/src/hir/ambig-unambig-ty-and-consts.md +++ /dev/null @@ -1,63 +0,0 @@ -# Ambig/Unambig Types and Consts - -Types and Consts args in the HIR can be in two kinds of positions ambiguous (ambig) or unambiguous (unambig). Ambig positions are where -it would be valid to parse either a type or a const, unambig positions are where only one kind would be valid to -parse. - -```rust -fn func(arg: T) { - // ^ Unambig type position - let a: _ = arg; - // ^ Unambig type position - - func::(arg); - // ^ ^ - // ^^^^ Ambig position - - let _: [u8; 10]; - // ^^ ^^ Unambig const position - // ^^ Unambig type position -} - -``` - -Most types/consts in ambig positions are able to be disambiguated as either a type or const during parsing. Single segment paths are always represented as types in the AST but may get resolved to a const parameter during name resolution, then lowered to a const argument during ast-lowering. The only generic arguments which remain ambiguous after lowering are inferred generic arguments (`_`) in path segments. For example, in `Foo<_>` it is not clear whether the `_` argument is an inferred type argument, or an inferred const argument. - -In unambig positions, inferred arguments are represented with [`hir::TyKind::Infer`][ty_infer] or [`hir::ConstArgKind::Infer`][const_infer] depending on whether it is a type or const position respectively. -In ambig positions, inferred arguments are represented with `hir::GenericArg::Infer`. - -A naive implementation of this would result in there being potentially 5 places where you might think an inferred type/const could be found in the HIR from looking at the structure of the HIR: -1. In unambig type position as a `hir::TyKind::Infer` -2. In unambig const arg position as a `hir::ConstArgKind::Infer` -3. In an ambig position as a [`GenericArg::Type(TyKind::Infer)`][generic_arg_ty] -4. In an ambig position as a [`GenericArg::Const(ConstArgKind::Infer)`][generic_arg_const] -5. In an ambig position as a [`GenericArg::Infer`][generic_arg_infer] - -Note that places 3 and 4 would never actually be possible to encounter as we always lower to `GenericArg::Infer` in generic arg position. - -This has a few failure modes: -- People may write visitors which check for `GenericArg::Infer` but forget to check for `hir::TyKind/ConstArgKind::Infer`, only handling infers in ambig positions by accident. -- People may write visitors which check for `hir::TyKind/ConstArgKind::Infer` but forget to check for `GenericArg::Infer`, only handling infers in unambig positions by accident. -- People may write visitors which check for `GenericArg::Type/Const(TyKind/ConstArgKind::Infer)` and `GenericArg::Infer`, not realising that we never represent inferred types/consts in ambig positions as a `GenericArg::Type/Const`. -- People may write visitors which check for *only* `TyKind::Infer` and not `ConstArgKind::Infer` forgetting that there are also inferred const arguments (and vice versa). - -To make writing HIR visitors less error prone when caring about inferred types/consts we have a relatively complex system: - -1. We have different types in the compiler for when a type or const is in an unambig or ambig position, `hir::Ty` and `hir::Ty<()>`. [`AmbigArg`][ambig_arg] is an uninhabited type which we use in the `Infer` variant of `TyKind` and `ConstArgKind` to selectively "disable" it if we are in an ambig position. - -2. The [`visit_ty`][visit_ty] and [`visit_const_arg`][visit_const_arg] methods on HIR visitors only accept the ambig position versions of types/consts. Unambig types/consts are implicitly converted to ambig types/consts during the visiting process, with the `Infer` variant handled by a dedicated [`visit_infer`][visit_infer] method. - -This has a number of benefits: -- It's clear that `GenericArg::Type/Const` cannot represent inferred type/const arguments -- Implementors of `visit_ty` and `visit_const_arg` will never encounter inferred types/consts making it impossible to write a visitor that seems to work right but handles edge cases wrong -- The `visit_infer` method handles *all* cases of inferred type/consts in the HIR making it easy for visitors to handle inferred type/consts in one dedicated place and not forget cases - -[ty_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.TyKind.html#variant.Infer -[const_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.ConstArgKind.html#variant.Infer -[generic_arg_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Type -[generic_arg_const]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Const -[generic_arg_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Infer -[ambig_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.AmbigArg.html -[visit_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_ty -[visit_const_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_const_arg -[visit_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_infer diff --git a/src/doc/rustc-dev-guide/src/notification-groups/about.md b/src/doc/rustc-dev-guide/src/notification-groups/about.md index c649b930fe9f..917a0f8a1020 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/about.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/about.md @@ -22,6 +22,7 @@ Here's the list of the notification groups: - [Apple](./apple.md) - [ARM](./arm.md) - [Emscripten](./emscripten.md) +- [LoongArch](./loongarch.md) - [RISC-V](./risc-v.md) - [WASI](./wasi.md) - [WebAssembly](./wasm.md) @@ -63,6 +64,7 @@ Example PRs: * [Example of adding yourself to the Apple group.](https://github.com/rust-lang/team/pull/1434) * [Example of adding yourself to the ARM group.](https://github.com/rust-lang/team/pull/358) * [Example of adding yourself to the Emscripten group.](https://github.com/rust-lang/team/pull/1579) +* [Example of adding yourself to the LoongArch group.](https://github.com/rust-lang/team/pull/2176) * [Example of adding yourself to the RISC-V group.](https://github.com/rust-lang/team/pull/394) * [Example of adding yourself to the WASI group.](https://github.com/rust-lang/team/pull/1580) * [Example of adding yourself to the WebAssembly group.](https://github.com/rust-lang/team/pull/1581) diff --git a/src/doc/rustc-dev-guide/src/notification-groups/loongarch.md b/src/doc/rustc-dev-guide/src/notification-groups/loongarch.md new file mode 100644 index 000000000000..0a3707a9ad99 --- /dev/null +++ b/src/doc/rustc-dev-guide/src/notification-groups/loongarch.md @@ -0,0 +1,22 @@ +# LoongArch notification group + +**Github Label:** [O-loongarch]
+**Ping command:** `@rustbot ping loongarch` + +[O-loongarch]: https://github.com/rust-lang/rust/labels/O-loongarch + +This list will be used to ask for help both in diagnosing and testing +LoongArch-related issues as well as suggestions on how to resolve +interesting questions regarding our LoongArch support. + +The group also has an associated Zulip channel ([`#t-compiler/loong-arch`]) +where people can go to pose questions and discuss LoongArch-specific topics. + +So, if you are interested in participating, please sign up for the +LoongArch group! To do so, open a PR against the [rust-lang/team] +repository. Just [follow this example][eg], but change the username to +your own! + +[`#t-compiler/loong-arch`]: https://rust-lang.zulipchat.com/#narrow/channel/551512-t-compiler.2Floong-arch +[rust-lang/team]: https://github.com/rust-lang/team +[eg]: https://github.com/rust-lang/team/pull/2176 diff --git a/src/doc/rustc-dev-guide/src/opaque-types-type-alias-impl-trait.md b/src/doc/rustc-dev-guide/src/opaque-types-type-alias-impl-trait.md index 956f568285a0..9f20bfc708dc 100644 --- a/src/doc/rustc-dev-guide/src/opaque-types-type-alias-impl-trait.md +++ b/src/doc/rustc-dev-guide/src/opaque-types-type-alias-impl-trait.md @@ -12,15 +12,16 @@ type Foo = impl Bar; This declares an opaque type named `Foo`, of which the only information is that it implements `Bar`. Therefore, any of `Bar`'s interface can be used on a `Foo`, -but nothing else (regardless of whether it implements any other traits). +but nothing else (regardless of whether the concrete type implements any other traits). Since there needs to be a concrete background type, -you can (as of January 2021) express that type +you can (as of May 2025) express that type by using the opaque type in a "defining use site". ```rust,ignore struct Struct; impl Bar for Struct { /* stuff */ } +#[define_opaque(Foo)] fn foo() -> Foo { Struct } @@ -28,6 +29,27 @@ fn foo() -> Foo { Any other "defining use site" needs to produce the exact same type. +Note that defining a type alias to an opaque type is an unstable feature. +To use it, you need `nightly` and the annotations `#![feature(type_alias_impl_trait)]` on the file and `#[define_opaque(Foo)]` on the method that links the opaque type to the concrete type. +Complete example: + +```rust +#![feature(type_alias_impl_trait)] + +trait Bar { /* stuff */ } + +type Foo = impl Bar; + +struct Struct; + +impl Bar for Struct { /* stuff */ } + +#[define_opaque(Foo)] +fn foo() -> Foo { + Struct +} +``` + ## Defining use site(s) Currently only the return value of a function can be a defining use site @@ -61,3 +83,28 @@ impl Baz for Quux { fn foo() -> Self::Foo { ... } } ``` + +For this you would also need to use `nightly` and the (different) `#![feature(impl_trait_in_assoc_type)]` annotation. +Note that you don't need a `#[define_opaque(Foo)]` on the method anymore as the opaque type is mentioned in the function signature (behind the associated type). +Complete example: + +``` +#![feature(impl_trait_in_assoc_type)] + +trait Bar {} +struct Zap; + +impl Bar for Zap {} + +trait Baz { + type Foo; + fn foo() -> Self::Foo; +} + +struct Quux; + +impl Baz for Quux { + type Foo = impl Bar; + fn foo() -> Self::Foo { Zap } +} +``` diff --git a/src/doc/rustc-dev-guide/src/query.md b/src/doc/rustc-dev-guide/src/query.md index 8377a7b2f31a..5df9de762038 100644 --- a/src/doc/rustc-dev-guide/src/query.md +++ b/src/doc/rustc-dev-guide/src/query.md @@ -69,22 +69,24 @@ are cheaply cloneable; insert an `Rc` if necessary). If, however, the query is *not* in the cache, then the compiler will call the corresponding **provider** function. A provider is a function -implemented in a specific module and **manually registered** into the -[`Providers`][providers_struct] struct during compiler initialization. -The macro system generates the [`Providers`][providers_struct] struct, -which acts as a function table for all query implementations, where each +implemented in a specific module and **manually registered** into either +the [`Providers`][providers_struct] struct (for local crate queries) or +the [`ExternProviders`][extern_providers_struct] struct (for external crate queries) +during compiler initialization. The macro system generates both structs, +which act as function tables for all query implementations, where each field is a function pointer to the actual provider. -**Note:** The `Providers` struct is generated by macros and acts as a function table for all query implementations. -It is **not** a Rust trait, but a plain struct with function pointer fields. +**Note:** Both the `Providers` and `ExternProviders` structs are generated by macros and act as function tables for all query implementations. +They are **not** Rust traits, but plain structs with function pointer fields. **Providers are defined per-crate.** The compiler maintains, internally, a table of providers for every crate, at least -conceptually. Right now, there are really two sets: the providers for -queries about the **local crate** (that is, the one being compiled) -and providers for queries about **external crates** (that is, -dependencies of the local crate). Note that what determines the crate -that a query is targeting is not the *kind* of query, but the *key*. +conceptually. There are two sets of providers: +- The `Providers` struct for queries about the **local crate** (that is, the one being compiled) +- The `ExternProviders` struct for queries about **external crates** (that is, +dependencies of the local crate) + +Note that what determines the crate that a query is targeting is not the *kind* of query, but the *key*. For example, when you invoke `tcx.type_of(def_id)`, that could be a local query or an external query, depending on what crate the `def_id` is referring to (see the [`self::keys::Key`][Key] trait for more @@ -117,31 +119,31 @@ they define both a `provide` and a `provide_extern` function, through ### How providers are set up -When the tcx is created, it is given the providers by its creator using -the [`Providers`][providers_struct] struct. This struct is generated by -the macros here, but it is basically a big list of function pointers: - -[providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html +When the tcx is created, it is given both the local and external providers by its creator using +the `Providers` struct from `rustc_middle::util`. This struct contains both the local and external providers: ```rust,ignore -struct Providers { - type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>, - // ... one field for each query +pub struct Providers { + pub queries: crate::query::Providers, // Local crate providers + pub extern_queries: crate::query::ExternProviders, // External crate providers + pub hooks: crate::hooks::Providers, } ``` +Each of these provider structs is generated by the macros and contains function pointers for their respective queries. + #### How are providers registered? -The `Providers` struct is filled in during compiler initialization, mainly by the `rustc_driver` crate. -But the actual provider functions are implemented in various `rustc_*` crates (like `rustc_middle`, `rustc_hir_analysis`, etc). +The `util::Providers` struct is filled in during compiler initialization, by the `rustc_interface` crate from the `DEFAULT_QUERY_PROVIDERS` static. +The actual provider functions are defined across various `rustc_*` crates (like `rustc_middle`, `rustc_hir_analysis`, etc). To register providers, each crate exposes a [`provide`][provide_fn] function that looks like this: [provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html ```rust,ignore -pub fn provide(providers: &mut Providers) { - *providers = Providers { +pub fn provide(providers: &mut query::Providers) { + *providers = query::Providers { type_of, // ... add more providers here ..*providers @@ -149,27 +151,104 @@ pub fn provide(providers: &mut Providers) { } ``` -- This function takes a mutable reference to the `Providers` struct and sets the fields to point to the correct provider functions. -- You can also assign fields individually, e.g. `providers.type_of = type_of;`. +Note that this function accepts `query::Providers` not `util::Providers`. It is exceedingly rare to need a `provide` function that doesn't just accept `query::Providers`. If more than the `queries` field of `util::Providers` is being updated then `util::Providers` can be accepted instead: +```rust,ignore +pub fn provide(providers: &mut rustc_middle::util::Providers) { + providers.queries.type_of = type_of; + // ... add more local providers here + + providers.extern_queries.type_of = extern_type_of; + // ... add more external providers here + + providers.hooks.some_hook = some_hook; + // ... add more hooks here +} +``` + +Note that `util::Providers` implements `DerefMut` to `query::Providers` so callers of the `provide` functions can pass in a `util::Providers` and it will just work for provider functions that accept `query::Providers` too + +- This function takes a mutable reference to the `query::Providers` struct and sets the fields to point to the correct provider functions. +- You can also assign queries individually, e.g. `providers.type_of = type_of;`. +- You can assign fields individually for each provider type (local, external, and hooks). #### Adding a new provider -Suppose you want to add a new query called `fubar`. You would: +Suppose you want to add a new query called `fubar`. This section focuses on wiring up the providers; for how to declare the query itself in the big `rustc_queries!` macro, see [Adding a new query](#adding-a-new-query) below. -1. Implement the provider function: +In practice you usually: + +1. Decide which crate "owns" the query (for example `rustc_hir_analysis`, `rustc_mir_build`, or another `rustc_*` crate). +2. In that crate, look for an existing `provide` function: ```rust,ignore - fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> Fubar<'tcx> { ... } + pub fn provide(providers: &mut query::Providers) { + // existing assignments + } ``` -2. Register it in the `provide` function: + If it exists, you will extend it to set the field for your new query. If the crate does not yet have a `provide` function, add one and make sure it is included in `DEFAULT_QUERY_PROVIDERS` in the `rustc_interface` crate so that it actually gets called during initialization (see the discussion above). +3. Implement the provider function itself: ```rust,ignore - pub fn provide(providers: &mut Providers) { - *providers = Providers { + fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: LocalDefId) -> Fubar<'tcx> { ... } + ``` +4. Register it in the crate's `provide` function: + ```rust,ignore + pub fn provide(providers: &mut query::Providers) { + *providers = query::Providers { fubar, ..*providers }; } ``` +### How queries interact with external crate metadata + +When a query is made for an external crate (i.e., a dependency), the query system needs to load the information from that crate's metadata. +This is handled by the [`rustc_metadata` crate][rustc_metadata], which is responsible for decoding and providing the information stored in the `.rmeta` files. + +The process works like this: + +1. When a query is made, the query system first checks if the `DefId` refers to a local or external crate by checking if `def_id.krate == LOCAL_CRATE`. + This determines whether to use the local provider from [`Providers`][providers_struct] or the external provider from [`ExternProviders`][extern_providers_struct]. + +2. For external crates, the query system will look for a provider in the [`ExternProviders`][extern_providers_struct] struct. + The `rustc_metadata` crate registers these external providers through the `provide_extern` function in `rustc_metadata/src/rmeta/decoder/cstore_impl.rs`. Just like: + ```rust + pub fn provide_extern(providers: &mut ExternProviders) { + providers.foo = |tcx, def_id| { + // Load and decode metadata for external crate + let cdata = CStore::from_tcx(tcx).get_crate_data(def_id.krate); + cdata.foo(def_id.index) + }; + // Register other external providers... + } + ``` + +3. The metadata is stored in a binary format in `.rmeta` files that contains pre-computed information about the external crate, such as types, function signatures, trait implementations, and other information needed by the compiler. When an external query is made, the `rustc_metadata` crate: + - Loads the `.rmeta` file for the external crate + - Decodes the metadata using the `Decodable` trait + - Returns the decoded information to the query system + +This approach avoids recompiling external crates, allows for faster compilation of dependent crates, and enables incremental compilation to work across crate boundaries. +Here is a simplified example, when you call `tcx.type_of(def_id)` for a type defined in an external crate, the query system will: +1. Detect that the `def_id` refers to an external crate by checking `def_id.krate != LOCAL_CRATE` +2. Call the appropriate provider from `ExternProviders` which was registered by `rustc_metadata` +3. The provider will load and decode the type information from the external crate's metadata +4. Return the decoded type to the caller + +This is why most `rustc_*` crates only need to provide local providers - the external providers are handled by the metadata system. +The only exception is when a crate needs to provide special handling for external queries, in which case it would implement both local and external providers. + +When we define a new query that should work across crates, it does not automatically become cross-crate just because it is listed in `rustc_queries!`. You will typically need to: + +- Add the query to `rustc_queries!` with appropriate modifiers (for example whether it is cached on disk). +- Implement a local provider in the owning crate and register it via that crate's `provide` function. +- Add an external provider in `rustc_metadata` via `provide_extern`, and ensure the query's result is encoded and decoded in the crate metadata. + +An example of introducing such a cross-crate query can be found in commit [`996a185`](https://github.com/rust-lang/rust/commit/996a185) in the `rust-lang/rust` repository. + +[rustc_metadata]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/index.html +[providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html +[extern_providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.ExternProviders.html + --- ## Adding a new query @@ -263,4 +342,3 @@ More discussion and issues: [GitHub issue #42633]: https://github.com/rust-lang/rust/issues/42633 [Incremental Compilation Beta]: https://internals.rust-lang.org/t/incremental-compilation-beta/4721 [Incremental Compilation Announcement]: https://blog.rust-lang.org/2016/09/08/incremental.html - diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md index e19a8204ab41..5781a12ca44c 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md @@ -4,7 +4,7 @@ This page is specifically about the test suite named `rustdoc-json`, which tests For other test suites used for testing rustdoc, see [§Rustdoc test suites](../tests/compiletest.md#rustdoc-test-suites). Tests are run with compiletest, and have access to the usual set of [directives](../tests/directives.md). -Frequenly used directives here are: +Frequently used directives here are: - [`//@ aux-build`][aux-build] to have dependencies. - `//@ edition: 2021` (or some other edition). @@ -23,8 +23,8 @@ Also, talk about how it works ## jsondocck -[jsondocck] processes direcives given in comments, to assert that the values in the output are expected. -It's alot like [htmldocck](./rustdoc-test-suite.md) in that way. +[jsondocck] processes directives given in comments, to assert that the values in the output are expected. +It's a lot like [htmldocck](./rustdoc-test-suite.md) in that way. It uses [JSONPath] as a query language, which takes a path, and returns a *list* of values that that path is said to match to. @@ -48,7 +48,7 @@ These are defined in [`directive.rs`]. Values can be either JSON values, or variables. - JSON values are JSON literals, e.g. `true`, `"string"`, `{"key": "value"}`. - These often need to be quoted using `'`, to be processed as 1 value. See [§Argument spliting](#argument-spliting) + These often need to be quoted using `'`, to be processed as 1 value. See [§Argument splitting](#argument-splitting) - Variables can be used to store the value in one path, and use it in later queries. They are set with the `//@ set = ` directive, and accessed with `$` @@ -57,7 +57,7 @@ Values can be either JSON values, or variables. //@ is $.some.other.path $foo ``` -### Argument spliting +### Argument splitting Arguments to directives are split using the [shlex] crate, which implements POSIX shell escaping. This is because both `` and `` arguments to [directives](#directives) frequently have both diff --git a/src/doc/rustc-dev-guide/src/solve/invariants.md b/src/doc/rustc-dev-guide/src/solve/invariants.md index 8ec15f339e51..4a2c22fec83f 100644 --- a/src/doc/rustc-dev-guide/src/solve/invariants.md +++ b/src/doc/rustc-dev-guide/src/solve/invariants.md @@ -116,6 +116,12 @@ We do however break this invariant in a few cases, some of which are due to bugs - the builtin trait object trait implementation can overlap with a user-defined impl: [#57893](https://github.com/rust-lang/rust/issues/57893) +### Goals with can be proven in a non-empty environment also hold during monomorphization ✅ + +If a goal can be proven in a generic environment, the goal should still hold after instantiating +it with fully concrete types and no where-clauses in scope. + +This is assumed by codegen which ICEs when encountering non-overflow ambiguity. This invariant is currently broken by specialization ([#147507](https://github.com/rust-lang/rust/issues/147507)) and by marker traits ([#149502](https://github.com/rust-lang/rust/issues/149502)). #### The type system is complete during the implicit negative overlap check in coherence ✅ @@ -168,5 +174,10 @@ We lookup types using structural equality during codegen, but this shouldn't nec Semantically different `'static` types need different `TypeId`s to avoid transmutes, for example `for<'a> fn(&'a str)` vs `fn(&'static str)` must have a different `TypeId`. +## Evaluation of const items is deterministic ✅ + +As the values of const items can feed into the type system, it is important that the value of a const item is always the same in every crate. If this isn't the case then we can wind up with associated types with "equal" const arguments and so are "equal" associated types, and yet when normalized during codegen in different crates actually wind up as different types. + +Notably this does *not* extend to const *functions*, as the type system only works with the results of const *items* it's actually fine for const functions to be non deterministic so long as that doesn't affect the final value of a const item. [coherence chapter]: ../coherence.md diff --git a/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md new file mode 100644 index 000000000000..bf0d4fe3a11b --- /dev/null +++ b/src/doc/rustc-dev-guide/src/solve/sharing-crates-with-rust-analyzer.md @@ -0,0 +1,192 @@ +# Sharing the trait solver with rust-analyzer + +rust-analyzer can be viewed as a compiler frontend: it performs tasks similar to the parts of rustc +that run before code generation, such as parsing, lexing, AST construction and lowering, HIR +lowering, and even limited MIR building and const evaluation. + +However, because rust-analyzer is primarily a language server, its architecture differs in several +important ways from that of rustc. +Despite these differences, a substantial portion of its responsibilities—most notably type +inference and trait solving—overlap with the compiler. + +To avoid duplication and to maintain consistency between the two implementations, rust-analyzer +reuses several crates from rustc, relying on shared abstractions wherever possible. + +## Shared Crates + +Currently, rust-analyzer depends on several `rustc_*` crates from the compiler: + +- `rustc_abi` +- `rustc_ast_ir` +- `rustc_index` +- `rustc_lexer` +- `rustc_next_trait_solver` +- `rustc_parse_format` +- `rustc_pattern_analysis` +- `rustc_type_ir` + +Since these crates are not published on `crates.io` as part of the compiler's normal distribution +process, rust-analyzer maintains its own publishing pipeline. +It uses the [rustc-auto-publish script][rustc-auto-publish] to publish these crates to `crates.io` +with the prefix `ra-ap-rustc_*` +(for example: https://crates.io/crates/ra-ap-rustc_next_trait_solver). +rust-analyzer then depends on these re-published crates in its own build. + +For trait solving specifically, the primary shared crates are `rustc_type_ir` and +`rustc_next_trait_solver`, which provide the core IR and solver logic used by both compiler +frontends. + +## The Abstraction Layer + +Because rust-analyzer is a language server, it must handle frequently changing source code and +partially invalid or incomplete source codes. +This requires an infrastructure quite different from rustc's, especially in the layers between +the source code and the HIR—for example, `Ty` and its backing interner. + +To bridge these differences, the compiler provides `rustc_type_ir` as an abstraction layer shared +between rustc and rust-analyzer. +This crate defines the fundamental interfaces used to represent types, predicates, and the context +required by the trait solver. +Both rustc and rust-analyzer implement these traits for their own concrete type representations, +and `rustc_next_trait_solver` is written to be generic over these abstractions. + +In addition to these interfaces, `rustc_type_ir` also includes several non-trivial components built +on top of the abstraction layer—such as elaboration logic and the search graph machinery used by the +solver. + +## Design Concepts + +`rustc_next_trait_solver` is intended to depend only on the abstract interfaces defined in +`rustc_type_ir`. +To support this, the type-system traits in `rustc_type_ir` must expose every interface the solver +requires—for example, [creating a new inference type variable][ir new_infer] +([rustc][rustc new_infer], [rust-analyzer][r-a new_infer]). +For items that do not need compiler-specific representations, `rustc_type_ir` defines them directly +as structs or enums parameterized over these traits—for example, [`TraitRef`][ir tr]. + +The following are some notable items from the `rustc_type_ir` crate. + +### `trait Interner` + +The central trait in this design is [`Interner`][ir interner], which specifies all +implementation-specific details for both rustc and rust-analyzer. +Among its essential responsibilities: + +- it **specifies** the concrete types used by the implementation via its + [associated types][ir interner assocs]; these form the backbone of how each compiler frontend + instantiates the shared IR, +- it provides the context required by the solver (e.g., querying [lang items][ir require_lang_item], + enumerating [all blanket impls for a trait][ir for_each_blanket_impl]); +- and it must implement [`IrPrint`][ir irprint] for formatting and tracing. + In practice, these `IrPrint` impls simply route to existing formatting logic inside rustc or + rust-analyzer. + +In rustc, [`TyCtxt` implements `Interner`][rustc interner impl]: it exposes the rustc's query +methods, and the required `Interner` trait methods are implemented by invoking those queries. +In rust-analyzer, the implementing type is named [`DbInterner`][r-a interner impl] (as it performs +most interning through the [salsa] database), and most of its methods are backed by salsa queries +rather than rustc queries. + +### `mod inherent` + +Another notable item in `rustc_type_ir` is the [`inherent` module][ir inherent]. +This module provides *forward definitions* of inherent methods—expressed as traits—corresponding to +methods that exist on compiler-specific types such as `Ty` or `GenericArg`. +These definitions allow the generic crates (such as `rustc_next_trait_solver`) to call methods that +are implemented differently in rustc and rust-analyzer. + +Code in generic crates should import these definitions with: + +```rust +use inherent::*; +``` + +These forward definitions **must never be used inside the concrete implementations themselves**. +Crates that implement the traits from `mod inherent` should call the actual inherent methods on +their concrete types once those types are nameable. + +You can find rustc’s implementations of these traits in the +[rustc_middle::ty::inherent][rustc inherent impl] module. +For rust-analyzer, the corresponding implementations are located across several modules under +`hir_ty::next_solver`, such as [hir_ty::next_solver::region][r-a inherent impl]. + +### `trait InferCtxtLike` and `trait SolverDelegate` + +These two traits correspond to the role of [`InferCtxt`][rustc inferctxt] in rustc. + +[`InferCtxtLike`][ir inferctxtlike] must be defined in `rustc_infer` due to coherence +constraints(orphan rules). +As a result, it cannot provide functionality that lives in `rustc_trait_selection`. +Instead, behavior that depends on trait-solving logic is abstracted into a separate trait, +[`SolverDelegate`][ir solverdelegate]. +Its implementator in rustc is [simply a newtype struct over `InferCtxt`][rustc solverdelegate impl] +in `rustc_trait_selection`. + +(In rust-analyzer, it is also implemented for a newtype wrapper over its own +[`InferCtxt`][r-a inferctxtlike impl], primarily to mirror rustc’s structure, although this is not +strictly necessary because all solver-related logic already resides in the `hir-ty` crate.) + +In the long term, the ideal design is to move all of the logic currently expressed through +`SolverDelegate` into `rustc_next_trait_solver`, with any required core operations added directly to +`InferCtxtLike`. +This would allow more of the solver’s behavior to live entirely inside the shared solver crate. + +### `rustc_type_ir::search_graph::{Cx, Delegate}` + +The abstraction traits [`Cx`][ir searchgraph cx impl] and [`Delegate`][ir searchgraph delegate impl] +are already implemented within `rustc_next_trait_solver` itself. +Therefore, users of the shared crates—both rustc and rust-analyzer—do not need to provide their own +implementations. + +These traits exist primarily to support fuzzing of the search graph independently of the full trait +solver. +This infrastructure is used by the external fuzzing project: +. + +## Long-term plans for supporting rust-analyzer + +In general, we aim to support rust-analyzer just as well as rustc in these shared crates—provided +doing so does not substantially harm rustc's performance or maintainability. +(e.g., [#145377][pr 145377], [#146111][pr 146111], [#146182][pr 146182] and [#147723][pr 147723]) + +Shared crates that require nightly-only features must guard such code behind a `nightly` feature +flag, since rust-analyzer is built with the stable toolchain. + +Looking forward, we plan to uplift more shared logic into `rustc_type_ir`. +There are still duplicated implementations between rustc and rust-analyzer—such as `ObligationCtxt` +([rustc][rustc oblctxt], [rust-analyzer][r-a oblctxt]) and type coercion logic +([rustc][rustc coerce], [rust-analyzer][r-a coerce])—that we would like to unify over time. + +[rustc-auto-publish]: https://github.com/rust-analyzer/rustc-auto-publish +[ir new_infer]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/inherent/trait.Ty.html#tymethod.new_infer +[rustc new_infer]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_middle/src/ty/sty.rs#L413-L420 +[r-a new_infer]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/next_solver/ty.rs#L59-L92 +[ir tr]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/struct.TraitRef.html +[ir interner]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html +[ir interner assocs]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#required-associated-types +[ir require_lang_item]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#tymethod.require_lang_item +[ir for_each_blanket_impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.Interner.html#tymethod.for_each_blanket_impl +[ir irprint]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/ir_print/trait.IrPrint.html +[rustc interner impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#impl-Interner-for-TyCtxt%3C'tcx%3E +[r-a interner impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/interner.rs +[salsa]: https://github.com/salsa-rs/salsa +[ir inherent]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/inherent/index.html +[rustc inherent impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_middle/ty/inherent/index.html +[r-a inherent impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/region.rs +[ir inferctxtlike]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_type_ir/trait.InferCtxtLike.html +[rustc inferctxt]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html +[rustc inferctxtlike impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_infer/infer/context.rs.html#14-332 +[r-a inferctxtlike impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/infer/context.rs +[ir solverdelegate]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_next_trait_solver/delegate/trait.SolverDelegate.html +[rustc solverdelegate impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/rustc_trait_selection/solve/delegate/struct.SolverDelegate.html +[r-a solverdelegate impl]: https://github.com/rust-lang/rust-analyzer/blob/a50c1ccc9cf3dab1afdc857a965a9992fbad7a53/crates/hir-ty/src/next_solver/solver.rs#L27-L330 +[ir searchgraph cx impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_type_ir/interner.rs.html#550-575 +[ir searchgraph delegate impl]: https://doc.rust-lang.org/1.91.1/nightly-rustc/src/rustc_next_trait_solver/solve/search_graph.rs.html#20-123 +[pr 145377]: https://github.com/rust-lang/rust/pull/145377 +[pr 146111]: https://github.com/rust-lang/rust/pull/146111 +[pr 146182]: https://github.com/rust-lang/rust/pull/146182 +[pr 147723]: https://github.com/rust-lang/rust/pull/147723 +[rustc oblctxt]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_trait_selection/src/traits/engine.rs#L48-L386 +[r-a oblctxt]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/next_solver/obligation_ctxt.rs +[rustc coerce]: https://github.com/rust-lang/rust/blob/63b1db05801271e400954e41b8600a3cf1482363/compiler/rustc_hir_typeck/src/coercion.rs +[r-a coerce]: https://github.com/rust-lang/rust-analyzer/blob/34f47d9298c478c12c6c4c0348771d1b05706e09/crates/hir-ty/src/infer/coerce.rs \ No newline at end of file diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index a30af309a54e..e132946ae83e 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -447,23 +447,6 @@ More information is available in the [toolstate documentation]. [rust-toolstate]: https://rust-lang-nursery.github.io/rust-toolstate [toolstate documentation]: https://forge.rust-lang.org/infra/toolstate.html -## Public CI dashboard - -To monitor the Rust CI, you can have a look at the [public dashboard] maintained by the infra team. - -These are some useful panels from the dashboard: - -- Pipeline duration: check how long the auto builds take to run. -- Top slowest jobs: check which jobs are taking the longest to run. -- Change in median job duration: check what jobs are slowest than before. Useful - to detect regressions. -- Top failed jobs: check which jobs are failing the most. - -To learn more about the dashboard, see the [Datadog CI docs]. - -[Datadog CI docs]: https://docs.datadoghq.com/continuous_integration/ -[public dashboard]: https://p.datadoghq.com/sb/3a172e20-e9e1-11ed-80e3-da7ad0900002-b5f7bb7e08b664a06b08527da85f7e30 - ## Determining the CI configuration If you want to determine which `bootstrap.toml` settings are used in CI for a diff --git a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md index 4325cc58797f..7a1c5b9a98c0 100644 --- a/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md +++ b/src/doc/rustc-dev-guide/src/tests/codegen-backend-tests/cg_gcc.md @@ -77,6 +77,26 @@ will be downloaded from CI or built locally. The default value is `true`, which will download GCC from CI if there are no local changes to the GCC sources and the given host target is available on CI. +## Providing your own GCC + +There are cases where you will want to provide yourself the `libgccjit.so` file. +One such case is when you want to cross-compile `rustc` to another target since GCC is not a multi-target compiler. +To support this use case, there is the bootstrap option `gcc.libgccjit-libs-dir`. +This option override `gcc.download-ci-gcc`, meaning `libgccjit.so` won't be downloaded or built locally by bootstrap. +The directory structure of this directory is `//libgccjit.so`, for instance: + +``` +. +├── m68k-unknown-linux-gnu +│ └── m68k-unknown-linux-gnu +│ └── libgccjit.so +└── x86_64-unknown-linux-gnu + ├── m68k-unknown-linux-gnu + │ └── libgccjit.so + └── x86_64-unknown-linux-gnu + └── libgccjit.so +``` + ## Running tests of the backend itself In addition to running the compiler's test suites using the GCC codegen backend, you can also run the test suite of the backend itself. diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index a691380234cd..e0924f8538ef 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -90,7 +90,8 @@ The following test suites are available, with links for more information: | `rustdoc-ui` | Check terminal output of `rustdoc` ([see also](ui.md)) | Some rustdoc-specific tests can also be found in `ui/rustdoc/`. -These check rustdoc-related or -specific lints that (also) run as part of `rustc`, not (only) `rustdoc`. +These tests ensure that certain lints that are emitted as part of executing rustdoc +are also run when executing rustc. Run-make tests pertaining to rustdoc are typically named `run-make/rustdoc-*/`. [rustdoc-html-tests]: ../rustdoc-internals/rustdoc-test-suite.md diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 7cf5869b719c..c8b0072dc101 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -189,6 +189,10 @@ settings: assertions. - `needs-std-debug-assertions` — ignores if std was not built with debug assertions. +- `ignore-std-remap-debuginfo` — ignores if std was built with remapping of + it's sources. +- `needs-std-remap-debugino` — ignores if std was not built with remapping of + it's sources. - `ignore-rustc-debug-assertions` — ignores if rustc was built with debug assertions. - `needs-rustc-debug-assertions` — ignores if rustc was not built with debug diff --git a/src/doc/rustc-dev-guide/src/ty_module/instantiating_binders.md b/src/doc/rustc-dev-guide/src/ty_module/instantiating_binders.md index 0d1108c72e0e..7e29b9543714 100644 --- a/src/doc/rustc-dev-guide/src/ty_module/instantiating_binders.md +++ b/src/doc/rustc-dev-guide/src/ty_module/instantiating_binders.md @@ -1,6 +1,6 @@ # Instantiating `Binder`s -Much like [`EarlyBinder`], when accessing the inside of a [`Binder`] we must first discharge it by replacing the bound vars with some other value. This is for much the same reason as with `EarlyBinder`, types referencing parameters introduced by the `Binder` do not make any sense outside of that binder, for example: +Much like [`EarlyBinder`], when accessing the inside of a [`Binder`] we must first discharge it by replacing the bound vars with some other value. This is for much the same reason as with `EarlyBinder`, types referencing parameters introduced by the `Binder` do not make any sense outside of that binder. See the following erroring example: ```rust,ignore fn foo<'a>(a: &'a u32) -> &'a u32 { a @@ -11,10 +11,11 @@ fn bar(a: fn(&u32) -> T) -> T { fn main() { let higher_ranked_fn_ptr = foo as for<'a> fn(&'a u32) -> &'a u32; + // Attempt to infer `T=for<'a> &'a u32` which is not satsifiable let references_bound_vars = bar(higher_ranked_fn_ptr); } ``` -In this example we are providing an argument of type `for<'a> fn(&'^0 u32) -> &'^0 u32` to `bar`, we do not want to allow `T` to be inferred to the type `&'^0 u32` as it would be rather nonsensical (and likely unsound if we did not happen to ICE, `main` has no idea what `'a` is so how would the borrow checker handle a borrow with lifetime `'a`). +In this example we are providing an argument of type `for<'a> fn(&'^0 u32) -> &'^0 u32` to `bar`, we do not want to allow `T` to be inferred to the type `&'^0 u32` as it would be rather nonsensical (and likely unsound if we did not happen to ICE). `main` doesn't know about `'a` so the borrow checker would not be able to handle a borrow with lifetime `'a`. Unlike `EarlyBinder` we typically do not instantiate `Binder` with some concrete set of arguments from the user, i.e. `['b, 'static]` as arguments to a `for<'a1, 'a2> fn(&'a1 u32, &'a2 u32)`. Instead we usually instantiate the binder with inference variables or placeholders. diff --git a/src/doc/rustc-dev-guide/triagebot.toml b/src/doc/rustc-dev-guide/triagebot.toml index 4a8852391520..fbfe74234148 100644 --- a/src/doc/rustc-dev-guide/triagebot.toml +++ b/src/doc/rustc-dev-guide/triagebot.toml @@ -53,10 +53,6 @@ allow-unauthenticated = [ # Documentation at: https://forge.rust-lang.org/triagebot/note.html [note] -# Prevents mentions in commits to avoid users being spammed -# Documentation at: https://forge.rust-lang.org/triagebot/no-mentions.html -[no-mentions] - # Canonicalize issue numbers to avoid closing the wrong issue # when commits are included in subtrees, as well as warning links in commits. # Documentation at: https://forge.rust-lang.org/triagebot/issue-links.html diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 832b5a69d47c..3b8852f8ff9f 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -112,6 +112,7 @@ - [riscv32i\*-unknown-none-elf](platform-support/riscv32-unknown-none-elf.md) - [riscv32im-risc0-zkvm-elf](platform-support/riscv32im-risc0-zkvm-elf.md) - [riscv32imac-unknown-xous-elf](platform-support/riscv32imac-unknown-xous-elf.md) + - [riscv64im-unknown-none-elf](platform-support/riscv64im-unknown-none-elf.md) - [riscv64gc-unknown-linux-gnu](platform-support/riscv64gc-unknown-linux-gnu.md) - [riscv64gc-unknown-linux-musl](platform-support/riscv64gc-unknown-linux-musl.md) - [riscv64a23-unknown-linux-gnu](platform-support/riscv64a23-unknown-linux-gnu.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d772702df76e..5164ab69d353 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -184,6 +184,7 @@ target | std | notes [`riscv32imc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA) [`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | ✓ |RISC-V Linux (kernel 4.20+, musl 1.2.5) `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA) +[`riscv64im-unknown-none-elf`](platform-support/riscv64im-unknown-none-elf.md) | * | Bare RISC-V (RV64IM ISA) `riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA) `sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4+, glibc 2.23) [`thumbv6m-none-eabi`](platform-support/thumbv6m-none-eabi.md) | * | Bare Armv6-M diff --git a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md index 16152dd2dad5..dbdb96283a5f 100644 --- a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md +++ b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md @@ -20,8 +20,8 @@ The format of binaries is a linked ELF. Binaries must be built with no-std. They can use `core` and `alloc` (`alloc` only if an allocator is supplied). -At least one function needs to use the `"gpu-kernel"` calling convention and should be marked with `no_mangle` for simplicity. -Functions using the `"gpu-kernel"` calling convention are kernel entrypoints and can be used from the host runtime. +At least one function should use the `"gpu-kernel"` calling convention and should be marked with `no_mangle` or `export_name`. +Functions using the `"gpu-kernel"` calling convention are kernel entrypoints and can be launched from the host runtime. ## Building the target @@ -34,6 +34,9 @@ The generations are exposed as different target-cpus in the backend. As there are many, Rust does not ship pre-compiled libraries for this target. Therefore, you have to build your own copy of `core` by using `cargo -Zbuild-std=core` or similar. +An allocator and `println!()` support is provided by the [`amdgpu-device-libs`] crate. +Both features rely on the [HIP] runtime. + To build a binary, create a no-std library: ```rust,ignore (platform-specific) // src/lib.rs @@ -65,6 +68,8 @@ lto = true The target-cpu must be from the list [supported by LLVM] (or printed with `rustc --target amdgcn-amd-amdhsa --print target-cpus`). The GPU version on the current system can be found e.g. with [`rocminfo`]. +For a GPU series that has xnack support but the target GPU has not, the `-xnack-support` target-feature needs to be enabled. +I.e. if the ISA info as printed with [`rocminfo`] says something about `xnack-`, e.g. `gfx1010:xnack-`, add `-Ctarget-feature=-xnack-support` to the rustflags. Example `.cargo/config.toml` file to set the target and GPU generation: ```toml @@ -72,6 +77,7 @@ Example `.cargo/config.toml` file to set the target and GPU generation: [build] target = "amdgcn-amd-amdhsa" rustflags = ["-Ctarget-cpu=gfx1100"] +# Add "-Ctarget-feature=-xnack-support" for xnack- GPUs (see above) [unstable] build-std = ["core"] # Optional: "alloc" @@ -85,8 +91,6 @@ Example code on how to load a compiled binary and run it is available in [ROCm e On Linux, binaries can also run through the HSA runtime as implemented in [ROCR-Runtime]. - - tests/ui/crashes/ice-9041.rs:5:19 + --> tests/ui/crashes/ice-9041.rs:6:19 | LL | things.iter().find(|p| is_thing_ready(p)).is_some() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|p| is_thing_ready(&p))` diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed index 1213fdcf6119..daae41c0c891 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed @@ -311,19 +311,23 @@ mod issue9120 { } } +// skip this test due to rust-lang/rust-clippy#16086 +/* #[allow(clippy::match_like_matches_macro)] fn issue15102() { let values = [None, Some(3)]; - let has_even = values.iter().any(|v| matches!(&v, Some(x) if x % 2 == 0)); - //~^ search_is_some + let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); + ~^ search_is_some println!("{has_even}"); let has_even = values .iter() - .any(|v| match &v { - //~^ search_is_some + .find(|v| match v { + ~^ search_is_some Some(x) if x % 2 == 0 => true, _ => false, - }); + }) + .is_some(); println!("{has_even}"); } +*/ diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs index 4294a39333f2..ead101a491a6 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs @@ -322,20 +322,23 @@ mod issue9120 { } } +// skip this test due to rust-lang/rust-clippy#16086 +/* #[allow(clippy::match_like_matches_macro)] fn issue15102() { let values = [None, Some(3)]; let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); - //~^ search_is_some + ~^ search_is_some println!("{has_even}"); let has_even = values .iter() .find(|v| match v { - //~^ search_is_some + ~^ search_is_some Some(x) if x % 2 == 0 => true, _ => false, }) .is_some(); println!("{has_even}"); } +*/ diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr index cee1eb08876b..c56fe859aac0 100644 --- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr +++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr @@ -346,32 +346,5 @@ error: called `is_some()` after searching an `Iterator` with `find` LL | let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|x: &u32| (*arg_no_deref_dyn)(&x))` -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:328:34 - | -LL | let has_even = values.iter().find(|v| matches!(v, Some(x) if x % 2 == 0)).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|v| matches!(&v, Some(x) if x % 2 == 0))` - -error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/search_is_some_fixable_some.rs:334:10 - | -LL | .find(|v| match v { - | __________^ -LL | | -LL | | Some(x) if x % 2 == 0 => true, -LL | | _ => false, -LL | | }) -LL | | .is_some(); - | |__________________^ - | -help: consider using - | -LL ~ .any(|v| match &v { -LL + -LL + Some(x) if x % 2 == 0 => true, -LL + _ => false, -LL ~ }); - | - -error: aborting due to 51 previous errors +error: aborting due to 49 previous errors diff --git a/src/tools/clippy/tests/ui/unit_arg.stderr b/src/tools/clippy/tests/ui/unit_arg.stderr index 0dcfb02b9fa0..eb7c56c45b59 100644 --- a/src/tools/clippy/tests/ui/unit_arg.stderr +++ b/src/tools/clippy/tests/ui/unit_arg.stderr @@ -204,21 +204,18 @@ error: passing a unit value to a function | LL | fn_take_unit(mac!(def)); | ^^^^^^^^^^^^^^^^^^^^^^^ - | error: passing a unit value to a function --> tests/ui/unit_arg.rs:173:5 | LL | fn_take_unit(mac!(func Default::default)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | error: passing a unit value to a function --> tests/ui/unit_arg.rs:175:5 | LL | fn_take_unit(mac!(nonempty_block Default::default())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | error: aborting due to 13 previous errors diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index cd0e79f5ab3f..9473e51688cb 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -438,6 +438,11 @@ pub struct Config { /// FIXME: make it clearer that this refers to the staged `std`, not stage 0 `std`. pub with_std_debug_assertions: bool, + /// Whether *staged* `std` was built with remapping of debuginfo. + /// + /// FIXME: make it clearer that this refers to the staged `std`, not stage 0 `std`. + pub with_std_remap_debuginfo: bool, + /// Only run tests that match these filters (using `libtest` "test name contains" filter logic). /// /// FIXME(#139660): the current hand-rolled test executor intentionally mimics the `libtest` diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs index b0d0372454e2..4a1a74ddfe1b 100644 --- a/src/tools/compiletest/src/debuggers.rs +++ b/src/tools/compiletest/src/debuggers.rs @@ -2,7 +2,7 @@ use std::env; use std::process::Command; use std::sync::Arc; -use camino::{Utf8Path, Utf8PathBuf}; +use camino::Utf8Path; use crate::common::{Config, Debugger}; @@ -54,51 +54,6 @@ pub(crate) fn configure_lldb(config: &Config) -> Option> { Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() })) } -/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing. -fn is_pc_windows_msvc_target(target: &str) -> bool { - target.ends_with("-pc-windows-msvc") -} - -/// FIXME: this is very questionable... -fn find_cdb(target: &str) -> Option { - if !(cfg!(windows) && is_pc_windows_msvc_target(target)) { - return None; - } - - let pf86 = Utf8PathBuf::from_path_buf( - env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?.into(), - ) - .unwrap(); - let cdb_arch = if cfg!(target_arch = "x86") { - "x86" - } else if cfg!(target_arch = "x86_64") { - "x64" - } else if cfg!(target_arch = "aarch64") { - "arm64" - } else if cfg!(target_arch = "arm") { - "arm" - } else { - return None; // No compatible CDB.exe in the Windows 10 SDK - }; - - let mut path = pf86; - path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too? - path.push(cdb_arch); - path.push(r"cdb.exe"); - - if !path.exists() { - return None; - } - - Some(path) -} - -/// Returns Path to CDB -pub(crate) fn discover_cdb(cdb: Option, target: &str) -> Option { - let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target)); - cdb -} - pub(crate) fn query_cdb_version(cdb: &Utf8Path) -> Option<[u16; 4]> { let mut version = None; if let Ok(output) = Command::new(cdb).arg("/version").output() { diff --git a/src/tools/compiletest/src/directives/cfg.rs b/src/tools/compiletest/src/directives/cfg.rs index 62d10f14b98f..e89c1330f467 100644 --- a/src/tools/compiletest/src/directives/cfg.rs +++ b/src/tools/compiletest/src/directives/cfg.rs @@ -202,6 +202,11 @@ pub(crate) fn prepare_conditions(config: &Config) -> PreparedConditions { config.with_std_debug_assertions, "when std is built with debug assertions", ); + builder.cond( + "std-remap-debuginfo", + config.with_std_remap_debuginfo, + "when std is built with remapping of debuginfo", + ); for &debugger in Debugger::STR_VARIANTS { builder.cond( diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index e9824edfef60..282e2a131d93 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -184,6 +184,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-sanitizer-support", "needs-sanitizer-thread", "needs-std-debug-assertions", + "needs-std-remap-debuginfo", "needs-subprocess", "needs-symlink", "needs-target-has-atomic", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 92b596c70ae9..36c2ca62cd87 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -181,6 +181,11 @@ pub(super) fn handle_needs( condition: config.with_std_debug_assertions, ignore_reason: "ignored if std wasn't built with debug assertions", }, + Need { + name: "needs-std-remap-debuginfo", + condition: config.with_std_remap_debuginfo, + ignore_reason: "ignored if std wasn't built with remapping of debuginfo", + }, Need { name: "needs-target-std", condition: build_helper::targets::target_supports_std(&config.target), diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 90e2cb77e304..93f1041a8f41 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -117,6 +117,7 @@ struct ConfigBuilder { profiler_runtime: bool, rustc_debug_assertions: bool, std_debug_assertions: bool, + std_remap_debuginfo: bool, } impl ConfigBuilder { @@ -185,6 +186,11 @@ impl ConfigBuilder { self } + fn std_remap_debuginfo(&mut self, is_enabled: bool) -> &mut Self { + self.std_remap_debuginfo = is_enabled; + self + } + fn build(&mut self) -> Config { let args = &[ "compiletest", @@ -246,6 +252,9 @@ impl ConfigBuilder { if self.std_debug_assertions { args.push("--with-std-debug-assertions".to_owned()); } + if self.std_remap_debuginfo { + args.push("--with-std-remap-debuginfo".to_owned()); + } args.push("--rustc-path".to_string()); args.push(std::env::var("TEST_RUSTC").expect("must be configured by bootstrap")); @@ -400,6 +409,19 @@ fn std_debug_assertions() { assert!(check_ignore(&config, "//@ ignore-std-debug-assertions")); } +#[test] +fn std_remap_debuginfo() { + let config: Config = cfg().std_remap_debuginfo(false).build(); + + assert!(check_ignore(&config, "//@ needs-std-remap-debuginfo")); + assert!(!check_ignore(&config, "//@ ignore-std-remap-debuginfo")); + + let config: Config = cfg().std_remap_debuginfo(true).build(); + + assert!(!check_ignore(&config, "//@ needs-std-remap-debuginfo")); + assert!(check_ignore(&config, "//@ ignore-std-remap-debuginfo")); +} + #[test] fn stage() { let config: Config = cfg().stage(1).stage_id("stage1-x86_64-unknown-linux-gnu").build(); diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 71dad3633793..83aee865e010 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -106,6 +106,7 @@ fn parse_config(args: Vec) -> Config { .optflag("", "has-enzyme", "run tests that require enzyme") .optflag("", "with-rustc-debug-assertions", "whether rustc was built with debug assertions") .optflag("", "with-std-debug-assertions", "whether std was built with debug assertions") + .optflag("", "with-std-remap-debuginfo", "whether std was built with remapping") .optmulti( "", "skip", @@ -263,7 +264,7 @@ fn parse_config(args: Vec) -> Config { let adb_device_status = target.contains("android") && adb_test_dir.is_some(); // FIXME: `cdb_version` is *derived* from cdb, but it's *not* technically a config! - let cdb = debuggers::discover_cdb(matches.opt_str("cdb"), &target); + let cdb = matches.opt_str("cdb").map(Utf8PathBuf::from); let cdb_version = cdb.as_deref().and_then(debuggers::query_cdb_version); // FIXME: `gdb_version` is *derived* from gdb, but it's *not* technically a config! let gdb = matches.opt_str("gdb").map(Utf8PathBuf::from); @@ -295,6 +296,7 @@ fn parse_config(args: Vec) -> Config { let run_ignored = matches.opt_present("ignored"); let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions"); let with_std_debug_assertions = matches.opt_present("with-std-debug-assertions"); + let with_std_remap_debuginfo = matches.opt_present("with-std-remap-debuginfo"); let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode"); let has_enzyme = matches.opt_present("has-enzyme"); let filters = if mode == TestMode::RunMake { @@ -402,6 +404,7 @@ fn parse_config(args: Vec) -> Config { run_ignored, with_rustc_debug_assertions, with_std_debug_assertions, + with_std_remap_debuginfo, filters, skip: matches.opt_strs("skip"), filter_exact: matches.opt_present("exact"), diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 68574eb4bfd6..ba3a12347367 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -243,6 +243,12 @@ impl TestCx<'_> { cmd.env("__STD_DEBUG_ASSERTIONS_ENABLED", "1"); } + cmd.env_remove("__STD_REMAP_DEBUGINFO_ENABLED"); + if self.config.with_std_remap_debuginfo { + // Used for `run_make_support::env::std_remap_debuginfo_enabled`. + cmd.env("__STD_REMAP_DEBUGINFO_ENABLED", "1"); + } + // We don't want RUSTFLAGS set from the outside to interfere with // compiler flags set in the test cases: cmd.env_remove("RUSTFLAGS"); diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index c30f3d1e10ea..2b057bb35519 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -83,6 +83,7 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { run_ignored: Default::default(), with_rustc_debug_assertions: Default::default(), with_std_debug_assertions: Default::default(), + with_std_remap_debuginfo: Default::default(), filters: Default::default(), skip: Default::default(), filter_exact: Default::default(), diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 073ad267476c..1995300c5bcb 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -351,7 +351,7 @@ you need to pull rustc changes into Miri first, and then re-do the rustc push. If this fails due to authentication problems, it can help to make josh push via ssh instead of https. Add the following to your `.gitconfig`: -```toml +```text [url "git@github.com:"] pushInsteadOf = https://github.com/ ``` diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 32494141589a..86f190134d45 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -651,6 +651,11 @@ Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows * [Stacked Borrows: An Aliasing Model for Rust](https://plv.mpi-sws.org/rustbelt/stacked-borrows/) * [Using Lightweight Formal Methods to Validate a Key-Value Storage Node in Amazon S3](https://www.amazon.science/publications/using-lightweight-formal-methods-to-validate-a-key-value-storage-node-in-amazon-s3) * [SyRust: Automatic Testing of Rust Libraries with Semantic-Aware Program Synthesis](https://dl.acm.org/doi/10.1145/3453483.3454084) +* [Crabtree: Rust API Test Synthesis Guided by Coverage and Type](https://dl.acm.org/doi/10.1145/3689733) +* [Rustlantis: Randomized Differential Testing of the Rust Compiler](https://dl.acm.org/doi/10.1145/3689780) +* [A Study of Undefined Behavior Across Foreign Function Boundaries in Rust Libraries](https://arxiv.org/abs/2404.11671) +* [Tree Borrows](https://plf.inf.ethz.ch/research/pldi25-tree-borrows.html) +* [Miri: Practical Undefined Behavior Detection for Rust](https://plf.inf.ethz.ch/research/popl26-miri.html) **(this paper describes Miri itself)** ## License diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 7da7a3d18948..e9d4849bc596 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -1,7 +1,6 @@ # **(WIP)** Documentation for Miri-GenMC -**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).** - +**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572) and [other limitations](#limitations). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).** [GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program. Miri-GenMC integrates that model checker into Miri. @@ -12,11 +11,14 @@ This includes all possible thread interleavings and all allowed return values fo It is hence still possible to have latent bugs in a test case even if they passed GenMC.) GenMC requires the input program to be bounded, i.e., have finitely many possible executions, otherwise it will not terminate. -Any loops that may run infinitely must be replaced or bounded (see below). +Any loops that may run infinitely must be replaced or bounded (see [below](#eliminating-unbounded-loops)). GenMC makes use of Dynamic Partial Order Reduction (DPOR) to reduce the number of executions that must be explored, but the runtime can still be super-exponential in the size of the input program (number of threads and amount of interaction between threads). Large programs may not be verifiable in a reasonable amount of time. +GenMC currently only supports Linux hosts. +Both the host and the target must be 64-bit little-endian. + ## Usage For testing/developing Miri-GenMC: @@ -50,16 +52,24 @@ Note that `cargo miri test` in GenMC mode is currently not supported. - `debug2`: Print the execution graph after every memory access. - `debug3`: Print reads-from values considered by GenMC. - `-Zmiri-genmc-verbose`: Show more information, such as estimated number of executions, and time taken for verification. - -#### Regular Miri parameters useful for GenMC mode - - `-Zmiri-disable-weak-memory-emulation`: Disable any weak memory effects (effectively upgrading all atomic orderings in the program to `SeqCst`). This option may reduce the number of explored program executions, but any bugs related to weak memory effects will be missed. This option can help determine if an error is caused by weak memory effects (i.e., if it disappears with this option enabled). -## Tips +## Limitations - +There are several limitations which can make GenMC miss bugs: +- GenMC does not support re-using freed memory for new allocations, so any bugs related to that will be missed. +- GenMC does not support `compare_exchange_weak`, so the consequences of spurious failures are not explored. + A warning will be emitted if this affects code you wrote (but not if it happens inside your dependencies). +- GenMC does not support the separate failure ordering of `compare_exchange`. Miri will take the maximum of the success and failure ordering and use that for the access; outcomes that rely on the real ordering being weaker will not be explored. + A warning will be emitted if this affects code you wrote (but not if it happens inside your dependencies). +- GenMC is incompatible with borrow tracking (Stacked/Tree Borrows). You need to set `-Zmiri-disable-stacked-borrows` to use GenMC. +- Like all C++ memory model verification tools, GenMC has to solve the [out-of-thin-air problem](https://www.cl.cam.ac.uk/~pes20/cpp/notes42.html). + It takes the [usual approach](https://plv.mpi-sws.org/scfix/paper.pdf) of requiring the union of "program-order" and "reads-from" to be acyclic. + This means it excludes certain behaviors allowed by the C++ memory model, some of which can occur on hardware that performs load buffering. + +## Tips ### Eliminating unbounded loops @@ -121,24 +131,11 @@ fn count_until_true_genmc(flag: &AtomicBool) -> u64 { -## Limitations - -Some or all of these limitations might get removed in the future: - -- Borrow tracking is currently incompatible (stacked/tree borrows). -- Only Linux is supported for now. -- No support for 32-bit or big-endian targets. -- No cross-target interpretation. - - - ## Development GenMC is written in C++, which complicates development a bit. The prerequisites for building Miri-GenMC are: -- A compiler with C++23 support. -- LLVM developments headers and clang. - +- A compiler with sufficient C++20 support (we are testing GCC 13). The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with its own maintainers). These sources need to be available to build Miri-GenMC. @@ -149,6 +146,8 @@ The process for obtaining them is as follows: If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid formatting the Rust files inside that folder. + + ### Formatting the C++ code For formatting the C++ code we provide a `.clang-format` file in the `genmc-sys` directory. @@ -157,7 +156,3 @@ With `clang-format` installed, run this command to format the c++ files (replace find ./genmc-sys/cpp/ -name "*.cpp" -o -name "*.hpp" | xargs clang-format --style=file:"./genmc-sys/.clang-format" -i ``` NOTE: this is currently not done automatically on pull requests to Miri. - - - - diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b6a1415f1834..e79ad9362c08 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -36b2369c91d32c2659887ed6fe3d570640f44fd2 +dc47a69ed94bc88b10b7d500cceacf29b87bcbbe diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index e8d97491acaf..a21898c506ab 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -826,15 +826,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { // FIXME: If we cannot determine the size (because the unsized tail is an `extern type`), // bail out -- we cannot reasonably figure out which memory range to reborrow. // See https://github.com/rust-lang/unsafe-code-guidelines/issues/276. - let size = match size { - Some(size) => size, - None => { - static DEDUP: AtomicBool = AtomicBool::new(false); - if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { - this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); - } - return interp_ok(place.clone()); + let Some(size) = size else { + static DEDUP: AtomicBool = AtomicBool::new(false); + if !DEDUP.swap(true, std::sync::atomic::Ordering::Relaxed) { + this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow); } + return interp_ok(place.clone()); }; // Compute new borrow. diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index a91f35a9dcad..055baca8542f 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -77,6 +77,32 @@ pub struct Event { pub span: Span, } +/// Diagnostics data about the current access and the location we are accessing. +/// Used to create history events and errors. +#[derive(Clone, Debug)] +pub struct DiagnosticInfo { + pub alloc_id: AllocId, + pub span: Span, + /// The range the diagnostic actually applies to. + /// This is always a subset of `access_range`. + pub transition_range: Range, + /// The range the access is happening to. Is `None` if this is the protector release access + pub access_range: Option, + pub access_cause: AccessCause, +} +impl DiagnosticInfo { + /// Creates a history event. + pub fn create_event(&self, transition: PermTransition, is_foreign: bool) -> Event { + Event { + transition, + is_foreign, + access_cause: self.access_cause, + access_range: self.access_range, + transition_range: self.transition_range.clone(), + span: self.span, + } + } +} /// List of all events that affected a tag. /// NOTE: not all of these events are relevant for a particular location, /// the events should be filtered before the generation of diagnostics. @@ -280,32 +306,29 @@ impl History { pub(super) struct TbError<'node> { /// What failure occurred. pub error_kind: TransitionError, - /// The allocation in which the error is happening. - pub alloc_id: AllocId, - /// The offset (into the allocation) at which the conflict occurred. - pub error_offset: u64, + /// Diagnostic data about the access that caused the error. + pub access_info: &'node DiagnosticInfo, /// The tag on which the error was triggered. /// On protector violations, this is the tag that was protected. /// On accesses rejected due to insufficient permissions, this is the /// tag that lacked those permissions. - pub conflicting_info: &'node NodeDebugInfo, - // What kind of access caused this error (read, write, reborrow, deallocation) - pub access_cause: AccessCause, + pub conflicting_node_info: &'node NodeDebugInfo, /// Which tag, if any, the access that caused this error was made through, i.e. /// which tag was used to read/write/deallocate. /// Not set on wildcard accesses. - pub accessed_info: Option<&'node NodeDebugInfo>, + pub accessed_node_info: Option<&'node NodeDebugInfo>, } impl TbError<'_> { /// Produce a UB error. pub fn build<'tcx>(self) -> InterpErrorKind<'tcx> { use TransitionError::*; - let cause = self.access_cause; - let accessed = self.accessed_info; + let cause = self.access_info.access_cause; + let error_offset = self.access_info.transition_range.start; + let accessed = self.accessed_node_info; let accessed_str = - self.accessed_info.map(|v| format!("{v}")).unwrap_or_else(|| "".into()); - let conflicting = self.conflicting_info; + self.accessed_node_info.map(|v| format!("{v}")).unwrap_or_else(|| "".into()); + let conflicting = self.conflicting_node_info; // An access is considered conflicting if it happened through a // different tag than the one who caused UB. // When doing a wildcard access (where `accessed` is `None`) we @@ -316,9 +339,8 @@ impl TbError<'_> { // all tags through which an access would cause UB. let accessed_is_conflicting = accessed.map(|a| a.tag) == Some(conflicting.tag); let title = format!( - "{cause} through {accessed_str} at {alloc_id:?}[{offset:#x}] is forbidden", - alloc_id = self.alloc_id, - offset = self.error_offset + "{cause} through {accessed_str} at {alloc_id:?}[{error_offset:#x}] is forbidden", + alloc_id = self.access_info.alloc_id ); let (title, details, conflicting_tag_name) = match self.error_kind { ChildAccessForbidden(perm) => { @@ -362,13 +384,13 @@ impl TbError<'_> { } }; let mut history = HistoryData::default(); - if let Some(accessed_info) = self.accessed_info + if let Some(accessed_info) = self.accessed_node_info && !accessed_is_conflicting { history.extend(accessed_info.history.forget(), "accessed", false); } history.extend( - self.conflicting_info.history.extract_relevant(self.error_offset, self.error_kind), + self.conflicting_node_info.history.extract_relevant(error_offset, self.error_kind), conflicting_tag_name, true, ); @@ -379,12 +401,12 @@ impl TbError<'_> { /// Cannot access this allocation with wildcard provenance, as there are no /// valid exposed references for this access kind. pub fn no_valid_exposed_references_error<'tcx>( - alloc_id: AllocId, - offset: u64, - access_cause: AccessCause, + DiagnosticInfo { alloc_id, transition_range, access_cause, .. }: &DiagnosticInfo, ) -> InterpErrorKind<'tcx> { - let title = - format!("{access_cause} through at {alloc_id:?}[{offset:#x}] is forbidden"); + let title = format!( + "{access_cause} through at {alloc_id:?}[{offset:#x}] is forbidden", + offset = transition_range.start + ); let details = vec![format!("there are no exposed tags which may perform this access here")]; let history = HistoryData::default(); err_machine_stop!(TerminationInfo::TreeBorrowsUb { title, details, history }) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 018421ad1064..173145788ee3 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -60,7 +60,9 @@ impl<'tcx> Tree { let span = machine.current_user_relevant_span(); self.perform_access( prov, - Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))), + range, + access_kind, + diagnostics::AccessCause::Explicit(access_kind), global, alloc_id, span, @@ -94,8 +96,7 @@ impl<'tcx> Tree { alloc_id: AllocId, // diagnostics ) -> InterpResult<'tcx> { let span = machine.current_user_relevant_span(); - // `None` makes it the magic on-protector-end operation - self.perform_access(ProvenanceExtra::Concrete(tag), None, global, alloc_id, span)?; + self.perform_protector_end_access(tag, global, alloc_id, span)?; self.update_exposure_for_protector_release(tag); @@ -219,26 +220,18 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; trace!("Reborrow of size {:?}", ptr_size); - let (alloc_id, base_offset, parent_prov) = match this.ptr_try_get_alloc_id(place.ptr(), 0) { - Ok(data) => { - // Unlike SB, we *do* a proper retag for size 0 if can identify the allocation. - // After all, the pointer may be lazily initialized outside this initial range. - data - } - Err(_) => { - assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here - // This pointer doesn't come with an AllocId, so there's no - // memory to do retagging in. - let new_prov = place.ptr().provenance; - trace!( - "reborrow of size 0: reusing {:?} (pointee {})", - place.ptr(), - place.layout.ty, - ); - log_creation(this, None)?; - // Keep original provenance. - return interp_ok(new_prov); - } + // Unlike SB, we *do* a proper retag for size 0 if can identify the allocation. + // After all, the pointer may be lazily initialized outside this initial range. + let Ok((alloc_id, base_offset, parent_prov)) = this.ptr_try_get_alloc_id(place.ptr(), 0) + else { + assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here + // This pointer doesn't come with an AllocId, so there's no + // memory to do retagging in. + let new_prov = place.ptr().provenance; + trace!("reborrow of size 0: reusing {:?} (pointee {})", place.ptr(), place.layout.ty,); + log_creation(this, None)?; + // Keep original provenance. + return interp_ok(new_prov); }; let new_prov = Provenance::Concrete { alloc_id, tag: new_tag }; @@ -344,7 +337,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { tree_borrows.perform_access( parent_prov, - Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)), + range_in_alloc, + AccessKind::Read, + diagnostics::AccessCause::Reborrow, this.machine.borrow_tracker.as_ref().unwrap(), alloc_id, this.machine.current_user_relevant_span(), @@ -609,8 +604,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let (tag, alloc_id) = match ptr.provenance { Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id), - _ => { - eprintln!("Can't give the name {name} to Wildcard pointer"); + Some(Provenance::Wildcard) => { + eprintln!("Can't give the name {name} to wildcard pointer"); + return interp_ok(()); + } + None => { + eprintln!("Can't give the name {name} to pointer without provenance"); return interp_ok(()); } }; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index ca68dc891020..900e9c3729c8 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -18,12 +18,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_span::Span; use smallvec::SmallVec; -use super::Permission; use super::diagnostics::{ - self, AccessCause, NodeDebugInfo, TbError, TransitionError, no_valid_exposed_references_error, + AccessCause, DiagnosticInfo, NodeDebugInfo, TbError, TransitionError, + no_valid_exposed_references_error, }; use super::foreign_access_skipping::IdempotentForeignAccess; -use super::perms::PermTransition; +use super::perms::{PermTransition, Permission}; use super::tree_visitor::{ChildrenVisitMode, ContinueTraversal, NodeAppArgs, TreeVisitor}; use super::unimap::{UniIndex, UniKeyMap, UniValMap}; use super::wildcard::WildcardState; @@ -91,12 +91,9 @@ impl LocationState { nodes: &mut UniValMap, wildcard_accesses: &mut UniValMap, access_kind: AccessKind, - access_cause: AccessCause, //diagnostics - access_range: Option, //diagnostics relatedness: AccessRelatedness, - span: Span, //diagnostics - location_range: Range, //diagnostics protected: bool, + diagnostics: &DiagnosticInfo, ) -> Result<(), TransitionError> { // Call this function now (i.e. only if we know `relatedness`), which // ensures it is only called when `skip_if_known_noop` returns @@ -107,14 +104,9 @@ impl LocationState { if !transition.is_noop() { let node = nodes.get_mut(idx).unwrap(); // Record the event as part of the history. - node.debug_info.history.push(diagnostics::Event { - transition, - is_foreign: relatedness.is_foreign(), - access_cause, - access_range, - transition_range: location_range, - span, - }); + node.debug_info + .history + .push(diagnostics.create_event(transition, relatedness.is_foreign())); // We need to update the wildcard state, if the permission // of an exposed pointer changes. @@ -544,7 +536,9 @@ impl<'tcx> Tree { ) -> InterpResult<'tcx> { self.perform_access( prov, - Some((access_range, AccessKind::Write, diagnostics::AccessCause::Dealloc)), + access_range, + AccessKind::Write, + AccessCause::Dealloc, global, alloc_id, span, @@ -558,6 +552,13 @@ impl<'tcx> Tree { // Check if this breaks any strong protector. // (Weak protectors are already handled by `perform_access`.) for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { + let diagnostics = DiagnosticInfo { + alloc_id, + span, + transition_range: loc_range, + access_range: Some(access_range), + access_cause: AccessCause::Dealloc, + }; // Checks the tree containing `idx` for strong protector violations. // It does this in traversal order. let mut check_tree = |idx| { @@ -584,12 +585,10 @@ impl<'tcx> Tree { && perm.accessed { Err(TbError { - conflicting_info: &node.debug_info, - access_cause: diagnostics::AccessCause::Dealloc, - alloc_id, - error_offset: loc_range.start, error_kind: TransitionError::ProtectedDealloc, - accessed_info: start_idx + access_info: &diagnostics, + conflicting_node_info: &node.debug_info, + accessed_node_info: start_idx .map(|idx| &args.nodes.get(idx).unwrap().debug_info), } .build()) @@ -620,14 +619,6 @@ impl<'tcx> Tree { /// to each location of the first component of `access_range_and_kind`, /// on every tag of the allocation. /// - /// If `access_range_and_kind` is `None`, this is interpreted as the special - /// access that is applied on protector release: - /// - the access will be applied only to accessed locations of the allocation, - /// - it will not be visible to children, - /// - it will be recorded as a `FnExit` diagnostic access - /// - and it will be a read except if the location is `Unique`, i.e. has been written to, - /// in which case it will be a write. - /// /// `LocationState::perform_access` will take care of raising transition /// errors and updating the `accessed` status of each location, /// this traversal adds to that: @@ -637,7 +628,9 @@ impl<'tcx> Tree { pub fn perform_access( &mut self, prov: ProvenanceExtra, - access_range_and_kind: Option<(AllocRange, AccessKind, diagnostics::AccessCause)>, + access_range: AllocRange, + access_kind: AccessKind, + access_cause: AccessCause, // diagnostics global: &GlobalState, alloc_id: AllocId, // diagnostics span: Span, // diagnostics @@ -651,61 +644,80 @@ impl<'tcx> Tree { ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()), ProvenanceExtra::Wildcard => None, }; - if let Some((access_range, access_kind, access_cause)) = access_range_and_kind { - // Default branch: this is a "normal" access through a known range. - // We iterate over affected locations and traverse the tree for each of them. - for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { + // We iterate over affected locations and traverse the tree for each of them. + for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) { + let diagnostics = DiagnosticInfo { + access_cause, + access_range: Some(access_range), + alloc_id, + span, + transition_range: loc_range, + }; + loc.perform_access( + self.roots.iter().copied(), + &mut self.nodes, + source_idx, + access_kind, + global, + ChildrenVisitMode::VisitChildrenOfAccessed, + &diagnostics, + )?; + } + interp_ok(()) + } + /// This is the special access that is applied on protector release: + /// - the access will be applied only to accessed locations of the allocation, + /// - it will not be visible to children, + /// - it will be recorded as a `FnExit` diagnostic access + /// - and it will be a read except if the location is `Unique`, i.e. has been written to, + /// in which case it will be a write. + /// - otherwise identical to `Tree::perform_access` + pub fn perform_protector_end_access( + &mut self, + tag: BorTag, + global: &GlobalState, + alloc_id: AllocId, // diagnostics + span: Span, // diagnostics + ) -> InterpResult<'tcx> { + #[cfg(feature = "expensive-consistency-checks")] + if self.roots.len() > 1 { + self.verify_wildcard_consistency(global); + } + + let source_idx = self.tag_mapping.get(&tag).unwrap(); + + // This is a special access through the entire allocation. + // It actually only affects `accessed` locations, so we need + // to filter on those before initiating the traversal. + // + // In addition this implicit access should not be visible to children, + // thus the use of `traverse_nonchildren`. + // See the test case `returned_mut_is_usable` from + // `tests/pass/tree_borrows/tree-borrows.rs` for an example of + // why this is important. + for (loc_range, loc) in self.locations.iter_mut_all() { + // Only visit accessed permissions + if let Some(p) = loc.perms.get(source_idx) + && let Some(access_kind) = p.permission.protector_end_access() + && p.accessed + { + let diagnostics = DiagnosticInfo { + access_cause: AccessCause::FnExit(access_kind), + access_range: None, + alloc_id, + span, + transition_range: loc_range, + }; loc.perform_access( self.roots.iter().copied(), &mut self.nodes, - source_idx, - loc_range, - Some(access_range), + Some(source_idx), access_kind, - access_cause, global, - alloc_id, - span, - ChildrenVisitMode::VisitChildrenOfAccessed, + ChildrenVisitMode::SkipChildrenOfAccessed, + &diagnostics, )?; } - } else { - // This is a special access through the entire allocation. - // It actually only affects `accessed` locations, so we need - // to filter on those before initiating the traversal. - // - // In addition this implicit access should not be visible to children, - // thus the use of `traverse_nonchildren`. - // See the test case `returned_mut_is_usable` from - // `tests/pass/tree_borrows/tree-borrows.rs` for an example of - // why this is important. - - // Wildcard references are never protected. So this can never be - // called with a wildcard reference. - let source_idx = source_idx.unwrap(); - - for (loc_range, loc) in self.locations.iter_mut_all() { - // Only visit accessed permissions - if let Some(p) = loc.perms.get(source_idx) - && let Some(access_kind) = p.permission.protector_end_access() - && p.accessed - { - let access_cause = diagnostics::AccessCause::FnExit(access_kind); - loc.perform_access( - self.roots.iter().copied(), - &mut self.nodes, - Some(source_idx), - loc_range, - None, - access_kind, - access_cause, - global, - alloc_id, - span, - ChildrenVisitMode::SkipChildrenOfAccessed, - )?; - } - } } interp_ok(()) } @@ -872,27 +884,19 @@ impl<'tcx> LocationTree { roots: impl Iterator, nodes: &mut UniValMap, access_source: Option, - loc_range: Range, // diagnostics - access_range: Option, // diagnostics access_kind: AccessKind, - access_cause: diagnostics::AccessCause, // diagnostics global: &GlobalState, - alloc_id: AllocId, // diagnostics - span: Span, // diagnostics visit_children: ChildrenVisitMode, + diagnostics: &DiagnosticInfo, ) -> InterpResult<'tcx> { let accessed_root = if let Some(idx) = access_source { Some(self.perform_normal_access( idx, nodes, - loc_range.clone(), - access_range, access_kind, - access_cause, global, - alloc_id, - span, visit_children, + diagnostics, )?) } else { // `SkipChildrenOfAccessed` only gets set on protector release, which only @@ -926,13 +930,9 @@ impl<'tcx> LocationTree { access_source, /*max_local_tag*/ accessed_root_tag, nodes, - loc_range.clone(), - access_range, access_kind, - access_cause, global, - alloc_id, - span, + diagnostics, )?; } interp_ok(()) @@ -948,14 +948,10 @@ impl<'tcx> LocationTree { &mut self, access_source: UniIndex, nodes: &mut UniValMap, - loc_range: Range, // diagnostics - access_range: Option, // diagnostics access_kind: AccessKind, - access_cause: diagnostics::AccessCause, // diagnostics global: &GlobalState, - alloc_id: AllocId, // diagnostics - span: Span, // diagnostics visit_children: ChildrenVisitMode, + diagnostics: &DiagnosticInfo, ) -> InterpResult<'tcx, UniIndex> { // Performs the per-node work: // - insert the permission if it does not exist @@ -987,21 +983,18 @@ impl<'tcx> LocationTree { args.nodes, &mut args.data.wildcard_accesses, access_kind, - access_cause, - access_range, args.rel_pos, - span, - loc_range.clone(), protected, + diagnostics, ) .map_err(|error_kind| { TbError { - conflicting_info: &args.nodes.get(args.idx).unwrap().debug_info, - access_cause, - alloc_id, - error_offset: loc_range.start, error_kind, - accessed_info: Some(&args.nodes.get(access_source).unwrap().debug_info), + access_info: diagnostics, + conflicting_node_info: &args.nodes.get(args.idx).unwrap().debug_info, + accessed_node_info: Some( + &args.nodes.get(access_source).unwrap().debug_info, + ), } .build() }) @@ -1030,13 +1023,9 @@ impl<'tcx> LocationTree { access_source: Option, max_local_tag: Option, nodes: &mut UniValMap, - loc_range: Range, // diagnostics - access_range: Option, // diagnostics access_kind: AccessKind, - access_cause: diagnostics::AccessCause, // diagnostics global: &GlobalState, - alloc_id: AllocId, // diagnostics - span: Span, // diagnostics + diagnostics: &DiagnosticInfo, ) -> InterpResult<'tcx> { let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| { let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default(); @@ -1090,11 +1079,7 @@ impl<'tcx> LocationTree { // This can only happen if `root` is the main root: We set // `max_foreign_access==Write` on all wildcard roots, so at least a foreign access // is always possible on all nodes in a wildcard subtree. - return Err(no_valid_exposed_references_error( - alloc_id, - loc_range.start, - access_cause, - )); + return Err(no_valid_exposed_references_error(diagnostics)); }; let Some(relatedness) = wildcard_relatedness.to_relatedness() else { @@ -1113,22 +1098,17 @@ impl<'tcx> LocationTree { args.nodes, &mut args.data.wildcard_accesses, access_kind, - access_cause, - access_range, relatedness, - span, - loc_range.clone(), protected, + diagnostics, ) .map_err(|trans| { let node = args.nodes.get(args.idx).unwrap(); TbError { - conflicting_info: &node.debug_info, - access_cause, - alloc_id, - error_offset: loc_range.start, error_kind: trans, - accessed_info: access_source + access_info: diagnostics, + conflicting_node_info: &node.debug_info, + accessed_node_info: access_source .map(|idx| &args.nodes.get(idx).unwrap().debug_info), } .build() diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index ae16898106d8..f539e783fd3f 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -171,11 +171,8 @@ impl AtomicRwOrd { (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Acquire, (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release) => AtomicRwOrd::Release, (AtomicReadOrd::Acquire, AtomicWriteOrd::Release) => AtomicRwOrd::AcqRel, - (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst, - _ => - panic!( - "Unsupported memory ordering combination ({read_ordering:?}, {write_ordering:?})" - ), + (AtomicReadOrd::SeqCst, _) => AtomicRwOrd::SeqCst, + (_, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst, } } diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 73da0e11daaf..36b1d2afade4 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -402,8 +402,20 @@ impl GenmcCtx { // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. let (effective_failure_ordering, _) = upgraded_success_ordering.split_memory_orderings(); - // Return a warning if the actual orderings don't match the upgraded ones. - if success != upgraded_success_ordering || effective_failure_ordering != fail { + + // Return a warning if we cannot explore all behaviors of this operation. + // Only emit this if the operation is "in user code": walk up across `#[track_caller]` + // frames, then check if the next frame is local. + let show_warning = || { + ecx.active_thread_stack() + .iter() + .rev() + .find(|f| !f.instance().def.requires_caller_location(*ecx.tcx)) + .is_none_or(|f| ecx.machine.is_local(f.instance())) + }; + if (success != upgraded_success_ordering || effective_failure_ordering != fail) + && show_warning() + { static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); ecx.dedup_diagnostic(&DEDUP, |_first| { NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { @@ -415,7 +427,7 @@ impl GenmcCtx { }); } // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. - if can_fail_spuriously { + if can_fail_spuriously && show_warning() { static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new(); ecx.dedup_diagnostic(&DEDUP, |_first| NonHaltingDiagnostic::GenmcCompareExchangeWeak); } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 5016e3b66ca6..1c404d419ef8 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -834,7 +834,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // sleep until the first callback. interp_ok(SchedulingAction::Sleep(sleep_time)) } else { - throw_machine_stop!(TerminationInfo::Deadlock); + throw_machine_stop!(TerminationInfo::GlobalDeadlock); } } } diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index 2255e0d48174..6fe73fec0f57 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -208,11 +208,10 @@ impl StoreBufferAlloc { range: AllocRange, ) -> InterpResult<'tcx, Option>> { let access_type = self.store_buffers.borrow().access_type(range); - let pos = match access_type { - AccessType::PerfectlyOverlapping(pos) => pos, + let AccessType::PerfectlyOverlapping(pos) = access_type else { // If there is nothing here yet, that means there wasn't an atomic write yet so // we can't return anything outdated. - _ => return interp_ok(None), + return interp_ok(None); }; let store_buffer = Ref::map(self.store_buffers.borrow(), |buffer| &buffer[pos]); interp_ok(Some(store_buffer)) diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 265609a49d83..189f493a5ed8 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -32,7 +32,10 @@ pub enum TerminationInfo { history: tree_diagnostics::HistoryData, }, Int2PtrWithStrictProvenance, - Deadlock, + /// All threads are blocked. + GlobalDeadlock, + /// Some thread discovered a deadlock condition (e.g. in a mutex with reentrancy checking). + LocalDeadlock, MultipleSymbolDefinitions { link_name: Symbol, first: SpanData, @@ -76,7 +79,8 @@ impl fmt::Display for TerminationInfo { ), StackedBorrowsUb { msg, .. } => write!(f, "{msg}"), TreeBorrowsUb { title, .. } => write!(f, "{title}"), - Deadlock => write!(f, "the evaluated program deadlocked"), + GlobalDeadlock => write!(f, "the evaluated program deadlocked"), + LocalDeadlock => write!(f, "a thread deadlocked"), MultipleSymbolDefinitions { link_name, .. } => write!(f, "multiple definitions of symbol `{link_name}`"), SymbolShimClashing { link_name, .. } => @@ -245,10 +249,39 @@ pub fn report_result<'tcx>( Some("unsupported operation"), StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } => Some("Undefined Behavior"), - Deadlock => { + LocalDeadlock => { labels.push(format!("this thread got stuck here")); None } + GlobalDeadlock => { + // Global deadlocks are reported differently: just show all blocked threads. + // The "active" thread might actually be terminated, so we ignore it. + let mut any_pruned = false; + for (thread, stack) in ecx.machine.threads.all_blocked_stacks() { + let stacktrace = Frame::generate_stacktrace_from_stack(stack); + let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); + any_pruned |= was_pruned; + report_msg( + DiagLevel::Error, + format!("the evaluated program deadlocked"), + vec![format!( + "thread `{}` got stuck here", + ecx.machine.threads.get_thread_display_name(thread) + )], + vec![], + vec![], + &stacktrace, + Some(thread), + &ecx.machine, + ) + } + if any_pruned { + ecx.tcx.dcx().note( + "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace" + ); + } + return None; + } MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None, }; #[rustfmt::skip] @@ -408,9 +441,7 @@ pub fn report_result<'tcx>( }; let stacktrace = ecx.generate_stacktrace(); - let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine); - - let mut show_all_threads = false; + let (stacktrace, pruned) = prune_stacktrace(stacktrace, &ecx.machine); // We want to dump the allocation if this is `InvalidUninitBytes`. // Since `format_interp_error` consumes `e`, we compute the outut early. @@ -425,15 +456,6 @@ pub fn report_result<'tcx>( .unwrap(); writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap(); } - MachineStop(info) => { - let info = info.downcast_ref::().expect("invalid MachineStop payload"); - match info { - TerminationInfo::Deadlock => { - show_all_threads = true; - } - _ => {} - } - } _ => {} } @@ -464,28 +486,8 @@ pub fn report_result<'tcx>( eprint!("{extra}"); // newlines are already in the string - if show_all_threads { - for (thread, stack) in ecx.machine.threads.all_blocked_stacks() { - if thread != ecx.active_thread() { - let stacktrace = Frame::generate_stacktrace_from_stack(stack); - let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); - any_pruned |= was_pruned; - report_msg( - DiagLevel::Error, - format!("the evaluated program deadlocked"), - vec![format!("this thread got stuck here")], - vec![], - vec![], - &stacktrace, - Some(thread), - &ecx.machine, - ) - } - } - } - // Include a note like `std` does when we omit frames from a backtrace - if any_pruned { + if pruned { ecx.tcx.dcx().note( "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace", ); @@ -574,7 +576,7 @@ pub fn report_msg<'tcx>( err.span(span); // Show main message. - if span != DUMMY_SP { + if !span.is_dummy() { for line in span_msg { err.span_label(span, line); } diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 01a54bbb3374..0423b0ea5abd 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -543,9 +543,7 @@ where { // Parse argv[0]. Slashes aren't escaped. Literal double quotes are not allowed. let mut cmd = { - let arg0 = if let Some(arg0) = args.next() { - arg0 - } else { + let Some(arg0) = args.next() else { return vec![0]; }; let arg0 = arg0.as_ref(); diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index f11860f4a1ad..218e4ffe5e2f 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -584,10 +584,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } fn aggregate_field_iter( - memory_index: &IndexVec, - ) -> impl Iterator + 'static { - let inverse_memory_index = memory_index.invert_bijective_mapping(); - inverse_memory_index.into_iter() + in_memory_order: &IndexVec, + ) -> impl Iterator { + in_memory_order.iter().copied() } // Hook to detect `UnsafeCell`. diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index fa8f455c9225..7ce2ab3b3a43 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -17,7 +17,6 @@ #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] -#![cfg_attr(bootstrap, feature(duration_from_nanos_u128))] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, @@ -46,7 +45,12 @@ rustc::potential_query_instability, rustc::untranslatable_diagnostic, )] -#![warn(rust_2018_idioms, unqualified_local_imports, clippy::as_conversions)] +#![warn( + rust_2018_idioms, + unqualified_local_imports, + clippy::as_conversions, + clippy::manual_let_else +)] // Needed for rustdoc from bootstrap (with `-Znormalize-docs`). #![recursion_limit = "256"] diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index bd3914b652ac..1ca814ee7aff 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -92,21 +92,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr, 0)?; // This has to be an actual global fn ptr, not a dlsym function. - let fn_instance = if let Some(GlobalAlloc::Function { instance, .. }) = - this.tcx.try_get_global_alloc(alloc_id) - { - instance - } else { + let Some(GlobalAlloc::Function { instance, .. }) = this.tcx.try_get_global_alloc(alloc_id) + else { throw_ub_format!("expected static function pointer, found {:?}", ptr); }; let lo = this.tcx.sess.source_map().lookup_char_pos(BytePos(offset.bytes().try_into().unwrap())); - let name = fn_instance.to_string(); + let name = instance.to_string(); let filename = lo.file.name.prefer_remapped_unconditionally().to_string(); - interp_ok((fn_instance, lo, name, filename)) + interp_ok((instance, lo, name, filename)) } fn handle_miri_resolve_frame( diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 8a959d06bf9c..722f34bac02e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -435,6 +435,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Return value: 0 on success, otherwise the size it would have needed. this.write_int(if success { 0 } else { needed_size }, dest)?; } + // Hint that a loop is spinning indefinitely. + "miri_spin_loop" => { + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + + // Try to run another thread to maximize the chance of finding actual bugs. + this.yield_active_thread(); + } // Obtains the size of a Miri backtrace. See the README for details. "miri_backtrace_size" => { this.handle_miri_backtrace_size(abi, link_name, args, dest)?; @@ -531,7 +538,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { code, crate::concurrency::ExitType::ExitCalled, )?; - todo!(); // FIXME(genmc): Add a way to return here that is allowed to not do progress (can't use existing EmulateItemResult variants). + return interp_ok(EmulateItemResult::AlreadyJumped); } throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); } diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 445f9618e7e3..12abe841c052 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -459,12 +459,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Get the pointer to the function in the shared object file if it exists. - let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) { - Some(ptr) => ptr, - None => { - // Shared object file does not export this function -- try the shims next. - return interp_ok(false); - } + let Some(code_ptr) = this.get_func_ptr_explicitly_from_lib(link_name) else { + // Shared object file does not export this function -- try the shims next. + return interp_ok(false); }; // Do we have ptrace? diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 614cc75c6d58..2bbae80a0b91 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -337,11 +337,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let duration = this.deref_pointer_as(duration, this.libc_ty_layout("timespec"))?; let _rem = this.read_pointer(rem)?; // Signal handlers are not supported, so rem will never be written to. - let duration = match this.read_timespec(&duration)? { - Some(duration) => duration, - None => { - return this.set_last_error_and_return_i32(LibcError("EINVAL")); - } + let Some(duration) = this.read_timespec(&duration)? else { + return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; this.block_thread( @@ -378,11 +375,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("clock_nanosleep: only CLOCK_MONOTONIC is supported"); } - let duration = match this.read_timespec(×pec)? { - Some(duration) => duration, - None => { - return this.set_last_error_and_return_i32(LibcError("EINVAL")); - } + let Some(duration) = this.read_timespec(×pec)? else { + return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; let timeout_anchor = if flags == 0 { diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index bd1ee3155372..ae8a167080b9 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -101,12 +101,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `uaddr2` points to a `struct _umtx_time`. let umtx_time_place = this.ptr_to_mplace(uaddr2, umtx_time_layout); - let umtx_time = match this.read_umtx_time(&umtx_time_place)? { - Some(ut) => ut, - None => { - return this - .set_last_error_and_return(LibcError("EINVAL"), dest); - } + let Some(umtx_time) = this.read_umtx_time(&umtx_time_place)? else { + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; let anchor = if umtx_time.abs_time { @@ -122,12 +118,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `uaddr2` points to a `struct timespec`. let timespec = this.ptr_to_mplace(uaddr2, timespec_layout); - let duration = match this.read_timespec(×pec)? { - Some(duration) => duration, - None => { - return this - .set_last_error_and_return(LibcError("EINVAL"), dest); - } + let Some(duration) = this.read_timespec(×pec)? else { + return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; // FreeBSD does not seem to document which clock is used when the timeout @@ -220,10 +212,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let timespec_place = this.project_field(ut, FieldIdx::from_u32(0))?; // Inner `timespec` must still be valid. - let duration = match this.read_timespec(×pec_place)? { - Some(dur) => dur, - None => return interp_ok(None), - }; + let Some(duration) = this.read_timespec(×pec_place)? else { return interp_ok(None) }; let flags_place = this.project_field(ut, FieldIdx::from_u32(1))?; let flags = this.read_scalar(&flags_place)?.to_u32()?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 650972be5574..e17456c3bc0e 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1157,10 +1157,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - // FIXME: Support ftruncate64 for all FDs - let file = fd.downcast::().ok_or_else(|| { - err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors") - })?; + let Some(file) = fd.downcast::() else { + // The docs say that EINVAL is returned when the FD "does not reference a regular file + // or a POSIX shared memory object" (and we don't support shmem objects). + return interp_ok(this.eval_libc("EINVAL")); + }; if file.writable { if let Ok(length) = length.try_into() { @@ -1202,10 +1203,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(fd) = this.machine.fds.get(fd_num) else { return interp_ok(this.eval_libc("EBADF")); }; - let file = match fd.downcast::() { - Some(file_handle) => file_handle, + let Some(file) = fd.downcast::() else { // Man page specifies to return ENODEV if `fd` is not a regular file. - None => return interp_ok(this.eval_libc("ENODEV")), + return interp_ok(this.eval_libc("ENODEV")); }; if !file.writable { diff --git a/src/tools/miri/src/shims/unix/linux_like/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs index 8ff7fe0a4563..00df4ee2b2ae 100644 --- a/src/tools/miri/src/shims/unix/linux_like/sync.rs +++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs @@ -71,11 +71,8 @@ pub fn futex<'tcx>( let timeout = if ecx.ptr_is_null(timeout.ptr())? { None } else { - let duration = match ecx.read_timespec(&timeout)? { - Some(duration) => duration, - None => { - return ecx.set_last_error_and_return(LibcError("EINVAL"), dest); - } + let Some(duration) = ecx.read_timespec(&timeout)? else { + return ecx.set_last_error_and_return(LibcError("EINVAL"), dest); }; let timeout_clock = if op & futex_realtime == futex_realtime { ecx.check_no_isolation( diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 39014f34c897..58a612102b18 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -538,7 +538,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_ub_format!( "trying to acquire default mutex already locked by the current thread" ), - MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock), + MutexKind::Normal => throw_machine_stop!(TerminationInfo::LocalDeadlock), MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"), MutexKind::Recursive => { this.mutex_lock(&mutex.mutex_ref)?; @@ -887,15 +887,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone(); // Extract the timeout. - let duration = match this + let Some(duration) = this .read_timespec(&this.deref_pointer_as(timeout_op, this.libc_ty_layout("timespec"))?)? - { - Some(duration) => duration, - None => { - let einval = this.eval_libc("EINVAL"); - this.write_scalar(einval, dest)?; - return interp_ok(()); - } + else { + let einval = this.eval_libc("EINVAL"); + this.write_scalar(einval, dest)?; + return interp_ok(()); }; let (clock, anchor) = if macos_relative_np { diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index 775c08388c07..ad22df2425af 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -321,11 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.windows_ty_layout("BY_HANDLE_FILE_INFORMATION"), )?; - let fd_num = if let Handle::File(fd_num) = file { - fd_num - } else { - this.invalid_handle("GetFileInformationByHandle")? - }; + let Handle::File(fd_num) = file else { this.invalid_handle("GetFileInformationByHandle")? }; let Some(desc) = this.machine.fds.get(fd_num) else { this.invalid_handle("GetFileInformationByHandle")? @@ -448,10 +444,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("`NtWriteFile` `Key` parameter is non-null, which is unsupported"); } - let fd = match handle { - Handle::File(fd) => fd, - _ => this.invalid_handle("NtWriteFile")?, - }; + let Handle::File(fd) = handle else { this.invalid_handle("NtWriteFile")? }; let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtWriteFile")? }; @@ -561,10 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let io_status_info = this.project_field_named(&io_status_block, "Information")?; - let fd = match handle { - Handle::File(fd) => fd, - _ => this.invalid_handle("NtWriteFile")?, - }; + let Handle::File(fd) = handle else { this.invalid_handle("NtWriteFile")? }; let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? }; @@ -620,10 +610,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_fp_ptr = this.read_pointer(new_fp)?; let move_method = this.read_scalar(move_method)?.to_u32()?; - let fd = match file { - Handle::File(fd) => fd, - _ => this.invalid_handle("SetFilePointerEx")?, - }; + let Handle::File(fd) = file else { this.invalid_handle("SetFilePointerEx")? }; let Some(desc) = this.machine.fds.get(fd) else { throw_unsup_format!("`SetFilePointerEx` is only supported on file backed handles"); diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr index c80cbf835a38..e3b0036c9aa9 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC - | -LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -23,6 +17,12 @@ LL | | }) LL | | .join() | |_______________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC + | +LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs index b88257992a5c..3008705ebfde 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs @@ -12,6 +12,6 @@ fn main() { assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0); // A "normal" mutex properly tries to acquire the lock even if its is already held // by the current thread -- and then we deadlock. - libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: the evaluated program deadlocked + libc::pthread_mutex_lock(&mut mutex as *mut _); //~ ERROR: a thread deadlocked } } diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr index c611d0ff7b00..782322d5c32b 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.stderr @@ -1,4 +1,4 @@ -error: the evaluated program deadlocked +error: a thread deadlocked --> tests/fail-dep/concurrency/libc_pthread_mutex_normal_reentrant.rs:LL:CC | LL | libc::pthread_mutex_lock(&mut mutex as *mut _); diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr index 763a52963a4d..ea8cbcada970 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs:LL:CC | LL | libc::pthread_rwlock_wrlock(rw.get()); - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr index f174d387f76e..255632870efc 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC - | -LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -23,6 +17,12 @@ LL | | }) LL | | .join() | |_______________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC + | +LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr index 4c8d3fddb97c..0208a5bae4f5 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs:LL:CC | LL | libc::pthread_rwlock_rdlock(rw.get()); - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr index 06b8e1246e01..6891b989d05f 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC - | -LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -23,6 +17,12 @@ LL | | }) LL | | .join() | |_______________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC + | +LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr index e6a4b2814d83..314e60b02360 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs:LL:CC | LL | libc::pthread_rwlock_wrlock(rw.get()); - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 0ab89676db37..d9cc93d0fc49 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -1,16 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/windows_join_main.rs:LL:CC - | -LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this thread got stuck here - | - = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -26,6 +18,14 @@ LL | | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_O LL | | .join() | |___________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/windows_join_main.rs:LL:CC + | +LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ thread `unnamed-ID` got stuck here + | + = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index bdfab966d6d9..f5515983da2b 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -1,14 +1,8 @@ -error: the evaluated program deadlocked - --> tests/fail-dep/concurrency/windows_join_self.rs:LL:CC - | -LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); - | ^ this thread got stuck here - error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -26,6 +20,12 @@ LL | | }) LL | | .join() | |___________^ +error: the evaluated program deadlocked + --> tests/fail-dep/concurrency/windows_join_self.rs:LL:CC + | +LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); + | ^ thread `unnamed-ID` got stuck here + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 2 previous errors diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr index 6253fe6d2c76..53ae7ea82bd9 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC | LL | let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() }; - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr index aecc54c2fd88..62810f17be88 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC | LL | libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs index cfebb7c64351..c7a2c5b4d297 100644 --- a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs +++ b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs @@ -1,5 +1,4 @@ //@ignore-target: windows # Sockets/pipes are not implemented yet -//~^ ERROR: the evaluated program deadlocked //@compile-flags: -Zmiri-deterministic-concurrency use std::thread; diff --git a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr index e451fdb9d19f..307762fb12c6 100644 --- a/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr +++ b/src/tools/miri/tests/fail-dep/libc/fcntl_fsetfl_while_blocking.stderr @@ -1,15 +1,10 @@ -error: the evaluated program deadlocked - | - = note: this thread got stuck here - = note: (no span available) - error: the evaluated program deadlocked --> tests/fail-dep/libc/fcntl_fsetfl_while_blocking.rs:LL:CC | LL | let _res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr index 14390d632738..dba12d190317 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC | LL | check_epoll_wait::(epfd, &expected, -1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this thread got stuck here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr index 97b5df7fb17f..a7c660e1adcc 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair-close-while-blocked.rs:LL:CC | LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 38db735e9b4d..faab75f7840b 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC | LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index a2f1c67b5efc..9f95d98beb6f 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - | ^ this thread got stuck here + | ^ thread `main` got stuck here | = note: BACKTRACE: = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC @@ -18,7 +18,7 @@ error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC | LL | let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; - | ^ this thread got stuck here + | ^ thread `unnamed-ID` got stuck here note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/src/tools/miri/tests/fail/closures/deref-in-pattern.rs b/src/tools/miri/tests/fail/closures/deref-in-pattern.rs new file mode 100644 index 000000000000..c729469c78b0 --- /dev/null +++ b/src/tools/miri/tests/fail/closures/deref-in-pattern.rs @@ -0,0 +1,20 @@ +// This test serves to document the change in semantics introduced by +// rust-lang/rust#138961. +// +// A corollary of partial-pattern.rs: while the tuple access testcase makes +// it clear why these semantics are useful, it is actually the dereference +// being performed by the pattern that matters. + +fn main() { + // the inner reference is dangling + let x: &&u32 = unsafe { + let x: u32 = 42; + &&* &raw const x + }; + + let _ = || { //~ ERROR: encountered a dangling reference + match x { + &&_y => {}, + } + }; +} diff --git a/src/tools/miri/tests/fail/closures/deref-in-pattern.stderr b/src/tools/miri/tests/fail/closures/deref-in-pattern.stderr new file mode 100644 index 000000000000..1264f4e3fb95 --- /dev/null +++ b/src/tools/miri/tests/fail/closures/deref-in-pattern.stderr @@ -0,0 +1,18 @@ +error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free) + --> tests/fail/closures/deref-in-pattern.rs:LL:CC + | +LL | let _ = || { + | _____________^ +LL | | match x { +LL | | &&_y => {}, +LL | | } +LL | | }; + | |_____^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/closures/partial-pattern.rs b/src/tools/miri/tests/fail/closures/partial-pattern.rs new file mode 100644 index 000000000000..cb6446208180 --- /dev/null +++ b/src/tools/miri/tests/fail/closures/partial-pattern.rs @@ -0,0 +1,28 @@ +// This test serves to document the change in semantics introduced by +// rust-lang/rust#138961. +// +// Previously, the closure would capture the entirety of x, and access *(*x).0 +// when called. Now, the closure only captures *(*x).0, which means that +// a &*(*x).0 reborrow happens when the closure is constructed. +// +// Hence, if one of the references is dangling, this constitutes newly introduced UB +// in the case where the closure doesn't get called. This isn't a big deal, +// because while opsem only now considers this to be UB, the unsafe code +// guidelines have long recommended against any handling of dangling references. + +fn main() { + // the inner references are dangling + let x: &(&u32, &u32) = unsafe { + let a = 21; + let b = 37; + let ra = &* &raw const a; + let rb = &* &raw const b; + &(ra, rb) + }; + + let _ = || { //~ ERROR: encountered a dangling reference + match x { + (&_y, _) => {}, + } + }; +} diff --git a/src/tools/miri/tests/fail/closures/partial-pattern.stderr b/src/tools/miri/tests/fail/closures/partial-pattern.stderr new file mode 100644 index 000000000000..5b7ee35ecc73 --- /dev/null +++ b/src/tools/miri/tests/fail/closures/partial-pattern.stderr @@ -0,0 +1,18 @@ +error: Undefined Behavior: constructing invalid value: encountered a dangling reference (use-after-free) + --> tests/fail/closures/partial-pattern.rs:LL:CC + | +LL | let _ = || { + | _____________^ +LL | | match x { +LL | | (&_y, _) => {}, +LL | | } +LL | | }; + | |_____^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/closures/uninhabited-variant.rs b/src/tools/miri/tests/fail/closures/uninhabited-variant.rs new file mode 100644 index 000000000000..9c50fdc13a35 --- /dev/null +++ b/src/tools/miri/tests/fail/closures/uninhabited-variant.rs @@ -0,0 +1,31 @@ +// Motivated by rust-lang/rust#138961, this shows how invalid discriminants interact with +// closure captures. +#![feature(never_type)] + +#[repr(C)] +#[allow(dead_code)] +enum E { + V0, // discriminant: 0 + V1, // 1 + V2(!), // 2 +} + +fn main() { + assert_eq!(std::mem::size_of::(), 4); + + let val = 2u32; + let ptr = (&raw const val).cast::(); + let r = unsafe { &*ptr }; + let f = || { + // After rust-lang/rust#138961, constructing the closure performs a reborrow of r. + // Nevertheless, the discriminant is only actually inspected when the closure + // is called. + match r { //~ ERROR: read discriminant of an uninhabited enum variant + E::V0 => {} + E::V1 => {} + E::V2(_) => {} + } + }; + + f(); +} diff --git a/src/tools/miri/tests/fail/closures/uninhabited-variant.stderr b/src/tools/miri/tests/fail/closures/uninhabited-variant.stderr new file mode 100644 index 000000000000..995a5e3eac14 --- /dev/null +++ b/src/tools/miri/tests/fail/closures/uninhabited-variant.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: read discriminant of an uninhabited enum variant + --> tests/fail/closures/uninhabited-variant.rs:LL:CC + | +LL | match r { + | ^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside closure at tests/fail/closures/uninhabited-variant.rs:LL:CC +note: inside `main` + --> tests/fail/closures/uninhabited-variant.rs:LL:CC + | +LL | f(); + | ^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs index b996fcaf45df..9c73f6c03edd 100644 --- a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs +++ b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.rs @@ -3,6 +3,9 @@ //@normalize-stderr-test: "LL \| .*" -> "LL | $$CODE" //@normalize-stderr-test: "\| +\^+" -> "| ^" //@normalize-stderr-test: "\n *= note:.*" -> "" +// On macOS we use chekced pthread mutexes which changes the error +//@normalize-stderr-test: "this thread got stuck here" -> "thread `main` got stuck here" +//@normalize-stderr-test: "a thread deadlocked" -> "the evaluated program deadlocked" use std::mem; use std::sync::Mutex; diff --git a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr index cc487c62d08d..7784132a54ce 100644 --- a/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr +++ b/src/tools/miri/tests/fail/concurrency/mutex-leak-move-deadlock.stderr @@ -2,7 +2,7 @@ error: the evaluated program deadlocked --> RUSTLIB/std/$FILE:LL:CC | LL | $CODE - | ^ this thread got stuck here + | ^ thread `main` got stuck here | note: inside `main` --> tests/fail/concurrency/mutex-leak-move-deadlock.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/fail/shims/exit.rs b/src/tools/miri/tests/genmc/fail/shims/exit.rs new file mode 100644 index 000000000000..4138f4e785bb --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/exit.rs @@ -0,0 +1,9 @@ +//@ compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +fn main() { + std::thread::spawn(|| { + unsafe { std::hint::unreachable_unchecked() }; //~ERROR: entering unreachable code + }); + // If we exit immediately, we might entirely miss the UB in the other thread. + std::process::exit(0); +} diff --git a/src/tools/miri/tests/genmc/fail/shims/exit.stderr b/src/tools/miri/tests/genmc/fail/shims/exit.stderr new file mode 100644 index 000000000000..dead5faaa514 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/shims/exit.stderr @@ -0,0 +1,126 @@ +Running GenMC Verification... +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/thread/mod.rs:LL:CC + | +LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC + = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/fail/shims/exit.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | unsafe { std::hint::unreachable_unchecked() }; +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + | +LL | || self + | ________________^ +LL | | .state +LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) + | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC + = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC + = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC + = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> tests/genmc/fail/shims/exit.rs:LL:CC + | +LL | / std::thread::spawn(|| { +LL | | unsafe { std::hint::unreachable_unchecked() }; +LL | | }); + | |______^ + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> RUSTLIB/std/src/rt.rs:LL:CC + | +LL | / CLEANUP.call_once(|| unsafe { +LL | | // Flush stdout and disable buffering. +LL | | crate::io::cleanup(); +... | +LL | | }); + | |______^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::process::exit` at RUSTLIB/std/src/process.rs:LL:CC +note: inside `main` + --> tests/genmc/fail/shims/exit.rs:LL:CC + | +LL | std::process::exit(0); + | ^^^^^^^^^^^^^^^^^^^^^ + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + | +LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE: + = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC + = note: inside `std::sys::pal::PLATFORM::os::exit` at RUSTLIB/std/src/sys/pal/PLATFORM/os.rs:LL:CC + = note: inside `std::process::exit` at RUSTLIB/std/src/process.rs:LL:CC +note: inside `main` + --> tests/genmc/fail/shims/exit.rs:LL:CC + | +LL | std::process::exit(0); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/shims/exit.rs:LL:CC + | +LL | unsafe { std::hint::unreachable_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + +error: aborting due to 1 previous error; 5 warnings emitted + diff --git a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr index e2148bedd318..db465969cda8 100644 --- a/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr +++ b/src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr @@ -1,64 +1,4 @@ Running GenMC Verification... -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `miri_start` - --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -LL | | let guard = guard; // avoid field capturing -LL | | drop(guard); -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `miri_start` - --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -LL | | let guard = guard; // avoid field capturing -LL | | drop(guard); -LL | | }); - | |______^ - error: Undefined Behavior: Invalid unlock() operation --> RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC | @@ -82,5 +22,5 @@ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report -error: aborting due to 1 previous error; 2 warnings emitted +error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs index e32c7cdf80c4..c19c81995d1b 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs @@ -30,5 +30,10 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { if 2 != VALUE.load(SeqCst) { std::process::abort() } + + // Check that we emit warnings for cases that are not fully supported. + let _ = VALUE.compare_exchange(99, 99, SeqCst, Relaxed); + let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst); + 0 } diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr index 7867be2dbe8e..4351b312c75d 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr @@ -1,2 +1,20 @@ Running GenMC Verification... +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'SeqCst', the failure ordering 'Relaxed' is treated like 'SeqCst'. Miri with GenMC might miss bugs related to this memory access. + --> tests/genmc/pass/atomics/cas_simple.rs:LL:CC + | +LL | let _ = VALUE.compare_exchange(99, 99, SeqCst, Relaxed); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Relaxed' was upgraded to 'SeqCst' to match failure ordering 'SeqCst'. Miri with GenMC might miss bugs related to this memory access. + --> tests/genmc/pass/atomics/cas_simple.rs:LL:CC + | +LL | let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + +warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. + --> tests/genmc/pass/atomics/cas_simple.rs:LL:CC + | +LL | let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr index fdbb9eff2faa..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr @@ -1,126 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | handle.join().unwrap(); - | ^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/arc.rs b/src/tools/miri/tests/genmc/pass/std/arc.rs index dee29127856d..addf6408c006 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.rs +++ b/src/tools/miri/tests/genmc/pass/std/arc.rs @@ -1,6 +1,5 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows //@revisions: check_count try_upgrade -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" // Check that various operations on `std::sync::Arc` are handled properly in GenMC mode. // diff --git a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr index a5423f9a398b..f527b6120232 100644 --- a/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr +++ b/src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr @@ -1,152 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let weak = Arc::downgrade(&data_clone); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | let handle = std::thread::spawn(move || { - | __________________^ -... | -LL | | }); - | |______^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside `main` - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | handle.join().unwrap(); - | ^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE on thread `unnamed-ID`: -note: inside closure - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | if let Some(strong) = weak_.upgrade() { - | ^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if self.inner()?.strong.fetch_update(Acquire, Relaxed, checked_increment).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE on thread `unnamed-ID`: -note: inside closure - --> tests/genmc/pass/std/arc.rs:LL:CC - | -LL | if let Some(strong) = weak_.upgrade() { - | ^^^^^^^^^^^^^^^ - Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.rs b/src/tools/miri/tests/genmc/pass/std/empty_main.rs index f0e4155ccd5d..2ffc3388fb36 100644 --- a/src/tools/miri/tests/genmc/pass/std/empty_main.rs +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.rs @@ -1,5 +1,4 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" // A lot of code runs before main, which we should be able to handle in GenMC mode. diff --git a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr index de07943b0d8d..7867be2dbe8e 100644 --- a/src/tools/miri/tests/genmc/pass/std/empty_main.stderr +++ b/src/tools/miri/tests/genmc/pass/std/empty_main.stderr @@ -1,45 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs index e32979dc2b51..dadbee47b986 100644 --- a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.rs @@ -1,5 +1,4 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" // We should be able to spawn and join standard library threads in GenMC mode. // Since these threads do nothing, we should only explore 1 program execution. diff --git a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr index 701934a7cd7f..7867be2dbe8e 100644 --- a/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr +++ b/src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr @@ -1,130 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside closure - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC - = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside closure - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside ` as std::iter::Iterator>::fold::<(), {closure@std::iter::adapters::map::map_fold, (), {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}, {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>::{closure#0}}>` at RUSTLIB/core/src/iter/adapters/map.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::for_each::<{closure@std::vec::Vec>::extend_trusted, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>::{closure#0}}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `> as std::vec::spec_extend::SpecExtend, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::spec_extend` at RUSTLIB/alloc/src/vec/spec_extend.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter_nested::SpecFromIterNested, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter_nested.rs:LL:CC - = note: inside `> as std::vec::spec_from_iter::SpecFromIter, std::iter::Map, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>>::from_iter` at RUSTLIB/alloc/src/vec/spec_from_iter.rs:LL:CC - = note: inside `> as std::iter::FromIterator>>::from_iter::, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>>` at RUSTLIB/alloc/src/vec/mod.rs:LL:CC - = note: inside `, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}> as std::iter::Iterator>::collect::>>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | let handles: Vec<_> = (0..N).map(|_| std::thread::spawn(thread_func)).collect(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside closure - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside `> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/alloc/src/vec/into_iter.rs:LL:CC - = note: inside `> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/spawn_std_threads.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.rs b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs index 4dac775d3407..d76975d2e92c 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.rs +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.rs @@ -1,5 +1,4 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-genmc -Zmiri-disable-stacked-borrows -//@normalize-stderr-test: "\n *= note: inside `std::.*" -> "" use std::alloc::{Layout, alloc}; use std::cell::Cell; diff --git a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr index fd6538fd70fa..bde951866d01 100644 --- a/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr +++ b/src/tools/miri/tests/genmc/pass/std/thread_locals.stderr @@ -1,113 +1,2 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/id.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | let r_ptr = R.get(); -LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); -LL | | }), - | |__________^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | R.set(unsafe { malloc() }); -LL | | let r_ptr = R.get(); -LL | | let _ = X.compare_exchange(std::ptr::null_mut(), r_ptr, SeqCst, SeqCst); -LL | | }), - | |__________^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/alloc/src/sync.rs:LL:CC - | -LL | if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: -note: inside closure - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^ - = note: inside closure at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC - = note: inside closure at RUSTLIB/core/src/ops/try_trait.rs:LL:CC - = note: inside closure at RUSTLIB/core/src/array/iter/iter_inner.rs:LL:CC - = note: inside `::try_fold::<(), {closure@std::array::iter::iter_inner::PolymorphicIter<[std::mem::MaybeUninit>]>::try_fold<(), {closure@std::ops::try_trait::NeverShortCircuit<()>::wrap_mut_2<(), std::thread::JoinHandle<()>, {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>::{closure#0}}, std::ops::try_trait::NeverShortCircuit<()>>` at RUSTLIB/core/src/ops/index_range.rs:LL:CC - = note: inside `, 3> as std::iter::Iterator>::fold::<(), {closure@std::iter::Iterator::for_each::call, {closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>::{closure#0}}>` at RUSTLIB/core/src/array/iter.rs:LL:CC - = note: inside `, 3> as std::iter::Iterator>::for_each::<{closure@tests/genmc/pass/std/thread_locals.rs:LL:CC}>` at RUSTLIB/core/src/iter/traits/iterator.rs:LL:CC -note: inside `main` - --> tests/genmc/pass/std/thread_locals.rs:LL:CC - | -LL | handles.into_iter().for_each(|handle| handle.join().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sync/once.rs:LL:CC - | -LL | self.inner.call(true, &mut |p| f.take().unwrap()(p)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sync/once.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/pass/ptr_int_casts.rs b/src/tools/miri/tests/pass/ptr_int_casts.rs index 94391eac3b27..2d99a8a449b3 100644 --- a/src/tools/miri/tests/pass/ptr_int_casts.rs +++ b/src/tools/miri/tests/pass/ptr_int_casts.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-permissive-provenance +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows use std::{mem, ptr}; fn eq_ref(x: &T, y: &T) -> bool { diff --git a/src/tools/miri/tests/pass/ptr_int_casts.tree.stderr b/src/tools/miri/tests/pass/ptr_int_casts.tree.stderr deleted file mode 100644 index 21c5b2b0dfe8..000000000000 --- a/src/tools/miri/tests/pass/ptr_int_casts.tree.stderr +++ /dev/null @@ -1,89 +0,0 @@ -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | assert_eq!(1 as *const i32 as usize, 1); - | ^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = help: this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program - = help: see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation - = help: to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead - = help: you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics - = help: Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | assert_eq!((1 as *const i32).wrapping_offset(4) as usize, 1 + 4 * 4); - | ^^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | *val = (1 as *const u8).wrapping_offset(-4); - | ^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | let y = y as *const _; - | ^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | let x: fn() -> i32 = unsafe { mem::transmute(y as *mut u8) }; - | ^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - -warning: integer-to-pointer cast - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | assert_eq!((-1i32) as usize as *const i32 as usize, (-1i32) as usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = note: BACKTRACE: - = note: inside `ptr_int_casts` at tests/pass/ptr_int_casts.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_casts.rs:LL:CC - | -LL | ptr_int_casts(); - | ^^^^^^^^^^^^^^^ - diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs index 98f8f15608e1..200e4b2ea3e9 100644 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.rs +++ b/src/tools/miri/tests/pass/ptr_int_from_exposed.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-permissive-provenance +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows use std::ptr; diff --git a/src/tools/miri/tests/pass/ptr_int_from_exposed.tree.stderr b/src/tools/miri/tests/pass/ptr_int_from_exposed.tree.stderr deleted file mode 100644 index aac6d0f48d01..000000000000 --- a/src/tools/miri/tests/pass/ptr_int_from_exposed.tree.stderr +++ /dev/null @@ -1,19 +0,0 @@ -warning: integer-to-pointer cast - --> tests/pass/ptr_int_from_exposed.rs:LL:CC - | -LL | let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = help: this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program - = help: see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation - = help: to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead - = help: you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics - = help: Tree Borrows does not support integer-to-pointer casts, so the program is likely to go wrong when this pointer gets used - = note: BACKTRACE: - = note: inside `ptr_roundtrip_out_of_bounds` at tests/pass/ptr_int_from_exposed.rs:LL:CC -note: inside `main` - --> tests/pass/ptr_int_from_exposed.rs:LL:CC - | -LL | ptr_roundtrip_out_of_bounds(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index c2cbdd548078..4ef5ce35bd6c 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -27,7 +27,7 @@ enum Mode { } fn miri_path() -> PathBuf { - PathBuf::from(env::var("MIRI").unwrap_or_else(|_| env!("CARGO_BIN_EXE_miri").into())) + env!("CARGO_BIN_EXE_miri").into() } // Build the shared object file for testing native function calls. diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index 09f9ca032d43..bd01866dc34c 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -155,4 +155,7 @@ extern "Rust" { /// Blocks the current execution if the argument is false pub fn miri_genmc_assume(condition: bool); + + /// Indicate to Miri that this thread is busy-waiting in a spin loop. + pub fn miri_spin_loop(); } diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml index 060b749676e6..57c8f5782e0a 100644 --- a/src/tools/miri/triagebot.toml +++ b/src/tools/miri/triagebot.toml @@ -53,9 +53,6 @@ new_draft = true # Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree. [canonicalize-issue-links] -# Prevents mentions in commits to avoid users being spammed. -[no-mentions] - # Show range-diff links on force pushes. [range-diff] diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs index cf1a6f7351a7..507d51471df0 100644 --- a/src/tools/run-make-support/src/env.rs +++ b/src/tools/run-make-support/src/env.rs @@ -34,6 +34,14 @@ pub fn std_debug_assertions_enabled() -> bool { std::env::var_os("__STD_DEBUG_ASSERTIONS_ENABLED").is_some() } +/// Check if staged `std`-under-test was built with remapping of it's sources. +#[track_caller] +#[must_use] +pub fn std_remap_debuginfo_enabled() -> bool { + // Note: we assume this env var is set when the test recipe is being executed. + std::env::var_os("__STD_REMAP_DEBUGINFO_ENABLED").is_some() +} + /// A wrapper around [`std::env::set_current_dir`] which includes the directory /// path in the panic message. #[track_caller] diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs index 5c980068e4bd..a32999ffc2f5 100644 --- a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs +++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs @@ -1,4 +1,4 @@ -use crate::{is_arm64ec, is_win7, is_windows, is_windows_msvc, uname}; +use crate::{is_arm64ec, is_win7, is_windows, is_windows_msvc, target, uname}; fn get_windows_msvc_libs() -> Vec<&'static str> { let mut libs = @@ -22,14 +22,22 @@ pub fn extra_c_flags() -> Vec<&'static str> { vec!["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"] } } else { - match uname() { - n if n.contains("Darwin") => vec!["-lresolv"], - n if n.contains("FreeBSD") => vec!["-lm", "-lpthread", "-lgcc_s"], - n if n.contains("SunOS") => { - vec!["-lm", "-lpthread", "-lposix4", "-lsocket", "-lresolv"] + // For cross-compilation targets, we need to check the target, not the host + let target_triple = target(); + if target_triple.contains("hexagon") { + // Hexagon targets need unwind support but don't have some Linux libraries + vec!["-lunwind", "-lclang_rt.builtins-hexagon"] + } else { + // For host-based detection, fall back to uname() for non-cross compilation + match uname() { + n if n.contains("Darwin") => vec!["-lresolv"], + n if n.contains("FreeBSD") => vec!["-lm", "-lpthread", "-lgcc_s"], + n if n.contains("SunOS") => { + vec!["-lm", "-lpthread", "-lposix4", "-lsocket", "-lresolv"] + } + n if n.contains("OpenBSD") => vec!["-lm", "-lpthread", "-lc++abi"], + _ => vec!["-lm", "-lrt", "-ldl", "-lpthread"], } - n if n.contains("OpenBSD") => vec!["-lm", "-lpthread", "-lc++abi"], - _ => vec!["-lm", "-lrt", "-ldl", "-lpthread"], } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs index 6c76c6fed062..9e761aa98ff8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs @@ -166,6 +166,9 @@ fn capture_specific_fields() { *a + x + (*b as i64) } } + // FIXME: These tests currently fail, because rust-analyzer hasn't yet implemented the changes + // introduced in rust-lang/rust#138961. See rust-lang/rust-analyzer#21274 for more discussion. + /* size_and_align_expr! { struct X(i64, i32, (u8, i128)); let y: X = X(2, 5, (7, 3)); @@ -183,6 +186,7 @@ fn capture_specific_fields() { a + x + (b as i64) } } + */ } #[test] @@ -194,6 +198,9 @@ fn match_pattern() { x } } + // FIXME: These tests currently fail, because rust-analyzer hasn't yet implemented the changes + // introduced in rust-lang/rust#138961. See rust-lang/rust-analyzer#21274 for more discussion. + /* size_and_align_expr! { minicore: copy; stmts: [ @@ -206,6 +213,7 @@ fn match_pattern() { } } } + */ size_and_align_expr! { minicore: copy; stmts: [ diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index dcf82c94aaee..95e1cd0db37c 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -0208ee09be465f69005a7a12c28d5eccac7d5f34 +f41f40408d719aa9ae0c6bfa17619d8f3f9e5b99 diff --git a/tests/assembly-llvm/nvptx-arch-default.rs b/tests/assembly-llvm/nvptx-arch-default.rs index a621fd6dcb29..22b4a680e322 100644 --- a/tests/assembly-llvm/nvptx-arch-default.rs +++ b/tests/assembly-llvm/nvptx-arch-default.rs @@ -1,5 +1,5 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -Z unstable-options -Clinker-flavor=llbc +//@ compile-flags: --crate-type cdylib //@ only-nvptx64 #![no_std] diff --git a/tests/assembly-llvm/nvptx-arch-target-cpu.rs b/tests/assembly-llvm/nvptx-arch-target-cpu.rs index 609ab297e636..e02ad0d558a9 100644 --- a/tests/assembly-llvm/nvptx-arch-target-cpu.rs +++ b/tests/assembly-llvm/nvptx-arch-target-cpu.rs @@ -1,5 +1,5 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -C target-cpu=sm_50 -Z unstable-options -Clinker-flavor=llbc +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_50 //@ only-nvptx64 #![no_std] diff --git a/tests/assembly-llvm/nvptx-c-abi-arg-v7.rs b/tests/assembly-llvm/nvptx-c-abi-arg-v7.rs index be98b167470a..6f5ef792e9b3 100644 --- a/tests/assembly-llvm/nvptx-c-abi-arg-v7.rs +++ b/tests/assembly-llvm/nvptx-c-abi-arg-v7.rs @@ -1,5 +1,5 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 -Z unstable-options -Clinker-flavor=llbc +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 //@ only-nvptx64 // The PTX ABI stability is tied to major versions of the PTX ISA diff --git a/tests/assembly-llvm/nvptx-c-abi-ret-v7.rs b/tests/assembly-llvm/nvptx-c-abi-ret-v7.rs index c68c71c872c2..02ec9facee7a 100644 --- a/tests/assembly-llvm/nvptx-c-abi-ret-v7.rs +++ b/tests/assembly-llvm/nvptx-c-abi-ret-v7.rs @@ -1,5 +1,5 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 -Z unstable-options -Clinker-flavor=llbc +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 //@ only-nvptx64 // The PTX ABI stability is tied to major versions of the PTX ISA diff --git a/tests/assembly-llvm/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs b/tests/assembly-llvm/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs index f245b4460f2b..00f6fe9332d9 100644 --- a/tests/assembly-llvm/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs +++ b/tests/assembly-llvm/nvptx-kernel-abi/nvptx-kernel-args-abi-v7.rs @@ -1,5 +1,5 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 -Z unstable-options -Clinker-flavor=llbc +//@ compile-flags: --crate-type cdylib -C target-cpu=sm_86 //@ only-nvptx64 // The following ABI tests are made with nvcc 11.6 does. diff --git a/tests/assembly-llvm/nvptx-safe-naming.rs b/tests/assembly-llvm/nvptx-safe-naming.rs index c70c63b7ee7c..85415b6ca5b5 100644 --- a/tests/assembly-llvm/nvptx-safe-naming.rs +++ b/tests/assembly-llvm/nvptx-safe-naming.rs @@ -1,5 +1,5 @@ //@ assembly-output: ptx-linker -//@ compile-flags: --crate-type cdylib -Z unstable-options -Clinker-flavor=llbc +//@ compile-flags: --crate-type cdylib //@ only-nvptx64 //@ revisions: LLVM20 LLVM21 //@ [LLVM21] min-llvm-version: 21 diff --git a/tests/assembly-llvm/slp-vectorize-closure.rs b/tests/assembly-llvm/slp-vectorize-closure.rs new file mode 100644 index 000000000000..fcdb469a2413 --- /dev/null +++ b/tests/assembly-llvm/slp-vectorize-closure.rs @@ -0,0 +1,28 @@ +//! Loop inside closure correctly compiled to assembly using SSE instructions. +//! Regression test for . +//! also see +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 +//@ only-x86_64 +// to test SSE instructions + +#![crate_type = "lib"] + +// CHECK-LABEL: for_in_closure +// CHECK: {{paddb|psubb}} +#[inline(never)] +#[no_mangle] +pub fn for_in_closure() { + let mut v = [[0u8; 4]; 60]; + + let mut closure = || { + for item in &mut v { + item[0] += 1; + item[1] += 1; + item[2] += 1; + item[3] += 1; + } + }; + + closure(); +} diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs index 3287e018b404..d1b51068fce5 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-32bit.rs @@ -7,7 +7,7 @@ //@ [strong] compile-flags: -Z stack-protector=strong //@ [basic] compile-flags: -Z stack-protector=basic //@ [none] compile-flags: -Z stack-protector=none -//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled +//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled -Cpanic=abort -Cdebuginfo=1 #![crate_type = "lib"] #![allow(internal_features)] @@ -39,6 +39,8 @@ pub fn array_char(f: fn(*const char)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: array_u8_1 @@ -55,6 +57,8 @@ pub fn array_u8_1(f: fn(*const u8)) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: array_u8_small: @@ -72,6 +76,8 @@ pub fn array_u8_small(f: fn(*const u8)) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: array_u8_large: @@ -88,6 +94,8 @@ pub fn array_u8_large(f: fn(*const u8)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } #[derive(Copy, Clone)] @@ -107,6 +115,8 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: local_var_addr_used_indirectly @@ -134,6 +144,8 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: local_string_addr_taken @@ -143,28 +155,15 @@ pub fn local_string_addr_taken(f: fn(&String)) { f(&x); // Taking the address of the local variable `x` leads to stack smash - // protection with the `strong` heuristic, but not with the `basic` - // heuristic. It does not matter that the reference is not mut. - // - // An interesting note is that a similar function in C++ *would* be - // protected by the `basic` heuristic, because `std::string` has a char - // array internally as a small object optimization: - // ``` - // cat < - // void f(void (*g)(const std::string&)) { - // std::string x; - // g(x); - // } - // EOF - // ``` - // + // protection. It does not matter that the reference is not mut. // all: __security_check_cookie - // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie + // strong: __security_check_cookie + // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } pub trait SelfByRef { @@ -194,6 +193,8 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32 // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } pub struct Gigastruct { @@ -231,6 +232,8 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: local_large_var_cloned @@ -260,6 +263,8 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } extern "C" { @@ -300,6 +305,8 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: alloca_large_compile_time_constant_arg @@ -312,6 +319,8 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // CHECK-LABEL: alloca_dynamic_arg @@ -324,14 +333,14 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } // The question then is: in what ways can Rust code generate array-`alloca` // LLVM instructions? This appears to only be generated by // rustc_codegen_ssa::traits::Builder::array_alloca() through -// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). FWICT -// this is support for the "unsized locals" unstable feature: -// https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html. +// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). // CHECK-LABEL: unsized_fn_param #[no_mangle] @@ -346,14 +355,11 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { // alloca, and is therefore not protected by the `strong` or `basic` // heuristics. - // We should have a __security_check_cookie call in `all` and `strong` modes but - // LLVM does not support generating stack protectors in functions with funclet - // based EH personalities. - // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4 // all-NOT: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .cv_fpo_endproc } diff --git a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs index 9a3dabc74dde..56a6f08da587 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-heuristics-effect-windows-64bit.rs @@ -7,7 +7,7 @@ //@ [strong] compile-flags: -Z stack-protector=strong //@ [basic] compile-flags: -Z stack-protector=basic //@ [none] compile-flags: -Z stack-protector=none -//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled +//@ compile-flags: -C opt-level=2 -Z merge-functions=disabled -Cpanic=abort #![crate_type = "lib"] #![feature(unsized_fn_params)] @@ -25,6 +25,7 @@ pub fn emptyfn() { // CHECK-LABEL: array_char #[no_mangle] pub fn array_char(f: fn(*const char)) { + // CHECK-DAG: .seh_endprologue let a = ['c'; 1]; let b = ['d'; 3]; let c = ['e'; 15]; @@ -38,11 +39,14 @@ pub fn array_char(f: fn(*const char)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: array_u8_1 #[no_mangle] pub fn array_u8_1(f: fn(*const u8)) { + // CHECK-DAG: .seh_endprologue let a = [0u8; 1]; f(&a as *const _); @@ -54,11 +58,14 @@ pub fn array_u8_1(f: fn(*const u8)) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: array_u8_small: #[no_mangle] pub fn array_u8_small(f: fn(*const u8)) { + // CHECK-DAG: .seh_endprologue let a = [0u8; 2]; let b = [0u8; 7]; f(&a as *const _); @@ -71,11 +78,14 @@ pub fn array_u8_small(f: fn(*const u8)) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: array_u8_large: #[no_mangle] pub fn array_u8_large(f: fn(*const u8)) { + // CHECK-DAG: .seh_endprologue let a = [0u8; 9]; f(&a as *const _); @@ -87,6 +97,8 @@ pub fn array_u8_large(f: fn(*const u8)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } #[derive(Copy, Clone)] @@ -95,6 +107,7 @@ pub struct ByteSizedNewtype(u8); // CHECK-LABEL: array_bytesizednewtype_9: #[no_mangle] pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { + // CHECK-DAG: .seh_endprologue let a = [ByteSizedNewtype(0); 9]; f(&a as *const _); @@ -106,11 +119,14 @@ pub fn array_bytesizednewtype_9(f: fn(*const ByteSizedNewtype)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: local_var_addr_used_indirectly #[no_mangle] pub fn local_var_addr_used_indirectly(f: fn(bool)) { + // CHECK-DAG: .seh_endprologue let a = 5; let a_addr = &a as *const _ as usize; f(a_addr & 0x10 == 0); @@ -133,6 +149,8 @@ pub fn local_var_addr_used_indirectly(f: fn(bool)) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: local_string_addr_taken @@ -143,31 +161,11 @@ pub fn local_string_addr_taken(f: fn(&String)) { f(&x); // Taking the address of the local variable `x` leads to stack smash - // protection with the `strong` heuristic, but not with the `basic` - // heuristic. It does not matter that the reference is not mut. - // - // An interesting note is that a similar function in C++ *would* be - // protected by the `basic` heuristic, because `std::string` has a char - // array internally as a small object optimization: - // ``` - // cat < - // void f(void (*g)(const std::string&)) { - // std::string x; - // g(x); - // } - // EOF - // ``` - // + // protection. It does not matter that the reference is not mut. - // We should have a __security_check_cookie call in `all` and `strong` modes but - // LLVM does not support generating stack protectors in functions with funclet - // based EH personalities. - // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4 - // all-NOT: __security_check_cookie - // strong-NOT: __security_check_cookie - - // basic-NOT: __security_check_cookie + // all: __security_check_cookie + // strong: __security_check_cookie + // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie @@ -187,6 +185,7 @@ impl SelfByRef for i32 { // CHECK-LABEL: local_var_addr_taken_used_locally_only #[no_mangle] pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32)) { + // CHECK-DAG: .seh_endprologue let x = factory(); let g = x.f(); sink(g); @@ -201,6 +200,8 @@ pub fn local_var_addr_taken_used_locally_only(factory: fn() -> i32, sink: fn(i32 // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } pub struct Gigastruct { @@ -214,6 +215,7 @@ pub struct Gigastruct { // CHECK-LABEL: local_large_var_moved #[no_mangle] pub fn local_large_var_moved(f: fn(Gigastruct)) { + // CHECK-DAG: .seh_endprologue let x = Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 }; f(x); @@ -238,11 +240,14 @@ pub fn local_large_var_moved(f: fn(Gigastruct)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: local_large_var_cloned #[no_mangle] pub fn local_large_var_cloned(f: fn(Gigastruct)) { + // CHECK-DAG: .seh_endprologue f(Gigastruct { does: 0, not: 1, have: 2, array: 3, members: 4 }); // A new instance of `Gigastruct` is passed to `f()`, without any apparent @@ -267,6 +272,8 @@ pub fn local_large_var_cloned(f: fn(Gigastruct)) { // basic: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } extern "C" { @@ -300,6 +307,7 @@ extern "C" { // CHECK-LABEL: alloca_small_compile_time_constant_arg #[no_mangle] pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { + // CHECK-DAG: .seh_endprologue f(unsafe { alloca(8) }); // all: __security_check_cookie @@ -307,11 +315,14 @@ pub fn alloca_small_compile_time_constant_arg(f: fn(*mut ())) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: alloca_large_compile_time_constant_arg #[no_mangle] pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { + // CHECK-DAG: .seh_endprologue f(unsafe { alloca(9) }); // all: __security_check_cookie @@ -319,11 +330,14 @@ pub fn alloca_large_compile_time_constant_arg(f: fn(*mut ())) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // CHECK-LABEL: alloca_dynamic_arg #[no_mangle] pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { + // CHECK-DAG: .seh_endprologue f(unsafe { alloca(n) }); // all: __security_check_cookie @@ -331,18 +345,19 @@ pub fn alloca_dynamic_arg(f: fn(*mut ()), n: usize) { // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } // The question then is: in what ways can Rust code generate array-`alloca` // LLVM instructions? This appears to only be generated by // rustc_codegen_ssa::traits::Builder::array_alloca() through -// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). FWICT -// this is support for the "unsized locals" unstable feature: -// https://doc.rust-lang.org/unstable-book/language-features/unsized-locals.html. +// rustc_codegen_ssa::mir::operand::OperandValue::store_unsized(). // CHECK-LABEL: unsized_fn_param #[no_mangle] pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { + // CHECK-DAG: .seh_endprologue let n = if l { 1 } else { 2 }; f(*Box::<[u8]>::from(&s[0..n])); // slice-copy with Box::from @@ -353,14 +368,11 @@ pub fn unsized_fn_param(s: [u8], l: bool, f: fn([u8])) { // alloca, and is therefore not protected by the `strong` or `basic` // heuristics. - // We should have a __security_check_cookie call in `all` and `strong` modes but - // LLVM does not support generating stack protectors in functions with funclet - // based EH personalities. - // https://github.com/llvm/llvm-project/blob/37fd3c96b917096d8a550038f6e61cdf0fc4174f/llvm/lib/CodeGen/StackProtector.cpp#L103C1-L109C4 // all-NOT: __security_check_cookie // strong-NOT: __security_check_cookie - // basic-NOT: __security_check_cookie // none-NOT: __security_check_cookie // missing-NOT: __security_check_cookie + + // CHECK-DAG: .seh_endproc } diff --git a/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs b/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs index 0218fe0a52f8..9f182985d157 100644 --- a/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs +++ b/tests/assembly-llvm/stack-protector/stack-protector-target-support.rs @@ -173,7 +173,7 @@ //@ [r84] needs-llvm-components: x86 //@ [r85] compile-flags: --target x86_64-unknown-redox //@ [r85] needs-llvm-components: x86 -//@ compile-flags: -Z stack-protector=all +//@ compile-flags: -Z stack-protector=all -Cpanic=abort //@ compile-flags: -C opt-level=2 #![crate_type = "lib"] diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index 324c7fc9da4e..c8b81cc858d6 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -520,6 +520,9 @@ //@ revisions: riscv64gc_unknown_redox //@ [riscv64gc_unknown_redox] compile-flags: --target riscv64gc-unknown-redox //@ [riscv64gc_unknown_redox] needs-llvm-components: riscv +//@ revisions: riscv64im_unknown_none_elf +//@ [riscv64im_unknown_none_elf] compile-flags: --target riscv64im-unknown-none-elf +//@ [riscv64im_unknown_none_elf] needs-llvm-components: riscv //@ revisions: riscv64imac_unknown_none_elf //@ [riscv64imac_unknown_none_elf] compile-flags: --target riscv64imac-unknown-none-elf //@ [riscv64imac_unknown_none_elf] needs-llvm-components: riscv diff --git a/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs b/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs index 7b5679c3f4de..5b3cd34a7fa5 100644 --- a/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs +++ b/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs @@ -23,3 +23,8 @@ fn inner() -> String { pub fn leaf_with_intrinsic(a: &[u64; 2], b: &[u64; 2]) -> bool { a == b } + +// This function's optimized MIR contains assert terminators, not calls. +pub fn leaf_with_assert(a: i32, b: i32) -> i32 { + a / b +} diff --git a/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs b/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs index 5e7912791ad6..3173adbd03ee 100644 --- a/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs +++ b/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs @@ -25,3 +25,12 @@ pub fn leaf_with_intrinsic_outer(a: &[u64; 2], b: &[u64; 2]) -> bool { // CHECK-NOT: call {{.*}}leaf_with_intrinsic leaf::leaf_with_intrinsic(a, b) } + +// Check that we inline functions with assert terminators +#[no_mangle] +pub fn leaf_with_assert(a: i32, b: i32) -> i32 { + // CHECK-NOT: call {{.*}}leaf_with_assert + // CHECK: sdiv i32 %a, %b + // CHECK-NOT: call {{.*}}leaf_with_assert + leaf::leaf_with_assert(a, b) +} diff --git a/tests/codegen-llvm/function-arguments.rs b/tests/codegen-llvm/function-arguments.rs index 953b654555d5..4d557470504e 100644 --- a/tests/codegen-llvm/function-arguments.rs +++ b/tests/codegen-llvm/function-arguments.rs @@ -85,7 +85,7 @@ pub fn option_nonzero_int(x: Option>) -> Option> { #[no_mangle] pub fn readonly_borrow(_: &i32) {} -// CHECK: noundef align 4 dereferenceable(4) ptr @readonly_borrow_ret() +// CHECK: noundef nonnull align 4 ptr @readonly_borrow_ret() #[no_mangle] pub fn readonly_borrow_ret() -> &'static i32 { loop {} @@ -116,7 +116,7 @@ pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {} #[no_mangle] pub fn mutable_borrow(_: &mut i32) {} -// CHECK: noundef align 4 dereferenceable(4) ptr @mutable_borrow_ret() +// CHECK: noundef nonnull align 4 ptr @mutable_borrow_ret() #[no_mangle] pub fn mutable_borrow_ret() -> &'static mut i32 { loop {} diff --git a/tests/codegen-llvm/gpu_offload/gpu_host.rs b/tests/codegen-llvm/gpu_offload/gpu_host.rs index b0f83c825705..ac179a65828d 100644 --- a/tests/codegen-llvm/gpu_offload/gpu_host.rs +++ b/tests/codegen-llvm/gpu_offload/gpu_host.rs @@ -11,6 +11,7 @@ // when inside of a function called main. This, too, is a temporary workaround for not having a // frontend. +#![feature(rustc_attrs)] #![feature(core_intrinsics)] #![no_main] @@ -21,29 +22,31 @@ fn main() { core::hint::black_box(&x); } -// CHECK: %struct.__tgt_offload_entry = type { i64, i16, i16, i32, ptr, ptr, i64, i64, ptr } // CHECK: %struct.ident_t = type { i32, i32, i32, i32, ptr } +// CHECK: %struct.__tgt_offload_entry = type { i64, i16, i16, i32, ptr, ptr, i64, i64, ptr } // CHECK: %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr } // CHECK: %struct.__tgt_kernel_arguments = type { i32, i32, ptr, ptr, ptr, ptr, ptr, ptr, i64, i64, [3 x i32], [3 x i32], i32 } -// CHECK: @.offload_sizes._kernel_1 = private unnamed_addr constant [1 x i64] [i64 1024] -// CHECK: @.offload_maptypes._kernel_1 = private unnamed_addr constant [1 x i64] [i64 35] -// CHECK: @._kernel_1.region_id = internal unnamed_addr constant i8 0 -// CHECK: @.offloading.entry_name._kernel_1 = internal unnamed_addr constant [10 x i8] c"_kernel_1\00", section ".llvm.rodata.offloading", align 1 -// CHECK: @.offloading.entry._kernel_1 = internal constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 0, ptr @._kernel_1.region_id, ptr @.offloading.entry_name._kernel_1, i64 0, i64 0, ptr null }, section "llvm_offload_entries", align 8 - // CHECK: @anon.{{.*}}.0 = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1 // CHECK: @anon.{{.*}}.1 = private unnamed_addr constant %struct.ident_t { i32 0, i32 2, i32 0, i32 22, ptr @anon.{{.*}}.0 }, align 8 -// CHECK: Function Attrs: -// CHECK-NEXT: define{{( dso_local)?}} void @main() +// CHECK: @.offload_sizes._kernel_1 = private unnamed_addr constant [1 x i64] [i64 1024] +// CHECK: @.offload_maptypes._kernel_1 = private unnamed_addr constant [1 x i64] [i64 35] +// CHECK: @._kernel_1.region_id = internal constant i8 0 +// CHECK: @.offloading.entry_name._kernel_1 = internal unnamed_addr constant [10 x i8] c"_kernel_1\00", section ".llvm.rodata.offloading", align 1 +// CHECK: @.offloading.entry._kernel_1 = internal constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 0, ptr @._kernel_1.region_id, ptr @.offloading.entry_name._kernel_1, i64 0, i64 0, ptr null }, section "llvm_offload_entries", align 8 + +// CHECK: Function Attrs: nounwind +// CHECK: declare i32 @__tgt_target_kernel(ptr, i64, i32, i32, ptr, ptr) + +// CHECK: define{{( dso_local)?}} void @main() // CHECK-NEXT: start: // CHECK-NEXT: %0 = alloca [8 x i8], align 8 // CHECK-NEXT: %x = alloca [1024 x i8], align 16 // CHECK: call void @kernel_1(ptr noalias noundef nonnull align 4 dereferenceable(1024) %x) // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr nonnull %0) // CHECK-NEXT: store ptr %x, ptr %0, align 8 -// CHECK-NEXT: call void asm sideeffect "", "r,~{memory}"(ptr nonnull %0) #4, !srcloc !4 +// CHECK-NEXT: call void asm sideeffect "", "r,~{memory}"(ptr nonnull %0) // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr nonnull %0) // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 1024, ptr nonnull %x) // CHECK-NEXT: ret void @@ -92,9 +95,6 @@ fn main() { // CHECK-NEXT: ret void // CHECK-NEXT: } -// CHECK: Function Attrs: nounwind -// CHECK: declare i32 @__tgt_target_kernel(ptr, i64, i32, i32, ptr, ptr) - #[unsafe(no_mangle)] #[inline(never)] pub fn kernel_1(x: &mut [f32; 256]) { diff --git a/tests/codegen-llvm/scalable-vectors/simple.rs b/tests/codegen-llvm/scalable-vectors/simple.rs new file mode 100644 index 000000000000..b3530e19ff33 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/simple.rs @@ -0,0 +1,49 @@ +//@ edition: 2021 +//@ only-aarch64 +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(simd_ffi, rustc_attrs, link_llvm_intrinsics)] + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svint32_t(i32); + +#[inline(never)] +#[target_feature(enable = "sve")] +pub unsafe fn svdup_n_s32(op: i32) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4i32")] + fn _svdup_n_s32(op: i32) -> svint32_t; + } + unsafe { _svdup_n_s32(op) } +} + +#[inline] +#[target_feature(enable = "sve,sve2")] +pub unsafe fn svxar_n_s32(op1: svint32_t, op2: svint32_t) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.xar.nxv4i32")] + fn _svxar_n_s32(op1: svint32_t, op2: svint32_t, imm3: i32) -> svint32_t; + } + unsafe { _svxar_n_s32(op1, op2, IMM3) } +} + +#[inline(never)] +#[no_mangle] +#[target_feature(enable = "sve,sve2")] +// CHECK: define @pass_as_ref(ptr {{.*}}align 16{{.*}} %a, %b) +pub unsafe fn pass_as_ref(a: &svint32_t, b: svint32_t) -> svint32_t { + // CHECK: load , ptr %a, align 16 + svxar_n_s32::<1>(*a, b) +} + +#[no_mangle] +#[target_feature(enable = "sve,sve2")] +// CHECK: define @test() +pub unsafe fn test() -> svint32_t { + let a = svdup_n_s32(1); + let b = svdup_n_s32(2); + // CHECK: %_0 = call @pass_as_ref(ptr {{.*}}align 16{{.*}} %a, %b) + pass_as_ref(&a, b) +} diff --git a/tests/codegen-units/item-collection/implicit-panic-call.rs b/tests/codegen-units/item-collection/implicit-panic-call.rs index 612132f056be..3b442942cc5c 100644 --- a/tests/codegen-units/item-collection/implicit-panic-call.rs +++ b/tests/codegen-units/item-collection/implicit-panic-call.rs @@ -1,5 +1,6 @@ // rust-lang/rust#90405 // Ensure implicit panic calls are collected +//@ compile-flags: -Zcross-crate-inline-threshold=never #![feature(lang_items)] #![feature(no_core)] diff --git a/tests/crashes/119786.rs b/tests/crashes/119786-1.rs similarity index 100% rename from tests/crashes/119786.rs rename to tests/crashes/119786-1.rs diff --git a/tests/crashes/119786-2.rs b/tests/crashes/119786-2.rs new file mode 100644 index 000000000000..76c5deb4605a --- /dev/null +++ b/tests/crashes/119786-2.rs @@ -0,0 +1,15 @@ +//@ known-bug: #119786 +//@ edition:2021 + +fn enum_upvar() { + type T = impl Copy; + let foo: T = Some((1u32, 2u32)); + let x = move || { + match foo { + None => (), + Some(_) => (), + } + }; +} + +pub fn main() {} diff --git a/tests/crashes/119786-3.rs b/tests/crashes/119786-3.rs new file mode 100644 index 000000000000..34bb90fd0fae --- /dev/null +++ b/tests/crashes/119786-3.rs @@ -0,0 +1,15 @@ +//@ known-bug: #119786 +//@ edition:2021 + +fn enum_upvar() { + type T = impl Copy; + let foo: T = Some((1u32, 2u32)); + let x = move || { + match foo { + None => (), + Some((a, b)) => (), + } + }; +} + +pub fn main() {} diff --git a/tests/crashes/137467-1.rs b/tests/crashes/137467-1.rs deleted file mode 100644 index b6bff2bdc4e8..000000000000 --- a/tests/crashes/137467-1.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: #137467 -//@ edition: 2021 -enum Camera { - Normal { base_transform: i32 }, - Volume { transform: i32 }, -} - -fn draw_ui(camera: &mut Camera) { - || { - let (Camera::Normal { - base_transform: _transform, - } - | Camera::Volume { - transform: _transform, - }) = camera; - }; -} diff --git a/tests/crashes/137467-2.rs b/tests/crashes/137467-2.rs deleted file mode 100644 index a70ea92b22dc..000000000000 --- a/tests/crashes/137467-2.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ known-bug: #137467 -//@ edition: 2021 - -enum Camera { - Normal { base_transform: i32 }, - Volume { transform: i32 }, -} - -fn draw_ui(camera: &mut Camera) { - || { - let (Camera::Normal { - base_transform: _, - } - | Camera::Volume { - transform: _, - }) = camera; - }; -} diff --git a/tests/crashes/137467-3.rs b/tests/crashes/137467-3.rs deleted file mode 100644 index cb81a9a912e7..000000000000 --- a/tests/crashes/137467-3.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ known-bug: #137467 -//@ edition: 2021 - -fn meow(x: (u32, u32, u32)) { - let f = || { - let ((0, a, _) | (_, _, a)) = x; - }; -} diff --git a/tests/crashes/140011.rs b/tests/crashes/140011.rs deleted file mode 100644 index b9d57a2822d2..000000000000 --- a/tests/crashes/140011.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: #140011 -//@compile-flags: -Wrust-2021-incompatible-closure-captures -enum b { - c(d), - e(f), -} -struct f; -fn g() { - let h; - || b::e(a) = h; -} diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir index b43af549b232..9ff1a90ab820 100644 --- a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir +++ b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir @@ -4,18 +4,16 @@ fn foo::{closure#0}::{closure#0}(_1: {async closure body@$DIR/async_closure_fake yields () { debug _task_context => _2; - debug f => (*(_1.0: &&Foo)); + debug f => (*(_1.0: &Foo)); let mut _0: (); let mut _3: &Foo; let mut _4: &&Foo; - let mut _5: &&&Foo; - let mut _6: isize; - let mut _7: bool; + let mut _5: isize; + let mut _6: bool; bb0: { - PlaceMention((*(_1.0: &&Foo))); - _6 = discriminant((*(*(_1.0: &&Foo)))); - switchInt(move _6) -> [0: bb2, otherwise: bb1]; + _5 = discriminant((*(_1.0: &Foo))); + switchInt(move _5) -> [0: bb2, otherwise: bb1]; } bb1: { @@ -32,17 +30,15 @@ yields () } bb4: { - FakeRead(ForMatchedPlace(None), (*(_1.0: &&Foo))); unreachable; } bb5: { - _3 = &fake shallow (*(*(_1.0: &&Foo))); - _4 = &fake shallow (*(_1.0: &&Foo)); - _5 = &fake shallow (_1.0: &&Foo); - StorageLive(_7); - _7 = const true; - switchInt(move _7) -> [0: bb8, otherwise: bb7]; + _3 = &fake shallow (*(_1.0: &Foo)); + _4 = &fake shallow (_1.0: &Foo); + StorageLive(_6); + _6 = const true; + switchInt(move _6) -> [0: bb8, otherwise: bb7]; } bb6: { @@ -50,10 +46,9 @@ yields () } bb7: { - StorageDead(_7); + StorageDead(_6); FakeRead(ForMatchGuard, _3); FakeRead(ForMatchGuard, _4); - FakeRead(ForMatchGuard, _5); _0 = const (); goto -> bb10; } @@ -63,7 +58,7 @@ yields () } bb9: { - StorageDead(_7); + StorageDead(_6); goto -> bb6; } diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir index 5623b6d64e97..4b745caf48c5 100644 --- a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir @@ -4,18 +4,16 @@ fn foo::{closure#0}::{synthetic#0}(_1: {async closure body@$DIR/async_closure_fa yields () { debug _task_context => _2; - debug f => (_1.0: &Foo); + debug f => (*(_1.0: &Foo)); let mut _0: (); let mut _3: &Foo; let mut _4: &&Foo; - let mut _5: &&&Foo; - let mut _6: isize; - let mut _7: bool; + let mut _5: isize; + let mut _6: bool; bb0: { - PlaceMention((_1.0: &Foo)); - _6 = discriminant((*(_1.0: &Foo))); - switchInt(move _6) -> [0: bb2, otherwise: bb1]; + _5 = discriminant((*(_1.0: &Foo))); + switchInt(move _5) -> [0: bb2, otherwise: bb1]; } bb1: { @@ -29,24 +27,22 @@ yields () bb3: { _3 = &fake shallow (*(_1.0: &Foo)); - _4 = &fake shallow (_1.0: &Foo); nop; - StorageLive(_7); - _7 = const true; - switchInt(move _7) -> [0: bb5, otherwise: bb4]; + StorageLive(_6); + _6 = const true; + switchInt(move _6) -> [0: bb5, otherwise: bb4]; } bb4: { - StorageDead(_7); + StorageDead(_6); FakeRead(ForMatchGuard, _3); FakeRead(ForMatchGuard, _4); - FakeRead(ForMatchGuard, _5); _0 = const (); goto -> bb6; } bb5: { - StorageDead(_7); + StorageDead(_6); falseEdge -> [real: bb1, imaginary: bb1]; } diff --git a/tests/run-make-cargo/same-crate-name-and-macro-name/consumer/Cargo.toml b/tests/run-make-cargo/same-crate-name-and-macro-name/consumer/Cargo.toml new file mode 100644 index 000000000000..870d333da3a4 --- /dev/null +++ b/tests/run-make-cargo/same-crate-name-and-macro-name/consumer/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "consumer" +version = "0.1.0" + +[dependencies] +mylib_v1 = { path = "../mylib_v1", package = "mylib" } +mylib_v2 = { path = "../mylib_v2", package = "mylib" } + +# Avoid interference with root workspace when casually testing +[workspace] diff --git a/tests/run-make-cargo/same-crate-name-and-macro-name/consumer/src/main.rs b/tests/run-make-cargo/same-crate-name-and-macro-name/consumer/src/main.rs new file mode 100644 index 000000000000..80e76ae28117 --- /dev/null +++ b/tests/run-make-cargo/same-crate-name-and-macro-name/consumer/src/main.rs @@ -0,0 +1,7 @@ +fn main() { + let v1 = mylib_v1::my_macro!(); + assert_eq!(v1, "version 1"); + + let v2 = mylib_v2::my_macro!(); + assert_eq!(v2, "version 2"); +} diff --git a/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v1/Cargo.toml b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v1/Cargo.toml new file mode 100644 index 000000000000..69f80bfc89ed --- /dev/null +++ b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v1/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "mylib" +version = "1.0.0" + +# Avoid interference with root workspace when casually testing +[workspace] diff --git a/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v1/src/lib.rs b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v1/src/lib.rs new file mode 100644 index 000000000000..0603b754ea82 --- /dev/null +++ b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v1/src/lib.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! my_macro { + () => { + "version 1" + }; +} diff --git a/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v2/Cargo.toml b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v2/Cargo.toml new file mode 100644 index 000000000000..f164616393ba --- /dev/null +++ b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v2/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "mylib" +version = "2.0.0" + +# Avoid interference with root workspace when casually testing +[workspace] diff --git a/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v2/src/lib.rs b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v2/src/lib.rs new file mode 100644 index 000000000000..352c7c514f83 --- /dev/null +++ b/tests/run-make-cargo/same-crate-name-and-macro-name/mylib_v2/src/lib.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! my_macro { + () => { + "version 2" + }; +} diff --git a/tests/run-make-cargo/same-crate-name-and-macro-name/rmake.rs b/tests/run-make-cargo/same-crate-name-and-macro-name/rmake.rs new file mode 100644 index 000000000000..cce98cebb804 --- /dev/null +++ b/tests/run-make-cargo/same-crate-name-and-macro-name/rmake.rs @@ -0,0 +1,13 @@ +//! Regression test for +//! +//! (that particular comment describes the issue well). +//! +//! We test that two library crates with the same name can export macros with +//! the same name without causing interference when both are used in another +//! crate. + +use run_make_support::cargo; + +fn main() { + cargo().current_dir("consumer").arg("run").run(); +} diff --git a/tests/run-make/branch-protection-check-IBT/lib.rs b/tests/run-make/branch-protection-check-IBT/lib.rs new file mode 100644 index 000000000000..0c9ac1ac8e4b --- /dev/null +++ b/tests/run-make/branch-protection-check-IBT/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/tests/run-make/branch-protection-check-IBT/main.rs b/tests/run-make/branch-protection-check-IBT/main.rs deleted file mode 100644 index 445b8795134c..000000000000 --- a/tests/run-make/branch-protection-check-IBT/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![feature(no_core)] -#![allow(internal_features)] -#![no_core] -#![no_std] -#![no_main] diff --git a/tests/run-make/branch-protection-check-IBT/rmake.rs b/tests/run-make/branch-protection-check-IBT/rmake.rs index 73109df12ae9..34f0693562c0 100644 --- a/tests/run-make/branch-protection-check-IBT/rmake.rs +++ b/tests/run-make/branch-protection-check-IBT/rmake.rs @@ -41,13 +41,19 @@ use run_make_support::{bare_rustc, llvm_readobj}; fn main() { - // `main.rs` is `#![no_std]` to not pull in the currently not-compiled-with-IBT precompiled std. + // `lib.rs` is `#![no_std]` to not pull in the currently not-compiled-with-IBT precompiled std. bare_rustc() - .input("main.rs") + .input("lib.rs") + .crate_type("lib") + .emit("obj=lib.o") .target("x86_64-unknown-linux-gnu") .arg("-Zcf-protection=branch") - .arg("-Clink-args=-nostartfiles") .run(); - llvm_readobj().arg("-nW").input("main").run().assert_stdout_contains(".note.gnu.property"); + llvm_readobj() + .arg("-nW") + .input("lib.o") + .run() + .assert_stdout_contains(".note.gnu.property") + .assert_stdout_contains("feature: IBT"); } diff --git a/tests/run-make/doctests-junit/doctest-2021.xml b/tests/run-make/doctests-junit/doctest-2021.xml new file mode 100644 index 000000000000..5facfb80ce62 --- /dev/null +++ b/tests/run-make/doctests-junit/doctest-2021.xml @@ -0,0 +1 @@ + diff --git a/tests/run-make/doctests-junit/doctest-2024.xml b/tests/run-make/doctests-junit/doctest-2024.xml new file mode 100644 index 000000000000..4f94f01c3e32 --- /dev/null +++ b/tests/run-make/doctests-junit/doctest-2024.xml @@ -0,0 +1,3 @@ + + + diff --git a/tests/run-make/doctests-junit/doctest.rs b/tests/run-make/doctests-junit/doctest.rs new file mode 100644 index 000000000000..1873d63a49c6 --- /dev/null +++ b/tests/run-make/doctests-junit/doctest.rs @@ -0,0 +1,14 @@ +/// ``` +/// assert_eq!(doctest::add(2, 2), 4); +/// ``` +/// +/// ```should_panic +/// assert_eq!(doctest::add(2, 2), 5); +/// ``` +/// +/// ```compile_fail +/// assert_eq!(doctest::add(2, 2), "banana"); +/// ``` +pub fn add(a: i32, b: i32) -> i32 { + a + b +} diff --git a/tests/run-make/doctests-junit/rmake.rs b/tests/run-make/doctests-junit/rmake.rs new file mode 100644 index 000000000000..e3198502bb17 --- /dev/null +++ b/tests/run-make/doctests-junit/rmake.rs @@ -0,0 +1,45 @@ +// Check rustdoc's test JUnit (XML) output against snapshots. + +//@ ignore-cross-compile (running doctests) +//@ ignore-stage1 (rustdoc depends on a fix in libtest) +//@ needs-unwind (test file contains `should_panic` test) + +use std::path::Path; + +use run_make_support::{cwd, diff, python_command, rustc, rustdoc}; + +fn main() { + let rlib = cwd().join("libdoctest.rlib"); + rustc().input("doctest.rs").crate_type("rlib").output(&rlib).run(); + + run_doctests(&rlib, "2021", "doctest-2021.xml"); + run_doctests(&rlib, "2024", "doctest-2024.xml"); +} + +#[track_caller] +fn run_doctests(rlib: &Path, edition: &str, expected_xml: &str) { + let rustdoc_out = rustdoc() + .input("doctest.rs") + .args(&[ + "--test", + "--test-args=-Zunstable-options", + "--test-args=--test-threads=1", + "--test-args=--format=junit", + ]) + .edition(edition) + .env("RUST_BACKTRACE", "0") + .extern_("doctest", rlib.display().to_string()) + .run(); + let rustdoc_stdout = &rustdoc_out.stdout_utf8(); + + // FIXME: merged output of compile_fail tests is broken + if edition != "2024" { + python_command().arg("validate_junit.py").stdin_buf(rustdoc_stdout).run(); + } + + diff() + .expected_file(expected_xml) + .actual_text("output", rustdoc_stdout) + .normalize(r#"\b(time|total_time|compilation_time)="[0-9.]+""#, r#"$1="$$TIME""#) + .run(); +} diff --git a/tests/run-make/doctests-junit/validate_junit.py b/tests/run-make/doctests-junit/validate_junit.py new file mode 100755 index 000000000000..a9cb0a059563 --- /dev/null +++ b/tests/run-make/doctests-junit/validate_junit.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# Trivial Python script that reads lines from stdin, and checks that each line +# is a well-formed XML document. +# +# This takes advantage of the fact that Python has a built-in XML parser, +# whereas doing the same check in Rust would require us to pull in an XML +# crate just for this relatively-minor test. +# +# If you're trying to remove Python scripts from the test suite, think twice +# before removing this one. You could do so, but it's probably not worth it. + +import sys +import xml.etree.ElementTree as ET + +# Read the entire output and try to decode it as XML. +junit = sys.stdin.read() +try: + ET.fromstring(junit) +except ET.ParseError: + print("Invalid xml: %r" % junit) + raise diff --git a/tests/run-make/libtest-junit/validate_junit.py b/tests/run-make/libtest-junit/validate_junit.py index f92473751b03..a9cb0a059563 100755 --- a/tests/run-make/libtest-junit/validate_junit.py +++ b/tests/run-make/libtest-junit/validate_junit.py @@ -13,10 +13,10 @@ import sys import xml.etree.ElementTree as ET -# Try to decode line in order to ensure it is a valid XML document -for line in sys.stdin: - try: - ET.fromstring(line) - except ET.ParseError: - print("Invalid xml: %r" % line) - raise +# Read the entire output and try to decode it as XML. +junit = sys.stdin.read() +try: + ET.fromstring(junit) +except ET.ParseError: + print("Invalid xml: %r" % junit) + raise diff --git a/tests/run-make/remap-path-prefix-std/rmake.rs b/tests/run-make/remap-path-prefix-std/rmake.rs new file mode 100644 index 000000000000..f5179038a9b1 --- /dev/null +++ b/tests/run-make/remap-path-prefix-std/rmake.rs @@ -0,0 +1,53 @@ +// This test makes sure that we do not leak paths to the checkout +// (ie. /checkout in CI) in the distributed `libstd` debuginfo. +// +// This test only runs on Linux and dist builder (or with `rust.remap-debuginfo = true` +// set in your `bootstrap.toml`). + +//@ needs-std-remap-debuginfo +//@ only-linux + +use std::path::PathBuf; + +use run_make_support::{llvm_dwarfdump, rfs, rustc, shallow_find_files, source_root}; + +fn main() { + // Find the target libdir for the current target + let target_libdir = { + let output = rustc().print("target-libdir").run(); + let stdout = output.stdout_utf8(); + let path = PathBuf::from(stdout.trim()); + + // Assert that the target-libdir path exists + assert!(path.exists(), "target-libdir: {path:?} does not exists"); + + path + }; + + // Find all the `libstd-.*.rlib` files under the libdir + let libstd_rlibs = shallow_find_files(&target_libdir, |p| { + if let Some(filename) = p.file_name() + && let filename = filename.to_string_lossy() + { + filename.starts_with("libstd-") && filename.ends_with(".rlib") + } else { + false + } + }); + + // Assert that there is only one rlib for the `libstd` + let [libstd_rlib] = &libstd_rlibs[..] else { + unreachable!("multiple libstd rlib: {libstd_rlibs:?} in {target_libdir:?}"); + }; + + // Symlink the libstd rlib here to avoid absolute paths from llvm-dwarfdump own output + // and not from the debuginfo it-self + rfs::symlink_file(libstd_rlib, "libstd.rlib"); + + // Check that there is only `/rustc/` paths and no `/checkout`, `/home`, or whatever + llvm_dwarfdump() + .input("libstd.rlib") + .run() + .assert_stdout_contains("/rustc/") + .assert_stdout_not_contains(source_root().to_string_lossy()); +} diff --git a/tests/run-make/simd-ffi/rmake.rs b/tests/run-make/simd-ffi/rmake.rs index c6315073fa8c..054ea402a698 100644 --- a/tests/run-make/simd-ffi/rmake.rs +++ b/tests/run-make/simd-ffi/rmake.rs @@ -17,6 +17,9 @@ fn main() { "arm-unknown-linux-gnueabi".to_owned(), ]); } + if llvm_components_contain("amdgpu") { + targets.push("amdgcn-amd-amdhsa".to_owned()); + } let mut x86_archs = Vec::new(); if llvm_components_contain("x86") { x86_archs.append(&mut vec!["i686", "x86_64"]); @@ -52,21 +55,25 @@ fn main() { // enabled by-default for i686 and ARM; these features will be invalid // on some platforms, but LLVM just prints a warning so that's fine for // now. + let mut cmd = rustc(); + cmd.target(&target).emit("llvm-ir,asm").input("simd.rs"); let target_feature = if target.starts_with("i686") || target.starts_with("x86") { "+sse2" } else if target.starts_with("arm") || target.starts_with("aarch64") { "-soft-float,+neon" } else if target.starts_with("mips") { "+msa,+fp64" + } else if target.starts_with("amdgcn") { + cmd.arg("-Ctarget-cpu=gfx900"); + "" } else { panic!("missing target_feature case for {target}"); }; - rustc() - .target(&target) - .emit("llvm-ir,asm") - .input("simd.rs") - .arg(format!("-Ctarget-feature={target_feature}")) - .arg(&format!("-Cextra-filename=-{target}")) - .run(); + + if !target_feature.is_empty() { + cmd.arg(format!("-Ctarget-feature={target_feature}")); + } + + cmd.arg(&format!("-Cextra-filename=-{target}")).run(); } } diff --git a/tests/rustdoc-ui/invalid-cfg.stderr b/tests/rustdoc-ui/invalid-cfg.stderr index 84f8cea54314..539611070969 100644 --- a/tests/rustdoc-ui/invalid-cfg.stderr +++ b/tests/rustdoc-ui/invalid-cfg.stderr @@ -2,7 +2,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:2:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:3:1 @@ -16,7 +18,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:7:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:8:1 @@ -30,7 +34,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:12:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:13:1 @@ -44,7 +50,9 @@ error[E0539]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:18:1 | LL | #[doc(cfg = "x")] - | ^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^-----^^ + | | + | expected this to be a list error[E0805]: malformed `doc` attribute input --> $DIR/invalid-cfg.rs:19:1 diff --git a/tests/rustdoc/auxiliary/remapped-paths.rs b/tests/rustdoc/auxiliary/remapped-paths.rs new file mode 100644 index 000000000000..f31d2d316f3a --- /dev/null +++ b/tests/rustdoc/auxiliary/remapped-paths.rs @@ -0,0 +1,11 @@ +//@ compile-flags:-Zunstable-options --remap-path-prefix={{src-base}}= + +pub struct MyStruct { + field: u32, +} + +impl MyStruct { + pub fn new() -> MyStruct { + MyStruct { field: 3 } + } +} diff --git a/tests/rustdoc/import-remapped-paths.rs b/tests/rustdoc/import-remapped-paths.rs new file mode 100644 index 000000000000..9e7518f7c017 --- /dev/null +++ b/tests/rustdoc/import-remapped-paths.rs @@ -0,0 +1,19 @@ +// This is a regression for `--remap-path-prefix` in an auxiliary dependency. +// +// We want to make sure that we can still have the "Source" links to the dependency +// even if its paths are remapped. +// +// See also rust-lang/rust#150100 + +//@ aux-build:remapped-paths.rs +//@ build-aux-docs + +#![crate_name = "foo"] + +extern crate remapped_paths; + +//@ has foo/struct.MyStruct.html +//@ has - '//a[@href="../src/remapped_paths/remapped-paths.rs.html#3"]' 'Source' +//@ has - '//a[@href="../src/remapped_paths/remapped-paths.rs.html#8"]' 'Source' + +pub use remapped_paths::MyStruct; diff --git a/tests/rustdoc/macro-expansion/field-followed-by-exclamation.rs b/tests/rustdoc/macro-expansion/field-followed-by-exclamation.rs new file mode 100644 index 000000000000..e80b4c4ec873 --- /dev/null +++ b/tests/rustdoc/macro-expansion/field-followed-by-exclamation.rs @@ -0,0 +1,26 @@ +// This test ensures that the macro expansion is correctly handled in cases like: +// `field: !f!`, because the `:` was simply not considered because of how paths +// are handled. + +//@ compile-flags: -Zunstable-options --generate-macro-expansion + +#![crate_name = "foo"] + +//@ has 'src/foo/field-followed-by-exclamation.rs.html' + +struct Bar { + bla: bool, +} + +macro_rules! f { + () => {{ false }} +} + +const X: Bar = Bar { + //@ has - '//*[@class="expansion"]/*[@class="original"]/*[@class="macro"]' 'f!' + //@ has - '//*[@class="expansion"]/*[@class="original"]' 'f!()' + //@ has - '//*[@class="expansion"]/*[@class="expanded"]' '{ false }' + // It includes both original and expanded code. + //@ has - '//*[@class="expansion"]' ' bla: !{ false }f!()' + bla: !f!(), +}; diff --git a/tests/ui/README.md b/tests/ui/README.md index 25e870fd8f18..da9be2e209ac 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -256,6 +256,10 @@ Some traits' implementation must be compared with their definition, checking for This subdirectory is *not* intended comparison traits (`PartialEq`, `Eq`, `PartialOrd`, `Ord`). +## `tests/ui/compile-flags/` + +Tests for compile flags. + ## `tests/ui/compiletest-self-test/`: compiletest "meta" tests Meta test suite of the test harness `compiletest` itself. @@ -548,6 +552,8 @@ A broad directory for tests on expressions. Tests on the `extern` keyword and `extern` blocks and functions. +**FIXME**: Merge with `tests/ui/abi/extern`. + ## `tests/ui/extern-flag/`: `--extern` command line flag Tests for the `--extern` CLI flag. @@ -556,6 +562,12 @@ Tests for the `--extern` CLI flag. Tests on feature-gating, and the `#![feature(..)]` mechanism itself. +## `tests/ui/ffi/`: Foreign Function Interface + +Tests for the `std::ffi` module. + +See [`std::ffi`](https://doc.rust-lang.org/std/ffi/index.html) + ## `tests/ui/ffi-attrs/`: `#![feature(ffi_const, ffi_pure)]` The `#[ffi_const]` and `#[ffi_pure]` attributes applies clang's `const` and `pure` attributes to foreign functions declarations, respectively. These attributes are the core element of the tests in this category. @@ -697,12 +709,6 @@ Tests on type inference. Tests for diagnostics on infinitely recursive types without indirection. -## `tests/ui/inherent-impls-overlap-check/` - -Checks that repeating the same function names across separate `impl` blocks triggers an informative error, but not if the `impl` are for different types, such as `Bar` and `Bar`. - -NOTE: This should maybe be a subdirectory within another related to duplicate definitions, such as `tests/ui/duplicate/`. - ## `tests/ui/inline-const/` These tests revolve around the inline `const` block that forces the compiler to const-eval its content. @@ -733,15 +739,9 @@ Various tests related to rejecting invalid inputs. **FIXME**: This is rather uninformative, possibly rehome into more meaningful directories. -## `tests/ui/invalid-compile-flags/` +## `tests/ui/io-checks/`: Input Output -Tests for checking that invalid usage of compiler flags are rejected. - -## `tests/ui/io-checks/` - -Contains a single test. The test tries to output a file into an invalid directory with `-o`, then checks that the result is an error, not an internal compiler error. - -**FIXME**: Rehome to invalid compiler flags maybe. +Tests for I/O related behaviour, covering stdout/stderr handling and error propagation. ## `tests/ui/issues/`: Tests directly related to GitHub issues diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index d050bfcbaa17..6d2ac90c0c97 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -41,7 +41,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index 816addd79576..f297aa984dd2 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -52,7 +52,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index 816addd79576..f297aa984dd2 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -52,7 +52,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index 816addd79576..f297aa984dd2 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -52,7 +52,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index d050bfcbaa17..6d2ac90c0c97 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -41,7 +41,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index 816addd79576..f297aa984dd2 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -52,7 +52,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index 04d6f50872a3..8a031b79780a 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -244,7 +244,7 @@ error: fn_abi_of(test_generic) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -328,7 +328,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -400,7 +400,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -479,7 +479,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -552,7 +552,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -629,7 +629,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -701,7 +701,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -779,7 +779,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -851,7 +851,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -962,7 +962,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 85c888c4fae0..00bd3febde4e 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -244,7 +244,7 @@ error: fn_abi_of(test_generic) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -328,7 +328,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -400,7 +400,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -479,7 +479,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -552,7 +552,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -629,7 +629,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -701,7 +701,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -779,7 +779,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -851,7 +851,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -962,7 +962,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 85c888c4fae0..00bd3febde4e 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -244,7 +244,7 @@ error: fn_abi_of(test_generic) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -328,7 +328,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -400,7 +400,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -479,7 +479,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -552,7 +552,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -629,7 +629,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -701,7 +701,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -779,7 +779,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -851,7 +851,7 @@ error: ABIs are not compatible }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -962,7 +962,7 @@ error: fn_abi_of(assoc_test) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index a93982dacfa1..03fe9ea46a4b 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -24,7 +24,7 @@ error: fn_abi_of(extern_c) = FnAbi { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -65,7 +65,7 @@ error: fn_abi_of(extern_c) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -106,7 +106,7 @@ error: fn_abi_of(extern_rust) = FnAbi { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -165,7 +165,7 @@ error: fn_abi_of(extern_rust) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/abi/sysv64-zst.stderr b/tests/ui/abi/sysv64-zst.stderr index 2233e8e4f623..82d3793c3532 100644 --- a/tests/ui/abi/sysv64-zst.stderr +++ b/tests/ui/abi/sysv64-zst.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -41,7 +41,7 @@ error: fn_abi_of(pass_zst) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/annotate-snippet/missing-type.rs b/tests/ui/annotate-snippet/missing-type.rs index 54088def3eea..2d06c13b96ab 100644 --- a/tests/ui/annotate-snippet/missing-type.rs +++ b/tests/ui/annotate-snippet/missing-type.rs @@ -1,5 +1,5 @@ //@ edition: 2015 -//@ compile-flags: --error-format human-annotate-rs -Z unstable-options +//@ compile-flags: --error-format human pub fn main() { let x: Iter; diff --git a/tests/ui/annotate-snippet/multiple-files.rs b/tests/ui/annotate-snippet/multiple-files.rs index c67a31d8f071..060e817bea08 100644 --- a/tests/ui/annotate-snippet/multiple-files.rs +++ b/tests/ui/annotate-snippet/multiple-files.rs @@ -1,5 +1,5 @@ //@ aux-build:other_file.rs -//@ compile-flags: --error-format human-annotate-rs -Z unstable-options +//@ compile-flags: --error-format human extern crate other_file; diff --git a/tests/ui/annotate-snippet/multispan.rs b/tests/ui/annotate-snippet/multispan.rs index c2054f62d24c..adbbef6d42f7 100644 --- a/tests/ui/annotate-snippet/multispan.rs +++ b/tests/ui/annotate-snippet/multispan.rs @@ -1,5 +1,5 @@ //@ proc-macro: multispan.rs -//@ compile-flags: --error-format human-annotate-rs -Z unstable-options +//@ compile-flags: --error-format human #![feature(proc_macro_hygiene)] diff --git a/tests/ui/issues/issue-34074.rs b/tests/ui/anon-params/anon-params-trait-method-multiple.rs similarity index 68% rename from tests/ui/issues/issue-34074.rs rename to tests/ui/anon-params/anon-params-trait-method-multiple.rs index d642c74d412a..ba06aa5115ba 100644 --- a/tests/ui/issues/issue-34074.rs +++ b/tests/ui/anon-params/anon-params-trait-method-multiple.rs @@ -1,3 +1,4 @@ +//! regression test for //@ edition: 2015 //@ check-pass // Make sure several unnamed function parameters don't conflict with each other @@ -7,5 +8,4 @@ trait Tr { fn f(u8, u8) {} } -fn main() { -} +fn main() {} diff --git a/tests/ui/any/try_as_dyn.rs b/tests/ui/any/try_as_dyn.rs new file mode 100644 index 000000000000..ee220f797ced --- /dev/null +++ b/tests/ui/any/try_as_dyn.rs @@ -0,0 +1,29 @@ +//@ run-pass +#![feature(try_as_dyn)] + +use std::fmt::Debug; + +// Look ma, no `T: Debug` +fn debug_format_with_try_as_dyn(t: &T) -> String { + match std::any::try_as_dyn::<_, dyn Debug>(t) { + Some(d) => format!("{d:?}"), + None => "default".to_string() + } +} + +// Test that downcasting to a dyn trait works as expected +fn main() { + #[allow(dead_code)] + #[derive(Debug)] + struct A { + index: usize + } + let a = A { index: 42 }; + let result = debug_format_with_try_as_dyn(&a); + assert_eq!("A { index: 42 }", result); + + struct B {} + let b = B {}; + let result = debug_format_with_try_as_dyn(&b); + assert_eq!("default", result); +} diff --git a/tests/ui/any/try_as_dyn_mut.rs b/tests/ui/any/try_as_dyn_mut.rs new file mode 100644 index 000000000000..ff7baa32ea87 --- /dev/null +++ b/tests/ui/any/try_as_dyn_mut.rs @@ -0,0 +1,20 @@ +//@ run-pass +#![feature(try_as_dyn)] + +use std::fmt::{Error, Write}; + +// Look ma, no `T: Write` +fn try_as_dyn_mut_write(t: &mut T, s: &str) -> Result<(), Error> { + match std::any::try_as_dyn_mut::<_, dyn Write>(t) { + Some(w) => w.write_str(s), + None => Ok(()) + } +} + +// Test that downcasting to a mut dyn trait works as expected +fn main() { + let mut buf = "Hello".to_string(); + + try_as_dyn_mut_write(&mut buf, " world!").unwrap(); + assert_eq!(buf, "Hello world!"); +} diff --git a/tests/ui/any/try_as_dyn_soundness_test1.rs b/tests/ui/any/try_as_dyn_soundness_test1.rs new file mode 100644 index 000000000000..0772e9a2d77e --- /dev/null +++ b/tests/ui/any/try_as_dyn_soundness_test1.rs @@ -0,0 +1,22 @@ +//@ run-pass +#![feature(try_as_dyn)] + +use std::any::try_as_dyn; + +trait Trait { + +} + +impl Trait for for<'a> fn(&'a Box) { + +} + +fn store(_: &'static Box) { + +} + +fn main() { + let fn_ptr: fn(&'static Box) = store; + let dt = try_as_dyn::<_, dyn Trait>(&fn_ptr); + assert!(dt.is_none()); +} diff --git a/tests/ui/any/try_as_dyn_soundness_test2.rs b/tests/ui/any/try_as_dyn_soundness_test2.rs new file mode 100644 index 000000000000..c16b50d0261e --- /dev/null +++ b/tests/ui/any/try_as_dyn_soundness_test2.rs @@ -0,0 +1,16 @@ +//@ run-pass +#![feature(try_as_dyn)] +use std::any::try_as_dyn; + +trait Trait { + +} + +impl Trait fn(&'a Box)> for () { + +} + +fn main() { + let dt = try_as_dyn::<_, dyn Trait)>>(&()); + assert!(dt.is_none()); +} diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs new file mode 100644 index 000000000000..6301a5a8ebf7 --- /dev/null +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs @@ -0,0 +1,24 @@ +#![deny(unused_attributes)] +#![feature(linkage)] +#![feature(fn_align)] + +trait Test { + #[cold] + //~^ ERROR cannot be used on required trait methods [unused_attributes] + //~| WARN previously accepted + fn method1(&self); + #[link_section = ".text"] + //~^ ERROR cannot be used on required trait methods [unused_attributes] + //~| WARN previously accepted + fn method2(&self); + #[linkage = "common"] + //~^ ERROR cannot be used on required trait methods [unused_attributes] + //~| WARN previously accepted + fn method3(&self); + #[track_caller] + fn method4(&self); + #[rustc_align(1)] + fn method5(&self); +} + +fn main() {} diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr new file mode 100644 index 000000000000..0770ccae4146 --- /dev/null +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr @@ -0,0 +1,34 @@ +error: `#[cold]` attribute cannot be used on required trait methods + --> $DIR/codegen_attr_on_required_trait_method.rs:6:5 + | +LL | #[cold] + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[cold]` can be applied to closures, foreign functions, functions, inherent methods, provided trait methods, and trait methods in impl blocks +note: the lint level is defined here + --> $DIR/codegen_attr_on_required_trait_method.rs:1:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: `#[link_section]` attribute cannot be used on required trait methods + --> $DIR/codegen_attr_on_required_trait_method.rs:10:5 + | +LL | #[link_section = ".text"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[link_section]` can be applied to functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks + +error: `#[linkage]` attribute cannot be used on required trait methods + --> $DIR/codegen_attr_on_required_trait_method.rs:14:5 + | +LL | #[linkage = "common"] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[linkage]` can be applied to foreign functions, foreign statics, functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks + +error: aborting due to 3 previous errors + diff --git a/tests/ui/attributes/crate-type-delimited.stderr b/tests/ui/attributes/crate-type-delimited.stderr index 23234efe169f..e9616f27174c 100644 --- a/tests/ui/attributes/crate-type-delimited.stderr +++ b/tests/ui/attributes/crate-type-delimited.stderr @@ -5,7 +5,6 @@ LL | #![crate_type(lib)] | ^^^^^^^^^^^^^^^^^^^ | = note: for more information, visit - | error: aborting due to 1 previous error diff --git a/tests/ui/attributes/crate-type-empty.stderr b/tests/ui/attributes/crate-type-empty.stderr index c1d474d9f17f..b8eca61748ea 100644 --- a/tests/ui/attributes/crate-type-empty.stderr +++ b/tests/ui/attributes/crate-type-empty.stderr @@ -5,7 +5,6 @@ LL | #![crate_type] | ^^^^^^^^^^^^^^ | = note: for more information, visit - | error: aborting due to 1 previous error diff --git a/tests/ui/attributes/crate-type-macro-call.stderr b/tests/ui/attributes/crate-type-macro-call.stderr index cd17b324041b..b3927fef4718 100644 --- a/tests/ui/attributes/crate-type-macro-call.stderr +++ b/tests/ui/attributes/crate-type-macro-call.stderr @@ -5,7 +5,6 @@ LL | #![crate_type = foo!()] | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: for more information, visit - | error: aborting due to 1 previous error diff --git a/tests/ui/attributes/invalid-macro-use.rs b/tests/ui/attributes/invalid-macro-use.rs index 52e4608303f0..4d05e933647b 100644 --- a/tests/ui/attributes/invalid-macro-use.rs +++ b/tests/ui/attributes/invalid-macro-use.rs @@ -2,7 +2,9 @@ //~^ NOTE the lint level is defined here #[macro_use = 5] -//~^ ERROR valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +//~^ ERROR malformed `macro_use` attribute input +//~| NOTE expected a list or no arguments here +//~| NOTE for more information, visit extern crate std as s1; #[macro_use(5)] diff --git a/tests/ui/attributes/invalid-macro-use.stderr b/tests/ui/attributes/invalid-macro-use.stderr index ff3ed6196d3d..fe235ab209f3 100644 --- a/tests/ui/attributes/invalid-macro-use.stderr +++ b/tests/ui/attributes/invalid-macro-use.stderr @@ -1,23 +1,35 @@ error[E0469]: imported macro not found - --> $DIR/invalid-macro-use.rs:51:13 + --> $DIR/invalid-macro-use.rs:53:13 | LL | #[macro_use(a)] | ^ error[E0469]: imported macro not found - --> $DIR/invalid-macro-use.rs:53:13 + --> $DIR/invalid-macro-use.rs:55:13 | LL | #[macro_use(b)] | ^ -error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +error[E0539]: malformed `macro_use` attribute input --> $DIR/invalid-macro-use.rs:4:1 | LL | #[macro_use = 5] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^---^ + | | + | expected a list or no arguments here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_use = 5] +LL + #[macro_use(name1, name2, ...)] + | +LL - #[macro_use = 5] +LL + #[macro_use] + | error[E0539]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:8:1 + --> $DIR/invalid-macro-use.rs:10:1 | LL | #[macro_use(5)] | ^^^^^^^^^^^^-^^ @@ -35,7 +47,7 @@ LL + #[macro_use] | error[E0565]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:14:1 + --> $DIR/invalid-macro-use.rs:16:1 | LL | #[macro_use(a = "b")] | ^^^^^^^^^^^^^^-----^^ @@ -53,7 +65,7 @@ LL + #[macro_use] | error[E0565]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:20:1 + --> $DIR/invalid-macro-use.rs:22:1 | LL | #[macro_use(a(b))] | ^^^^^^^^^^^^^---^^ @@ -71,7 +83,7 @@ LL + #[macro_use] | error[E0539]: malformed `macro_use` attribute input - --> $DIR/invalid-macro-use.rs:26:1 + --> $DIR/invalid-macro-use.rs:28:1 | LL | #[macro_use(a::b)] | ^^^^^^^^^^^^----^^ @@ -89,13 +101,13 @@ LL + #[macro_use] | error: unused attribute - --> $DIR/invalid-macro-use.rs:32:1 + --> $DIR/invalid-macro-use.rs:34:1 | LL | #[macro_use(a)] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-macro-use.rs:34:1 + --> $DIR/invalid-macro-use.rs:36:1 | LL | #[macro_use] | ^^^^^^^^^^^^ @@ -106,25 +118,25 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/invalid-macro-use.rs:40:1 + --> $DIR/invalid-macro-use.rs:42:1 | LL | #[macro_use(a)] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-macro-use.rs:38:1 + --> $DIR/invalid-macro-use.rs:40:1 | LL | #[macro_use] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/invalid-macro-use.rs:46:1 + --> $DIR/invalid-macro-use.rs:48:1 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/invalid-macro-use.rs:44:1 + --> $DIR/invalid-macro-use.rs:46:1 | LL | #[macro_use] | ^^^^^^^^^^^^ diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 26ee89dd7b3b..37ccf9faa1a3 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -59,7 +59,7 @@ #[cold = 1] //~^ ERROR malformed #[must_use()] -//~^ ERROR valid forms for the attribute are +//~^ ERROR malformed #[no_mangle = 1] //~^ ERROR malformed #[unsafe(naked())] @@ -214,12 +214,12 @@ static mut TLS: u8 = 42; #[no_link()] //~^ ERROR malformed #[macro_use = 1] -//~^ ERROR valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +//~^ ERROR malformed extern crate wloop; //~^ ERROR can't find crate for `wloop` [E0463] #[macro_export = 18] -//~^ ERROR valid forms for the attribute are +//~^ ERROR malformed #[allow_internal_unsafe = 1] //~^ ERROR malformed //~| ERROR allow_internal_unsafe side-steps the unsafe_code lint diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index e1ebe4ac9eab..d0e5cb631c1e 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -141,20 +141,6 @@ LL | #[forbid(lint1, lint2, ...)] LL | #[forbid(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ -error: malformed `thread_local` attribute input - --> $DIR/malformed-attrs.rs:210:1 - | -LL | #[thread_local()] - | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[thread_local]` - -error: malformed `no_link` attribute input - --> $DIR/malformed-attrs.rs:214:1 - | -LL | #[no_link()] - | ^^^^^^^^^^^^ help: must be of the form: `#[no_link]` - | - = note: for more information, visit - error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/malformed-attrs.rs:105:1 | @@ -314,11 +300,23 @@ LL | #[cold = 1] | | didn't expect any arguments here | help: must be of the form: `#[cold]` -error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` +error[E0539]: malformed `must_use` attribute input --> $DIR/malformed-attrs.rs:61:1 | LL | #[must_use()] - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^--^ + | | + | didn't expect a list here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[must_use()] +LL + #[must_use = "reason"] + | +LL - #[must_use()] +LL + #[must_use] + | error[E0565]: malformed `no_mangle` attribute input --> $DIR/malformed-attrs.rs:63:1 @@ -614,17 +612,58 @@ LL | #[non_exhaustive = 1] | | didn't expect any arguments here | help: must be of the form: `#[non_exhaustive]` -error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +error[E0565]: malformed `thread_local` attribute input + --> $DIR/malformed-attrs.rs:210:1 + | +LL | #[thread_local()] + | ^^^^^^^^^^^^^^--^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[thread_local]` + +error[E0565]: malformed `no_link` attribute input + --> $DIR/malformed-attrs.rs:214:1 + | +LL | #[no_link()] + | ^^^^^^^^^--^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[no_link]` + +error[E0539]: malformed `macro_use` attribute input --> $DIR/malformed-attrs.rs:216:1 | LL | #[macro_use = 1] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^---^ + | | + | expected a list or no arguments here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_use = 1] +LL + #[macro_use(name1, name2, ...)] + | +LL - #[macro_use = 1] +LL + #[macro_use] + | -error: valid forms for the attribute are `#[macro_export(local_inner_macros)]` and `#[macro_export]` +error[E0539]: malformed `macro_export` attribute input --> $DIR/malformed-attrs.rs:221:1 | LL | #[macro_export = 18] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^----^ + | | + | expected a list or no arguments here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_export = 18] +LL + #[macro_export(local_inner_macros)] + | +LL - #[macro_export = 18] +LL + #[macro_export] + | error[E0565]: malformed `allow_internal_unsafe` attribute input --> $DIR/malformed-attrs.rs:223:1 diff --git a/tests/ui/attributes/malformed-fn-align.stderr b/tests/ui/attributes/malformed-fn-align.stderr index b419df8ea2d1..ad01457d063b 100644 --- a/tests/ui/attributes/malformed-fn-align.stderr +++ b/tests/ui/attributes/malformed-fn-align.stderr @@ -20,9 +20,9 @@ error[E0539]: malformed `rustc_align` attribute input --> $DIR/malformed-fn-align.rs:17:1 | LL | #[rustc_align = 16] - | ^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^----^ + | | | + | | expected this to be a list | help: must be of the form: `#[rustc_align()]` error[E0589]: invalid alignment value: not an unsuffixed integer diff --git a/tests/ui/attributes/malformed-must_use.rs b/tests/ui/attributes/malformed-must_use.rs index 4b98affa8abd..79a1c369f838 100644 --- a/tests/ui/attributes/malformed-must_use.rs +++ b/tests/ui/attributes/malformed-must_use.rs @@ -1,4 +1,4 @@ -#[must_use()] //~ ERROR valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` +#[must_use()] //~ ERROR malformed struct Test; fn main() {} diff --git a/tests/ui/attributes/malformed-must_use.stderr b/tests/ui/attributes/malformed-must_use.stderr index c948ba677444..d4797baa1b0b 100644 --- a/tests/ui/attributes/malformed-must_use.stderr +++ b/tests/ui/attributes/malformed-must_use.stderr @@ -1,8 +1,21 @@ -error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` +error[E0539]: malformed `must_use` attribute input --> $DIR/malformed-must_use.rs:1:1 | LL | #[must_use()] - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^--^ + | | + | didn't expect a list here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[must_use()] +LL + #[must_use = "reason"] + | +LL - #[must_use()] +LL + #[must_use] + | error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/attributes/malformed-static-align.stderr b/tests/ui/attributes/malformed-static-align.stderr index e618ca8acd75..6f5225f7278d 100644 --- a/tests/ui/attributes/malformed-static-align.stderr +++ b/tests/ui/attributes/malformed-static-align.stderr @@ -2,9 +2,9 @@ error[E0539]: malformed `rustc_align_static` attribute input --> $DIR/malformed-static-align.rs:4:1 | LL | #[rustc_align_static = 16] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^^----^ + | | | + | | expected this to be a list | help: must be of the form: `#[rustc_align_static()]` error[E0589]: invalid alignment value: not an unsuffixed integer diff --git a/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr index 094987e944fd..04907f5d638e 100644 --- a/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr +++ b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr @@ -11,9 +11,9 @@ error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input --> $DIR/rustc_skip_during_method_dispatch.rs:7:1 | LL | #[rustc_skip_during_method_dispatch = "array"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------^ + | | | + | | expected this to be a list | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]` error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input diff --git a/tests/ui/autoref-autoderef/autoderef-arc-boxed-closure-call.rs b/tests/ui/autoref-autoderef/autoderef-arc-boxed-closure-call.rs new file mode 100644 index 000000000000..fb7a8ee103e1 --- /dev/null +++ b/tests/ui/autoref-autoderef/autoderef-arc-boxed-closure-call.rs @@ -0,0 +1,10 @@ +//! regression test for +//@ run-pass + +use std::sync::Arc; + +fn main() { + let x = 5; + let command = Arc::new(Box::new(|| x * 2)); + assert_eq!(command(), 10); +} diff --git a/tests/ui/issues/issue-46472.rs b/tests/ui/borrowck/return-ref-to-temporary.rs similarity index 55% rename from tests/ui/issues/issue-46472.rs rename to tests/ui/borrowck/return-ref-to-temporary.rs index b9e20e8dbcb5..90609b8d6da6 100644 --- a/tests/ui/issues/issue-46472.rs +++ b/tests/ui/borrowck/return-ref-to-temporary.rs @@ -1,6 +1,7 @@ +//! regression test for issue https://github.com/rust-lang/rust/issues/46472 fn bar<'a>() -> &'a mut u32 { &mut 4 //~^ ERROR cannot return reference to temporary value [E0515] } -fn main() { } +fn main() {} diff --git a/tests/ui/issues/issue-46472.stderr b/tests/ui/borrowck/return-ref-to-temporary.stderr similarity index 88% rename from tests/ui/issues/issue-46472.stderr rename to tests/ui/borrowck/return-ref-to-temporary.stderr index 6115da28cc91..85faf0a9e375 100644 --- a/tests/ui/issues/issue-46472.stderr +++ b/tests/ui/borrowck/return-ref-to-temporary.stderr @@ -1,5 +1,5 @@ error[E0515]: cannot return reference to temporary value - --> $DIR/issue-46472.rs:2:5 + --> $DIR/return-ref-to-temporary.rs:3:5 | LL | &mut 4 | ^^^^^- diff --git a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr index a86b28d98ecc..584416f58f86 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(take_va_list) = FnAbi { }, fields: Arbitrary { offsets: $OFFSETS, - memory_index: $MEMORY_INDEX, + in_memory_order: $MEMORY_INDEX, }, largest_niche: None, uninhabited: false, @@ -52,7 +52,7 @@ error: fn_abi_of(take_va_list) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/c-variadic/pass-by-value-abi.rs b/tests/ui/c-variadic/pass-by-value-abi.rs index 1e3935067cc2..a7cf37295a74 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.rs +++ b/tests/ui/c-variadic/pass-by-value-abi.rs @@ -1,7 +1,7 @@ //@ check-fail //@ normalize-stderr: "randomization_seed: \d+" -> "randomization_seed: $$SEED" //@ normalize-stderr: "valid_range: 0\.\.=\d+" -> "valid_range: 0..=$$MAX" -//@ normalize-stderr: "memory_index: \[[^\]]+\]" -> "memory_index: $$MEMORY_INDEX" +//@ normalize-stderr: "in_memory_order: \[[^\]]+\]" -> "in_memory_order: $$MEMORY_INDEX" //@ normalize-stderr: "offsets: \[[^\]]+\]" -> "offsets: $$OFFSETS" //@ revisions: x86_64 aarch64 win //@ compile-flags: -O diff --git a/tests/ui/c-variadic/pass-by-value-abi.win.stderr b/tests/ui/c-variadic/pass-by-value-abi.win.stderr index 8ff93961f667..b8e3f699b30e 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.win.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.win.stderr @@ -20,7 +20,7 @@ error: fn_abi_of(take_va_list) = FnAbi { ), fields: Arbitrary { offsets: $OFFSETS, - memory_index: $MEMORY_INDEX, + in_memory_order: $MEMORY_INDEX, }, largest_niche: None, uninhabited: false, @@ -55,7 +55,7 @@ error: fn_abi_of(take_va_list) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr index aafe89d59e33..b5e0e8589af1 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr @@ -13,7 +13,7 @@ error: fn_abi_of(take_va_list) = FnAbi { }, fields: Arbitrary { offsets: $OFFSETS, - memory_index: $MEMORY_INDEX, + in_memory_order: $MEMORY_INDEX, }, largest_niche: None, uninhabited: false, @@ -52,7 +52,7 @@ error: fn_abi_of(take_va_list) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -91,7 +91,7 @@ error: fn_abi_of(take_va_list_sysv64) = FnAbi { }, fields: Arbitrary { offsets: $OFFSETS, - memory_index: $MEMORY_INDEX, + in_memory_order: $MEMORY_INDEX, }, largest_niche: None, uninhabited: false, @@ -130,7 +130,7 @@ error: fn_abi_of(take_va_list_sysv64) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -171,7 +171,7 @@ error: fn_abi_of(take_va_list_win64) = FnAbi { }, fields: Arbitrary { offsets: $OFFSETS, - memory_index: $MEMORY_INDEX, + in_memory_order: $MEMORY_INDEX, }, largest_niche: None, uninhabited: false, @@ -210,7 +210,7 @@ error: fn_abi_of(take_va_list_win64) = FnAbi { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index d25e7f094964..72edc03e10a4 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -121,6 +121,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `frecipe` `frintts` `fxsr` +`gc` `gfni` `guarded-storage` `hard-float` diff --git a/tests/ui/closures/2229_closure_analysis/capture-enums.rs b/tests/ui/closures/2229_closure_analysis/capture-enums.rs index d9c06a68c95b..4c600ccdaa43 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-enums.rs +++ b/tests/ui/closures/2229_closure_analysis/capture-enums.rs @@ -22,6 +22,7 @@ fn multi_variant_enum() { //~| ERROR Min Capture analysis includes: if let Info::Point(_, _, str) = point { //~^ NOTE: Capturing point[] -> Immutable + //~| NOTE: Capturing point[] -> Immutable //~| NOTE: Capturing point[(2, 0)] -> ByValue //~| NOTE: Min Capture point[] -> ByValue println!("{}", str); @@ -29,6 +30,7 @@ fn multi_variant_enum() { if let Info::Meta(_, v) = meta { //~^ NOTE: Capturing meta[] -> Immutable + //~| NOTE: Capturing meta[] -> Immutable //~| NOTE: Capturing meta[(1, 1)] -> ByValue //~| NOTE: Min Capture meta[] -> ByValue println!("{:?}", v); diff --git a/tests/ui/closures/2229_closure_analysis/capture-enums.stderr b/tests/ui/closures/2229_closure_analysis/capture-enums.stderr index 89a879cec468..b62384ffe12e 100644 --- a/tests/ui/closures/2229_closure_analysis/capture-enums.stderr +++ b/tests/ui/closures/2229_closure_analysis/capture-enums.stderr @@ -9,7 +9,7 @@ LL | let c = #[rustc_capture_analysis] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: attributes on expressions are experimental - --> $DIR/capture-enums.rs:48:13 + --> $DIR/capture-enums.rs:50:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,18 +34,28 @@ note: Capturing point[] -> Immutable | LL | if let Info::Point(_, _, str) = point { | ^^^^^ +note: Capturing point[] -> Immutable + --> $DIR/capture-enums.rs:23:41 + | +LL | if let Info::Point(_, _, str) = point { + | ^^^^^ note: Capturing point[(2, 0)] -> ByValue --> $DIR/capture-enums.rs:23:41 | LL | if let Info::Point(_, _, str) = point { | ^^^^^ note: Capturing meta[] -> Immutable - --> $DIR/capture-enums.rs:30:35 + --> $DIR/capture-enums.rs:31:35 + | +LL | if let Info::Meta(_, v) = meta { + | ^^^^ +note: Capturing meta[] -> Immutable + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ note: Capturing meta[(1, 1)] -> ByValue - --> $DIR/capture-enums.rs:30:35 + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ @@ -67,13 +77,13 @@ note: Min Capture point[] -> ByValue LL | if let Info::Point(_, _, str) = point { | ^^^^^ note: Min Capture meta[] -> ByValue - --> $DIR/capture-enums.rs:30:35 + --> $DIR/capture-enums.rs:31:35 | LL | if let Info::Meta(_, v) = meta { | ^^^^ error: First Pass analysis includes: - --> $DIR/capture-enums.rs:52:5 + --> $DIR/capture-enums.rs:54:5 | LL | / || { LL | | @@ -85,13 +95,13 @@ LL | | }; | |_____^ | note: Capturing point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:55:47 + --> $DIR/capture-enums.rs:57:47 | LL | let SingleVariant::Point(_, _, str) = point; | ^^^^^ error: Min Capture analysis includes: - --> $DIR/capture-enums.rs:52:5 + --> $DIR/capture-enums.rs:54:5 | LL | / || { LL | | @@ -103,7 +113,7 @@ LL | | }; | |_____^ | note: Min Capture point[(2, 0)] -> ByValue - --> $DIR/capture-enums.rs:55:47 + --> $DIR/capture-enums.rs:57:47 | LL | let SingleVariant::Point(_, _, str) = point; | ^^^^^ diff --git a/tests/ui/closures/2229_closure_analysis/deref-mut-in-pattern.rs b/tests/ui/closures/2229_closure_analysis/deref-mut-in-pattern.rs new file mode 100644 index 000000000000..b2c0eac9efa2 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/deref-mut-in-pattern.rs @@ -0,0 +1,54 @@ +// Newly accepted examples as a result of the changes introduced in #138961. +// +//@ edition:2024 +//@ check-pass +#![allow(unused_assignments)] + +// Reading the length as part of a pattern captures the pointee. +fn f() { + let mut x: &mut [u8] = &mut [1, 2, 3]; + let c = || { + match x { + [] => (), + _ => (), + } + }; + x = &mut []; + c(); +} + +// Plain old deref as part of pattern behaves similarly +fn g() { + let mut x: &mut bool = &mut false; + let mut t = true; + let c = || { + match x { + true => (), + false => (), + } + }; + x = &mut t; + c(); +} + +// Like f, but the lifetime implications are expressed in terms of +// returning a closure. +fn f2<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l { + || match *x { + &[] => (), + _ => (), + } +} + +// Related testcase that was already accepted before +fn f3<'l: 's, 's>(x: &'s mut &'l [u8]) -> impl Fn() + 'l { + || match **x { + [] => (), + _ => (), + } +} + +fn main() { + f(); + g(); +} diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr index d82db0481a06..3f5fe9eda423 100644 --- a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr @@ -4,7 +4,7 @@ error[E0505]: cannot move out of `ts` because it is borrowed LL | let _b = || { match ts { | -- -- borrow occurs due to use in closure | | - | borrow of `ts` occurs here + | borrow of `ts.x` occurs here ... LL | let mut mut_ts = ts; | ^^ move out of `ts` occurs here diff --git a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs index 40330af4088c..16cb9d7355da 100644 --- a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs +++ b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs @@ -64,9 +64,8 @@ fn test_6_should_capture_single_variant() { //~^ ERROR First Pass analysis includes: //~| ERROR Min Capture analysis includes: match variant { - //~^ NOTE: Capturing variant[] -> Immutable - //~| NOTE: Capturing variant[(0, 0)] -> Immutable - //~| NOTE: Min Capture variant[] -> Immutable + //~^ NOTE: Capturing variant[(0, 0)] -> Immutable + //~| NOTE: Min Capture variant[(0, 0)] -> Immutable SingleVariant::Points(a) => { println!("{:?}", a); } @@ -149,8 +148,8 @@ fn test_7_should_capture_slice_len() { //~^ ERROR First Pass analysis includes: //~| ERROR Min Capture analysis includes: match slice { - //~^ NOTE: Capturing slice[] -> Immutable - //~| NOTE: Min Capture slice[] -> Immutable + //~^ NOTE: Capturing slice[Deref] -> Immutable + //~| NOTE: Min Capture slice[Deref] -> Immutable [_,_,_] => {}, _ => {} } @@ -161,8 +160,8 @@ fn test_7_should_capture_slice_len() { //~^ ERROR First Pass analysis includes: //~| ERROR Min Capture analysis includes: match slice { - //~^ NOTE: Capturing slice[] -> Immutable - //~| NOTE: Min Capture slice[] -> Immutable + //~^ NOTE: Capturing slice[Deref] -> Immutable + //~| NOTE: Min Capture slice[Deref] -> Immutable [] => {}, _ => {} } @@ -173,8 +172,8 @@ fn test_7_should_capture_slice_len() { //~^ ERROR First Pass analysis includes: //~| ERROR Min Capture analysis includes: match slice { - //~^ NOTE: Capturing slice[] -> Immutable - //~| NOTE: Min Capture slice[] -> Immutable + //~^ NOTE: Capturing slice[Deref] -> Immutable + //~| NOTE: Min Capture slice[Deref] -> Immutable [_, .. ,_] => {}, _ => {} } diff --git a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr index e7e5e7f7fa1b..73c685e15276 100644 --- a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr @@ -65,11 +65,6 @@ LL | | match variant { LL | | }; | |_____^ | -note: Capturing variant[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:66:15 - | -LL | match variant { - | ^^^^^^^ note: Capturing variant[(0, 0)] -> Immutable --> $DIR/patterns-capture-analysis.rs:66:15 | @@ -87,14 +82,14 @@ LL | | match variant { LL | | }; | |_____^ | -note: Min Capture variant[] -> Immutable +note: Min Capture variant[(0, 0)] -> Immutable --> $DIR/patterns-capture-analysis.rs:66:15 | LL | match variant { | ^^^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:83:5 + --> $DIR/patterns-capture-analysis.rs:82:5 | LL | / || { LL | | @@ -105,7 +100,7 @@ LL | | }; | |_____^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:95:5 + --> $DIR/patterns-capture-analysis.rs:94:5 | LL | / || { LL | | @@ -116,7 +111,7 @@ LL | | }; | |_____^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:108:5 + --> $DIR/patterns-capture-analysis.rs:107:5 | LL | / || { LL | | @@ -127,7 +122,7 @@ LL | | }; | |_____^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:130:5 + --> $DIR/patterns-capture-analysis.rs:129:5 | LL | / || { LL | | @@ -138,13 +133,13 @@ LL | | }; | |_____^ | note: Capturing variant[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:133:15 + --> $DIR/patterns-capture-analysis.rs:132:15 | LL | match variant { | ^^^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:130:5 + --> $DIR/patterns-capture-analysis.rs:129:5 | LL | / || { LL | | @@ -155,13 +150,13 @@ LL | | }; | |_____^ | note: Min Capture variant[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:133:15 + --> $DIR/patterns-capture-analysis.rs:132:15 | LL | match variant { | ^^^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:148:5 + --> $DIR/patterns-capture-analysis.rs:147:5 | LL | / || { LL | | @@ -171,14 +166,14 @@ LL | | match slice { LL | | }; | |_____^ | -note: Capturing slice[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:151:15 +note: Capturing slice[Deref] -> Immutable + --> $DIR/patterns-capture-analysis.rs:150:15 | LL | match slice { | ^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:148:5 + --> $DIR/patterns-capture-analysis.rs:147:5 | LL | / || { LL | | @@ -188,14 +183,14 @@ LL | | match slice { LL | | }; | |_____^ | -note: Min Capture slice[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:151:15 +note: Min Capture slice[Deref] -> Immutable + --> $DIR/patterns-capture-analysis.rs:150:15 | LL | match slice { | ^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:160:5 + --> $DIR/patterns-capture-analysis.rs:159:5 | LL | / || { LL | | @@ -205,14 +200,14 @@ LL | | match slice { LL | | }; | |_____^ | -note: Capturing slice[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:163:15 +note: Capturing slice[Deref] -> Immutable + --> $DIR/patterns-capture-analysis.rs:162:15 | LL | match slice { | ^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:160:5 + --> $DIR/patterns-capture-analysis.rs:159:5 | LL | / || { LL | | @@ -222,14 +217,14 @@ LL | | match slice { LL | | }; | |_____^ | -note: Min Capture slice[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:163:15 +note: Min Capture slice[Deref] -> Immutable + --> $DIR/patterns-capture-analysis.rs:162:15 | LL | match slice { | ^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:172:5 + --> $DIR/patterns-capture-analysis.rs:171:5 | LL | / || { LL | | @@ -239,14 +234,14 @@ LL | | match slice { LL | | }; | |_____^ | -note: Capturing slice[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:175:15 +note: Capturing slice[Deref] -> Immutable + --> $DIR/patterns-capture-analysis.rs:174:15 | LL | match slice { | ^^^^^ error: Min Capture analysis includes: - --> $DIR/patterns-capture-analysis.rs:172:5 + --> $DIR/patterns-capture-analysis.rs:171:5 | LL | / || { LL | | @@ -256,14 +251,14 @@ LL | | match slice { LL | | }; | |_____^ | -note: Min Capture slice[] -> Immutable - --> $DIR/patterns-capture-analysis.rs:175:15 +note: Min Capture slice[Deref] -> Immutable + --> $DIR/patterns-capture-analysis.rs:174:15 | LL | match slice { | ^^^^^ error: First Pass analysis includes: - --> $DIR/patterns-capture-analysis.rs:189:5 + --> $DIR/patterns-capture-analysis.rs:188:5 | LL | / || { LL | | diff --git a/tests/ui/closures/2229_closure_analysis/only-inhabited-variant-stable.rs b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant-stable.rs new file mode 100644 index 000000000000..c7f367cc48ab --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant-stable.rs @@ -0,0 +1,22 @@ +// This example used to compile, but the fact that it should was never properly +// discussed. With further experience, we concluded that capture precision +// depending on whether some types are inhabited goes too far, introducing a +// bunch of headaches without much benefit. +//@ edition:2021 +enum Void {} + +pub fn main() { + let mut r = Result::::Err((0, 0)); + let mut f = || { + let Err((ref mut a, _)) = r; + *a = 1; + }; + let mut g = || { + //~^ ERROR: cannot borrow `r` as mutable more than once at a time + let Err((_, ref mut b)) = r; + *b = 2; + }; + f(); + g(); + assert!(matches!(r, Err((1, 2)))); +} diff --git a/tests/ui/closures/2229_closure_analysis/only-inhabited-variant-stable.stderr b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant-stable.stderr new file mode 100644 index 000000000000..7f4c8942b0d9 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant-stable.stderr @@ -0,0 +1,20 @@ +error[E0499]: cannot borrow `r` as mutable more than once at a time + --> $DIR/only-inhabited-variant-stable.rs:14:17 + | +LL | let mut f = || { + | -- first mutable borrow occurs here +LL | let Err((ref mut a, _)) = r; + | - first borrow occurs due to use of `r` in closure +... +LL | let mut g = || { + | ^^ second mutable borrow occurs here +LL | +LL | let Err((_, ref mut b)) = r; + | - second borrow occurs due to use of `r` in closure +... +LL | f(); + | - first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.exhaustive_patterns.stderr b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.exhaustive_patterns.stderr new file mode 100644 index 000000000000..58a5348aa391 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.exhaustive_patterns.stderr @@ -0,0 +1,20 @@ +error[E0499]: cannot borrow `r` as mutable more than once at a time + --> $DIR/only-inhabited-variant.rs:16:17 + | +LL | let mut f = || { + | -- first mutable borrow occurs here +LL | let Err((ref mut a, _)) = r; + | - first borrow occurs due to use of `r` in closure +... +LL | let mut g = || { + | ^^ second mutable borrow occurs here +LL | +LL | let Err((_, ref mut b)) = r; + | - second borrow occurs due to use of `r` in closure +... +LL | f(); + | - first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.normal.stderr b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.normal.stderr new file mode 100644 index 000000000000..58a5348aa391 --- /dev/null +++ b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.normal.stderr @@ -0,0 +1,20 @@ +error[E0499]: cannot borrow `r` as mutable more than once at a time + --> $DIR/only-inhabited-variant.rs:16:17 + | +LL | let mut f = || { + | -- first mutable borrow occurs here +LL | let Err((ref mut a, _)) = r; + | - first borrow occurs due to use of `r` in closure +... +LL | let mut g = || { + | ^^ second mutable borrow occurs here +LL | +LL | let Err((_, ref mut b)) = r; + | - second borrow occurs due to use of `r` in closure +... +LL | f(); + | - first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.rs similarity index 55% rename from tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs rename to tests/ui/closures/2229_closure_analysis/only-inhabited-variant.rs index 74f37b514e4a..463838734726 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs +++ b/tests/ui/closures/2229_closure_analysis/only-inhabited-variant.rs @@ -1,8 +1,9 @@ -// Test precise capture of a multi-variant enum (when remaining variants are -// visibly uninhabited). +// This example used to compile, but the fact that it should was never properly +// discussed. With further experience, we concluded that capture precision +// depending on whether some types are inhabited goes too far, introducing a +// bunch of headaches without much benefit. //@ revisions: normal exhaustive_patterns //@ edition:2021 -//@ run-pass #![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))] #![feature(never_type)] @@ -13,6 +14,7 @@ pub fn main() { *a = 1; }; let mut g = || { + //~^ ERROR: cannot borrow `r` as mutable more than once at a time let Err((_, ref mut b)) = r; *b = 2; }; diff --git a/tests/ui/closures/at-pattern-weirdness-issue-137553.rs b/tests/ui/closures/at-pattern-weirdness-issue-137553.rs new file mode 100644 index 000000000000..7c934d4a1433 --- /dev/null +++ b/tests/ui/closures/at-pattern-weirdness-issue-137553.rs @@ -0,0 +1,41 @@ +//@ edition:2024 +//@ check-pass + +// Background: +fn f1() { + let mut a = (21, 37); + // only captures a.0, example compiles fine + let mut f = || { + let (ref mut x, _) = a; + *x = 42; + }; + a.1 = 69; + f(); +} + +// This used to error out: +fn f2() { + let mut a = (21, 37); + // used to capture all of a, now captures only a.0 + let mut f = || { + match a { + (ref mut x, _) => *x = 42, + } + }; + a.1 = 69; + f(); +} + +// This was inconsistent with the following: +fn main() { + let mut a = (21, 37); + // the useless @-pattern would cause it to capture only a.0. now the + // behavior is consistent with the case that doesn't use the @-pattern + let mut f = || { + match a { + (ref mut x @ _, _) => *x = 42, + } + }; + a.1 = 69; + f(); +} diff --git a/tests/ui/issues/issue-19499.rs b/tests/ui/closures/closure-upvar-trait-caching.rs similarity index 85% rename from tests/ui/issues/issue-19499.rs rename to tests/ui/closures/closure-upvar-trait-caching.rs index d2a6862e05c4..82588631c99c 100644 --- a/tests/ui/issues/issue-19499.rs +++ b/tests/ui/closures/closure-upvar-trait-caching.rs @@ -7,8 +7,9 @@ // reasonable examples) let to ambiguity errors about not being able // to infer sufficient type information. - fn main() { let n = 0; - let it = Some(1_usize).into_iter().inspect(|_| {n;}); + let it = Some(1_usize).into_iter().inspect(|_| { + n; + }); } diff --git a/tests/ui/closures/malformed-pattern-issue-140011.rs b/tests/ui/closures/malformed-pattern-issue-140011.rs new file mode 100644 index 000000000000..18061613e988 --- /dev/null +++ b/tests/ui/closures/malformed-pattern-issue-140011.rs @@ -0,0 +1,13 @@ +//@compile-flags: -Wrust-2021-incompatible-closure-captures +enum B { + C(D), //~ ERROR: cannot find type `D` in this scope + E(F), +} +struct F; +fn f(h: B) { + || { + let B::E(a) = h; //~ ERROR: refutable pattern in local binding + }; +} + +fn main() {} diff --git a/tests/ui/closures/malformed-pattern-issue-140011.stderr b/tests/ui/closures/malformed-pattern-issue-140011.stderr new file mode 100644 index 000000000000..43beb7c58632 --- /dev/null +++ b/tests/ui/closures/malformed-pattern-issue-140011.stderr @@ -0,0 +1,36 @@ +error[E0425]: cannot find type `D` in this scope + --> $DIR/malformed-pattern-issue-140011.rs:3:7 + | +LL | C(D), + | ^ not found in this scope + | +help: you might be missing a type parameter + | +LL | enum B { + | +++ + +error[E0005]: refutable pattern in local binding + --> $DIR/malformed-pattern-issue-140011.rs:9:13 + | +LL | let B::E(a) = h; + | ^^^^^^^ pattern `B::C(_)` not covered + | + = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html +note: `B` defined here + --> $DIR/malformed-pattern-issue-140011.rs:2:6 + | +LL | enum B { + | ^ +LL | C(D), + | - not covered + = note: the matched value is of type `B` +help: you might want to use `let...else` to handle the variant that isn't matched + | +LL | let B::E(a) = h else { todo!() }; + | ++++++++++++++++ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0005, E0425. +For more information about an error, try `rustc --explain E0005`. diff --git a/tests/ui/closures/nested-closure-call.rs b/tests/ui/closures/nested-closure-call.rs new file mode 100644 index 000000000000..9d0860167a1a --- /dev/null +++ b/tests/ui/closures/nested-closure-call.rs @@ -0,0 +1,5 @@ +//! regression test for https://github.com/rust-lang/rust/issues/24779 +//@ run-pass +fn main() { + assert_eq!((|| || 42)()(), 42); +} diff --git a/tests/ui/closures/or-patterns-issue-137467.rs b/tests/ui/closures/or-patterns-issue-137467.rs new file mode 100644 index 000000000000..5a1e84e1c9a0 --- /dev/null +++ b/tests/ui/closures/or-patterns-issue-137467.rs @@ -0,0 +1,177 @@ +//@ edition:2024 +//@ check-pass + +const X: u32 = 0; + +fn match_literal(x: (u32, u32, u32)) { + let _ = || { + let ((0, a, _) | (_, _, a)) = x; + a + }; +} + +fn match_range(x: (u32, u32, u32)) { + let _ = || { + let ((0..5, a, _) | (_, _, a)) = x; + a + }; +} + +fn match_const(x: (u32, u32, u32)) { + let _ = || { + let ((X, a, _) | (_, _, a)) = x; + a + }; +} + +// related testcase reported in #138973 +fn without_bindings(x: u32) { + let _ = || { + let (0 | _) = x; + }; +} + +enum Choice { A, B } + +fn match_unit_variant(x: (Choice, u32, u32)) { + let _ = || { + let ((Choice::A, a, _) | (Choice::B, _, a)) = x; + a + }; +} + +struct Unit; + +fn match_unit_struct(mut x: (Unit, u32)) { + let r = &mut x.0; + let _ = || { + let (Unit, a) = x; + a + }; + + let _ = *r; +} + +enum Also { Unit } + +fn match_unit_enum(mut x: (Also, u32)) { + let r = &mut x.0; + let _ = || { + let (Also::Unit, a) = x; + a + }; + + let _ = *r; +} + +enum TEnum { + A(u32), + B(u32), +} + +enum SEnum { + A { a: u32 }, + B { a: u32 }, +} + +fn match_tuple_enum(x: TEnum) { + let _ = || { + let (TEnum::A(a) | TEnum::B(a)) = x; + a + }; +} + +fn match_struct_enum(x: SEnum) { + let _ = || { + let (SEnum::A { a } | SEnum::B { a }) = x; + a + }; +} + +enum TSingle { + A(u32, u32), +} + +enum SSingle { + A { a: u32, b: u32 }, +} + +struct TStruct(u32, u32); +struct SStruct { a: u32, b: u32 } + +fn match_struct(mut x: SStruct) { + let r = &mut x.a; + let _ = || { + let SStruct { b, .. } = x; + b + }; + + let _ = *r; +} + +fn match_tuple_struct(mut x: TStruct) { + let r = &mut x.0; + let _ = || { + let TStruct(_, a) = x; + a + }; + + let _ = *r; +} + +fn match_singleton(mut x: SSingle) { + let SSingle::A { a: ref mut r, .. } = x; + let _ = || { + let SSingle::A { b, .. } = x; + b + }; + + let _ = *r; +} + +fn match_tuple_singleton(mut x: TSingle) { + let TSingle::A(ref mut r, _) = x; + let _ = || { + let TSingle::A(_, a) = x; + a + }; + + let _ = *r; +} + +fn match_slice(x: (&[u32], u32, u32)) { + let _ = || { + let (([], a, _) | ([_, ..], _, a)) = x; + a + }; +} + +// Original testcase, for completeness +enum Camera { + Normal { base_transform: i32 }, + Volume { transform: i32 }, +} + +fn draw_ui(camera: &mut Camera) { + || { + let (Camera::Normal { + base_transform: _transform, + } + | Camera::Volume { + transform: _transform, + }) = camera; + }; +} + +fn draw_ui2(camera: &mut Camera) { + || { + let (Camera::Normal { + base_transform: _, + } + | Camera::Volume { + transform: _, + }) = camera; + }; +} + +fn main() {} diff --git a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGS.stderr b/tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.BADFLAGS.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGS.stderr rename to tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.BADFLAGS.stderr diff --git a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGSPC.stderr b/tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.BADFLAGSPC.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADFLAGSPC.stderr rename to tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.BADFLAGSPC.stderr diff --git a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADTARGET.stderr b/tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.BADTARGET.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.BADTARGET.stderr rename to tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.BADTARGET.stderr diff --git a/tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.rs b/tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.rs similarity index 100% rename from tests/ui/invalid-compile-flags/branch-protection-missing-pac-ret.rs rename to tests/ui/compile-flags/invalid/branch-protection-missing-pac-ret.rs diff --git a/tests/ui/invalid-compile-flags/codegen-option-without-group.rs b/tests/ui/compile-flags/invalid/codegen-option-without-group.rs similarity index 100% rename from tests/ui/invalid-compile-flags/codegen-option-without-group.rs rename to tests/ui/compile-flags/invalid/codegen-option-without-group.rs diff --git a/tests/ui/invalid-compile-flags/codegen-option-without-group.stderr b/tests/ui/compile-flags/invalid/codegen-option-without-group.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/codegen-option-without-group.stderr rename to tests/ui/compile-flags/invalid/codegen-option-without-group.stderr diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.empty_crate_type.stderr b/tests/ui/compile-flags/invalid/crate-type-flag.empty_crate_type.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/crate-type-flag.empty_crate_type.stderr rename to tests/ui/compile-flags/invalid/crate-type-flag.empty_crate_type.stderr diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.proc_underscore_macro.stderr b/tests/ui/compile-flags/invalid/crate-type-flag.proc_underscore_macro.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/crate-type-flag.proc_underscore_macro.stderr rename to tests/ui/compile-flags/invalid/crate-type-flag.proc_underscore_macro.stderr diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.rs b/tests/ui/compile-flags/invalid/crate-type-flag.rs similarity index 100% rename from tests/ui/invalid-compile-flags/crate-type-flag.rs rename to tests/ui/compile-flags/invalid/crate-type-flag.rs diff --git a/tests/ui/invalid-compile-flags/crate-type-flag.unknown.stderr b/tests/ui/compile-flags/invalid/crate-type-flag.unknown.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/crate-type-flag.unknown.stderr rename to tests/ui/compile-flags/invalid/crate-type-flag.unknown.stderr diff --git a/tests/ui/invalid-compile-flags/debug-option-without-group.rs b/tests/ui/compile-flags/invalid/debug-option-without-group.rs similarity index 100% rename from tests/ui/invalid-compile-flags/debug-option-without-group.rs rename to tests/ui/compile-flags/invalid/debug-option-without-group.rs diff --git a/tests/ui/invalid-compile-flags/debug-option-without-group.stderr b/tests/ui/compile-flags/invalid/debug-option-without-group.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/debug-option-without-group.stderr rename to tests/ui/compile-flags/invalid/debug-option-without-group.stderr diff --git a/tests/ui/invalid-compile-flags/emit-output-types-without-args.rs b/tests/ui/compile-flags/invalid/emit-output-types-without-args.rs similarity index 100% rename from tests/ui/invalid-compile-flags/emit-output-types-without-args.rs rename to tests/ui/compile-flags/invalid/emit-output-types-without-args.rs diff --git a/tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr b/tests/ui/compile-flags/invalid/emit-output-types-without-args.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/emit-output-types-without-args.stderr rename to tests/ui/compile-flags/invalid/emit-output-types-without-args.stderr diff --git a/tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.aarch64.stderr b/tests/ui/compile-flags/invalid/function-return/requires-x86-or-x86_64.aarch64.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.aarch64.stderr rename to tests/ui/compile-flags/invalid/function-return/requires-x86-or-x86_64.aarch64.stderr diff --git a/tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.rs b/tests/ui/compile-flags/invalid/function-return/requires-x86-or-x86_64.rs similarity index 100% rename from tests/ui/invalid-compile-flags/function-return/requires-x86-or-x86_64.rs rename to tests/ui/compile-flags/invalid/function-return/requires-x86-or-x86_64.rs diff --git a/tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.large.stderr b/tests/ui/compile-flags/invalid/function-return/thunk-extern-requires-non-large-code-model.large.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.large.stderr rename to tests/ui/compile-flags/invalid/function-return/thunk-extern-requires-non-large-code-model.large.stderr diff --git a/tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.rs b/tests/ui/compile-flags/invalid/function-return/thunk-extern-requires-non-large-code-model.rs similarity index 100% rename from tests/ui/invalid-compile-flags/function-return/thunk-extern-requires-non-large-code-model.rs rename to tests/ui/compile-flags/invalid/function-return/thunk-extern-requires-non-large-code-model.rs diff --git a/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr b/tests/ui/compile-flags/invalid/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr rename to tests/ui/compile-flags/invalid/indirect-branch-cs-prefix/requires-x86-or-x86_64.aarch64.stderr diff --git a/tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs b/tests/ui/compile-flags/invalid/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs similarity index 100% rename from tests/ui/invalid-compile-flags/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs rename to tests/ui/compile-flags/invalid/indirect-branch-cs-prefix/requires-x86-or-x86_64.rs diff --git a/tests/ui/invalid-compile-flags/invalid-llvm-passes.rs b/tests/ui/compile-flags/invalid/invalid-llvm-passes.rs similarity index 100% rename from tests/ui/invalid-compile-flags/invalid-llvm-passes.rs rename to tests/ui/compile-flags/invalid/invalid-llvm-passes.rs diff --git a/tests/ui/invalid-compile-flags/invalid-llvm-passes.stderr b/tests/ui/compile-flags/invalid/invalid-llvm-passes.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/invalid-llvm-passes.stderr rename to tests/ui/compile-flags/invalid/invalid-llvm-passes.stderr diff --git a/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs b/tests/ui/compile-flags/invalid/need-crate-arg-ignore-tidy$x.rs similarity index 100% rename from tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.rs rename to tests/ui/compile-flags/invalid/need-crate-arg-ignore-tidy$x.rs diff --git a/tests/ui/compile-flags/invalid/need-crate-arg-ignore-tidy$x.stderr b/tests/ui/compile-flags/invalid/need-crate-arg-ignore-tidy$x.stderr new file mode 100644 index 000000000000..3c0c80eae90d --- /dev/null +++ b/tests/ui/compile-flags/invalid/need-crate-arg-ignore-tidy$x.stderr @@ -0,0 +1,4 @@ +error: invalid character '$' in crate name: `need_crate_arg_ignore_tidy$x` + +error: aborting due to 1 previous error + diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs b/tests/ui/compile-flags/invalid/print-crate-name-request-malformed-crate-name.rs similarity index 100% rename from tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.rs rename to tests/ui/compile-flags/invalid/print-crate-name-request-malformed-crate-name.rs diff --git a/tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr b/tests/ui/compile-flags/invalid/print-crate-name-request-malformed-crate-name.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/print-crate-name-request-malformed-crate-name.stderr rename to tests/ui/compile-flags/invalid/print-crate-name-request-malformed-crate-name.stderr diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.rs b/tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-1.rs similarity index 100% rename from tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.rs rename to tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-1.rs diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr b/tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-1.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-1.stderr rename to tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-1.stderr diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs b/tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-2.rs similarity index 100% rename from tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.rs rename to tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-2.rs diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr b/tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-2.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name-2.stderr rename to tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name-2.stderr diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs b/tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name.rs similarity index 100% rename from tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.rs rename to tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name.rs diff --git a/tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr b/tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/print-file-names-request-malformed-crate-name.stderr rename to tests/ui/compile-flags/invalid/print-file-names-request-malformed-crate-name.stderr diff --git a/tests/ui/invalid-compile-flags/print-without-arg.rs b/tests/ui/compile-flags/invalid/print-without-arg.rs similarity index 100% rename from tests/ui/invalid-compile-flags/print-without-arg.rs rename to tests/ui/compile-flags/invalid/print-without-arg.rs diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/compile-flags/invalid/print-without-arg.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/print-without-arg.stderr rename to tests/ui/compile-flags/invalid/print-without-arg.stderr diff --git a/tests/ui/invalid-compile-flags/print.rs b/tests/ui/compile-flags/invalid/print.rs similarity index 100% rename from tests/ui/invalid-compile-flags/print.rs rename to tests/ui/compile-flags/invalid/print.rs diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/compile-flags/invalid/print.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/print.stderr rename to tests/ui/compile-flags/invalid/print.stderr diff --git a/tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.aarch64.stderr b/tests/ui/compile-flags/invalid/reg-struct-return/requires-x86.aarch64.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.aarch64.stderr rename to tests/ui/compile-flags/invalid/reg-struct-return/requires-x86.aarch64.stderr diff --git a/tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.rs b/tests/ui/compile-flags/invalid/reg-struct-return/requires-x86.rs similarity index 100% rename from tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.rs rename to tests/ui/compile-flags/invalid/reg-struct-return/requires-x86.rs diff --git a/tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.x86_64.stderr b/tests/ui/compile-flags/invalid/reg-struct-return/requires-x86.x86_64.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/reg-struct-return/requires-x86.x86_64.stderr rename to tests/ui/compile-flags/invalid/reg-struct-return/requires-x86.x86_64.stderr diff --git a/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.regparm4.stderr b/tests/ui/compile-flags/invalid/regparm/regparm-valid-values.regparm4.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/regparm/regparm-valid-values.regparm4.stderr rename to tests/ui/compile-flags/invalid/regparm/regparm-valid-values.regparm4.stderr diff --git a/tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs b/tests/ui/compile-flags/invalid/regparm/regparm-valid-values.rs similarity index 100% rename from tests/ui/invalid-compile-flags/regparm/regparm-valid-values.rs rename to tests/ui/compile-flags/invalid/regparm/regparm-valid-values.rs diff --git a/tests/ui/invalid-compile-flags/regparm/requires-x86.aarch64.stderr b/tests/ui/compile-flags/invalid/regparm/requires-x86.aarch64.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/regparm/requires-x86.aarch64.stderr rename to tests/ui/compile-flags/invalid/regparm/requires-x86.aarch64.stderr diff --git a/tests/ui/invalid-compile-flags/regparm/requires-x86.rs b/tests/ui/compile-flags/invalid/regparm/requires-x86.rs similarity index 100% rename from tests/ui/invalid-compile-flags/regparm/requires-x86.rs rename to tests/ui/compile-flags/invalid/regparm/requires-x86.rs diff --git a/tests/ui/invalid-compile-flags/regparm/requires-x86.x86_64.stderr b/tests/ui/compile-flags/invalid/regparm/requires-x86.x86_64.stderr similarity index 100% rename from tests/ui/invalid-compile-flags/regparm/requires-x86.x86_64.stderr rename to tests/ui/compile-flags/invalid/regparm/requires-x86.x86_64.stderr diff --git a/tests/ui/issues/issue-24945-repeat-dash-opts.rs b/tests/ui/compile-flags/run-pass/repeated-debug-opt-flags.rs similarity index 72% rename from tests/ui/issues/issue-24945-repeat-dash-opts.rs rename to tests/ui/compile-flags/run-pass/repeated-debug-opt-flags.rs index 5d8044b0a1a0..d584ec9fd280 100644 --- a/tests/ui/issues/issue-24945-repeat-dash-opts.rs +++ b/tests/ui/compile-flags/run-pass/repeated-debug-opt-flags.rs @@ -1,3 +1,4 @@ +//! regression test for https://github.com/rust-lang/rust/issues/24945 //@ run-pass // This test is just checking that we continue to accept `-g -g -O -O` // as options to the compiler. diff --git a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr index e73b20f2d5d3..1be52de708e5 100644 --- a/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg-attr-syntax-validation.stderr @@ -13,9 +13,9 @@ error[E0539]: malformed `cfg` attribute input --> $DIR/cfg-attr-syntax-validation.rs:7:1 | LL | #[cfg = 10] - | ^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^----^ + | | | + | | expected this to be a list | help: must be of the form: `#[cfg(predicate)]` | = note: for more information, visit diff --git a/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr b/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr index 1c97eaddfe1b..cc35035edf95 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-105608.stderr @@ -3,7 +3,6 @@ error[E0282]: type annotations needed | LL | Combination::<0>.and::<_>().and::<_>(); | ^^^ cannot infer type of the type parameter `M` declared on the method `and` - | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts.rs b/tests/ui/const-generics/mgca/explicit_anon_consts.rs index 459282776266..31391b023bfe 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts.rs +++ b/tests/ui/const-generics/mgca/explicit_anon_consts.rs @@ -1,5 +1,8 @@ #![feature(associated_const_equality, generic_const_items, min_generic_const_args)] #![expect(incomplete_features)] +// library crates exercise weirder code paths around +// DefIds which were created for const args. +#![crate_type = "lib"] struct Foo; @@ -66,5 +69,3 @@ struct Default3; struct Default4; //~^ ERROR: complex const arguments must be placed inside of a `const` block struct Default5; - -fn main() {} diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr index e4bf49c71efe..b3d960e315ea 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr +++ b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr @@ -1,5 +1,5 @@ error: generic parameters may not be used in const operations - --> $DIR/explicit_anon_consts.rs:8:41 + --> $DIR/explicit_anon_consts.rs:11:41 | LL | type Adt3 = Foo; | ^ cannot perform const operation using `N` @@ -8,7 +8,7 @@ LL | type Adt3 = Foo; = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/explicit_anon_consts.rs:16:42 + --> $DIR/explicit_anon_consts.rs:19:42 | LL | type Arr3 = [(); const { N }]; | ^ cannot perform const operation using `N` @@ -17,7 +17,7 @@ LL | type Arr3 = [(); const { N }]; = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/explicit_anon_consts.rs:25:27 + --> $DIR/explicit_anon_consts.rs:28:27 | LL | let _3 = [(); const { N }]; | ^ cannot perform const operation using `N` @@ -26,7 +26,7 @@ LL | let _3 = [(); const { N }]; = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/explicit_anon_consts.rs:37:46 + --> $DIR/explicit_anon_consts.rs:40:46 | LL | const ITEM3: usize = const { N }; | ^ cannot perform const operation using `N` @@ -35,7 +35,7 @@ LL | const ITEM3: usize = const { N }; = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/explicit_anon_consts.rs:55:31 + --> $DIR/explicit_anon_consts.rs:58:31 | LL | T3: Trait, | ^ cannot perform const operation using `N` @@ -44,7 +44,7 @@ LL | T3: Trait, = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions error: generic parameters may not be used in const operations - --> $DIR/explicit_anon_consts.rs:64:58 + --> $DIR/explicit_anon_consts.rs:67:58 | LL | struct Default3; | ^ cannot perform const operation using `N` @@ -53,37 +53,37 @@ LL | struct Default3; = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:10:33 + --> $DIR/explicit_anon_consts.rs:13:33 | LL | type Adt4 = Foo<{ 1 + 1 }>; | ^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:18:34 + --> $DIR/explicit_anon_consts.rs:21:34 | LL | type Arr4 = [(); 1 + 1]; | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:27:19 + --> $DIR/explicit_anon_consts.rs:30:19 | LL | let _4 = [(); 1 + 1]; | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:40:38 + --> $DIR/explicit_anon_consts.rs:43:38 | LL | const ITEM4: usize = { 1 + 1 }; | ^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:57:23 + --> $DIR/explicit_anon_consts.rs:60:23 | LL | T4: Trait, | ^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:66:50 + --> $DIR/explicit_anon_consts.rs:69:50 | LL | struct Default4; | ^^^^^^^^^ diff --git a/tests/ui/const-generics/mgca/unused_speculative_def_id.rs b/tests/ui/const-generics/mgca/unused_speculative_def_id.rs new file mode 100644 index 000000000000..e617daba24b6 --- /dev/null +++ b/tests/ui/const-generics/mgca/unused_speculative_def_id.rs @@ -0,0 +1,24 @@ +#![feature(min_generic_const_args, generic_const_exprs, generic_const_items)] +#![expect(incomplete_features)] + +// Previously we would create a `DefId` to represent the const argument to `A` +// except it would go unused as it's a MGCA path const arg. We would also make +// a `DefId` for the `const { 1 }` anon const arg to `ERR` which would wind up +// with a `DefId` parent of the speculatively created `DefId` for the argument to +// `A`. +// +// This then caused Problems:tm: in the rest of the compiler that did not expect +// to encounter such nonsensical `DefId`s. +// +// The `ERR` path must fail to resolve as if it can be resolved then broken GCE +// logic will attempt to evaluate the constant directly which is wrong for +// `type_const`s which do not have bodies. + +struct A; + +struct Foo { + field: A<{ ERR:: }>, + //~^ ERROR: cannot find value `ERR` in this scope +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/unused_speculative_def_id.stderr b/tests/ui/const-generics/mgca/unused_speculative_def_id.stderr new file mode 100644 index 000000000000..e087e046ec35 --- /dev/null +++ b/tests/ui/const-generics/mgca/unused_speculative_def_id.stderr @@ -0,0 +1,18 @@ +error[E0425]: cannot find value `ERR` in this scope + --> $DIR/unused_speculative_def_id.rs:20:16 + | +LL | field: A<{ ERR:: }>, + | ^^^ + | + --> $SRC_DIR/core/src/result.rs:LL:COL + | + = note: similarly named tuple variant `Err` defined here +help: a tuple variant with a similar name exists + | +LL - field: A<{ ERR:: }>, +LL + field: A<{ Err:: }>, + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/consts/const-closure-fn-trait-object.rs b/tests/ui/consts/const-closure-fn-trait-object.rs new file mode 100644 index 000000000000..e47a118dd3f4 --- /dev/null +++ b/tests/ui/consts/const-closure-fn-trait-object.rs @@ -0,0 +1,5 @@ +//! regression test for +//@ run-pass +fn main() { + const _C: &'static dyn Fn() = &|| {}; +} diff --git a/tests/ui/delegation/auxiliary/recursive-delegation-aux.rs b/tests/ui/delegation/auxiliary/recursive-delegation-aux.rs new file mode 100644 index 000000000000..5df644974f3e --- /dev/null +++ b/tests/ui/delegation/auxiliary/recursive-delegation-aux.rs @@ -0,0 +1,7 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +fn foo() {} + +reuse foo as bar; +pub reuse bar as goo; diff --git a/tests/ui/delegation/ice-issue-124347.rs b/tests/ui/delegation/ice-issue-124347.rs index 3e0a5b36ddcf..6bf3a08ba5b4 100644 --- a/tests/ui/delegation/ice-issue-124347.rs +++ b/tests/ui/delegation/ice-issue-124347.rs @@ -1,12 +1,12 @@ #![feature(fn_delegation)] #![allow(incomplete_features)] -// FIXME(fn_delegation): `recursive delegation` error should be emitted here trait Trait { reuse Trait::foo { &self.0 } + //~^ ERROR failed to resolve delegation callee } reuse foo; -//~^ ERROR cycle detected when computing generics of `foo` +//~^ ERROR failed to resolve delegation callee fn main() {} diff --git a/tests/ui/delegation/ice-issue-124347.stderr b/tests/ui/delegation/ice-issue-124347.stderr index 2955c0442034..40be6be4abfa 100644 --- a/tests/ui/delegation/ice-issue-124347.stderr +++ b/tests/ui/delegation/ice-issue-124347.stderr @@ -1,17 +1,14 @@ -error[E0391]: cycle detected when computing generics of `foo` +error: failed to resolve delegation callee + --> $DIR/ice-issue-124347.rs:5:18 + | +LL | reuse Trait::foo { &self.0 } + | ^^^ + +error: failed to resolve delegation callee --> $DIR/ice-issue-124347.rs:9:7 | LL | reuse foo; | ^^^ - | - = note: ...which immediately requires computing generics of `foo` again -note: cycle used when checking that `foo` is well-formed - --> $DIR/ice-issue-124347.rs:9:7 - | -LL | reuse foo; - | ^^^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index 1f43ec335448..f332bc6a7a21 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -5,7 +5,6 @@ LL | dbg!(b); | ^^^^^^^ expected named lifetime parameter | = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info) - | error[E0425]: cannot find function `a` in this scope --> $DIR/ice-line-bounds-issue-148732.rs:1:7 diff --git a/tests/ui/delegation/recursive-delegation-errors.rs b/tests/ui/delegation/recursive-delegation-errors.rs new file mode 100644 index 000000000000..194182e20ed0 --- /dev/null +++ b/tests/ui/delegation/recursive-delegation-errors.rs @@ -0,0 +1,54 @@ +#![feature(fn_delegation)] +#![allow(incomplete_features)] + + +mod first_mod { + reuse foo; + //~^ ERROR failed to resolve delegation callee +} + +mod second_mod { + reuse foo as bar; + //~^ ERROR encountered a cycle during delegation signature resolution + reuse bar as foo; + //~^ ERROR encountered a cycle during delegation signature resolution +} + +mod third_mod { + reuse foo as foo1; + //~^ ERROR encountered a cycle during delegation signature resolution + reuse foo1 as foo2; + //~^ ERROR encountered a cycle during delegation signature resolution + reuse foo2 as foo3; + //~^ ERROR encountered a cycle during delegation signature resolution + reuse foo3 as foo4; + //~^ ERROR encountered a cycle during delegation signature resolution + reuse foo4 as foo5; + //~^ ERROR encountered a cycle during delegation signature resolution + reuse foo5 as foo; + //~^ ERROR encountered a cycle during delegation signature resolution +} + +mod fourth_mod { + trait Trait { + reuse Trait::foo as bar; + //~^ ERROR encountered a cycle during delegation signature resolution + reuse Trait::bar as foo; + //~^ ERROR encountered a cycle during delegation signature resolution + } +} + +mod fifth_mod { + reuse super::fifth_mod::{bar as foo, foo as bar}; + //~^ ERROR encountered a cycle during delegation signature resolution + //~| ERROR encountered a cycle during delegation signature resolution + + trait GlobReuse { + reuse GlobReuse::{foo as bar, bar as goo, goo as foo}; + //~^ ERROR encountered a cycle during delegation signature resolution + //~| ERROR encountered a cycle during delegation signature resolution + //~| ERROR encountered a cycle during delegation signature resolution + } +} + +fn main() {} diff --git a/tests/ui/delegation/recursive-delegation-errors.stderr b/tests/ui/delegation/recursive-delegation-errors.stderr new file mode 100644 index 000000000000..9c4e316745ae --- /dev/null +++ b/tests/ui/delegation/recursive-delegation-errors.stderr @@ -0,0 +1,98 @@ +error: failed to resolve delegation callee + --> $DIR/recursive-delegation-errors.rs:6:11 + | +LL | reuse foo; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:11:11 + | +LL | reuse foo as bar; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:13:11 + | +LL | reuse bar as foo; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:18:11 + | +LL | reuse foo as foo1; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:20:11 + | +LL | reuse foo1 as foo2; + | ^^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:22:11 + | +LL | reuse foo2 as foo3; + | ^^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:24:11 + | +LL | reuse foo3 as foo4; + | ^^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:26:11 + | +LL | reuse foo4 as foo5; + | ^^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:28:11 + | +LL | reuse foo5 as foo; + | ^^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:34:22 + | +LL | reuse Trait::foo as bar; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:36:22 + | +LL | reuse Trait::bar as foo; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:42:30 + | +LL | reuse super::fifth_mod::{bar as foo, foo as bar}; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:42:42 + | +LL | reuse super::fifth_mod::{bar as foo, foo as bar}; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:47:27 + | +LL | reuse GlobReuse::{foo as bar, bar as goo, goo as foo}; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:47:39 + | +LL | reuse GlobReuse::{foo as bar, bar as goo, goo as foo}; + | ^^^ + +error: encountered a cycle during delegation signature resolution + --> $DIR/recursive-delegation-errors.rs:47:51 + | +LL | reuse GlobReuse::{foo as bar, bar as goo, goo as foo}; + | ^^^ + +error: aborting due to 16 previous errors + diff --git a/tests/ui/delegation/recursive-delegation-pass.rs b/tests/ui/delegation/recursive-delegation-pass.rs new file mode 100644 index 000000000000..93f8fa401b55 --- /dev/null +++ b/tests/ui/delegation/recursive-delegation-pass.rs @@ -0,0 +1,68 @@ +//@ check-pass +//@ edition:2018 +//@ aux-crate:recursive_delegation_aux=recursive-delegation-aux.rs + +#![feature(fn_delegation)] +#![allow(incomplete_features)] + +mod first_mod { + pub mod to_reuse { + pub fn foo(x: usize) -> usize { + x + 1 + } + } + + mod single_reuse { + reuse crate::first_mod::to_reuse::foo; + reuse foo as bar; + reuse foo as bar1; + reuse bar as goo; + reuse goo as koo; + reuse koo as too; + } + + mod glob_reuse { + reuse super::to_reuse::{foo as bar, foo as bar1} { self } + reuse super::glob_reuse::{bar as goo, goo as koo, koo as too} { self } + } +} + +mod second_mod { + trait T { + fn foo(&self); + reuse T::foo as bar; + reuse T::bar as goo; + reuse T::goo as poo; + } + + trait TGlob { + fn xd(&self) -> &Self; + fn foo1(&self); + fn foo2(&self); + fn foo3(&self); + fn foo4(&self); + + reuse TGlob::{foo1 as bar1, foo3 as bar3, bar1 as bar11, bar11 as bar111} { self.xd() } + } +} + +mod third_mod { + reuse crate::first_mod::to_reuse::foo { + reuse foo as bar { + reuse bar as goo { + bar(123) + } + + goo(123) + } + + bar(123) + } +} + +mod fourth_mod { + reuse recursive_delegation_aux::goo as bar; + reuse bar as foo; +} + +fn main() {} diff --git a/tests/ui/delegation/unsupported.current.stderr b/tests/ui/delegation/unsupported.current.stderr index 5c4115630c00..2bb0633621f2 100644 --- a/tests/ui/delegation/unsupported.current.stderr +++ b/tests/ui/delegation/unsupported.current.stderr @@ -36,24 +36,15 @@ LL | reuse ToReuse::opaque_ret; | ^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: recursive delegation is not supported yet - --> $DIR/unsupported.rs:46:22 - | -LL | pub reuse to_reuse2::foo; - | --- callee defined here -... -LL | reuse to_reuse1::foo; - | ^^^ - error[E0283]: type annotations needed - --> $DIR/unsupported.rs:55:18 + --> $DIR/unsupported.rs:54:18 | LL | reuse Trait::foo; | ^^^ cannot infer type | = note: cannot satisfy `_: effects::Trait` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0283, E0391. For more information about an error, try `rustc --explain E0283`. diff --git a/tests/ui/delegation/unsupported.next.stderr b/tests/ui/delegation/unsupported.next.stderr index a626da9a1442..1665d1f39d6d 100644 --- a/tests/ui/delegation/unsupported.next.stderr +++ b/tests/ui/delegation/unsupported.next.stderr @@ -28,24 +28,15 @@ LL | reuse ToReuse::opaque_ret; = note: cycle used when computing implied outlives bounds for `::opaque_ret::{anon_assoc#0}` (hack disabled = false) = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: recursive delegation is not supported yet - --> $DIR/unsupported.rs:46:22 - | -LL | pub reuse to_reuse2::foo; - | --- callee defined here -... -LL | reuse to_reuse1::foo; - | ^^^ - error[E0283]: type annotations needed - --> $DIR/unsupported.rs:55:18 + --> $DIR/unsupported.rs:54:18 | LL | reuse Trait::foo; | ^^^ cannot infer type | = note: cannot satisfy `_: effects::Trait` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0283, E0391. For more information about an error, try `rustc --explain E0283`. diff --git a/tests/ui/delegation/unsupported.rs b/tests/ui/delegation/unsupported.rs index 5e2bd832a4d8..1681888e34ea 100644 --- a/tests/ui/delegation/unsupported.rs +++ b/tests/ui/delegation/unsupported.rs @@ -44,7 +44,6 @@ mod recursive { } reuse to_reuse1::foo; - //~^ ERROR recursive delegation is not supported yet } mod effects { diff --git a/tests/ui/deprecation/deprecation-sanity.rs b/tests/ui/deprecation/deprecation-sanity.rs index 45ee91741e5a..d1061dc1e170 100644 --- a/tests/ui/deprecation/deprecation-sanity.rs +++ b/tests/ui/deprecation/deprecation-sanity.rs @@ -3,7 +3,7 @@ // Various checks that deprecation attributes are used correctly mod bogus_attribute_types_1 { - #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason' + #[deprecated(since = "a", note = "a", reason)] //~ ERROR malformed `deprecated` attribute input [E0539] fn f1() { } #[deprecated(since = "a", note)] //~ ERROR malformed `deprecated` attribute input [E0539] diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index a96d4a0bdea8..a4dc9f23d3d2 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -1,8 +1,10 @@ -error[E0541]: unknown meta item 'reason' - --> $DIR/deprecation-sanity.rs:6:43 +error[E0539]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:6:5 | LL | #[deprecated(since = "a", note = "a", reason)] - | ^^^^^^ expected one of `since`, `note` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^ + | | + | valid arguments are `since` or `note` error[E0539]: malformed `deprecated` attribute input --> $DIR/deprecation-sanity.rs:9:5 @@ -86,5 +88,5 @@ LL | #[deprecated = "hello"] error: aborting due to 10 previous errors -Some errors have detailed explanations: E0538, E0539, E0541, E0565. +Some errors have detailed explanations: E0538, E0539, E0565. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/inherent-impls-overlap-check/auxiliary/repeat.rs b/tests/ui/duplicate/inherent-impls-overlap-check/auxiliary/repeat.rs similarity index 100% rename from tests/ui/inherent-impls-overlap-check/auxiliary/repeat.rs rename to tests/ui/duplicate/inherent-impls-overlap-check/auxiliary/repeat.rs diff --git a/tests/ui/issues/issue-19097.rs b/tests/ui/duplicate/inherent-impls-overlap-check/disjoint-ref-mut-method.rs similarity index 69% rename from tests/ui/issues/issue-19097.rs rename to tests/ui/duplicate/inherent-impls-overlap-check/disjoint-ref-mut-method.rs index a329ba6f073e..d4594b2e2c95 100644 --- a/tests/ui/issues/issue-19097.rs +++ b/tests/ui/duplicate/inherent-impls-overlap-check/disjoint-ref-mut-method.rs @@ -1,6 +1,6 @@ +//! regression test for //@ check-pass #![allow(dead_code)] -// regression test for #19097 struct Foo(T); diff --git a/tests/ui/inherent-impls-overlap-check/no-overlap.rs b/tests/ui/duplicate/inherent-impls-overlap-check/no-overlap.rs similarity index 100% rename from tests/ui/inherent-impls-overlap-check/no-overlap.rs rename to tests/ui/duplicate/inherent-impls-overlap-check/no-overlap.rs diff --git a/tests/ui/inherent-impls-overlap-check/overlap.rs b/tests/ui/duplicate/inherent-impls-overlap-check/overlap.rs similarity index 100% rename from tests/ui/inherent-impls-overlap-check/overlap.rs rename to tests/ui/duplicate/inherent-impls-overlap-check/overlap.rs diff --git a/tests/ui/inherent-impls-overlap-check/overlap.stderr b/tests/ui/duplicate/inherent-impls-overlap-check/overlap.stderr similarity index 100% rename from tests/ui/inherent-impls-overlap-check/overlap.stderr rename to tests/ui/duplicate/inherent-impls-overlap-check/overlap.stderr diff --git a/tests/ui/eii/wrong_target.rs b/tests/ui/eii/attribute_targets.rs similarity index 100% rename from tests/ui/eii/wrong_target.rs rename to tests/ui/eii/attribute_targets.rs diff --git a/tests/ui/eii/wrong_target.stderr b/tests/ui/eii/attribute_targets.stderr similarity index 67% rename from tests/ui/eii/wrong_target.stderr rename to tests/ui/eii/attribute_targets.stderr index 9b27f49fb302..bf04c323c95c 100644 --- a/tests/ui/eii/wrong_target.stderr +++ b/tests/ui/eii/attribute_targets.stderr @@ -1,107 +1,107 @@ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:7:1 + --> $DIR/attribute_targets.rs:7:1 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:9:1 + --> $DIR/attribute_targets.rs:9:1 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:13:1 + --> $DIR/attribute_targets.rs:13:1 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:15:1 + --> $DIR/attribute_targets.rs:15:1 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:21:1 + --> $DIR/attribute_targets.rs:21:1 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:23:1 + --> $DIR/attribute_targets.rs:23:1 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:27:1 + --> $DIR/attribute_targets.rs:27:1 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:29:1 + --> $DIR/attribute_targets.rs:29:1 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:32:5 + --> $DIR/attribute_targets.rs:32:5 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:34:5 + --> $DIR/attribute_targets.rs:34:5 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:39:1 + --> $DIR/attribute_targets.rs:39:1 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:41:1 + --> $DIR/attribute_targets.rs:41:1 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:44:5 + --> $DIR/attribute_targets.rs:44:5 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:46:5 + --> $DIR/attribute_targets.rs:46:5 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:51:1 + --> $DIR/attribute_targets.rs:51:1 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:53:1 + --> $DIR/attribute_targets.rs:53:1 | LL | #[eii] | ^^^^^^ error: `#[foo]` is only valid on functions - --> $DIR/wrong_target.rs:56:5 + --> $DIR/attribute_targets.rs:56:5 | LL | #[foo] | ^^^^^^ error: `#[eii]` is only valid on functions - --> $DIR/wrong_target.rs:58:5 + --> $DIR/attribute_targets.rs:58:5 | LL | #[eii] | ^^^^^^ diff --git a/tests/ui/eii/default/multiple-default-impls-same-name.rs b/tests/ui/eii/default/multiple-default-impls-same-name.rs new file mode 100644 index 000000000000..080e0a46b755 --- /dev/null +++ b/tests/ui/eii/default/multiple-default-impls-same-name.rs @@ -0,0 +1,19 @@ +#![crate_type = "lib"] +#![feature(extern_item_impls)] +// `eii` expands to, among other things, `macro eii() {}`. +// If we have two eiis named the same thing, we have a duplicate definition +// for that macro. The compiler happily continues compiling on duplicate +// definitions though, to emit as many diagnostics as possible. +// However, in the case of eiis, this can break the assumption that every +// eii has only one default implementation, since the default for both eiis will +// name resolve to the same eii definiton (since the other definition was duplicate) +// This test tests for the previously-ICE that occurred when this assumption +// (of 1 default) was broken which was reported in #149982. + +#[eii(eii1)] +fn a() {} + +#[eii(eii1)] +//~^ ERROR the name `eii1` is defined multiple times +fn a() {} +//~^ ERROR the name `a` is defined multiple times diff --git a/tests/ui/eii/default/multiple-default-impls-same-name.stderr b/tests/ui/eii/default/multiple-default-impls-same-name.stderr new file mode 100644 index 000000000000..20544fcfeeba --- /dev/null +++ b/tests/ui/eii/default/multiple-default-impls-same-name.stderr @@ -0,0 +1,25 @@ +error[E0428]: the name `a` is defined multiple times + --> $DIR/multiple-default-impls-same-name.rs:18:1 + | +LL | fn a() {} + | ------ previous definition of the value `a` here +... +LL | fn a() {} + | ^^^^^^ `a` redefined here + | + = note: `a` must be defined only once in the value namespace of this module + +error[E0428]: the name `eii1` is defined multiple times + --> $DIR/multiple-default-impls-same-name.rs:16:1 + | +LL | #[eii(eii1)] + | ------------ previous definition of the macro `eii1` here +... +LL | #[eii(eii1)] + | ^^^^^^^^^^^^ `eii1` redefined here + | + = note: `eii1` must be defined only once in the macro namespace of this module + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/eii/default/multiple-default-impls.rs b/tests/ui/eii/default/multiple-default-impls.rs new file mode 100644 index 000000000000..8d2ad0c1e137 --- /dev/null +++ b/tests/ui/eii/default/multiple-default-impls.rs @@ -0,0 +1,18 @@ +#![crate_type = "lib"] +#![feature(extern_item_impls)] +// `eii` expands to, among other things, `macro eii() {}`. +// If we have two eiis named the same thing, we have a duplicate definition +// for that macro. The compiler happily continues compiling on duplicate +// definitions though, to emit as many diagnostics as possible. +// However, in the case of eiis, this can break the assumption that every +// eii has only one default implementation, since the default for both eiis will +// name resolve to the same eii definiton (since the other definition was duplicate) +// This test tests for the previously-ICE that occurred when this assumption +// (of 1 default) was broken which was reported in #149982. + +#[eii(eii1)] +fn a() {} + +#[eii(eii1)] +//~^ ERROR the name `eii1` is defined multiple times +fn b() {} diff --git a/tests/ui/eii/default/multiple-default-impls.stderr b/tests/ui/eii/default/multiple-default-impls.stderr new file mode 100644 index 000000000000..b270b2346cd5 --- /dev/null +++ b/tests/ui/eii/default/multiple-default-impls.stderr @@ -0,0 +1,14 @@ +error[E0428]: the name `eii1` is defined multiple times + --> $DIR/multiple-default-impls.rs:16:1 + | +LL | #[eii(eii1)] + | ------------ previous definition of the macro `eii1` here +... +LL | #[eii(eii1)] + | ^^^^^^^^^^^^ `eii1` redefined here + | + = note: `eii1` must be defined only once in the macro namespace of this module + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/eii/subtype_1.rs b/tests/ui/eii/type_checking/subtype_1.rs similarity index 100% rename from tests/ui/eii/subtype_1.rs rename to tests/ui/eii/type_checking/subtype_1.rs diff --git a/tests/ui/eii/subtype_1.stderr b/tests/ui/eii/type_checking/subtype_1.stderr similarity index 100% rename from tests/ui/eii/subtype_1.stderr rename to tests/ui/eii/type_checking/subtype_1.stderr diff --git a/tests/ui/eii/subtype_2.rs b/tests/ui/eii/type_checking/subtype_2.rs similarity index 100% rename from tests/ui/eii/subtype_2.rs rename to tests/ui/eii/type_checking/subtype_2.rs diff --git a/tests/ui/eii/subtype_2.stderr b/tests/ui/eii/type_checking/subtype_2.stderr similarity index 100% rename from tests/ui/eii/subtype_2.stderr rename to tests/ui/eii/type_checking/subtype_2.stderr diff --git a/tests/ui/eii/subtype_3.rs b/tests/ui/eii/type_checking/subtype_3.rs similarity index 100% rename from tests/ui/eii/subtype_3.rs rename to tests/ui/eii/type_checking/subtype_3.rs diff --git a/tests/ui/eii/subtype_4.rs b/tests/ui/eii/type_checking/subtype_4.rs similarity index 100% rename from tests/ui/eii/subtype_4.rs rename to tests/ui/eii/type_checking/subtype_4.rs diff --git a/tests/ui/eii/wrong_ret_ty.rs b/tests/ui/eii/type_checking/wrong_ret_ty.rs similarity index 100% rename from tests/ui/eii/wrong_ret_ty.rs rename to tests/ui/eii/type_checking/wrong_ret_ty.rs diff --git a/tests/ui/eii/wrong_ret_ty.stderr b/tests/ui/eii/type_checking/wrong_ret_ty.stderr similarity index 100% rename from tests/ui/eii/wrong_ret_ty.stderr rename to tests/ui/eii/type_checking/wrong_ret_ty.stderr diff --git a/tests/ui/eii/wrong_ty.rs b/tests/ui/eii/type_checking/wrong_ty.rs similarity index 100% rename from tests/ui/eii/wrong_ty.rs rename to tests/ui/eii/type_checking/wrong_ty.rs diff --git a/tests/ui/eii/wrong_ty.stderr b/tests/ui/eii/type_checking/wrong_ty.stderr similarity index 100% rename from tests/ui/eii/wrong_ty.stderr rename to tests/ui/eii/type_checking/wrong_ty.stderr diff --git a/tests/ui/eii/wrong_ty_2.rs b/tests/ui/eii/type_checking/wrong_ty_2.rs similarity index 100% rename from tests/ui/eii/wrong_ty_2.rs rename to tests/ui/eii/type_checking/wrong_ty_2.rs diff --git a/tests/ui/eii/wrong_ty_2.stderr b/tests/ui/eii/type_checking/wrong_ty_2.stderr similarity index 100% rename from tests/ui/eii/wrong_ty_2.stderr rename to tests/ui/eii/type_checking/wrong_ty_2.stderr diff --git a/tests/ui/enum-discriminant/wrapping_niche.stderr b/tests/ui/enum-discriminant/wrapping_niche.stderr index e3e1755e14dd..9b97ad4aeac7 100644 --- a/tests/ui/enum-discriminant/wrapping_niche.stderr +++ b/tests/ui/enum-discriminant/wrapping_niche.stderr @@ -16,7 +16,7 @@ error: layout_of(UnsignedAroundZero) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -52,7 +52,7 @@ error: layout_of(UnsignedAroundZero) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -73,7 +73,7 @@ error: layout_of(UnsignedAroundZero) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -94,7 +94,7 @@ error: layout_of(UnsignedAroundZero) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -134,7 +134,7 @@ error: layout_of(SignedAroundZero) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -170,7 +170,7 @@ error: layout_of(SignedAroundZero) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -191,7 +191,7 @@ error: layout_of(SignedAroundZero) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -212,7 +212,7 @@ error: layout_of(SignedAroundZero) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/enum/enum-variant-no-field.rs b/tests/ui/enum/enum-variant-no-field.rs new file mode 100644 index 000000000000..a69766b7c48d --- /dev/null +++ b/tests/ui/enum/enum-variant-no-field.rs @@ -0,0 +1,9 @@ +//! regression test for https://github.com/rust-lang/rust/issues/23253 +enum Foo { + Bar, +} + +fn main() { + Foo::Bar.a; + //~^ ERROR no field `a` on type `Foo` +} diff --git a/tests/ui/issues/issue-23253.stderr b/tests/ui/enum/enum-variant-no-field.stderr similarity index 83% rename from tests/ui/issues/issue-23253.stderr rename to tests/ui/enum/enum-variant-no-field.stderr index ec7696909e72..4d02bf876189 100644 --- a/tests/ui/issues/issue-23253.stderr +++ b/tests/ui/enum/enum-variant-no-field.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `a` on type `Foo` - --> $DIR/issue-23253.rs:4:14 + --> $DIR/enum-variant-no-field.rs:7:14 | LL | Foo::Bar.a; | ^ unknown field diff --git a/tests/ui/issues/issue-50442.rs b/tests/ui/enum/enum-with-uninhabited-variant.rs similarity index 58% rename from tests/ui/issues/issue-50442.rs rename to tests/ui/enum/enum-with-uninhabited-variant.rs index 70c764f33ddf..8dfa492e6ea5 100644 --- a/tests/ui/issues/issue-50442.rs +++ b/tests/ui/enum/enum-with-uninhabited-variant.rs @@ -1,3 +1,4 @@ +//! regression test for issue https://github.com/rust-lang/rust/issues/50442 //@ run-pass #![allow(dead_code)] enum Void {} @@ -5,7 +6,7 @@ enum Void {} enum Foo { A(i32), B(Void), - C(i32) + C(i32), } fn main() { diff --git a/tests/ui/expr/return-in-block-tuple.rs b/tests/ui/expr/return-in-block-tuple.rs new file mode 100644 index 000000000000..2b3a59f21412 --- /dev/null +++ b/tests/ui/expr/return-in-block-tuple.rs @@ -0,0 +1,7 @@ +//! regression test for https://github.com/rust-lang/rust/issues/18110 +//@ run-pass +#![allow(unreachable_code)] + +fn main() { + ({ return },); +} diff --git a/tests/ui/issues/issue-24161.rs b/tests/ui/extern/extern-fn-pointer-clone-copy.rs similarity index 54% rename from tests/ui/issues/issue-24161.rs rename to tests/ui/extern/extern-fn-pointer-clone-copy.rs index 974add438616..a67e0d1d703a 100644 --- a/tests/ui/issues/issue-24161.rs +++ b/tests/ui/extern/extern-fn-pointer-clone-copy.rs @@ -1,11 +1,12 @@ +//! regression test for //@ check-pass #![allow(dead_code)] -#[derive(Copy,Clone)] +#[derive(Copy, Clone)] struct Functions { a: fn(u32) -> u32, b: extern "C" fn(u32) -> u32, c: unsafe fn(u32) -> u32, - d: unsafe extern "C" fn(u32) -> u32 + d: unsafe extern "C" fn(u32) -> u32, } pub fn main() {} diff --git a/tests/ui/issues/issue-19398.rs b/tests/ui/extern/extern-rust-trait-method.rs similarity index 66% rename from tests/ui/issues/issue-19398.rs rename to tests/ui/extern/extern-rust-trait-method.rs index 473e43650c2c..d71e5a8b6d53 100644 --- a/tests/ui/issues/issue-19398.rs +++ b/tests/ui/extern/extern-rust-trait-method.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/19398 //@ check-pass trait T { diff --git a/tests/ui/extern/extern-types-field-offset.rs b/tests/ui/extern/extern-types-field-offset.rs index 470ae07a0b55..9fe1cdf3bc47 100644 --- a/tests/ui/extern/extern-types-field-offset.rs +++ b/tests/ui/extern/extern-types-field-offset.rs @@ -2,6 +2,7 @@ //@ check-run-results //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stderr: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ ignore-backends: gcc #![feature(extern_types)] diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index 0b0dd80f0115..1560f2b5f4a7 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -1,4 +1,3 @@ -//~ NOTE: not an `extern crate` item // This is testing whether various builtin attributes signals an // error or warning when put in "weird" places. // @@ -29,7 +28,7 @@ //~| WARN cannot be used on crates //~| WARN previously accepted #![no_link] -//~^ ERROR: attribute should be applied to an `extern crate` item +//~^ ERROR: `#[no_link]` attribute cannot be used on crates #![export_name = "2200"] //~^ ERROR: attribute cannot be used on //~| NOTE takes precedence @@ -60,29 +59,40 @@ mod inline { } #[no_link] -//~^ ERROR attribute should be applied to an `extern crate` item +//~^ ERROR `#[no_link]` attribute cannot be used on modules mod no_link { - //~^ NOTE not an `extern crate` item - mod inner { #![no_link] } - //~^ ERROR attribute should be applied to an `extern crate` item - //~| NOTE not an `extern crate` item + //~^ ERROR `#[no_link]` attribute cannot be used on modules - #[no_link] fn f() { } - //~^ ERROR attribute should be applied to an `extern crate` item - //~| NOTE not an `extern crate` item + #[no_link] fn f() { + //~^ ERROR `#[no_link]` attribute cannot be used on functions + match () { + #[no_link] + //~^ WARN `#[no_link]` attribute cannot be used on match arms [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + _ => () + } + } - #[no_link] struct S; - //~^ ERROR attribute should be applied to an `extern crate` item - //~| NOTE not an `extern crate` item + #[no_link] + //~^ ERROR `#[no_link]` attribute cannot be used on structs + struct S { + #[no_link] + //~^ WARN `#[no_link]` attribute cannot be used on struct fields [unused_attributes] + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + field: () + } #[no_link]type T = S; - //~^ ERROR attribute should be applied to an `extern crate` item - //~| NOTE not an `extern crate` item + //~^ ERROR `#[no_link]` attribute cannot be used on type aliases #[no_link] impl S { } - //~^ ERROR attribute should be applied to an `extern crate` item - //~| NOTE not an `extern crate` item + //~^ ERROR `#[no_link]` attribute cannot be used on inherent impl blocks + + #[no_link] + //~^ WARN `#[no_link]` attribute cannot be used on macro defs + //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + macro_rules! m{() => {}} } #[export_name = "2200"] diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 013e52923811..662776e58026 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -1,5 +1,5 @@ error[E0658]: use of an internal attribute - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:12:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:11:1 | LL | #![rustc_main] | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #![rustc_main] = note: the `#[rustc_main]` attribute is used internally to specify test entry point function error: `#[macro_export]` attribute cannot be used on crates - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:10:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:9:1 | LL | #![macro_export] | ^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | #![macro_export] = help: `#[macro_export]` can only be applied to macro defs error: `#[rustc_main]` attribute cannot be used on crates - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:12:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:11:1 | LL | #![rustc_main] | ^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | #![rustc_main] = help: `#[rustc_main]` can only be applied to functions error: `#[path]` attribute cannot be used on crates - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:21:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:20:1 | LL | #![path = "3800"] | ^^^^^^^^^^^^^^^^^ @@ -33,15 +33,23 @@ LL | #![path = "3800"] = help: `#[path]` can only be applied to modules error: `#[automatically_derived]` attribute cannot be used on crates - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:23:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:22:1 | LL | #![automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: `#[automatically_derived]` can only be applied to trait impl blocks +error: `#[no_link]` attribute cannot be used on crates + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:30:1 + | +LL | #![no_link] + | ^^^^^^^^^^^ + | + = help: `#[no_link]` can only be applied to extern crates + error: `#[export_name]` attribute cannot be used on crates - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:33:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1 | LL | #![export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +57,7 @@ LL | #![export_name = "2200"] = help: `#[export_name]` can be applied to functions and statics error: `#[inline]` attribute cannot be used on crates - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:36:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:35:1 | LL | #![inline] | ^^^^^^^^^^ @@ -57,7 +65,7 @@ LL | #![inline] = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:38:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:37:1 | LL | #[inline] | ^^^^^^^^^ @@ -65,7 +73,7 @@ LL | #[inline] = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:43:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:42:17 | LL | mod inner { #![inline] } | ^^^^^^^^^^ @@ -73,7 +81,7 @@ LL | mod inner { #![inline] } = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:52:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:51:5 | LL | #[inline] struct S; | ^^^^^^^^^ @@ -81,7 +89,7 @@ LL | #[inline] struct S; = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:55:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:54:5 | LL | #[inline] type T = S; | ^^^^^^^^^ @@ -89,15 +97,63 @@ LL | #[inline] type T = S; = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:58:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:57:5 | LL | #[inline] impl S { } | ^^^^^^^^^ | = help: `#[inline]` can only be applied to functions +error: `#[no_link]` attribute cannot be used on modules + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:61:1 + | +LL | #[no_link] + | ^^^^^^^^^^ + | + = help: `#[no_link]` can only be applied to extern crates + +error: `#[no_link]` attribute cannot be used on modules + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:64:17 + | +LL | mod inner { #![no_link] } + | ^^^^^^^^^^^ + | + = help: `#[no_link]` can only be applied to extern crates + +error: `#[no_link]` attribute cannot be used on functions + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:67:5 + | +LL | #[no_link] fn f() { + | ^^^^^^^^^^ + | + = help: `#[no_link]` can only be applied to extern crates + +error: `#[no_link]` attribute cannot be used on structs + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:77:5 + | +LL | #[no_link] + | ^^^^^^^^^^ + | + = help: `#[no_link]` can only be applied to extern crates + +error: `#[no_link]` attribute cannot be used on type aliases + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:86:5 + | +LL | #[no_link]type T = S; + | ^^^^^^^^^^ + | + = help: `#[no_link]` can only be applied to extern crates + +error: `#[no_link]` attribute cannot be used on inherent impl blocks + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:89:5 + | +LL | #[no_link] impl S { } + | ^^^^^^^^^^ + | + = help: `#[no_link]` can only be applied to extern crates + error: `#[export_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:88:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:98:1 | LL | #[export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +161,7 @@ LL | #[export_name = "2200"] = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:91:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:101:17 | LL | mod inner { #![export_name="2200"] } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +169,7 @@ LL | mod inner { #![export_name="2200"] } = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:96:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:106:5 | LL | #[export_name = "2200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +177,7 @@ LL | #[export_name = "2200"] struct S; = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:99:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:109:5 | LL | #[export_name = "2200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +185,7 @@ LL | #[export_name = "2200"] type T = S; = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:102:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:112:5 | LL | #[export_name = "2200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -137,29 +193,15 @@ LL | #[export_name = "2200"] impl S { } = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on required trait methods - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:106:9 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:116:9 | LL | #[export_name = "2200"] fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: `#[export_name]` can be applied to functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks -error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:62:1 - | -LL | #[no_link] - | ^^^^^^^^^^ -LL | -LL | / mod no_link { -LL | | -LL | | -LL | | mod inner { #![no_link] } -... | -LL | | } - | |_- not an `extern crate` item - error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:113:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:123:8 | LL | #[repr(C)] | ^ @@ -172,7 +214,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:137:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:147:8 | LL | #[repr(Rust)] | ^^^^ @@ -184,20 +226,14 @@ LL | | mod inner { #![repr(Rust)] } LL | | } | |_- not a struct, enum, or union -error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:31:1 - | -LL | #![no_link] - | ^^^^^^^^^^^ not an `extern crate` item - warning: `#[no_mangle]` attribute may not be used in combination with `#[export_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:25:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:24:1 | LL | #![no_mangle] | ^^^^^^^^^^^^^ `#[no_mangle]` is ignored | note: `#[export_name]` takes precedence - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:33:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1 | LL | #![export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -208,7 +244,7 @@ LL - #![no_mangle] | error: `repr` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 | LL | #![repr()] | ^^^^^^^^^^ @@ -222,86 +258,56 @@ LL - #![repr()] LL + #[repr()] | -error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:67:17 - | -LL | mod inner { #![no_link] } - | ------------^^^^^^^^^^^-- not an `extern crate` item - -error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:71:5 - | -LL | #[no_link] fn f() { } - | ^^^^^^^^^^ ---------- not an `extern crate` item - -error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:75:5 - | -LL | #[no_link] struct S; - | ^^^^^^^^^^ --------- not an `extern crate` item - -error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:79:5 - | -LL | #[no_link]type T = S; - | ^^^^^^^^^^----------- not an `extern crate` item - -error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:83:5 - | -LL | #[no_link] impl S { } - | ^^^^^^^^^^ ---------- not an `extern crate` item - error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:117:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:25 | LL | mod inner { #![repr(C)] } | --------------------^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:121:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:12 | LL | #[repr(C)] fn f() { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:127:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:137:12 | LL | #[repr(C)] type T = S; | ^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:12 | LL | #[repr(C)] impl S { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:141:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:151:25 | LL | mod inner { #![repr(Rust)] } | --------------------^^^^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:145:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:155:12 | LL | #[repr(Rust)] fn f() { } | ^^^^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:151:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:161:12 | LL | #[repr(Rust)] type T = S; | ^^^^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:155:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:165:12 | LL | #[repr(Rust)] impl S { } | ^^^^ ---------- not a struct, enum, or union error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:45:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ @@ -310,8 +316,35 @@ LL | #[inline = "2100"] fn f() { } = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default +warning: `#[no_link]` attribute cannot be used on match arms + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:70:13 + | +LL | #[no_link] + | ^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[no_link]` can only be applied to extern crates + +warning: `#[no_link]` attribute cannot be used on struct fields + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:80:9 + | +LL | #[no_link] + | ^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[no_link]` can only be applied to extern crates + +warning: `#[no_link]` attribute cannot be used on macro defs + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:92:5 + | +LL | #[no_link] + | ^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[no_link]` can only be applied to extern crates + warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:17:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:16:1 | LL | #![repr()] | ^^^^^^^^^^ help: remove this attribute @@ -319,7 +352,7 @@ LL | #![repr()] = note: using `repr` with an empty list has no effect warning: `#[no_mangle]` attribute cannot be used on crates - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:25:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:24:1 | LL | #![no_mangle] | ^^^^^^^^^^^^^ @@ -327,13 +360,13 @@ LL | #![no_mangle] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[no_mangle]` can be applied to functions and statics -error: aborting due to 37 previous errors; 3 warnings emitted +error: aborting due to 37 previous errors; 6 warnings emitted Some errors have detailed explanations: E0517, E0658. For more information about an error, try `rustc --explain E0517`. Future incompatibility report: Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:45:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 4b5420a2ff8b..6716e78a7197 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -680,6 +680,10 @@ mod link_section { //~| HELP remove the attribute trait Tr { #[link_section = "1800"] + //~^ WARN attribute cannot be used on + //~| WARN previously accepted + //~| HELP can be applied to + //~| HELP remove the attribute fn inside_tr_no_default(&self); #[link_section = "1800"] diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 676372ad85e0..8ed39a0079ba 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -203,7 +203,7 @@ LL | #![reexport_test_harness_main = "2900"] | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:710:1 | LL | #[link(name = "x")] | ^^^^^^^^^^^^^^^^^^^ @@ -219,7 +219,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:832:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:836:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -230,7 +230,7 @@ LL | #![crate_type = "0800"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:856:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL | #![feature(x0600)] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:881:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:885:1 | LL | #[no_main] | ^^^^^^^^^^ @@ -252,7 +252,7 @@ LL | #![no_main] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:905:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:909:1 | LL | #[no_builtins] | ^^^^^^^^^^^^^^ @@ -329,7 +329,7 @@ LL | #![reexport_test_harness_main = "2900"] impl S { } | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:17 | LL | mod inner { #![link(name = "x")] } | ------------^^^^^^^^^^^^^^^^^^^^-- not an `extern` block @@ -337,7 +337,7 @@ LL | mod inner { #![link(name = "x")] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 | LL | #[link(name = "x")] fn f() { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -345,7 +345,7 @@ LL | #[link(name = "x")] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:722:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:726:5 | LL | #[link(name = "x")] struct S; | ^^^^^^^^^^^^^^^^^^^ --------- not an `extern` block @@ -353,7 +353,7 @@ LL | #[link(name = "x")] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:727:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:731:5 | LL | #[link(name = "x")] type T = S; | ^^^^^^^^^^^^^^^^^^^ ----------- not an `extern` block @@ -361,7 +361,7 @@ LL | #[link(name = "x")] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:732:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:736:5 | LL | #[link(name = "x")] impl S { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -369,7 +369,7 @@ LL | #[link(name = "x")] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:741:5 | LL | #[link(name = "x")] extern "Rust" {} | ^^^^^^^^^^^^^^^^^^^ @@ -377,13 +377,13 @@ LL | #[link(name = "x")] extern "Rust" {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:836:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:840:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -394,7 +394,7 @@ LL | #![crate_type = "0800"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:847:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -405,7 +405,7 @@ LL | #![crate_type = "0800"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:847:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:851:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -416,7 +416,7 @@ LL | #![crate_type = "0800"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:851:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:855:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -427,13 +427,13 @@ LL | #![crate_type = "0800"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:864:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:863:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ @@ -444,7 +444,7 @@ LL | #![feature(x0600)] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:871:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ @@ -455,7 +455,7 @@ LL | #![feature(x0600)] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:871:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:875:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ @@ -466,7 +466,7 @@ LL | #![feature(x0600)] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:875:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:879:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ @@ -477,13 +477,13 @@ LL | #![feature(x0600)] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:885:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:889:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:888:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:892:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ @@ -494,7 +494,7 @@ LL | #![no_main] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:892:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:896:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ @@ -505,7 +505,7 @@ LL | #![no_main] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:896:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:900:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ @@ -516,7 +516,7 @@ LL | #![no_main] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:900:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:904:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ @@ -527,13 +527,13 @@ LL | #![no_main] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:909:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:913:17 | LL | mod inner { #![no_builtins] } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:912:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:916:5 | LL | #[no_builtins] fn f() { } | ^^^^^^^^^^^^^^ @@ -544,7 +544,7 @@ LL | #![no_builtins] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:916:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:920:5 | LL | #[no_builtins] struct S; | ^^^^^^^^^^^^^^ @@ -555,7 +555,7 @@ LL | #![no_builtins] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:920:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:924:5 | LL | #[no_builtins] type T = S; | ^^^^^^^^^^^^^^ @@ -566,7 +566,7 @@ LL | #![no_builtins] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:924:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:928:5 | LL | #[no_builtins] impl S { } | ^^^^^^^^^^^^^^ @@ -1222,8 +1222,17 @@ LL | #[link_section = "1800"] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[link_section]` can be applied to functions and statics +warning: `#[link_section]` attribute cannot be used on required trait methods + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:682:9 + | +LL | #[link_section = "1800"] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[link_section]` can be applied to functions, inherent methods, provided trait methods, statics, and trait methods in impl blocks + warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:757:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:761:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -1232,7 +1241,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:766:17 | LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ @@ -1241,7 +1250,7 @@ LL | mod inner { #![must_use] } = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: `#[must_use]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:771:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:775:5 | LL | #[must_use] type T = S; | ^^^^^^^^^^^ @@ -1250,7 +1259,7 @@ LL | #[must_use] type T = S; = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: `#[must_use]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5 | LL | #[must_use] impl S { } | ^^^^^^^^^^^ @@ -1259,13 +1268,13 @@ LL | #[must_use] impl S { } = help: `#[must_use]` can be applied to data types, functions, traits, and unions warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![windows_subsystem]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:782:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:786:1 | LL | #[windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:788:1 | LL | / mod windows_subsystem { LL | | @@ -1275,67 +1284,67 @@ LL | | } | |_^ warning: the `#![windows_subsystem]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:786:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:790:17 | LL | mod inner { #![windows_subsystem="windows"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![windows_subsystem]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:5 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:38 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:38 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![windows_subsystem]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:5 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:38 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:38 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![windows_subsystem]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:5 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:38 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:38 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![windows_subsystem]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:805:5 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:38 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:805:38 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:812:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:810:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:1 | LL | / mod crate_name { LL | | @@ -1345,67 +1354,67 @@ LL | | } | |_^ warning: the `#![crate_name]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:812:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:816:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:28 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:823:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:823:28 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:823:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:827:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:823:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:827:28 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:827:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:831:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:827:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:831:28 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:931:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:935:1 | LL | / mod recursion_limit { LL | | @@ -1415,67 +1424,67 @@ LL | | } | |_^ warning: the `#![recursion_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:936:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:936:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:31 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:31 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:948:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:948:31 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:948:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:952:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:948:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:952:31 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:959:1 | LL | / mod type_length_limit { LL | | @@ -1485,55 +1494,55 @@ LL | | } | |_^ warning: the `#![type_length_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:960:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:960:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:33 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:968:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:968:33 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:968:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:972:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:968:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:972:33 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:972:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:976:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:972:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:976:33 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^ @@ -1601,5 +1610,5 @@ LL | #![must_use] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to data types, functions, traits, and unions -warning: 174 warnings emitted +warning: 175 warnings emitted diff --git a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs index 67959a318297..274faa4495ef 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.rs @@ -13,7 +13,7 @@ mod macro_escape { //~^ ERROR arguments to `macro_use` are not allowed here #[macro_use = "2700"] struct S; - //~^ ERROR valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` + //~^ ERROR malformed //~| WARN cannot be used on //~| WARN previously accepted diff --git a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr index 5be17e96fb15..1aa0e8fc2830 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-macro_use.stderr @@ -16,11 +16,23 @@ error: arguments to `macro_use` are not allowed here LL | #![macro_use(my_macro)] | ^^^^^^^^^^^^^^^^^^^^^^^ -error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and `#[macro_use]` +error[E0539]: malformed `macro_use` attribute input --> $DIR/issue-43106-gating-of-macro_use.rs:15:5 | LL | #[macro_use = "2700"] struct S; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^--------^ + | | + | expected a list or no arguments here + | + = note: for more information, visit +help: try changing it to one of the following valid forms of the attribute + | +LL - #[macro_use = "2700"] struct S; +LL + #[macro_use(name1, name2, ...)] struct S; + | +LL - #[macro_use = "2700"] struct S; +LL + #[macro_use] struct S; + | warning: `#[macro_use]` attribute cannot be used on structs --> $DIR/issue-43106-gating-of-macro_use.rs:15:5 @@ -61,3 +73,4 @@ LL | #[macro_use] impl S { } error: aborting due to 4 previous errors; 4 warnings emitted +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/issues/issue-3656.rs b/tests/ui/ffi/ffi-struct-size-alignment.rs similarity index 100% rename from tests/ui/issues/issue-3656.rs rename to tests/ui/ffi/ffi-struct-size-alignment.rs diff --git a/tests/ui/fmt/println-float.rs b/tests/ui/fmt/println-float.rs new file mode 100644 index 000000000000..3adb0b434349 --- /dev/null +++ b/tests/ui/fmt/println-float.rs @@ -0,0 +1,5 @@ +//! regression test for https://github.com/rust-lang/rust/issues/11382 +//@ run-pass +fn main() { + println!("{}", 1.2); +} diff --git a/tests/ui/issues/issue-23036.rs b/tests/ui/hashmap/hashmap-path-key.rs similarity index 71% rename from tests/ui/issues/issue-23036.rs rename to tests/ui/hashmap/hashmap-path-key.rs index 5186fccd042b..0551ba7ea8ee 100644 --- a/tests/ui/issues/issue-23036.rs +++ b/tests/ui/hashmap/hashmap-path-key.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass use std::collections::HashMap; diff --git a/tests/ui/hygiene/panic-location.rs b/tests/ui/hygiene/panic-location.rs index 4731d8e1d538..27850f3e4c3a 100644 --- a/tests/ui/hygiene/panic-location.rs +++ b/tests/ui/hygiene/panic-location.rs @@ -2,6 +2,7 @@ //@ check-run-results //@ exec-env:RUST_BACKTRACE=0 //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" // // Regression test for issue #70963 // The reported panic location should not be `<::core::macros::panic macros>`. diff --git a/tests/ui/indexing/ref-array-indexing.rs b/tests/ui/indexing/ref-array-indexing.rs new file mode 100644 index 000000000000..302255798df2 --- /dev/null +++ b/tests/ui/indexing/ref-array-indexing.rs @@ -0,0 +1,6 @@ +//! regression test for https://github.com/rust-lang/rust/issues/43205 +//@ run-pass +fn main() { + let _ = &&[()][0]; + println!("{:?}", &[(), ()][1]); +} diff --git a/tests/ui/inference/inference-thread-loop-closure.rs b/tests/ui/inference/inference-thread-loop-closure.rs new file mode 100644 index 000000000000..78b1b145367e --- /dev/null +++ b/tests/ui/inference/inference-thread-loop-closure.rs @@ -0,0 +1,16 @@ +//! regression test for +//@ check-pass +#![allow(unused_must_use)] +use std::thread; + +fn _foo() { + thread::spawn(move || { + // no need for -> () + loop { + println!("hello"); + } + }) + .join(); +} + +fn main() {} diff --git a/tests/ui/internal-lints/diagnostics_incorrect.rs b/tests/ui/internal-lints/diagnostics_incorrect.rs index c1532aa9d57e..787acdad3884 100644 --- a/tests/ui/internal-lints/diagnostics_incorrect.rs +++ b/tests/ui/internal-lints/diagnostics_incorrect.rs @@ -3,7 +3,7 @@ #![feature(rustc_attrs)] #[rustc_lint_diagnostics] -//~^ ERROR attribute should be applied to a function +//~^ ERROR `#[rustc_lint_diagnostics]` attribute cannot be used on structs struct Foo; impl Foo { diff --git a/tests/ui/internal-lints/diagnostics_incorrect.stderr b/tests/ui/internal-lints/diagnostics_incorrect.stderr index e849ca2829e4..4d509acec790 100644 --- a/tests/ui/internal-lints/diagnostics_incorrect.stderr +++ b/tests/ui/internal-lints/diagnostics_incorrect.stderr @@ -1,17 +1,20 @@ -error: malformed `rustc_lint_diagnostics` attribute input - --> $DIR/diagnostics_incorrect.rs:10:5 - | -LL | #[rustc_lint_diagnostics(a)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_lint_diagnostics]` - -error: attribute should be applied to a function definition +error: `#[rustc_lint_diagnostics]` attribute cannot be used on structs --> $DIR/diagnostics_incorrect.rs:5:1 | LL | #[rustc_lint_diagnostics] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | struct Foo; - | ----------- not a function definition + | + = help: `#[rustc_lint_diagnostics]` can only be applied to functions + +error[E0565]: malformed `rustc_lint_diagnostics` attribute input + --> $DIR/diagnostics_incorrect.rs:10:5 + | +LL | #[rustc_lint_diagnostics(a)] + | ^^^^^^^^^^^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[rustc_lint_diagnostics]` error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/internal-lints/query_stability_incorrect.rs b/tests/ui/internal-lints/query_stability_incorrect.rs index a428611caaa4..cefd89867711 100644 --- a/tests/ui/internal-lints/query_stability_incorrect.rs +++ b/tests/ui/internal-lints/query_stability_incorrect.rs @@ -3,7 +3,7 @@ #![feature(rustc_attrs)] #[rustc_lint_query_instability] -//~^ ERROR attribute should be applied to a function +//~^ ERROR `#[rustc_lint_query_instability]` attribute cannot be used on structs struct Foo; impl Foo { diff --git a/tests/ui/internal-lints/query_stability_incorrect.stderr b/tests/ui/internal-lints/query_stability_incorrect.stderr index 3f78b39edd96..8149ac3b9518 100644 --- a/tests/ui/internal-lints/query_stability_incorrect.stderr +++ b/tests/ui/internal-lints/query_stability_incorrect.stderr @@ -1,17 +1,20 @@ -error: malformed `rustc_lint_query_instability` attribute input - --> $DIR/query_stability_incorrect.rs:10:5 - | -LL | #[rustc_lint_query_instability(a)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_lint_query_instability]` - -error: attribute should be applied to a function definition +error: `#[rustc_lint_query_instability]` attribute cannot be used on structs --> $DIR/query_stability_incorrect.rs:5:1 | LL | #[rustc_lint_query_instability] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | struct Foo; - | ----------- not a function definition + | + = help: `#[rustc_lint_query_instability]` can only be applied to functions + +error[E0565]: malformed `rustc_lint_query_instability` attribute input + --> $DIR/query_stability_incorrect.rs:10:5 + | +LL | #[rustc_lint_query_instability(a)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[rustc_lint_query_instability]` error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.stderr b/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.stderr deleted file mode 100644 index 861510212f92..000000000000 --- a/tests/ui/invalid-compile-flags/need-crate-arg-ignore-tidy$x.stderr +++ /dev/null @@ -1,6 +0,0 @@ -error: invalid character '$' in crate name: `need_crate_arg_ignore_tidy$x` - | - = help: you can either pass `--crate-name` on the command line or add `#![crate_name = "…"]` to set the crate name - -error: aborting due to 1 previous error - diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs b/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs index 73a0363904ae..03a28fac95de 100644 --- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs +++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.rs @@ -9,20 +9,24 @@ fn foo2() {} #[rustc_legacy_const_generics(2)] //~ ERROR index exceeds number of arguments fn foo3(_: u8) {} -#[rustc_legacy_const_generics(a)] //~ ERROR arguments should be non-negative integers +#[rustc_legacy_const_generics(a)] //~ ERROR malformed `rustc_legacy_const_generics` attribute input fn foo4() {} -#[rustc_legacy_const_generics(1, a, 2, b)] //~ ERROR arguments should be non-negative integers +#[rustc_legacy_const_generics(1, a, 2, b)] +//~^ ERROR malformed `rustc_legacy_const_generics` attribute input +//~^^ ERROR malformed `rustc_legacy_const_generics` attribute input fn foo5() {} -#[rustc_legacy_const_generics(0)] //~ ERROR attribute should be applied to a function +#[rustc_legacy_const_generics(0)] //~ ERROR `#[rustc_legacy_const_generics]` attribute cannot be used on structs struct S; -#[rustc_legacy_const_generics(0usize)] //~ ERROR suffixed literals are not allowed in attributes +#[rustc_legacy_const_generics(0usize)] +//~^ ERROR suffixed literals are not allowed in attributes +//~^^ ERROR malformed `rustc_legacy_const_generics` attribute input fn foo6() {} extern "C" { - #[rustc_legacy_const_generics(1)] //~ ERROR attribute should be applied to a function + #[rustc_legacy_const_generics(1)] //~ ERROR `#[rustc_legacy_const_generics]` attribute cannot be used on foreign functions fn foo7(); //~ ERROR foreign items may not have const parameters } @@ -30,14 +34,14 @@ extern "C" { fn foo8() {} impl S { - #[rustc_legacy_const_generics(0)] //~ ERROR attribute should be applied to a function + #[rustc_legacy_const_generics(0)] //~ ERROR `#[rustc_legacy_const_generics]` attribute cannot be used on inherent methods fn foo9() {} } -#[rustc_legacy_const_generics] //~ ERROR malformed `rustc_legacy_const_generics` attribute +#[rustc_legacy_const_generics] //~ ERROR malformed `rustc_legacy_const_generics` attribute input fn bar1() {} -#[rustc_legacy_const_generics = 1] //~ ERROR malformed `rustc_legacy_const_generics` attribute +#[rustc_legacy_const_generics = 1] //~ ERROR malformed `rustc_legacy_const_generics` attribute input fn bar2() {} fn main() {} diff --git a/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr b/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr index 1ced1433fe95..e4da6c86c83e 100644 --- a/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr +++ b/tests/ui/invalid/invalid-rustc_legacy_const_generics-arguments.stderr @@ -1,22 +1,88 @@ +error[E0539]: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:12:1 + | +LL | #[rustc_legacy_const_generics(a)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[rustc_legacy_const_generics(N)]` + +error[E0539]: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:15:1 + | +LL | #[rustc_legacy_const_generics(1, a, 2, b)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^^^^^^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[rustc_legacy_const_generics(N)]` + +error[E0539]: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:15:1 + | +LL | #[rustc_legacy_const_generics(1, a, 2, b)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[rustc_legacy_const_generics(N)]` + +error: `#[rustc_legacy_const_generics]` attribute cannot be used on structs + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:20:1 + | +LL | #[rustc_legacy_const_generics(0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_legacy_const_generics]` can only be applied to functions + error: suffixed literals are not allowed in attributes - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:21:31 + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:23:31 | LL | #[rustc_legacy_const_generics(0usize)] | ^^^^^^ | = help: instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) -error: malformed `rustc_legacy_const_generics` attribute input - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:37:1 +error[E0539]: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:23:1 + | +LL | #[rustc_legacy_const_generics(0usize)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^ + | | | + | | expected an integer literal here + | help: must be of the form: `#[rustc_legacy_const_generics(N)]` + +error: `#[rustc_legacy_const_generics]` attribute cannot be used on foreign functions + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:29:5 + | +LL | #[rustc_legacy_const_generics(1)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_legacy_const_generics]` can only be applied to functions + +error: `#[rustc_legacy_const_generics]` attribute cannot be used on inherent methods + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:37:5 + | +LL | #[rustc_legacy_const_generics(0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_legacy_const_generics]` can only be applied to functions + +error[E0539]: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:41:1 | LL | #[rustc_legacy_const_generics] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[rustc_legacy_const_generics(N)]` -error: malformed `rustc_legacy_const_generics` attribute input - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:40:1 +error[E0539]: malformed `rustc_legacy_const_generics` attribute input + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:44:1 | LL | #[rustc_legacy_const_generics = 1] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_legacy_const_generics(N)]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^ + | | | + | | expected this to be a list + | help: must be of the form: `#[rustc_legacy_const_generics(N)]` error: #[rustc_legacy_const_generics] must have one index for each generic parameter --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:3:1 @@ -38,58 +104,23 @@ error: index exceeds number of arguments LL | #[rustc_legacy_const_generics(2)] | ^ there are only 2 arguments -error: arguments should be non-negative integers - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:12:31 - | -LL | #[rustc_legacy_const_generics(a)] - | ^ - -error: arguments should be non-negative integers - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:15:34 - | -LL | #[rustc_legacy_const_generics(1, a, 2, b)] - | ^ ^ - -error: attribute should be applied to a function definition - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:18:1 - | -LL | #[rustc_legacy_const_generics(0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | struct S; - | --------- not a function definition - error: #[rustc_legacy_const_generics] functions must only have const generics - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:29:1 + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:33:1 | LL | #[rustc_legacy_const_generics(0)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | fn foo8() {} | - non-const generic parameter -error: attribute should be applied to a function definition - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:33:5 - | -LL | #[rustc_legacy_const_generics(0)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | fn foo9() {} - | ---------------------------- not a function definition - -error: attribute should be applied to a function definition - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:25:5 - | -LL | #[rustc_legacy_const_generics(1)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | fn foo7(); - | -------------------------- not a function definition - error[E0044]: foreign items may not have const parameters - --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:26:5 + --> $DIR/invalid-rustc_legacy_const_generics-arguments.rs:30:5 | LL | fn foo7(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ can't have const parameters | = help: replace the const parameters with concrete consts -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors -For more information about this error, try `rustc --explain E0044`. +Some errors have detailed explanations: E0044, E0539. +For more information about an error, try `rustc --explain E0044`. diff --git a/tests/ui/issues/issue-10656.rs b/tests/ui/issues/issue-10656.rs deleted file mode 100644 index 250c4bc442f9..000000000000 --- a/tests/ui/issues/issue-10656.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![deny(missing_docs)] -#![crate_type="lib"] -//~^^ ERROR missing documentation for the crate diff --git a/tests/ui/issues/issue-11382.rs b/tests/ui/issues/issue-11382.rs deleted file mode 100644 index 18c8c756f32e..000000000000 --- a/tests/ui/issues/issue-11382.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ run-pass -fn main() { - println!("{}", 1.2); -} diff --git a/tests/ui/issues/issue-17001.rs b/tests/ui/issues/issue-17001.rs deleted file mode 100644 index 68cb2865fdc8..000000000000 --- a/tests/ui/issues/issue-17001.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod foo {} - -fn main() { - let p = foo { x: () }; //~ ERROR expected struct, variant or union type, found module `foo` -} diff --git a/tests/ui/issues/issue-17450.rs b/tests/ui/issues/issue-17450.rs deleted file mode 100644 index d8b20169ee0c..000000000000 --- a/tests/ui/issues/issue-17450.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ build-pass -#![allow(dead_code, warnings)] - -static mut x: isize = 3; -static mut y: isize = unsafe { x }; - -fn main() {} diff --git a/tests/ui/issues/issue-18110.rs b/tests/ui/issues/issue-18110.rs deleted file mode 100644 index 6d563a5bae1c..000000000000 --- a/tests/ui/issues/issue-18110.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ run-pass -#![allow(unreachable_code)] - -fn main() { - ({return},); -} diff --git a/tests/ui/issues/issue-18159.rs b/tests/ui/issues/issue-18159.rs deleted file mode 100644 index bd347d632984..000000000000 --- a/tests/ui/issues/issue-18159.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - let x; //~ ERROR type annotations needed -} diff --git a/tests/ui/issues/issue-18532.rs b/tests/ui/issues/issue-18532.rs deleted file mode 100644 index 31fd87961dc9..000000000000 --- a/tests/ui/issues/issue-18532.rs +++ /dev/null @@ -1,7 +0,0 @@ -// Test that overloaded call parameter checking does not ICE -// when a type error or unconstrained type variable propagates -// into it. - -fn main() { - (return)((),()); //~ ERROR expected function, found `!` -} diff --git a/tests/ui/issues/issue-20454.rs b/tests/ui/issues/issue-20454.rs deleted file mode 100644 index e56f2ffa371a..000000000000 --- a/tests/ui/issues/issue-20454.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ check-pass -#![allow(unused_must_use)] -use std::thread; - -fn _foo() { - thread::spawn(move || { // no need for -> () - loop { - println!("hello"); - } - }).join(); -} - -fn main() {} diff --git a/tests/ui/issues/issue-20772.rs b/tests/ui/issues/issue-20772.rs deleted file mode 100644 index 1500bc831528..000000000000 --- a/tests/ui/issues/issue-20772.rs +++ /dev/null @@ -1,5 +0,0 @@ -trait T : Iterator -//~^ ERROR cycle detected -{} - -fn main() {} diff --git a/tests/ui/issues/issue-21177.rs b/tests/ui/issues/issue-21177.rs deleted file mode 100644 index 258e362d1317..000000000000 --- a/tests/ui/issues/issue-21177.rs +++ /dev/null @@ -1,9 +0,0 @@ -trait Trait { - type A; - type B; -} - -fn foo>() { } -//~^ ERROR cycle detected - -fn main() { } diff --git a/tests/ui/issues/issue-21291.rs b/tests/ui/issues/issue-21291.rs deleted file mode 100644 index 06f50ac6996d..000000000000 --- a/tests/ui/issues/issue-21291.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -//@ needs-threads - -// Regression test for unwrapping the result of `join`, issue #21291 - -use std::thread; - -fn main() { - thread::spawn(|| {}).join().unwrap() -} diff --git a/tests/ui/issues/issue-21306.rs b/tests/ui/issues/issue-21306.rs deleted file mode 100644 index bf42e70a5bc0..000000000000 --- a/tests/ui/issues/issue-21306.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass - -use std::sync::Arc; - -fn main() { - let x = 5; - let command = Arc::new(Box::new(|| { x*2 })); - assert_eq!(command(), 10); -} diff --git a/tests/ui/issues/issue-21449.rs b/tests/ui/issues/issue-21449.rs deleted file mode 100644 index 00ce2b7fffa8..000000000000 --- a/tests/ui/issues/issue-21449.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod MyMod {} - -fn main() { - let myVar = MyMod { T: 0 }; - //~^ ERROR expected struct, variant or union type, found module `MyMod` -} diff --git a/tests/ui/issues/issue-21449.stderr b/tests/ui/issues/issue-21449.stderr deleted file mode 100644 index cd1059d48993..000000000000 --- a/tests/ui/issues/issue-21449.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0574]: expected struct, variant or union type, found module `MyMod` - --> $DIR/issue-21449.rs:4:17 - | -LL | let myVar = MyMod { T: 0 }; - | ^^^^^ not a struct, variant or union type - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0574`. diff --git a/tests/ui/issues/issue-21891.rs b/tests/ui/issues/issue-21891.rs deleted file mode 100644 index 0da6071cdac4..000000000000 --- a/tests/ui/issues/issue-21891.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ build-pass -#![allow(dead_code)] -#![allow(non_upper_case_globals)] - - -static foo: [usize; 3] = [1, 2, 3]; - -static slice_1: &'static [usize] = &foo; -static slice_2: &'static [usize] = &foo; - -fn main() {} diff --git a/tests/ui/issues/issue-23189.rs b/tests/ui/issues/issue-23189.rs deleted file mode 100644 index e5526357cb0e..000000000000 --- a/tests/ui/issues/issue-23189.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod module {} - -fn main() { - let _ = module { x: 0 }; //~ERROR expected struct -} diff --git a/tests/ui/issues/issue-23189.stderr b/tests/ui/issues/issue-23189.stderr deleted file mode 100644 index 37d778dc992e..000000000000 --- a/tests/ui/issues/issue-23189.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0574]: expected struct, variant or union type, found module `module` - --> $DIR/issue-23189.rs:4:13 - | -LL | let _ = module { x: 0 }; - | ^^^^^^ not a struct, variant or union type - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0574`. diff --git a/tests/ui/issues/issue-23253.rs b/tests/ui/issues/issue-23253.rs deleted file mode 100644 index b285cb81ee23..000000000000 --- a/tests/ui/issues/issue-23253.rs +++ /dev/null @@ -1,6 +0,0 @@ -enum Foo { Bar } - -fn main() { - Foo::Bar.a; - //~^ ERROR no field `a` on type `Foo` -} diff --git a/tests/ui/issues/issue-24779.rs b/tests/ui/issues/issue-24779.rs deleted file mode 100644 index f371828606c4..000000000000 --- a/tests/ui/issues/issue-24779.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ run-pass -fn main() { - assert_eq!((||||42)()(), 42); -} diff --git a/tests/ui/issues/issue-27268.rs b/tests/ui/issues/issue-27268.rs deleted file mode 100644 index e8704d215e88..000000000000 --- a/tests/ui/issues/issue-27268.rs +++ /dev/null @@ -1,4 +0,0 @@ -//@ run-pass -fn main() { - const _C: &'static dyn Fn() = &||{}; -} diff --git a/tests/ui/issues/issue-43205.rs b/tests/ui/issues/issue-43205.rs deleted file mode 100644 index 9f54d4258bf1..000000000000 --- a/tests/ui/issues/issue-43205.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ run-pass -fn main() { - let _ = &&[()][0]; - println!("{:?}", &[(),()][1]); -} diff --git a/tests/ui/issues/issue-4968.rs b/tests/ui/issues/issue-4968.rs deleted file mode 100644 index 08539d6debd8..000000000000 --- a/tests/ui/issues/issue-4968.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Regression test for issue #4968 - -//@ dont-require-annotations: NOTE - -const A: (isize,isize) = (4,2); -fn main() { - match 42 { A => () } - //~^ ERROR mismatched types - //~| NOTE expected type `{integer}` - //~| NOTE found tuple `(isize, isize)` - //~| NOTE expected integer, found `(isize, isize)` -} diff --git a/tests/ui/issues/issue-4968.stderr b/tests/ui/issues/issue-4968.stderr deleted file mode 100644 index 2c1e1d7bfe5a..000000000000 --- a/tests/ui/issues/issue-4968.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-4968.rs:7:16 - | -LL | const A: (isize,isize) = (4,2); - | ---------------------- constant defined here -LL | fn main() { -LL | match 42 { A => () } - | -- ^ - | | | - | | expected integer, found `(isize, isize)` - | | `A` is interpreted as a constant, not a new binding - | | help: introduce a new binding instead: `other_a` - | this expression has type `{integer}` - | - = note: expected type `{integer}` - found tuple `(isize, isize)` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-50471.rs b/tests/ui/issues/issue-50471.rs deleted file mode 100644 index 1d8bad20377c..000000000000 --- a/tests/ui/issues/issue-50471.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ check-pass - -fn main() { - assert!({false}); - - assert!(r"\u{41}" == "A"); - - assert!(r"\u{".is_empty()); -} diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index b2ce6385ab65..79ce21eb532b 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -16,7 +16,7 @@ error: layout_of(E) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -52,7 +52,7 @@ error: layout_of(E) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -77,7 +77,7 @@ error: layout_of(E) = Layout { Size(4 bytes), Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, 1, 2, @@ -130,7 +130,7 @@ error: layout_of(S) = Layout { Size(8 bytes), Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, 2, 1, @@ -200,7 +200,7 @@ error: layout_of(Result) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -251,7 +251,7 @@ error: layout_of(Result) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -289,7 +289,7 @@ error: layout_of(Result) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/layout/hexagon-enum.stderr b/tests/ui/layout/hexagon-enum.stderr index 659013c8a6f1..20e0a8642a66 100644 --- a/tests/ui/layout/hexagon-enum.stderr +++ b/tests/ui/layout/hexagon-enum.stderr @@ -16,7 +16,7 @@ error: layout_of(A) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -52,7 +52,7 @@ error: layout_of(A) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -92,7 +92,7 @@ error: layout_of(B) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -128,7 +128,7 @@ error: layout_of(B) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -168,7 +168,7 @@ error: layout_of(C) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -204,7 +204,7 @@ error: layout_of(C) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -244,7 +244,7 @@ error: layout_of(P) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -280,7 +280,7 @@ error: layout_of(P) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -320,7 +320,7 @@ error: layout_of(T) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -356,7 +356,7 @@ error: layout_of(T) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index 2087fedeb19b..61cfcbdc07f7 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -22,7 +22,7 @@ error: layout_of(MissingPayloadField) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -72,7 +72,7 @@ error: layout_of(MissingPayloadField) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -95,7 +95,7 @@ error: layout_of(MissingPayloadField) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -142,7 +142,7 @@ error: layout_of(CommonPayloadField) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -193,7 +193,7 @@ error: layout_of(CommonPayloadField) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -231,7 +231,7 @@ error: layout_of(CommonPayloadField) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -279,7 +279,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -329,7 +329,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -366,7 +366,7 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -414,7 +414,7 @@ error: layout_of(NicheFirst) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -470,7 +470,7 @@ error: layout_of(NicheFirst) = Layout { Size(0 bytes), Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, 1, ], @@ -503,7 +503,7 @@ error: layout_of(NicheFirst) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -524,7 +524,7 @@ error: layout_of(NicheFirst) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -570,7 +570,7 @@ error: layout_of(NicheSecond) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -626,7 +626,7 @@ error: layout_of(NicheSecond) = Layout { Size(1 bytes), Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 1, 0, ], @@ -659,7 +659,7 @@ error: layout_of(NicheSecond) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -680,7 +680,7 @@ error: layout_of(NicheSecond) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/layout/issue-96185-overaligned-enum.stderr b/tests/ui/layout/issue-96185-overaligned-enum.stderr index 6bcc5b4906b5..64e2f42c042f 100644 --- a/tests/ui/layout/issue-96185-overaligned-enum.stderr +++ b/tests/ui/layout/issue-96185-overaligned-enum.stderr @@ -10,7 +10,7 @@ error: layout_of(Aligned1) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -46,7 +46,7 @@ error: layout_of(Aligned1) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -69,7 +69,7 @@ error: layout_of(Aligned1) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -113,7 +113,7 @@ error: layout_of(Aligned2) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -149,7 +149,7 @@ error: layout_of(Aligned2) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -172,7 +172,7 @@ error: layout_of(Aligned2) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/layout/thumb-enum.stderr b/tests/ui/layout/thumb-enum.stderr index 1ef22daf2a9d..a6e603652123 100644 --- a/tests/ui/layout/thumb-enum.stderr +++ b/tests/ui/layout/thumb-enum.stderr @@ -16,7 +16,7 @@ error: layout_of(A) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -52,7 +52,7 @@ error: layout_of(A) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -92,7 +92,7 @@ error: layout_of(B) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -128,7 +128,7 @@ error: layout_of(B) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -168,7 +168,7 @@ error: layout_of(C) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -204,7 +204,7 @@ error: layout_of(C) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -244,7 +244,7 @@ error: layout_of(P) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -280,7 +280,7 @@ error: layout_of(P) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -320,7 +320,7 @@ error: layout_of(T) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -356,7 +356,7 @@ error: layout_of(T) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, diff --git a/tests/ui/layout/zero-sized-array-enum-niche.stderr b/tests/ui/layout/zero-sized-array-enum-niche.stderr index 1707b8aff81c..23f909277808 100644 --- a/tests/ui/layout/zero-sized-array-enum-niche.stderr +++ b/tests/ui/layout/zero-sized-array-enum-niche.stderr @@ -10,7 +10,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -48,7 +48,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -73,7 +73,7 @@ error: layout_of(Result<[u32; 0], bool>) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -118,7 +118,7 @@ error: layout_of(MultipleAlignments) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -156,7 +156,7 @@ error: layout_of(MultipleAlignments) = Layout { offsets: [ Size(2 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -181,7 +181,7 @@ error: layout_of(MultipleAlignments) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -206,7 +206,7 @@ error: layout_of(MultipleAlignments) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -251,7 +251,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -289,7 +289,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -314,7 +314,7 @@ error: layout_of(Result<[u32; 0], Packed>>) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -359,7 +359,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -401,7 +401,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -426,7 +426,7 @@ error: layout_of(Result<[u32; 0], Packed>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/issues/issue-17758.rs b/tests/ui/lifetimes/trait-method-lifetime-suggestion.rs similarity index 50% rename from tests/ui/issues/issue-17758.rs rename to tests/ui/lifetimes/trait-method-lifetime-suggestion.rs index e2ee84694e39..39cf4ae64185 100644 --- a/tests/ui/issues/issue-17758.rs +++ b/tests/ui/lifetimes/trait-method-lifetime-suggestion.rs @@ -1,5 +1,6 @@ -// Test that regionck suggestions in a provided method of a trait -// don't ICE +//! regression test for https://github.com/rust-lang/rust/issues/17758 +//! Test that regionck suggestions in a provided method of a trait +//! don't ICE trait Foo<'a> { fn foo(&'a self); diff --git a/tests/ui/issues/issue-17758.stderr b/tests/ui/lifetimes/trait-method-lifetime-suggestion.stderr similarity index 87% rename from tests/ui/issues/issue-17758.stderr rename to tests/ui/lifetimes/trait-method-lifetime-suggestion.stderr index 7a7d2374ef87..64347577d070 100644 --- a/tests/ui/issues/issue-17758.stderr +++ b/tests/ui/lifetimes/trait-method-lifetime-suggestion.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/issue-17758.rs:7:9 + --> $DIR/trait-method-lifetime-suggestion.rs:8:9 | LL | trait Foo<'a> { | -- lifetime `'a` defined here diff --git a/tests/ui/issues/issue-29540.rs b/tests/ui/limits/limit-huge-struct-derive-debug.rs similarity index 99% rename from tests/ui/issues/issue-29540.rs rename to tests/ui/limits/limit-huge-struct-derive-debug.rs index 6bfeae8559dc..eb793aadd8c6 100644 --- a/tests/ui/issues/issue-29540.rs +++ b/tests/ui/limits/limit-huge-struct-derive-debug.rs @@ -1,3 +1,4 @@ +//! regression test for //@ build-pass #[derive(Debug)] pub struct Config { diff --git a/tests/ui/link-native-libs/link-attr-validation-early.stderr b/tests/ui/link-native-libs/link-attr-validation-early.stderr index 101df0371b54..4bf88e150f45 100644 --- a/tests/ui/link-native-libs/link-attr-validation-early.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-early.stderr @@ -10,7 +10,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/link-attr-validation-early.rs:3:1 | LL | #[link = "foo"] - | ^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^-------^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/link-native-libs/link-attr-validation-late.stderr b/tests/ui/link-native-libs/link-attr-validation-late.stderr index a5f654ca0aeb..b09431f923aa 100644 --- a/tests/ui/link-native-libs/link-attr-validation-late.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-late.stderr @@ -142,9 +142,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/link-attr-validation-late.rs:24:1 | LL | #[link(name = "...", cfg = "literal")] - | ^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/lint/dangling-pointers-from-locals.stderr b/tests/ui/lint/dangling-pointers-from-locals.stderr index 45acc74ac34e..05ced1cdc004 100644 --- a/tests/ui/lint/dangling-pointers-from-locals.stderr +++ b/tests/ui/lint/dangling-pointers-from-locals.stderr @@ -9,6 +9,7 @@ LL | &x | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see = note: `#[warn(dangling_pointers_from_locals)]` on by default warning: function returns a dangling pointer to dropped local variable `x` @@ -24,6 +25,7 @@ LL | x | ^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:24:12 @@ -38,6 +40,7 @@ LL | return y; | ^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:30:5 @@ -52,6 +55,7 @@ LL | &x as *const u8 | dangling pointer created here | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:37:5 @@ -66,6 +70,7 @@ LL | x as *const u8 | ^^^^^^^^^^^^^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:43:12 @@ -80,6 +85,7 @@ LL | return &mut x as *mut u8 as *const u8 as *mut u8; | dangling pointer created here | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:49:5 @@ -92,6 +98,7 @@ LL | &{ x } | ^^^^^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:57:13 @@ -108,6 +115,7 @@ LL | | } | |_____________^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:67:12 @@ -120,6 +128,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:73:12 @@ -132,6 +141,7 @@ LL | return &mut x; | ^^^^^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:80:16 @@ -145,6 +155,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:88:5 @@ -157,6 +168,7 @@ LL | &x | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:94:12 @@ -169,6 +181,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: closure returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:101:16 @@ -181,6 +194,7 @@ LL | return &x; | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `x` --> $DIR/dangling-pointers-from-locals.rs:113:5 @@ -194,6 +208,7 @@ LL | &x | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:118:5 @@ -206,6 +221,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:123:5 @@ -218,6 +234,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:128:5 @@ -230,6 +247,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: function returns a dangling pointer to dropped local variable `a` --> $DIR/dangling-pointers-from-locals.rs:133:5 @@ -242,6 +260,7 @@ LL | &a | ^^ | = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: for more information, see warning: 19 warnings emitted diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs b/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs index d892ebdf6069..7ecee90479d2 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/allow.rs @@ -7,7 +7,7 @@ fn main() { #[deny(dangling_pointers_from_temporaries)] { dbg!(String::new().as_ptr()); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } S.foo() } @@ -18,6 +18,6 @@ impl S { #[warn(dangling_pointers_from_temporaries)] fn foo(self) { dbg!(String::new().as_ptr()); - //~^ WARNING a dangling pointer will be produced because the temporary `String` will be dropped + //~^ WARNING dangling pointer } } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr index e1c12cfd1a50..a235af144e48 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/allow.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/allow.rs:9:28 | LL | dbg!(String::new().as_ptr()); - | ------------- ^^^^^^ this pointer will immediately be invalid + | ------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/allow.rs:7:12 | LL | #[deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -warning: a dangling pointer will be produced because the temporary `String` will be dropped +warning: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/allow.rs:20:28 | LL | dbg!(String::new().as_ptr()); - | ------------- ^^^^^^ this pointer will immediately be invalid + | ------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/allow.rs:18:12 | diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs b/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs index b376582a8867..80787390176f 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/calls.rs @@ -25,12 +25,12 @@ fn ok() { fn not_ok() { { let ptr = cstring().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer consume(ptr); } consume({ let ptr = cstring().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer ptr }); consume({ @@ -39,11 +39,11 @@ fn not_ok() { //^ FIXME: should error }); let _ptr: *const u8 = cstring().as_ptr().cast(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer let _ptr: *const u8 = { cstring() }.as_ptr().cast(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer let _ptr: *const u8 = { cstring().as_ptr() }.cast(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer } fn main() { diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr index 41c6cdd0e3ef..4e302dcc9942 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/calls.stderr @@ -1,72 +1,72 @@ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:27:29 | LL | let ptr = cstring().as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/calls.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:32:29 | LL | let ptr = cstring().as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:41:37 | LL | let _ptr: *const u8 = cstring().as_ptr().cast(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:43:41 | LL | let _ptr: *const u8 = { cstring() }.as_ptr().cast(); - | ------------- ^^^^^^ this pointer will immediately be invalid + | ------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/calls.rs:45:39 | LL | let _ptr: *const u8 = { cstring().as_ptr() }.cast(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 5 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs index a98378794abc..7036019362cd 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.rs @@ -7,12 +7,12 @@ use std::ffi::CString; macro_rules! mymacro { () => { let s = CString::new("some text").unwrap().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer } } fn main() { let s = CString::new("some text").unwrap().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer mymacro!(); } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr index d4126ba231f7..be9f8b19545b 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/cstring-as-ptr.stderr @@ -6,39 +6,39 @@ LL | #![deny(temporary_cstring_as_ptr)] | = note: `#[warn(renamed_and_removed_lints)]` on by default -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/cstring-as-ptr.rs:15:48 | LL | let s = CString::new("some text").unwrap().as_ptr(); - | ---------------------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/cstring-as-ptr.rs:2:9 | LL | #![deny(temporary_cstring_as_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/cstring-as-ptr.rs:9:52 | LL | let s = CString::new("some text").unwrap().as_ptr(); - | ---------------------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement ... LL | mymacro!(); | ---------- in this macro invocation | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see = note: this error originates in the macro `mymacro` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors; 1 warning emitted diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs index 0fb07a3f3bc9..771ddb349344 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.rs @@ -3,9 +3,9 @@ const MAX_PATH: usize = 260; fn main() { let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer let str2 = String::from("TotototototototototototototototototoT").as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer unsafe { std::ptr::copy_nonoverlapping(str2, str1, 30); println!("{:?}", String::from_raw_parts(str1, 30, 30)); diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr index aace55e92cf1..b8baa2bf5601 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/example-from-issue123613.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/example-from-issue123613.rs:5:48 | LL | let str1 = String::with_capacity(MAX_PATH).as_mut_ptr(); - | ------------------------------- ^^^^^^^^^^ this pointer will immediately be invalid + | ------------------------------- ^^^^^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_mut_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_mut_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/example-from-issue123613.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/example-from-issue123613.rs:7:70 | LL | let str2 = String::from("TotototototototototototototototototoT").as_ptr(); - | ----------------------------------------------------- ^^^^^^ this pointer will immediately be invalid + | ----------------------------------------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs b/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs index a5e84d36090f..e56bc0f433e4 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/ext.rs @@ -26,7 +26,7 @@ impl Ext2 for *const u32 { fn main() { let _ptr1 = Vec::::new().as_ptr().dbg(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer let _ptr2 = vec![0].as_ptr().foo(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr index 976334ddef9c..f780f88134f2 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/ext.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/ext.rs:28:35 | LL | let _ptr1 = Vec::::new().as_ptr().dbg(); - | ----------------- ^^^^^^ this pointer will immediately be invalid + | ----------------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/ext.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/ext.rs:30:25 | LL | let _ptr2 = vec![0].as_ptr().foo(); - | ------- ^^^^^^ this pointer will immediately be invalid + | ------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs b/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs index 26019b376d3a..10666e0a8282 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/methods.rs @@ -2,7 +2,7 @@ fn main() { vec![0u8].as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer vec![0u8].as_mut_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr index a86a69bc39a2..367dfae9e65d 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/methods.stderr @@ -1,33 +1,33 @@ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/methods.rs:4:15 | LL | vec![0u8].as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/methods.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/methods.rs:6:15 | LL | vec![0u8].as_mut_ptr(); - | --------- ^^^^^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_mut_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_mut_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_mut_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 2 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs index 1f216586ae81..1f816996d514 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.rs @@ -19,18 +19,18 @@ fn main() { // Call string().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // MethodCall "hello".to_string().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // Tup // impossible // Binary (string() + "hello").as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // Path { @@ -66,7 +66,7 @@ fn main() { // If { (if true { String::new() } else { "hello".into() }).as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } // Loop @@ -75,7 +75,7 @@ fn main() { break String::new(); }) .as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } // Match @@ -84,7 +84,7 @@ fn main() { s => s, } .as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer } // Closure @@ -92,7 +92,7 @@ fn main() { // Block { string() }.as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer // Assign, AssignOp // impossible @@ -132,5 +132,5 @@ fn main() { // Macro vec![0u8].as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr index e8994703cabf..be001acad747 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/temporaries.stderr @@ -1,115 +1,115 @@ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:21:14 | LL | string().as_ptr(); - | -------- ^^^^^^ this pointer will immediately be invalid + | -------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/temporaries.rs:2:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:25:25 | LL | "hello".to_string().as_ptr(); - | ------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:32:26 | LL | (string() + "hello").as_ptr(); - | -------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:68:61 | LL | (if true { String::new() } else { "hello".into() }).as_ptr(); - | --------------------------------------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------------------------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:77:10 | LL | / (loop { LL | | break String::new(); LL | | }) - | |__________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | |__________- this `String` is dropped at end of statement LL | .as_ptr(); - | ^^^^^^ this pointer will immediately be invalid + | ^^^^^^ pointer created here | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:86:10 | LL | / match string() { LL | | s => s, LL | | } - | |_________- this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | |_________- this `String` is dropped at end of statement LL | .as_ptr(); - | ^^^^^^ this pointer will immediately be invalid + | ^^^^^^ pointer created here | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/temporaries.rs:94:18 | LL | { string() }.as_ptr(); - | ------------ ^^^^^^ this pointer will immediately be invalid + | ------------ ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/temporaries.rs:134:15 | LL | vec![0u8].as_ptr(); - | --------- ^^^^^^ this pointer will immediately be invalid + | --------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 8 previous errors diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.rs b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs index 17c3eca89e27..072ec25f4d83 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/types.rs +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.rs @@ -19,39 +19,39 @@ fn declval() -> T { fn main() { declval::().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `CString` will be dropped + //~^ ERROR dangling pointer because temporary `CString` declval::().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `String` will be dropped + //~^ ERROR dangling pointer because temporary `String` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer because temporary `Vec` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped + //~^ ERROR dangling pointer because temporary `Box<[u8]>` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::<[u8; 10]>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped + //~^ ERROR dangling pointer because temporary `[u8; 10]` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped + //~^ ERROR dangling pointer because temporary `Box<[u8; 10]>` declval::>>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box>` will be dropped + //~^ ERROR dangling pointer because temporary `Box>` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box` will be dropped + //~^ ERROR dangling pointer because temporary `Box` declval::>>>>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Box>>>` will be dropped + //~^ ERROR dangling pointer because temporary `Box>>>` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Cell` will be dropped + //~^ ERROR dangling pointer because temporary `Cell` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `MaybeUninit` will be dropped + //~^ ERROR dangling pointer because temporary `MaybeUninit` declval::>().as_ptr(); - //~^ ERROR a dangling pointer will be produced because the temporary `Vec` will be dropped + //~^ ERROR dangling pointer because temporary `Vec` declval::>().get(); - //~^ ERROR a dangling pointer will be produced because the temporary `UnsafeCell` will be dropped + //~^ ERROR dangling pointer because temporary `UnsafeCell` declval::>().get(); - //~^ ERROR a dangling pointer will be produced because the temporary `SyncUnsafeCell` will be dropped + //~^ ERROR dangling pointer because temporary `SyncUnsafeCell` declval::>().as_ptr(); declval::().as_ptr(); } diff --git a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr index fab2459b53f6..bb37c8ad211b 100644 --- a/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr +++ b/tests/ui/lint/dangling-pointers-from-temporaries/types.stderr @@ -1,228 +1,228 @@ -error: a dangling pointer will be produced because the temporary `CString` will be dropped +error: this creates a dangling pointer because temporary `CString` is dropped at end of statement --> $DIR/types.rs:21:26 | LL | declval::().as_ptr(); - | -------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------- ^^^^^^ pointer created here | | - | this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `CString` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `CString` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `CString` inside the function will not suffice - = help: for more information, see + = help: bind the `CString` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see note: the lint level is defined here --> $DIR/types.rs:1:9 | LL | #![deny(dangling_pointers_from_temporaries)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: a dangling pointer will be produced because the temporary `String` will be dropped +error: this creates a dangling pointer because temporary `String` is dropped at end of statement --> $DIR/types.rs:23:25 | LL | declval::().as_ptr(); - | ------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------- ^^^^^^ pointer created here | | - | this `String` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `String` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `String` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `String` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `String` inside the function will not suffice - = help: for more information, see + = help: bind the `String` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/types.rs:25:26 | LL | declval::>().as_ptr(); - | -------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:27:31 | LL | declval::>().as_ptr(); - | ------------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------------- ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box<[u8]>` will be dropped +error: this creates a dangling pointer because temporary `Box<[u8]>` is dropped at end of statement --> $DIR/types.rs:29:28 | LL | declval::>().as_ptr(); - | ---------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------- ^^^^^^ pointer created here | | - | this `Box<[u8]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box<[u8]>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box<[u8]>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box<[u8]>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box<[u8]>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:31:27 | LL | declval::>().as_ptr(); - | --------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------- ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:33:28 | LL | declval::>().as_ptr(); - | ---------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------- ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `[u8; 10]` will be dropped +error: this creates a dangling pointer because temporary `[u8; 10]` is dropped at end of statement --> $DIR/types.rs:35:27 | LL | declval::<[u8; 10]>().as_ptr(); - | --------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------- ^^^^^^ pointer created here | | - | this `[u8; 10]` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `[u8; 10]` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `[u8; 10]` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `[u8; 10]` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `[u8; 10]` inside the function will not suffice - = help: for more information, see + = help: bind the `[u8; 10]` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box<[u8; 10]>` will be dropped +error: this creates a dangling pointer because temporary `Box<[u8; 10]>` is dropped at end of statement --> $DIR/types.rs:37:32 | LL | declval::>().as_ptr(); - | -------------------------- ^^^^^^ this pointer will immediately be invalid + | -------------------------- ^^^^^^ pointer created here | | - | this `Box<[u8; 10]>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box<[u8; 10]>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box<[u8; 10]>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box<[u8; 10]>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box<[u8; 10]>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box<[u8; 10]>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box>` will be dropped +error: this creates a dangling pointer because temporary `Box>` is dropped at end of statement --> $DIR/types.rs:39:31 | LL | declval::>>().as_ptr(); - | ------------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------------- ^^^^^^ pointer created here | | - | this `Box>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box` will be dropped +error: this creates a dangling pointer because temporary `Box` is dropped at end of statement --> $DIR/types.rs:41:30 | LL | declval::>().as_ptr(); - | ------------------------ ^^^^^^ this pointer will immediately be invalid + | ------------------------ ^^^^^^ pointer created here | | - | this `Box` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box` inside the function will not suffice - = help: for more information, see + = help: bind the `Box` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Box>>>` will be dropped +error: this creates a dangling pointer because temporary `Box>>>` is dropped at end of statement --> $DIR/types.rs:43:43 | LL | declval::>>>>().as_ptr(); - | ------------------------------------- ^^^^^^ this pointer will immediately be invalid + | ------------------------------------- ^^^^^^ pointer created here | | - | this `Box>>>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Box>>>` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Box>>>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Box>>>` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Box>>>` inside the function will not suffice - = help: for more information, see + = help: bind the `Box>>>` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Cell` will be dropped +error: this creates a dangling pointer because temporary `Cell` is dropped at end of statement --> $DIR/types.rs:45:27 | LL | declval::>().as_ptr(); - | --------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------- ^^^^^^ pointer created here | | - | this `Cell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Cell` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Cell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Cell` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Cell` inside the function will not suffice - = help: for more information, see + = help: bind the `Cell` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `MaybeUninit` will be dropped +error: this creates a dangling pointer because temporary `MaybeUninit` is dropped at end of statement --> $DIR/types.rs:47:34 | LL | declval::>().as_ptr(); - | ---------------------------- ^^^^^^ this pointer will immediately be invalid + | ---------------------------- ^^^^^^ pointer created here | | - | this `MaybeUninit` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `MaybeUninit` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `MaybeUninit` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `MaybeUninit` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `MaybeUninit` inside the function will not suffice - = help: for more information, see + = help: bind the `MaybeUninit` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `Vec` will be dropped +error: this creates a dangling pointer because temporary `Vec` is dropped at end of statement --> $DIR/types.rs:49:33 | LL | declval::>().as_ptr(); - | --------------------------- ^^^^^^ this pointer will immediately be invalid + | --------------------------- ^^^^^^ pointer created here | | - | this `Vec` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `Vec` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `as_ptr` the `Vec` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `Vec` to lives at least as long as the pointer returned by the call to `as_ptr` - = help: in particular, if this pointer is returned from the current function, binding the `Vec` inside the function will not suffice - = help: for more information, see + = help: bind the `Vec` to a variable such that it outlives the pointer returned by `as_ptr` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `UnsafeCell` will be dropped +error: this creates a dangling pointer because temporary `UnsafeCell` is dropped at end of statement --> $DIR/types.rs:51:33 | LL | declval::>().get(); - | --------------------------- ^^^ this pointer will immediately be invalid + | --------------------------- ^^^ pointer created here | | - | this `UnsafeCell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `UnsafeCell` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `get` the `UnsafeCell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `UnsafeCell` to lives at least as long as the pointer returned by the call to `get` - = help: in particular, if this pointer is returned from the current function, binding the `UnsafeCell` inside the function will not suffice - = help: for more information, see + = help: bind the `UnsafeCell` to a variable such that it outlives the pointer returned by `get` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see -error: a dangling pointer will be produced because the temporary `SyncUnsafeCell` will be dropped +error: this creates a dangling pointer because temporary `SyncUnsafeCell` is dropped at end of statement --> $DIR/types.rs:53:37 | LL | declval::>().get(); - | ------------------------------- ^^^ this pointer will immediately be invalid + | ------------------------------- ^^^ pointer created here | | - | this `SyncUnsafeCell` is deallocated at the end of the statement, bind it to a variable to extend its lifetime + | this `SyncUnsafeCell` is dropped at end of statement | - = note: pointers do not have a lifetime; when calling `get` the `SyncUnsafeCell` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - = help: you must make sure that the variable you bind the `SyncUnsafeCell` to lives at least as long as the pointer returned by the call to `get` - = help: in particular, if this pointer is returned from the current function, binding the `SyncUnsafeCell` inside the function will not suffice - = help: for more information, see + = help: bind the `SyncUnsafeCell` to a variable such that it outlives the pointer returned by `get` + = note: a dangling pointer is safe, but dereferencing one is undefined behavior + = note: returning a pointer to a local variable will always result in a dangling pointer + = note: for more information, see error: aborting due to 17 previous errors diff --git a/tests/ui/lint/lint-missing-doc-crate-attr.rs b/tests/ui/lint/lint-missing-doc-crate-attr.rs new file mode 100644 index 000000000000..51959785c53b --- /dev/null +++ b/tests/ui/lint/lint-missing-doc-crate-attr.rs @@ -0,0 +1,4 @@ +// regression test for https://github.com/rust-lang/rust/issues/10656 +#![deny(missing_docs)] +//~^ ERROR missing documentation for the crate +#![crate_type = "lib"] diff --git a/tests/ui/issues/issue-10656.stderr b/tests/ui/lint/lint-missing-doc-crate-attr.stderr similarity index 58% rename from tests/ui/issues/issue-10656.stderr rename to tests/ui/lint/lint-missing-doc-crate-attr.stderr index 61828f9c02a6..caff87df664b 100644 --- a/tests/ui/issues/issue-10656.stderr +++ b/tests/ui/lint/lint-missing-doc-crate-attr.stderr @@ -1,12 +1,13 @@ error: missing documentation for the crate - --> $DIR/issue-10656.rs:1:1 + --> $DIR/lint-missing-doc-crate-attr.rs:2:1 | LL | / #![deny(missing_docs)] -LL | | #![crate_type="lib"] - | |____________________^ +LL | | +LL | | #![crate_type = "lib"] + | |______________________^ | note: the lint level is defined here - --> $DIR/issue-10656.rs:1:9 + --> $DIR/lint-missing-doc-crate-attr.rs:2:9 | LL | #![deny(missing_docs)] | ^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-missing-doc-crate.rs b/tests/ui/lint/lint-missing-doc-crate-flags.rs similarity index 100% rename from tests/ui/lint/lint-missing-doc-crate.rs rename to tests/ui/lint/lint-missing-doc-crate-flags.rs diff --git a/tests/ui/lint/lint-missing-doc-crate.stderr b/tests/ui/lint/lint-missing-doc-crate-flags.stderr similarity index 81% rename from tests/ui/lint/lint-missing-doc-crate.stderr rename to tests/ui/lint/lint-missing-doc-crate-flags.stderr index 8efd3a17263f..040d60a56ebf 100644 --- a/tests/ui/lint/lint-missing-doc-crate.stderr +++ b/tests/ui/lint/lint-missing-doc-crate-flags.stderr @@ -1,5 +1,5 @@ error: missing documentation for the crate - --> $DIR/lint-missing-doc-crate.rs:4:47 + --> $DIR/lint-missing-doc-crate-flags.rs:4:47 | LL | | ^ diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 86506dba5f08..54ed351d4348 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -16,18 +16,6 @@ note: the lint level is defined here LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:37:1 - | -LL | #[no_link] - | ^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:36:1 - | -LL | #[no_link] - | ^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:34:1 | @@ -40,6 +28,18 @@ note: attribute also specified here LL | #![no_builtins] | ^^^^^^^^^^^^^^^ +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:37:1 + | +LL | #[no_link] + | ^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:36:1 + | +LL | #[no_link] + | ^^^^^^^^^^ + error: unused attribute --> $DIR/unused-attr-duplicate.rs:41:1 | diff --git a/tests/ui/issues/issue-26646.rs b/tests/ui/lint/unused/unused-attr-repr-packed-c-order.rs similarity index 60% rename from tests/ui/issues/issue-26646.rs rename to tests/ui/lint/unused/unused-attr-repr-packed-c-order.rs index b1789b1a91fc..a9da3e4175bd 100644 --- a/tests/ui/issues/issue-26646.rs +++ b/tests/ui/lint/unused/unused-attr-repr-packed-c-order.rs @@ -1,3 +1,4 @@ +//! regression test for //@ check-pass #![deny(unused_attributes)] @@ -9,4 +10,4 @@ pub struct Foo; #[repr(C)] pub struct Bar; -fn main() { } +fn main() {} diff --git a/tests/ui/macros/cfg_select.rs b/tests/ui/macros/cfg_select.rs index 9fc680307025..2369158ba82a 100644 --- a/tests/ui/macros/cfg_select.rs +++ b/tests/ui/macros/cfg_select.rs @@ -47,6 +47,89 @@ fn arm_rhs_expr_3() -> i32 { } } +fn expand_to_statements() -> i32 { + cfg_select! { + true => { + let a = 1; + a + 1 + } + false => { + let b = 2; + b + 1 + } + } +} + +type ExpandToType = cfg_select! { + unix => u32, + _ => i32, +}; + +fn expand_to_pattern(x: Option) -> bool { + match x { + (cfg_select! { + unix => Some(n), + _ => None, + }) => true, + _ => false, + } +} + +cfg_select! { + true => { + fn foo() {} + } + _ => { + fn bar() {} + } +} + +struct S; + +impl S { + cfg_select! { + true => { + fn foo() {} + } + _ => { + fn bar() {} + } + } +} + +trait T { + cfg_select! { + true => { + fn a(); + } + _ => { + fn b(); + } + } +} + +impl T for S { + cfg_select! { + true => { + fn a() {} + } + _ => { + fn b() {} + } + } +} + +extern "C" { + cfg_select! { + true => { + fn puts(s: *const i8) -> i32; + } + _ => { + fn printf(fmt: *const i8, ...) -> i32; + } + } +} + cfg_select! { _ => {} true => {} diff --git a/tests/ui/macros/cfg_select.stderr b/tests/ui/macros/cfg_select.stderr index 5e2d705760cf..ffd8540425ab 100644 --- a/tests/ui/macros/cfg_select.stderr +++ b/tests/ui/macros/cfg_select.stderr @@ -1,5 +1,5 @@ warning: unreachable predicate - --> $DIR/cfg_select.rs:52:5 + --> $DIR/cfg_select.rs:135:5 | LL | _ => {} | - always matches @@ -7,7 +7,7 @@ LL | true => {} | ^^^^ this predicate is never reached error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:56:1 + --> $DIR/cfg_select.rs:139:1 | LL | / cfg_select! { LL | | @@ -16,57 +16,55 @@ LL | | } | |_^ error: none of the predicates in this `cfg_select` evaluated to true - --> $DIR/cfg_select.rs:61:1 + --> $DIR/cfg_select.rs:144:1 | LL | cfg_select! {} | ^^^^^^^^^^^^^^ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `=>` - --> $DIR/cfg_select.rs:65:5 + --> $DIR/cfg_select.rs:148:5 | LL | => {} | ^^ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression - --> $DIR/cfg_select.rs:70:5 + --> $DIR/cfg_select.rs:153:5 | LL | () => {} | ^^ expressions are not allowed here error[E0539]: malformed `cfg_select` macro input - --> $DIR/cfg_select.rs:75:5 + --> $DIR/cfg_select.rs:158:5 | LL | "str" => {} | ^^^^^ expected a valid identifier here - | error[E0539]: malformed `cfg_select` macro input - --> $DIR/cfg_select.rs:80:5 + --> $DIR/cfg_select.rs:163:5 | LL | a::b => {} | ^^^^ expected a valid identifier here - | error[E0537]: invalid predicate `a` - --> $DIR/cfg_select.rs:85:5 + --> $DIR/cfg_select.rs:168:5 | LL | a() => {} | ^^^ error: expected one of `(`, `::`, `=>`, or `=`, found `+` - --> $DIR/cfg_select.rs:90:7 + --> $DIR/cfg_select.rs:173:7 | LL | a + 1 => {} | ^ expected one of `(`, `::`, `=>`, or `=` error: expected one of `(`, `::`, `=>`, or `=`, found `!` - --> $DIR/cfg_select.rs:96:8 + --> $DIR/cfg_select.rs:179:8 | LL | cfg!() => {} | ^ expected one of `(`, `::`, `=>`, or `=` warning: unexpected `cfg` condition name: `a` - --> $DIR/cfg_select.rs:90:5 + --> $DIR/cfg_select.rs:173:5 | LL | a + 1 => {} | ^ help: found config with similar value: `target_feature = "a"` @@ -77,7 +75,7 @@ LL | a + 1 => {} = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `cfg` - --> $DIR/cfg_select.rs:96:5 + --> $DIR/cfg_select.rs:179:5 | LL | cfg!() => {} | ^^^ diff --git a/tests/ui/macros/cfg_select_parse_error.rs b/tests/ui/macros/cfg_select_parse_error.rs new file mode 100644 index 000000000000..90fcb2309b34 --- /dev/null +++ b/tests/ui/macros/cfg_select_parse_error.rs @@ -0,0 +1,18 @@ +#![feature(cfg_select)] +#![crate_type = "lib"] + +// Check that parse errors in arms that are not selected are still reported. + +fn print() { + println!(cfg_select! { + false => { 1 ++ 2 } + //~^ ERROR Rust has no postfix increment operator + _ => { "not unix" } + }); +} + +cfg_select! { + false => { fn foo() { 1 +++ 2 } } + //~^ ERROR Rust has no postfix increment operator + _ => {} +} diff --git a/tests/ui/macros/cfg_select_parse_error.stderr b/tests/ui/macros/cfg_select_parse_error.stderr new file mode 100644 index 000000000000..d4c86c3ceade --- /dev/null +++ b/tests/ui/macros/cfg_select_parse_error.stderr @@ -0,0 +1,26 @@ +error: Rust has no postfix increment operator + --> $DIR/cfg_select_parse_error.rs:8:22 + | +LL | false => { 1 ++ 2 } + | ^^ not a valid postfix operator + | +help: use `+= 1` instead + | +LL - false => { 1 ++ 2 } +LL + false => { { let tmp = 1 ; 1 += 1; tmp } 2 } + | + +error: Rust has no postfix increment operator + --> $DIR/cfg_select_parse_error.rs:15:29 + | +LL | false => { fn foo() { 1 +++ 2 } } + | ^^ not a valid postfix operator + | +help: use `+= 1` instead + | +LL - false => { fn foo() { 1 +++ 2 } } +LL + false => { fn foo() { { let tmp = 1 ; 1 += 1; tmp }+ 2 } } + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/issues/issue-19734.rs b/tests/ui/macros/undefined-macro-in-impl.rs similarity index 60% rename from tests/ui/issues/issue-19734.rs rename to tests/ui/macros/undefined-macro-in-impl.rs index fe4a327aef49..64b394f984be 100644 --- a/tests/ui/issues/issue-19734.rs +++ b/tests/ui/macros/undefined-macro-in-impl.rs @@ -1,3 +1,4 @@ +//! regression test for https://github.com/rust-lang/rust/issues/19734 fn main() {} struct Type; diff --git a/tests/ui/issues/issue-19734.stderr b/tests/ui/macros/undefined-macro-in-impl.stderr similarity index 75% rename from tests/ui/issues/issue-19734.stderr rename to tests/ui/macros/undefined-macro-in-impl.stderr index ed48714fe507..8d1bd958d7f6 100644 --- a/tests/ui/issues/issue-19734.stderr +++ b/tests/ui/macros/undefined-macro-in-impl.stderr @@ -1,5 +1,5 @@ error: cannot find macro `undef` in this scope - --> $DIR/issue-19734.rs:6:5 + --> $DIR/undefined-macro-in-impl.rs:7:5 | LL | undef!(); | ^^^^^ diff --git a/tests/ui/malformed/malformed-regressions.stderr b/tests/ui/malformed/malformed-regressions.stderr index f46afda1e477..2bf6ff3a9e7a 100644 --- a/tests/ui/malformed/malformed-regressions.stderr +++ b/tests/ui/malformed/malformed-regressions.stderr @@ -10,7 +10,9 @@ error[E0539]: malformed `link` attribute input --> $DIR/malformed-regressions.rs:10:1 | LL | #[link = ""] - | ^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^----^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/match/issue-92100.stderr b/tests/ui/match/issue-92100.stderr index eb9f4ba1ad69..13aacc4782af 100644 --- a/tests/ui/match/issue-92100.stderr +++ b/tests/ui/match/issue-92100.stderr @@ -4,10 +4,16 @@ error[E0425]: cannot find value `a` in this scope LL | [a.., a] => {} | ^ not found in this scope | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern help: if you meant to collect the rest of the slice in `a`, use the at operator | LL | [a @ .., a] => {} | + +help: if you meant to destructure a range use a struct pattern + | +LL - [a.., a] => {} +LL + [std::ops::RangeFrom { start: a }, a] => {} + | error: aborting due to 1 previous error diff --git a/tests/ui/match/match-const-tuple-type-mismatch.rs b/tests/ui/match/match-const-tuple-type-mismatch.rs new file mode 100644 index 000000000000..c9358e3d5faa --- /dev/null +++ b/tests/ui/match/match-const-tuple-type-mismatch.rs @@ -0,0 +1,13 @@ +//! Regression test for issue https://github.com/rust-lang/rust/issues/4968 +//@ dont-require-annotations: NOTE + +const A: (isize, isize) = (4, 2); +fn main() { + match 42 { + A => (), + //~^ ERROR mismatched types + //~| NOTE expected type `{integer}` + //~| NOTE found tuple `(isize, isize)` + //~| NOTE expected integer, found `(isize, isize)` + } +} diff --git a/tests/ui/match/match-const-tuple-type-mismatch.stderr b/tests/ui/match/match-const-tuple-type-mismatch.stderr new file mode 100644 index 000000000000..e7dd97c4e9a6 --- /dev/null +++ b/tests/ui/match/match-const-tuple-type-mismatch.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/match-const-tuple-type-mismatch.rs:7:9 + | +LL | const A: (isize, isize) = (4, 2); + | ----------------------- constant defined here +LL | fn main() { +LL | match 42 { + | -- this expression has type `{integer}` +LL | A => (), + | ^ + | | + | expected integer, found `(isize, isize)` + | `A` is interpreted as a constant, not a new binding + | help: introduce a new binding instead: `other_a` + | + = note: expected type `{integer}` + found tuple `(isize, isize)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-18464.rs b/tests/ui/match/match-range-char-const.rs similarity index 65% rename from tests/ui/issues/issue-18464.rs rename to tests/ui/match/match-range-char-const.rs index 995064719839..84881fd56cc0 100644 --- a/tests/ui/issues/issue-18464.rs +++ b/tests/ui/match/match-range-char-const.rs @@ -1,3 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18464 //@ run-pass #![deny(dead_code)] @@ -7,6 +8,6 @@ const HIGH_RANGE: char = '9'; fn main() { match '5' { LOW_RANGE..=HIGH_RANGE => (), - _ => () + _ => (), }; } diff --git a/tests/ui/issues/issue-17933.rs b/tests/ui/match/match-static-pattern.rs similarity index 56% rename from tests/ui/issues/issue-17933.rs rename to tests/ui/match/match-static-pattern.rs index 6da4e6e15284..5d367f9ec17c 100644 --- a/tests/ui/issues/issue-17933.rs +++ b/tests/ui/match/match-static-pattern.rs @@ -1,9 +1,10 @@ +//! regression test for issue https://github.com/rust-lang/rust/issues/17933 pub static X: usize = 1; fn main() { match 1 { - self::X => { }, + self::X => {} //~^ ERROR expected unit struct, unit variant or constant, found static `self::X` - _ => { }, + _ => {} } } diff --git a/tests/ui/issues/issue-17933.stderr b/tests/ui/match/match-static-pattern.stderr similarity index 80% rename from tests/ui/issues/issue-17933.stderr rename to tests/ui/match/match-static-pattern.stderr index 42a7e0442073..1b111fbcc2de 100644 --- a/tests/ui/issues/issue-17933.stderr +++ b/tests/ui/match/match-static-pattern.stderr @@ -1,7 +1,7 @@ error[E0532]: expected unit struct, unit variant or constant, found static `self::X` - --> $DIR/issue-17933.rs:5:9 + --> $DIR/match-static-pattern.rs:6:9 | -LL | self::X => { }, +LL | self::X => {} | ^^^^^^^ not a unit struct, unit variant or constant error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-37686.rs b/tests/ui/match/match-usize-min-max-pattern.rs similarity index 59% rename from tests/ui/issues/issue-37686.rs rename to tests/ui/match/match-usize-min-max-pattern.rs index 5a72f2fc74c2..6a801406895d 100644 --- a/tests/ui/issues/issue-37686.rs +++ b/tests/ui/match/match-usize-min-max-pattern.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass fn main() { match (0, 0) { diff --git a/tests/ui/mir/lint/storage-live.rs b/tests/ui/mir/lint/storage-live.rs index 32bd32754ddd..047ceaaf82c6 100644 --- a/tests/ui/mir/lint/storage-live.rs +++ b/tests/ui/mir/lint/storage-live.rs @@ -4,6 +4,7 @@ //@ normalize-stderr: "thread 'rustc'.*panicked.*\n" -> "" //@ normalize-stderr: "storage_live\[....\]" -> "storage_live[HASH]" //@ normalize-stderr: "(delayed at [^:]+):\d+:\d+ - " -> "$1:LL:CC - " +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ rustc-env:RUST_BACKTRACE=0 #![feature(custom_mir, core_intrinsics)] diff --git a/tests/ui/mir/lint/storage-live.stderr b/tests/ui/mir/lint/storage-live.stderr index 50df9ae061fc..ef0d253b15a8 100644 --- a/tests/ui/mir/lint/storage-live.stderr +++ b/tests/ui/mir/lint/storage-live.stderr @@ -1,12 +1,12 @@ error: internal compiler error: broken MIR in Item(DefId(0:8 ~ storage_live[HASH]::multiple_storage)) (after pass CheckForceInline) at bb0[1]: StorageLive(_1) which already has storage here - --> $DIR/storage-live.rs:21:13 + --> $DIR/storage-live.rs:22:13 | LL | StorageLive(a); | ^^^^^^^^^^^^^^ | note: delayed at compiler/rustc_mir_transform/src/lint.rs:LL:CC - disabled backtrace - --> $DIR/storage-live.rs:21:13 + --> $DIR/storage-live.rs:22:13 | LL | StorageLive(a); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-16783.rs b/tests/ui/moves/array-copy-move.rs similarity index 56% rename from tests/ui/issues/issue-16783.rs rename to tests/ui/moves/array-copy-move.rs index 2ecc42b579d5..ea95bc06a369 100644 --- a/tests/ui/issues/issue-16783.rs +++ b/tests/ui/moves/array-copy-move.rs @@ -1,3 +1,4 @@ +//! regression test for issue https://github.com/rust-lang/rust/issues/16783 //@ run-pass #![allow(unused_variables)] diff --git a/tests/ui/issues/issue-17373.rs b/tests/ui/never_type/never-deref.rs similarity index 51% rename from tests/ui/issues/issue-17373.rs rename to tests/ui/never_type/never-deref.rs index dc3be48a7ead..6e4b89db04a5 100644 --- a/tests/ui/issues/issue-17373.rs +++ b/tests/ui/never_type/never-deref.rs @@ -1,3 +1,4 @@ +//! regression test for https://github.com/rust-lang/rust/issues/17373 fn main() { *return //~ ERROR type `!` cannot be dereferenced ; diff --git a/tests/ui/issues/issue-17373.stderr b/tests/ui/never_type/never-deref.stderr similarity index 87% rename from tests/ui/issues/issue-17373.stderr rename to tests/ui/never_type/never-deref.stderr index 0e16d08c87d3..76f31f377987 100644 --- a/tests/ui/issues/issue-17373.stderr +++ b/tests/ui/never_type/never-deref.stderr @@ -1,5 +1,5 @@ error[E0614]: type `!` cannot be dereferenced - --> $DIR/issue-17373.rs:2:5 + --> $DIR/never-deref.rs:3:5 | LL | *return | ^^^^^^^ can't be dereferenced diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs b/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.rs similarity index 66% rename from tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs rename to tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.rs index 5d21fa100a43..addfc7b91391 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs +++ b/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.rs @@ -1,6 +1,5 @@ -// Test where we fail to approximate due to demanding a postdom -// relationship between our upper bounds. - +// Test that we can propagate multiple region errors for closure constraints +// where the longer region has multiple non-local lower bounds without any postdominating one. //@ compile-flags:-Zverbose-internals #![feature(rustc_attrs)] @@ -13,9 +12,8 @@ use std::cell::Cell; // 'x: 'b // 'c: 'y // -// we have to prove that `'x: 'y`. We currently can only approximate -// via a postdominator -- hence we fail to choose between `'a` and -// `'b` here and report the error in the closure. +// we have to prove that `'x: 'y`. We find non-local lower bounds of 'x to be 'a and 'b and +// non-local upper bound of 'y to be 'c. So we propagate `'b: 'c` and `'a: 'c`. fn establish_relationships<'a, 'b, 'c, F>( _cell_a: Cell<&'a u32>, _cell_b: Cell<&'b u32>, @@ -36,6 +34,8 @@ fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32) #[rustc_regions] fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) { + //~vv ERROR lifetime may not live long enough + //~v ERROR lifetime may not live long enough establish_relationships( cell_a, cell_b, @@ -43,7 +43,7 @@ fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell |_outlives1, _outlives2, _outlives3, x, y| { // Only works if 'x: 'y: let p = x.get(); - demand_y(x, y, p) //~ ERROR + demand_y(x, y, p) }, ); } diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.stderr new file mode 100644 index 000000000000..af7ea253cc52 --- /dev/null +++ b/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.stderr @@ -0,0 +1,79 @@ +note: external requirements + --> $DIR/propagate-approximated-both-lower-bounds.rs:43:9 + | +LL | |_outlives1, _outlives2, _outlives3, x, y| { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply::{closure#0} with closure args [ + i16, + for extern "rust-call" fn((std::cell::Cell<&'?1 &'^0 u32>, std::cell::Cell<&'?2 &'^0 u32>, std::cell::Cell<&'^1 &'?3 u32>, std::cell::Cell<&'^0 u32>, std::cell::Cell<&'^1 u32>)), + (), + ] + = note: late-bound region is '?7 + = note: late-bound region is '?8 + = note: late-bound region is '?4 + = note: late-bound region is '?5 + = note: late-bound region is '?6 + = note: number of external vids: 7 + = note: where '?2: '?3 + = note: where '?1: '?3 + +note: no external requirements + --> $DIR/propagate-approximated-both-lower-bounds.rs:36:1 + | +LL | fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: defining type: supply + +error: lifetime may not live long enough + --> $DIR/propagate-approximated-both-lower-bounds.rs:39:5 + | +LL | fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) { + | -- -- lifetime `'c` defined here + | | + | lifetime `'a` defined here +... +LL | / establish_relationships( +LL | | cell_a, +LL | | cell_b, +LL | | cell_c, +... | +LL | | }, +LL | | ); + | |_____^ argument requires that `'a` must outlive `'c` + | + = help: consider adding the following bound: `'a: 'c` + = note: requirement occurs because of the type `Cell<&'?10 u32>`, which makes the generic argument `&'?10 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + +error: lifetime may not live long enough + --> $DIR/propagate-approximated-both-lower-bounds.rs:39:5 + | +LL | fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) { + | -- -- lifetime `'c` defined here + | | + | lifetime `'b` defined here +... +LL | / establish_relationships( +LL | | cell_a, +LL | | cell_b, +LL | | cell_c, +... | +LL | | }, +LL | | ); + | |_____^ argument requires that `'b` must outlive `'c` + | + = help: consider adding the following bound: `'b: 'c` + = note: requirement occurs because of the type `Cell<&'?10 u32>`, which makes the generic argument `&'?10 u32` invariant + = note: the struct `Cell` is invariant over the parameter `T` + = help: see for more information about variance + +help: the following changes may resolve your lifetime errors + | + = help: add bound `'a: 'c` + = help: add bound `'b: 'c` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr deleted file mode 100644 index 134ce99014d8..000000000000 --- a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ /dev/null @@ -1,42 +0,0 @@ -note: no external requirements - --> $DIR/propagate-approximated-fail-no-postdom.rs:43:9 - | -LL | |_outlives1, _outlives2, _outlives3, x, y| { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: defining type: supply::{closure#0} with closure args [ - i16, - for extern "rust-call" fn((std::cell::Cell<&'?1 &'^0 u32>, std::cell::Cell<&'?2 &'^0 u32>, std::cell::Cell<&'^1 &'?3 u32>, std::cell::Cell<&'^0 u32>, std::cell::Cell<&'^1 u32>)), - (), - ] - = note: late-bound region is '?7 - = note: late-bound region is '?8 - = note: late-bound region is '?4 - = note: late-bound region is '?5 - = note: late-bound region is '?6 - -error: lifetime may not live long enough - --> $DIR/propagate-approximated-fail-no-postdom.rs:46:13 - | -LL | |_outlives1, _outlives2, _outlives3, x, y| { - | ---------- ---------- has type `Cell<&'2 &'?3 u32>` - | | - | has type `Cell<&'?1 &'1 u32>` -... -LL | demand_y(x, y, p) - | ^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` - | - = note: requirement occurs because of the type `Cell<&'?34 u32>`, which makes the generic argument `&'?34 u32` invariant - = note: the struct `Cell` is invariant over the parameter `T` - = help: see for more information about variance - -note: no external requirements - --> $DIR/propagate-approximated-fail-no-postdom.rs:38:1 - | -LL | fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: defining type: supply - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/issue-38987.rs b/tests/ui/numbers-arithmetic/i128-min-literal-parses.rs similarity index 54% rename from tests/ui/issues/issue-38987.rs rename to tests/ui/numbers-arithmetic/i128-min-literal-parses.rs index 713fd5027918..623a8a92ae62 100644 --- a/tests/ui/issues/issue-38987.rs +++ b/tests/ui/numbers-arithmetic/i128-min-literal-parses.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass fn main() { let _ = -0x8000_0000_0000_0000_0000_0000_0000_0000i128; diff --git a/tests/ui/panics/panic-in-cleanup.rs b/tests/ui/panics/panic-in-cleanup.rs index 2e307de43939..23430d1f496e 100644 --- a/tests/ui/panics/panic-in-cleanup.rs +++ b/tests/ui/panics/panic-in-cleanup.rs @@ -5,6 +5,7 @@ //@ normalize-stderr: "\n +[0-9]+:[^\n]+" -> "" //@ normalize-stderr: "\n +at [^\n]+" -> "" //@ normalize-stderr: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ needs-unwind //@ ignore-emscripten "RuntimeError" junk in output //@ ignore-msvc SEH doesn't do panic-during-cleanup the same way as everyone else diff --git a/tests/ui/panics/panic-in-cleanup.run.stderr b/tests/ui/panics/panic-in-cleanup.run.stderr index bfe3dc8c9d73..b6b396d8617d 100644 --- a/tests/ui/panics/panic-in-cleanup.run.stderr +++ b/tests/ui/panics/panic-in-cleanup.run.stderr @@ -1,9 +1,9 @@ -thread 'main' ($TID) panicked at $DIR/panic-in-cleanup.rs:22:5: +thread 'main' ($TID) panicked at $DIR/panic-in-cleanup.rs:23:5: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace -thread 'main' ($TID) panicked at $DIR/panic-in-cleanup.rs:16:9: +thread 'main' ($TID) panicked at $DIR/panic-in-cleanup.rs:17:9: BOOM stack backtrace: diff --git a/tests/ui/panics/panic-in-ffi.rs b/tests/ui/panics/panic-in-ffi.rs index b926d0fa7761..ef40a3b6f7d6 100644 --- a/tests/ui/panics/panic-in-ffi.rs +++ b/tests/ui/panics/panic-in-ffi.rs @@ -6,6 +6,7 @@ //@ normalize-stderr: "\n +[0-9]+:[^\n]+" -> "" //@ normalize-stderr: "\n +at [^\n]+" -> "" //@ normalize-stderr: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ needs-unwind //@ ignore-emscripten "RuntimeError" junk in output diff --git a/tests/ui/panics/panic-in-ffi.run.stderr b/tests/ui/panics/panic-in-ffi.run.stderr index ce907d64358f..49eb92ef7c91 100644 --- a/tests/ui/panics/panic-in-ffi.run.stderr +++ b/tests/ui/panics/panic-in-ffi.run.stderr @@ -1,5 +1,5 @@ -thread 'main' ($TID) panicked at $DIR/panic-in-ffi.rs:21:5: +thread 'main' ($TID) panicked at $DIR/panic-in-ffi.rs:22:5: Test note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Noisy Drop diff --git a/tests/ui/issues/issue-22644.rs b/tests/ui/parser/cast-angle-bracket-precedence.rs similarity index 92% rename from tests/ui/issues/issue-22644.rs rename to tests/ui/parser/cast-angle-bracket-precedence.rs index e3ada65049d5..65b598d03bde 100644 --- a/tests/ui/issues/issue-22644.rs +++ b/tests/ui/parser/cast-angle-bracket-precedence.rs @@ -1,3 +1,5 @@ +//! regression test for https://github.com/rust-lang/rust/issues/22644 + fn main() { let a: usize = 0; let long_name: usize = 0; diff --git a/tests/ui/issues/issue-22644.stderr b/tests/ui/parser/cast-angle-bracket-precedence.stderr similarity index 89% rename from tests/ui/issues/issue-22644.stderr rename to tests/ui/parser/cast-angle-bracket-precedence.stderr index c6d41cc856dd..975bfea9425a 100644 --- a/tests/ui/issues/issue-22644.stderr +++ b/tests/ui/parser/cast-angle-bracket-precedence.stderr @@ -1,5 +1,5 @@ error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:6:31 + --> $DIR/cast-angle-bracket-precedence.rs:8:31 | LL | println!("{}", a as usize < long_name); | ^ --------- interpreted as generic arguments @@ -12,7 +12,7 @@ LL | println!("{}", (a as usize) < long_name); | + + error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:7:33 + --> $DIR/cast-angle-bracket-precedence.rs:9:33 | LL | println!("{}{}", a as usize < long_name, long_name); | ^ -------------------- interpreted as generic arguments @@ -25,7 +25,7 @@ LL | println!("{}{}", (a as usize) < long_name, long_name); | + + error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:9:31 + --> $DIR/cast-angle-bracket-precedence.rs:11:31 | LL | println!("{}", a as usize < 4); | ^ - interpreted as generic arguments @@ -38,7 +38,7 @@ LL | println!("{}", (a as usize) < 4); | + + error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:14:20 + --> $DIR/cast-angle-bracket-precedence.rs:16:20 | LL | < | ^ not interpreted as comparison @@ -53,7 +53,7 @@ LL ~ usize) | error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison - --> $DIR/issue-22644.rs:23:20 + --> $DIR/cast-angle-bracket-precedence.rs:25:20 | LL | < | ^ not interpreted as comparison @@ -70,7 +70,7 @@ LL ~ usize) | error: `<<` is interpreted as a start of generic arguments for `usize`, not a shift - --> $DIR/issue-22644.rs:26:31 + --> $DIR/cast-angle-bracket-precedence.rs:28:31 | LL | println!("{}", a as usize << long_name); | ^^ --------- interpreted as generic arguments diff --git a/tests/ui/issues/issue-18352.rs b/tests/ui/pattern/const-pattern-str-match-lifetime.rs similarity index 65% rename from tests/ui/issues/issue-18352.rs rename to tests/ui/pattern/const-pattern-str-match-lifetime.rs index 8b6aa82ea8c0..8814d55c63b6 100644 --- a/tests/ui/issues/issue-18352.rs +++ b/tests/ui/pattern/const-pattern-str-match-lifetime.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass const X: &'static str = "12345"; @@ -5,7 +6,7 @@ const X: &'static str = "12345"; fn test(s: String) -> bool { match &*s { X => true, - _ => false + _ => false, } } diff --git a/tests/ui/issues/issue-27033.rs b/tests/ui/pattern/match-at-pattern-shadows-name.rs similarity index 58% rename from tests/ui/issues/issue-27033.rs rename to tests/ui/pattern/match-at-pattern-shadows-name.rs index a23819a20f9a..3cd2040c9faf 100644 --- a/tests/ui/issues/issue-27033.rs +++ b/tests/ui/pattern/match-at-pattern-shadows-name.rs @@ -1,10 +1,12 @@ +//! regression test for https://github.com/rust-lang/rust/issues/27033 fn main() { match Some(1) { None @ _ => {} //~ ERROR match bindings cannot shadow unit variants }; const C: u8 = 1; match 1 { - C @ 2 => { //~ ERROR match bindings cannot shadow constant + C @ 2 => { + //~^ ERROR match bindings cannot shadow constant println!("{}", C); } _ => {} diff --git a/tests/ui/issues/issue-27033.stderr b/tests/ui/pattern/match-at-pattern-shadows-name.stderr similarity index 86% rename from tests/ui/issues/issue-27033.stderr rename to tests/ui/pattern/match-at-pattern-shadows-name.stderr index 129870f8c409..6e2fbf546e82 100644 --- a/tests/ui/issues/issue-27033.stderr +++ b/tests/ui/pattern/match-at-pattern-shadows-name.stderr @@ -1,5 +1,5 @@ error[E0530]: match bindings cannot shadow unit variants - --> $DIR/issue-27033.rs:3:9 + --> $DIR/match-at-pattern-shadows-name.rs:4:9 | LL | None @ _ => {} | ^^^^ cannot be named the same as a unit variant @@ -9,7 +9,7 @@ LL | None @ _ => {} = note: the unit variant `None` is defined here error[E0530]: match bindings cannot shadow constants - --> $DIR/issue-27033.rs:7:9 + --> $DIR/match-at-pattern-shadows-name.rs:8:9 | LL | const C: u8 = 1; | ---------------- the constant `C` is defined here diff --git a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr index 37b2d96bb019..378ff04d3a1a 100644 --- a/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr +++ b/tests/ui/pattern/range-pattern-meant-to-be-slice-rest-pattern.stderr @@ -16,10 +16,16 @@ error[E0425]: cannot find value `rest` in this scope LL | [1, rest..] => println!("{rest}"), | ^^^^ not found in this scope | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern help: if you meant to collect the rest of the slice in `rest`, use the at operator | LL | [1, rest @ ..] => println!("{rest}"), | + +help: if you meant to destructure a range use a struct pattern + | +LL - [1, rest..] => println!("{rest}"), +LL + [1, std::ops::RangeFrom { start: rest }] => println!("{rest}"), + | error[E0425]: cannot find value `rest` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:3:35 @@ -33,11 +39,17 @@ error[E0425]: cannot find value `tail` in this scope LL | [_, ..tail] => println!("{tail}"), | ^^^^ not found in this scope | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern help: if you meant to collect the rest of the slice in `tail`, use the at operator | LL - [_, ..tail] => println!("{tail}"), LL + [_, tail @ ..] => println!("{tail}"), | +help: if you meant to destructure a range use a struct pattern + | +LL - [_, ..tail] => println!("{tail}"), +LL + [_, std::ops::RangeTo { end: tail }] => println!("{tail}"), + | error[E0425]: cannot find value `tail` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:11:35 @@ -51,11 +63,17 @@ error[E0425]: cannot find value `tail` in this scope LL | [_, ...tail] => println!("{tail}"), | ^^^^ not found in this scope | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern help: if you meant to collect the rest of the slice in `tail`, use the at operator | LL - [_, ...tail] => println!("{tail}"), LL + [_, tail @ ..] => println!("{tail}"), | +help: if you meant to destructure a range use a struct pattern + | +LL - [_, ...tail] => println!("{tail}"), +LL + [_, std::ops::RangeToInclusive { end: tail }] => println!("{tail}"), + | error[E0425]: cannot find value `tail` in this scope --> $DIR/range-pattern-meant-to-be-slice-rest-pattern.rs:17:36 diff --git a/tests/ui/proc-macro/attribute.stderr b/tests/ui/proc-macro/attribute.stderr index e7127c8ef1d2..24962cf270a4 100644 --- a/tests/ui/proc-macro/attribute.stderr +++ b/tests/ui/proc-macro/attribute.stderr @@ -16,7 +16,9 @@ error[E0539]: malformed `proc_macro_derive` attribute input --> $DIR/attribute.rs:15:1 | LL | #[proc_macro_derive = ""] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^^^^^^^^^^^----^ + | | + | expected this to be a list | = note: for more information, visit help: try changing it to one of the following valid forms of the attribute diff --git a/tests/ui/process/println-with-broken-pipe.rs b/tests/ui/process/println-with-broken-pipe.rs index e87e20773702..51521f1dcf42 100644 --- a/tests/ui/process/println-with-broken-pipe.rs +++ b/tests/ui/process/println-with-broken-pipe.rs @@ -11,6 +11,7 @@ //@ ignore-visionos no 'head' //@ ignore-backends: gcc //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ compile-flags: -Zon-broken-pipe=error // Test what the error message looks like when `println!()` panics because of diff --git a/tests/ui/regions/closure-prop-issue-104477-case1.rs b/tests/ui/regions/closure-prop-issue-104477-case1.rs new file mode 100644 index 000000000000..307ca930baea --- /dev/null +++ b/tests/ui/regions/closure-prop-issue-104477-case1.rs @@ -0,0 +1,14 @@ +//@ check-pass +// This checks that the compiler does not require that 'a: 'b. '_ has 'a and 'b as non-local +// upper bounds, but the compiler should not propagate 'a: 'b OR 'b: 'a when checking +// the closures. If it did, this would fail to compile, eventhough it's a valid program. +// PR #148329 explains this in detail. + +struct MyTy<'x, 'a, 'b>(std::cell::Cell<(&'x &'a u8, &'x &'b u8)>); +fn wf(_: T) {} +fn test<'a, 'b>() { + |_: &'a u8, x: MyTy<'_, 'a, 'b>| wf(x); + |x: MyTy<'_, 'a, 'b>, _: &'a u8| wf(x); +} + +fn main(){} diff --git a/tests/ui/regions/closure-prop-issue-104477-case2.rs b/tests/ui/regions/closure-prop-issue-104477-case2.rs new file mode 100644 index 000000000000..a77a11d0eceb --- /dev/null +++ b/tests/ui/regions/closure-prop-issue-104477-case2.rs @@ -0,0 +1,12 @@ +//@ check-pass +// This test checks that the compiler propagates outlives requirements for both +// non-local lower bounds ['a, 'b] of '_, instead of conservatively finding a post-dominiting one +// from those 2. + +struct MyTy<'a, 'b, 'x>(std::cell::Cell<(&'a &'x str, &'b &'x str)>); +fn wf(_: T) {} +fn test<'a, 'b, 'x>() { + |x: MyTy<'a, 'b, '_>| wf(x); +} + +fn main() {} diff --git a/tests/ui/regions/closure-prop-issue-148289.rs b/tests/ui/regions/closure-prop-issue-148289.rs new file mode 100644 index 000000000000..7f89927a76e0 --- /dev/null +++ b/tests/ui/regions/closure-prop-issue-148289.rs @@ -0,0 +1,26 @@ +//@ check-pass +// This test checks that the compiler does not propagate 'd: 'c when propagating region errors +// for the closure argument. If it did, this would fail to compile, eventhough it's a valid program. +// It should only propagate 'd: 'b. +// PR #148329 explains this in detail. + +#[derive(Clone, Copy)] +struct Inv<'a>(*mut &'a ()); +impl<'a> Inv<'a> { + fn outlived_by<'b: 'a>(self, _: Inv<'b>) {} +} +struct OutlivedBy<'a, 'b: 'a>(Inv<'a>, Inv<'b>); + +fn closure_arg<'b, 'c, 'd>( + _: impl for<'a> FnOnce(Inv<'a>, OutlivedBy<'a, 'b>, OutlivedBy<'a, 'c>, Inv<'d>), +) { +} +fn foo<'b, 'c, 'd: 'b>() { + closure_arg::<'b, 'c, 'd>(|a, b, c, d| { + a.outlived_by(b.1); + a.outlived_by(c.1); + b.1.outlived_by(d); + }); +} + +fn main() {} diff --git a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs index a6bd5b299c96..02cd9f4a3b29 100644 --- a/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs +++ b/tests/ui/repeat-expr/copy-check-deferred-after-fallback.rs @@ -1,6 +1,6 @@ // Test when deferring repeat expr copy checks to end of typechecking whether they're // checked before integer fallback occurs or not. We accomplish this by having a repeat -// count that can only be inferred after integer fallback has occured. This test will +// count that can only be inferred after integer fallback has occurred. This test will // pass if we were to check repeat exprs after integer fallback. use std::marker::PhantomData; diff --git a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr index 1a1929c530ff..62f6ec92d493 100644 --- a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr @@ -16,7 +16,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -60,7 +60,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -108,7 +108,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -158,7 +158,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -195,7 +195,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -231,7 +231,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -270,7 +270,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Size(8 bytes), Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, 1, ], @@ -298,7 +298,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr index 67729e9fcef9..3e0efad974cd 100644 --- a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr +++ b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr @@ -16,7 +16,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -60,7 +60,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -108,7 +108,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -158,7 +158,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -195,7 +195,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -231,7 +231,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -270,7 +270,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Size(8 bytes), Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, 1, ], @@ -298,7 +298,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr index 1a1929c530ff..62f6ec92d493 100644 --- a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr +++ b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr @@ -16,7 +16,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -60,7 +60,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -108,7 +108,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -158,7 +158,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -195,7 +195,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -231,7 +231,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -270,7 +270,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Size(8 bytes), Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, 1, ], @@ -298,7 +298,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr index 1a1929c530ff..62f6ec92d493 100644 --- a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr @@ -16,7 +16,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -60,7 +60,7 @@ error: layout_of(Univariant) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -108,7 +108,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -158,7 +158,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -195,7 +195,7 @@ error: layout_of(TwoVariants) = Layout { offsets: [ Size(4 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -231,7 +231,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -270,7 +270,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { Size(8 bytes), Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, 1, ], @@ -298,7 +298,7 @@ error: layout_of(DeadBranchHasOtherField) = Layout { offsets: [ Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/repr/repr-c-int-dead-variants.stderr b/tests/ui/repr/repr-c-int-dead-variants.stderr index d88a842f8848..b25e4a9b7b6f 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.stderr +++ b/tests/ui/repr/repr-c-int-dead-variants.stderr @@ -16,7 +16,7 @@ error: layout_of(UnivariantU8) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -60,7 +60,7 @@ error: layout_of(UnivariantU8) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -108,7 +108,7 @@ error: layout_of(TwoVariantsU8) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -158,7 +158,7 @@ error: layout_of(TwoVariantsU8) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -195,7 +195,7 @@ error: layout_of(TwoVariantsU8) = Layout { offsets: [ Size(1 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -231,7 +231,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -270,7 +270,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { Size(8 bytes), Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, 1, ], @@ -298,7 +298,7 @@ error: layout_of(DeadBranchHasOtherFieldU8) = Layout { offsets: [ Size(8 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/repr/repr.stderr b/tests/ui/repr/repr.stderr index e8168f8f9a58..a842590c9639 100644 --- a/tests/ui/repr/repr.stderr +++ b/tests/ui/repr/repr.stderr @@ -10,7 +10,9 @@ error[E0539]: malformed `repr` attribute input --> $DIR/repr.rs:4:1 | LL | #[repr = "B"] - | ^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^-----^ + | | + | expected this to be a list | = note: for more information, visit @@ -18,7 +20,9 @@ error[E0539]: malformed `repr` attribute input --> $DIR/repr.rs:7:1 | LL | #[repr = "C"] - | ^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^-----^ + | | + | expected this to be a list | = note: for more information, visit diff --git a/tests/ui/resolve/module-used-as-struct-constructor.rs b/tests/ui/resolve/module-used-as-struct-constructor.rs new file mode 100644 index 000000000000..bdc7b2bf5841 --- /dev/null +++ b/tests/ui/resolve/module-used-as-struct-constructor.rs @@ -0,0 +1,6 @@ +//! regression test for https://github.com/rust-lang/rust/issues/17001, https://github.com/rust-lang/rust/issues/21449, https://github.com/rust-lang/rust/issues/23189 +mod foo {} + +fn main() { + let p = foo { x: () }; //~ ERROR expected struct, variant or union type, found module `foo` +} diff --git a/tests/ui/issues/issue-17001.stderr b/tests/ui/resolve/module-used-as-struct-constructor.stderr similarity index 83% rename from tests/ui/issues/issue-17001.stderr rename to tests/ui/resolve/module-used-as-struct-constructor.stderr index 6ea32e0a45c3..b94a28762a7f 100644 --- a/tests/ui/issues/issue-17001.stderr +++ b/tests/ui/resolve/module-used-as-struct-constructor.stderr @@ -1,5 +1,5 @@ error[E0574]: expected struct, variant or union type, found module `foo` - --> $DIR/issue-17001.rs:4:13 + --> $DIR/module-used-as-struct-constructor.rs:5:13 | LL | let p = foo { x: () }; | ^^^ not a struct, variant or union type diff --git a/tests/ui/resolve/multiple_definitions_attribute_merging.rs b/tests/ui/resolve/multiple_definitions_attribute_merging.rs index 519b989fbe87..9f1bff51a3d4 100644 --- a/tests/ui/resolve/multiple_definitions_attribute_merging.rs +++ b/tests/ui/resolve/multiple_definitions_attribute_merging.rs @@ -7,6 +7,7 @@ //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc'.*panicked.*\n" -> "" //@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ rustc-env:RUST_BACKTRACE=0 #[repr(packed)] diff --git a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr index 10ce68e506e6..b8b33e3417bf 100644 --- a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr +++ b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `Dealigned` is defined multiple times - --> $DIR/multiple_definitions_attribute_merging.rs:17:1 + --> $DIR/multiple_definitions_attribute_merging.rs:18:1 | LL | struct Dealigned(u8, T); | --------------------------- previous definition of the type `Dealigned` here @@ -8,7 +8,7 @@ LL | struct Dealigned(u8, T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Dealigned` redefined here | = error: internal compiler error: compiler/rustc_mir_transform/src/check_packed_ref.rs:LL:CC: builtin derive created an unaligned reference - --> $DIR/multiple_definitions_attribute_merging.rs:17:25 + --> $DIR/multiple_definitions_attribute_merging.rs:18:25 | LL | #[derive(PartialEq)] | --------- in this derive macro expansion @@ -19,8 +19,8 @@ LL | struct Dealigned(u8, T); Box query stack during panic: -#0 [mir_built] building MIR for `::eq` -#1 [check_unsafety] unsafety-checking `::eq` +#0 [mir_built] building MIR for `::eq` +#1 [check_unsafety] unsafety-checking `::eq` ... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/proc_macro_generated_packed.rs b/tests/ui/resolve/proc_macro_generated_packed.rs index 41236c732bbd..a8175895af04 100644 --- a/tests/ui/resolve/proc_macro_generated_packed.rs +++ b/tests/ui/resolve/proc_macro_generated_packed.rs @@ -7,6 +7,7 @@ //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc'.*panicked.*\n" -> "" //@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ rustc-env:RUST_BACKTRACE=0 extern crate proc_macro_generate_packed; diff --git a/tests/ui/resolve/proc_macro_generated_packed.stderr b/tests/ui/resolve/proc_macro_generated_packed.stderr index 47de5be82ddf..d8e160d0c6a0 100644 --- a/tests/ui/resolve/proc_macro_generated_packed.stderr +++ b/tests/ui/resolve/proc_macro_generated_packed.stderr @@ -1,5 +1,5 @@ error: internal compiler error: compiler/rustc_mir_transform/src/check_packed_ref.rs:LL:CC: builtin derive created an unaligned reference - --> $DIR/proc_macro_generated_packed.rs:18:25 + --> $DIR/proc_macro_generated_packed.rs:19:25 | LL | #[derive(PartialEq)] | --------- in this derive macro expansion @@ -10,8 +10,8 @@ LL | struct Dealigned(u8, T); Box query stack during panic: -#0 [mir_built] building MIR for `::eq` -#1 [check_unsafety] unsafety-checking `::eq` +#0 [mir_built] building MIR for `::eq` +#1 [check_unsafety] unsafety-checking `::eq` ... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 1 previous error diff --git a/tests/ui/resolve/suggest-range-struct-destructuring.rs b/tests/ui/resolve/suggest-range-struct-destructuring.rs new file mode 100644 index 000000000000..ee8a99ceaa1a --- /dev/null +++ b/tests/ui/resolve/suggest-range-struct-destructuring.rs @@ -0,0 +1,40 @@ +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive}; + +fn test_range(r: Range) { + let start..end = r; + //~^ ERROR cannot find value `start` + //~| ERROR cannot find value `end` +} + +fn test_inclusive(r: RangeInclusive) { + let start..=end = r; + //~^ ERROR cannot find value `start` + //~| ERROR cannot find value `end` +} + +fn test_from(r: RangeFrom) { + let start.. = r; + //~^ ERROR cannot find value `start` +} + +fn test_to(r: RangeTo) { + let ..end = r; + //~^ ERROR cannot find value `end` +} + +fn test_to_inclusive(r: RangeToInclusive) { + let ..=end = r; + //~^ ERROR cannot find value `end` +} + +// Case 6: Complex Path (Keep this! It works!) +mod my { + // We don't define MISSING here to trigger the error +} +fn test_path(r: Range) { + let my::MISSING..end = r; + //~^ ERROR cannot find value `MISSING` + //~| ERROR cannot find value `end` +} + +fn main() {} diff --git a/tests/ui/resolve/suggest-range-struct-destructuring.stderr b/tests/ui/resolve/suggest-range-struct-destructuring.stderr new file mode 100644 index 000000000000..78a248ed777d --- /dev/null +++ b/tests/ui/resolve/suggest-range-struct-destructuring.stderr @@ -0,0 +1,127 @@ +error[E0425]: cannot find value `start` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:4:9 + | +LL | let start..end = r; + | ^^^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to destructure a range use a struct pattern + | +LL - let start..end = r; +LL + let Range { start, end } = r; + | + +error[E0425]: cannot find value `end` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:4:16 + | +LL | let start..end = r; + | ^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to destructure a range use a struct pattern + | +LL - let start..end = r; +LL + let Range { start, end } = r; + | + +error[E0425]: cannot find value `start` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:10:9 + | +LL | let start..=end = r; + | ^^^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to destructure a range use a struct pattern + | +LL - let start..=end = r; +LL + let RangeInclusive { start, end } = r; + | + +error[E0425]: cannot find value `end` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:10:17 + | +LL | let start..=end = r; + | ^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to destructure a range use a struct pattern + | +LL - let start..=end = r; +LL + let RangeInclusive { start, end } = r; + | + +error[E0425]: cannot find value `start` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:16:9 + | +LL | let start.. = r; + | ^^^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to collect the rest of the slice in `start`, use the at operator + | +LL | let start @ .. = r; + | + +help: if you meant to destructure a range use a struct pattern + | +LL - let start.. = r; +LL + let RangeFrom { start } = r; + | + +error[E0425]: cannot find value `end` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:21:11 + | +LL | let ..end = r; + | ^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to collect the rest of the slice in `end`, use the at operator + | +LL - let ..end = r; +LL + let end @ .. = r; + | +help: if you meant to destructure a range use a struct pattern + | +LL - let ..end = r; +LL + let RangeTo { end } = r; + | + +error[E0425]: cannot find value `end` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:26:12 + | +LL | let ..=end = r; + | ^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to collect the rest of the slice in `end`, use the at operator + | +LL - let ..=end = r; +LL + let end @ .. = r; + | +help: if you meant to destructure a range use a struct pattern + | +LL - let ..=end = r; +LL + let RangeToInclusive { end } = r; + | + +error[E0425]: cannot find value `MISSING` in module `my` + --> $DIR/suggest-range-struct-destructuring.rs:35:13 + | +LL | let my::MISSING..end = r; + | ^^^^^^^ not found in `my` + +error[E0425]: cannot find value `end` in this scope + --> $DIR/suggest-range-struct-destructuring.rs:35:22 + | +LL | let my::MISSING..end = r; + | ^^^ not found in this scope + | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern +help: if you meant to destructure a range use a struct pattern + | +LL - let my::MISSING..end = r; +LL + let Range { start: my::MISSING, end } = r; + | + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/sanitize-attr/invalid-sanitize.stderr b/tests/ui/sanitize-attr/invalid-sanitize.stderr index 2a3497678bdc..26ef31603d88 100644 --- a/tests/ui/sanitize-attr/invalid-sanitize.stderr +++ b/tests/ui/sanitize-attr/invalid-sanitize.stderr @@ -42,7 +42,9 @@ error[E0539]: malformed `sanitize` attribute input --> $DIR/invalid-sanitize.rs:18:1 | LL | #[sanitize = "off"] - | ^^^^^^^^^^^^^^^^^^^ expected this to be a list + | ^^^^^^^^^^^-------^ + | | + | expected this to be a list error[E0539]: malformed `sanitize` attribute input --> $DIR/invalid-sanitize.rs:21:1 diff --git a/tests/ui/scalable-vectors/async.rs b/tests/ui/scalable-vectors/async.rs new file mode 100644 index 000000000000..916c64ee7435 --- /dev/null +++ b/tests/ui/scalable-vectors/async.rs @@ -0,0 +1,44 @@ +//@ only-aarch64 +//@ edition:2021 + +#![allow(incomplete_features, internal_features)] +#![feature( + core_intrinsics, + simd_ffi, + rustc_attrs, + link_llvm_intrinsics +)] + +use core::intrinsics::transmute_unchecked; + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svint32_t(i32); + +#[target_feature(enable = "sve")] +pub unsafe fn svdup_n_s32(op: i32) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4i32")] + fn _svdup_n_s32(op: i32) -> svint32_t; + } + unsafe { _svdup_n_s32(op) } +} + +#[target_feature(enable = "sve")] +async fn another() -> i32 { + 42 +} + +#[no_mangle] +#[target_feature(enable = "sve")] +pub async fn test_function() { + unsafe { + let x = svdup_n_s32(1); //~ ERROR: scalable vectors cannot be held over await points + let temp = another().await; + let y: svint32_t = transmute_unchecked(x); + } +} + +fn main() { + let _ = unsafe { test_function() }; +} diff --git a/tests/ui/scalable-vectors/async.stderr b/tests/ui/scalable-vectors/async.stderr new file mode 100644 index 000000000000..fa81c7b8ed4d --- /dev/null +++ b/tests/ui/scalable-vectors/async.stderr @@ -0,0 +1,8 @@ +error: scalable vectors cannot be held over await points + --> $DIR/async.rs:36:13 + | +LL | let x = svdup_n_s32(1); + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/scalable-vectors/closure-capture.rs b/tests/ui/scalable-vectors/closure-capture.rs new file mode 100644 index 000000000000..d6a45f76e214 --- /dev/null +++ b/tests/ui/scalable-vectors/closure-capture.rs @@ -0,0 +1,51 @@ +//@ compile-flags: --crate-type=lib +//@ only-aarch64 + +#![allow(incomplete_features, internal_features)] +#![feature( + link_llvm_intrinsics, + rustc_attrs, + simd_ffi +)] + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svint32_t(i32); + +#[inline(never)] +#[target_feature(enable = "sve")] +pub unsafe fn svdup_n_s32(op: i32) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4i32")] + fn _svdup_n_s32(op: i32) -> svint32_t; + } + unsafe { _svdup_n_s32(op) } +} + +#[inline] +#[target_feature(enable = "sve,sve2")] +pub unsafe fn svxar_n_s32(op1: svint32_t, op2: svint32_t) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.xar.nxv4i32")] + fn _svxar_n_s32(op1: svint32_t, op2: svint32_t, imm3: i32) -> svint32_t; + } + unsafe { _svxar_n_s32(op1, op2, IMM3) } +} + +#[inline(never)] +#[target_feature(enable = "sve,sve2")] +fn run(f: impl Fn() -> ()) { + f(); +} + +#[target_feature(enable = "sve,sve2")] +fn foo() { + unsafe { + let a = svdup_n_s32(42); + run(move || { +//~^ ERROR: scalable vectors cannot be tuple fields + svxar_n_s32::<2>(a, a); + }); + } +} diff --git a/tests/ui/scalable-vectors/closure-capture.stderr b/tests/ui/scalable-vectors/closure-capture.stderr new file mode 100644 index 000000000000..ea53066988e5 --- /dev/null +++ b/tests/ui/scalable-vectors/closure-capture.stderr @@ -0,0 +1,8 @@ +error: scalable vectors cannot be tuple fields + --> $DIR/closure-capture.rs:46:9 + | +LL | run(move || { + | ^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/scalable-vectors/copy-clone.rs b/tests/ui/scalable-vectors/copy-clone.rs new file mode 100644 index 000000000000..7576b6ec18dc --- /dev/null +++ b/tests/ui/scalable-vectors/copy-clone.rs @@ -0,0 +1,31 @@ +//@ build-pass +//@ only-aarch64 +#![feature(simd_ffi, rustc_attrs, link_llvm_intrinsics)] + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svint32_t(i32); + +#[target_feature(enable = "sve")] +pub unsafe fn svdup_n_s32(op: i32) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4i32")] + fn _svdup_n_s32(op: i32) -> svint32_t; +//~^ WARN: `extern` block uses type `svint32_t`, which is not FFI-safe + } + unsafe { _svdup_n_s32(op) } +} + +#[target_feature(enable = "sve")] +fn require_copy(t: T) {} + +#[target_feature(enable = "sve")] +fn test() { + unsafe { + let a = svdup_n_s32(1); + require_copy(a); + } +} + +fn main() {} diff --git a/tests/ui/scalable-vectors/copy-clone.stderr b/tests/ui/scalable-vectors/copy-clone.stderr new file mode 100644 index 000000000000..8b07aba8e1c0 --- /dev/null +++ b/tests/ui/scalable-vectors/copy-clone.stderr @@ -0,0 +1,17 @@ +warning: `extern` block uses type `svint32_t`, which is not FFI-safe + --> $DIR/copy-clone.rs:14:37 + | +LL | fn _svdup_n_s32(op: i32) -> svint32_t; + | ^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/copy-clone.rs:8:1 + | +LL | pub struct svint32_t(i32); + | ^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(improper_ctypes)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/scalable-vectors/fn-trait.rs b/tests/ui/scalable-vectors/fn-trait.rs new file mode 100644 index 000000000000..5203b5fa0efd --- /dev/null +++ b/tests/ui/scalable-vectors/fn-trait.rs @@ -0,0 +1,13 @@ +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_scalable_vector(4)] +pub struct ScalableSimdFloat(f32); + +unsafe fn test(f: T) +where + T: Fn(ScalableSimdFloat), //~ ERROR: scalable vectors cannot be tuple fields +{ +} + +fn main() {} diff --git a/tests/ui/scalable-vectors/fn-trait.stderr b/tests/ui/scalable-vectors/fn-trait.stderr new file mode 100644 index 000000000000..4d00272dd1b5 --- /dev/null +++ b/tests/ui/scalable-vectors/fn-trait.stderr @@ -0,0 +1,8 @@ +error: scalable vectors cannot be tuple fields + --> $DIR/fn-trait.rs:9:8 + | +LL | T: Fn(ScalableSimdFloat), + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/scalable-vectors/illformed-element-type.rs b/tests/ui/scalable-vectors/illformed-element-type.rs new file mode 100644 index 000000000000..469ca006f5e9 --- /dev/null +++ b/tests/ui/scalable-vectors/illformed-element-type.rs @@ -0,0 +1,93 @@ +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(extern_types)] +#![feature(never_type)] +#![feature(rustc_attrs)] + +struct Foo; +enum Bar {} +union Baz { x: u16 } +extern "C" { + type Qux; +} + +#[rustc_scalable_vector(4)] +struct TyChar(char); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(2)] +struct TyConstPtr(*const u8); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(2)] +struct TyMutPtr(*mut u8); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyStruct(Foo); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyEnum(Bar); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyUnion(Baz); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyForeign(Qux); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyArray([u32; 4]); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TySlice([u32]); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyRef<'a>(&'a u32); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyFnPtr(fn(u32) -> u32); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyDyn(dyn std::io::Write); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyNever(!); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +#[rustc_scalable_vector(4)] +struct TyTuple((u32, u32)); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +type ValidAlias = u32; +type InvalidAlias = String; + +#[rustc_scalable_vector(4)] +struct TyValidAlias(ValidAlias); + +#[rustc_scalable_vector(4)] +struct TyInvalidAlias(InvalidAlias); +//~^ ERROR: element type of a scalable vector must be a primitive scalar + +trait Tr { + type Valid; + type Invalid; +} + +impl Tr for () { + type Valid = u32; + type Invalid = String; +} + +struct TyValidProjection(<() as Tr>::Valid); + +struct TyInvalidProjection(<() as Tr>::Invalid); +// FIXME: element type of a scalable vector must be a primitive scalar diff --git a/tests/ui/scalable-vectors/illformed-element-type.stderr b/tests/ui/scalable-vectors/illformed-element-type.stderr new file mode 100644 index 000000000000..f8ca8b76215f --- /dev/null +++ b/tests/ui/scalable-vectors/illformed-element-type.stderr @@ -0,0 +1,122 @@ +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:15:1 + | +LL | struct TyChar(char); + | ^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:19:1 + | +LL | struct TyConstPtr(*const u8); + | ^^^^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:23:1 + | +LL | struct TyMutPtr(*mut u8); + | ^^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:27:1 + | +LL | struct TyStruct(Foo); + | ^^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:31:1 + | +LL | struct TyEnum(Bar); + | ^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:35:1 + | +LL | struct TyUnion(Baz); + | ^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:39:1 + | +LL | struct TyForeign(Qux); + | ^^^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:43:1 + | +LL | struct TyArray([u32; 4]); + | ^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:47:1 + | +LL | struct TySlice([u32]); + | ^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:51:1 + | +LL | struct TyRef<'a>(&'a u32); + | ^^^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:55:1 + | +LL | struct TyFnPtr(fn(u32) -> u32); + | ^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:59:1 + | +LL | struct TyDyn(dyn std::io::Write); + | ^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:63:1 + | +LL | struct TyNever(!); + | ^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:67:1 + | +LL | struct TyTuple((u32, u32)); + | ^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: element type of a scalable vector must be a primitive scalar + --> $DIR/illformed-element-type.rs:77:1 + | +LL | struct TyInvalidAlias(InvalidAlias); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: only `u*`, `i*`, `f*` and `bool` types are accepted + +error: aborting due to 15 previous errors + diff --git a/tests/ui/scalable-vectors/illformed-tuples-of-scalable-vectors.rs b/tests/ui/scalable-vectors/illformed-tuples-of-scalable-vectors.rs new file mode 100644 index 000000000000..4f89a8f9055e --- /dev/null +++ b/tests/ui/scalable-vectors/illformed-tuples-of-scalable-vectors.rs @@ -0,0 +1,36 @@ +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_scalable_vector(2)] +struct ValidI64(i64); + +#[rustc_scalable_vector(4)] +struct ValidI32(i32); + +#[rustc_scalable_vector] +struct ValidTuple(ValidI32, ValidI32, ValidI32); + +#[rustc_scalable_vector] +struct Struct { x: ValidI64, y: ValidI64 } +//~^ ERROR: scalable vectors must be tuple structs + +#[rustc_scalable_vector] +struct DifferentVectorTypes(ValidI64, ValidI32); +//~^ ERROR: all fields in a scalable vector struct must be the same type + +#[rustc_scalable_vector] +struct NonVectorTypes(u32, u64); +//~^ ERROR: scalable vector structs can only have scalable vector fields + +#[rustc_scalable_vector] +struct DifferentNonVectorTypes(u32, u64); +//~^ ERROR: scalable vector structs can only have scalable vector fields + +#[rustc_scalable_vector] +struct SomeVectorTypes(ValidI64, u64); +//~^ ERROR: scalable vector structs can only have scalable vector fields + +#[rustc_scalable_vector] +struct NestedTuple(ValidTuple, ValidTuple); +//~^ ERROR: scalable vector structs cannot contain other scalable vector structs diff --git a/tests/ui/scalable-vectors/illformed-tuples-of-scalable-vectors.stderr b/tests/ui/scalable-vectors/illformed-tuples-of-scalable-vectors.stderr new file mode 100644 index 000000000000..f5fd963204a2 --- /dev/null +++ b/tests/ui/scalable-vectors/illformed-tuples-of-scalable-vectors.stderr @@ -0,0 +1,38 @@ +error: scalable vectors must be tuple structs + --> $DIR/illformed-tuples-of-scalable-vectors.rs:15:1 + | +LL | struct Struct { x: ValidI64, y: ValidI64 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: all fields in a scalable vector struct must be the same type + --> $DIR/illformed-tuples-of-scalable-vectors.rs:19:39 + | +LL | struct DifferentVectorTypes(ValidI64, ValidI32); + | ^^^^^^^^ + +error: scalable vector structs can only have scalable vector fields + --> $DIR/illformed-tuples-of-scalable-vectors.rs:23:23 + | +LL | struct NonVectorTypes(u32, u64); + | ^^^ + +error: scalable vector structs can only have scalable vector fields + --> $DIR/illformed-tuples-of-scalable-vectors.rs:27:32 + | +LL | struct DifferentNonVectorTypes(u32, u64); + | ^^^ + +error: scalable vector structs can only have scalable vector fields + --> $DIR/illformed-tuples-of-scalable-vectors.rs:31:34 + | +LL | struct SomeVectorTypes(ValidI64, u64); + | ^^^ + +error: scalable vector structs cannot contain other scalable vector structs + --> $DIR/illformed-tuples-of-scalable-vectors.rs:35:20 + | +LL | struct NestedTuple(ValidTuple, ValidTuple); + | ^^^^^^^^^^ + +error: aborting due to 6 previous errors + diff --git a/tests/ui/scalable-vectors/illformed-within-types.rs b/tests/ui/scalable-vectors/illformed-within-types.rs new file mode 100644 index 000000000000..81d960e4d4e1 --- /dev/null +++ b/tests/ui/scalable-vectors/illformed-within-types.rs @@ -0,0 +1,23 @@ +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_scalable_vector(2)] +struct ValidI64(i64); + +struct Struct { + x: ValidI64, +//~^ ERROR: scalable vectors cannot be fields of a struct + in_tuple: (ValidI64,), +//~^ ERROR: scalable vectors cannot be tuple fields +} + +struct TupleStruct(ValidI64); +//~^ ERROR: scalable vectors cannot be fields of a struct + +enum Enum { + StructVariant { _ty: ValidI64 }, +//~^ ERROR: scalable vectors cannot be fields of a variant + TupleVariant(ValidI64), +//~^ ERROR: scalable vectors cannot be fields of a variant +} diff --git a/tests/ui/scalable-vectors/illformed-within-types.stderr b/tests/ui/scalable-vectors/illformed-within-types.stderr new file mode 100644 index 000000000000..e76ef26f2aa4 --- /dev/null +++ b/tests/ui/scalable-vectors/illformed-within-types.stderr @@ -0,0 +1,32 @@ +error: scalable vectors cannot be fields of a struct + --> $DIR/illformed-within-types.rs:9:8 + | +LL | x: ValidI64, + | ^^^^^^^^ + +error: scalable vectors cannot be tuple fields + --> $DIR/illformed-within-types.rs:11:15 + | +LL | in_tuple: (ValidI64,), + | ^^^^^^^^^^^ + +error: scalable vectors cannot be fields of a struct + --> $DIR/illformed-within-types.rs:15:20 + | +LL | struct TupleStruct(ValidI64); + | ^^^^^^^^ + +error: scalable vectors cannot be fields of a variant + --> $DIR/illformed-within-types.rs:19:26 + | +LL | StructVariant { _ty: ValidI64 }, + | ^^^^^^^^ + +error: scalable vectors cannot be fields of a variant + --> $DIR/illformed-within-types.rs:21:18 + | +LL | TupleVariant(ValidI64), + | ^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/scalable-vectors/illformed.rs b/tests/ui/scalable-vectors/illformed.rs new file mode 100644 index 000000000000..de135413a9f2 --- /dev/null +++ b/tests/ui/scalable-vectors/illformed.rs @@ -0,0 +1,59 @@ +//@ compile-flags: --crate-type=lib +#![allow(internal_features)] +#![feature(rustc_attrs)] + +#[rustc_scalable_vector(4)] +struct NoFieldsStructWithElementCount {} +//~^ ERROR: scalable vectors must have a single field +//~^^ ERROR: scalable vectors must be tuple structs + +#[rustc_scalable_vector(4)] +struct NoFieldsTupleWithElementCount(); +//~^ ERROR: scalable vectors must have a single field + +#[rustc_scalable_vector(4)] +struct NoFieldsUnitWithElementCount; +//~^ ERROR: scalable vectors must have a single field +//~^^ ERROR: scalable vectors must be tuple structs + +#[rustc_scalable_vector] +struct NoFieldsStructWithoutElementCount {} +//~^ ERROR: scalable vectors must have a single field +//~^^ ERROR: scalable vectors must be tuple structs + +#[rustc_scalable_vector] +struct NoFieldsTupleWithoutElementCount(); +//~^ ERROR: scalable vectors must have a single field + +#[rustc_scalable_vector] +struct NoFieldsUnitWithoutElementCount; +//~^ ERROR: scalable vectors must have a single field +//~^^ ERROR: scalable vectors must be tuple structs + +#[rustc_scalable_vector(4)] +struct MultipleFieldsStructWithElementCount { +//~^ ERROR: scalable vectors cannot have multiple fields +//~^^ ERROR: scalable vectors must be tuple structs + _ty: f32, + other: u32, +} + +#[rustc_scalable_vector(4)] +struct MultipleFieldsTupleWithElementCount(f32, u32); +//~^ ERROR: scalable vectors cannot have multiple fields + +#[rustc_scalable_vector] +struct MultipleFieldsStructWithoutElementCount { +//~^ ERROR: scalable vectors must be tuple structs + _ty: f32, +//~^ ERROR: scalable vector structs can only have scalable vector fields + other: u32, +} + +#[rustc_scalable_vector] +struct MultipleFieldsTupleWithoutElementCount(f32, u32); +//~^ ERROR: scalable vector structs can only have scalable vector fields + +#[rustc_scalable_vector(2)] +struct SingleFieldStruct { _ty: f64 } +//~^ ERROR: scalable vectors must be tuple structs diff --git a/tests/ui/scalable-vectors/illformed.stderr b/tests/ui/scalable-vectors/illformed.stderr new file mode 100644 index 000000000000..bdf519c91058 --- /dev/null +++ b/tests/ui/scalable-vectors/illformed.stderr @@ -0,0 +1,125 @@ +error: scalable vectors must be tuple structs + --> $DIR/illformed.rs:6:1 + | +LL | struct NoFieldsStructWithElementCount {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: scalable vectors must be tuple structs + --> $DIR/illformed.rs:15:1 + | +LL | struct NoFieldsUnitWithElementCount; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: scalable vectors must be tuple structs + --> $DIR/illformed.rs:20:1 + | +LL | struct NoFieldsStructWithoutElementCount {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: scalable vectors must be tuple structs + --> $DIR/illformed.rs:29:1 + | +LL | struct NoFieldsUnitWithoutElementCount; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: scalable vectors must be tuple structs + --> $DIR/illformed.rs:34:1 + | +LL | / struct MultipleFieldsStructWithElementCount { +LL | | +LL | | +LL | | _ty: f32, +LL | | other: u32, +LL | | } + | |_^ + +error: scalable vectors must be tuple structs + --> $DIR/illformed.rs:46:1 + | +LL | / struct MultipleFieldsStructWithoutElementCount { +LL | | +LL | | _ty: f32, +... | +LL | | } + | |_^ + +error: scalable vectors must be tuple structs + --> $DIR/illformed.rs:58:1 + | +LL | struct SingleFieldStruct { _ty: f64 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: scalable vectors must have a single field + --> $DIR/illformed.rs:6:1 + | +LL | struct NoFieldsStructWithElementCount {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: scalable vector types' only field must be a primitive scalar type + +error: scalable vectors must have a single field + --> $DIR/illformed.rs:11:1 + | +LL | struct NoFieldsTupleWithElementCount(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: scalable vector types' only field must be a primitive scalar type + +error: scalable vectors must have a single field + --> $DIR/illformed.rs:15:1 + | +LL | struct NoFieldsUnitWithElementCount; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: scalable vector types' only field must be a primitive scalar type + +error: scalable vectors must have a single field + --> $DIR/illformed.rs:20:1 + | +LL | struct NoFieldsStructWithoutElementCount {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: tuples of scalable vectors can only contain multiple of the same scalable vector type + +error: scalable vectors must have a single field + --> $DIR/illformed.rs:25:1 + | +LL | struct NoFieldsTupleWithoutElementCount(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: tuples of scalable vectors can only contain multiple of the same scalable vector type + +error: scalable vectors must have a single field + --> $DIR/illformed.rs:29:1 + | +LL | struct NoFieldsUnitWithoutElementCount; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: tuples of scalable vectors can only contain multiple of the same scalable vector type + +error: scalable vectors cannot have multiple fields + --> $DIR/illformed.rs:34:1 + | +LL | struct MultipleFieldsStructWithElementCount { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: scalable vectors cannot have multiple fields + --> $DIR/illformed.rs:42:1 + | +LL | struct MultipleFieldsTupleWithElementCount(f32, u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: scalable vector structs can only have scalable vector fields + --> $DIR/illformed.rs:48:5 + | +LL | _ty: f32, + | ^^^^^^^^ + +error: scalable vector structs can only have scalable vector fields + --> $DIR/illformed.rs:54:47 + | +LL | struct MultipleFieldsTupleWithoutElementCount(f32, u32); + | ^^^ + +error: aborting due to 17 previous errors + diff --git a/tests/ui/scalable-vectors/invalid.rs b/tests/ui/scalable-vectors/invalid.rs new file mode 100644 index 000000000000..beb3ad66b78b --- /dev/null +++ b/tests/ui/scalable-vectors/invalid.rs @@ -0,0 +1,163 @@ +//@ edition: 2024 +#![allow(internal_features, unused_imports, unused_macros)] +#![feature(extern_types)] +#![feature(gen_blocks)] +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] +#![feature(trait_alias)] + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on extern crates +extern crate std as other_std; + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on use statements +use std::vec::Vec; + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on statics +static _X: u32 = 0; + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on constants +const _Y: u32 = 0; + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on modules +mod bar { +} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on foreign modules +unsafe extern "C" { + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on foreign statics + static X: &'static u32; + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on foreign types + type Y; + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on foreign functions + fn foo(); +} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on type aliases +type Foo = u32; + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on enums +enum Bar<#[rustc_scalable_vector(4)] T> { +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on function params + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on enum variants + Baz(std::marker::PhantomData), +} + +struct Qux { + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on struct fields + field: u32, +} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on unions +union FooBar { + x: u32, + y: u32, +} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on traits +trait FooBaz { + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on associated types + type Foo; + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on associated consts + const Bar: i32; + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on provided trait methods + fn foo() {} +} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on trait aliases +trait FooQux = FooBaz; + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on inherent impl blocks +impl Bar { + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on inherent methods + fn foo() {} +} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on trait impl blocks +impl FooBaz for Bar { + type Foo = u32; + const Bar: i32 = 3; +} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on macro defs +macro_rules! barqux { ($foo:tt) => { $foo }; } + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on functions +fn barqux(#[rustc_scalable_vector(4)] _x: u32) {} +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on function params +//~^^ ERROR: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on functions +async fn async_foo() {} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on functions +gen fn gen_foo() {} + +#[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on functions +async gen fn async_gen_foo() {} + +fn main() { + let _x = #[rustc_scalable_vector(4)] || { }; +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on closures + let _y = #[rustc_scalable_vector(4)] 3 + 4; +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on expressions + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on statements + let _z = 3; + + match _z { + #[rustc_scalable_vector(4)] +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on match arms + 1 => (), + _ => (), + } +} + +#[rustc_scalable_vector("4")] +//~^ ERROR: malformed `rustc_scalable_vector` attribute input +struct ArgNotLit(f32); + +#[rustc_scalable_vector(4, 2)] +//~^ ERROR: malformed `rustc_scalable_vector` attribute input +struct ArgMultipleLits(f32); + +#[rustc_scalable_vector(count = "4")] +//~^ ERROR: malformed `rustc_scalable_vector` attribute input +struct ArgKind(f32); + +#[rustc_scalable_vector(65536)] +//~^ ERROR: element count in `rustc_scalable_vector` is too large: `65536` +struct CountTooLarge(f32); + +#[rustc_scalable_vector(4)] +struct Okay(f32); + +#[rustc_scalable_vector] +struct OkayNoArg(f32); +//~^ ERROR: scalable vector structs can only have scalable vector fields diff --git a/tests/ui/scalable-vectors/invalid.stderr b/tests/ui/scalable-vectors/invalid.stderr new file mode 100644 index 000000000000..43be84a8b005 --- /dev/null +++ b/tests/ui/scalable-vectors/invalid.stderr @@ -0,0 +1,339 @@ +error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters + --> $DIR/invalid.rs:109:11 + | +LL | fn barqux(#[rustc_scalable_vector(4)] _x: u32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[rustc_scalable_vector]` attribute cannot be used on extern crates + --> $DIR/invalid.rs:9:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on use statements + --> $DIR/invalid.rs:13:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on statics + --> $DIR/invalid.rs:17:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on constants + --> $DIR/invalid.rs:21:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on modules + --> $DIR/invalid.rs:25:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on foreign modules + --> $DIR/invalid.rs:30:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on foreign statics + --> $DIR/invalid.rs:33:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on foreign types + --> $DIR/invalid.rs:36:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on foreign functions + --> $DIR/invalid.rs:39:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on type aliases + --> $DIR/invalid.rs:44:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on enums + --> $DIR/invalid.rs:48:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on function params + --> $DIR/invalid.rs:50:10 + | +LL | enum Bar<#[rustc_scalable_vector(4)] T> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on enum variants + --> $DIR/invalid.rs:52:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on struct fields + --> $DIR/invalid.rs:58:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on unions + --> $DIR/invalid.rs:63:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on traits + --> $DIR/invalid.rs:70:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on associated types + --> $DIR/invalid.rs:73:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on associated consts + --> $DIR/invalid.rs:76:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on provided trait methods + --> $DIR/invalid.rs:79:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on trait aliases + --> $DIR/invalid.rs:84:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on inherent impl blocks + --> $DIR/invalid.rs:88:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on inherent methods + --> $DIR/invalid.rs:91:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on trait impl blocks + --> $DIR/invalid.rs:96:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on macro defs + --> $DIR/invalid.rs:103:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on functions + --> $DIR/invalid.rs:107:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on function params + --> $DIR/invalid.rs:109:11 + | +LL | fn barqux(#[rustc_scalable_vector(4)] _x: u32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on functions + --> $DIR/invalid.rs:113:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on functions + --> $DIR/invalid.rs:117:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on functions + --> $DIR/invalid.rs:121:1 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on closures + --> $DIR/invalid.rs:126:14 + | +LL | let _x = #[rustc_scalable_vector(4)] || { }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on expressions + --> $DIR/invalid.rs:128:14 + | +LL | let _y = #[rustc_scalable_vector(4)] 3 + 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on statements + --> $DIR/invalid.rs:130:5 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error: `#[rustc_scalable_vector]` attribute cannot be used on match arms + --> $DIR/invalid.rs:135:9 + | +LL | #[rustc_scalable_vector(4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_scalable_vector]` can only be applied to structs + +error[E0539]: malformed `rustc_scalable_vector` attribute input + --> $DIR/invalid.rs:142:1 + | +LL | #[rustc_scalable_vector("4")] + | ^^^^^^^^^^^^^^^^^^^^^^^^---^^ + | | + | expected an integer literal here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[rustc_scalable_vector("4")] +LL + #[rustc_scalable_vector(count)] + | +LL - #[rustc_scalable_vector("4")] +LL + #[rustc_scalable_vector] + | + +error[E0805]: malformed `rustc_scalable_vector` attribute input + --> $DIR/invalid.rs:146:1 + | +LL | #[rustc_scalable_vector(4, 2)] + | ^^^^^^^^^^^^^^^^^^^^^^^------^ + | | + | expected a single argument here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[rustc_scalable_vector(4, 2)] +LL + #[rustc_scalable_vector(count)] + | +LL - #[rustc_scalable_vector(4, 2)] +LL + #[rustc_scalable_vector] + | + +error[E0539]: malformed `rustc_scalable_vector` attribute input + --> $DIR/invalid.rs:150:1 + | +LL | #[rustc_scalable_vector(count = "4")] + | ^^^^^^^^^^^^^^^^^^^^^^^^-----------^^ + | | + | expected an integer literal here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[rustc_scalable_vector(count = "4")] +LL + #[rustc_scalable_vector(count)] + | +LL - #[rustc_scalable_vector(count = "4")] +LL + #[rustc_scalable_vector] + | + +error: element count in `rustc_scalable_vector` is too large: `65536` + --> $DIR/invalid.rs:154:1 + | +LL | #[rustc_scalable_vector(65536)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the value may not exceed `u16::MAX` + +error: scalable vector structs can only have scalable vector fields + --> $DIR/invalid.rs:162:18 + | +LL | struct OkayNoArg(f32); + | ^^^ + +error: aborting due to 39 previous errors + +Some errors have detailed explanations: E0539, E0805. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/scalable-vectors/require-target-feature.rs b/tests/ui/scalable-vectors/require-target-feature.rs new file mode 100644 index 000000000000..b3c1d3e51007 --- /dev/null +++ b/tests/ui/scalable-vectors/require-target-feature.rs @@ -0,0 +1,40 @@ +//@ build-fail +//@ compile-flags: --crate-type=lib +//@ only-aarch64 +#![allow(incomplete_features, internal_features)] +#![feature( + simd_ffi, + rustc_attrs, + link_llvm_intrinsics +)] + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svint32_t(i32); + +#[inline(never)] +#[target_feature(enable = "sve")] +pub unsafe fn svdup_n_s32(op: i32) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4i32")] + fn _svdup_n_s32(op: i32) -> svint32_t; +//~^ WARN: `extern` block uses type `svint32_t`, which is not FFI-safe + } + unsafe { _svdup_n_s32(op) } +} + +pub fn non_annotated_callee(x: svint32_t) {} +//~^ ERROR: this function definition uses scalable vector type `svint32_t` + +#[target_feature(enable = "sve")] +pub fn annotated_callee(x: svint32_t) {} // okay! + +#[target_feature(enable = "sve")] +pub fn caller() { + unsafe { + let a = svdup_n_s32(42); + non_annotated_callee(a); + annotated_callee(a); + } +} diff --git a/tests/ui/scalable-vectors/require-target-feature.stderr b/tests/ui/scalable-vectors/require-target-feature.stderr new file mode 100644 index 000000000000..85b9e5b6579c --- /dev/null +++ b/tests/ui/scalable-vectors/require-target-feature.stderr @@ -0,0 +1,25 @@ +warning: `extern` block uses type `svint32_t`, which is not FFI-safe + --> $DIR/require-target-feature.rs:21:37 + | +LL | fn _svdup_n_s32(op: i32) -> svint32_t; + | ^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/require-target-feature.rs:14:1 + | +LL | pub struct svint32_t(i32); + | ^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(improper_ctypes)]` on by default + +error: this function definition uses scalable vector type `svint32_t` which (with the chosen ABI) requires the `sve` target feature, which is not enabled + --> $DIR/require-target-feature.rs:27:1 + | +LL | pub fn non_annotated_callee(x: svint32_t) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here + | + = help: consider enabling it globally (`-C target-feature=+sve`) or locally (`#[target_feature(enable="sve")]`) + +error: aborting due to 1 previous error; 1 warning emitted + diff --git a/tests/ui/scalable-vectors/value-type.rs b/tests/ui/scalable-vectors/value-type.rs new file mode 100644 index 000000000000..31a9ee8344eb --- /dev/null +++ b/tests/ui/scalable-vectors/value-type.rs @@ -0,0 +1,37 @@ +//@ build-pass +//@ compile-flags: --crate-type=lib +//@ only-aarch64 +#![allow(internal_features)] +#![feature( + link_llvm_intrinsics, + rustc_attrs, + simd_ffi, +)] + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svint32_t(i32); + +#[target_feature(enable = "sve")] +pub unsafe fn svdup_n_s32(op: i32) -> svint32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4i32")] + fn _svdup_n_s32(op: i32) -> svint32_t; +//~^ WARN: `extern` block uses type `svint32_t`, which is not FFI-safe + } + unsafe { _svdup_n_s32(op) } +} + +// Tests that scalable vectors can be locals, arguments and return types. + +#[target_feature(enable = "sve")] +fn id(v: svint32_t) -> svint32_t { v } + +#[target_feature(enable = "sve")] +fn foo() { + unsafe { + let v = svdup_n_s32(1); + let v = id(v); + } +} diff --git a/tests/ui/scalable-vectors/value-type.stderr b/tests/ui/scalable-vectors/value-type.stderr new file mode 100644 index 000000000000..3fc90ebd874e --- /dev/null +++ b/tests/ui/scalable-vectors/value-type.stderr @@ -0,0 +1,17 @@ +warning: `extern` block uses type `svint32_t`, which is not FFI-safe + --> $DIR/value-type.rs:20:37 + | +LL | fn _svdup_n_s32(op: i32) -> svint32_t; + | ^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/value-type.rs:14:1 + | +LL | pub struct svint32_t(i32); + | ^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(improper_ctypes)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/scalable-vectors/wellformed-arrays.rs b/tests/ui/scalable-vectors/wellformed-arrays.rs new file mode 100644 index 000000000000..b8f0bf291eea --- /dev/null +++ b/tests/ui/scalable-vectors/wellformed-arrays.rs @@ -0,0 +1,10 @@ +//@ check-pass +//@ compile-flags: --crate-type=lib +#![feature(rustc_attrs)] + +#[rustc_scalable_vector(16)] +struct ScalableU8(u8); + +fn main() { + let x: [ScalableU8; 4] = todo!(); +} diff --git a/tests/ui/scalable-vectors/wellformed.rs b/tests/ui/scalable-vectors/wellformed.rs new file mode 100644 index 000000000000..cb6a22d6c433 --- /dev/null +++ b/tests/ui/scalable-vectors/wellformed.rs @@ -0,0 +1,48 @@ +//@ check-pass +//@ compile-flags: --crate-type=lib +#![feature(rustc_attrs)] + +#[rustc_scalable_vector(16)] +struct ScalableU8(u8); + +#[rustc_scalable_vector(8)] +struct ScalableU16(u16); + +#[rustc_scalable_vector(4)] +struct ScalableU32(u32); + +#[rustc_scalable_vector(2)] +struct ScalableU64(u64); + +#[rustc_scalable_vector(1)] +struct ScalableU128(u128); + +#[rustc_scalable_vector(16)] +struct ScalableI8(i8); + +#[rustc_scalable_vector(8)] +struct ScalableI16(i16); + +#[rustc_scalable_vector(4)] +struct ScalableI32(i32); + +#[rustc_scalable_vector(2)] +struct ScalableI64(i64); + +#[rustc_scalable_vector(1)] +struct ScalableI128(i128); + +#[rustc_scalable_vector(8)] +struct ScalableF16(f32); + +#[rustc_scalable_vector(4)] +struct ScalableF32(f32); + +#[rustc_scalable_vector(2)] +struct ScalableF64(f64); + +#[rustc_scalable_vector(16)] +struct ScalableBool(bool); + +#[rustc_scalable_vector] +struct ScalableTuple(ScalableU8, ScalableU8, ScalableU8); diff --git a/tests/ui/issues/issue-34047.rs b/tests/ui/shadowed/match-binding-shadows-const.rs similarity index 66% rename from tests/ui/issues/issue-34047.rs rename to tests/ui/shadowed/match-binding-shadows-const.rs index 55196177f693..95f2e087b5e7 100644 --- a/tests/ui/issues/issue-34047.rs +++ b/tests/ui/shadowed/match-binding-shadows-const.rs @@ -1,3 +1,4 @@ +//! regression test for https://github.com/rust-lang/rust/issues/34047 const C: u8 = 0; fn main() { diff --git a/tests/ui/issues/issue-34047.stderr b/tests/ui/shadowed/match-binding-shadows-const.stderr similarity index 87% rename from tests/ui/issues/issue-34047.stderr rename to tests/ui/shadowed/match-binding-shadows-const.stderr index 97b1230ce350..345c67700e62 100644 --- a/tests/ui/issues/issue-34047.stderr +++ b/tests/ui/shadowed/match-binding-shadows-const.stderr @@ -1,5 +1,5 @@ error[E0530]: match bindings cannot shadow constants - --> $DIR/issue-34047.rs:5:13 + --> $DIR/match-binding-shadows-const.rs:6:13 | LL | const C: u8 = 0; | ---------------- the constant `C` is defined here diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.stderr index 10308ec07da5..f16006a8e085 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.stderr @@ -16,7 +16,6 @@ error[E0282]: type annotations needed | LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` - | error: aborting due to 2 previous errors diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs index 92e300d33d6e..dabff97ad52d 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs @@ -7,7 +7,7 @@ #[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR malformed `stable` attribute input [E0538] fn f1() { } -#[stable(feature = "a", sinse = "1.0.0")] //~ ERROR unknown meta item 'sinse' +#[stable(feature = "a", sinse = "1.0.0")] //~ ERROR malformed `stable` attribute input [E0539] fn f2() { } #[unstable(feature = "a", issue = "no")] diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr index 5b35a51cad72..7beb9fd979ce 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr @@ -7,11 +7,14 @@ LL | #[stable(feature = "a", feature = "b", since = "1.0.0")] | | found `feature` used as a key more than once | help: must be of the form: `#[stable(feature = "name", since = "version")]` -error[E0541]: unknown meta item 'sinse' - --> $DIR/stability-attribute-sanity-2.rs:10:25 +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity-2.rs:10:1 | LL | #[stable(feature = "a", sinse = "1.0.0")] - | ^^^^^^^^^^^^^^^ expected one of `feature`, `since` + | ^^^^^^^^^^^^^^^^^^^^^^^^---------------^^ + | | | + | | valid arguments are `feature` or `since` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0545]: `issue` must be a non-zero numeric string or "none" --> $DIR/stability-attribute-sanity-2.rs:13:27 @@ -23,5 +26,5 @@ LL | #[unstable(feature = "a", issue = "no")] error: aborting due to 3 previous errors -Some errors have detailed explanations: E0538, E0541, E0545. +Some errors have detailed explanations: E0538, E0539, E0545. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr b/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr index f656aeaa16c7..9b3f540198ce 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity-4.stderr @@ -11,9 +11,9 @@ error[E0539]: malformed `unstable` attribute input --> $DIR/stability-attribute-sanity-4.rs:11:5 | LL | #[unstable = "b"] - | ^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^-----^ + | | | + | | expected this to be a list | help: must be of the form: `#[unstable(feature = "name", reason = "...", issue = "N")]` error[E0539]: malformed `stable` attribute input @@ -29,9 +29,9 @@ error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity-4.rs:17:5 | LL | #[stable = "a"] - | ^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^-----^ + | | | + | | expected this to be a list | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0542]: missing 'since' diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.rs b/tests/ui/stability-attribute/stability-attribute-sanity.rs index c4c86e12d267..cee8d5fae1d2 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity.rs @@ -5,7 +5,7 @@ #![stable(feature = "rust1", since = "1.0.0")] mod bogus_attribute_types_1 { - #[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR unknown meta item 'reason' [E0541] + #[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR malformed `stable` attribute input [E0539] fn f1() { } #[stable(feature = "a", since)] //~ ERROR malformed `stable` attribute input [E0539] diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr index ae948237d7ed..05c34484b9f8 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr @@ -1,8 +1,11 @@ -error[E0541]: unknown meta item 'reason' - --> $DIR/stability-attribute-sanity.rs:8:46 +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity.rs:8:5 | LL | #[stable(feature = "a", since = "4.4.4", reason)] - | ^^^^^^ expected one of `feature`, `since` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^ + | | | + | | valid arguments are `feature` or `since` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity.rs:11:5 @@ -138,5 +141,5 @@ LL | #[stable(feature = "a", since = "1.0.0")] error: aborting due to 20 previous errors -Some errors have detailed explanations: E0539, E0541, E0542, E0543, E0544, E0546, E0547, E0549, E0711. +Some errors have detailed explanations: E0539, E0542, E0543, E0544, E0546, E0547, E0549, E0711. For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/static/static-array-shared-slice-references.rs b/tests/ui/static/static-array-shared-slice-references.rs new file mode 100644 index 000000000000..a2ef169e2a29 --- /dev/null +++ b/tests/ui/static/static-array-shared-slice-references.rs @@ -0,0 +1,10 @@ +//! regression test for +//@ build-pass +#![allow(dead_code)] + +static FOO: [usize; 3] = [1, 2, 3]; + +static SLICE_1: &'static [usize] = &FOO; +static SLICE_2: &'static [usize] = &FOO; + +fn main() {} diff --git a/tests/ui/statics/static-mut-unsafe-init.rs b/tests/ui/statics/static-mut-unsafe-init.rs new file mode 100644 index 000000000000..ee8472100a08 --- /dev/null +++ b/tests/ui/statics/static-mut-unsafe-init.rs @@ -0,0 +1,8 @@ +//! regression test for https://github.com/rust-lang/rust/issues/17450 +//@ build-pass +#![allow(dead_code)] + +static mut X: isize = 3; +static mut Y: isize = unsafe { X }; + +fn main() {} diff --git a/tests/ui/str/raw-string-literal-unescaped-unicode.rs b/tests/ui/str/raw-string-literal-unescaped-unicode.rs new file mode 100644 index 000000000000..c0dc0743a28f --- /dev/null +++ b/tests/ui/str/raw-string-literal-unescaped-unicode.rs @@ -0,0 +1,10 @@ +//! regression test for +//@ check-pass + +fn main() { + assert!({ false }); + + assert!(r"\u{41}" == "A"); + + assert!(r"\u{".is_empty()); +} diff --git a/tests/ui/structs/ice-line-bounds-issue-148684.stderr b/tests/ui/structs/ice-line-bounds-issue-148684.stderr index f26d96cd1172..71ed393bf056 100644 --- a/tests/ui/structs/ice-line-bounds-issue-148684.stderr +++ b/tests/ui/structs/ice-line-bounds-issue-148684.stderr @@ -9,7 +9,6 @@ LL | | } ... LL | A(2, vec![]) | ^^^^^^^^^^^^ - | error: aborting due to 1 previous error diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index eaa26aa3ecaf..05a836b01af5 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -26,9 +26,9 @@ error[E0539]: malformed `target_feature` attribute input --> $DIR/invalid-attribute.rs:17:1 | LL | #[target_feature = "+sse2"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | expected this to be a list + | ^^^^^^^^^^^^^^^^^---------^ + | | | + | | expected this to be a list | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error[E0539]: malformed `target_feature` attribute input diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 8a2bad56c126..ecc5fb21435f 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -95,7 +95,7 @@ body: did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM - repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 13397682652773712997 } + repr: ReprOptions { int: None, align: None, pack: None, flags: , scalable: None, field_shuffle_seed: 13397682652773712997 } args: [] variant_index: 0 subpatterns: [ @@ -109,7 +109,7 @@ body: did: DefId(0:3 ~ thir_tree_match[fcf8]::Bar) variants: [VariantDef { def_id: DefId(0:4 ~ thir_tree_match[fcf8]::Bar::First), ctor: Some((Const, DefId(0:5 ~ thir_tree_match[fcf8]::Bar::First::{constructor#0}))), name: "First", discr: Relative(0), fields: [], tainted: None, flags: }, VariantDef { def_id: DefId(0:6 ~ thir_tree_match[fcf8]::Bar::Second), ctor: Some((Const, DefId(0:7 ~ thir_tree_match[fcf8]::Bar::Second::{constructor#0}))), name: "Second", discr: Relative(1), fields: [], tainted: None, flags: }, VariantDef { def_id: DefId(0:8 ~ thir_tree_match[fcf8]::Bar::Third), ctor: Some((Const, DefId(0:9 ~ thir_tree_match[fcf8]::Bar::Third::{constructor#0}))), name: "Third", discr: Relative(2), fields: [], tainted: None, flags: }] flags: IS_ENUM - repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 7908585036048874241 } + repr: ReprOptions { int: None, align: None, pack: None, flags: , scalable: None, field_shuffle_seed: 7908585036048874241 } args: [] variant_index: 0 subpatterns: [] @@ -157,7 +157,7 @@ body: did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM - repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 13397682652773712997 } + repr: ReprOptions { int: None, align: None, pack: None, flags: , scalable: None, field_shuffle_seed: 13397682652773712997 } args: [] variant_index: 0 subpatterns: [ @@ -209,7 +209,7 @@ body: did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe, value: None }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM - repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 13397682652773712997 } + repr: ReprOptions { int: None, align: None, pack: None, flags: , scalable: None, field_shuffle_seed: 13397682652773712997 } args: [] variant_index: 1 subpatterns: [] diff --git a/tests/ui/thread-local/non-static.rs b/tests/ui/thread-local/non-static.rs index f1c4273870bf..f14ff20f449b 100644 --- a/tests/ui/thread-local/non-static.rs +++ b/tests/ui/thread-local/non-static.rs @@ -2,19 +2,19 @@ #![feature(thread_local)] #[thread_local] -//~^ ERROR attribute should be applied to a static +//~^ ERROR `#[thread_local]` attribute cannot be used on constants const A: u32 = 0; #[thread_local] -//~^ ERROR attribute should be applied to a static +//~^ ERROR `#[thread_local]` attribute cannot be used on functions fn main() { #[thread_local] || {}; - //~^ ERROR attribute should be applied to a static + //~^ ERROR `#[thread_local]` attribute cannot be used on closures } struct S { #[thread_local] - //~^ ERROR attribute should be applied to a static + //~^ ERROR `#[thread_local]` attribute cannot be used on struct fields a: String, b: String, } diff --git a/tests/ui/thread-local/non-static.stderr b/tests/ui/thread-local/non-static.stderr index 09a1618d6e71..c83a6beb4c6a 100644 --- a/tests/ui/thread-local/non-static.stderr +++ b/tests/ui/thread-local/non-static.stderr @@ -1,38 +1,34 @@ -error: attribute should be applied to a static +error: `#[thread_local]` attribute cannot be used on constants --> $DIR/non-static.rs:4:1 | LL | #[thread_local] | ^^^^^^^^^^^^^^^ -LL | -LL | const A: u32 = 0; - | ----------------- not a static + | + = help: `#[thread_local]` can be applied to foreign statics and statics -error: attribute should be applied to a static +error: `#[thread_local]` attribute cannot be used on functions --> $DIR/non-static.rs:8:1 | -LL | #[thread_local] - | ^^^^^^^^^^^^^^^ -LL | -LL | / fn main() { -LL | | #[thread_local] || {}; -LL | | -LL | | } - | |_- not a static +LL | #[thread_local] + | ^^^^^^^^^^^^^^^ + | + = help: `#[thread_local]` can be applied to foreign statics and statics -error: attribute should be applied to a static +error: `#[thread_local]` attribute cannot be used on closures --> $DIR/non-static.rs:11:5 | LL | #[thread_local] || {}; - | ^^^^^^^^^^^^^^^ ----- not a static + | ^^^^^^^^^^^^^^^ + | + = help: `#[thread_local]` can be applied to foreign statics and statics -error: attribute should be applied to a static +error: `#[thread_local]` attribute cannot be used on struct fields --> $DIR/non-static.rs:16:5 | LL | #[thread_local] | ^^^^^^^^^^^^^^^ -LL | -LL | a: String, - | --------- not a static + | + = help: `#[thread_local]` can be applied to foreign statics and statics error: aborting due to 4 previous errors diff --git a/tests/ui/threads-sendsync/thread-join-unwrap.rs b/tests/ui/threads-sendsync/thread-join-unwrap.rs new file mode 100644 index 000000000000..6288e15b1874 --- /dev/null +++ b/tests/ui/threads-sendsync/thread-join-unwrap.rs @@ -0,0 +1,9 @@ +//! Regression test for unwrapping the result of `join`, issue https://github.com/rust-lang/rust/issues/21291 +//@ run-pass +//@ needs-threads + +use std::thread; + +fn main() { + thread::spawn(|| {}).join().unwrap() +} diff --git a/tests/ui/track-diagnostics/track.rs b/tests/ui/track-diagnostics/track.rs index 2d6d6170fe0b..09864a6e3090 100644 --- a/tests/ui/track-diagnostics/track.rs +++ b/tests/ui/track-diagnostics/track.rs @@ -7,6 +7,7 @@ // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" //@ normalize-stderr: "note: rustc .+ running on .+" -> "note: rustc $$VERSION running on $$TARGET" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" // The test becomes too flaky if we care about exact args. If `-Z ui-testing` // from compiletest and `-Z track-diagnostics` from `// compile-flags` at the diff --git a/tests/ui/track-diagnostics/track2.rs b/tests/ui/track-diagnostics/track2.rs index 591b84f330b7..145b2a3e5d57 100644 --- a/tests/ui/track-diagnostics/track2.rs +++ b/tests/ui/track-diagnostics/track2.rs @@ -4,6 +4,7 @@ // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" fn main() { let _moved @ _from = String::from("foo"); diff --git a/tests/ui/track-diagnostics/track3.rs b/tests/ui/track-diagnostics/track3.rs index a39e71915d90..144f2794e910 100644 --- a/tests/ui/track-diagnostics/track3.rs +++ b/tests/ui/track-diagnostics/track3.rs @@ -4,6 +4,7 @@ // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" fn main() { let _unimported = Blah { field: u8 }; diff --git a/tests/ui/track-diagnostics/track4.rs b/tests/ui/track-diagnostics/track4.rs index 0038c616aa55..2ab7d5423303 100644 --- a/tests/ui/track-diagnostics/track4.rs +++ b/tests/ui/track-diagnostics/track4.rs @@ -4,6 +4,7 @@ // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" pub onion { //~^ ERROR missing `enum` for enum definition diff --git a/tests/ui/track-diagnostics/track5.rs b/tests/ui/track-diagnostics/track5.rs index 09fda4eb5273..4d47f56e5806 100644 --- a/tests/ui/track-diagnostics/track5.rs +++ b/tests/ui/track-diagnostics/track5.rs @@ -4,6 +4,7 @@ // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" } //~^ ERROR unexpected closing delimiter: `}` diff --git a/tests/ui/track-diagnostics/track6.rs b/tests/ui/track-diagnostics/track6.rs index 11d3b7e9764b..aa75c5691e5f 100644 --- a/tests/ui/track-diagnostics/track6.rs +++ b/tests/ui/track-diagnostics/track6.rs @@ -4,13 +4,13 @@ // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" - +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" pub trait Foo { fn bar(); } -impl Foo for T { +impl Foo for T { default fn bar() {} //~^ ERROR specialization is unstable //~| NOTE created at diff --git a/tests/ui/issues/issue-20162.rs b/tests/ui/trait-bounds/sort-missing-ord-bound.rs similarity index 50% rename from tests/ui/issues/issue-20162.rs rename to tests/ui/trait-bounds/sort-missing-ord-bound.rs index b491bc37f515..8d4ea8815017 100644 --- a/tests/ui/issues/issue-20162.rs +++ b/tests/ui/trait-bounds/sort-missing-ord-bound.rs @@ -1,4 +1,7 @@ -struct X { x: i32 } +//! regression test for issue https://github.com/rust-lang/rust/issues/20162 +struct X { + x: i32, +} fn main() { let mut b: Vec = vec![]; diff --git a/tests/ui/issues/issue-20162.stderr b/tests/ui/trait-bounds/sort-missing-ord-bound.stderr similarity index 88% rename from tests/ui/issues/issue-20162.stderr rename to tests/ui/trait-bounds/sort-missing-ord-bound.stderr index 8f45f0d36c71..c11781e5fff3 100644 --- a/tests/ui/issues/issue-20162.stderr +++ b/tests/ui/trait-bounds/sort-missing-ord-bound.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `X: Ord` is not satisfied - --> $DIR/issue-20162.rs:5:7 + --> $DIR/sort-missing-ord-bound.rs:8:7 | LL | b.sort(); | ^^^^ the trait `Ord` is not implemented for `X` @@ -9,7 +9,7 @@ note: required by a bound in `slice::::sort` help: consider annotating `X` with `#[derive(Ord)]` | LL + #[derive(Ord)] -LL | struct X { x: i32 } +LL | struct X { | error: aborting due to 1 previous error diff --git a/tests/ui/traits/const-traits/call.rs b/tests/ui/traits/const-traits/call.rs index 360c08e1b7fe..c36adc4248f0 100644 --- a/tests/ui/traits/const-traits/call.rs +++ b/tests/ui/traits/const-traits/call.rs @@ -6,6 +6,7 @@ const _: () = { assert!((const || true)()); //~^ ERROR }: [const] Fn()` is not satisfied + //~| ERROR }: [const] FnMut()` is not satisfied }; fn main() {} diff --git a/tests/ui/traits/const-traits/call.stderr b/tests/ui/traits/const-traits/call.stderr index 8e32cab6dfcf..b688746e2506 100644 --- a/tests/ui/traits/const-traits/call.stderr +++ b/tests/ui/traits/const-traits/call.stderr @@ -4,6 +4,15 @@ error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: [const] Fn()` LL | assert!((const || true)()); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0277]: the trait bound `{closure@$DIR/call.rs:7:14: 7:22}: [const] FnMut()` is not satisfied + --> $DIR/call.rs:7:13 + | +LL | assert!((const || true)()); + | ^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `call` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs index 30002038f689..e355ee724d1b 100644 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs +++ b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.rs @@ -13,6 +13,4 @@ impl Foo for () { fn main() { (const || (()).foo())(); - // ^ ERROR: cannot call non-const method `<() as Foo>::foo` in constant functions - // FIXME(const_trait_impl) this should probably say constant closures } diff --git a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr index dab3f14161fa..90e87c724f54 100644 --- a/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr +++ b/tests/ui/traits/const-traits/const_closure-const_trait_impl-ice-113381.stderr @@ -4,6 +4,15 @@ error[E0277]: the trait bound `{closure@$DIR/const_closure-const_trait_impl-ice- LL | (const || (()).foo())(); | ^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0277]: the trait bound `{closure@$DIR/const_closure-const_trait_impl-ice-113381.rs:15:6: 15:14}: [const] FnMut()` is not satisfied + --> $DIR/const_closure-const_trait_impl-ice-113381.rs:15:5 + | +LL | (const || (()).foo())(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `call` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs index d0470fa34584..ee4ff02f4c7c 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.rs @@ -12,4 +12,5 @@ impl Foo for () { fn main() { (const || { (()).foo() })(); //~^ ERROR: }: [const] Fn()` is not satisfied + //~| ERROR: }: [const] FnMut()` is not satisfied } diff --git a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr index dcf65ab69408..69d289537da1 100644 --- a/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr +++ b/tests/ui/traits/const-traits/non-const-op-const-closure-non-const-outer.stderr @@ -4,6 +4,15 @@ error[E0277]: the trait bound `{closure@$DIR/non-const-op-const-closure-non-cons LL | (const || { (()).foo() })(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error[E0277]: the trait bound `{closure@$DIR/non-const-op-const-closure-non-const-outer.rs:13:6: 13:14}: [const] FnMut()` is not satisfied + --> $DIR/non-const-op-const-closure-non-const-outer.rs:13:5 + | +LL | (const || { (()).foo() })(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: required by a bound in `call` + --> $SRC_DIR/core/src/ops/function.rs:LL:COL + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs index df1d5105a34a..dfea4b93f46d 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.rs +++ b/tests/ui/traits/question-mark-result-err-mismatch.rs @@ -9,7 +9,7 @@ fn foo() -> Result { //~ NOTE expected `String` because of this }); let one = x .map(|s| ()) - .map_err(|e| { //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>` + .map_err(|e| { //~ NOTE this has type `Result<_, ()>` e; //~ HELP remove this semicolon }) .map(|()| "")?; //~ ERROR `?` couldn't convert the error to `String` diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr index 0f83c9e73a31..be3f17cfc527 100644 --- a/tests/ui/traits/question-mark-result-err-mismatch.stderr +++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr @@ -9,7 +9,7 @@ LL | .map_err(|e| { LL | | e; | | - help: remove this semicolon LL | | }) - | |__________- this can't be annotated with `?` because it has type `Result<_, ()>` + | |__________- this has type `Result<_, ()>` LL | .map(|()| "")?; | ^ the trait `From<()>` is not implemented for `String` | diff --git a/tests/ui/traits/question-mark-span-144304.rs b/tests/ui/traits/question-mark-span-144304.rs new file mode 100644 index 000000000000..bb015cae8ac9 --- /dev/null +++ b/tests/ui/traits/question-mark-span-144304.rs @@ -0,0 +1,8 @@ +//@ ignore-arm - armhf-gnu have more types implement trait `From`, let's skip it +fn f() -> Result<(), i32> { + Err("str").map_err(|e| e)?; //~ ERROR `?` couldn't convert the error to `i32` + Err("str").map_err(|e| e.to_string())?; //~ ERROR `?` couldn't convert the error to `i32` + Ok(()) +} + +fn main() {} diff --git a/tests/ui/traits/question-mark-span-144304.stderr b/tests/ui/traits/question-mark-span-144304.stderr new file mode 100644 index 000000000000..a412da0d235d --- /dev/null +++ b/tests/ui/traits/question-mark-span-144304.stderr @@ -0,0 +1,41 @@ +error[E0277]: `?` couldn't convert the error to `i32` + --> $DIR/question-mark-span-144304.rs:3:30 + | +LL | fn f() -> Result<(), i32> { + | --------------- expected `i32` because of this +LL | Err("str").map_err(|e| e)?; + | ---------- ^ the trait `From<&str>` is not implemented for `i32` + | | + | this has type `Result<_, &str>` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = help: the following other types implement trait `From`: + `i32` implements `From` + `i32` implements `From` + `i32` implements `From` + `i32` implements `From` + `i32` implements `From` + +error[E0277]: `?` couldn't convert the error to `i32` + --> $DIR/question-mark-span-144304.rs:4:42 + | +LL | fn f() -> Result<(), i32> { + | --------------- expected `i32` because of this +LL | Err("str").map_err(|e| e)?; +LL | Err("str").map_err(|e| e.to_string())?; + | ---------- --------------------------^ the trait `From` is not implemented for `i32` + | | | + | | this can't be annotated with `?` because it has type `Result<_, String>` + | this has type `Result<_, &str>` + | + = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait + = help: the following other types implement trait `From`: + `i32` implements `From` + `i32` implements `From` + `i32` implements `From` + `i32` implements `From` + `i32` implements `From` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/solver-cycles/assoc-equality-cycle.rs b/tests/ui/traits/solver-cycles/assoc-equality-cycle.rs new file mode 100644 index 000000000000..f82bffc598b8 --- /dev/null +++ b/tests/ui/traits/solver-cycles/assoc-equality-cycle.rs @@ -0,0 +1,10 @@ +//! regression test for https://github.com/rust-lang/rust/issues/21177 +trait Trait { + type A; + type B; +} + +fn foo>() {} +//~^ ERROR cycle detected + +fn main() {} diff --git a/tests/ui/issues/issue-21177.stderr b/tests/ui/traits/solver-cycles/assoc-equality-cycle.stderr similarity index 78% rename from tests/ui/issues/issue-21177.stderr rename to tests/ui/traits/solver-cycles/assoc-equality-cycle.stderr index 9f66f43a195a..a8c0228601a2 100644 --- a/tests/ui/issues/issue-21177.stderr +++ b/tests/ui/traits/solver-cycles/assoc-equality-cycle.stderr @@ -1,14 +1,14 @@ error[E0391]: cycle detected when computing the bounds for type parameter `T` - --> $DIR/issue-21177.rs:6:21 + --> $DIR/assoc-equality-cycle.rs:7:21 | -LL | fn foo>() { } +LL | fn foo>() {} | ^^^^ | = note: ...which immediately requires computing the bounds for type parameter `T` again note: cycle used when computing explicit predicates of `foo` - --> $DIR/issue-21177.rs:6:21 + --> $DIR/assoc-equality-cycle.rs:7:21 | -LL | fn foo>() { } +LL | fn foo>() {} | ^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information diff --git a/tests/ui/traits/solver-cycles/self-item-cycle.rs b/tests/ui/traits/solver-cycles/self-item-cycle.rs new file mode 100644 index 000000000000..b0b5ab4235be --- /dev/null +++ b/tests/ui/traits/solver-cycles/self-item-cycle.rs @@ -0,0 +1,6 @@ +//! regression test for https://github.com/rust-lang/rust/issues/20772 +trait T: Iterator //~ ERROR cycle detected +{ +} + +fn main() {} diff --git a/tests/ui/issues/issue-20772.stderr b/tests/ui/traits/solver-cycles/self-item-cycle.stderr similarity index 69% rename from tests/ui/issues/issue-20772.stderr rename to tests/ui/traits/solver-cycles/self-item-cycle.stderr index 81f80aef5944..9ffb9955af8a 100644 --- a/tests/ui/issues/issue-20772.stderr +++ b/tests/ui/traits/solver-cycles/self-item-cycle.stderr @@ -1,15 +1,15 @@ error[E0391]: cycle detected when computing the super traits of `T` with associated type name `Item` - --> $DIR/issue-20772.rs:1:1 + --> $DIR/self-item-cycle.rs:2:1 | -LL | trait T : Iterator - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait T: Iterator + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: ...which immediately requires computing the super traits of `T` with associated type name `Item` again note: cycle used when computing the super predicates of `T` - --> $DIR/issue-20772.rs:1:1 + --> $DIR/self-item-cycle.rs:2:1 | -LL | trait T : Iterator - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | trait T: Iterator + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs index 7f0f6a214aae..383d9108eb90 100644 --- a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs +++ b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs @@ -23,15 +23,6 @@ fn upvar() { }; } -fn enum_upvar() { - type T = impl Copy; - let foo: T = Some((1u32, 2u32)); - let x = move || match foo { - None => (), - Some((a, b)) => (), - }; -} - fn r#struct() { #[derive(Copy, Clone)] struct Foo((u32, u32)); diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs index ff99ac98926c..29f193a4d7f3 100644 --- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs @@ -3,6 +3,7 @@ //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc'.*panicked.*\n" -> "" //@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " +//@ normalize-stderr: "/rustc(?:-dev)?/[a-z0-9.]+/" -> "" //@ rustc-env:RUST_BACKTRACE=0 #![feature(pattern_types, pattern_type_macro, generic_const_exprs)] diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr index ffc6068eb17e..d053d8aed062 100644 --- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr @@ -1,12 +1,12 @@ warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/bad_const_generics_args_on_const_param.rs:8:47 + --> $DIR/bad_const_generics_args_on_const_param.rs:9:47 | LL | #![feature(pattern_types, pattern_type_macro, generic_const_exprs)] | ^^^^^^^^^^^^^^^^^^^ | = note: see issue #76560 for more information = error: internal compiler error: compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs:LL:CC: try_lower_anon_const_lit: received const param which shouldn't be possible - --> $DIR/bad_const_generics_args_on_const_param.rs:12:36 + --> $DIR/bad_const_generics_args_on_const_param.rs:13:36 | LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/type/pattern_types/non_null.stderr b/tests/ui/type/pattern_types/non_null.stderr index ad61e9a59147..0847af2d086f 100644 --- a/tests/ui/type/pattern_types/non_null.stderr +++ b/tests/ui/type/pattern_types/non_null.stderr @@ -17,7 +17,7 @@ error: layout_of((*const T) is !null) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -64,7 +64,7 @@ error: layout_of(Option<(*const ()) is !null>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -96,7 +96,7 @@ error: layout_of(Option<(*const ()) is !null>) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -126,7 +126,7 @@ error: layout_of(Option<(*const ()) is !null>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -186,7 +186,7 @@ error: layout_of((*const [u8]) is !null) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/type/pattern_types/or_patterns.stderr b/tests/ui/type/pattern_types/or_patterns.stderr index adc7320829b9..7206c570187b 100644 --- a/tests/ui/type/pattern_types/or_patterns.stderr +++ b/tests/ui/type/pattern_types/or_patterns.stderr @@ -57,7 +57,7 @@ error: layout_of((i8) is (i8::MIN..=-1 | 1..)) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -102,7 +102,7 @@ error: layout_of((i8) is (i8::MIN..=-2 | 0..)) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/type/pattern_types/range_patterns.stderr b/tests/ui/type/pattern_types/range_patterns.stderr index c9a846e22f58..6c9acac7eb7b 100644 --- a/tests/ui/type/pattern_types/range_patterns.stderr +++ b/tests/ui/type/pattern_types/range_patterns.stderr @@ -16,7 +16,7 @@ error: layout_of(NonZero) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -61,7 +61,7 @@ error: layout_of((u32) is 1..) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -106,7 +106,7 @@ error: layout_of(Option<(u32) is 1..>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -137,7 +137,7 @@ error: layout_of(Option<(u32) is 1..>) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -166,7 +166,7 @@ error: layout_of(Option<(u32) is 1..>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -217,7 +217,7 @@ error: layout_of(Option>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -248,7 +248,7 @@ error: layout_of(Option>) = Layout { }, fields: Arbitrary { offsets: [], - memory_index: [], + in_memory_order: [], }, largest_niche: None, uninhabited: false, @@ -277,7 +277,7 @@ error: layout_of(Option>) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -328,7 +328,7 @@ error: layout_of(NonZeroU32New) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -401,7 +401,7 @@ error: layout_of((i8) is -10..=10) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, @@ -446,7 +446,7 @@ error: layout_of((i8) is i8::MIN..=0) = Layout { offsets: [ Size(0 bytes), ], - memory_index: [ + in_memory_order: [ 0, ], }, diff --git a/tests/ui/typeck/issue-105946.stderr b/tests/ui/typeck/issue-105946.stderr index 30fe2000a461..3f8733bda763 100644 --- a/tests/ui/typeck/issue-105946.stderr +++ b/tests/ui/typeck/issue-105946.stderr @@ -4,10 +4,16 @@ error[E0425]: cannot find value `_y` in this scope LL | let [_y..] = [Box::new(1), Box::new(2)]; | ^^ not found in this scope | + = note: range patterns match against the start and end of a range; to bind the components, use a struct pattern help: if you meant to collect the rest of the slice in `_y`, use the at operator | LL | let [_y @ ..] = [Box::new(1), Box::new(2)]; | + +help: if you meant to destructure a range use a struct pattern + | +LL - let [_y..] = [Box::new(1), Box::new(2)]; +LL + let [std::ops::RangeFrom { start: _y }] = [Box::new(1), Box::new(2)]; + | error[E0658]: `X..` patterns in slices are experimental --> $DIR/issue-105946.rs:7:10 diff --git a/tests/ui/typeck/missing-type-annotation.rs b/tests/ui/typeck/missing-type-annotation.rs new file mode 100644 index 000000000000..d13a7a8559e3 --- /dev/null +++ b/tests/ui/typeck/missing-type-annotation.rs @@ -0,0 +1,4 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18159 +fn main() { + let x; //~ ERROR type annotations needed +} diff --git a/tests/ui/issues/issue-18159.stderr b/tests/ui/typeck/missing-type-annotation.stderr similarity index 87% rename from tests/ui/issues/issue-18159.stderr rename to tests/ui/typeck/missing-type-annotation.stderr index 5de13a5c314c..88716e757a93 100644 --- a/tests/ui/issues/issue-18159.stderr +++ b/tests/ui/typeck/missing-type-annotation.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/issue-18159.rs:2:9 + --> $DIR/missing-type-annotation.rs:3:9 | LL | let x; | ^ diff --git a/tests/ui/issues/issue-49854.rs b/tests/ui/typeck/osstring-str-equality.rs similarity index 71% rename from tests/ui/issues/issue-49854.rs rename to tests/ui/typeck/osstring-str-equality.rs index b5a3f07dd4b8..47d3dd632bdc 100644 --- a/tests/ui/issues/issue-49854.rs +++ b/tests/ui/typeck/osstring-str-equality.rs @@ -1,3 +1,4 @@ +//! regression test for https://github.com/rust-lang/rust/issues/49854 //@ run-pass use std::ffi::OsString; diff --git a/tests/ui/typeck/return-expression-invalid-callee.rs b/tests/ui/typeck/return-expression-invalid-callee.rs new file mode 100644 index 000000000000..1416f3e9ddfe --- /dev/null +++ b/tests/ui/typeck/return-expression-invalid-callee.rs @@ -0,0 +1,8 @@ +//! regression test for issue https://github.com/rust-lang/rust/issues/18532 +//! Test that overloaded call parameter checking does not ICE +//! when a type error or unconstrained type variable propagates +//! into it. + +fn main() { + (return)((), ()); //~ ERROR expected function, found `!` +} diff --git a/tests/ui/issues/issue-18532.stderr b/tests/ui/typeck/return-expression-invalid-callee.stderr similarity index 67% rename from tests/ui/issues/issue-18532.stderr rename to tests/ui/typeck/return-expression-invalid-callee.stderr index 059c7f137819..5a36602af8bb 100644 --- a/tests/ui/issues/issue-18532.stderr +++ b/tests/ui/typeck/return-expression-invalid-callee.stderr @@ -1,8 +1,8 @@ error[E0618]: expected function, found `!` - --> $DIR/issue-18532.rs:6:5 + --> $DIR/return-expression-invalid-callee.rs:7:5 | -LL | (return)((),()); - | ^^^^^^^^------- +LL | (return)((), ()); + | ^^^^^^^^-------- | | | call expression requires function diff --git a/triagebot.toml b/triagebot.toml index 1a1f71bd2edd..de9ac961dc8c 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -229,6 +229,7 @@ exclude_labels = [ "T-infra", "T-release", "requires-nightly", + "F-*", ] [autolabel."T-rustdoc"] diff --git a/typos.toml b/typos.toml index 758239ffe751..b9d9c6c3522c 100644 --- a/typos.toml +++ b/typos.toml @@ -50,6 +50,8 @@ unstalled = "unstalled" debug_aranges = "debug_aranges" DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME = "DNS_ERROR_INVAILD_VIRTUALIZATION_INSTANCE_NAME" EnzymeTypeTreeShiftIndiciesEq = "EnzymeTypeTreeShiftIndiciesEq" +EnzymeTypeTreeShiftIndiciesEqFn = "EnzymeTypeTreeShiftIndiciesEqFn" +shift_indicies_eq = "shift_indicies_eq" ERRNO_ACCES = "ERRNO_ACCES" ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS = "ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS" ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC = "ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC"