From 043bd767688281d574ea8cc0c368156021baa61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 27 Feb 2026 06:23:06 +0100 Subject: [PATCH 01/38] Make `layout_of` cycles fatal errors --- compiler/rustc_codegen_llvm/src/context.rs | 6 +----- .../src/interpret/eval_context.rs | 3 +-- .../src/hir_ty_lowering/cmse.rs | 3 +-- compiler/rustc_middle/src/ty/layout.rs | 6 +----- .../src/handle_cycle_error.rs | 4 +--- compiler/rustc_transmute/src/layout/tree.rs | 1 - .../html/templates/type_layout.html | 5 ----- tests/ui/layout/layout-cycle.rs | 1 - tests/ui/layout/layout-cycle.stderr | 16 ++------------ tests/ui/layout/post-mono-layout-cycle.stderr | 6 ------ ...ck-overflow-trait-infer-98842.64bit.stderr | 21 ------------------- .../sized/stack-overflow-trait-infer-98842.rs | 4 +--- ...> stack-overflow-trait-infer-98842.stderr} | 13 +++--------- 13 files changed, 11 insertions(+), 78 deletions(-) delete mode 100644 tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr rename tests/ui/sized/{stack-overflow-trait-infer-98842.32bit.stderr => stack-overflow-trait-infer-98842.stderr} (55%) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index e02c4ae6ea695..80d939a25701e 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1134,11 +1134,7 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { fn_abi_request: FnAbiRequest<'tcx>, ) -> ! { match err { - FnAbiError::Layout( - LayoutError::SizeOverflow(_) - | LayoutError::Cycle(_) - | LayoutError::InvalidSimd { .. }, - ) => { + FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) => { self.tcx.dcx().emit_fatal(Spanned { span, node: err }); } _ => match fn_abi_request { diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 0bfe012bfe7a4..04f0e7099d840 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -107,8 +107,7 @@ impl<'tcx, M: Machine<'tcx>> LayoutOfHelpers<'tcx> for InterpCx<'tcx, M> { | LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. } | LayoutError::TooGeneric(_) - | LayoutError::ReferencesError(_) - | LayoutError::Cycle(_) => {} + | LayoutError::ReferencesError(_) => {} } err_inval!(Layout(err)) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 58c296d92c24e..a1b169c6a1661 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -194,8 +194,7 @@ fn should_emit_layout_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError< | SizeOverflow(..) | InvalidSimd { .. } | NormalizationFailure(..) - | ReferencesError(..) - | Cycle(..) => { + | ReferencesError(..) => { false // not our job to report these } } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 4ca51c078bef5..46682abc823d8 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -260,8 +260,6 @@ pub enum LayoutError<'tcx> { NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>), /// A non-layout error is reported elsewhere. ReferencesError(ErrorGuaranteed), - /// A type has cyclic layout, i.e. the type contains itself without indirection. - Cycle(ErrorGuaranteed), } impl<'tcx> fmt::Display for LayoutError<'tcx> { @@ -286,7 +284,6 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { t, e.get_type_for_failure() ), - LayoutError::Cycle(_) => write!(f, "a cycle occurred during layout computation"), LayoutError::ReferencesError(_) => write!(f, "the type has an unknown layout"), } } @@ -358,8 +355,7 @@ impl<'tcx> SizeSkeleton<'tcx> { Err(err @ LayoutError::TooGeneric(_)) => err, // We can't extract SizeSkeleton info from other layout errors Err( - e @ LayoutError::Cycle(_) - | e @ LayoutError::Unknown(_) + e @ LayoutError::Unknown(_) | e @ LayoutError::SizeOverflow(_) | e @ LayoutError::InvalidSimd { .. } | e @ LayoutError::NormalizationFailure(..) diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index 5676669bf1c0e..22f8ac9837f6d 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -12,7 +12,6 @@ use rustc_middle::bug; use rustc_middle::queries::{QueryVTables, TaggedQueryKey}; use rustc_middle::query::Cycle; use rustc_middle::query::erase::erase_val; -use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{ErrorGuaranteed, Span}; @@ -203,8 +202,7 @@ fn layout_of<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> &'tcx ty::layout::L || create_cycle_error(tcx, &cycle), ); - let guar = diag.emit(); - tcx.arena.alloc(LayoutError::Cycle(guar)) + diag.emit().raise_fatal() } // item_and_field_ids should form a cycle where each field contains the diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 1202ed2384315..1bfe6e94cc3b6 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -282,7 +282,6 @@ pub(crate) mod rustc { | LayoutError::InvalidSimd { .. } | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, - LayoutError::Cycle(err) => Self::TypeError(*err), } } } diff --git a/src/librustdoc/html/templates/type_layout.html b/src/librustdoc/html/templates/type_layout.html index 49153d58fe98c..4d4222a34956a 100644 --- a/src/librustdoc/html/templates/type_layout.html +++ b/src/librustdoc/html/templates/type_layout.html @@ -60,11 +60,6 @@

{# #} Note: Encountered an error during type layout; {#+ #} the type failed to be normalized. {# #}

- {% when Err(LayoutError::Cycle(_)) %} -

{# #} - Note: Encountered an error during type layout; {#+ #} - the type's layout depended on the type's layout itself. {# #} -

{% when Err(LayoutError::InvalidSimd {..}) %}

{# #} Note: Encountered an error during type layout; {#+ #} diff --git a/tests/ui/layout/layout-cycle.rs b/tests/ui/layout/layout-cycle.rs index b38bd52c6ade9..846ce0882cad1 100644 --- a/tests/ui/layout/layout-cycle.rs +++ b/tests/ui/layout/layout-cycle.rs @@ -1,6 +1,5 @@ //@ build-fail //~^ ERROR: cycle detected when computing layout of -//~? ERROR: a cycle occurred during layout computation // Issue #111176 -- ensure that we do not emit ICE on layout cycles diff --git a/tests/ui/layout/layout-cycle.stderr b/tests/ui/layout/layout-cycle.stderr index e05ff614567c4..28c35d431226e 100644 --- a/tests/ui/layout/layout-cycle.stderr +++ b/tests/ui/layout/layout-cycle.stderr @@ -6,18 +6,6 @@ note: cycle used when const-evaluating + checking `core::mem::SizedTypePropertie --> $SRC_DIR/core/src/mem/mod.rs:LL:COL = 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[E0080]: a cycle occurred during layout computation - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - | - = note: evaluation of `> as std::mem::SizedTypeProperties>::SIZE` failed here - -note: the above error was encountered while instantiating `fn std::mem::size_of::>>` - --> $DIR/layout-cycle.rs:26:5 - | -LL | mem::size_of::>() - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0080, E0391. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/layout/post-mono-layout-cycle.stderr b/tests/ui/layout/post-mono-layout-cycle.stderr index 7f246b3d409ad..b9b1b988499e6 100644 --- a/tests/ui/layout/post-mono-layout-cycle.stderr +++ b/tests/ui/layout/post-mono-layout-cycle.stderr @@ -5,12 +5,6 @@ error[E0391]: cycle detected when computing layout of `Wrapper<()>` = note: cycle used when computing layout of `core::option::Option>` = 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 -note: the above error was encountered while instantiating `fn abi::<()>` - --> $DIR/post-mono-layout-cycle.rs:19:5 - | -LL | abi::(None); - | ^^^^^^^^^^^^^^ - error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr deleted file mode 100644 index d097b809b5698..0000000000000 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0391]: cycle detected when computing layout of `Foo` - | - = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... - = note: ...which again requires computing layout of `Foo`, completing the cycle -note: cycle used when const-evaluating + checking `_` - --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ - = 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[E0080]: a cycle occurred during layout computation - --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0080, E0391. -For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.rs b/tests/ui/sized/stack-overflow-trait-infer-98842.rs index 1c9f6c593f447..d6522e3cfb643 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.rs +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.rs @@ -2,8 +2,7 @@ // issue: rust-lang/rust#98842 //@ check-fail //@ edition:2021 -//@ stderr-per-bitwidth -//~^^^^^ ERROR cycle detected when computing layout of `Foo` +//~^^^^ ERROR cycle detected when computing layout of `Foo` // If the inner `Foo` is named through an associated type, // the "infinite size" error does not occur. @@ -12,6 +11,5 @@ struct Foo(<&'static Foo as ::core::ops::Deref>::Target); // and it will infinitely recurse somewhere trying to figure out the // size of this pointer (is my guess): const _: *const Foo = 0 as _; -//~^ ERROR a cycle occurred during layout computation pub fn main() {} diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr similarity index 55% rename from tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr rename to tests/ui/sized/stack-overflow-trait-infer-98842.stderr index d097b809b5698..5557a6fc45b89 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr @@ -3,19 +3,12 @@ error[E0391]: cycle detected when computing layout of `Foo` = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... = note: ...which again requires computing layout of `Foo`, completing the cycle note: cycle used when const-evaluating + checking `_` - --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 + --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ = 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[E0080]: a cycle occurred during layout computation - --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 - | -LL | const _: *const Foo = 0 as _; - | ^^^^^^^^^^^^^^^^^^^ evaluation of `_` failed here - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0080, E0391. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0391`. From 4fd1d9713af32df00514bb57e5e888b84d34291d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 16 Mar 2026 22:18:59 +0100 Subject: [PATCH 02/38] Skip ICE message for fatal errors --- src/tools/miri/src/eval.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 1e75df7d278fb..cf4f7d689ac22 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -10,6 +10,7 @@ use std::{iter, thread}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::FatalErrorMarker; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutCx}; @@ -477,7 +478,11 @@ pub fn eval_entry<'tcx>( let res: thread::Result> = panic::catch_unwind(AssertUnwindSafe(|| ecx.run_threads())); let res = res.unwrap_or_else(|panic_payload| { - ecx.handle_ice(); + // rustc "handles" some errors by unwinding with FatalErrorMarker + // (after emitting suitable diagnostics), so do not treat those as ICEs. + if !panic_payload.is::() { + ecx.handle_ice(); + } panic::resume_unwind(panic_payload) }); // Obtain the result of the execution. This is always an `Err`, but that doesn't necessarily From db30a36c8edb360eb16de681ff07972980dfbba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 16 Mar 2026 22:19:21 +0100 Subject: [PATCH 03/38] Update tests --- src/tools/miri/tests/fail/layout_cycle.rs | 3 +-- src/tools/miri/tests/fail/layout_cycle.stderr | 11 ++--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/tests/fail/layout_cycle.rs b/src/tools/miri/tests/fail/layout_cycle.rs index 3e0dd881db84e..8d5f1914d0c32 100644 --- a/src/tools/miri/tests/fail/layout_cycle.rs +++ b/src/tools/miri/tests/fail/layout_cycle.rs @@ -1,5 +1,4 @@ -//@error-in-other-file: a cycle occurred during layout computation -//~^ ERROR: cycle detected when computing layout of +//~ ERROR: cycle detected when computing layout of use std::mem; diff --git a/src/tools/miri/tests/fail/layout_cycle.stderr b/src/tools/miri/tests/fail/layout_cycle.stderr index dae6934931228..f8d555e5a10b8 100644 --- a/src/tools/miri/tests/fail/layout_cycle.stderr +++ b/src/tools/miri/tests/fail/layout_cycle.stderr @@ -9,13 +9,6 @@ LL | const SIZE: usize = intrinsics::size_of::(); | ^^^^^^^^^^^^^^^^^ = 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[E0080]: a cycle occurred during layout computation - --> RUSTLIB/core/src/mem/mod.rs:LL:CC - | -LL | const SIZE: usize = intrinsics::size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `> as std::mem::SizedTypeProperties>::SIZE` failed here - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0080, E0391. -For more information about an error, try `rustc --explain E0080`. +For more information about this error, try `rustc --explain E0391`. From 7d0577e95815194acf59cb3e876ca521f5638232 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 19 Feb 2026 11:03:14 +0000 Subject: [PATCH 04/38] ty_utils: lower tuples to `ScalableVector` repr Instead of just using regular struct lowering for these types, which results in an incorrect ABI (e.g. returning indirectly), use `BackendRepr::ScalableVector` which will lower to the correct type and be passed in registers. This also enables some simplifications for generating alloca of scalable vectors and greater re-use of `scalable_vector_parts`. A LLVM codegen test demonstrating the changed IR this generates is included in the next commit alongside some intrinsics that make these tuples usable. --- compiler/rustc_abi/src/layout.rs | 21 ++++++--- compiler/rustc_abi/src/lib.rs | 33 ++++++++++++-- compiler/rustc_codegen_gcc/src/builder.rs | 7 +-- compiler/rustc_codegen_llvm/src/builder.rs | 18 +++----- compiler/rustc_codegen_llvm/src/type_of.rs | 44 ++++++++++++++++++- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 4 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 15 +++---- .../rustc_codegen_ssa/src/traits/builder.rs | 2 +- compiler/rustc_middle/src/ty/sty.rs | 24 +++++++--- compiler/rustc_public/src/abi.rs | 5 +++ .../src/unstable/convert/stable/abi.rs | 25 +++++++++-- compiler/rustc_ty_utils/src/layout.rs | 32 ++++++-------- 12 files changed, 161 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index ca6128b6f1be4..cca1d499088f4 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -10,8 +10,8 @@ use tracing::{debug, trace}; use crate::{ AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer, - LayoutData, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding, - TargetDataLayout, Variants, WrappingRange, + LayoutData, Niche, NonZeroUsize, NumScalableVectors, Primitive, ReprOptions, Scalar, Size, + StructKind, TagEncoding, TargetDataLayout, Variants, WrappingRange, }; mod coroutine; @@ -204,13 +204,19 @@ impl LayoutCalculator { &self, element: F, count: u64, + number_of_vectors: NumScalableVectors, ) -> LayoutCalculatorResult where FieldIdx: Idx, VariantIdx: Idx, F: AsRef> + fmt::Debug, { - vector_type_layout(SimdVectorKind::Scalable, self.cx.data_layout(), element, count) + vector_type_layout( + SimdVectorKind::Scalable(number_of_vectors), + self.cx.data_layout(), + element, + count, + ) } pub fn simd_type( @@ -1526,7 +1532,7 @@ impl LayoutCalculator { enum SimdVectorKind { /// `#[rustc_scalable_vector]` - Scalable, + Scalable(NumScalableVectors), /// `#[repr(simd, packed)]` PackedFixed, /// `#[repr(simd)]` @@ -1559,9 +1565,10 @@ where let size = elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?; let (repr, align) = match kind { - SimdVectorKind::Scalable => { - (BackendRepr::SimdScalableVector { element, count }, dl.llvmlike_vector_align(size)) - } + SimdVectorKind::Scalable(number_of_vectors) => ( + BackendRepr::SimdScalableVector { element, count, number_of_vectors }, + 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. diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 253dff6f8e75c..4f3bd6ef208da 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1702,6 +1702,28 @@ impl AddressSpace { pub const ZERO: Self = AddressSpace(0); } +/// How many scalable vectors are in a `BackendRepr::ScalableVector`? +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +pub struct NumScalableVectors(pub u8); + +impl NumScalableVectors { + /// Returns a `NumScalableVector` for a non-tuple scalable vector (e.g. a single vector). + pub fn for_non_tuple() -> Self { + NumScalableVectors(1) + } + + // Returns `NumScalableVectors` for values of two through eight, which are a valid number of + // fields for a tuple of scalable vectors to have. `1` is a valid value of `NumScalableVectors` + // but not for a tuple which would have a field count. + pub fn from_field_count(count: usize) -> Option { + match count { + 2..8 => Some(NumScalableVectors(count as u8)), + _ => None, + } + } +} + /// The way we represent values to the backend /// /// Previously this was conflated with the "ABI" a type is given, as in the platform-specific ABI. @@ -1720,6 +1742,7 @@ pub enum BackendRepr { SimdScalableVector { element: Scalar, count: u64, + number_of_vectors: NumScalableVectors, }, SimdVector { element: Scalar, @@ -1826,8 +1849,12 @@ impl BackendRepr { BackendRepr::SimdVector { element: element.to_union(), count } } BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true }, - BackendRepr::SimdScalableVector { element, count } => { - BackendRepr::SimdScalableVector { element: element.to_union(), count } + BackendRepr::SimdScalableVector { element, count, number_of_vectors } => { + BackendRepr::SimdScalableVector { + element: element.to_union(), + count, + number_of_vectors, + } } } } @@ -2167,7 +2194,7 @@ impl LayoutData { } /// Returns `true` if the size of the type is only known at runtime. - pub fn is_runtime_sized(&self) -> bool { + pub fn is_scalable_vector(&self) -> bool { matches!(self.backend_repr, BackendRepr::SimdScalableVector { .. }) } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 3eb0fd95284a1..08964113b944a 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -24,7 +24,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ - FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, + LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, AtomicOrdering, Instance, Ty, TyCtxt}; use rustc_span::Span; @@ -943,8 +944,8 @@ 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 alloca_with_ty(&mut self, ty: TyAndLayout<'tcx>) -> RValue<'gcc> { + self.alloca(ty.layout.size, ty.layout.align.abi) } fn load(&mut self, pointee_ty: Type<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index f3508c10d1f61..9e59d7aa7c20a 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -7,8 +7,7 @@ pub(crate) mod autodiff; pub(crate) mod gpu_offload; use libc::{c_char, c_uint}; -use rustc_abi as abi; -use rustc_abi::{Align, Size, WrappingRange}; +use rustc_abi::{self as abi, Align, Size, WrappingRange}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -616,21 +615,14 @@ 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 { + fn alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> 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"), - }; + let scalable_vector_ty = layout.llvm_type(self.cx); 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); + let alloca = llvm::LLVMBuildAlloca(&bx.llbuilder, scalable_vector_ty, UNNAMED); + llvm::LLVMSetAlignment(alloca, layout.align.abi.bytes() as c_uint); alloca } } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index e586ed0dd6b07..6d0490e4a1f79 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -24,14 +24,54 @@ fn uncached_llvm_type<'a, 'tcx>( let element = layout.scalar_llvm_type_at(cx, element); return cx.type_vector(element, count); } - BackendRepr::SimdScalableVector { ref element, count } => { + BackendRepr::SimdScalableVector { ref element, count, number_of_vectors } => { let element = if element.is_bool() { cx.type_i1() } else { layout.scalar_llvm_type_at(cx, *element) }; - return cx.type_scalable_vector(element, count); + let vector_type = cx.type_scalable_vector(element, count); + return match number_of_vectors.0 { + 1 => vector_type, + 2 => cx.type_struct(&[vector_type, vector_type], false), + 3 => cx.type_struct(&[vector_type, vector_type, vector_type], false), + 4 => cx.type_struct(&[vector_type, vector_type, vector_type, vector_type], false), + 5 => cx.type_struct( + &[vector_type, vector_type, vector_type, vector_type, vector_type], + false, + ), + 6 => cx.type_struct( + &[vector_type, vector_type, vector_type, vector_type, vector_type, vector_type], + false, + ), + 7 => cx.type_struct( + &[ + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + ], + false, + ), + 8 => cx.type_struct( + &[ + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + ], + false, + ), + _ => bug!("`#[rustc_scalable_vector]` tuple struct with too many fields"), + }; } BackendRepr::Memory { .. } | BackendRepr::ScalarPair(..) => {} } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 2f93f688c316d..60ab13dbc6f76 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -438,8 +438,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if operand.layout.ty.is_scalable_vector() && bx.sess().target.arch == rustc_target::spec::Arch::AArch64 { - let (count, element_ty) = - operand.layout.ty.scalable_vector_element_count_and_type(bx.tcx()); + let (count, element_ty, _) = + operand.layout.ty.scalable_vector_parts(bx.tcx()).unwrap(); // i.e. `` when `N != 16` if element_ty.is_bool() && count != 16 { return; diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index d62e622b6fed3..53518fd816f31 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -1,3 +1,5 @@ +use std::ops::Deref as _; + use rustc_abi::{ Align, BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants, }; @@ -109,8 +111,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx: &mut Bx, layout: TyAndLayout<'tcx>, ) -> Self { - if layout.is_runtime_sized() { - Self::alloca_runtime_sized(bx, layout) + if layout.deref().is_scalable_vector() { + Self::alloca_scalable(bx, layout) } else { Self::alloca_size(bx, layout.size, layout) } @@ -151,16 +153,11 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } } - fn alloca_runtime_sized>( + fn alloca_scalable>( 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) + PlaceValue::new_sized(bx.alloca_with_ty(layout), layout.align.abi).with_type(layout) } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 05e94b8019f49..5092f28a33f7b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -235,7 +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 alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> 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_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 0d047b348d9e1..621ceeffac658 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -7,7 +7,7 @@ use std::debug_assert_matches; use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; -use rustc_abi::{FIRST_VARIANT, FieldIdx, ScalableElt, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, NumScalableVectors, ScalableElt, VariantIdx}; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::LangItem; @@ -1261,17 +1261,27 @@ impl<'tcx> Ty<'tcx> { } } - pub fn scalable_vector_element_count_and_type(self, tcx: TyCtxt<'tcx>) -> (u16, Ty<'tcx>) { + pub fn scalable_vector_parts( + self, + tcx: TyCtxt<'tcx>, + ) -> Option<(u16, Ty<'tcx>, NumScalableVectors)> { let Adt(def, args) = self.kind() else { - bug!("`scalable_vector_size_and_type` called on invalid type") + return None; }; - let Some(ScalableElt::ElementCount(element_count)) = def.repr().scalable else { - bug!("`scalable_vector_size_and_type` called on non-scalable vector type"); + let (num_vectors, vec_def) = match def.repr().scalable? { + ScalableElt::ElementCount(_) => (NumScalableVectors::for_non_tuple(), *def), + ScalableElt::Container => ( + NumScalableVectors::from_field_count(def.non_enum_variant().fields.len())?, + def.non_enum_variant().fields[FieldIdx::ZERO].ty(tcx, args).ty_adt_def()?, + ), }; - let variant = def.non_enum_variant(); + let Some(ScalableElt::ElementCount(element_count)) = vec_def.repr().scalable else { + return None; + }; + let variant = vec_def.non_enum_variant(); assert_eq!(variant.fields.len(), 1); let field_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args); - (element_count, field_ty) + Some((element_count, field_ty, num_vectors)) } pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 1403e57a7e6a9..4a780d652df81 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -232,6 +232,10 @@ pub enum TagEncoding { }, } +/// How many scalable vectors are in a `ValueAbi::ScalableVector`? +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct NumScalableVectors(pub(crate) u8); + /// Describes how values of the type are passed by target ABIs, /// in terms of categories of C types there are ABI rules for. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] @@ -245,6 +249,7 @@ pub enum ValueAbi { ScalableVector { element: Scalar, count: u64, + number_of_vectors: NumScalableVectors, }, Aggregate { /// If true, the size is exact, otherwise it's only a lower bound. diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index b3edc6194c307..d8c4cee7abbe4 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -10,8 +10,9 @@ use rustc_target::callconv; use crate::abi::{ AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength, - IntegerType, Layout, LayoutShape, PassMode, Primitive, ReprFlags, ReprOptions, Scalar, - TagEncoding, TyAndLayout, ValueAbi, VariantFields, VariantsShape, WrappingRange, + IntegerType, Layout, LayoutShape, NumScalableVectors, PassMode, Primitive, ReprFlags, + ReprOptions, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantFields, VariantsShape, + WrappingRange, }; use crate::compiler_interface::BridgeTys; use crate::target::MachineSize as Size; @@ -249,6 +250,18 @@ impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding { } } +impl<'tcx> Stable<'tcx> for rustc_abi::NumScalableVectors { + type T = NumScalableVectors; + + fn stable<'cx>( + &self, + _tables: &mut Tables<'cx, BridgeTys>, + _cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + NumScalableVectors(self.0) + } +} + impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { type T = ValueAbi; @@ -265,8 +278,12 @@ 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::SimdScalableVector { element, count } => { - ValueAbi::ScalableVector { element: element.stable(tables, cx), count } + rustc_abi::BackendRepr::SimdScalableVector { element, count, number_of_vectors } => { + ValueAbi::ScalableVector { + element: element.stable(tables, cx), + count, + number_of_vectors: number_of_vectors.stable(tables, cx), + } } rustc_abi::BackendRepr::Memory { sized } => ValueAbi::Aggregate { sized }, } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 391f50edf23fa..136df923ee47a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -4,8 +4,8 @@ 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, ScalableElt, Scalar, Size, StructKind, - TagEncoding, VariantIdx, Variants, WrappingRange, + LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, + VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; use rustc_hir as hir; @@ -572,30 +572,26 @@ fn layout_of_uncached<'tcx>( // ```rust (ignore, example) // #[rustc_scalable_vector(3)] // struct svuint32_t(u32); + // + // #[rustc_scalable_vector] + // struct svuint32x2_t(svuint32_t, svuint32_t); // ``` - 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)) + ty::Adt(def, _args) if def.repr().scalable() => { + let Some((element_count, element_ty, number_of_vectors)) = + ty.scalable_vector_parts(tcx) 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"); + .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))? + map_layout(cx.calc.scalable_vector_type( + element_layout, + element_count as u64, + number_of_vectors, + ))? } // SIMD vector types. From 93dfff5c9065fffebc1587949235cf4ddb7eabd4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 19 Feb 2026 11:03:14 +0000 Subject: [PATCH 05/38] cg_llvm: `sve_tuple_{create,get,set}` intrinsics Clang changed to representing tuples of scalable vectors as structs rather than as wide vectors (that is, scalable vector types where the `N` part of the `` type was multiplied by the number of vectors). rustc mirrored this in the initial implementation of scalable vectors. Earlier versions of our patches used the wide vector representation and our intrinsic patches used the legacy `llvm.aarch64.sve.tuple.{create,get,set}{2,3,4}` intrinsics for creating these tuples/getting/setting the vectors, which were only supported due to LLVM's `AutoUpgrade` pass converting these intrinsics into `llvm.vector.insert`. `AutoUpgrade` only supports these legacy intrinsics with the wide vector representation. With the current struct representation, Clang has special handling in codegen for generating `insertvalue`/`extractvalue` instructions for these operations, which must be replicated by rustc's codegen for our intrinsics to use. This patch implements new intrinsics in `core::intrinsics::scalable` (mirroring the structure of `core::intrinsics::simd`) which rustc lowers to the appropriate `insertvalue`/`extractvalue` instructions. --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 112 +++++++++++++++++- .../rustc_hir_analysis/src/check/intrinsic.rs | 6 + compiler/rustc_span/src/symbol.rs | 5 + .../src/intrinsics/{simd.rs => simd/mod.rs} | 2 + library/core/src/intrinsics/simd/scalable.rs | 76 ++++++++++++ .../scalable-vectors/tuple-intrinsics.rs | 100 ++++++++++++++++ .../simd/masked-load-store-check-fail.stderr | 4 +- triagebot.toml | 2 +- 8 files changed, 303 insertions(+), 4 deletions(-) rename library/core/src/intrinsics/{simd.rs => simd/mod.rs} (99%) create mode 100644 library/core/src/intrinsics/simd/scalable.rs create mode 100644 tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 39bf4c10dab18..ad2c23c99820b 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -3,7 +3,8 @@ use std::ffi::c_uint; use std::{assert_matches, ptr}; use rustc_abi::{ - Align, BackendRepr, ExternAbi, Float, HasDataLayout, Primitive, Size, WrappingRange, + Align, BackendRepr, ExternAbi, Float, HasDataLayout, NumScalableVectors, Primitive, Size, + WrappingRange, }; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -605,6 +606,115 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.pointercast(val, self.type_ptr()) } + sym::sve_tuple_create2 => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + let tuple_ty = self.layout_of(fn_args.type_at(1)); + assert_matches!( + tuple_ty.backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(2), + .. + } + ); + let ret = self.const_poison(self.backend_type(tuple_ty)); + let ret = self.insert_value(ret, args[0].immediate(), 0); + self.insert_value(ret, args[1].immediate(), 1) + } + + sym::sve_tuple_create3 => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + let tuple_ty = self.layout_of(fn_args.type_at(1)); + assert_matches!( + tuple_ty.backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(3), + .. + } + ); + let ret = self.const_poison(self.backend_type(tuple_ty)); + let ret = self.insert_value(ret, args[0].immediate(), 0); + let ret = self.insert_value(ret, args[1].immediate(), 1); + self.insert_value(ret, args[2].immediate(), 2) + } + + sym::sve_tuple_create4 => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + let tuple_ty = self.layout_of(fn_args.type_at(1)); + assert_matches!( + tuple_ty.backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(4), + .. + } + ); + let ret = self.const_poison(self.backend_type(tuple_ty)); + let ret = self.insert_value(ret, args[0].immediate(), 0); + let ret = self.insert_value(ret, args[1].immediate(), 1); + let ret = self.insert_value(ret, args[2].immediate(), 2); + self.insert_value(ret, args[3].immediate(), 3) + } + + sym::sve_tuple_get => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8), + .. + } + ); + assert_matches!( + self.layout_of(fn_args.type_at(1)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + self.extract_value( + args[0].immediate(), + fn_args.const_at(2).to_leaf().to_i32() as u64, + ) + } + + sym::sve_tuple_set => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8), + .. + } + ); + assert_matches!( + self.layout_of(fn_args.type_at(1)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + self.insert_value( + args[0].immediate(), + args[1].immediate(), + fn_args.const_at(2).to_leaf().to_i32() as u64, + ) + } + _ if name.as_str().starts_with("simd_") => { // Unpack non-power-of-2 #[repr(packed, simd)] arguments. // This gives them the expected layout of a regular #[repr(simd)] vector. diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index b1dc593331c6c..ca57921089fae 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -783,6 +783,12 @@ pub(crate) fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_const_generic => (2, 1, vec![param(0), param(0)], param(1)), + sym::sve_tuple_create2 => (2, 0, vec![param(0), param(0)], param(1)), + sym::sve_tuple_create3 => (2, 0, vec![param(0), param(0), param(0)], param(1)), + sym::sve_tuple_create4 => (2, 0, vec![param(0), param(0), param(0), param(0)], param(1)), + sym::sve_tuple_get => (2, 1, vec![param(0)], param(1)), + sym::sve_tuple_set => (2, 1, vec![param(0), param(1)], param(0)), + sym::atomic_cxchg | sym::atomic_cxchgweak => ( 1, 2, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7b359dcd6b252..e44e9f4ea1ce7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1977,6 +1977,11 @@ symbols! { suggestion, super_let, supertrait_item_shadowing, + sve_tuple_create2, + sve_tuple_create3, + sve_tuple_create4, + sve_tuple_get, + sve_tuple_set, sym, sync, synthetic, diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd/mod.rs similarity index 99% rename from library/core/src/intrinsics/simd.rs rename to library/core/src/intrinsics/simd/mod.rs index ae86690dc418d..084d8a3f1f247 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd/mod.rs @@ -2,6 +2,8 @@ //! //! In this module, a "vector" is any `repr(simd)` type. +pub mod scalable; + use crate::marker::ConstParamTy; /// Inserts an element into a vector, returning the updated vector. diff --git a/library/core/src/intrinsics/simd/scalable.rs b/library/core/src/intrinsics/simd/scalable.rs new file mode 100644 index 0000000000000..a569d1ffbc548 --- /dev/null +++ b/library/core/src/intrinsics/simd/scalable.rs @@ -0,0 +1,76 @@ +//! Scalable vector compiler intrinsics. +//! +//! In this module, a "vector" is any `#[rustc_scalable_vector]`-annotated type. + +/// Create a tuple of two vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svcreate2*` builtins. +#[cfg(target_arch = "aarch64")] +#[rustc_nounwind] +#[rustc_intrinsic] +#[target_feature(enable = "sve")] +pub unsafe fn sve_tuple_create2(x0: SVec, x1: SVec) -> SVecTup; + +/// Create a tuple of three vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svcreate3*` builtins. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[target_feature(enable = "sve")] +pub unsafe fn sve_tuple_create3(x0: SVec, x1: SVec, x2: SVec) -> SVecTup; + +/// Create a tuple of four vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svcreate4*` builtins. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[target_feature(enable = "sve")] +pub unsafe fn sve_tuple_create4(x0: SVec, x1: SVec, x2: SVec, x3: SVec) -> SVecTup; + +/// Get one vector from a tuple of vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svget*` builtins. +/// +/// # Safety +/// +/// `IDX` must be in-bounds of the tuple. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[target_feature(enable = "sve")] +pub unsafe fn sve_tuple_get(tuple: SVecTup) -> SVec; + +/// Change one vector in a tuple of vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svset*` builtins. +/// +/// # Safety +/// +/// `IDX` must be in-bounds of the tuple. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[target_feature(enable = "sve")] +pub unsafe fn sve_tuple_set(tuple: SVecTup, x: SVec) -> SVecTup; diff --git a/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs new file mode 100644 index 0000000000000..e19fc40cb9d67 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs @@ -0,0 +1,100 @@ +//@ build-pass +//@ only-aarch64 +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(abi_unadjusted, core_intrinsics, link_llvm_intrinsics, rustc_attrs)] + +// Tests that tuples of scalable vectors are passed as immediates and that the intrinsics for +// creating/getting/setting tuples of scalable vectors generate the correct assembly + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svfloat32_t(f32); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +pub struct svfloat32x2_t(svfloat32_t, svfloat32_t); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +pub struct svfloat32x3_t(svfloat32_t, svfloat32_t, svfloat32_t); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +pub struct svfloat32x4_t(svfloat32_t, svfloat32_t, svfloat32_t, svfloat32_t); + +#[inline(never)] +#[target_feature(enable = "sve")] +pub fn svdup_n_f32(op: f32) -> svfloat32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4f32")] + fn _svdup_n_f32(op: f32) -> svfloat32_t; + } + unsafe { _svdup_n_f32(op) } +} + +// CHECK: define { , } @svcreate2_f32( %x0, %x1) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svcreate2_f32(x0: svfloat32_t, x1: svfloat32_t) -> svfloat32x2_t { + // CHECK: %1 = insertvalue { , } poison, %x0, 0 + // CHECK-NEXT: %2 = insertvalue { , } %1, %x1, 1 + unsafe { std::intrinsics::simd::scalable::sve_tuple_create2(x0, x1) } +} + +// CHECK: define { , , } @svcreate3_f32( %x0, %x1, %x2) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svcreate3_f32(x0: svfloat32_t, x1: svfloat32_t, x2: svfloat32_t) -> svfloat32x3_t { + // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate3_f32 + // CHECK: %1 = insertvalue { , , } poison, %x0, 0 + // CHECK-NEXT: %2 = insertvalue { , , } %1, %x1, 1 + // CHECK-NEXT: %3 = insertvalue { , , } %2, %x2, 2 + unsafe { std::intrinsics::simd::scalable::sve_tuple_create3(x0, x1, x2) } +} + +// CHECK: define { , , , } @svcreate4_f32( %x0, %x1, %x2, %x3) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svcreate4_f32( + x0: svfloat32_t, + x1: svfloat32_t, + x2: svfloat32_t, + x3: svfloat32_t, +) -> svfloat32x4_t { + // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate4_f32 + // CHECK: %1 = insertvalue { , , , } poison, %x0, 0 + // CHECK-NEXT: %2 = insertvalue { , , , } %1, %x1, 1 + // CHECK-NEXT: %3 = insertvalue { , , , } %2, %x2, 2 + // CHECK-NEXT: %4 = insertvalue { , , , } %3, %x3, 3 + unsafe { std::intrinsics::simd::scalable::sve_tuple_create4(x0, x1, x2, x3) } +} + +// CHECK: define @svget2_f32({ , } %tup) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svget2_f32(tup: svfloat32x2_t) -> svfloat32_t { + // CHECK: %1 = extractvalue { , } %tup, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_get::<_, _, { IDX }>(tup) } +} + +// CHECK: define { , } @svset2_f32({ , } %tup, %x) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svset2_f32(tup: svfloat32x2_t, x: svfloat32_t) -> svfloat32x2_t { + // CHECK: %1 = insertvalue { , } %tup, %x, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_set::<_, _, { IDX }>(tup, x) } +} + +// This function exists only so there are calls to the generic functions +#[target_feature(enable = "sve")] +pub fn test() { + let x = svdup_n_f32(2f32); + let tup = svcreate2_f32(x, x); + let x = svget2_f32::<0>(tup); + let tup = svset2_f32::<0>(tup, x); +} diff --git a/tests/ui/simd/masked-load-store-check-fail.stderr b/tests/ui/simd/masked-load-store-check-fail.stderr index 4e63d04a3b158..037855c8ec9ca 100644 --- a/tests/ui/simd/masked-load-store-check-fail.stderr +++ b/tests/ui/simd/masked-load-store-check-fail.stderr @@ -21,7 +21,7 @@ LL | | Simd::([9; 4]), LL | | ); | |_________^ note: function defined here - --> $SRC_DIR/core/src/intrinsics/simd.rs:LL:COL + --> $SRC_DIR/core/src/intrinsics/simd/mod.rs:LL:COL error[E0308]: mismatched types --> $DIR/masked-load-store-check-fail.rs:25:13 @@ -46,7 +46,7 @@ LL | | default, LL | | ); | |_________^ note: function defined here - --> $SRC_DIR/core/src/intrinsics/simd.rs:LL:COL + --> $SRC_DIR/core/src/intrinsics/simd/mod.rs:LL:COL error: aborting due to 2 previous errors diff --git a/triagebot.toml b/triagebot.toml index f99d700310dfe..719fe2a75c82b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1077,7 +1077,7 @@ cc = ["@Amanieu", "@folkertdev", "@sayantn"] message = "Some changes occurred in `std_detect`" cc = ["@Amanieu", "@folkertdev", "@sayantn"] -[mentions."library/core/src/intrinsics/simd.rs"] +[mentions."library/core/src/intrinsics/simd/mod.rs"] message = """ Some changes occurred to the platform-builtins intrinsics. Make sure the LLVM backend as well as portable-simd gets adapted for the changes. From a05afe5b6750e5324324d8ef69dba89095840eb7 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 26 Feb 2026 15:35:20 +0000 Subject: [PATCH 06/38] cg_llvm/debuginfo: scalable vectors Generate debuginfo for scalable vectors, following the structure that Clang generates for scalable vectors. --- compiler/rustc_codegen_gcc/src/common.rs | 4 + compiler/rustc_codegen_llvm/src/common.rs | 4 + .../src/debuginfo/dwarf_const.rs | 8 + .../src/debuginfo/metadata.rs | 120 +++++++++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 17 ++ .../rustc_codegen_ssa/src/traits/consts.rs | 1 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 36 ++++- .../scalable-vectors/debuginfo-tuples-x2.rs | 149 ++++++++++++++++++ .../scalable-vectors/debuginfo-tuples-x3.rs | 149 ++++++++++++++++++ .../scalable-vectors/debuginfo-tuples-x4.rs | 149 ++++++++++++++++++ .../scalable-vectors/debuginfo.rs | 123 +++++++++++++++ 11 files changed, 754 insertions(+), 6 deletions(-) create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo.rs diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 9e548ac0a8b01..dd0064d34bc4a 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -145,6 +145,10 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i64(&self, i: i64) -> RValue<'gcc> { + self.const_int(self.type_i64(), i) + } + fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> { self.gcc_int(typ, int) } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index a134e97cc8915..dadf8e9e7d5fa 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -159,6 +159,10 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i64(&self, i: i64) -> &'ll Value { + self.const_int(self.type_i64(), i as i64) + } + fn const_int(&self, t: &'ll Type, i: i64) -> &'ll Value { debug_assert!( self.type_kind(t) == TypeKind::Integer, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs index 52d04625749b9..1172660af4a29 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs @@ -35,6 +35,14 @@ declare_constant!(DW_OP_plus_uconst: u64); /// Double-checked by a static assertion in `RustWrapper.cpp`. #[allow(non_upper_case_globals)] pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_constu: u64 = 0x10; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_minus: u64 = 0x1c; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_mul: u64 = 0x1e; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_bregx: u64 = 0x92; // It describes the actual value of a source variable which might not exist in registers or in memory. #[allow(non_upper_case_globals)] pub(crate) const DW_OP_stack_value: u64 = 0x9f; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 04c0b6953290c..25307823b37c7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use std::{iter, ptr}; use libc::{c_longlong, c_uint}; -use rustc_abi::{Align, Size}; +use rustc_abi::{Align, Layout, NumScalableVectors, Size}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; use rustc_hir::def::{CtorKind, DefKind}; @@ -16,12 +16,12 @@ use rustc_middle::ty::layout::{ HasTypingEnv, LayoutOf, TyAndLayout, WIDE_PTR_ADDR, WIDE_PTR_EXTRA, }; use rustc_middle::ty::{ - self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility, + self, AdtDef, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility, }; use rustc_session::config::{self, DebugInfo, Lto}; use rustc_span::{DUMMY_SP, FileName, RemapPathScopeComponents, SourceFile, Span, Symbol, hygiene}; use rustc_symbol_mangling::typeid_for_trait_ref; -use rustc_target::spec::DebuginfoKind; +use rustc_target::spec::{Arch, DebuginfoKind}; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -33,7 +33,7 @@ use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_na use super::utils::{DIB, debug_context, get_namespace_for_item, is_node_local_to_unit}; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::metadata::type_map::build_type_with_children; -use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; +use crate::debuginfo::utils::{WidePtrKind, create_DIArray, wide_pointer_kind}; use crate::debuginfo::{DIBuilderExt, dwarf_const}; use crate::llvm::debuginfo::{ DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, @@ -1039,6 +1039,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( span: Span, ) -> DINodeCreationResult<'ll> { let struct_type = unique_type_id.expect_ty(); + let ty::Adt(adt_def, _) = struct_type.kind() else { bug!("build_struct_type_di_node() called with non-struct-type: {:?}", struct_type); }; @@ -1051,6 +1052,21 @@ fn build_struct_type_di_node<'ll, 'tcx>( } else { None }; + let name = compute_debuginfo_type_name(cx.tcx, struct_type, false); + + if struct_type.is_scalable_vector() { + let parts = struct_type.scalable_vector_parts(cx.tcx).unwrap(); + return build_scalable_vector_di_node( + cx, + unique_type_id, + name, + *adt_def, + parts, + struct_type_and_layout.layout, + def_location, + containing_scope, + ); + } type_map::build_type_with_children( cx, @@ -1058,7 +1074,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( cx, Stub::Struct, unique_type_id, - &compute_debuginfo_type_name(cx.tcx, struct_type, false), + &name, def_location, size_and_align_of(struct_type_and_layout), Some(containing_scope), @@ -1101,6 +1117,100 @@ fn build_struct_type_di_node<'ll, 'tcx>( ) } +/// Generate debuginfo for a `#[rustc_scalable_vector]` type. +/// +/// Debuginfo for a scalable vector uses a derived type based on a composite type. The composite +/// type has the `DIFlagVector` flag set and is based on the element type of the scalable vector. +/// The composite type has a subrange from 0 to an expression that calculates the number of +/// elements in the vector. +/// +/// ```text,ignore +/// !1 = !DIDerivedType(tag: DW_TAG_typedef, name: "svint16_t", ..., baseType: !2, ...) +/// !2 = !DICompositeType(tag: DW_TAG_array_type, baseType: !3, ..., flags: DIFlagVector, elements: !4) +/// !3 = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) +/// !4 = !{!5} +/// !5 = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) +/// ``` +/// +/// See the `CodegenType::CreateType(const BuiltinType *BT)` implementation in Clang for how this +/// is generated for C and C++. +fn build_scalable_vector_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, + name: String, + adt_def: AdtDef<'tcx>, + (element_count, element_ty, number_of_vectors): (u16, Ty<'tcx>, NumScalableVectors), + layout: Layout<'tcx>, + def_location: Option>, + containing_scope: &'ll DIScope, +) -> DINodeCreationResult<'ll> { + use dwarf_const::{DW_OP_bregx, DW_OP_constu, DW_OP_minus, DW_OP_mul}; + assert!(adt_def.repr().scalable()); + // This logic is specific to AArch64 for the moment, but can be extended for other architectures + // later. + assert_matches!(cx.tcx.sess.target.arch, Arch::AArch64); + + let (file_metadata, line_number) = if let Some(def_location) = def_location { + (def_location.0, def_location.1) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + + let (bitstride, element_di_node) = if element_ty.is_bool() { + (Some(llvm::LLVMValueAsMetadata(cx.const_i64(1))), type_di_node(cx, cx.tcx.types.u8)) + } else { + (None, type_di_node(cx, element_ty)) + }; + + let number_of_elements: u64 = (element_count as u64) * (number_of_vectors.0 as u64); + let number_of_elements_per_vg = number_of_elements / 2; + let mut expr = smallvec::SmallVec::<[u64; 9]>::new(); + // `($number_of_elements_per_vector_granule * (value_of_register(AArch64::VG) + 0)) - 1` + expr.push(DW_OP_constu); // Push a constant onto the stack + expr.push(number_of_elements_per_vg); + expr.push(DW_OP_bregx); // Push the value of a register + offset on to the stack + expr.push(/* AArch64::VG */ 46u64); + expr.push(0u64); + expr.push(DW_OP_mul); // Multiply top two values on stack + expr.push(DW_OP_constu); // Push a constant onto the stack + expr.push(1u64); + expr.push(DW_OP_minus); // Subtract top two values on stack + + let di_builder = DIB(cx); + let metadata = unsafe { + let upper = llvm::LLVMDIBuilderCreateExpression(di_builder, expr.as_ptr(), expr.len()); + let subrange = llvm::LLVMRustDIGetOrCreateSubrange( + di_builder, + /* CountNode */ None, + llvm::LLVMValueAsMetadata(cx.const_i64(0)), + upper, + /* Stride */ None, + ); + let subscripts = create_DIArray(di_builder, &[Some(subrange)]); + let vector_ty = llvm::LLVMRustDICreateVectorType( + di_builder, + /* Size */ 0, + layout.align.bits() as u32, + element_di_node, + subscripts, + bitstride, + ); + llvm::LLVMDIBuilderCreateTypedef( + di_builder, + vector_ty, + name.as_ptr(), + name.len(), + file_metadata, + line_number, + Some(containing_scope), + layout.align.bits() as u32, + ) + }; + + debug_context(cx).type_map.insert(unique_type_id, metadata); + DINodeCreationResult { di_node: metadata, already_stored_in_typemap: true } +} + //=----------------------------------------------------------------------------- // Tuples //=----------------------------------------------------------------------------- diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7355d11367920..0ad74c9ca43a7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2302,6 +2302,23 @@ unsafe extern "C" { Params: Option<&'a DIArray>, ); + pub(crate) fn LLVMRustDIGetOrCreateSubrange<'a>( + Builder: &DIBuilder<'a>, + CountNode: Option<&'a Metadata>, + LB: &'a Metadata, + UB: &'a Metadata, + Stride: Option<&'a Metadata>, + ) -> &'a Metadata; + + pub(crate) fn LLVMRustDICreateVectorType<'a>( + Builder: &DIBuilder<'a>, + Size: u64, + AlignInBits: u32, + Type: &'a DIType, + Subscripts: &'a DIArray, + BitStride: Option<&'a Metadata>, + ) -> &'a Metadata; + pub(crate) fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>( Location: &'a DILocation, BD: c_uint, diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 4178a9742e268..22784a8868ab5 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -20,6 +20,7 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_i8(&self, i: i8) -> Self::Value; fn const_i16(&self, i: i16) -> Self::Value; fn const_i32(&self, i: i32) -> Self::Value; + fn const_i64(&self, i: i64) -> Self::Value; fn const_int(&self, t: Self::Type, i: i64) -> Self::Value; fn const_u8(&self, i: u8) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 63ff0b2a0a0df..f0cda4493c851 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -70,6 +70,10 @@ using namespace llvm::object; // This opcode is an LLVM detail that could hypothetically change (?), so // verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM. static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000); +static_assert(dwarf::DW_OP_constu == 0x10); +static_assert(dwarf::DW_OP_minus == 0x1c); +static_assert(dwarf::DW_OP_mul == 0x1e); +static_assert(dwarf::DW_OP_bregx == 0x92); static_assert(dwarf::DW_OP_stack_value == 0x9f); static LLVM_THREAD_LOCAL char *LastError; @@ -731,7 +735,7 @@ extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, } template DIT *unwrapDIPtr(LLVMMetadataRef Ref) { - return (DIT *)(Ref ? unwrap(Ref) : nullptr); + return (DIT *)(Ref ? unwrap(Ref) : nullptr); } #define DIDescriptor DIScope @@ -1207,6 +1211,36 @@ extern "C" void LLVMRustDICompositeTypeReplaceArrays( DINodeArray(unwrap(Params))); } +// LLVM's C FFI bindings don't expose the overload of `GetOrCreateSubrange` +// which takes a metadata node as the upper bound. +extern "C" LLVMMetadataRef +LLVMRustDIGetOrCreateSubrange(LLVMDIBuilderRef Builder, + LLVMMetadataRef CountNode, LLVMMetadataRef LB, + LLVMMetadataRef UB, LLVMMetadataRef Stride) { + return wrap(unwrap(Builder)->getOrCreateSubrange( + unwrapDI(CountNode), unwrapDI(LB), + unwrapDI(UB), unwrapDI(Stride))); +} + +// LLVM's CI FFI bindings don't expose the `BitStride` parameter of +// `createVectorType`. +extern "C" LLVMMetadataRef +LLVMRustDICreateVectorType(LLVMDIBuilderRef Builder, uint64_t Size, + uint32_t AlignInBits, LLVMMetadataRef Type, + LLVMMetadataRef Subscripts, + LLVMMetadataRef BitStride) { +#if LLVM_VERSION_GE(22, 0) + return wrap(unwrap(Builder)->createVectorType( + Size, AlignInBits, unwrapDI(Type), + DINodeArray(unwrapDI(Subscripts)), + unwrapDI(BitStride))); +#else + return wrap(unwrap(Builder)->createVectorType( + Size, AlignInBits, unwrapDI(Type), + DINodeArray(unwrapDI(Subscripts)))); +#endif +} + extern "C" LLVMMetadataRef LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location, unsigned BD) { diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs new file mode 100644 index 0000000000000..1aaba621d0e10 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs @@ -0,0 +1,149 @@ +//@ only-aarch64 +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint8x2_t(svint8_t, svint8_t); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint8x2_t(svuint8_t, svuint8_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint16x2_t(svint16_t, svint16_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint16x2_t(svuint16_t, svuint16_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint32x2_t(svint32_t, svint32_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint32x2_t(svuint32_t, svuint32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint64x2_t(svint64_t, svint64_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint64x2_t(svuint64_t, svuint64_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat32x2_t(svfloat32_t, svfloat32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat64x2_t(svfloat64_t, svfloat64_t); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svint8x2_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x2:[0-9]+]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS8x2]] = !{![[REALELTS8x2:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8x2]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 16, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s8: svint8x2_t; + + // CHECK-DAG: name: "svuint8x2_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x2]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + let u8: svuint8x2_t; + + // CHECK-DAG: name: "svint16x2_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x2:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16x2]] = !{![[REALELTS16x2:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16x2]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 8, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16x2_t; + + // CHECK-DAG: name: "svuint16x2_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x2]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16x2_t; + + // CHECK-DAG: name: "svint32x2_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x2:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32x2]] = !{![[REALELTS32x2:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32x2]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32x2_t; + + // CHECK-DAG: name: "svuint32x2_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x2]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32x2_t; + + // CHECK-DAG: name: "svint64x2_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x2_64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS1x2_64]] = !{![[REALELTS1x2_64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS1x2_64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 2, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64x2_t; + + // CHECK-DAG: name: "svuint64x2_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x2_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64x2_t; + + // CHECK: name: "svfloat32x2_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x2]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32x2_t; + + // CHECK: name: "svfloat64x2_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x2_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64x2_t; +} diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs new file mode 100644 index 0000000000000..b19051e2c743d --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs @@ -0,0 +1,149 @@ +//@ only-aarch64 +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint8x3_t(svint8_t, svint8_t, svint8_t); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint8x3_t(svuint8_t, svuint8_t, svuint8_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint16x3_t(svint16_t, svint16_t, svint16_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint16x3_t(svuint16_t, svuint16_t, svuint16_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint32x3_t(svint32_t, svint32_t, svint32_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint32x3_t(svuint32_t, svuint32_t, svuint32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint64x3_t(svint64_t, svint64_t, svint64_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint64x3_t(svuint64_t, svuint64_t, svuint64_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat32x3_t(svfloat32_t, svfloat32_t, svfloat32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat64x3_t(svfloat64_t, svfloat64_t, svfloat64_t); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svint8x3_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x3:[0-9]+]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS8x3]] = !{![[REALELTS8x3:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8x3]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 24, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s8: svint8x3_t; + + // CHECK-DAG: name: "svuint8x3_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x3]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + let u8: svuint8x3_t; + + // CHECK-DAG: name: "svint16x3_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x3:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16x3]] = !{![[REALELTS16x3:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16x3]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 12, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16x3_t; + + // CHECK-DAG: name: "svuint16x3_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x3]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16x3_t; + + // CHECK-DAG: name: "svint32x3_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x3:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32x3]] = !{![[REALELTS32x3:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32x3]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 6, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32x3_t; + + // CHECK-DAG: name: "svuint32x3_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x3]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32x3_t; + + // CHECK-DAG: name: "svint64x3_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x3_64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS1x3_64]] = !{![[REALELTS1x3_64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS1x3_64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 3, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64x3_t; + + // CHECK-DAG: name: "svuint64x3_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x3_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64x3_t; + + // CHECK: name: "svfloat32x3_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x3]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32x3_t; + + // CHECK: name: "svfloat64x3_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x3_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64x3_t; +} diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs new file mode 100644 index 0000000000000..911af76f42ebb --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs @@ -0,0 +1,149 @@ +//@ only-aarch64 +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint8x4_t(svint8_t, svint8_t, svint8_t, svint8_t); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint8x4_t(svuint8_t, svuint8_t, svuint8_t, svuint8_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint16x4_t(svint16_t, svint16_t, svint16_t, svint16_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint16x4_t(svuint16_t, svuint16_t, svuint16_t, svuint16_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint32x4_t(svint32_t, svint32_t, svint32_t, svint32_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint32x4_t(svuint32_t, svuint32_t, svuint32_t, svuint32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint64x4_t(svint64_t, svint64_t, svint64_t, svint64_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint64x4_t(svuint64_t, svuint64_t, svuint64_t, svuint64_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat32x4_t(svfloat32_t, svfloat32_t, svfloat32_t, svfloat32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat64x4_t(svfloat64_t, svfloat64_t, svfloat64_t, svfloat64_t); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svint8x4_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x4:[0-9]+]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS8x4]] = !{![[REALELTS8x4:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8x4]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 32, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s8: svint8x4_t; + + // CHECK-DAG: name: "svuint8x4_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x4]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + let u8: svuint8x4_t; + + // CHECK-DAG: name: "svint16x4_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x4:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16x4]] = !{![[REALELTS16x4:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16x4]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 16, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16x4_t; + + // CHECK-DAG: name: "svuint16x4_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x4]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16x4_t; + + // CHECK-DAG: name: "svint32x4_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x4:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32x4]] = !{![[REALELTS32x4:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32x4]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 8, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32x4_t; + + // CHECK-DAG: name: "svuint32x4_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x4]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32x4_t; + + // CHECK-DAG: name: "svint64x4_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x4_64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS1x4_64]] = !{![[REALELTS1x4_64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS1x4_64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64x4_t; + + // CHECK-DAG: name: "svuint64x4_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x4_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64x4_t; + + // CHECK: name: "svfloat32x4_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x4]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32x4_t; + + // CHECK: name: "svfloat64x4_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x4_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64x4_t; +} diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo.rs b/tests/codegen-llvm/scalable-vectors/debuginfo.rs new file mode 100644 index 0000000000000..f4b34a5e1e7eb --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo.rs @@ -0,0 +1,123 @@ +// ignore-tidy-linelength +//@ only-aarch64 +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 +//@ revisions: POST-LLVM-22 PRE-LLVM-22 +//@ [PRE-LLVM-22] max-llvm-major-version: 21 +//@ [POST-LLVM-22] min-llvm-version: 22 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svbool_t(bool); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svbool_t",{{.*}}, baseType: ![[CT1:[0-9]+]] + // PRE-LLVM-22-DAG: ![[CT1]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYU8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8:[0-9]+]]) + // POST-LLVM-22-DAG: ![[CT1]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYU8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8:[0-9]+]], bitStride: i64 1) + // CHECK-DAG: ![[ELTTYU8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + // CHECK-DAG: ![[ELTS8]] = !{![[REALELTS8:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 8, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let b8: svbool_t; + + // CHECK-DAG: name: "svint8_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYS8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8:[0-9]+]]) + // CHECK-DAG: ![[ELTTYS8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + let s8: svint8_t; + + // PRE-LLVM-22-DAG: name: "svuint8_t",{{.*}}, baseType: ![[CT1:[0-9]+]] + // POST-LLVM-22-DAG: name: "svuint8_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // POST-LLVM-22-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYU8]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8]]) + let u8: svuint8_t; + + // CHECK-DAG: name: "svint16_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16]] = !{![[REALELTS16:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16_t; + + // CHECK-DAG: name: "svuint16_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16_t; + + // CHECK-DAG: name: "svint32_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32]] = !{![[REALELTS32:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 2, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32_t; + + // CHECK-DAG: name: "svuint32_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32_t; + + // CHECK-DAG: name: "svint64_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS64]] = !{![[REALELTS64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 1, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64_t; + + // CHECK-DAG: name: "svuint64_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64_t; + + // CHECK: name: "svfloat32_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32_t; + + // CHECK: name: "svfloat64_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64_t; +} From 72f2ace16006d04a7c32d9d92ce9d051f1daf39e Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 28 Feb 2026 16:48:59 +0000 Subject: [PATCH 07/38] cg_llvm: `sve_cast` intrinsic Abstract over the existing `simd_cast` intrinsic to implement a new `sve_cast` intrinsic - this is better than allowing scalable vectors to be used with all of the generic `simd_*` intrinsics. --- .../src/debuginfo/metadata.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 203 ++++++++++-------- .../rustc_hir_analysis/src/check/intrinsic.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/simd/scalable.rs | 23 ++ tests/ui/scalable-vectors/cast-intrinsic.rs | 65 ++++++ 6 files changed, 205 insertions(+), 90 deletions(-) create mode 100644 tests/ui/scalable-vectors/cast-intrinsic.rs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 25307823b37c7..c91d3ec63a028 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; use std::path::PathBuf; use std::sync::Arc; -use std::{iter, ptr}; +use std::{assert_matches, iter, ptr}; use libc::{c_longlong, c_uint}; use rustc_abi::{Align, Layout, NumScalableVectors, Size}; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ad2c23c99820b..3e600914d6f42 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -606,6 +606,27 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.pointercast(val, self.type_ptr()) } + sym::sve_cast => { + let Some((in_cnt, in_elem, in_num_vecs)) = + args[0].layout.ty.scalable_vector_parts(self.cx.tcx) + else { + bug!("input parameter to `sve_cast` was not scalable vector"); + }; + let out_layout = self.layout_of(fn_args.type_at(1)); + let Some((out_cnt, out_elem, out_num_vecs)) = + out_layout.ty.scalable_vector_parts(self.cx.tcx) + else { + bug!("output parameter to `sve_cast` was not scalable vector"); + }; + assert_eq!(in_cnt, out_cnt); + assert_eq!(in_num_vecs, out_num_vecs); + let out_llty = self.backend_type(out_layout); + match simd_cast(self, sym::simd_cast, args, out_llty, in_elem, out_elem) { + Some(val) => val, + _ => bug!("could not cast scalable vectors"), + } + } + sym::sve_tuple_create2 => { assert_matches!( self.layout_of(fn_args.type_at(0)).backend_repr, @@ -2772,96 +2793,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( out_len } ); - // casting cares about nominal type, not just structural type - if in_elem == out_elem { - return Ok(args[0].immediate()); - } - - #[derive(Copy, Clone)] - enum Sign { - Unsigned, - Signed, - } - use Sign::*; - - enum Style { - Float, - Int(Sign), - Unsupported, - } - - let (in_style, in_width) = match in_elem.kind() { - // vectors of pointer-sized integers should've been - // disallowed before here, so this unwrap is safe. - ty::Int(i) => ( - Style::Int(Signed), - i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Uint(u) => ( - Style::Int(Unsigned), - u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Float(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0), - }; - let (out_style, out_width) = match out_elem.kind() { - ty::Int(i) => ( - Style::Int(Signed), - i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Uint(u) => ( - Style::Int(Unsigned), - u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Float(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0), - }; - - match (in_style, out_style) { - (Style::Int(sign), Style::Int(_)) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => match sign { - Sign::Signed => bx.sext(args[0].immediate(), llret_ty), - Sign::Unsigned => bx.zext(args[0].immediate(), llret_ty), - }, - }); - } - (Style::Int(Sign::Signed), Style::Float) => { - return Ok(bx.sitofp(args[0].immediate(), llret_ty)); - } - (Style::Int(Sign::Unsigned), Style::Float) => { - return Ok(bx.uitofp(args[0].immediate(), llret_ty)); - } - (Style::Float, Style::Int(sign)) => { - return Ok(match (sign, name == sym::simd_as) { - (Sign::Unsigned, false) => bx.fptoui(args[0].immediate(), llret_ty), - (Sign::Signed, false) => bx.fptosi(args[0].immediate(), llret_ty), - (_, true) => bx.cast_float_to_int( - matches!(sign, Sign::Signed), - args[0].immediate(), - llret_ty, - ), - }); - } - (Style::Float, Style::Float) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => bx.fpext(args[0].immediate(), llret_ty), - }); - } - _ => { /* Unsupported. Fallthrough. */ } + match simd_cast(bx, name, args, llret_ty, in_elem, out_elem) { + Some(val) => return Ok(val), + None => return_error!(InvalidMonomorphization::UnsupportedCast { + span, + name, + in_ty, + in_elem, + ret_ty, + out_elem + }), } - return_error!(InvalidMonomorphization::UnsupportedCast { - span, - name, - in_ty, - in_elem, - ret_ty, - out_elem - }); } macro_rules! arith_binary { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { @@ -3035,3 +2977,86 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } + +/// Implementation of `core::intrinsics::simd_cast`, re-used by `core::scalable::sve_cast`. +fn simd_cast<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + name: Symbol, + args: &[OperandRef<'tcx, &'ll Value>], + llret_ty: &'ll Type, + in_elem: Ty<'tcx>, + out_elem: Ty<'tcx>, +) -> Option<&'ll Value> { + // Casting cares about nominal type, not just structural type + if in_elem == out_elem { + return Some(args[0].immediate()); + } + + #[derive(Copy, Clone)] + enum Sign { + Unsigned, + Signed, + } + use Sign::*; + + enum Style { + Float, + Int(Sign), + Unsupported, + } + + let (in_style, in_width) = match in_elem.kind() { + // vectors of pointer-sized integers should've been + // disallowed before here, so this unwrap is safe. + ty::Int(i) => ( + Style::Int(Signed), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(Unsigned), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Float(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0), + }; + let (out_style, out_width) = match out_elem.kind() { + ty::Int(i) => ( + Style::Int(Signed), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(Unsigned), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Float(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0), + }; + + match (in_style, out_style) { + (Style::Int(sign), Style::Int(_)) => Some(match in_width.cmp(&out_width) { + Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => match sign { + Sign::Signed => bx.sext(args[0].immediate(), llret_ty), + Sign::Unsigned => bx.zext(args[0].immediate(), llret_ty), + }, + }), + (Style::Int(Sign::Signed), Style::Float) => Some(bx.sitofp(args[0].immediate(), llret_ty)), + (Style::Int(Sign::Unsigned), Style::Float) => { + Some(bx.uitofp(args[0].immediate(), llret_ty)) + } + (Style::Float, Style::Int(sign)) => Some(match (sign, name == sym::simd_as) { + (Sign::Unsigned, false) => bx.fptoui(args[0].immediate(), llret_ty), + (Sign::Signed, false) => bx.fptosi(args[0].immediate(), llret_ty), + (_, true) => { + bx.cast_float_to_int(matches!(sign, Sign::Signed), args[0].immediate(), llret_ty) + } + }), + (Style::Float, Style::Float) => Some(match in_width.cmp(&out_width) { + Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => bx.fpext(args[0].immediate(), llret_ty), + }), + _ => None, + } +} diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index ca57921089fae..58454cfc489c6 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -783,6 +783,7 @@ pub(crate) fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_const_generic => (2, 1, vec![param(0), param(0)], param(1)), + sym::sve_cast => (2, 0, vec![param(0)], param(1)), sym::sve_tuple_create2 => (2, 0, vec![param(0), param(0)], param(1)), sym::sve_tuple_create3 => (2, 0, vec![param(0), param(0), param(0)], param(1)), sym::sve_tuple_create4 => (2, 0, vec![param(0), param(0), param(0), param(0)], param(1)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e44e9f4ea1ce7..d5c815653b72a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1977,6 +1977,7 @@ symbols! { suggestion, super_let, supertrait_item_shadowing, + sve_cast, sve_tuple_create2, sve_tuple_create3, sve_tuple_create4, diff --git a/library/core/src/intrinsics/simd/scalable.rs b/library/core/src/intrinsics/simd/scalable.rs index a569d1ffbc548..1a8e90012bce8 100644 --- a/library/core/src/intrinsics/simd/scalable.rs +++ b/library/core/src/intrinsics/simd/scalable.rs @@ -2,6 +2,29 @@ //! //! In this module, a "vector" is any `#[rustc_scalable_vector]`-annotated type. +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` must be vectors of integers or floats, and must have the same length. +/// +/// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +/// +/// # Safety +/// Casting from integer types is always safe. +/// Casting between two float types is also always safe. +/// +/// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. +/// Specifically, each element must: +/// * Not be `NaN` +/// * Not be infinite +/// * Be representable in the return type, after truncating off its fractional part +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +#[target_feature(enable = "sve")] +pub unsafe fn sve_cast(x: T) -> U; + /// Create a tuple of two vectors. /// /// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a diff --git a/tests/ui/scalable-vectors/cast-intrinsic.rs b/tests/ui/scalable-vectors/cast-intrinsic.rs new file mode 100644 index 0000000000000..f2157d8bcc14b --- /dev/null +++ b/tests/ui/scalable-vectors/cast-intrinsic.rs @@ -0,0 +1,65 @@ +//@ check-pass +//@ only-aarch64 +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features, improper_ctypes)] +#![feature(abi_unadjusted, core_intrinsics, link_llvm_intrinsics, rustc_attrs)] + +use std::intrinsics::simd::scalable::sve_cast; + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +pub struct svbool_t(bool); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +pub struct svbool2_t(bool); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +pub struct svint64_t(i64); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +pub struct nxv2i16(i16); + +pub trait SveInto: Sized { + unsafe fn sve_into(self) -> T; +} + +impl SveInto for svbool_t { + #[target_feature(enable = "sve")] + unsafe fn sve_into(self) -> svbool2_t { + unsafe extern "C" { + #[cfg_attr( + target_arch = "aarch64", + link_name = concat!("llvm.aarch64.sve.convert.from.svbool.nxv2i1") + )] + fn convert_from_svbool(b: svbool_t) -> svbool2_t; + } + unsafe { convert_from_svbool(self) } + } +} + +#[target_feature(enable = "sve")] +pub unsafe fn svld1sh_gather_s64offset_s64( + pg: svbool_t, + base: *const i16, + offsets: svint64_t, +) -> svint64_t { + unsafe extern "unadjusted" { + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ld1.gather.nxv2i16" + )] + fn _svld1sh_gather_s64offset_s64( + pg: svbool2_t, + base: *const i16, + offsets: svint64_t, + ) -> nxv2i16; + } + sve_cast(_svld1sh_gather_s64offset_s64(pg.sve_into(), base, offsets)) +} From 9bdf217810e77b7d58e43030dd61a65172ab1354 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 31 Mar 2026 12:51:12 -0500 Subject: [PATCH 08/38] rustdoc: seperate methods and associated functions in sidebar --- src/librustdoc/clean/types.rs | 9 ++ src/librustdoc/html/render/sidebar.rs | 132 ++++++++++++-------- tests/rustdoc-gui/hash-item-expansion.goml | 4 +- tests/rustdoc-gui/sidebar-mobile.goml | 2 +- tests/rustdoc-html/sidebar/sidebar-items.rs | 8 ++ tests/rustdoc-html/typedef.rs | 2 +- 6 files changed, 103 insertions(+), 54 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ad70fc1096691..7d40bc95ec089 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -465,6 +465,15 @@ impl Item { .unwrap_or(false) } + /// Returns true if item is an associated function with a `self` parameter. + pub(crate) fn has_self_param(&self) -> bool { + if let ItemKind::MethodItem(box Function { decl, .. }, _) = &self.inner.kind { + decl.receiver_type().is_some() + } else { + false + } + } + pub(crate) fn span(&self, tcx: TyCtxt<'_>) -> Option { let kind = match &self.kind { ItemKind::StrippedItem(k) => k, diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index a4535792ac3ce..d40c9501cabc1 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -433,6 +433,7 @@ fn sidebar_assoc_items<'a>( let mut assoc_consts = Vec::new(); let mut assoc_types = Vec::new(); + let mut assoc_fns = Vec::new(); let mut methods = Vec::new(); if let Some(v) = cache.impls.get(&did) { let mut used_links = FxHashSet::default(); @@ -443,7 +444,12 @@ fn sidebar_assoc_items<'a>( for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) { assoc_consts.extend(get_associated_constants(impl_, used_links_bor)); assoc_types.extend(get_associated_types(impl_, used_links_bor)); - methods.extend(get_methods(impl_, false, used_links_bor, false, cx.tcx())); + methods.extend(get_methods( + impl_, + GetMethodsMode::AlsoCollectAssocFns { assoc_fns: &mut assoc_fns }, + used_links_bor, + cx.tcx(), + )); } // We want links' order to be reproducible so we don't use unstable sort. assoc_consts.sort(); @@ -462,6 +468,11 @@ fn sidebar_assoc_items<'a>( "associatedtype", assoc_types, ), + LinkBlock::new( + Link::new("implementations", "Associated Functions"), + "method", + assoc_fns, + ), LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), ]; @@ -546,7 +557,15 @@ fn sidebar_deref_methods<'a>( i.inner_impl().trait_.is_none() && real_target.is_doc_subtype_of(&i.inner_impl().for_, c) }) - .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) + .flat_map(|i| { + get_methods( + i.inner_impl(), + GetMethodsMode::Deref { deref_mut }, + used_links, + cx.tcx(), + ) + .collect::>() + }) .collect::>(); if !ret.is_empty() { let id = if let Some(target_def_id) = real_target.def_id(c) { @@ -734,69 +753,82 @@ fn get_next_url(used_links: &mut FxHashSet, url: String) -> String { format!("{url}-{add}") } +enum GetMethodsMode<'r, 'l> { + Deref { deref_mut: bool }, + AlsoCollectAssocFns { assoc_fns: &'r mut Vec> }, +} + fn get_methods<'a>( i: &'a clean::Impl, - for_deref: bool, + mut mode: GetMethodsMode<'_, 'a>, used_links: &mut FxHashSet, - deref_mut: bool, tcx: TyCtxt<'_>, -) -> Vec> { - i.items - .iter() - .filter_map(|item| { - if let Some(ref name) = item.name - && item.is_method() - && (!for_deref || super::should_render_item(item, deref_mut, tcx)) - { - Some(Link::new( +) -> impl Iterator> { + i.items.iter().filter_map(move |item| { + if let Some(ref name) = item.name + && item.is_method() + { + let mut build_link = || { + Link::new( get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::Method)), name.as_str(), - )) - } else { - None + ) + }; + match &mut mode { + &mut GetMethodsMode::Deref { deref_mut } => { + if super::should_render_item(item, deref_mut, tcx) { + Some(build_link()) + } else { + None + } + } + GetMethodsMode::AlsoCollectAssocFns { assoc_fns } => { + if item.has_self_param() { + Some(build_link()) + } else { + assoc_fns.push(build_link()); + None + } + } } - }) - .collect() + } else { + None + } + }) } fn get_associated_constants<'a>( i: &'a clean::Impl, used_links: &mut FxHashSet, -) -> Vec> { - i.items - .iter() - .filter_map(|item| { - if let Some(ref name) = item.name - && item.is_associated_const() - { - Some(Link::new( - get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)), - name.as_str(), - )) - } else { - None - } - }) - .collect() +) -> impl Iterator> { + i.items.iter().filter_map(|item| { + if let Some(ref name) = item.name + && item.is_associated_const() + { + Some(Link::new( + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocConst)), + name.as_str(), + )) + } else { + None + } + }) } fn get_associated_types<'a>( i: &'a clean::Impl, used_links: &mut FxHashSet, -) -> Vec> { - i.items - .iter() - .filter_map(|item| { - if let Some(ref name) = item.name - && item.is_associated_type() - { - Some(Link::new( - get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)), - name.as_str(), - )) - } else { - None - } - }) - .collect() +) -> impl Iterator> { + i.items.iter().filter_map(|item| { + if let Some(ref name) = item.name + && item.is_associated_type() + { + Some(Link::new( + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)), + name.as_str(), + )) + } else { + None + } + }) } diff --git a/tests/rustdoc-gui/hash-item-expansion.goml b/tests/rustdoc-gui/hash-item-expansion.goml index a7a5c3cb48345..8661641206685 100644 --- a/tests/rustdoc-gui/hash-item-expansion.goml +++ b/tests/rustdoc-gui/hash-item-expansion.goml @@ -5,7 +5,7 @@ assert-attribute: ("#blanket-implementations-list > details:nth-child(2)", {"ope // We first check that the impl block is open by default. assert-attribute: ("#implementations-list details", {"open": ""}) // To ensure that we will click on the currently hidden method. -assert-text: (".sidebar-elems section .block li > a", "must_use") -click: ".sidebar-elems section .block li > a" +assert-text: (".sidebar-elems section ul:nth-of-type(2) li > a", "must_use") +click: ".sidebar-elems ul:nth-of-type(2) li > a" // We check that the impl block was opened as expected so that we can see the method. assert-attribute: ("#implementations-list > details", {"open": ""}) diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index 3183650b555a8..61c1555fbc0e3 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -48,7 +48,7 @@ assert-property: ("rustdoc-topbar", {"clientHeight": "45"}) // Check that clicking an element from the sidebar scrolls to the right place // so the target is not obscured by the topbar. click: ".sidebar-menu-toggle" -click: ".sidebar-elems section .block li > a" +click: ".sidebar-elems section ul:nth-of-type(2) li > a" assert-position: ("#method\.must_use", {"y": 45}) // Check that the bottom-most item on the sidebar menu can be scrolled fully into view. diff --git a/tests/rustdoc-html/sidebar/sidebar-items.rs b/tests/rustdoc-html/sidebar/sidebar-items.rs index 6e13457796e5e..bd0893dac3e36 100644 --- a/tests/rustdoc-html/sidebar/sidebar-items.rs +++ b/tests/rustdoc-html/sidebar/sidebar-items.rs @@ -42,6 +42,14 @@ pub struct Bar { waza: u32, } +//@ has foo/struct.Bar.html +//@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#implementations"]' 'Associated Functions' +//@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#implementations"]' 'Methods' +impl Bar { + pub fn method(&self) {} + pub fn assoc_fn() {} +} + //@ has foo/enum.En.html //@ has - '//div[@class="sidebar-elems"]//h3/a[@href="#variants"]' 'Variants' //@ has - '//*[@class="sidebar-elems"]//section//a' 'Foo' diff --git a/tests/rustdoc-html/typedef.rs b/tests/rustdoc-html/typedef.rs index 3fdc2788bcacf..4eae3c7258213 100644 --- a/tests/rustdoc-html/typedef.rs +++ b/tests/rustdoc-html/typedef.rs @@ -13,7 +13,7 @@ impl MyStruct { //@ has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl MyTrait for MyAlias' //@ hasraw - 'Alias docstring' //@ has - '//*[@class="sidebar"]//*[@class="location"]' 'MyAlias' -//@ has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Methods' +//@ has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Associated Functions' //@ has - '//*[@class="sidebar"]//a[@href="#trait-implementations"]' 'Trait Implementations' /// Alias docstring pub type MyAlias = MyStruct; From 8277043ea3a47a0279ed166b3d8d95a32a2baece Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 1 Apr 2026 10:48:35 +0800 Subject: [PATCH 09/38] Avoid creating async return opaques for foreign async fns --- compiler/rustc_resolve/src/def_collector.rs | 7 +++++-- .../bad-external-async-fn-issue-146754.rs | 8 +++++++ .../bad-external-async-fn-issue-146754.stderr | 21 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/ui/extern/bad-external-async-fn-issue-146754.rs create mode 100644 tests/ui/extern/bad-external-async-fn-issue-146754.stderr diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index de36f01b6d0e5..0acc42a6efbe1 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -209,12 +209,15 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) { match fn_kind { FnKind::Fn( - _ctxt, + ctxt, _vis, Fn { sig: FnSig { header, decl, span: _ }, ident, generics, contract, body, .. }, - ) if let Some(coroutine_kind) = header.coroutine_kind => { + ) if let Some(coroutine_kind) = header.coroutine_kind + // Foreign ones are denied, so don't create them here. + && ctxt != visit::FnCtxt::Foreign => + { self.visit_ident(ident); self.visit_fn_header(header); self.visit_generics(generics); diff --git a/tests/ui/extern/bad-external-async-fn-issue-146754.rs b/tests/ui/extern/bad-external-async-fn-issue-146754.rs new file mode 100644 index 0000000000000..394341c129654 --- /dev/null +++ b/tests/ui/extern/bad-external-async-fn-issue-146754.rs @@ -0,0 +1,8 @@ +//@ edition:2024 +#![crate_type = "lib"] + +unsafe extern "C" { + async fn function() -> [(); || {}]; + //~^ ERROR functions in `extern` blocks cannot have `async` qualifier + //~^^ ERROR mismatched types +} diff --git a/tests/ui/extern/bad-external-async-fn-issue-146754.stderr b/tests/ui/extern/bad-external-async-fn-issue-146754.stderr new file mode 100644 index 0000000000000..2a04b23630430 --- /dev/null +++ b/tests/ui/extern/bad-external-async-fn-issue-146754.stderr @@ -0,0 +1,21 @@ +error: functions in `extern` blocks cannot have `async` qualifier + --> $DIR/bad-external-async-fn-issue-146754.rs:5:5 + | +LL | unsafe extern "C" { + | ----------------- in this `extern` block +LL | async fn function() -> [(); || {}]; + | ^^^^^ help: remove the `async` qualifier + +error[E0308]: mismatched types + --> $DIR/bad-external-async-fn-issue-146754.rs:5:33 + | +LL | async fn function() -> [(); || {}]; + | ^^^^^ expected `usize`, found closure + | + = note: expected type `usize` + found closure `{closure@$DIR/bad-external-async-fn-issue-146754.rs:5:33: 5:35}` + = note: array length can only be `usize` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From 910677fcc625fcff770158b4184ff64e4b57320c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Apr 2026 16:38:30 +1100 Subject: [PATCH 10/38] Rename `HirCtx` as `Hcx`. PR #154634 recently renamed many type parameters that impl `HashStableContext` as `Hcx`. It missed a few that are named `HirCtx`. This commit renames them. --- compiler/rustc_hir/src/stable_hash_impls.rs | 36 ++++++++++----------- compiler/rustc_middle/src/hir/mod.rs | 4 +-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index 58649a694880b..d2d6529619662 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -13,48 +13,48 @@ use crate::lints::DelayedLints; /// instead of implementing everything in `rustc_middle`. pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {} -impl ToStableHashKey for BodyId { +impl ToStableHashKey for BodyId { type KeyType = (DefPathHash, ItemLocalId); #[inline] - fn to_stable_hash_key(&self, hcx: &HirCtx) -> (DefPathHash, ItemLocalId) { + fn to_stable_hash_key(&self, hcx: &Hcx) -> (DefPathHash, ItemLocalId) { let BodyId { hir_id } = *self; hir_id.to_stable_hash_key(hcx) } } -impl ToStableHashKey for ItemId { +impl ToStableHashKey for ItemId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &Hcx) -> DefPathHash { self.owner_id.def_id.to_stable_hash_key(hcx) } } -impl ToStableHashKey for TraitItemId { +impl ToStableHashKey for TraitItemId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &Hcx) -> DefPathHash { self.owner_id.def_id.to_stable_hash_key(hcx) } } -impl ToStableHashKey for ImplItemId { +impl ToStableHashKey for ImplItemId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &Hcx) -> DefPathHash { self.owner_id.def_id.to_stable_hash_key(hcx) } } -impl ToStableHashKey for ForeignItemId { +impl ToStableHashKey for ForeignItemId { type KeyType = DefPathHash; #[inline] - fn to_stable_hash_key(&self, hcx: &HirCtx) -> DefPathHash { + fn to_stable_hash_key(&self, hcx: &Hcx) -> DefPathHash { self.owner_id.def_id.to_stable_hash_key(hcx) } } @@ -66,8 +66,8 @@ impl ToStableHashKey for ForeignItemId // want to pick up on a reference changing its target, so we hash the NodeIds // in "DefPath Mode". -impl<'tcx, HirCtx: crate::HashStableContext> HashStable for OwnerNodes<'tcx> { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { +impl<'tcx, Hcx: crate::HashStableContext> HashStable for OwnerNodes<'tcx> { + fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { // We ignore the `nodes` and `bodies` fields since these refer to information included in // `hash` which is hashed in the collector and used for the crate hash. // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing @@ -78,15 +78,15 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable for OwnerNodes<' } } -impl HashStable for DelayedLints { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { +impl HashStable for DelayedLints { + fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { let DelayedLints { opt_hash, .. } = *self; opt_hash.unwrap().hash_stable(hcx, hasher); } } -impl<'tcx, HirCtx: crate::HashStableContext> HashStable for AttributeMap<'tcx> { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { +impl<'tcx, Hcx: crate::HashStableContext> HashStable for AttributeMap<'tcx> { + fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { // We ignore the `map` since it refers to information included in `opt_hash` which is // hashed in the collector and used for the crate hash. let AttributeMap { opt_hash, define_opaque: _, map: _ } = *self; @@ -94,8 +94,8 @@ impl<'tcx, HirCtx: crate::HashStableContext> HashStable for AttributeMap } } -impl HashStable for HashIgnoredAttrId { - fn hash_stable(&self, _hcx: &mut HirCtx, _hasher: &mut StableHasher) { +impl HashStable for HashIgnoredAttrId { + fn hash_stable(&self, _hcx: &mut Hcx, _hasher: &mut StableHasher) { /* we don't hash HashIgnoredAttrId, we ignore them */ } } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ad56e462d2934..dfce7e0917608 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -77,8 +77,8 @@ impl<'hir> Crate<'hir> { } } -impl HashStable for Crate<'_> { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { +impl HashStable for Crate<'_> { + fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { let Crate { opt_hir_hash, .. } = self; opt_hir_hash.unwrap().hash_stable(hcx, hasher) } From d9a8a553cd601d6c25f7557021a01ea0370738bb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Apr 2026 16:51:01 +1100 Subject: [PATCH 11/38] Reorder `use`/`mod` items in `rustc_session`. They're in multiple sections with no rhyme or reason. --- compiler/rustc_session/src/lib.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 1741dde90f5cf..29192f267ed3a 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -11,28 +11,24 @@ #![recursion_limit = "256"] // tidy-alphabetical-end -pub mod errors; - -pub mod utils; +pub use getopts; pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass}; pub use rustc_lint_defs as lint; -pub mod parse; +pub use session::*; pub mod code_stats; +pub mod errors; +pub mod parse; +pub mod utils; #[macro_use] pub mod config; pub mod cstore; pub mod filesearch; mod macros; mod options; +pub mod output; pub mod search_paths; - mod session; -pub use session::*; - -pub mod output; - -pub use getopts; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro From 1a9a284ad253fb0fd1d52e880789dc6848599d93 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 1 Apr 2026 16:41:28 +1100 Subject: [PATCH 12/38] Simplify `HashStableContext`. `derive(HashStable_Generic)` generates impls like this: ``` impl<__CTX> HashStable<__CTX> for ExpnKind where __CTX: crate::HashStableContext { fn hash_stable(&self, hcx : &mut __CTX, __hasher: &mut StableHasher) { ... } } ``` This is used for crates that are upstream of `rustc_middle`. The `crate::HashStableContext` bound means every crate that uses `derive(HashStable_Generic)` must provide (or import) a trait `HashStableContext` which `rustc_middle` then impls. In `rustc_span` this trait is sensible, with three methods. In other crates, this trait is empty, and there is the following trait hierarchy: ``` rustc_session::HashStableContext | | | rustc_hir::HashStableContext | / \ rustc_ast::HashStableContext rustc_abi::HashStableContext | rustc_span::HashStableContext ``` All very strange and unnecessary. This commit changes `derive(HashStable_Generic)` to use `rustc_span::HashStableContext` instead of `crate::HashStableContext`. This eliminates the need for all the empty `HashStableContext` traits and impls. Much better. --- compiler/rustc_abi/src/lib.rs | 6 ------ compiler/rustc_ast/src/ast.rs | 5 +++-- compiler/rustc_ast/src/lib.rs | 5 ----- compiler/rustc_ast/src/tokenstream.rs | 4 ++-- compiler/rustc_hir/src/def.rs | 4 ++-- compiler/rustc_hir/src/lib.rs | 1 - compiler/rustc_hir/src/stable_hash_impls.rs | 24 +++++++++------------ compiler/rustc_hir_id/src/lib.rs | 4 ++-- compiler/rustc_lint_defs/src/lib.rs | 4 ++-- compiler/rustc_macros/src/hash_stable.rs | 2 +- compiler/rustc_middle/src/hir/mod.rs | 2 +- compiler/rustc_middle/src/ich/hcx.rs | 9 ++------ compiler/rustc_session/src/config.rs | 5 +++-- compiler/rustc_session/src/lib.rs | 5 ----- compiler/rustc_span/src/lib.rs | 8 +++---- compiler/rustc_target/src/lib.rs | 2 -- 16 files changed, 32 insertions(+), 58 deletions(-) diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 253dff6f8e75c..42e272fdafb5d 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -67,12 +67,6 @@ pub use layout::{FIRST_VARIANT, FieldIdx, LayoutCalculator, LayoutCalculatorErro #[cfg(feature = "nightly")] pub use layout::{Layout, TyAbiInterface, TyAndLayout}; -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in `rustc_middle`. -#[cfg(feature = "nightly")] -pub trait HashStableContext {} - #[derive(Clone, Copy, PartialEq, Eq, Default)] #[cfg_attr( feature = "nightly", diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 71ec1c5042fda..87633cade1b6a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -31,7 +31,8 @@ use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable}; pub use rustc_span::AttrId; use rustc_span::{ - ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Spanned, Symbol, kw, respan, sym, + ByteSymbol, DUMMY_SP, ErrorGuaranteed, HashStableContext, Ident, Span, Spanned, Symbol, kw, + respan, sym, }; use thin_vec::{ThinVec, thin_vec}; @@ -120,7 +121,7 @@ impl PartialEq<&[Symbol]> for Path { } } -impl HashStable for Path { +impl HashStable for Path { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { self.segments.len().hash_stable(hcx, hasher); for segment in &self.segments { diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index ac3e77b0b5d6f..4178db1bfb09d 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -36,8 +36,3 @@ pub mod visit; pub use self::ast::*; pub use self::ast_traits::{AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; - -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: rustc_span::HashStableContext {} diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 8953391ac58bf..06bd6f03e9350 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,7 +14,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync; use rustc_macros::{Decodable, Encodable, HashStable_Generic, Walkable}; use rustc_serialize::{Decodable, Encodable}; -use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; +use rustc_span::{DUMMY_SP, HashStableContext, Span, SpanDecoder, SpanEncoder, Symbol, sym}; use thin_vec::ThinVec; use crate::ast::AttrStyle; @@ -826,7 +826,7 @@ impl FromIterator for TokenStream { impl HashStable for TokenStream where - Hcx: crate::HashStableContext, + Hcx: HashStableContext, { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { for sub_tt in self.iter() { diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 78bd709dd4844..cae8bb89233b2 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -8,9 +8,9 @@ use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::unord::UnordMap; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::Symbol; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::hygiene::MacroKind; +use rustc_span::{HashStableContext, Symbol}; use crate::definitions::DefPathData; use crate::hir; @@ -712,7 +712,7 @@ impl IntoDiagArg for Namespace { } } -impl ToStableHashKey for Namespace { +impl ToStableHashKey for Namespace { type KeyType = Namespace; #[inline] diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 9c2fec8677854..c2d9f879cd601 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -43,7 +43,6 @@ pub use hir::*; pub use lang_items::{LangItem, LanguageItems}; pub use rustc_ast::attr::version::*; pub use stability::*; -pub use stable_hash_impls::HashStableContext; pub use target::{MethodKind, Target}; arena_types!(rustc_arena::declare_arena); diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index d2d6529619662..a157fc0ccbb0c 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -1,4 +1,5 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_span::HashStableContext; use rustc_span::def_id::DefPathHash; use crate::HashIgnoredAttrId; @@ -8,12 +9,7 @@ use crate::hir::{ use crate::hir_id::ItemLocalId; use crate::lints::DelayedLints; -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {} - -impl ToStableHashKey for BodyId { +impl ToStableHashKey for BodyId { type KeyType = (DefPathHash, ItemLocalId); #[inline] @@ -23,7 +19,7 @@ impl ToStableHashKey for BodyId { } } -impl ToStableHashKey for ItemId { +impl ToStableHashKey for ItemId { type KeyType = DefPathHash; #[inline] @@ -32,7 +28,7 @@ impl ToStableHashKey for ItemId { } } -impl ToStableHashKey for TraitItemId { +impl ToStableHashKey for TraitItemId { type KeyType = DefPathHash; #[inline] @@ -41,7 +37,7 @@ impl ToStableHashKey for TraitItemId { } } -impl ToStableHashKey for ImplItemId { +impl ToStableHashKey for ImplItemId { type KeyType = DefPathHash; #[inline] @@ -50,7 +46,7 @@ impl ToStableHashKey for ImplItemId { } } -impl ToStableHashKey for ForeignItemId { +impl ToStableHashKey for ForeignItemId { type KeyType = DefPathHash; #[inline] @@ -66,7 +62,7 @@ impl ToStableHashKey for ForeignItemId { // want to pick up on a reference changing its target, so we hash the NodeIds // in "DefPath Mode". -impl<'tcx, Hcx: crate::HashStableContext> HashStable for OwnerNodes<'tcx> { +impl<'tcx, Hcx: HashStableContext> HashStable for OwnerNodes<'tcx> { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { // We ignore the `nodes` and `bodies` fields since these refer to information included in // `hash` which is hashed in the collector and used for the crate hash. @@ -78,14 +74,14 @@ impl<'tcx, Hcx: crate::HashStableContext> HashStable for OwnerNodes<'tcx> { } } -impl HashStable for DelayedLints { +impl HashStable for DelayedLints { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { let DelayedLints { opt_hash, .. } = *self; opt_hash.unwrap().hash_stable(hcx, hasher); } } -impl<'tcx, Hcx: crate::HashStableContext> HashStable for AttributeMap<'tcx> { +impl<'tcx, Hcx: HashStableContext> HashStable for AttributeMap<'tcx> { fn hash_stable(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { // We ignore the `map` since it refers to information included in `opt_hash` which is // hashed in the collector and used for the crate hash. @@ -94,7 +90,7 @@ impl<'tcx, Hcx: crate::HashStableContext> HashStable for AttributeMap<'tcx> } } -impl HashStable for HashIgnoredAttrId { +impl HashStable for HashIgnoredAttrId { fn hash_stable(&self, _hcx: &mut Hcx, _hasher: &mut StableHasher) { /* we don't hash HashIgnoredAttrId, we ignore them */ } diff --git a/compiler/rustc_hir_id/src/lib.rs b/compiler/rustc_hir_id/src/lib.rs index ffff3f979f9e3..064ce4ed4cafe 100644 --- a/compiler/rustc_hir_id/src/lib.rs +++ b/compiler/rustc_hir_id/src/lib.rs @@ -8,7 +8,7 @@ use std::fmt::{self, Debug}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -pub use rustc_span::HashStableContext; +use rustc_span::HashStableContext; use rustc_span::def_id::{CRATE_DEF_ID, DefId, DefIndex, DefPathHash, LocalDefId}; #[derive(Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)] @@ -176,7 +176,7 @@ pub const CRATE_HIR_ID: HirId = pub const CRATE_OWNER_ID: OwnerId = OwnerId { def_id: CRATE_DEF_ID }; -impl ToStableHashKey for HirId { +impl ToStableHashKey for HirId { type KeyType = (DefPathHash, ItemLocalId); #[inline] diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index af1d1854fa5a0..1c86d553f9b6a 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -8,11 +8,11 @@ use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; -use rustc_hir_id::{HashStableContext, HirId, ItemLocalId}; +use rustc_hir_id::{HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; -use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_span::{HashStableContext, Ident, Span, Symbol, sym}; use serde::{Deserialize, Serialize}; pub use self::Level::*; diff --git a/compiler/rustc_macros/src/hash_stable.rs b/compiler/rustc_macros/src/hash_stable.rs index fa67adb406ed2..adb93e375c32c 100644 --- a/compiler/rustc_macros/src/hash_stable.rs +++ b/compiler/rustc_macros/src/hash_stable.rs @@ -84,7 +84,7 @@ fn hash_stable_derive_with_mode( match mode { HashStableMode::Normal => {} HashStableMode::Generic => { - s.add_where_predicate(parse_quote! { __CTX: crate::HashStableContext }); + s.add_where_predicate(parse_quote! { __CTX: ::rustc_span::HashStableContext }); } HashStableMode::NoContext => {} } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index dfce7e0917608..814b333cfb0f8 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -21,7 +21,7 @@ use rustc_hir::lints::DelayedLint; use rustc_hir::*; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::{ErrorGuaranteed, ExpnId, Span}; +use rustc_span::{ErrorGuaranteed, ExpnId, HashStableContext, Span}; use crate::query::Providers; use crate::ty::{ResolverAstLowering, TyCtxt}; diff --git a/compiler/rustc_middle/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs index 2e118dc3359fa..0e1cee2970f71 100644 --- a/compiler/rustc_middle/src/ich/hcx.rs +++ b/compiler/rustc_middle/src/ich/hcx.rs @@ -6,7 +6,7 @@ use rustc_hir::definitions::DefPathHash; use rustc_session::Session; use rustc_session::cstore::Untracked; use rustc_span::source_map::SourceMap; -use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span}; +use rustc_span::{CachingSourceMapView, DUMMY_SP, HashStableContext, Pos, Span}; // Very often, we are hashing something that does not need the `CachingSourceMapView`, so we // initialize it lazily. @@ -73,7 +73,7 @@ impl<'a> StableHashingContext<'a> { } } -impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { +impl<'a> HashStableContext for StableHashingContext<'a> { /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`). /// Instead, we hash the (file name, line, column) triple, which stays the same even if the @@ -189,8 +189,3 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { ); } } - -impl<'a> rustc_abi::HashStableContext for StableHashingContext<'a> {} -impl<'a> rustc_ast::HashStableContext for StableHashingContext<'a> {} -impl<'a> rustc_hir::HashStableContext for StableHashingContext<'a> {} -impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e37247d7dd837..1e95482a8c7e6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -23,7 +23,8 @@ use rustc_macros::{BlobDecodable, Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; use rustc_span::{ - FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, Symbol, sym, + FileName, HashStableContext, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, + Symbol, sym, }; use rustc_target::spec::{ FramePointer, LinkSelfContainedComponents, LinkerFeatures, PanicStrategy, SplitDebuginfo, @@ -38,7 +39,7 @@ use crate::errors::FileWriteFail; pub use crate::options::*; use crate::search_paths::SearchPath; use crate::utils::CanonicalizedPath; -use crate::{EarlyDiagCtxt, HashStableContext, Session, filesearch, lint}; +use crate::{EarlyDiagCtxt, Session, filesearch, lint}; mod cfg; mod externs; diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 29192f267ed3a..04e12f1afce68 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -29,8 +29,3 @@ mod options; pub mod output; pub mod search_paths; mod session; - -/// Requirements for a `StableHashingContext` to be used in this crate. -/// This is a hack to allow using the `HashStable_Generic` derive macro -/// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: rustc_ast::HashStableContext + rustc_hir::HashStableContext {} diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 6794ffb311e32..97de708290fb4 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -2796,10 +2796,10 @@ impl InnerSpan { } } -/// Requirements for a `StableHashingContext` to be used in this crate. -/// -/// This is a hack to allow using the [`HashStable_Generic`] derive macro -/// instead of implementing everything in rustc_middle. +/// This trait lets `HashStable` and `derive(HashStable_Generic)` be used in +/// this crate (and other crates upstream of `rustc_middle`), while leaving +/// certain operations to be defined in `rustc_middle` where more things are +/// visible. pub trait HashStableContext { /// The main event: stable hashing of a span. fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher); diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 1dc62cb3659cc..d46802bf45d1a 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -24,8 +24,6 @@ pub mod target_features; #[cfg(test)] mod tests; -use rustc_abi::HashStableContext; - /// The name of rustc's own place to organize libraries. /// /// Used to be `rustc`, now the default is `rustlib`. From e18dd4a9925015045bbb54e0291d0bf060cdcf43 Mon Sep 17 00:00:00 2001 From: Makai Date: Mon, 16 Mar 2026 22:07:03 +0800 Subject: [PATCH 13/38] add `TypeFlags::HAS_NON_REGION_ERROR` and `TypeFlags::HAS_RE_ERROR` --- compiler/rustc_middle/src/ty/region.rs | 2 +- compiler/rustc_type_ir/src/flags.rs | 31 +++++++++++++++----------- compiler/rustc_type_ir/src/visit.rs | 19 ++++++++++++++++ 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs index a497501ef19d5..798b98c5def5c 100644 --- a/compiler/rustc_middle/src/ty/region.rs +++ b/compiler/rustc_middle/src/ty/region.rs @@ -291,7 +291,7 @@ impl<'tcx> Region<'tcx> { } ty::ReError(_) => { flags = flags | TypeFlags::HAS_FREE_REGIONS; - flags = flags | TypeFlags::HAS_ERROR; + flags = flags | TypeFlags::HAS_RE_ERROR; } } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 6962a7ab1d727..f311298119649 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -91,19 +91,24 @@ bitflags::bitflags! { | TypeFlags::HAS_TY_INHERENT.bits() | TypeFlags::HAS_CT_PROJECTION.bits(); + /// Is a type or const error reachable? + const HAS_NON_REGION_ERROR = 1 << 15; + /// Is a region error reachable? + const HAS_RE_ERROR = 1 << 16; /// Is an error type/lifetime/const reachable? - const HAS_ERROR = 1 << 15; + const HAS_ERROR = TypeFlags::HAS_NON_REGION_ERROR.bits() + | TypeFlags::HAS_RE_ERROR.bits(); /// Does this have any region that "appears free" in the type? /// Basically anything but `ReBound` and `ReErased`. - const HAS_FREE_REGIONS = 1 << 16; + const HAS_FREE_REGIONS = 1 << 17; /// Does this have any `ReBound` regions? - const HAS_RE_BOUND = 1 << 17; + const HAS_RE_BOUND = 1 << 18; /// Does this have any `Bound` types? - const HAS_TY_BOUND = 1 << 18; + const HAS_TY_BOUND = 1 << 19; /// Does this have any `ConstKind::Bound` consts? - const HAS_CT_BOUND = 1 << 19; + const HAS_CT_BOUND = 1 << 20; /// Does this have any bound variables? /// Used to check if a global bound is safe to evaluate. const HAS_BOUND_VARS = TypeFlags::HAS_RE_BOUND.bits() @@ -111,7 +116,7 @@ bitflags::bitflags! { | TypeFlags::HAS_CT_BOUND.bits(); /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 20; + const HAS_RE_ERASED = 1 << 21; /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? @@ -123,19 +128,19 @@ bitflags::bitflags! { | TypeFlags::HAS_CT_INFER.bits(); /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 21; + const HAS_TY_FRESH = 1 << 22; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 22; + const HAS_CT_FRESH = 1 << 23; /// Does this have any binders with bound vars (e.g. that need to be anonymized)? - const HAS_BINDER_VARS = 1 << 23; + const HAS_BINDER_VARS = 1 << 24; /// Does this type have any coroutines in it? - const HAS_TY_CORO = 1 << 24; + const HAS_TY_CORO = 1 << 25; /// Does this have have a `Bound(BoundVarIndexKind::Canonical, _)`? - const HAS_CANONICAL_BOUND = 1 << 25; + const HAS_CANONICAL_BOUND = 1 << 26; } } @@ -240,7 +245,7 @@ impl FlagComputation { | ty::Str | ty::Foreign(..) => {} - ty::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), + ty::Error(_) => self.add_flags(TypeFlags::HAS_NON_REGION_ERROR), ty::Param(_) => { self.add_flags(TypeFlags::HAS_TY_PARAM); @@ -489,7 +494,7 @@ impl FlagComputation { } } ty::ConstKind::Expr(e) => self.add_args(e.args().as_slice()), - ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR), + ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_NON_REGION_ERROR), } } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 1ee4bff6b7a11..cfb4588965368 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -279,6 +279,8 @@ pub trait TypeVisitableExt: TypeVisitable { fn error_reported(&self) -> Result<(), I::ErrorGuaranteed>; + fn non_region_error_reported(&self) -> Result<(), I::ErrorGuaranteed>; + fn has_non_region_param(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM) } @@ -352,6 +354,11 @@ pub trait TypeVisitableExt: TypeVisitable { fn still_further_specializable(&self) -> bool { self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE) } + + /// True if a type or const error is reachable + fn has_non_region_error(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_NON_REGION_ERROR) + } } impl> TypeVisitableExt for T { @@ -376,6 +383,18 @@ impl> TypeVisitableExt for T { Ok(()) } } + + fn non_region_error_reported(&self) -> Result<(), I::ErrorGuaranteed> { + if self.has_non_region_error() { + if let ControlFlow::Break(guar) = self.visit_with(&mut HasErrorVisitor) { + Err(guar) + } else { + panic!("type flags said there was an non region error, but now there is not") + } + } else { + Ok(()) + } + } } #[derive(Debug, PartialEq, Eq, Copy, Clone)] From 339fb64d1c751de5d781f4c8d51ae37b613ba644 Mon Sep 17 00:00:00 2001 From: Makai Date: Mon, 16 Mar 2026 22:08:32 +0800 Subject: [PATCH 14/38] skip early return for region-only errors in projection types --- .../src/traits/project.rs | 4 +++- .../dropck-after-failed-type-lowering.rs | 3 ++- .../dropck-after-failed-type-lowering.stderr | 24 ++++++++++++++++--- .../unsized/thin-ptr-to-unsized-projection.rs | 6 +++++ .../thin-ptr-to-unsized-projection.stderr | 23 ++++++++++++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 tests/ui/unsized/thin-ptr-to-unsized-projection.rs create mode 100644 tests/ui/unsized/thin-ptr-to-unsized-projection.stderr diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 3df5c9e33438a..72d3ba9629f4d 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -659,7 +659,9 @@ fn project<'cx, 'tcx>( ))); } - if let Err(guar) = obligation.predicate.error_reported() { + // We can still compute a projection type when there are only region errors, + // but type/const errors require early return. + if let Err(guar) = obligation.predicate.non_region_error_reported() { return Ok(Projected::Progress(Progress::error_for_term( selcx.tcx(), obligation.predicate, diff --git a/tests/ui/dropck/dropck-after-failed-type-lowering.rs b/tests/ui/dropck/dropck-after-failed-type-lowering.rs index 2441e26fec96c..ee55e0dcaa491 100644 --- a/tests/ui/dropck/dropck-after-failed-type-lowering.rs +++ b/tests/ui/dropck/dropck-after-failed-type-lowering.rs @@ -3,11 +3,12 @@ trait B { type C<'a>; fn d() -> F { + //~^ ERROR: the trait bound `E: B` is not satisfied todo!() } } struct F { - h: Option<::C>, + h: Option<::C>, //~ ERROR: the trait bound `G: B` is not satisfied //~^ ERROR missing generics for associated type `B::C` } diff --git a/tests/ui/dropck/dropck-after-failed-type-lowering.stderr b/tests/ui/dropck/dropck-after-failed-type-lowering.stderr index 56ea72de0c5f2..0922d2e4340e8 100644 --- a/tests/ui/dropck/dropck-after-failed-type-lowering.stderr +++ b/tests/ui/dropck/dropck-after-failed-type-lowering.stderr @@ -1,5 +1,5 @@ error[E0107]: missing generics for associated type `B::C` - --> $DIR/dropck-after-failed-type-lowering.rs:10:25 + --> $DIR/dropck-after-failed-type-lowering.rs:11:25 | LL | h: Option<::C>, | ^ expected 1 lifetime argument @@ -14,6 +14,24 @@ help: add missing lifetime argument LL | h: Option<::C<'a>>, | ++++ -error: aborting due to 1 previous error +error[E0277]: the trait bound `G: B` is not satisfied + --> $DIR/dropck-after-failed-type-lowering.rs:11:8 + | +LL | h: Option<::C>, + | ^^^^^^^^^^^^^^^^^^^ the trait `B` is not implemented for `G` + | +help: consider restricting type parameter `G` with trait `B` + | +LL | struct F { + | +++ + +error[E0277]: the trait bound `E: B` is not satisfied + --> $DIR/dropck-after-failed-type-lowering.rs:5:18 + | +LL | fn d() -> F { + | ^^^^ the trait `B` is not implemented for `E` + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0107, E0277. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/unsized/thin-ptr-to-unsized-projection.rs b/tests/ui/unsized/thin-ptr-to-unsized-projection.rs new file mode 100644 index 0000000000000..79918e6a0722d --- /dev/null +++ b/tests/ui/unsized/thin-ptr-to-unsized-projection.rs @@ -0,0 +1,6 @@ +// This is a regression test for +struct Foo<'a>(<& /*'a*/ [fn()] as core::ops::Deref>::Target); // adding the lifetime solves the ice + //~^ ERROR: missing lifetime specifier [E0106] +const _: *const Foo = 0 as _; + //~^ ERROR: cannot cast `i32` to a pointer that is wide [E0606] +fn main() {} diff --git a/tests/ui/unsized/thin-ptr-to-unsized-projection.stderr b/tests/ui/unsized/thin-ptr-to-unsized-projection.stderr new file mode 100644 index 0000000000000..256523c4a47ec --- /dev/null +++ b/tests/ui/unsized/thin-ptr-to-unsized-projection.stderr @@ -0,0 +1,23 @@ +error[E0106]: missing lifetime specifier + --> $DIR/thin-ptr-to-unsized-projection.rs:2:17 + | +LL | struct Foo<'a>(<& /*'a*/ [fn()] as core::ops::Deref>::Target); // adding the lifetime solves the ice + | ^ expected named lifetime parameter + | +help: consider using the `'a` lifetime + | +LL | struct Foo<'a>(<&'a /*'a*/ [fn()] as core::ops::Deref>::Target); // adding the lifetime solves the ice + | ++ + +error[E0606]: cannot cast `i32` to a pointer that is wide + --> $DIR/thin-ptr-to-unsized-projection.rs:4:28 + | +LL | const _: *const Foo = 0 as _; + | - ^ creating a `*const Foo<'_>` requires both an address and a length + | | + | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0106, E0606. +For more information about an error, try `rustc --explain E0106`. From fec09987324f37dda32c79f12d2d8c342805ae11 Mon Sep 17 00:00:00 2001 From: Nik Revenco Date: Thu, 26 Mar 2026 21:46:14 +0000 Subject: [PATCH 15/38] Export `derive` at `core::derive` and `std::derive` --- library/core/src/lib.rs | 2 ++ library/core/src/macros/mod.rs | 2 +- library/core/src/prelude/v1.rs | 6 +++++- library/std/src/lib.rs | 2 ++ library/std/src/prelude/v1.rs | 6 +++++- tests/ui/imports/global-derive-path.rs | 10 ++++++++++ 6 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/ui/imports/global-derive-path.rs diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 35f93d8fb33b2..d10d86077bb8c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -227,6 +227,8 @@ pub mod autodiff { #[unstable(feature = "contracts", issue = "128044")] pub mod contracts; +#[unstable(feature = "derive_macro_global_path", issue = "154645")] +pub use crate::macros::builtin::derive; #[stable(feature = "cfg_select", since = "1.95.0")] pub use crate::macros::cfg_select; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 7f35e94d3df30..33397e56b86c5 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1720,7 +1720,7 @@ pub(crate) mod builtin { /// /// See [the reference] for more info. /// - /// [the reference]: ../../../reference/attributes/derive.html + /// [the reference]: ../reference/attributes/derive.html #[stable(feature = "rust1", since = "1.0.0")] #[rustc_builtin_macro] pub macro derive($item:item) { diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index f2eb047d342bc..6122ab12ec351 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -120,9 +120,13 @@ pub use crate::trace_macros; // (no public module for them to be re-exported from). #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use crate::macros::builtin::{ - alloc_error_handler, bench, derive, global_allocator, test, test_case, + alloc_error_handler, bench, global_allocator, test, test_case, }; +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use crate::macros::builtin::derive; + #[unstable(feature = "derive_const", issue = "118304")] pub use crate::macros::builtin::derive_const; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c8c8a6c897142..c6e5176dd1bbb 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -704,6 +704,8 @@ pub use core::cfg_select; reason = "`concat_bytes` is not stable enough for use and is subject to change" )] pub use core::concat_bytes; +#[unstable(feature = "derive_macro_global_path", issue = "154645")] +pub use core::derive; #[stable(feature = "matches_macro", since = "1.42.0")] #[allow(deprecated, deprecated_in_future)] pub use core::matches; diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index ee57e031c959c..aeefec8b9e084 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -115,9 +115,13 @@ pub use core::prelude::v1::trace_macros; // (no public module for them to be re-exported from). #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use core::prelude::v1::{ - alloc_error_handler, bench, derive, global_allocator, test, test_case, + alloc_error_handler, bench, global_allocator, test, test_case, }; +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use core::prelude::v1::derive; + #[unstable(feature = "derive_const", issue = "118304")] pub use core::prelude::v1::derive_const; diff --git a/tests/ui/imports/global-derive-path.rs b/tests/ui/imports/global-derive-path.rs new file mode 100644 index 0000000000000..5f0a6bb86bf30 --- /dev/null +++ b/tests/ui/imports/global-derive-path.rs @@ -0,0 +1,10 @@ +//@ edition: 2024 +//@ check-pass +#![crate_type = "lib"] +#![feature(derive_macro_global_path)] + +#[::core::derive(Clone)] +struct Y; + +#[::std::derive(Clone)] +struct X; From 12f8364d4d65f0641464b3afc8e12d705ddd2344 Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Wed, 1 Apr 2026 09:22:42 +0200 Subject: [PATCH 16/38] Introduce #[diagnostic::on_move] on `Arc` This annotates the `Arc` type with the diagnostic attribute `#[diagnostic::on_move]`. Now when a moved `Arc` is borrowed, a suggestion to clone it is made, with a label explaining why. --- library/alloc/src/lib.rs | 1 + library/alloc/src/sync.rs | 5 +++++ tests/ui/moves/arc-consumed-in-looped-closure.rs | 3 ++- tests/ui/moves/arc-consumed-in-looped-closure.stderr | 6 ++++-- tests/ui/moves/no-capture-arc.rs | 2 +- tests/ui/moves/no-capture-arc.stderr | 5 +++-- tests/ui/moves/no-reuse-move-arc.fixed | 2 +- tests/ui/moves/no-reuse-move-arc.rs | 2 +- tests/ui/moves/no-reuse-move-arc.stderr | 5 +++-- 9 files changed, 21 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index bcd9e092a310f..aff10c4320fe1 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -111,6 +111,7 @@ #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] #![feature(deref_pure_trait)] +#![feature(diagnostic_on_move)] #![feature(dispatch_from_dyn)] #![feature(ergonomic_clones)] #![feature(error_generic_member_access)] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b3e49af868b8c..af1eaf2015e9d 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -261,6 +261,11 @@ macro_rules! acquire { #[rustc_diagnostic_item = "Arc"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] +#[diagnostic::on_move( + message = "the type `{Self}` does not implement `Copy`", + label = "this move could be avoided by cloning the original `{Self}`, which is inexpensive", + note = "consider using `Arc::clone`" +)] pub struct Arc< T: ?Sized, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, diff --git a/tests/ui/moves/arc-consumed-in-looped-closure.rs b/tests/ui/moves/arc-consumed-in-looped-closure.rs index 8700c78508476..6016850838d3c 100644 --- a/tests/ui/moves/arc-consumed-in-looped-closure.rs +++ b/tests/ui/moves/arc-consumed-in-looped-closure.rs @@ -19,7 +19,7 @@ impl ThreadPool { } fn main() { - let results = Arc::new(Mutex::new(Vec::new())); //~ NOTE move occurs because + let results = Arc::new(Mutex::new(Vec::new())); //~ NOTE this move could be avoided by cloning the original `Arc`, which is inexpensive let pool = ThreadPool { workers: vec![], queue: Arc::new(()), @@ -29,6 +29,7 @@ fn main() { // let results = Arc::clone(&results); // Forgot this. pool.execute(move || { //~ ERROR E0382 //~^ NOTE value moved into closure here, in previous iteration of loop + //~| NOTE consider using `Arc::clone` //~| HELP consider cloning the value before moving it into the closure let mut r = results.lock().unwrap(); //~ NOTE use occurs due to use in closure r.push(i); diff --git a/tests/ui/moves/arc-consumed-in-looped-closure.stderr b/tests/ui/moves/arc-consumed-in-looped-closure.stderr index 47d6fd6cbad3b..462eb274ce5af 100644 --- a/tests/ui/moves/arc-consumed-in-looped-closure.stderr +++ b/tests/ui/moves/arc-consumed-in-looped-closure.stderr @@ -1,8 +1,8 @@ -error[E0382]: use of moved value: `results` +error[E0382]: the type `Arc` does not implement `Copy` --> $DIR/arc-consumed-in-looped-closure.rs:30:22 | LL | let results = Arc::new(Mutex::new(Vec::new())); - | ------- move occurs because `results` has type `Arc>>`, which does not implement the `Copy` trait + | ------- this move could be avoided by cloning the original `Arc`, which is inexpensive ... LL | for i in 0..20 { | -------------- inside of this loop @@ -13,12 +13,14 @@ LL | pool.execute(move || { LL | let mut r = results.lock().unwrap(); | ------- use occurs due to use in closure | + = note: consider using `Arc::clone` help: consider cloning the value before moving it into the closure | LL ~ let value = results.clone(); LL ~ pool.execute(move || { LL | LL | +LL | LL ~ let mut r = value.lock().unwrap(); | diff --git a/tests/ui/moves/no-capture-arc.rs b/tests/ui/moves/no-capture-arc.rs index 9c957a4e01b41..eea9858532d4c 100644 --- a/tests/ui/moves/no-capture-arc.rs +++ b/tests/ui/moves/no-capture-arc.rs @@ -9,7 +9,7 @@ fn main() { assert_eq!((*arc_v)[3], 4); }); - assert_eq!((*arc_v)[2], 3); //~ ERROR borrow of moved value: `arc_v` + assert_eq!((*arc_v)[2], 3); //~ ERROR the type `Arc` does not implement `Copy` println!("{:?}", *arc_v); } diff --git a/tests/ui/moves/no-capture-arc.stderr b/tests/ui/moves/no-capture-arc.stderr index 6d4a867fa88d0..e0fce32ba7fc0 100644 --- a/tests/ui/moves/no-capture-arc.stderr +++ b/tests/ui/moves/no-capture-arc.stderr @@ -1,8 +1,8 @@ -error[E0382]: borrow of moved value: `arc_v` +error[E0382]: the type `Arc` does not implement `Copy` --> $DIR/no-capture-arc.rs:12:18 | LL | let arc_v = Arc::new(v); - | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait + | ----- this move could be avoided by cloning the original `Arc`, which is inexpensive LL | LL | thread::spawn(move|| { | ------ value moved into closure here @@ -12,6 +12,7 @@ LL | assert_eq!((*arc_v)[3], 4); LL | assert_eq!((*arc_v)[2], 3); | ^^^^^ value borrowed here after move | + = note: consider using `Arc::clone` = note: borrow occurs due to deref coercion to `Vec` help: consider cloning the value before moving it into the closure | diff --git a/tests/ui/moves/no-reuse-move-arc.fixed b/tests/ui/moves/no-reuse-move-arc.fixed index a5dac8cc14bf2..c9094cb78a391 100644 --- a/tests/ui/moves/no-reuse-move-arc.fixed +++ b/tests/ui/moves/no-reuse-move-arc.fixed @@ -11,7 +11,7 @@ fn main() { assert_eq!((*value)[3], 4); }); - assert_eq!((*arc_v)[2], 3); //~ ERROR borrow of moved value: `arc_v` + assert_eq!((*arc_v)[2], 3); //~ ERROR the type `Arc` does not implement `Copy` println!("{:?}", *arc_v); } diff --git a/tests/ui/moves/no-reuse-move-arc.rs b/tests/ui/moves/no-reuse-move-arc.rs index 0d67aa56489ce..29452220aebcc 100644 --- a/tests/ui/moves/no-reuse-move-arc.rs +++ b/tests/ui/moves/no-reuse-move-arc.rs @@ -10,7 +10,7 @@ fn main() { assert_eq!((*arc_v)[3], 4); }); - assert_eq!((*arc_v)[2], 3); //~ ERROR borrow of moved value: `arc_v` + assert_eq!((*arc_v)[2], 3); //~ ERROR the type `Arc` does not implement `Copy` println!("{:?}", *arc_v); } diff --git a/tests/ui/moves/no-reuse-move-arc.stderr b/tests/ui/moves/no-reuse-move-arc.stderr index aff979af905e4..8f56b32f96a89 100644 --- a/tests/ui/moves/no-reuse-move-arc.stderr +++ b/tests/ui/moves/no-reuse-move-arc.stderr @@ -1,8 +1,8 @@ -error[E0382]: borrow of moved value: `arc_v` +error[E0382]: the type `Arc` does not implement `Copy` --> $DIR/no-reuse-move-arc.rs:13:18 | LL | let arc_v = Arc::new(v); - | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait + | ----- this move could be avoided by cloning the original `Arc`, which is inexpensive LL | LL | thread::spawn(move|| { | ------ value moved into closure here @@ -12,6 +12,7 @@ LL | assert_eq!((*arc_v)[3], 4); LL | assert_eq!((*arc_v)[2], 3); | ^^^^^ value borrowed here after move | + = note: consider using `Arc::clone` = note: borrow occurs due to deref coercion to `Vec` help: consider cloning the value before moving it into the closure | From 253c03aa9d1e810cc4226fce7ccb1431dd4108d0 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Tue, 31 Mar 2026 17:57:34 +0100 Subject: [PATCH 17/38] Add a test for a past ICE when calling a const fn with the wrong number of arguments --- .../ice-extra-args-fn-abi-issue-127423.rs | 18 +++++++++ .../ice-extra-args-fn-abi-issue-127423.stderr | 39 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs create mode 100644 tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr diff --git a/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs new file mode 100644 index 0000000000000..4b87ae6080698 --- /dev/null +++ b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs @@ -0,0 +1,18 @@ +// #127423: ICE: assertion failed: sig.c_variadic || extra_args.is_empty() +// Calling a const fn with the wrong number of arguments where the function has +// an unresolved type should not trigger an ICE. +// issue: rust-lang/rust#127423 + +#![allow(dead_code)] + +const fn add(a: &'self isize) -> usize { + //~^ ERROR use of undeclared lifetime name `'self` + //~| ERROR lifetimes cannot use keyword names + Qux + y + //~^ ERROR cannot find value `Qux` in this scope + //~| ERROR cannot find value `y` in this scope +} + +const ARR: [i32; add(1, 2)] = [5, 6, 7]; + +pub fn main() {} diff --git a/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr new file mode 100644 index 0000000000000..a5b2f5a578150 --- /dev/null +++ b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr @@ -0,0 +1,39 @@ +error: lifetimes cannot use keyword names + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:8:18 + | +LL | const fn add(a: &'self isize) -> usize { + | ^^^^^ + +error[E0261]: use of undeclared lifetime name `'self` + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:8:18 + | +LL | const fn add(a: &'self isize) -> usize { + | ^^^^^ undeclared lifetime + | +help: consider introducing lifetime `'self` here + | +LL | const fn add<'self>(a: &'self isize) -> usize { + | +++++++ + +error[E0425]: cannot find value `Qux` in this scope + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:11:5 + | +LL | Qux + y + | ^^^ not found in this scope + +error[E0425]: cannot find value `y` in this scope + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:11:11 + | +LL | Qux + y + | ^ + | +help: a local variable with a similar name exists + | +LL - Qux + y +LL + Qux + a + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0261, E0425. +For more information about an error, try `rustc --explain E0261`. From a7a7938d2e08d8ad78cd999d344ce9419cc65c52 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 14:31:35 +0200 Subject: [PATCH 18/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `inline::load_attrs` function --- src/librustdoc/clean/inline.rs | 7 ++++--- src/librustdoc/clean/mod.rs | 6 +++--- src/librustdoc/passes/propagate_doc_cfg.rs | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 09b2bc5dcef1d..1b4bcc18ca9a8 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -218,10 +218,10 @@ pub(crate) fn try_inline_glob( } } -pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [hir::Attribute] { +pub(crate) fn load_attrs<'hir>(tcx: TyCtxt<'hir>, did: DefId) -> &'hir [hir::Attribute] { // FIXME: all uses should use `find_attr`! #[allow(deprecated)] - cx.tcx.get_all_attrs(did) + tcx.get_all_attrs(did) } pub(crate) fn item_relative_path(tcx: TyCtxt<'_>, def_id: DefId) -> Vec { @@ -623,7 +623,8 @@ pub(crate) fn build_impl( // doesn't matter at this point. // // We need to pass this empty `CfgInfo` because `merge_attrs` is used when computing the `cfg`. - let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs, &mut CfgInfo::default()); + let (merged_attrs, cfg) = + merge_attrs(cx, load_attrs(cx.tcx, did), attrs, &mut CfgInfo::default()); trace!("merged_attrs={merged_attrs:?}"); trace!( diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f54339429fa58..e0fcd3184f36d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -184,7 +184,7 @@ fn generate_item_with_correct_attrs( import_ids: &[LocalDefId], renamed: Option, ) -> Item { - let target_attrs = inline::load_attrs(cx, def_id); + let target_attrs = inline::load_attrs(cx.tcx, def_id); let attrs = if !import_ids.is_empty() { let mut attrs = Vec::with_capacity(import_ids.len()); let mut is_inline = false; @@ -196,7 +196,7 @@ fn generate_item_with_correct_attrs( // cfgs on the path up until the glob can be removed, and only cfgs on the globbed item // itself matter), for non-inlined re-exports see #85043. let import_is_inline = find_attr!( - inline::load_attrs(cx, import_id.to_def_id()), + inline::load_attrs(cx.tcx, import_id.to_def_id()), Doc(d) if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) ) || (is_glob_import(cx.tcx, import_id) @@ -2663,7 +2663,7 @@ fn get_all_import_attributes<'hir>( .iter() .flat_map(|reexport| reexport.id()) { - let import_attrs = inline::load_attrs(cx, def_id); + let import_attrs = inline::load_attrs(cx.tcx, def_id); if first { // This is the "original" reexport so we get all its attributes without filtering them. attrs = import_attrs.iter().map(|attr| (Cow::Borrowed(attr), Some(def_id))).collect(); diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index f73db253af062..3b4a2c752ca01 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -64,7 +64,7 @@ impl CfgPropagator<'_, '_> { && let Some(mut next_def_id) = item.item_id.as_local_def_id() { while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) { - let x = load_attrs(self.cx, parent_def_id.to_def_id()); + let x = load_attrs(self.cx.tcx, parent_def_id.to_def_id()); add_only_cfg_attributes(&mut attrs, x); next_def_id = parent_def_id; } From 65fbfa4e814cf9097856f10f4c2821b0298a970b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 14:34:55 +0200 Subject: [PATCH 19/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `inline::merge_attrs` function --- src/librustdoc/clean/inline.rs | 11 ++++------- src/librustdoc/passes/propagate_doc_cfg.rs | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 1b4bcc18ca9a8..5b9f8a14506ee 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -416,7 +416,7 @@ pub(crate) fn build_impls( } pub(crate) fn merge_attrs( - cx: &mut DocContext<'_>, + tcx: TyCtxt<'_>, old_attrs: &[hir::Attribute], new_attrs: Option<(&[hir::Attribute], Option)>, cfg_info: &mut CfgInfo, @@ -434,13 +434,10 @@ pub(crate) fn merge_attrs( } else { Attributes::from_hir(&both) }, - extract_cfg_from_attrs(both.iter(), cx.tcx, cfg_info), + extract_cfg_from_attrs(both.iter(), tcx, cfg_info), ) } else { - ( - Attributes::from_hir(old_attrs), - extract_cfg_from_attrs(old_attrs.iter(), cx.tcx, cfg_info), - ) + (Attributes::from_hir(old_attrs), extract_cfg_from_attrs(old_attrs.iter(), tcx, cfg_info)) } } @@ -624,7 +621,7 @@ pub(crate) fn build_impl( // // We need to pass this empty `CfgInfo` because `merge_attrs` is used when computing the `cfg`. let (merged_attrs, cfg) = - merge_attrs(cx, load_attrs(cx.tcx, did), attrs, &mut CfgInfo::default()); + merge_attrs(cx.tcx, load_attrs(cx.tcx, did), attrs, &mut CfgInfo::default()); trace!("merged_attrs={merged_attrs:?}"); trace!( diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 3b4a2c752ca01..92798bb9bb011 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -71,7 +71,7 @@ impl CfgPropagator<'_, '_> { } let (_, cfg) = merge_attrs( - self.cx, + self.cx.tcx, item.attrs.other_attrs.as_slice(), Some((&attrs, None)), &mut self.cfg_info, From 9f95c3830d8a12874dd398a9efc62ab34d6bf907 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 14:39:42 +0200 Subject: [PATCH 20/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `utils::display_macro_source` function --- src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/clean/utils.rs | 15 +++++---------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 5b9f8a14506ee..58951fee8b7a6 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -779,7 +779,7 @@ fn build_macro( // kinds LoadedMacro::MacroDef { def, .. } => match macro_kinds { MacroKinds::BANG => clean::MacroItem(clean::Macro { - source: utils::display_macro_source(cx, name, &def), + source: utils::display_macro_source(cx.tcx, name, &def), macro_rules: def.macro_rules, }), MacroKinds::DERIVE => clean::ProcMacroItem(clean::ProcMacro { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e0fcd3184f36d..1e109e03afcc4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2861,7 +2861,7 @@ fn clean_maybe_renamed_item<'tcx>( // FIXME: handle attributes and derives that aren't proc macros, and macros with // multiple kinds ItemKind::Macro(_, macro_def, MacroKinds::BANG) => MacroItem(Macro { - source: display_macro_source(cx, name, macro_def), + source: display_macro_source(cx.tcx, name, macro_def), macro_rules: macro_def.macro_rules, }), ItemKind::Macro(_, _, MacroKinds::ATTR) => { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 834b73997c959..992d78332b933 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -602,26 +602,21 @@ fn render_macro_arms<'a>( out } -pub(super) fn display_macro_source( - cx: &mut DocContext<'_>, - name: Symbol, - def: &ast::MacroDef, -) -> String { +pub(super) fn display_macro_source(tcx: TyCtxt<'_>, name: Symbol, def: &ast::MacroDef) -> String { // Extract the spans of all matchers. They represent the "interface" of the macro. let matchers = def.body.tokens.chunks(4).map(|arm| &arm[0]); if def.macro_rules { - format!("macro_rules! {name} {{\n{arms}}}", arms = render_macro_arms(cx.tcx, matchers, ";")) + format!("macro_rules! {name} {{\n{arms}}}", arms = render_macro_arms(tcx, matchers, ";")) } else { if matchers.len() <= 1 { format!( "macro {name}{matchers} {{\n ...\n}}", - matchers = matchers - .map(|matcher| render_macro_matcher(cx.tcx, matcher)) - .collect::(), + matchers = + matchers.map(|matcher| render_macro_matcher(tcx, matcher)).collect::(), ) } else { - format!("macro {name} {{\n{arms}}}", arms = render_macro_arms(cx.tcx, matchers, ",")) + format!("macro {name} {{\n{arms}}}", arms = render_macro_arms(tcx, matchers, ",")) } } } From f3ba2c41e63ac58ae8aafbe7725db6b5e6021d00 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 14:41:59 +0200 Subject: [PATCH 21/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `inline::build_macro` function --- src/librustdoc/clean/inline.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 58951fee8b7a6..a8fb32b550856 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -137,7 +137,7 @@ pub(crate) fn try_inline( }) } Res::Def(DefKind::Macro(kinds), did) => { - let mac = build_macro(cx, did, name, kinds); + let mac = build_macro(cx.tcx, did, name, kinds); // FIXME: handle attributes and derives that aren't proc macros, and macros with // multiple kinds @@ -769,17 +769,17 @@ fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::St } fn build_macro( - cx: &mut DocContext<'_>, + tcx: TyCtxt<'_>, def_id: DefId, name: Symbol, macro_kinds: MacroKinds, ) -> clean::ItemKind { - match CStore::from_tcx(cx.tcx).load_macro_untracked(cx.tcx, def_id) { + match CStore::from_tcx(tcx).load_macro_untracked(tcx, def_id) { // FIXME: handle attributes and derives that aren't proc macros, and macros with multiple // kinds LoadedMacro::MacroDef { def, .. } => match macro_kinds { MacroKinds::BANG => clean::MacroItem(clean::Macro { - source: utils::display_macro_source(cx.tcx, name, &def), + source: utils::display_macro_source(tcx, name, &def), macro_rules: def.macro_rules, }), MacroKinds::DERIVE => clean::ProcMacroItem(clean::ProcMacro { From 58077cc37ab38a4e7a8c3ad5d378e3bf83def129 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 14:49:22 +0200 Subject: [PATCH 22/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `clean::clean_middle_region` function --- src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 14 +++++++------- src/librustdoc/clean/utils.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index a8fb32b550856..566159ecfa040 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -388,8 +388,8 @@ pub(crate) fn build_impls( attrs: Option<(&[hir::Attribute], Option)>, ret: &mut Vec, ) { - let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls"); let tcx = cx.tcx; + let _prof_timer = tcx.sess.prof.generic_activity("build_inherent_impls"); // for each implementation of an item represented by `did`, build the clean::Item for that impl for &did in tcx.inherent_impls(did).iter() { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1e109e03afcc4..86db3521babaf 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -350,9 +350,9 @@ pub(crate) fn clean_middle_const<'tcx>( pub(crate) fn clean_middle_region<'tcx>( region: ty::Region<'tcx>, - cx: &mut DocContext<'tcx>, + tcx: TyCtxt<'tcx>, ) -> Option { - region.get_name(cx.tcx).map(Lifetime) + region.get_name(tcx).map(Lifetime) } fn clean_where_predicate<'tcx>( @@ -436,9 +436,9 @@ fn clean_region_outlives_predicate<'tcx>( let ty::OutlivesPredicate(a, b) = pred; WherePredicate::RegionPredicate { - lifetime: clean_middle_region(a, cx).expect("failed to clean lifetime"), + lifetime: clean_middle_region(a, cx.tcx).expect("failed to clean lifetime"), bounds: vec![GenericBound::Outlives( - clean_middle_region(b, cx).expect("failed to clean bounds"), + clean_middle_region(b, cx.tcx).expect("failed to clean bounds"), )], } } @@ -452,7 +452,7 @@ fn clean_type_outlives_predicate<'tcx>( WherePredicate::BoundPredicate { ty: clean_middle_ty(pred.rebind(ty), cx, None, None), bounds: vec![GenericBound::Outlives( - clean_middle_region(lt, cx).expect("failed to clean lifetimes"), + clean_middle_region(lt, cx.tcx).expect("failed to clean lifetimes"), )], bound_params: Vec::new(), } @@ -2067,7 +2067,7 @@ pub(crate) fn clean_middle_ty<'tcx>( RawPointer(mutbl, Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None))) } ty::Ref(r, ty, mutbl) => BorrowedRef { - lifetime: clean_middle_region(r, cx), + lifetime: clean_middle_region(r, cx.tcx), mutability: mutbl, type_: Box::new(clean_middle_ty( bound_ty.rebind(ty), @@ -2301,7 +2301,7 @@ fn clean_middle_opaque_bounds<'tcx>( let trait_ref = match bound_predicate.skip_binder() { ty::ClauseKind::Trait(tr) => bound_predicate.rebind(tr.trait_ref), ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_ty, reg)) => { - return clean_middle_region(reg, cx).map(GenericBound::Outlives); + return clean_middle_region(reg, cx.tcx).map(GenericBound::Outlives); } _ => return None, }; diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 992d78332b933..c0cb2f501c348 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -135,7 +135,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( match arg.skip_binder().kind() { GenericArgKind::Lifetime(lt) => Some(GenericArg::Lifetime( - clean_middle_region(lt, cx).unwrap_or(Lifetime::elided()), + clean_middle_region(lt, cx.tcx).unwrap_or(Lifetime::elided()), )), GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty( arg.rebind(ty), From 5a48d5e8cfa37049bec7c2205a20061b34571a56 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 14:51:55 +0200 Subject: [PATCH 23/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `clean::clean_region_outlives_predicate` function --- src/librustdoc/clean/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 86db3521babaf..f3d657be00543 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -394,7 +394,7 @@ pub(crate) fn clean_predicate<'tcx>( let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { ty::ClauseKind::Trait(pred) => clean_poly_trait_predicate(bound_predicate.rebind(pred), cx), - ty::ClauseKind::RegionOutlives(pred) => Some(clean_region_outlives_predicate(pred, cx)), + ty::ClauseKind::RegionOutlives(pred) => Some(clean_region_outlives_predicate(pred, cx.tcx)), ty::ClauseKind::TypeOutlives(pred) => { Some(clean_type_outlives_predicate(bound_predicate.rebind(pred), cx)) } @@ -431,14 +431,14 @@ fn clean_poly_trait_predicate<'tcx>( fn clean_region_outlives_predicate<'tcx>( pred: ty::RegionOutlivesPredicate<'tcx>, - cx: &mut DocContext<'tcx>, + tcx: TyCtxt<'tcx>, ) -> WherePredicate { let ty::OutlivesPredicate(a, b) = pred; WherePredicate::RegionPredicate { - lifetime: clean_middle_region(a, cx.tcx).expect("failed to clean lifetime"), + lifetime: clean_middle_region(a, tcx).expect("failed to clean lifetime"), bounds: vec![GenericBound::Outlives( - clean_middle_region(b, cx.tcx).expect("failed to clean bounds"), + clean_middle_region(b, tcx).expect("failed to clean bounds"), )], } } From d77a2ea613bebf410a3920ccd0681b3c102f8b6a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 14:58:05 +0200 Subject: [PATCH 24/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `clean::clean_proc_macro` function --- src/librustdoc/clean/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f3d657be00543..96fc753bff661 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1001,12 +1001,12 @@ fn clean_proc_macro<'tcx>( item: &hir::Item<'tcx>, name: &mut Symbol, kind: MacroKind, - cx: &mut DocContext<'tcx>, + tcx: TyCtxt<'tcx>, ) -> ItemKind { if kind != MacroKind::Derive { return ProcMacroItem(ProcMacro { kind, helpers: vec![] }); } - let attrs = cx.tcx.hir_attrs(item.hir_id()); + let attrs = tcx.hir_attrs(item.hir_id()); let Some((trait_name, helper_attrs)) = find_attr!(attrs, ProcMacroDerive { trait_name, helper_attrs, ..} => (*trait_name, helper_attrs)) else { return ProcMacroItem(ProcMacro { kind, helpers: vec![] }); @@ -1037,7 +1037,7 @@ fn clean_fn_or_proc_macro<'tcx>( }; match macro_kind { - Some(kind) => clean_proc_macro(item, name, kind, cx), + Some(kind) => clean_proc_macro(item, name, kind, cx.tcx), None => { let mut func = clean_function(cx, sig, generics, ParamsSrc::Body(body_id)); clean_fn_decl_legacy_const_generics(&mut func, attrs); @@ -2865,10 +2865,10 @@ fn clean_maybe_renamed_item<'tcx>( macro_rules: macro_def.macro_rules, }), ItemKind::Macro(_, _, MacroKinds::ATTR) => { - clean_proc_macro(item, &mut name, MacroKind::Attr, cx) + clean_proc_macro(item, &mut name, MacroKind::Attr, cx.tcx) } ItemKind::Macro(_, _, MacroKinds::DERIVE) => { - clean_proc_macro(item, &mut name, MacroKind::Derive, cx) + clean_proc_macro(item, &mut name, MacroKind::Derive, cx.tcx) } ItemKind::Macro(_, _, _) => todo!("Handle macros with multiple kinds"), // proc macros can have a name set by attributes From 3520351527bb60ad91d038b9f51adacd29baa8ac Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 15:04:34 +0200 Subject: [PATCH 25/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `Type::from_def_id_and_parts` function --- src/librustdoc/clean/mod.rs | 24 ++++++++++++------------ src/librustdoc/clean/types.rs | 4 ++-- src/librustdoc/clean/utils.rs | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 96fc753bff661..9910cd2bd0201 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1248,7 +1248,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext RequiredAssocTypeItem(generics, bounds) } }; - Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx) + Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx.tcx) }) } @@ -1289,7 +1289,7 @@ pub(crate) fn clean_impl_item<'tcx>( } }; - Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx) + Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx.tcx) }) } @@ -1517,7 +1517,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } }; - Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name()), kind, cx) + Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name()), kind, cx.tcx) } fn first_non_private_clean_path<'tcx>( @@ -2399,7 +2399,7 @@ pub(crate) fn clean_field_with_def_id( ty: Type, cx: &mut DocContext<'_>, ) -> Item { - Item::from_def_id_and_parts(def_id, Some(name), StructFieldItem(ty), cx) + Item::from_def_id_and_parts(def_id, Some(name), StructFieldItem(ty), cx.tcx) } pub(crate) fn clean_variant_def(variant: &ty::VariantDef, cx: &mut DocContext<'_>) -> Item { @@ -2422,7 +2422,7 @@ pub(crate) fn clean_variant_def(variant: &ty::VariantDef, cx: &mut DocContext<'_ variant.def_id, Some(variant.name), VariantItem(Variant { kind, discriminant }), - cx, + cx.tcx, ) } @@ -2499,7 +2499,7 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( variant.def_id, Some(variant.name), VariantItem(Variant { kind, discriminant }), - cx, + cx.tcx, ) } @@ -2907,7 +2907,7 @@ fn clean_maybe_renamed_item<'tcx>( fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx)); - Item::from_def_id_and_parts(variant.def_id.to_def_id(), Some(variant.ident.name), kind, cx) + Item::from_def_id_and_parts(variant.def_id.to_def_id(), Some(variant.ident.name), kind, cx.tcx) } fn clean_impl<'tcx>( @@ -2928,7 +2928,7 @@ fn clean_impl<'tcx>( def_id.to_def_id(), None, PlaceholderImplItem, - cx, + tcx, )]; } Some(clean_trait_ref(&t.trait_ref, cx)) @@ -2983,7 +2983,7 @@ fn clean_impl<'tcx>( }, is_deprecated, })); - Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, cx) + Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, tcx) }; if let Some(type_alias) = type_alias { ret.push(make_item(trait_.clone(), type_alias, items.clone())); @@ -3031,7 +3031,7 @@ fn clean_extern_crate<'tcx>( krate_owner_def_id.to_def_id(), Some(name), ExternCrateItem { src: orig_name }, - cx, + cx.tcx, )] } @@ -3160,14 +3160,14 @@ fn clean_use_statement_inner<'tcx>( import_def_id.to_def_id(), None, ImportItem(Import::new_simple(name, resolve_use_source(cx, path), false)), - cx, + cx.tcx, )); return items; } Import::new_simple(name, resolve_use_source(cx, path), true) }; - vec![Item::from_def_id_and_parts(import_def_id.to_def_id(), None, ImportItem(inner), cx)] + vec![Item::from_def_id_and_parts(import_def_id.to_def_id(), None, ImportItem(inner), cx.tcx)] } fn clean_maybe_renamed_foreign_item<'tcx>( diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ad70fc1096691..e49efec9d2c40 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -514,10 +514,10 @@ impl Item { def_id: DefId, name: Option, kind: ItemKind, - cx: &mut DocContext<'_>, + tcx: TyCtxt<'_>, ) -> Item { #[allow(deprecated)] - let hir_attrs = cx.tcx.get_all_attrs(def_id); + let hir_attrs = tcx.get_all_attrs(def_id); Self::from_def_id_and_attrs_and_parts( def_id, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index c0cb2f501c348..2f859d2c3b4e8 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -72,14 +72,14 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate { def_id, Some(prim.as_sym()), ItemKind::PrimitiveItem(prim), - cx, + cx.tcx, ) })); m.items.extend(keywords.map(|(def_id, kw)| { - Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem, cx) + Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem, cx.tcx) })); m.items.extend(documented_attributes.into_iter().map(|(def_id, kw)| { - Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::AttributeItem, cx) + Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::AttributeItem, cx.tcx) })); } From 509a444d39edc5875f537e6e996646566a374556 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 15:11:13 +0200 Subject: [PATCH 26/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `clean::clean_field_with_def_id` function --- src/librustdoc/clean/mod.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9910cd2bd0201..8b0c9fad9f688 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2376,7 +2376,12 @@ fn clean_middle_opaque_bounds<'tcx>( } pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item { - clean_field_with_def_id(field.def_id.to_def_id(), field.ident.name, clean_ty(field.ty, cx), cx) + clean_field_with_def_id( + field.def_id.to_def_id(), + field.ident.name, + clean_ty(field.ty, cx), + cx.tcx, + ) } pub(crate) fn clean_middle_field(field: &ty::FieldDef, cx: &mut DocContext<'_>) -> Item { @@ -2389,7 +2394,7 @@ pub(crate) fn clean_middle_field(field: &ty::FieldDef, cx: &mut DocContext<'_>) Some(field.did), None, ), - cx, + cx.tcx, ) } @@ -2397,9 +2402,9 @@ pub(crate) fn clean_field_with_def_id( def_id: DefId, name: Symbol, ty: Type, - cx: &mut DocContext<'_>, + tcx: TyCtxt<'_>, ) -> Item { - Item::from_def_id_and_parts(def_id, Some(name), StructFieldItem(ty), cx.tcx) + Item::from_def_id_and_parts(def_id, Some(name), StructFieldItem(ty), tcx) } pub(crate) fn clean_variant_def(variant: &ty::VariantDef, cx: &mut DocContext<'_>) -> Item { @@ -2463,7 +2468,7 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( field.did, field.name, clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), - cx, + cx.tcx, ) }) .collect(), @@ -2488,7 +2493,7 @@ pub(crate) fn clean_variant_def_with_args<'tcx>( field.did, field.name, clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None), - cx, + cx.tcx, ) }) .collect(), From 822579e6e4f6c71f5feb5b5635c82803aa55a55e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 15:22:31 +0200 Subject: [PATCH 27/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `types::GenericBound` methods --- src/librustdoc/clean/inline.rs | 4 ++-- src/librustdoc/clean/mod.rs | 8 ++++---- src/librustdoc/clean/simplify.rs | 4 ++-- src/librustdoc/clean/types.rs | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 566159ecfa040..53655cbe5c200 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -289,7 +289,7 @@ pub(crate) fn build_trait(cx: &mut DocContext<'_>, did: DefId) -> clean::Trait { supertrait_bounds.retain(|b| { // FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized` // is shown and none of the new sizedness traits leak into documentation. - !b.is_meta_sized_bound(cx) + !b.is_meta_sized_bound(cx.tcx) }); clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds } @@ -302,7 +302,7 @@ fn build_trait_alias(cx: &mut DocContext<'_>, did: DefId) -> clean::TraitAlias { bounds.retain(|b| { // FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized` // is shown and none of the new sizedness traits leak into documentation. - !b.is_meta_sized_bound(cx) + !b.is_meta_sized_bound(cx.tcx) }); clean::TraitAlias { generics, bounds } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8b0c9fad9f688..33da99f65407c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -898,10 +898,10 @@ fn clean_ty_generics_inner<'tcx>( for (idx, mut bounds) in impl_trait { let mut has_sized = false; bounds.retain(|b| { - if b.is_sized_bound(cx) { + if b.is_sized_bound(cx.tcx) { has_sized = true; false - } else if b.is_meta_sized_bound(cx) { + } else if b.is_meta_sized_bound(cx.tcx) { // FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized` // is shown and none of the new sizedness traits leak into documentation. false @@ -1459,7 +1459,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo bounds.retain(|b| { // FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized` // is shown and none of the new sizedness traits leak into documentation. - !b.is_meta_sized_bound(cx) + !b.is_meta_sized_bound(cx.tcx) }); // Our Sized/?Sized bound didn't get handled when creating the generics @@ -1467,7 +1467,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo // (some of them may have come from the trait). If we do have a sized // bound, we remove it, and if we don't then we add the `?Sized` bound // at the end. - match bounds.iter().position(|b| b.is_sized_bound(cx)) { + match bounds.iter().position(|b| b.is_sized_bound(cx.tcx)) { Some(i) => { bounds.remove(i); } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index 6fb878ea54856..c0ee741503ad9 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -139,10 +139,10 @@ pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generi return true; }; - if bounds.iter().any(|b| b.is_sized_bound(cx)) { + if bounds.iter().any(|b| b.is_sized_bound(cx.tcx)) { sized_params.insert(*param); false - } else if bounds.iter().any(|b| b.is_meta_sized_bound(cx)) { + } else if bounds.iter().any(|b| b.is_meta_sized_bound(cx.tcx)) { // FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized` // is shown and none of the new sizedness traits leak into documentation. false diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index e49efec9d2c40..cd5a3346b26d0 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1119,20 +1119,20 @@ impl GenericBound { matches!(self, Self::TraitBound(..)) } - pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool { - self.is_bounded_by_lang_item(cx, LangItem::Sized) + pub(crate) fn is_sized_bound(&self, tcx: TyCtxt<'_>) -> bool { + self.is_bounded_by_lang_item(tcx, LangItem::Sized) } - pub(crate) fn is_meta_sized_bound(&self, cx: &DocContext<'_>) -> bool { - self.is_bounded_by_lang_item(cx, LangItem::MetaSized) + pub(crate) fn is_meta_sized_bound(&self, tcx: TyCtxt<'_>) -> bool { + self.is_bounded_by_lang_item(tcx, LangItem::MetaSized) } - fn is_bounded_by_lang_item(&self, cx: &DocContext<'_>, lang_item: LangItem) -> bool { + fn is_bounded_by_lang_item(&self, tcx: TyCtxt<'_>, lang_item: LangItem) -> bool { if let GenericBound::TraitBound( PolyTrait { ref trait_, .. }, rustc_hir::TraitBoundModifiers::NONE, ) = *self - && cx.tcx.is_lang_item(trait_.def_id(), lang_item) + && tcx.is_lang_item(trait_.def_id(), lang_item) { return true; } From e2f3f78593f79488808d6f9eb6dca8e2f4bab57d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 15:29:09 +0200 Subject: [PATCH 28/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `clean::clean_bound_vars` function --- src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 53655cbe5c200..96e15eac0a920 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -312,7 +312,7 @@ pub(super) fn build_function(cx: &mut DocContext<'_>, def_id: DefId) -> Box( GenericBound::TraitBound( PolyTrait { trait_: clean_trait_ref_with_constraints(cx, poly_trait_ref, constraints), - generic_params: clean_bound_vars(poly_trait_ref.bound_vars(), cx), + generic_params: clean_bound_vars(poly_trait_ref.bound_vars(), cx.tcx), }, hir::TraitBoundModifiers::NONE, ) @@ -2080,7 +2080,7 @@ pub(crate) fn clean_middle_ty<'tcx>( // FIXME: should we merge the outer and inner binders somehow? let sig = bound_ty.skip_binder().fn_sig(cx.tcx); let decl = clean_poly_fn_sig(cx, None, sig); - let generic_params = clean_bound_vars(sig.bound_vars(), cx); + let generic_params = clean_bound_vars(sig.bound_vars(), cx.tcx); BareFunction(Box::new(BareFunctionDecl { safety: sig.safety(), @@ -2090,7 +2090,7 @@ pub(crate) fn clean_middle_ty<'tcx>( })) } ty::UnsafeBinder(inner) => { - let generic_params = clean_bound_vars(inner.bound_vars(), cx); + let generic_params = clean_bound_vars(inner.bound_vars(), cx.tcx); let ty = clean_middle_ty(inner.into(), cx, None, None); UnsafeBinder(Box::new(UnsafeBinderTy { generic_params, ty })) } @@ -3238,13 +3238,13 @@ fn clean_assoc_item_constraint<'tcx>( fn clean_bound_vars<'tcx>( bound_vars: &ty::List>, - cx: &mut DocContext<'tcx>, + tcx: TyCtxt<'tcx>, ) -> Vec { bound_vars .into_iter() .filter_map(|var| match var { ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id)) => { - let name = cx.tcx.item_name(def_id); + let name = tcx.item_name(def_id); if name != kw::UnderscoreLifetime { Some(GenericParamDef::lifetime(def_id, name)) } else { @@ -3252,7 +3252,7 @@ fn clean_bound_vars<'tcx>( } } ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => { - let name = cx.tcx.item_name(def_id); + let name = tcx.item_name(def_id); Some(GenericParamDef { name, def_id, From 28ca36d4853d2af68f334197624e885805c1cc76 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 15:41:36 +0200 Subject: [PATCH 29/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `simplify` functions --- src/librustdoc/clean/auto_trait.rs | 2 +- src/librustdoc/clean/mod.rs | 4 ++-- src/librustdoc/clean/simplify.rs | 15 ++++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 847e688d03d0a..fc61103d939fb 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -203,7 +203,7 @@ fn clean_param_env<'tcx>( let mut generics = clean::Generics { params, where_predicates }; simplify::sized_bounds(cx, &mut generics); - generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics.where_predicates = simplify::where_clauses(cx.tcx, generics.where_predicates); generics } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 11481c25862d6..6bb585aa8933a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -925,7 +925,7 @@ fn clean_ty_generics_inner<'tcx>( if let Some(proj) = impl_trait_proj.remove(&idx) { for (trait_did, name, rhs) in proj { let rhs = clean_middle_term(rhs, cx); - simplify::merge_bounds(cx, &mut bounds, trait_did, name, &rhs); + simplify::merge_bounds(cx.tcx, &mut bounds, trait_did, name, &rhs); } } @@ -939,7 +939,7 @@ fn clean_ty_generics_inner<'tcx>( let mut generics = Generics { params, where_predicates }; simplify::sized_bounds(cx, &mut generics); - generics.where_predicates = simplify::where_clauses(cx, generics.where_predicates); + generics.where_predicates = simplify::where_clauses(cx.tcx, generics.where_predicates); generics } diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index c0ee741503ad9..154f31e89dbff 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -15,12 +15,13 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::DefId; +use rustc_middle::ty::TyCtxt; use crate::clean; use crate::clean::{GenericArgs as PP, WherePredicate as WP}; use crate::core::DocContext; -pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVec { +pub(crate) fn where_clauses(tcx: TyCtxt<'_>, clauses: ThinVec) -> ThinVec { // First, partition the where clause into its separate components. // // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to @@ -47,7 +48,7 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVe // general bound predicates. equalities.retain(|(lhs, rhs)| { let Some((bounds, _)) = tybounds.get_mut(&lhs.self_type) else { return true }; - merge_bounds(cx, bounds, lhs.trait_.as_ref().unwrap().def_id(), lhs.assoc.clone(), rhs) + merge_bounds(tcx, bounds, lhs.trait_.as_ref().unwrap().def_id(), lhs.assoc.clone(), rhs) }); // And finally, let's reassemble everything @@ -65,7 +66,7 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: ThinVec) -> ThinVe } pub(crate) fn merge_bounds( - cx: &clean::DocContext<'_>, + tcx: TyCtxt<'_>, bounds: &mut [clean::GenericBound], trait_did: DefId, assoc: clean::PathSegment, @@ -79,7 +80,7 @@ pub(crate) fn merge_bounds( // If this QPath's trait `trait_did` is the same as, or a supertrait // of, the bound's trait `did` then we can keep going, otherwise // this is just a plain old equality bound. - if !trait_is_same_or_supertrait(cx, trait_ref.trait_.def_id(), trait_did) { + if !trait_is_same_or_supertrait(tcx, trait_ref.trait_.def_id(), trait_did) { return false; } let last = trait_ref.trait_.segments.last_mut().expect("segments were empty"); @@ -108,15 +109,15 @@ pub(crate) fn merge_bounds( }) } -fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) -> bool { +fn trait_is_same_or_supertrait(tcx: TyCtxt<'_>, child: DefId, trait_: DefId) -> bool { if child == trait_ { return true; } - let predicates = cx.tcx.explicit_super_predicates_of(child); + let predicates = tcx.explicit_super_predicates_of(child); predicates .iter_identity_copied() .filter_map(|(pred, _)| Some(pred.as_trait_clause()?.def_id())) - .any(|did| trait_is_same_or_supertrait(cx, did, trait_)) + .any(|did| trait_is_same_or_supertrait(tcx, did, trait_)) } pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generics) { From d1f11b23ca69387b8c52b9d0066937d547fe21ea Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Wed, 1 Apr 2026 14:38:56 +0100 Subject: [PATCH 30/38] Change the test comment per PR review feedback --- tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs | 5 +---- tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs index 4b87ae6080698..67aed9122e89f 100644 --- a/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs +++ b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.rs @@ -1,7 +1,4 @@ -// #127423: ICE: assertion failed: sig.c_variadic || extra_args.is_empty() -// Calling a const fn with the wrong number of arguments where the function has -// an unresolved type should not trigger an ICE. -// issue: rust-lang/rust#127423 +//! Regression test for https://github.com/rust-lang/rust/issues/127423 #![allow(dead_code)] diff --git a/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr index a5b2f5a578150..f01e9bd51357d 100644 --- a/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr +++ b/tests/ui/consts/ice-extra-args-fn-abi-issue-127423.stderr @@ -1,11 +1,11 @@ error: lifetimes cannot use keyword names - --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:8:18 + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:5:18 | LL | const fn add(a: &'self isize) -> usize { | ^^^^^ error[E0261]: use of undeclared lifetime name `'self` - --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:8:18 + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:5:18 | LL | const fn add(a: &'self isize) -> usize { | ^^^^^ undeclared lifetime @@ -16,13 +16,13 @@ LL | const fn add<'self>(a: &'self isize) -> usize { | +++++++ error[E0425]: cannot find value `Qux` in this scope - --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:11:5 + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:8:5 | LL | Qux + y | ^^^ not found in this scope error[E0425]: cannot find value `y` in this scope - --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:11:11 + --> $DIR/ice-extra-args-fn-abi-issue-127423.rs:8:11 | LL | Qux + y | ^ From 14aa3a904226d3e018e410407252869a9af0c0cb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 15:46:03 +0200 Subject: [PATCH 31/38] Remove unused `DocContext` argument in `clean_middle_const` --- src/librustdoc/clean/mod.rs | 5 ++--- src/librustdoc/clean/utils.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6bb585aa8933a..768831d4e40ec 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -342,7 +342,6 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg<'tcx>) -> ConstantKind pub(crate) fn clean_middle_const<'tcx>( constant: ty::Binder<'tcx, ty::Const<'tcx>>, - _cx: &mut DocContext<'tcx>, ) -> ConstantKind { // FIXME: instead of storing the stringified expression, store `self` directly instead. ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() } @@ -464,7 +463,7 @@ fn clean_middle_term<'tcx>( ) -> Term { match term.skip_binder().kind() { ty::TermKind::Ty(ty) => Term::Type(clean_middle_ty(term.rebind(ty), cx, None, None)), - ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c), cx)), + ty::TermKind::Const(c) => Term::Constant(clean_middle_const(term.rebind(c))), } } @@ -479,7 +478,7 @@ fn clean_hir_term<'tcx>( // FIXME(generic_const_items): this should instantiate with the alias item's args let ty = cx.tcx.type_of(assoc_item.unwrap()).instantiate_identity(); let ct = lower_const_arg_for_rustdoc(cx.tcx, c, ty); - Term::Constant(clean_middle_const(ty::Binder::dummy(ct), cx)) + Term::Constant(clean_middle_const(ty::Binder::dummy(ct))) } } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 2f859d2c3b4e8..846b22dc4b475 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -148,7 +148,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( }), ))), GenericArgKind::Const(ct) => { - Some(GenericArg::Const(Box::new(clean_middle_const(arg.rebind(ct), cx)))) + Some(GenericArg::Const(Box::new(clean_middle_const(arg.rebind(ct))))) } } }; From 4b39934646c01de8dd50c59c003d71b2f0bb3c7b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 15:55:17 +0200 Subject: [PATCH 32/38] Replace `DocContext` argument with `TyCtxt` in rustdoc `utils::print_const` function --- src/librustdoc/clean/mod.rs | 6 +++--- src/librustdoc/clean/utils.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 768831d4e40ec..d6c57dd1639bb 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1830,7 +1830,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, cx.tcx.types.usize); let typing_env = ty::TypingEnv::post_analysis(cx.tcx, *def_id); let ct = cx.tcx.normalize_erasing_regions(typing_env, ct); - print_const(cx, ct) + print_const(cx.tcx, ct) } hir::ConstArgKind::Struct(..) | hir::ConstArgKind::Path(..) @@ -1839,7 +1839,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T | hir::ConstArgKind::Array(..) | hir::ConstArgKind::Literal { .. } => { let ct = lower_const_arg_for_rustdoc(cx.tcx, const_arg, cx.tcx.types.usize); - print_const(cx, ct) + print_const(cx.tcx, ct) } }; Array(Box::new(clean_ty(ty, cx)), length.into()) @@ -2059,7 +2059,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ), ty::Array(ty, n) => { let n = cx.tcx.normalize_erasing_regions(cx.typing_env(), n); - let n = print_const(cx, n); + let n = print_const(cx.tcx, n); Array(Box::new(clean_middle_ty(bound_ty.rebind(ty), cx, None, None)), n.into()) } ty::RawPtr(ty, mutbl) => { diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 846b22dc4b475..2284b815a09a9 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -347,13 +347,13 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { }) } -pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { +pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { match n.kind() { ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => { if let Some(def) = def.as_local() { - rendered_const(cx.tcx, cx.tcx.hir_body_owned_by(def), def) + rendered_const(tcx, tcx.hir_body_owned_by(def), def) } else { - inline::print_inlined_const(cx.tcx, def) + inline::print_inlined_const(tcx, def) } } // array lengths are obviously usize From 0b941ed495342e0bccd6c408b7e1c58074f43e94 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 1 Apr 2026 16:11:05 +0200 Subject: [PATCH 33/38] Last `DocContext`/`TyCtxt` cleanup --- src/librustdoc/clean/mod.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d6c57dd1639bb..67eee30e99a9f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -184,7 +184,8 @@ fn generate_item_with_correct_attrs( import_ids: &[LocalDefId], renamed: Option, ) -> Item { - let target_attrs = inline::load_attrs(cx.tcx, def_id); + let tcx = cx.tcx; + let target_attrs = inline::load_attrs(tcx, def_id); let attrs = if !import_ids.is_empty() { let mut attrs = Vec::with_capacity(import_ids.len()); let mut is_inline = false; @@ -196,11 +197,11 @@ fn generate_item_with_correct_attrs( // cfgs on the path up until the glob can be removed, and only cfgs on the globbed item // itself matter), for non-inlined re-exports see #85043. let import_is_inline = find_attr!( - inline::load_attrs(cx.tcx, import_id.to_def_id()), + inline::load_attrs(tcx, import_id.to_def_id()), Doc(d) if d.inline.first().is_some_and(|(inline, _)| *inline == DocInline::Inline) - ) || (is_glob_import(cx.tcx, import_id) - && (cx.document_hidden() || !cx.tcx.is_doc_hidden(def_id))); + ) || (is_glob_import(tcx, import_id) + && (cx.document_hidden() || !tcx.is_doc_hidden(def_id))); attrs.extend(get_all_import_attributes(cx, import_id, def_id, is_inline)); is_inline = is_inline || import_is_inline; } @@ -1458,7 +1459,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo bounds.retain(|b| { // FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized` // is shown and none of the new sizedness traits leak into documentation. - !b.is_meta_sized_bound(cx.tcx) + !b.is_meta_sized_bound(tcx) }); // Our Sized/?Sized bound didn't get handled when creating the generics @@ -1466,7 +1467,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo // (some of them may have come from the trait). If we do have a sized // bound, we remove it, and if we don't then we add the `?Sized` bound // at the end. - match bounds.iter().position(|b| b.is_sized_bound(cx.tcx)) { + match bounds.iter().position(|b| b.is_sized_bound(tcx)) { Some(i) => { bounds.remove(i); } @@ -1516,7 +1517,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } }; - Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name()), kind, cx.tcx) + Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name()), kind, tcx) } fn first_non_private_clean_path<'tcx>( @@ -2767,12 +2768,8 @@ fn clean_maybe_renamed_item<'tcx>( import_ids: &[LocalDefId], ) -> Vec { use hir::ItemKind; - fn get_name( - cx: &DocContext<'_>, - item: &hir::Item<'_>, - renamed: Option, - ) -> Option { - renamed.or_else(|| cx.tcx.hir_opt_name(item.hir_id())) + fn get_name(tcx: TyCtxt<'_>, item: &hir::Item<'_>, renamed: Option) -> Option { + renamed.or_else(|| tcx.hir_opt_name(item.hir_id())) } let def_id = item.owner_id.to_def_id(); @@ -2789,7 +2786,7 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::Use(path, kind) => { return clean_use_statement( item, - get_name(cx, item, renamed), + get_name(cx.tcx, item, renamed), path, kind, cx, @@ -2799,7 +2796,7 @@ fn clean_maybe_renamed_item<'tcx>( _ => {} } - let mut name = get_name(cx, item, renamed).unwrap(); + let mut name = get_name(cx.tcx, item, renamed).unwrap(); let kind = match item.kind { ItemKind::Static(mutability, _, ty, body_id) => StaticItem(Static { From 98688ef1f93ede0a6f0b71287f6c16e1b114a61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 1 Apr 2026 17:01:56 +0200 Subject: [PATCH 34/38] Simplify feature gating checks & macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * utilize Kleene `?` two avoid having two macro matchers with almost identical body * inline `gate_legacy` since it only has one use and since it shouldn't be used anywhere else anyway * remove unnecessary explicit borrows of the visitor * replace `if let Some(spans) = spans.get(…) { for span in …` with `for &span in spans.get(…).into_iter().flatten()` to avoid rightward drift and explicit dereferences * rename `gate_all_legacy…` to `soft_gate_all_legacy…` since it's not a "proper" gate that issues an error but merely one that emits a warning --- compiler/rustc_ast_passes/src/feature_gate.rs | 232 +++++++----------- 1 file changed, 89 insertions(+), 143 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 830eb3d6d8170..84f07aa8ae521 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -14,31 +14,22 @@ use crate::errors; /// The common case. macro_rules! gate { - ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{ + ($visitor:expr, $feature:ident, $span:expr, $explain:expr $(, $help:expr)?) => {{ if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) { - feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit(); - } - }}; - ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{ - if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) { - feature_err(&$visitor.sess, sym::$feature, $span, $explain).with_help($help).emit(); + feature_err($visitor.sess, sym::$feature, $span, $explain) + $(.with_help($help))? + .emit(); } }}; } /// The unusual case, where the `has_feature` condition is non-standard. macro_rules! gate_alt { - ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr) => {{ + ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr $(, $notes:expr)?) => {{ if !$has_feature && !$span.allows_unstable($name) { - feature_err(&$visitor.sess, $name, $span, $explain).emit(); - } - }}; - ($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr, $notes: expr) => {{ - if !$has_feature && !$span.allows_unstable($name) { - let mut diag = feature_err(&$visitor.sess, $name, $span, $explain); - for note in $notes { - diag.note(*note); - } + #[allow(unused_mut)] + let mut diag = feature_err($visitor.sess, $name, $span, $explain); + $(for ¬e in $notes { diag.note(note); })? diag.emit(); } }}; @@ -51,21 +42,12 @@ macro_rules! gate_multi { let spans: Vec<_> = $spans.filter(|span| !span.allows_unstable(sym::$feature)).collect(); if !spans.is_empty() { - feature_err(&$visitor.sess, sym::$feature, spans, $explain).emit(); + feature_err($visitor.sess, sym::$feature, spans, $explain).emit(); } } }}; } -/// The legacy case. -macro_rules! gate_legacy { - ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{ - if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) { - feature_warn(&$visitor.sess, sym::$feature, $span, $explain); - } - }}; -} - pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) { PostExpansionVisitor { sess, features }.visit_attribute(attr) } @@ -89,14 +71,14 @@ impl<'a> PostExpansionVisitor<'a> { if let ast::TyKind::ImplTrait(..) = ty.kind { if self.in_associated_ty { gate!( - &self.vis, + self.vis, impl_trait_in_assoc_type, ty.span, "`impl Trait` in associated types is unstable" ); } else { gate!( - &self.vis, + self.vis, type_alias_impl_trait, ty.span, "`impl Trait` in type aliases is unstable" @@ -211,7 +193,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { if item.has_name(sym::simd) { gate!( - &self, + self, repr_simd, attr.span, "SIMD types are experimental and possibly buggy" @@ -224,7 +206,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) => { if let ast::ImplPolarity::Negative(span) = of_trait.polarity { gate!( - &self, + self, negative_impls, span.to(of_trait.trait_ref.path.span), "negative trait bounds are not fully implemented; \ @@ -233,26 +215,21 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } if let ast::Defaultness::Default(_) = of_trait.defaultness { - gate!(&self, specialization, i.span, "specialization is unstable"); + gate!(self, specialization, i.span, "specialization is unstable"); } } ast::ItemKind::Trait(box ast::Trait { is_auto: ast::IsAuto::Yes, .. }) => { - gate!( - &self, - auto_traits, - i.span, - "auto traits are experimental and possibly buggy" - ); + gate!(self, auto_traits, i.span, "auto traits are experimental and possibly buggy"); } ast::ItemKind::TraitAlias(..) => { - gate!(&self, trait_alias, i.span, "trait aliases are experimental"); + gate!(self, trait_alias, i.span, "trait aliases are experimental"); } ast::ItemKind::MacroDef(_, ast::MacroDef { macro_rules: false, .. }) => { let msg = "`macro` is experimental"; - gate!(&self, decl_macro, i.span, msg); + gate!(self, decl_macro, i.span, msg); } ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => { @@ -264,7 +241,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { }) => { // Make sure this is only allowed if the feature gate is enabled. // #![feature(min_generic_const_args)] - gate!(&self, min_generic_const_args, i.span, "top-level `type const` are unstable"); + gate!(self, min_generic_const_args, i.span, "top-level `type const` are unstable"); } _ => {} @@ -280,7 +257,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { let links_to_llvm = link_name.is_some_and(|val| val.as_str().starts_with("llvm.")); if links_to_llvm { gate!( - &self, + self, link_llvm_intrinsics, i.span, "linking to LLVM intrinsics is experimental" @@ -288,7 +265,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } ast::ForeignItemKind::TyAlias(..) => { - gate!(&self, extern_types, i.span, "extern types are experimental"); + gate!(self, extern_types, i.span, "extern types are experimental"); } ast::ForeignItemKind::MacCall(..) => {} } @@ -303,10 +280,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { self.check_late_bound_lifetime_defs(&fn_ptr_ty.generic_params); } ast::TyKind::Never => { - gate!(&self, never_type, ty.span, "the `!` type is experimental"); + gate!(self, never_type, ty.span, "the `!` type is experimental"); } ast::TyKind::Pat(..) => { - gate!(&self, pattern_types, ty.span, "pattern types are unstable"); + gate!(self, pattern_types, ty.span, "pattern types are unstable"); } _ => {} } @@ -339,7 +316,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { && let ast::FnRetTy::Ty(ref ty) = generic_args.output && matches!(ty.kind, ast::TyKind::Never) { - gate!(&self, never_type, ty.span, "the `!` type is experimental"); + gate!(self, never_type, ty.span, "the `!` type is experimental"); } visit::walk_generic_args(self, args); } @@ -348,7 +325,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { match e.kind { ast::ExprKind::TryBlock(_, None) => { // `try { ... }` is old and is only gated post-expansion here. - gate!(&self, try_blocks, e.span, "`try` expression is experimental"); + gate!(self, try_blocks, e.span, "`try` expression is experimental"); } ast::ExprKind::TryBlock(_, Some(_)) => { // `try_blocks_heterogeneous` is new, and gated pre-expansion instead. @@ -359,10 +336,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { .. }) => match suffix { Some(sym::f16) => { - gate!(&self, f16, e.span, "the type `f16` is unstable") + gate!(self, f16, e.span, "the type `f16` is unstable") } Some(sym::f128) => { - gate!(&self, f128, e.span, "the type `f128` is unstable") + gate!(self, f128, e.span, "the type `f128` is unstable") } _ => (), }, @@ -381,7 +358,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { }; if let PatKind::Range(Some(_), None, Spanned { .. }) = inner_pat.kind { gate!( - &self, + self, half_open_range_patterns_in_slices, pat.span, "`X..` patterns in slices are experimental" @@ -390,7 +367,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } PatKind::Box(..) => { - gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental"); + gate!(self, box_patterns, pattern.span, "box pattern syntax is experimental"); } _ => {} } @@ -412,7 +389,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() { - gate!(&self, c_variadic, span, "C-variadic functions are unstable"); + gate!(self, c_variadic, span, "C-variadic functions are unstable"); } visit::walk_fn(self, fn_kind) @@ -424,7 +401,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::AssocItemKind::Type(box ast::TyAlias { ty, .. }) => { if let (Some(_), AssocCtxt::Trait) = (ty, ctxt) { gate!( - &self, + self, associated_type_defaults, i.span, "associated type defaults are unstable" @@ -441,18 +418,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { }) => { // Make sure this is only allowed if the feature gate is enabled. // #![feature(min_generic_const_args)] - gate!( - &self, - min_generic_const_args, - i.span, - "associated `type const` are unstable" - ); + gate!(self, min_generic_const_args, i.span, "associated `type const` are unstable"); // Make sure associated `type const` defaults in traits are only allowed // if the feature gate is enabled. // #![feature(associated_type_defaults)] if ctxt == AssocCtxt::Trait && rhs.is_some() { gate!( - &self, + self, associated_type_defaults, i.span, "associated type defaults are unstable" @@ -486,18 +458,9 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { let spans = sess.psess.gated_spans.spans.borrow(); macro_rules! gate_all { - ($gate:ident, $msg:literal) => { - if let Some(spans) = spans.get(&sym::$gate) { - for span in spans { - gate!(&visitor, $gate, *span, $msg); - } - } - }; - ($gate:ident, $msg:literal, $help:literal) => { - if let Some(spans) = spans.get(&sym::$gate) { - for span in spans { - gate!(&visitor, $gate, *span, $msg, $help); - } + ($feature:ident, $explain:literal $(, $help:literal)?) => { + for &span in spans.get(&sym::$feature).into_iter().flatten() { + gate!(visitor, $feature, span, $explain $(, $help)?); } }; } @@ -513,18 +476,15 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { "consider removing `for<...>`" ); gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); - // yield can be enabled either by `coroutines` or `gen_blocks` - if let Some(spans) = spans.get(&sym::yield_expr) { - for span in spans { - if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) - && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) - && (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr)) - { - // Emit yield_expr as the error, since that will be sufficient. You can think of it - // as coroutines and gen_blocks imply yield_expr. - feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental") - .emit(); - } + // Yield exprs can be enabled either by `yield_expr`, by `coroutines` or by `gen_blocks`. + for &span in spans.get(&sym::yield_expr).into_iter().flatten() { + if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) + && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) + && (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr)) + { + // Emit yield_expr as the error, since that will be sufficient. You can think of it + // as coroutines and gen_blocks imply yield_expr. + feature_err(visitor.sess, sym::yield_expr, span, "yield syntax is experimental").emit(); } } gate_all!(gen_blocks, "gen blocks are experimental"); @@ -546,41 +506,27 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental"); - // associated_const_equality is stabilized as part of min_generic_const_args - if let Some(spans) = spans.get(&sym::associated_const_equality) { - for span in spans { - if !visitor.features.min_generic_const_args() - && !span.allows_unstable(sym::min_generic_const_args) - { - feature_err( - &visitor.sess, - sym::min_generic_const_args, - *span, - "associated const equality is incomplete", - ) - .emit(); - } - } + // `associated_const_equality` will be stabilized as part of `min_generic_const_args`. + for &span in spans.get(&sym::associated_const_equality).into_iter().flatten() { + gate!(visitor, min_generic_const_args, span, "associated const equality is incomplete"); } // `mgca_type_const_syntax` is part of `min_generic_const_args` so either // or both are enabled we don't need to emit a feature error. - if let Some(spans) = spans.get(&sym::mgca_type_const_syntax) { - for span in spans { - if visitor.features.min_generic_const_args() - || visitor.features.mgca_type_const_syntax() - || span.allows_unstable(sym::min_generic_const_args) - || span.allows_unstable(sym::mgca_type_const_syntax) - { - continue; - } - feature_err( - &visitor.sess, - sym::min_generic_const_args, - *span, - "`type const` syntax is experimental", - ) - .emit(); + for &span in spans.get(&sym::mgca_type_const_syntax).into_iter().flatten() { + if visitor.features.min_generic_const_args() + || visitor.features.mgca_type_const_syntax() + || span.allows_unstable(sym::min_generic_const_args) + || span.allows_unstable(sym::mgca_type_const_syntax) + { + continue; } + feature_err( + visitor.sess, + sym::min_generic_const_args, + span, + "`type const` syntax is experimental", + ) + .emit(); } gate_all!(global_registration, "global registration is experimental"); @@ -599,30 +545,28 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(impl_restriction, "`impl` restrictions are experimental"); if !visitor.features.never_patterns() { - if let Some(spans) = spans.get(&sym::never_patterns) { - for &span in spans { - if span.allows_unstable(sym::never_patterns) { - continue; - } - let sm = sess.source_map(); - // We gate two types of spans: the span of a `!` pattern, and the span of a - // match arm without a body. For the latter we want to give the user a normal - // error. - if let Ok(snippet) = sm.span_to_snippet(span) - && snippet == "!" - { - feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental") - .emit(); - } else { - let suggestion = span.shrink_to_hi(); - sess.dcx().emit_err(errors::MatchArmWithNoBody { span, suggestion }); - } + for &span in spans.get(&sym::never_patterns).into_iter().flatten() { + if span.allows_unstable(sym::never_patterns) { + continue; + } + let sm = sess.source_map(); + // We gate two types of spans: the span of a `!` pattern, and the span of a + // match arm without a body. For the latter we want to give the user a normal + // error. + if let Ok(snippet) = sm.span_to_snippet(span) + && snippet == "!" + { + feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental") + .emit(); + } else { + let suggestion = span.shrink_to_hi(); + sess.dcx().emit_err(errors::MatchArmWithNoBody { span, suggestion }); } } } if !visitor.features.negative_bounds() { - for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { + for &span in spans.get(&sym::negative_bounds).into_iter().flatten() { sess.dcx().emit_err(errors::NegativeBoundUnsupported { span }); } } @@ -631,19 +575,21 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // and subsequently disabled (with the non-early gating readded). // We emit an early future-incompatible warning for these. // New syntax gates should go above here to get a hard error gate. - macro_rules! gate_all_legacy_dont_use { - ($gate:ident, $msg:literal) => { - for span in spans.get(&sym::$gate).unwrap_or(&vec![]) { - gate_legacy!(&visitor, $gate, *span, $msg); + macro_rules! soft_gate_all_legacy_dont_use { + ($feature:ident, $explain:literal) => { + for &span in spans.get(&sym::$feature).into_iter().flatten() { + if !visitor.features.$feature() && !span.allows_unstable(sym::$feature) { + feature_warn(&visitor.sess, sym::$feature, span, $explain); + } } }; } - gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); - gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); - gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); - gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); - gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable"); + soft_gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); + soft_gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); + soft_gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); + soft_gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); + soft_gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable"); visit::walk_crate(&mut visitor, krate); } From dd96eff7690df4d7c83176e2a3ab081b09f985c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 1 Apr 2026 14:28:01 +0200 Subject: [PATCH 35/38] Sort pre-expansion gates and add more visible disclaimers --- compiler/rustc_ast_passes/src/feature_gate.rs | 145 +++++++++++------- .../precise-capturing/bound-modifiers.stderr | 20 +-- 2 files changed, 97 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 84f07aa8ae521..d8ace8e38634c 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -59,6 +59,15 @@ struct PostExpansionVisitor<'a> { features: &'a Features, } +// ----------------------------------------------------------------------------- +// POST-EXPANSION FEATURE GATES FOR UNSTABLE ATTRIBUTES ETC. +// **LEGACY** POST-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX **LEGACY** +// ----------------------------------------------------------------------------- + +// IMPORTANT: Don't add any new post-expansion feature gates for new unstable syntax! +// It's a legacy mechanism for them. +// Instead, register a pre-expansion feature gate using `gate_all` in fn `check_crate`. + impl<'a> PostExpansionVisitor<'a> { /// Feature gate `impl Trait` inside `type Alias = $type_expr;`. fn check_impl_trait(&self, ty: &ast::Ty, in_associated_ty: bool) { @@ -448,6 +457,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } +// ----------------------------------------------------------------------------- + pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { maybe_stage_features(sess, features, krate); check_incompatible_features(sess, features); @@ -456,6 +467,10 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { let mut visitor = PostExpansionVisitor { sess, features }; + // ----------------------------------------------------------------------------- + // PRE-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX + // ----------------------------------------------------------------------------- + let spans = sess.psess.gated_spans.spans.borrow(); macro_rules! gate_all { ($feature:ident, $explain:literal $(, $help:literal)?) => { @@ -464,54 +479,63 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } }; } + + // tidy-alphabetical-start + gate_all!(async_for_loop, "`for await` loops are experimental"); + gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); + gate_all!(const_block_items, "const block items are experimental"); + gate_all!(const_closures, "const closures are experimental"); + gate_all!(const_trait_impl, "const trait impls are experimental"); + gate_all!(contracts, "contracts are incomplete"); + gate_all!(contracts_internals, "contract internal machinery is for internal use only"); + gate_all!(coroutines, "coroutine syntax is experimental"); + gate_all!(default_field_values, "default values on fields are experimental"); + gate_all!(ergonomic_clones, "ergonomic clones are experimental"); + gate_all!(explicit_tail_calls, "`become` expression is experimental"); + gate_all!(final_associated_functions, "`final` on trait functions is experimental"); + gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); + gate_all!(frontmatter, "frontmatters are experimental"); + gate_all!(gen_blocks, "gen blocks are experimental"); + gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(global_registration, "global registration is experimental"); + gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); + gate_all!(impl_restriction, "`impl` restrictions are experimental"); + gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental"); + gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); + gate_all!(mut_ref, "mutable by-reference bindings are experimental"); + gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); + gate_all!(postfix_match, "postfix match is experimental"); + gate_all!(return_type_notation, "return type notation is experimental"); + gate_all!(super_let, "`super let` is experimental"); + gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental"); + gate_all!(unsafe_binders, "unsafe binder types are experimental"); + gate_all!(unsafe_fields, "`unsafe` fields are experimental"); + gate_all!(where_clause_attrs, "attributes in `where` clause are unstable"); + gate_all!(yeet_expr, "`do yeet` expression is experimental"); + // tidy-alphabetical-end + gate_all!( async_trait_bounds, "`async` trait bounds are unstable", "use the desugared name of the async trait, such as `AsyncFn`" ); - gate_all!(async_for_loop, "`for await` loops are experimental"); gate_all!( closure_lifetime_binder, "`for<...>` binders for closures are experimental", "consider removing `for<...>`" ); - gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); - // Yield exprs can be enabled either by `yield_expr`, by `coroutines` or by `gen_blocks`. - for &span in spans.get(&sym::yield_expr).into_iter().flatten() { - if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) - && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) - && (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr)) - { - // Emit yield_expr as the error, since that will be sufficient. You can think of it - // as coroutines and gen_blocks imply yield_expr. - feature_err(visitor.sess, sym::yield_expr, span, "yield syntax is experimental").emit(); - } - } - gate_all!(gen_blocks, "gen blocks are experimental"); - gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!( half_open_range_patterns_in_slices, "half-open range patterns in slices are unstable" ); - gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental"); - gate_all!(yeet_expr, "`do yeet` expression is experimental"); - gate_all!(const_closures, "const closures are experimental"); - gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); - gate_all!(ergonomic_clones, "ergonomic clones are experimental"); - gate_all!(explicit_tail_calls, "`become` expression is experimental"); - gate_all!(generic_const_items, "generic const items are experimental"); - gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); - gate_all!(default_field_values, "default values on fields are experimental"); - gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); - gate_all!(postfix_match, "postfix match is experimental"); - gate_all!(mut_ref, "mutable by-reference bindings are experimental"); - gate_all!(min_generic_const_args, "unbraced const blocks as const args are experimental"); - // `associated_const_equality` will be stabilized as part of `min_generic_const_args`. + + // `associated_const_equality` will be stabilized as part of `min_generic_const_args`. for &span in spans.get(&sym::associated_const_equality).into_iter().flatten() { gate!(visitor, min_generic_const_args, span, "associated const equality is incomplete"); } - // `mgca_type_const_syntax` is part of `min_generic_const_args` so either - // or both are enabled we don't need to emit a feature error. + + // `mgca_type_const_syntax` is part of `min_generic_const_args` so if + // either or both are enabled we don't need to emit a feature error. for &span in spans.get(&sym::mgca_type_const_syntax).into_iter().flatten() { if visitor.features.min_generic_const_args() || visitor.features.mgca_type_const_syntax() @@ -529,33 +553,23 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { .emit(); } - gate_all!(global_registration, "global registration is experimental"); - gate_all!(return_type_notation, "return type notation is experimental"); - gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); - gate_all!(unsafe_fields, "`unsafe` fields are experimental"); - gate_all!(unsafe_binders, "unsafe binder types are experimental"); - gate_all!(contracts, "contracts are incomplete"); - gate_all!(contracts_internals, "contract internal machinery is for internal use only"); - gate_all!(where_clause_attrs, "attributes in `where` clause are unstable"); - gate_all!(super_let, "`super let` is experimental"); - gate_all!(frontmatter, "frontmatters are experimental"); - gate_all!(coroutines, "coroutine syntax is experimental"); - gate_all!(const_block_items, "const block items are experimental"); - gate_all!(final_associated_functions, "`final` on trait functions is experimental"); - gate_all!(impl_restriction, "`impl` restrictions are experimental"); + // Negative bounds are *super* internal. + // Under no circumstances do we want to advertise the feature name to users! + if !visitor.features.negative_bounds() { + for &span in spans.get(&sym::negative_bounds).into_iter().flatten() { + sess.dcx().emit_err(errors::NegativeBoundUnsupported { span }); + } + } if !visitor.features.never_patterns() { for &span in spans.get(&sym::never_patterns).into_iter().flatten() { if span.allows_unstable(sym::never_patterns) { continue; } - let sm = sess.source_map(); // We gate two types of spans: the span of a `!` pattern, and the span of a // match arm without a body. For the latter we want to give the user a normal // error. - if let Ok(snippet) = sm.span_to_snippet(span) - && snippet == "!" - { + if let Ok("!") = sess.source_map().span_to_snippet(span).as_deref() { feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental") .emit(); } else { @@ -565,16 +579,27 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } } - if !visitor.features.negative_bounds() { - for &span in spans.get(&sym::negative_bounds).into_iter().flatten() { - sess.dcx().emit_err(errors::NegativeBoundUnsupported { span }); + // Yield exprs can be enabled either by `yield_expr`, by `coroutines` or by `gen_blocks`. + for &span in spans.get(&sym::yield_expr).into_iter().flatten() { + if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) + && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) + && (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr)) + { + // Only mentioned `yield_expr` in the diagnostic since that'll be sufficient. + // You can think of it as `coroutines` and `gen_blocks` implying `yield_expr`. + feature_err(visitor.sess, sym::yield_expr, span, "yield syntax is experimental").emit(); } } - // All uses of `gate_all_legacy_dont_use!` below this point were added in #65742, - // and subsequently disabled (with the non-early gating readded). - // We emit an early future-incompatible warning for these. - // New syntax gates should go above here to get a hard error gate. + // ----------------------------------------------------------------------------- + // **LEGACY** SOFT PRE-EXPANSION FEATURE GATES FOR UNSTABLE SYNTAX **LEGACY** + // ----------------------------------------------------------------------------- + + // IMPORTANT: Do not extend the list below! New syntax should go above and use `gate_all`. + + // FIXME(#154045): Migrate all of these to erroring feature gates and + // remove the corresponding post-expansion feature gates. + macro_rules! soft_gate_all_legacy_dont_use { ($feature:ident, $explain:literal) => { for &span in spans.get(&sym::$feature).into_iter().flatten() { @@ -585,11 +610,15 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { }; } + // tidy-alphabetical-start + soft_gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable"); soft_gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); - soft_gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); soft_gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); + soft_gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); soft_gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); - soft_gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable"); + // tidy-alphabetical-end + + // ----------------------------------------------------------------------------- visit::walk_crate(&mut visitor, krate); } diff --git a/tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr b/tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr index deab31c251f83..4a0532284c176 100644 --- a/tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr +++ b/tests/ui/impl-trait/precise-capturing/bound-modifiers.stderr @@ -46,6 +46,16 @@ error[E0405]: cannot find trait `r#use` in this scope LL | fn binder() -> impl Sized + for<'a> use<> {} | ^^^ not found in this scope +error[E0658]: const trait impls are experimental + --> $DIR/bound-modifiers.rs:12:32 + | +LL | fn constness() -> impl Sized + const use<> {} + | ^^^^^ + | + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + error[E0658]: `async` trait bounds are unstable --> $DIR/bound-modifiers.rs:7:32 | @@ -57,16 +67,6 @@ LL | fn asyncness() -> impl Sized + async use<> {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: use the desugared name of the async trait, such as `AsyncFn` -error[E0658]: const trait impls are experimental - --> $DIR/bound-modifiers.rs:12:32 - | -LL | fn constness() -> impl Sized + const use<> {} - | ^^^^^ - | - = note: see issue #143874 for more information - = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - error: aborting due to 10 previous errors Some errors have detailed explanations: E0405, E0658. From 394012bcd28612217ba6d54ad8948e143974d16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 29 Mar 2026 14:36:35 +0200 Subject: [PATCH 36/38] Emit pre-expansion feature gate warning for negative impls --- compiler/rustc_ast_passes/src/feature_gate.rs | 5 ++-- compiler/rustc_parse/src/parser/item.rs | 1 + tests/ui/auto-traits/ungated-impl.rs | 7 ------ tests/ui/auto-traits/ungated-impl.stderr | 23 ------------------- .../feature-gates/feature-gate-auto-traits.rs | 11 ++------- .../feature-gate-auto-traits.stderr | 16 ++++++------- .../soft-feature-gate-negative_impls.rs | 12 ++++++++++ .../soft-feature-gate-negative_impls.stderr | 14 +++++++++++ .../feature-gate-negative_impls.rs | 4 +++- .../feature-gate-negative_impls.stderr | 5 ++-- 10 files changed, 46 insertions(+), 52 deletions(-) delete mode 100644 tests/ui/auto-traits/ungated-impl.rs delete mode 100644 tests/ui/auto-traits/ungated-impl.stderr create mode 100644 tests/ui/feature-gates/soft-feature-gate-negative_impls.rs create mode 100644 tests/ui/feature-gates/soft-feature-gate-negative_impls.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index d8ace8e38634c..ae046c61c2767 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -218,8 +218,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { self, negative_impls, span.to(of_trait.trait_ref.path.span), - "negative trait bounds are not fully implemented; \ - use marker types for now" + "negative impls are experimental", + "use marker types for now" ); } @@ -614,6 +614,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { soft_gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable"); soft_gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); soft_gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); + soft_gate_all_legacy_dont_use!(negative_impls, "negative impls are experimental"); soft_gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); soft_gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); // tidy-alphabetical-end diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 0f4927432f6fa..823d5957949cb 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -603,6 +603,7 @@ impl<'a> Parser<'a> { fn parse_polarity(&mut self) -> ast::ImplPolarity { // Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type. if self.check(exp!(Bang)) && self.look_ahead(1, |t| t.can_begin_type()) { + self.psess.gated_spans.gate(sym::negative_impls, self.token.span); self.bump(); // `!` ast::ImplPolarity::Negative(self.prev_token.span) } else { diff --git a/tests/ui/auto-traits/ungated-impl.rs b/tests/ui/auto-traits/ungated-impl.rs deleted file mode 100644 index d46b4b01af9c7..0000000000000 --- a/tests/ui/auto-traits/ungated-impl.rs +++ /dev/null @@ -1,7 +0,0 @@ -auto trait MyTrait {} -//~^ ERROR auto traits are experimental and possibly buggy - -impl !MyTrait for *mut T {} -//~^ ERROR negative trait bounds are not fully implemented - -fn main() {} diff --git a/tests/ui/auto-traits/ungated-impl.stderr b/tests/ui/auto-traits/ungated-impl.stderr deleted file mode 100644 index 9d10d46a90283..0000000000000 --- a/tests/ui/auto-traits/ungated-impl.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0658]: auto traits are experimental and possibly buggy - --> $DIR/ungated-impl.rs:1:1 - | -LL | auto trait MyTrait {} - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #13231 for more information - = help: add `#![feature(auto_traits)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: negative trait bounds are not fully implemented; use marker types for now - --> $DIR/ungated-impl.rs:4:9 - | -LL | impl !MyTrait for *mut T {} - | ^^^^^^^^ - | - = note: see issue #68318 for more information - = help: add `#![feature(negative_impls)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-auto-traits.rs b/tests/ui/feature-gates/feature-gate-auto-traits.rs index aab9e784fe9d7..e900fb84a02d9 100644 --- a/tests/ui/feature-gates/feature-gate-auto-traits.rs +++ b/tests/ui/feature-gates/feature-gate-auto-traits.rs @@ -1,12 +1,5 @@ -// Test that default and negative trait implementations are gated by -// `auto_traits` feature gate +auto trait DummyAutoTrait {} //~ ERROR auto traits are experimental and possibly buggy -struct DummyStruct; - -auto trait AutoDummyTrait {} -//~^ ERROR auto traits are experimental and possibly buggy - -impl !AutoDummyTrait for DummyStruct {} -//~^ ERROR negative trait bounds are not fully implemented; use marker types for now +pub unsafe auto trait AnotherAutoTrait {} //~ ERROR auto traits are experimental and possibly buggy fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-auto-traits.stderr b/tests/ui/feature-gates/feature-gate-auto-traits.stderr index 8fa5168b2d041..d476b4fecbe58 100644 --- a/tests/ui/feature-gates/feature-gate-auto-traits.stderr +++ b/tests/ui/feature-gates/feature-gate-auto-traits.stderr @@ -1,21 +1,21 @@ error[E0658]: auto traits are experimental and possibly buggy - --> $DIR/feature-gate-auto-traits.rs:6:1 + --> $DIR/feature-gate-auto-traits.rs:1:1 | -LL | auto trait AutoDummyTrait {} +LL | auto trait DummyAutoTrait {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #13231 for more information = help: add `#![feature(auto_traits)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: negative trait bounds are not fully implemented; use marker types for now - --> $DIR/feature-gate-auto-traits.rs:9:6 +error[E0658]: auto traits are experimental and possibly buggy + --> $DIR/feature-gate-auto-traits.rs:3:1 | -LL | impl !AutoDummyTrait for DummyStruct {} - | ^^^^^^^^^^^^^^^ +LL | pub unsafe auto trait AnotherAutoTrait {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #68318 for more information - = help: add `#![feature(negative_impls)]` to the crate attributes to enable + = note: see issue #13231 for more information + = help: add `#![feature(auto_traits)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/soft-feature-gate-negative_impls.rs b/tests/ui/feature-gates/soft-feature-gate-negative_impls.rs new file mode 100644 index 0000000000000..7c578f20d2dc0 --- /dev/null +++ b/tests/ui/feature-gates/soft-feature-gate-negative_impls.rs @@ -0,0 +1,12 @@ +// For historical reasons, negative impls don't have a proper pre-expansion feature gate. +// We're now at least issuing a *warning* for those that only exist before macro expansion. +// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// As part of this, move these test cases into `feature-gate-negative_impls.rs`. +//@ check-pass + +#[cfg(false)] +impl !Trait for () {} +//~^ WARN negative impls are experimental +//~| WARN unstable syntax can change at any point in the future + +fn main() {} diff --git a/tests/ui/feature-gates/soft-feature-gate-negative_impls.stderr b/tests/ui/feature-gates/soft-feature-gate-negative_impls.stderr new file mode 100644 index 0000000000000..35e125cb2d24c --- /dev/null +++ b/tests/ui/feature-gates/soft-feature-gate-negative_impls.stderr @@ -0,0 +1,14 @@ +warning: negative impls are experimental + --> $DIR/soft-feature-gate-negative_impls.rs:8:6 + | +LL | impl !Trait for () {} + | ^ + | + = note: see issue #68318 for more information + = help: add `#![feature(negative_impls)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: 1 warning emitted + diff --git a/tests/ui/traits/negative-impls/feature-gate-negative_impls.rs b/tests/ui/traits/negative-impls/feature-gate-negative_impls.rs index 8d3f6ff6d78c7..1b3c5abe10918 100644 --- a/tests/ui/traits/negative-impls/feature-gate-negative_impls.rs +++ b/tests/ui/traits/negative-impls/feature-gate-negative_impls.rs @@ -1,3 +1,5 @@ trait MyTrait {} -impl !MyTrait for u32 {} //~ ERROR negative trait bounds are not fully implemented + +impl !MyTrait for u32 {} //~ ERROR negative impls are experimental + fn main() {} diff --git a/tests/ui/traits/negative-impls/feature-gate-negative_impls.stderr b/tests/ui/traits/negative-impls/feature-gate-negative_impls.stderr index 1777dfcc993f1..36bc5bb4be2bc 100644 --- a/tests/ui/traits/negative-impls/feature-gate-negative_impls.stderr +++ b/tests/ui/traits/negative-impls/feature-gate-negative_impls.stderr @@ -1,5 +1,5 @@ -error[E0658]: negative trait bounds are not fully implemented; use marker types for now - --> $DIR/feature-gate-negative_impls.rs:2:6 +error[E0658]: negative impls are experimental + --> $DIR/feature-gate-negative_impls.rs:3:6 | LL | impl !MyTrait for u32 {} | ^^^^^^^^ @@ -7,6 +7,7 @@ LL | impl !MyTrait for u32 {} = note: see issue #68318 for more information = help: add `#![feature(negative_impls)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: use marker types for now error: aborting due to 1 previous error From 92fbfae16b868be626465d005222366ad4b31960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 28 Mar 2026 19:20:21 +0100 Subject: [PATCH 37/38] Emit pre-expansion feature gate warning for item modifier `default` --- compiler/rustc_ast_passes/src/feature_gate.rs | 15 ++++- compiler/rustc_parse/src/parser/item.rs | 11 +++- tests/ui/macros/stringify.rs | 3 +- .../trait-item-with-defaultness-pass.rs | 1 + .../feature-gate-specialization.rs | 21 +++++++ .../feature-gate-specialization.stderr | 45 ++++++++++++++ ...feature-gate-specialization.default.stderr | 62 +++++++++++++++++++ ...oft-feature-gate-specialization.min.stderr | 38 ++++++++++++ .../soft-feature-gate-specialization.rs | 44 +++++++++++++ .../specialization-feature-gate-default.rs | 13 ---- ...specialization-feature-gate-default.stderr | 13 ---- tests/ui/track-diagnostics/track6.rs | 2 +- tests/ui/track-diagnostics/track6.stderr | 2 +- 13 files changed, 238 insertions(+), 32 deletions(-) create mode 100644 tests/ui/specialization/feature-gate-specialization.rs create mode 100644 tests/ui/specialization/feature-gate-specialization.stderr create mode 100644 tests/ui/specialization/soft-feature-gate-specialization.default.stderr create mode 100644 tests/ui/specialization/soft-feature-gate-specialization.min.stderr create mode 100644 tests/ui/specialization/soft-feature-gate-specialization.rs delete mode 100644 tests/ui/specialization/specialization-feature-gate-default.rs delete mode 100644 tests/ui/specialization/specialization-feature-gate-default.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index ae046c61c2767..1b615b611258f 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -224,7 +224,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } if let ast::Defaultness::Default(_) = of_trait.defaultness { - gate!(self, specialization, i.span, "specialization is unstable"); + gate!(self, specialization, i.span, "specialization is experimental"); } } @@ -450,7 +450,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { self.features.specialization() || (is_fn && self.features.min_specialization()), sym::specialization, i.span, - "specialization is unstable" + "specialization is experimental" ); } visit::walk_assoc_item(self, i, ctxt) @@ -615,10 +615,21 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { soft_gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental"); soft_gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental"); soft_gate_all_legacy_dont_use!(negative_impls, "negative impls are experimental"); + soft_gate_all_legacy_dont_use!(specialization, "specialization is experimental"); soft_gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental"); soft_gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); // tidy-alphabetical-end + for &span in spans.get(&sym::min_specialization).into_iter().flatten() { + if !visitor.features.specialization() + && !visitor.features.min_specialization() + && !span.allows_unstable(sym::specialization) + && !span.allows_unstable(sym::min_specialization) + { + feature_warn(visitor.sess, sym::specialization, span, "specialization is experimental"); + } + } + // ----------------------------------------------------------------------------- visit::walk_crate(&mut visitor, krate); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 823d5957949cb..79b79db6ccc07 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -248,10 +248,18 @@ impl<'a> Parser<'a> { self.parse_use_item()? } else if self.check_fn_front_matter(check_pub, case) { // FUNCTION ITEM + let defaultness = def_(); + if let Defaultness::Default(span) = defaultness { + // Default functions should only require feature `min_specialization`. We remove the + // `specialization` tag again as such spans *require* feature `specialization` to be + // enabled. In a later stage, we make `specialization` imply `min_specialization`. + self.psess.gated_spans.gate(sym::min_specialization, span); + self.psess.gated_spans.ungate_last(sym::specialization, span); + } let (ident, sig, generics, contract, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?; ItemKind::Fn(Box::new(Fn { - defaultness: def_(), + defaultness, ident, sig, generics, @@ -1016,6 +1024,7 @@ impl<'a> Parser<'a> { if self.check_keyword(exp!(Default)) && self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As)) { + self.psess.gated_spans.gate(sym::specialization, self.token.span); self.bump(); // `default` Defaultness::Default(self.prev_token_uninterpolated_span()) } else if self.eat_keyword(exp!(Final)) { diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index af2ba7a809ad0..46f50593c4e95 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -9,12 +9,13 @@ #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] +#![feature(macro_guard_matcher)] #![feature(more_qualified_paths)] #![feature(never_patterns)] +#![feature(specialization)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(yeet_expr)] -#![feature(macro_guard_matcher)] #![deny(unused_macros)] // These macros force the use of AST pretty-printing by converting the input to diff --git a/tests/ui/parser/trait-item-with-defaultness-pass.rs b/tests/ui/parser/trait-item-with-defaultness-pass.rs index 164d0b13b539c..e8452fcbd5f99 100644 --- a/tests/ui/parser/trait-item-with-defaultness-pass.rs +++ b/tests/ui/parser/trait-item-with-defaultness-pass.rs @@ -1,4 +1,5 @@ //@ check-pass +#![feature(specialization)] fn main() {} diff --git a/tests/ui/specialization/feature-gate-specialization.rs b/tests/ui/specialization/feature-gate-specialization.rs new file mode 100644 index 0000000000000..82e467ad98d32 --- /dev/null +++ b/tests/ui/specialization/feature-gate-specialization.rs @@ -0,0 +1,21 @@ +trait Trait { + type Ty; + const CT: (); + fn fn_(&self); +} + +impl Trait for T { + default type Ty = (); //~ ERROR specialization is experimental + default const CT: () = (); //~ ERROR specialization is experimental + default fn fn_(&self) {} //~ ERROR specialization is experimental +} + +trait OtherTrait { + fn fn_(); +} + +default impl OtherTrait for T { //~ ERROR specialization is experimental + fn fn_() {} +} + +fn main() {} diff --git a/tests/ui/specialization/feature-gate-specialization.stderr b/tests/ui/specialization/feature-gate-specialization.stderr new file mode 100644 index 0000000000000..2efc0faa7c23e --- /dev/null +++ b/tests/ui/specialization/feature-gate-specialization.stderr @@ -0,0 +1,45 @@ +error[E0658]: specialization is experimental + --> $DIR/feature-gate-specialization.rs:8:5 + | +LL | default type Ty = (); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: specialization is experimental + --> $DIR/feature-gate-specialization.rs:9:5 + | +LL | default const CT: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: specialization is experimental + --> $DIR/feature-gate-specialization.rs:10:5 + | +LL | default fn fn_(&self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: specialization is experimental + --> $DIR/feature-gate-specialization.rs:17:1 + | +LL | / default impl OtherTrait for T { +LL | | fn fn_() {} +LL | | } + | |_^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/specialization/soft-feature-gate-specialization.default.stderr b/tests/ui/specialization/soft-feature-gate-specialization.default.stderr new file mode 100644 index 0000000000000..f5961090947ed --- /dev/null +++ b/tests/ui/specialization/soft-feature-gate-specialization.default.stderr @@ -0,0 +1,62 @@ +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:21:5 + | +LL | default type Ty = (); + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:24:5 + | +LL | default const CT: () = (); + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:40:1 + | +LL | default impl Trait for () {} + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:27:5 + | +LL | default fn fn_(); + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:35:1 + | +LL | default fn fn_() {} + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: 5 warnings emitted + diff --git a/tests/ui/specialization/soft-feature-gate-specialization.min.stderr b/tests/ui/specialization/soft-feature-gate-specialization.min.stderr new file mode 100644 index 0000000000000..aa4ce0cc58be2 --- /dev/null +++ b/tests/ui/specialization/soft-feature-gate-specialization.min.stderr @@ -0,0 +1,38 @@ +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:21:5 + | +LL | default type Ty = (); + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:24:5 + | +LL | default const CT: () = (); + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: specialization is experimental + --> $DIR/soft-feature-gate-specialization.rs:40:1 + | +LL | default impl Trait for () {} + | ^^^^^^^ + | + = note: see issue #31844 for more information + = help: add `#![feature(specialization)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = warning: unstable syntax can change at any point in the future, causing a hard error! + = note: for more information, see issue #65860 + +warning: 3 warnings emitted + diff --git a/tests/ui/specialization/soft-feature-gate-specialization.rs b/tests/ui/specialization/soft-feature-gate-specialization.rs new file mode 100644 index 0000000000000..e671bc20a1f34 --- /dev/null +++ b/tests/ui/specialization/soft-feature-gate-specialization.rs @@ -0,0 +1,44 @@ +// For historical reasons, item modifier `default` doesn't have a proper pre-expansion feature gate. +// We're now at least issuing a *warning* for those that only exist before macro expansion. +// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// As part of this, move these test cases into `feature-gate-specialization.rs`. +// +// Moreover, `specialization` implies `min_specialization` similar to the post-expansion gate. +// +// However, while we only gate `default` *associated* functions only behind `min_specialization` OR +// `specialization` in the post-expansion case, in the pre-expansion case we gate all kinds of +// functions (free, assoc, foreign) behind `min_specialization` OR `specialization` if marked with +// `default` for simplicity of implementation. Ultimately it doesn't matter since we later reject +// `default` on anything other than impls & impl assoc items during semantic analysis. +// +//@ revisions: default min full +//@ check-pass +#![cfg_attr(min, feature(min_specialization))] +#![cfg_attr(full, feature(specialization))] + +#[cfg(false)] +impl Trait for () { + default type Ty = (); + //[default,min]~^ WARN specialization is experimental + //[default,min]~| WARN unstable syntax can change at any point in the future + default const CT: () = (); + //[default,min]~^ WARN specialization is experimental + //[default,min]~| WARN unstable syntax can change at any point in the future + default fn fn_(); + //[default]~^ WARN specialization is experimental + //[default]~| WARN unstable syntax can change at any point in the future +} + +// While free ty/ct/fn items marked `default` are +// semantically malformed we still need to gate the keyword! +#[cfg(false)] +default fn fn_() {} +//[default]~^ WARN specialization is experimental +//[default]~| WARN unstable syntax can change at any point in the future + +#[cfg(false)] +default impl Trait for () {} +//[default,min]~^ WARN specialization is experimental +//[default,min]~| WARN unstable syntax can change at any point in the future + +fn main() {} diff --git a/tests/ui/specialization/specialization-feature-gate-default.rs b/tests/ui/specialization/specialization-feature-gate-default.rs deleted file mode 100644 index 8bad3ac0a1fb3..0000000000000 --- a/tests/ui/specialization/specialization-feature-gate-default.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Check that specialization must be ungated to use the `default` keyword - -// gate-test-specialization - -trait Foo { - fn foo(&self); -} - -impl Foo for T { - default fn foo(&self) {} //~ ERROR specialization is unstable -} - -fn main() {} diff --git a/tests/ui/specialization/specialization-feature-gate-default.stderr b/tests/ui/specialization/specialization-feature-gate-default.stderr deleted file mode 100644 index 3e651b6ee4f37..0000000000000 --- a/tests/ui/specialization/specialization-feature-gate-default.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: specialization is unstable - --> $DIR/specialization-feature-gate-default.rs:10:5 - | -LL | default fn foo(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #31844 for more information - = help: add `#![feature(specialization)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/track-diagnostics/track6.rs b/tests/ui/track-diagnostics/track6.rs index aa75c5691e5f9..38d34f46270a4 100644 --- a/tests/ui/track-diagnostics/track6.rs +++ b/tests/ui/track-diagnostics/track6.rs @@ -12,7 +12,7 @@ pub trait Foo { impl Foo for T { default fn bar() {} - //~^ ERROR specialization is unstable + //~^ ERROR specialization is experimental //~| NOTE created at } diff --git a/tests/ui/track-diagnostics/track6.stderr b/tests/ui/track-diagnostics/track6.stderr index a61f7855e3231..30d518a69cadd 100644 --- a/tests/ui/track-diagnostics/track6.stderr +++ b/tests/ui/track-diagnostics/track6.stderr @@ -1,4 +1,4 @@ -error[E0658]: specialization is unstable +error[E0658]: specialization is experimental --> $DIR/track6.rs:LL:CC | LL | default fn bar() {} From 3e658d3744373f0b362acacd6c9daf50ee7441c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 1 Apr 2026 14:15:26 +0200 Subject: [PATCH 38/38] Reword descriptions of soft feature gate tests --- tests/ui/feature-gates/soft-feature-gate-auto_traits.rs | 6 +++--- tests/ui/feature-gates/soft-feature-gate-box_patterns.rs | 6 +++--- tests/ui/feature-gates/soft-feature-gate-decl_macro.rs | 6 +++--- tests/ui/feature-gates/soft-feature-gate-negative_impls.rs | 6 +++--- tests/ui/feature-gates/soft-feature-gate-trait_alias.rs | 6 +++--- tests/ui/feature-gates/soft-feature-gate-try_blocks.rs | 6 +++--- tests/ui/specialization/soft-feature-gate-specialization.rs | 4 ++-- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/ui/feature-gates/soft-feature-gate-auto_traits.rs b/tests/ui/feature-gates/soft-feature-gate-auto_traits.rs index 0ccbaf1e476a9..01f44df0d7621 100644 --- a/tests/ui/feature-gates/soft-feature-gate-auto_traits.rs +++ b/tests/ui/feature-gates/soft-feature-gate-auto_traits.rs @@ -1,6 +1,6 @@ -// For historical reasons, auto traits don't have a proper pre-expansion feature gate. -// We're now at least issuing a *warning* for those that only exist before macro expansion. -// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// For historical reasons, auto traits don't have an erroring pre-expansion feature gate. +// We're now at least issuing a warning for those that only exist before macro expansion. +// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate. // As part of this, move these test cases into `feature-gate-auto-traits.rs`. //@ check-pass diff --git a/tests/ui/feature-gates/soft-feature-gate-box_patterns.rs b/tests/ui/feature-gates/soft-feature-gate-box_patterns.rs index 8ab0e80e6be31..9fdaa7a0ec08c 100644 --- a/tests/ui/feature-gates/soft-feature-gate-box_patterns.rs +++ b/tests/ui/feature-gates/soft-feature-gate-box_patterns.rs @@ -1,6 +1,6 @@ -// For historical reasons, box patterns don't have a proper pre-expansion feature gate. -// We're now at least issuing a *warning* for those that only exist before macro expansion. -// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// For historical reasons, box patterns don't have an erroring pre-expansion feature gate. +// We're now at least issuing a warning for those that only exist before macro expansion. +// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate. // As part of this, move these test cases into `feature-gate-box_patterns.rs`. //@ check-pass diff --git a/tests/ui/feature-gates/soft-feature-gate-decl_macro.rs b/tests/ui/feature-gates/soft-feature-gate-decl_macro.rs index 7d2d48503797a..dbc0b940265fc 100644 --- a/tests/ui/feature-gates/soft-feature-gate-decl_macro.rs +++ b/tests/ui/feature-gates/soft-feature-gate-decl_macro.rs @@ -1,6 +1,6 @@ -// For historical reasons, decl macros 2.0 don't have a proper pre-expansion feature gate. -// We're now at least issuing a *warning* for those that only exist before macro expansion. -// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// For historical reasons, decl macros 2.0 don't have an erroring pre-expansion feature gate. +// We're now at least issuing a warning for those that only exist before macro expansion. +// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate. // As part of this, move these test cases into `feature-gate-decl_macro.rs`. //@ check-pass diff --git a/tests/ui/feature-gates/soft-feature-gate-negative_impls.rs b/tests/ui/feature-gates/soft-feature-gate-negative_impls.rs index 7c578f20d2dc0..0071ce6350b16 100644 --- a/tests/ui/feature-gates/soft-feature-gate-negative_impls.rs +++ b/tests/ui/feature-gates/soft-feature-gate-negative_impls.rs @@ -1,6 +1,6 @@ -// For historical reasons, negative impls don't have a proper pre-expansion feature gate. -// We're now at least issuing a *warning* for those that only exist before macro expansion. -// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// For historical reasons, negative impls don't have an erroring pre-expansion feature gate. +// We're now at least issuing a warning for those that only exist before macro expansion. +// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate. // As part of this, move these test cases into `feature-gate-negative_impls.rs`. //@ check-pass diff --git a/tests/ui/feature-gates/soft-feature-gate-trait_alias.rs b/tests/ui/feature-gates/soft-feature-gate-trait_alias.rs index 553e88375a2a7..7f929f5198a70 100644 --- a/tests/ui/feature-gates/soft-feature-gate-trait_alias.rs +++ b/tests/ui/feature-gates/soft-feature-gate-trait_alias.rs @@ -1,6 +1,6 @@ -// For historical reasons, trait aliases don't have a proper pre-expansion feature gate. -// We're now at least issuing a *warning* for those that only exist before macro expansion. -// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// For historical reasons, trait aliases don't have an erroring pre-expansion feature gate. +// We're now at least issuing a warning for those that only exist before macro expansion. +// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate. // As part of this, move these test cases into `feature-gate-trait-alias.rs`. //@ check-pass diff --git a/tests/ui/feature-gates/soft-feature-gate-try_blocks.rs b/tests/ui/feature-gates/soft-feature-gate-try_blocks.rs index aa51e60d56b4a..3f439d0c26893 100644 --- a/tests/ui/feature-gates/soft-feature-gate-try_blocks.rs +++ b/tests/ui/feature-gates/soft-feature-gate-try_blocks.rs @@ -1,6 +1,6 @@ -// For historical reasons, try blocks don't have a proper pre-expansion feature gate. -// We're now at least issuing a *warning* for those that only exist before macro expansion. -// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// For historical reasons, try blocks don't have an erroring pre-expansion feature gate. +// We're now at least issuing a warning for those that only exist before macro expansion. +// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate. // As part of this, move these test cases into `feature-gate-try_blocks.rs`. //@ edition: 2018 //@ check-pass diff --git a/tests/ui/specialization/soft-feature-gate-specialization.rs b/tests/ui/specialization/soft-feature-gate-specialization.rs index e671bc20a1f34..46e4b28e61d23 100644 --- a/tests/ui/specialization/soft-feature-gate-specialization.rs +++ b/tests/ui/specialization/soft-feature-gate-specialization.rs @@ -1,6 +1,6 @@ // For historical reasons, item modifier `default` doesn't have a proper pre-expansion feature gate. -// We're now at least issuing a *warning* for those that only exist before macro expansion. -// FIXME(#154045): Turn their post-expansion feature gate into a proper pre-expansion one. +// We're now at least issuing a warning for those that only exist before macro expansion. +// FIXME(#154045): Turn this pre-expansion warning into an error and remove the post-expansion gate. // As part of this, move these test cases into `feature-gate-specialization.rs`. // // Moreover, `specialization` implies `min_specialization` similar to the post-expansion gate.