From 65a91d179f56e9866c7d501293121887330e4956 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 8 Mar 2026 22:02:16 +0900 Subject: [PATCH 01/68] Add suggestion for E0401 on inner const items --- compiler/rustc_resolve/src/diagnostics.rs | 26 +++++++---- compiler/rustc_resolve/src/errors.rs | 15 ++++++ compiler/rustc_resolve/src/ident.rs | 8 ++-- compiler/rustc_resolve/src/lib.rs | 3 +- ...om-outer-item-in-const-item.default.stderr | 19 +++++++- ...m-in-const-item.generic_const_items.stderr | 19 +++++++- ...ic-params-from-outer-item-in-const-item.rs | 3 ++ ...ic-params-from-outer-item-suggestion.fixed | 39 ++++++++++++++++ ...neric-params-from-outer-item-suggestion.rs | 39 ++++++++++++++++ ...c-params-from-outer-item-suggestion.stderr | 46 +++++++++++++++++++ 10 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 tests/ui/resolve/generic-params-from-outer-item-suggestion.fixed create mode 100644 tests/ui/resolve/generic-params-from-outer-item-suggestion.rs create mode 100644 tests/ui/resolve/generic-params-from-outer-item-suggestion.stderr diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 1b1198af41c0e..a8a78854d7293 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -573,12 +573,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span, label: None, refer_to_type_directly: None, + use_let: None, sugg: None, static_or_const, is_self, - item: inner_item.as_ref().map(|(span, kind)| { + item: inner_item.as_ref().map(|(label_span, _, kind)| { errs::GenericParamsFromOuterItemInnerItem { - span: *span, + span: *label_span, descr: kind.descr().to_string(), is_self, } @@ -586,10 +587,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let sm = self.tcx.sess.source_map(); + // Note: do not early return for missing def_id here, + // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`. let def_id = match outer_res { Res::SelfTyParam { .. } => { err.label = Some(Label::SelfTyParam(span)); - return self.dcx().create_err(err); + None } Res::SelfTyAlias { alias_to: def_id, .. } => { err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword( @@ -598,15 +601,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ))); err.refer_to_type_directly = current_self_ty.map(|snippet| errs::UseTypeDirectly { span, snippet }); - return self.dcx().create_err(err); + None } Res::Def(DefKind::TyParam, def_id) => { err.label = Some(Label::TyParam(self.def_span(def_id))); - def_id + Some(def_id) } Res::Def(DefKind::ConstParam, def_id) => { err.label = Some(Label::ConstParam(self.def_span(def_id))); - def_id + Some(def_id) } _ => { bug!( @@ -617,8 +620,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } }; - if let HasGenericParams::Yes(span) = has_generic_params - && !matches!(inner_item, Some((_, ItemKind::Delegation(..)))) + if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() { + err.use_let = Some(errs::GenericParamsFromOuterItemUseLet { + span: sm.span_until_whitespace(*item_span), + }); + } + + if let Some(def_id) = def_id + && let HasGenericParams::Yes(span) = has_generic_params + && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..)))) { let name = self.tcx.item_name(def_id); let (span, snippet) = if span.is_empty() { diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 63ffdbc37f7d1..d09c76d7947a9 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -33,6 +33,8 @@ pub(crate) struct GenericParamsFromOuterItem { #[subdiagnostic] pub(crate) refer_to_type_directly: Option, #[subdiagnostic] + pub(crate) use_let: Option, + #[subdiagnostic] pub(crate) sugg: Option, #[subdiagnostic] pub(crate) static_or_const: Option, @@ -87,6 +89,19 @@ pub(crate) struct GenericParamsFromOuterItemSugg { pub(crate) span: Span, pub(crate) snippet: String, } + +#[derive(Subdiagnostic)] +#[suggestion( + "try using a local `let` binding instead", + code = "let", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct GenericParamsFromOuterItemUseLet { + #[primary_span] + pub(crate) span: Span, +} + #[derive(Subdiagnostic)] #[suggestion( "refer to the type directly here instead", diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 7cfd5b5f861a4..56e77cdde91f1 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1582,12 +1582,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let item = if let Some(diag_metadata) = diag_metadata && let Some(current_item) = diag_metadata.current_item { - let span = current_item + let label_span = current_item .kind .ident() .map(|i| i.span) .unwrap_or(current_item.span); - Some((span, current_item.kind.clone())) + Some((label_span, current_item.span, current_item.kind.clone())) } else { None }; @@ -1679,12 +1679,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let item = if let Some(diag_metadata) = diag_metadata && let Some(current_item) = diag_metadata.current_item { - let span = current_item + let label_span = current_item .kind .ident() .map(|i| i.span) .unwrap_or(current_item.span); - Some((span, current_item.kind.clone())) + Some((label_span, current_item.span, current_item.kind.clone())) } else { None }; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index ead69473b00d1..3cd5c827693a4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -247,7 +247,8 @@ enum ResolutionError<'ra> { outer_res: Res, has_generic_params: HasGenericParams, def_kind: DefKind, - inner_item: Option<(Span, ast::ItemKind)>, + /// 1. label span, 2. item span, 3. item kind + inner_item: Option<(Span, Span, ast::ItemKind)>, current_self_ty: Option, }, /// Error E0403: the name is already used for a type or const parameter in this generic diff --git a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr index 8827d1bbb49d3..21f80198f1978 100644 --- a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr +++ b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.default.stderr @@ -10,9 +10,14 @@ LL | const K: u32 = T::C; | = note: nested items are independent from their parent item for everything except for privacy and name resolution = note: a `const` is a separate item from the item that contains it +help: try using a local `let` binding instead + | +LL - const K: u32 = T::C; +LL + let K: u32 = T::C; + | error[E0401]: can't use generic parameters from outer item - --> $DIR/generic-params-from-outer-item-in-const-item.rs:19:24 + --> $DIR/generic-params-from-outer-item-in-const-item.rs:20:24 | LL | impl Tr for T { // outer impl block | - type parameter from outer item @@ -24,9 +29,14 @@ LL | const I: u32 = T::C; | = note: nested items are independent from their parent item for everything except for privacy and name resolution = note: a `const` is a separate item from the item that contains it +help: try using a local `let` binding instead + | +LL - const I: u32 = T::C; +LL + let I: u32 = T::C; + | error[E0401]: can't use generic parameters from outer item - --> $DIR/generic-params-from-outer-item-in-const-item.rs:27:20 + --> $DIR/generic-params-from-outer-item-in-const-item.rs:29:20 | LL | struct S(U32<{ // outer struct | - type parameter from outer item @@ -37,6 +47,11 @@ LL | const _: u32 = T::C; | = note: nested items are independent from their parent item for everything except for privacy and name resolution = note: a `const` is a separate item from the item that contains it +help: try using a local `let` binding instead + | +LL - const _: u32 = T::C; +LL + let _: u32 = T::C; + | error: aborting due to 3 previous errors diff --git a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr index 8ff9771b9ade6..54a53a097bcbe 100644 --- a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr +++ b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.generic_const_items.stderr @@ -10,13 +10,18 @@ LL | const K: u32 = T::C; | = note: nested items are independent from their parent item for everything except for privacy and name resolution = note: a `const` is a separate item from the item that contains it +help: try using a local `let` binding instead + | +LL - const K: u32 = T::C; +LL + let K: u32 = T::C; + | help: try introducing a local generic parameter here | LL | const K: u32 = T::C; | +++ error[E0401]: can't use generic parameters from outer item - --> $DIR/generic-params-from-outer-item-in-const-item.rs:19:24 + --> $DIR/generic-params-from-outer-item-in-const-item.rs:20:24 | LL | impl Tr for T { // outer impl block | - type parameter from outer item @@ -28,13 +33,18 @@ LL | const I: u32 = T::C; | = note: nested items are independent from their parent item for everything except for privacy and name resolution = note: a `const` is a separate item from the item that contains it +help: try using a local `let` binding instead + | +LL - const I: u32 = T::C; +LL + let I: u32 = T::C; + | help: try introducing a local generic parameter here | LL | const I: u32 = T::C; | +++ error[E0401]: can't use generic parameters from outer item - --> $DIR/generic-params-from-outer-item-in-const-item.rs:27:20 + --> $DIR/generic-params-from-outer-item-in-const-item.rs:29:20 | LL | struct S(U32<{ // outer struct | - type parameter from outer item @@ -45,6 +55,11 @@ LL | const _: u32 = T::C; | = note: nested items are independent from their parent item for everything except for privacy and name resolution = note: a `const` is a separate item from the item that contains it +help: try using a local `let` binding instead + | +LL - const _: u32 = T::C; +LL + let _: u32 = T::C; + | help: try introducing a local generic parameter here | LL | const _: u32 = T::C; diff --git a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs index c9a64de7f6bba..9616144ec3103 100644 --- a/tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs +++ b/tests/ui/resolve/generic-params-from-outer-item-in-const-item.rs @@ -11,6 +11,7 @@ fn outer() { // outer function const K: u32 = T::C; //~^ ERROR can't use generic parameters from outer item + //~| HELP try using a local `let` binding instead //[generic_const_items]~| HELP try introducing a local generic parameter here } @@ -18,6 +19,7 @@ impl Tr for T { // outer impl block const C: u32 = { const I: u32 = T::C; //~^ ERROR can't use generic parameters from outer item + //~| HELP try using a local `let` binding instead //[generic_const_items]~| HELP try introducing a local generic parameter here I }; @@ -26,6 +28,7 @@ impl Tr for T { // outer impl block struct S(U32<{ // outer struct const _: u32 = T::C; //~^ ERROR can't use generic parameters from outer item + //~| HELP try using a local `let` binding instead //[generic_const_items]~| HELP try introducing a local generic parameter here 0 }>); diff --git a/tests/ui/resolve/generic-params-from-outer-item-suggestion.fixed b/tests/ui/resolve/generic-params-from-outer-item-suggestion.fixed new file mode 100644 index 0000000000000..fea9f7a6f8516 --- /dev/null +++ b/tests/ui/resolve/generic-params-from-outer-item-suggestion.fixed @@ -0,0 +1,39 @@ +// Regression test for . +// Ensure we provide a suggestion to change from `const` to `let` +// on the generic params from outer item errors. + +//@ run-rustfix + +#![allow(unused, non_snake_case)] + +const fn size_plus_one() -> usize { + //~^ NOTE type parameter from outer item + let size: usize = core::mem::size_of::(); + //~^ ERROR can't use generic parameters from outer item + //~| NOTE nested items are independent + //~| NOTE a `const` is a separate item + //~| NOTE use of generic parameter from outer item + //~| NOTE generic parameter used in this inner constant item + //~| HELP try using a local `let` binding instead + size + 1 +} + +struct A; + +impl A { + //~^ NOTE `Self` type implicitly declared here, by this `impl` + const VALUE: u32 = 1; + + fn f() { + let K: u32 = A::VALUE; + //~^ ERROR can't use `Self` from outer item + //~| NOTE use of `Self` from outer item + //~| NOTE `Self` used in this inner constant item + //~| NOTE nested items are independent + //~| NOTE a `const` is a separate item + //~| HELP refer to the type directly here instead + //~| HELP try using a local `let` binding instead + } +} + +fn main() {} diff --git a/tests/ui/resolve/generic-params-from-outer-item-suggestion.rs b/tests/ui/resolve/generic-params-from-outer-item-suggestion.rs new file mode 100644 index 0000000000000..3f87f3e2a2ae8 --- /dev/null +++ b/tests/ui/resolve/generic-params-from-outer-item-suggestion.rs @@ -0,0 +1,39 @@ +// Regression test for . +// Ensure we provide a suggestion to change from `const` to `let` +// on the generic params from outer item errors. + +//@ run-rustfix + +#![allow(unused, non_snake_case)] + +const fn size_plus_one() -> usize { + //~^ NOTE type parameter from outer item + const size: usize = core::mem::size_of::(); + //~^ ERROR can't use generic parameters from outer item + //~| NOTE nested items are independent + //~| NOTE a `const` is a separate item + //~| NOTE use of generic parameter from outer item + //~| NOTE generic parameter used in this inner constant item + //~| HELP try using a local `let` binding instead + size + 1 +} + +struct A; + +impl A { + //~^ NOTE `Self` type implicitly declared here, by this `impl` + const VALUE: u32 = 1; + + fn f() { + const K: u32 = Self::VALUE; + //~^ ERROR can't use `Self` from outer item + //~| NOTE use of `Self` from outer item + //~| NOTE `Self` used in this inner constant item + //~| NOTE nested items are independent + //~| NOTE a `const` is a separate item + //~| HELP refer to the type directly here instead + //~| HELP try using a local `let` binding instead + } +} + +fn main() {} diff --git a/tests/ui/resolve/generic-params-from-outer-item-suggestion.stderr b/tests/ui/resolve/generic-params-from-outer-item-suggestion.stderr new file mode 100644 index 0000000000000..3d3280cfbbb4b --- /dev/null +++ b/tests/ui/resolve/generic-params-from-outer-item-suggestion.stderr @@ -0,0 +1,46 @@ +error[E0401]: can't use generic parameters from outer item + --> $DIR/generic-params-from-outer-item-suggestion.rs:11:46 + | +LL | const fn size_plus_one() -> usize { + | - type parameter from outer item +LL | +LL | const size: usize = core::mem::size_of::(); + | ---- ^ use of generic parameter from outer item + | | + | generic parameter used in this inner constant item + | + = note: nested items are independent from their parent item for everything except for privacy and name resolution + = note: a `const` is a separate item from the item that contains it +help: try using a local `let` binding instead + | +LL - const size: usize = core::mem::size_of::(); +LL + let size: usize = core::mem::size_of::(); + | + +error[E0401]: can't use `Self` from outer item + --> $DIR/generic-params-from-outer-item-suggestion.rs:28:24 + | +LL | impl A { + | ---- `Self` type implicitly declared here, by this `impl` +... +LL | const K: u32 = Self::VALUE; + | - ^^^^ use of `Self` from outer item + | | + | `Self` used in this inner constant item + | + = note: nested items are independent from their parent item for everything except for privacy and name resolution + = note: a `const` is a separate item from the item that contains it +help: refer to the type directly here instead + | +LL - const K: u32 = Self::VALUE; +LL + const K: u32 = A::VALUE; + | +help: try using a local `let` binding instead + | +LL - const K: u32 = Self::VALUE; +LL + let K: u32 = Self::VALUE; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0401`. From 3f7c6455178d2918803dde16bb5ed1b47c327c2b Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Mon, 30 Mar 2026 17:41:22 +0100 Subject: [PATCH 02/68] Suggest public re-exports when a private module blocks an import path When an import like `use crate::a::b::Item` fails because module `b` is private, the resolver now looks up public re-export paths for `Item` and suggests them to the user. --- compiler/rustc_resolve/src/diagnostics.rs | 111 +++++++++++++++++++++- compiler/rustc_resolve/src/imports.rs | 21 ++++ 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 97c88064e9799..e16645ec0df45 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -2116,6 +2116,111 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` } + /// Returns the path segments (as symbols) of a module, including `kw::Crate` at the start. + /// For example, for `crate::foo::bar`, returns `[Crate, foo, bar]`. + /// Returns `None` for block modules that don't have a `DefId`. + fn module_path_names(&self, module: Module<'ra>) -> Option> { + let mut path = Vec::new(); + let mut def_id = module.opt_def_id()?; + while let Some(parent) = self.tcx.opt_parent(def_id) { + if let Some(name) = self.tcx.opt_item_name(def_id) { + path.push(name); + } + if parent.is_top_level_module() { + break; + } + def_id = parent; + } + path.reverse(); + path.insert(0, kw::Crate); + Some(path) + } + + /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) + /// relative to the current scope, if possible. Only applies to crate-local items and + /// only when the resulting path is actually shorter than the original. + fn shorten_candidate_path( + &self, + suggestion: &mut ImportSuggestion, + current_module: Module<'ra>, + ) { + const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; + + // Only shorten local items. + if suggestion.did.is_none_or(|did| !did.is_local()) { + return; + } + + // Build current module path: [Crate, foo, bar, ...]. + let Some(current_mod_path) = self.module_path_names(current_module) else { + return; + }; + + // Normalise candidate path: filter out `PathRoot` (`::`), and if the path + // doesn't start with `Crate`, prepend it (edition 2015 paths are relative + // to the crate root without an explicit `crate::` prefix). + let candidate_names = { + let filtered_segments: Vec<_> = suggestion + .path + .segments + .iter() + .filter(|segment| segment.ident.name != kw::PathRoot) + .collect(); + + let mut candidate_names: Vec = + filtered_segments.iter().map(|segment| segment.ident.name).collect(); + if candidate_names.first() != Some(&kw::Crate) { + candidate_names.insert(0, kw::Crate); + } + if candidate_names.len() < 2 { + return; + } + candidate_names + }; + + // The candidate's module path is everything except the last segment (the item name). + let candidate_mod_names = &candidate_names[..candidate_names.len() - 1]; + + // Find the longest common prefix between the current module and candidate module paths. + let common_prefix_length = current_mod_path + .iter() + .zip(candidate_mod_names.iter()) + .take_while(|(current, candidate)| current == candidate) + .count(); + + // Non-crate-local item; keep the full absolute path. + if common_prefix_length == 0 { + return; + } + + let super_count = current_mod_path.len() - common_prefix_length; + + // At the crate root, `use` paths resolve from the crate root anyway, so we can + // drop the `crate::` prefix entirely instead of replacing it with `self::`. + let at_crate_root = current_mod_path.len() == 1; + + let mut new_segments = if super_count == 0 && at_crate_root { + ThinVec::new() + } else { + let prefix_keyword = match super_count { + 0 => kw::SelfLower, + 1..=MAX_SUPER_PATH_ITEMS_IN_SUGGESTION => kw::Super, + _ => return, // Too many `super` levels; keep the full absolute path. + }; + thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(prefix_keyword),)] + }; + for &name in &candidate_names[common_prefix_length..] { + new_segments.push(ast::PathSegment::from_ident(Ident::with_dummy_span(name))); + } + + // Only apply if the result is strictly shorter than the original path. + if new_segments.len() >= suggestion.path.segments.len() { + return; + } + + suggestion.path = Path { span: suggestion.path.span, segments: new_segments, tokens: None }; + } + fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { let PrivacyError { ident, @@ -2144,12 +2249,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut not_publicly_reexported = false; if let Some((this_res, outer_ident)) = outermost_res { - let import_suggestions = self.lookup_import_candidates( + let mut import_suggestions = self.lookup_import_candidates( outer_ident, this_res.ns().unwrap_or(Namespace::TypeNS), &parent_scope, &|res: Res| res == this_res, ); + // Shorten candidate paths using `super::` or `self::` when possible. + for suggestion in &mut import_suggestions { + self.shorten_candidate_path(suggestion, parent_scope.module); + } let point_to_def = !show_candidates( self.tcx, &mut err, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 6507ee3477379..db098b4a14bc4 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1188,6 +1188,27 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for error in &mut self.privacy_errors[privacy_errors_len..] { error.outermost_res = res; } + } else { + // The final item is not a module (e.g., a struct, function, or macro). + // Resolve it directly in the parent module to get its Res, so + // `report_privacy_error()` can search for public re-export paths. + for ns in [TypeNS, ValueNS, MacroNS] { + if let Ok(binding) = self.cm().resolve_ident_in_module( + module, + ident, + ns, + &import.parent_scope, + None, + ignore_decl, + None, + ) { + let res = binding.res(); + for error in &mut self.privacy_errors[privacy_errors_len..] { + error.outermost_res = Some((res, ident)); + } + break; + } + } } } From 70fd887aa2c6a7a5dad1a3a635526e6b2d89097e Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Mon, 30 Mar 2026 17:41:33 +0100 Subject: [PATCH 03/68] Add and update tests for public re-export suggestions on private module errors --- tests/ui/imports/reexports.stderr | 8 +- .../suggest-public-reexport-for-use.rs | 49 ++++++++ .../suggest-public-reexport-for-use.stderr | 119 ++++++++++++++++++ tests/ui/privacy/privacy1.stderr | 17 ++- 4 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 tests/ui/imports/suggest-public-reexport-for-use.rs create mode 100644 tests/ui/imports/suggest-public-reexport-for-use.stderr diff --git a/tests/ui/imports/reexports.stderr b/tests/ui/imports/reexports.stderr index 0ebcf8e58d627..cb3836305c1ab 100644 --- a/tests/ui/imports/reexports.stderr +++ b/tests/ui/imports/reexports.stderr @@ -14,7 +14,9 @@ error[E0603]: module import `foo` is private --> $DIR/reexports.rs:36:22 | LL | use crate::b::a::foo::S; - | ^^^ private module import + | ^^^ - struct `S` is not publicly re-exported + | | + | private module import | note: the module import `foo` is defined here... --> $DIR/reexports.rs:24:17 @@ -31,7 +33,9 @@ error[E0603]: module import `foo` is private --> $DIR/reexports.rs:37:22 | LL | use crate::b::b::foo::S as T; - | ^^^ private module import + | ^^^ - struct `S` is not publicly re-exported + | | + | private module import | note: the module import `foo` is defined here... --> $DIR/reexports.rs:29:17 diff --git a/tests/ui/imports/suggest-public-reexport-for-use.rs b/tests/ui/imports/suggest-public-reexport-for-use.rs new file mode 100644 index 0000000000000..161d257510d26 --- /dev/null +++ b/tests/ui/imports/suggest-public-reexport-for-use.rs @@ -0,0 +1,49 @@ +//@ edition:2021 + +// When a `use` statement accesses an item through a private module, +// the compiler should suggest a public re-export if one exists. + +mod outer { + pub use self::inner::MyStruct; + pub use self::inner::my_function; + pub use self::inner::MyTrait; + pub use self::inner::MyEnum; + + mod inner { + pub struct MyStruct; + pub fn my_function() {} + pub trait MyTrait {} + pub enum MyEnum { + Variant, + } + } +} + +// Accessing items through a private module should suggest the public re-export. +use outer::inner::MyStruct; //~ ERROR module `inner` is private +use outer::inner::my_function; //~ ERROR module `inner` is private +use outer::inner::MyTrait; //~ ERROR module `inner` is private +use outer::inner::MyEnum; //~ ERROR module `inner` is private + +// From a sibling module, the suggestion should use `super::`. +mod sibling { + use crate::outer::inner::MyStruct; //~ ERROR module `inner` is private +} + +// From a deeply nested module, the suggestion should keep the full path. +mod deep { + mod nested { + use crate::outer::inner::MyStruct; //~ ERROR module `inner` is private + } +} + +// Items with no public re-export should say "not publicly re-exported". +mod no_reexport { + mod hidden { + pub struct Secret; + } +} + +use no_reexport::hidden::Secret; //~ ERROR module `hidden` is private + +fn main() {} diff --git a/tests/ui/imports/suggest-public-reexport-for-use.stderr b/tests/ui/imports/suggest-public-reexport-for-use.stderr new file mode 100644 index 0000000000000..2a86e52155ed0 --- /dev/null +++ b/tests/ui/imports/suggest-public-reexport-for-use.stderr @@ -0,0 +1,119 @@ +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:23:12 + | +LL | use outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:12:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use outer::inner::MyStruct; +LL + use crate::outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:24:12 + | +LL | use outer::inner::my_function; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:12:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this function through its public re-export instead + | +LL - use outer::inner::my_function; +LL + use crate::outer::my_function; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:25:12 + | +LL | use outer::inner::MyTrait; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:12:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this trait through its public re-export instead + | +LL - use outer::inner::MyTrait; +LL + use crate::outer::MyTrait; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:26:12 + | +LL | use outer::inner::MyEnum; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:12:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this enum through its public re-export instead + | +LL - use outer::inner::MyEnum; +LL + use crate::outer::MyEnum; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:30:23 + | +LL | use crate::outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:12:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use crate::outer::inner::MyStruct; +LL + use crate::outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:36:27 + | +LL | use crate::outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:12:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use crate::outer::inner::MyStruct; +LL + use crate::outer::MyStruct; + | + +error[E0603]: module `hidden` is private + --> $DIR/suggest-public-reexport-for-use.rs:47:18 + | +LL | use no_reexport::hidden::Secret; + | ^^^^^^ ------ struct `Secret` is not publicly re-exported + | | + | private module + | +note: the module `hidden` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:42:5 + | +LL | mod hidden { + | ^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/privacy/privacy1.stderr b/tests/ui/privacy/privacy1.stderr index f62ef3ae2e4cc..5d1e003c703b4 100644 --- a/tests/ui/privacy/privacy1.stderr +++ b/tests/ui/privacy/privacy1.stderr @@ -4,6 +4,12 @@ error[E0603]: module `baz` is private LL | use bar::baz::{foo, bar}; | ^^^ private module | +help: consider importing this function through its public re-export instead: + bar::foo + --> $DIR/privacy1.rs:139:24 + | +LL | use bar::baz::{foo, bar}; + | ^^^ note: the module `baz` is defined here --> $DIR/privacy1.rs:57:5 | @@ -16,12 +22,17 @@ error[E0603]: module `baz` is private LL | use bar::baz::{foo, bar}; | ^^^ private module | +help: consider importing this function through its public re-export instead: + bar::bar + --> $DIR/privacy1.rs:139:29 + | +LL | use bar::baz::{foo, bar}; + | ^^^ note: the module `baz` is defined here --> $DIR/privacy1.rs:57:5 | LL | mod baz { | ^^^^^^^ - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0603]: module `baz` is private --> $DIR/privacy1.rs:148:18 @@ -39,7 +50,9 @@ error[E0603]: module `i` is private --> $DIR/privacy1.rs:172:20 | LL | use self::foo::i::A; - | ^ private module + | ^ - struct `A` is not publicly re-exported + | | + | private module | note: the module `i` is defined here --> $DIR/privacy1.rs:177:9 From d4617fa747d99d7eab4a0f9f98ddd806b597044d Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Mon, 30 Mar 2026 22:09:47 +0100 Subject: [PATCH 04/68] Parametrise the newly added test by all four editions --- ...public-reexport-for-use.edition2015.stderr | 119 ++++++++++++++++++ ...ublic-reexport-for-use.edition2018.stderr} | 36 +++--- ...public-reexport-for-use.edition2021.stderr | 119 ++++++++++++++++++ ...public-reexport-for-use.edition2024.stderr | 119 ++++++++++++++++++ .../suggest-public-reexport-for-use.rs | 21 +++- 5 files changed, 392 insertions(+), 22 deletions(-) create mode 100644 tests/ui/imports/suggest-public-reexport-for-use.edition2015.stderr rename tests/ui/imports/{suggest-public-reexport-for-use.stderr => suggest-public-reexport-for-use.edition2018.stderr} (74%) create mode 100644 tests/ui/imports/suggest-public-reexport-for-use.edition2021.stderr create mode 100644 tests/ui/imports/suggest-public-reexport-for-use.edition2024.stderr diff --git a/tests/ui/imports/suggest-public-reexport-for-use.edition2015.stderr b/tests/ui/imports/suggest-public-reexport-for-use.edition2015.stderr new file mode 100644 index 0000000000000..815f91a0b6c8a --- /dev/null +++ b/tests/ui/imports/suggest-public-reexport-for-use.edition2015.stderr @@ -0,0 +1,119 @@ +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:27:12 + | +LL | use outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use outer::inner::MyStruct; +LL + use outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:28:12 + | +LL | use outer::inner::my_function; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this function through its public re-export instead + | +LL - use outer::inner::my_function; +LL + use outer::my_function; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:29:12 + | +LL | use outer::inner::MyTrait; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this trait through its public re-export instead + | +LL - use outer::inner::MyTrait; +LL + use outer::MyTrait; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:30:12 + | +LL | use outer::inner::MyEnum; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this enum through its public re-export instead + | +LL - use outer::inner::MyEnum; +LL + use outer::MyEnum; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:36:16 + | +LL | use outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use outer::inner::MyStruct; +LL + use outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:46:20 + | +LL | use outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use outer::inner::MyStruct; +LL + use outer::MyStruct; + | + +error[E0603]: module `hidden` is private + --> $DIR/suggest-public-reexport-for-use.rs:60:18 + | +LL | use no_reexport::hidden::Secret; + | ^^^^^^ ------ struct `Secret` is not publicly re-exported + | | + | private module + | +note: the module `hidden` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:55:5 + | +LL | mod hidden { + | ^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/suggest-public-reexport-for-use.stderr b/tests/ui/imports/suggest-public-reexport-for-use.edition2018.stderr similarity index 74% rename from tests/ui/imports/suggest-public-reexport-for-use.stderr rename to tests/ui/imports/suggest-public-reexport-for-use.edition2018.stderr index 2a86e52155ed0..0295cb29ca3e2 100644 --- a/tests/ui/imports/suggest-public-reexport-for-use.stderr +++ b/tests/ui/imports/suggest-public-reexport-for-use.edition2018.stderr @@ -1,79 +1,79 @@ error[E0603]: module `inner` is private - --> $DIR/suggest-public-reexport-for-use.rs:23:12 + --> $DIR/suggest-public-reexport-for-use.rs:27:12 | LL | use outer::inner::MyStruct; | ^^^^^ private module | note: the module `inner` is defined here - --> $DIR/suggest-public-reexport-for-use.rs:12:5 + --> $DIR/suggest-public-reexport-for-use.rs:16:5 | LL | mod inner { | ^^^^^^^^^ help: consider importing this struct through its public re-export instead | LL - use outer::inner::MyStruct; -LL + use crate::outer::MyStruct; +LL + use outer::MyStruct; | error[E0603]: module `inner` is private - --> $DIR/suggest-public-reexport-for-use.rs:24:12 + --> $DIR/suggest-public-reexport-for-use.rs:28:12 | LL | use outer::inner::my_function; | ^^^^^ private module | note: the module `inner` is defined here - --> $DIR/suggest-public-reexport-for-use.rs:12:5 + --> $DIR/suggest-public-reexport-for-use.rs:16:5 | LL | mod inner { | ^^^^^^^^^ help: consider importing this function through its public re-export instead | LL - use outer::inner::my_function; -LL + use crate::outer::my_function; +LL + use outer::my_function; | error[E0603]: module `inner` is private - --> $DIR/suggest-public-reexport-for-use.rs:25:12 + --> $DIR/suggest-public-reexport-for-use.rs:29:12 | LL | use outer::inner::MyTrait; | ^^^^^ private module | note: the module `inner` is defined here - --> $DIR/suggest-public-reexport-for-use.rs:12:5 + --> $DIR/suggest-public-reexport-for-use.rs:16:5 | LL | mod inner { | ^^^^^^^^^ help: consider importing this trait through its public re-export instead | LL - use outer::inner::MyTrait; -LL + use crate::outer::MyTrait; +LL + use outer::MyTrait; | error[E0603]: module `inner` is private - --> $DIR/suggest-public-reexport-for-use.rs:26:12 + --> $DIR/suggest-public-reexport-for-use.rs:30:12 | LL | use outer::inner::MyEnum; | ^^^^^ private module | note: the module `inner` is defined here - --> $DIR/suggest-public-reexport-for-use.rs:12:5 + --> $DIR/suggest-public-reexport-for-use.rs:16:5 | LL | mod inner { | ^^^^^^^^^ help: consider importing this enum through its public re-export instead | LL - use outer::inner::MyEnum; -LL + use crate::outer::MyEnum; +LL + use outer::MyEnum; | error[E0603]: module `inner` is private - --> $DIR/suggest-public-reexport-for-use.rs:30:23 + --> $DIR/suggest-public-reexport-for-use.rs:39:23 | LL | use crate::outer::inner::MyStruct; | ^^^^^ private module | note: the module `inner` is defined here - --> $DIR/suggest-public-reexport-for-use.rs:12:5 + --> $DIR/suggest-public-reexport-for-use.rs:16:5 | LL | mod inner { | ^^^^^^^^^ @@ -84,13 +84,13 @@ LL + use crate::outer::MyStruct; | error[E0603]: module `inner` is private - --> $DIR/suggest-public-reexport-for-use.rs:36:27 + --> $DIR/suggest-public-reexport-for-use.rs:49:27 | LL | use crate::outer::inner::MyStruct; | ^^^^^ private module | note: the module `inner` is defined here - --> $DIR/suggest-public-reexport-for-use.rs:12:5 + --> $DIR/suggest-public-reexport-for-use.rs:16:5 | LL | mod inner { | ^^^^^^^^^ @@ -101,7 +101,7 @@ LL + use crate::outer::MyStruct; | error[E0603]: module `hidden` is private - --> $DIR/suggest-public-reexport-for-use.rs:47:18 + --> $DIR/suggest-public-reexport-for-use.rs:60:18 | LL | use no_reexport::hidden::Secret; | ^^^^^^ ------ struct `Secret` is not publicly re-exported @@ -109,7 +109,7 @@ LL | use no_reexport::hidden::Secret; | private module | note: the module `hidden` is defined here - --> $DIR/suggest-public-reexport-for-use.rs:42:5 + --> $DIR/suggest-public-reexport-for-use.rs:55:5 | LL | mod hidden { | ^^^^^^^^^^ diff --git a/tests/ui/imports/suggest-public-reexport-for-use.edition2021.stderr b/tests/ui/imports/suggest-public-reexport-for-use.edition2021.stderr new file mode 100644 index 0000000000000..0295cb29ca3e2 --- /dev/null +++ b/tests/ui/imports/suggest-public-reexport-for-use.edition2021.stderr @@ -0,0 +1,119 @@ +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:27:12 + | +LL | use outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use outer::inner::MyStruct; +LL + use outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:28:12 + | +LL | use outer::inner::my_function; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this function through its public re-export instead + | +LL - use outer::inner::my_function; +LL + use outer::my_function; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:29:12 + | +LL | use outer::inner::MyTrait; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this trait through its public re-export instead + | +LL - use outer::inner::MyTrait; +LL + use outer::MyTrait; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:30:12 + | +LL | use outer::inner::MyEnum; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this enum through its public re-export instead + | +LL - use outer::inner::MyEnum; +LL + use outer::MyEnum; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:39:23 + | +LL | use crate::outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use crate::outer::inner::MyStruct; +LL + use crate::outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:49:27 + | +LL | use crate::outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use crate::outer::inner::MyStruct; +LL + use crate::outer::MyStruct; + | + +error[E0603]: module `hidden` is private + --> $DIR/suggest-public-reexport-for-use.rs:60:18 + | +LL | use no_reexport::hidden::Secret; + | ^^^^^^ ------ struct `Secret` is not publicly re-exported + | | + | private module + | +note: the module `hidden` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:55:5 + | +LL | mod hidden { + | ^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/suggest-public-reexport-for-use.edition2024.stderr b/tests/ui/imports/suggest-public-reexport-for-use.edition2024.stderr new file mode 100644 index 0000000000000..0295cb29ca3e2 --- /dev/null +++ b/tests/ui/imports/suggest-public-reexport-for-use.edition2024.stderr @@ -0,0 +1,119 @@ +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:27:12 + | +LL | use outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use outer::inner::MyStruct; +LL + use outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:28:12 + | +LL | use outer::inner::my_function; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this function through its public re-export instead + | +LL - use outer::inner::my_function; +LL + use outer::my_function; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:29:12 + | +LL | use outer::inner::MyTrait; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this trait through its public re-export instead + | +LL - use outer::inner::MyTrait; +LL + use outer::MyTrait; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:30:12 + | +LL | use outer::inner::MyEnum; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this enum through its public re-export instead + | +LL - use outer::inner::MyEnum; +LL + use outer::MyEnum; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:39:23 + | +LL | use crate::outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use crate::outer::inner::MyStruct; +LL + use crate::outer::MyStruct; + | + +error[E0603]: module `inner` is private + --> $DIR/suggest-public-reexport-for-use.rs:49:27 + | +LL | use crate::outer::inner::MyStruct; + | ^^^^^ private module + | +note: the module `inner` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:16:5 + | +LL | mod inner { + | ^^^^^^^^^ +help: consider importing this struct through its public re-export instead + | +LL - use crate::outer::inner::MyStruct; +LL + use crate::outer::MyStruct; + | + +error[E0603]: module `hidden` is private + --> $DIR/suggest-public-reexport-for-use.rs:60:18 + | +LL | use no_reexport::hidden::Secret; + | ^^^^^^ ------ struct `Secret` is not publicly re-exported + | | + | private module + | +note: the module `hidden` is defined here + --> $DIR/suggest-public-reexport-for-use.rs:55:5 + | +LL | mod hidden { + | ^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0603`. diff --git a/tests/ui/imports/suggest-public-reexport-for-use.rs b/tests/ui/imports/suggest-public-reexport-for-use.rs index 161d257510d26..1658735e36cf9 100644 --- a/tests/ui/imports/suggest-public-reexport-for-use.rs +++ b/tests/ui/imports/suggest-public-reexport-for-use.rs @@ -1,4 +1,8 @@ -//@ edition:2021 +//@ revisions: edition2015 edition2018 edition2021 edition2024 +//@ [edition2015] edition:2015 +//@ [edition2018] edition:2018 +//@ [edition2021] edition:2021 +//@ [edition2024] edition:2024 // When a `use` statement accesses an item through a private module, // the compiler should suggest a public re-export if one exists. @@ -25,15 +29,24 @@ use outer::inner::my_function; //~ ERROR module `inner` is private use outer::inner::MyTrait; //~ ERROR module `inner` is private use outer::inner::MyEnum; //~ ERROR module `inner` is private -// From a sibling module, the suggestion should use `super::`. +// From a sibling module, the suggestion should keep the full path +// (shortening to `super::` would not reduce the segment count here). mod sibling { - use crate::outer::inner::MyStruct; //~ ERROR module `inner` is private + #[cfg(edition2015)] + use outer::inner::MyStruct; //[edition2015]~ ERROR module `inner` is private + + #[cfg(not(edition2015))] + use crate::outer::inner::MyStruct; //[edition2018,edition2021,edition2024]~ ERROR module `inner` is private } // From a deeply nested module, the suggestion should keep the full path. mod deep { mod nested { - use crate::outer::inner::MyStruct; //~ ERROR module `inner` is private + #[cfg(edition2015)] + use outer::inner::MyStruct; //[edition2015]~ ERROR module `inner` is private + + #[cfg(not(edition2015))] + use crate::outer::inner::MyStruct; //[edition2018,edition2021,edition2024]~ ERROR module `inner` is private } } From d09d4c999dc0796b820dcdb4d77eb1e10343a18d Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Mon, 30 Mar 2026 23:19:55 +0100 Subject: [PATCH 05/68] Update the expected results for two other tests --- tests/ui/shadowed/shadowed-use-visibility.stderr | 10 ++++++++++ tests/ui/use/use-mod/use-mod-3.stderr | 2 ++ 2 files changed, 12 insertions(+) diff --git a/tests/ui/shadowed/shadowed-use-visibility.stderr b/tests/ui/shadowed/shadowed-use-visibility.stderr index b062341dc8be8..bbec8380fd032 100644 --- a/tests/ui/shadowed/shadowed-use-visibility.stderr +++ b/tests/ui/shadowed/shadowed-use-visibility.stderr @@ -14,6 +14,11 @@ note: ...and refers to the module `foo` which is defined here | LL | mod foo { | ^^^^^^^ +help: consider importing this function instead + | +LL - use crate::foo::bar::f as g; +LL + use foo::f as g; + | error[E0603]: module import `f` is private --> $DIR/shadowed-use-visibility.rs:15:10 @@ -31,6 +36,11 @@ note: ...and refers to the module `foo` which is defined here | LL | mod foo { | ^^^^^^^ +help: consider importing this function through its public re-export instead + | +LL - use bar::f::f; +LL + use bar::f; + | error: aborting due to 2 previous errors diff --git a/tests/ui/use/use-mod/use-mod-3.stderr b/tests/ui/use/use-mod/use-mod-3.stderr index 1b12b3c6fa09a..2143d09f6b983 100644 --- a/tests/ui/use/use-mod/use-mod-3.stderr +++ b/tests/ui/use/use-mod/use-mod-3.stderr @@ -15,6 +15,8 @@ error[E0603]: module `bar` is private | LL | use foo::bar::{ | ^^^ private module +LL | Bar + | --- type alias `Bar` is not publicly re-exported | note: the module `bar` is defined here --> $DIR/use-mod-3.rs:9:5 From 526f3d9bbe8927f9862a23f45c3d06aa3761b89b Mon Sep 17 00:00:00 2001 From: ujjwalVishwakarma2006 <2023ucs0116@iitjammu.ac.in> Date: Sun, 19 Apr 2026 23:44:20 +0530 Subject: [PATCH 06/68] Move test files from issues/ to appropriate subdirectories --- .../dont-allow-inline-and-repr-at-invalid-positions.rs} | 0 .../dont-allow-inline-and-repr-at-invalid-positions.stderr} | 0 .../issue-33202.rs => attributes/repr-on-single-variant-Enum.rs} | 0 .../foreign-fn-with-more-than-8-byte-arg-size.rs} | 0 .../recursive-struct-with-raw-pointer-field.rs} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-31769.rs => attributes/dont-allow-inline-and-repr-at-invalid-positions.rs} (100%) rename tests/ui/{issues/issue-31769.stderr => attributes/dont-allow-inline-and-repr-at-invalid-positions.stderr} (100%) rename tests/ui/{issues/issue-33202.rs => attributes/repr-on-single-variant-Enum.rs} (100%) rename tests/ui/{issues/issue-38763.rs => foreign/foreign-fn-with-more-than-8-byte-arg-size.rs} (100%) rename tests/ui/{issues/issue-19001.rs => recursion/recursive-struct-with-raw-pointer-field.rs} (100%) diff --git a/tests/ui/issues/issue-31769.rs b/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.rs similarity index 100% rename from tests/ui/issues/issue-31769.rs rename to tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.rs diff --git a/tests/ui/issues/issue-31769.stderr b/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.stderr similarity index 100% rename from tests/ui/issues/issue-31769.stderr rename to tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.stderr diff --git a/tests/ui/issues/issue-33202.rs b/tests/ui/attributes/repr-on-single-variant-Enum.rs similarity index 100% rename from tests/ui/issues/issue-33202.rs rename to tests/ui/attributes/repr-on-single-variant-Enum.rs diff --git a/tests/ui/issues/issue-38763.rs b/tests/ui/foreign/foreign-fn-with-more-than-8-byte-arg-size.rs similarity index 100% rename from tests/ui/issues/issue-38763.rs rename to tests/ui/foreign/foreign-fn-with-more-than-8-byte-arg-size.rs diff --git a/tests/ui/issues/issue-19001.rs b/tests/ui/recursion/recursive-struct-with-raw-pointer-field.rs similarity index 100% rename from tests/ui/issues/issue-19001.rs rename to tests/ui/recursion/recursive-struct-with-raw-pointer-field.rs From c4f6148fd9556c46ad7cc490e0b204c4dfb98c73 Mon Sep 17 00:00:00 2001 From: ujjwalVishwakarma2006 <2023ucs0116@iitjammu.ac.in> Date: Sun, 19 Apr 2026 23:51:38 +0530 Subject: [PATCH 07/68] Add issue links --- .../dont-allow-inline-and-repr-at-invalid-positions.rs | 1 + .../dont-allow-inline-and-repr-at-invalid-positions.stderr | 4 ++-- tests/ui/attributes/repr-on-single-variant-Enum.rs | 1 + tests/ui/foreign/foreign-fn-with-more-than-8-byte-arg-size.rs | 1 + tests/ui/recursion/recursive-struct-with-raw-pointer-field.rs | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.rs b/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.rs index 354c1be9ed554..f295ecf302598 100644 --- a/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.rs +++ b/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.rs @@ -1,3 +1,4 @@ +//! Regression test for fn main() { #[inline] struct Foo; //~ ERROR attribute cannot be used on #[repr(C)] fn foo() {} //~ ERROR attribute should be applied to a struct, enum, or union diff --git a/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.stderr b/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.stderr index 0f75e84f2a704..fc8e22f171c20 100644 --- a/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.stderr +++ b/tests/ui/attributes/dont-allow-inline-and-repr-at-invalid-positions.stderr @@ -1,5 +1,5 @@ error: `#[inline]` attribute cannot be used on structs - --> $DIR/issue-31769.rs:2:5 + --> $DIR/dont-allow-inline-and-repr-at-invalid-positions.rs:3:5 | LL | #[inline] struct Foo; | ^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[inline] struct Foo; = help: `#[inline]` can only be applied to functions error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-31769.rs:3:12 + --> $DIR/dont-allow-inline-and-repr-at-invalid-positions.rs:4:12 | LL | #[repr(C)] fn foo() {} | ^ ----------- not a struct, enum, or union diff --git a/tests/ui/attributes/repr-on-single-variant-Enum.rs b/tests/ui/attributes/repr-on-single-variant-Enum.rs index 3fef98606afab..ab45c76ef5aae 100644 --- a/tests/ui/attributes/repr-on-single-variant-Enum.rs +++ b/tests/ui/attributes/repr-on-single-variant-Enum.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #[repr(C)] pub enum CPOption { diff --git a/tests/ui/foreign/foreign-fn-with-more-than-8-byte-arg-size.rs b/tests/ui/foreign/foreign-fn-with-more-than-8-byte-arg-size.rs index 87c758db1723c..4bd78a423cd2c 100644 --- a/tests/ui/foreign/foreign-fn-with-more-than-8-byte-arg-size.rs +++ b/tests/ui/foreign/foreign-fn-with-more-than-8-byte-arg-size.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass //@ needs-threads diff --git a/tests/ui/recursion/recursive-struct-with-raw-pointer-field.rs b/tests/ui/recursion/recursive-struct-with-raw-pointer-field.rs index 51aebf88c95fb..97fa08fab1d93 100644 --- a/tests/ui/recursion/recursive-struct-with-raw-pointer-field.rs +++ b/tests/ui/recursion/recursive-struct-with-raw-pointer-field.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #![allow(dead_code)] // check that we handle recursive arrays correctly in `type_of` From 560a6305213d3031c94c4dd8157bc8e022bfebf0 Mon Sep 17 00:00:00 2001 From: Redddy Date: Thu, 23 Apr 2026 17:31:00 +0900 Subject: [PATCH 08/68] Update link to queries.rs in tracing.md --- src/doc/rustc-dev-guide/src/tracing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 78a0fe4af2ff2..ae819003e1f3e 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -101,7 +101,7 @@ RUSTC_LOG=[typeck{key=.*name_of_item.*}] Different queries have different arguments. You can find a list of queries and their arguments in -[`rustc_middle/src/query/mod.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_middle/src/query/mod.rs#L18). +[`rustc_middle/src/queries.rs`](https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_middle/src/queries.rs). ## Broad module level filters From 3a55fcb8c26b088b6b158456d4b63b64665de639 Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Fri, 24 Apr 2026 00:01:24 +0800 Subject: [PATCH 09/68] Clarify THIR element layout description --- src/doc/rustc-dev-guide/src/thir.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/thir.md b/src/doc/rustc-dev-guide/src/thir.md index d5f7d8b5fcb70..07eba3eecb036 100644 --- a/src/doc/rustc-dev-guide/src/thir.md +++ b/src/doc/rustc-dev-guide/src/thir.md @@ -27,9 +27,9 @@ But it has some other interesting features that distinguish it from the HIR: are made explicit, and method calls and overloaded operators are converted into plain function calls. Destruction scopes are also made explicit. -- Statements, expressions, and match arms are stored separately. For example, statements in the - `stmts` array reference expressions by their index (represented as a [`ExprId`]) in the `exprs` - array. +- Statements, expressions, match arms, blocks, and parameters are stored separately. For example, + statements in the `stmts` array reference expressions by their index (represented as a + [`ExprId`]) in the `exprs` array. [HIR]: ./hir.md [`ExprId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/thir/struct.ExprId.html From 57363548f9ed8e66430dfec0f6aa8d7d3daf4113 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 19:07:34 +0200 Subject: [PATCH 10/68] sembr src/thir.md --- src/doc/rustc-dev-guide/src/thir.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/thir.md b/src/doc/rustc-dev-guide/src/thir.md index 07eba3eecb036..802df2286095d 100644 --- a/src/doc/rustc-dev-guide/src/thir.md +++ b/src/doc/rustc-dev-guide/src/thir.md @@ -15,19 +15,23 @@ the types have been filled in, which is possible after type checking has complet But it has some other interesting features that distinguish it from the HIR: - Like the MIR, the THIR only represents bodies, i.e. "executable code"; this includes - function bodies, but also `const` initializers, for example. Specifically, all [body owners] have - THIR created. Consequently, the THIR has no representation for items like `struct`s or `trait`s. + function bodies, but also `const` initializers, for example. + Specifically, all [body owners] have THIR created. + Consequently, the THIR has no representation for items like `struct`s or `trait`s. - Each body of THIR is only stored temporarily and is dropped as soon as it's no longer needed, as opposed to being stored until the end of the compilation process (which is what is done with the HIR). - Besides making the types of all nodes available, the THIR also has additional - desugaring compared to the HIR. For example, automatic references and dereferences + desugaring compared to the HIR. + For example, automatic references and dereferences are made explicit, and method calls and overloaded operators are converted into - plain function calls. Destruction scopes are also made explicit. + plain function calls. + Destruction scopes are also made explicit. -- Statements, expressions, match arms, blocks, and parameters are stored separately. For example, +- Statements, expressions, match arms, blocks, and parameters are stored separately. + For example, statements in the `stmts` array reference expressions by their index (represented as a [`ExprId`]) in the `exprs` array. @@ -35,10 +39,13 @@ But it has some other interesting features that distinguish it from the HIR: [`ExprId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/thir/struct.ExprId.html [body owners]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.BodyOwnerKind.html -The THIR lives in [`rustc_mir_build::thir`][thir-docs]. To construct a [`thir::Expr`], +The THIR lives in [`rustc_mir_build::thir`][thir-docs]. +To construct a [`thir::Expr`], you can use the [`thir_body`] function, passing in the memory arena where the THIR -will be allocated. Dropping this arena will result in the THIR being destroyed, -which is useful to keep peak memory in check. Having a THIR representation of +will be allocated. +Dropping this arena will result in the THIR being destroyed, +which is useful to keep peak memory in check. +Having a THIR representation of all bodies of a crate in memory at the same time would be very heavy. You can get a debug representation of the THIR by passing the `-Zunpretty=thir-tree` flag From 8679accf662796b7dea9faea118222162ce0f2e8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 19:17:38 +0200 Subject: [PATCH 11/68] sembr src/name-resolution.md --- src/doc/rustc-dev-guide/src/name-resolution.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/name-resolution.md b/src/doc/rustc-dev-guide/src/name-resolution.md index 79d0897b4279e..2723207a96688 100644 --- a/src/doc/rustc-dev-guide/src/name-resolution.md +++ b/src/doc/rustc-dev-guide/src/name-resolution.md @@ -62,8 +62,7 @@ files and expanding `macros`. This phase produces links from all the names in the source to relevant places where the name was introduced. It also generates helpful error messages, -like typo suggestions, traits to import or lints about -unused items. +like typo suggestions, traits to import or lints about unused items. A successful run of the second phase ([`Resolver::resolve_crate`]) creates kind of an index the rest of the compilation may use to ask about the present names From cd434216e03b4018753eda4453cb7ed28681b5e6 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 19:18:17 +0200 Subject: [PATCH 12/68] missing pause --- src/doc/rustc-dev-guide/src/name-resolution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/name-resolution.md b/src/doc/rustc-dev-guide/src/name-resolution.md index 2723207a96688..83d2996420507 100644 --- a/src/doc/rustc-dev-guide/src/name-resolution.md +++ b/src/doc/rustc-dev-guide/src/name-resolution.md @@ -62,7 +62,7 @@ files and expanding `macros`. This phase produces links from all the names in the source to relevant places where the name was introduced. It also generates helpful error messages, -like typo suggestions, traits to import or lints about unused items. +like typo suggestions, traits to import, or lints about unused items. A successful run of the second phase ([`Resolver::resolve_crate`]) creates kind of an index the rest of the compilation may use to ask about the present names From 4bcdedb24c61e1933389b55479112040044efc65 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 19:19:16 +0200 Subject: [PATCH 13/68] sembr src/building/suggested.md --- src/doc/rustc-dev-guide/src/building/suggested.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index d9921cbb133ea..c8b25edc1e532 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -154,9 +154,10 @@ For Neovim users, there are a few options: #### neoconf.nvim [neoconf.nvim][neoconf.nvim] allows for project-local configuration -files with the native LSP. The steps for how to use it are below. Note that they require -rust-analyzer to already be configured with Neovim. Steps for this can be -[found here][r-a nvim lsp]. +files with the native LSP. +The steps for how to use it are below. +Note that they require rust-analyzer to already be configured with Neovim. +Steps for this can be [found here][r-a nvim lsp]. 1. First install the plugin. This can be done by following the steps in the README. From 2fa6f286eb8ea5cd997da62b304b36bda600b63c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 19:20:02 +0200 Subject: [PATCH 14/68] sembr src/building/prerequisites.md --- .../src/building/prerequisites.md | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/prerequisites.md b/src/doc/rustc-dev-guide/src/building/prerequisites.md index d984cc4e391d4..74e93d78c0cff 100644 --- a/src/doc/rustc-dev-guide/src/building/prerequisites.md +++ b/src/doc/rustc-dev-guide/src/building/prerequisites.md @@ -6,35 +6,44 @@ See [the `rust-lang/rust` INSTALL](https://github.com/rust-lang/rust/blob/HEAD/I ## Hardware -You will need an internet connection to build. The bootstrapping process -involves updating git submodules and downloading a beta compiler. It doesn't -need to be super fast, but that can help. +You will need an internet connection to build. +The bootstrapping process +involves updating git submodules and downloading a beta compiler. +It doesn't need to be super fast, but that can help. There are no strict hardware requirements, but building the compiler is computationally expensive, so a beefier machine will help, and I wouldn't -recommend trying to build on a Raspberry Pi! We recommend the following. -* 30GB+ of free disk space. Otherwise, you will have to keep - clearing incremental caches. More space is better, the compiler is a bit of a +recommend trying to build on a Raspberry Pi! +We recommend the following. +* 30GB+ of free disk space. + Otherwise, you will have to keep clearing incremental caches. + More space is better, the compiler is a bit of a hog; it's a problem we are aware of. * 8GB+ RAM -* 2+ cores. Having more cores really helps. 10 or 20 or more is not too many! +* 2+ cores. + Having more cores really helps. + 10 or 20 or more is not too many! -Beefier machines will lead to much faster builds. If your machine is not very +Beefier machines will lead to much faster builds. +If your machine is not very powerful, a common strategy is to only use `./x check` on your local machine and let the CI build test your changes when you push to a PR branch. Building the compiler takes more than half an hour on my moderately powerful -laptop. We suggest downloading LLVM from CI so you don't have to build it from source +laptop. +We suggest downloading LLVM from CI so you don't have to build it from source ([see here][config]). -Like `cargo`, the build system will use as many cores as possible. Sometimes -this can cause you to run low on memory. You can use `-j` to adjust the number -of concurrent jobs. If a full build takes more than ~45 minutes to an hour, you +Like `cargo`, the build system will use as many cores as possible. +Sometimes this can cause you to run low on memory. +You can use `-j` to adjust the number of concurrent jobs. +If a full build takes more than ~45 minutes to an hour, you are probably spending most of the time swapping memory in and out; try using `-j1`. If you don't have too much free disk space, you may want to turn off -incremental compilation ([see here][config]). This will make compilation take +incremental compilation ([see here][config]). +This will make compilation take longer (especially after a rebase), but will save a ton of space from the incremental caches. From f7f28861104b94970d0cce68180e164768de4032 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 19:29:21 +0200 Subject: [PATCH 15/68] reflow --- src/doc/rustc-dev-guide/src/building/prerequisites.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/prerequisites.md b/src/doc/rustc-dev-guide/src/building/prerequisites.md index 74e93d78c0cff..0613a931bf8df 100644 --- a/src/doc/rustc-dev-guide/src/building/prerequisites.md +++ b/src/doc/rustc-dev-guide/src/building/prerequisites.md @@ -25,8 +25,8 @@ We recommend the following. 10 or 20 or more is not too many! Beefier machines will lead to much faster builds. -If your machine is not very -powerful, a common strategy is to only use `./x check` on your local machine +If your machine is not very powerful, +a common strategy is to only use `./x check` on your local machine and let the CI build test your changes when you push to a PR branch. Building the compiler takes more than half an hour on my moderately powerful From 364e9ebf11ea1ea3cb6f0fc9107f64981537cbd1 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 19:30:57 +0200 Subject: [PATCH 16/68] whose laptop --- src/doc/rustc-dev-guide/src/building/prerequisites.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/prerequisites.md b/src/doc/rustc-dev-guide/src/building/prerequisites.md index 0613a931bf8df..4833d34412022 100644 --- a/src/doc/rustc-dev-guide/src/building/prerequisites.md +++ b/src/doc/rustc-dev-guide/src/building/prerequisites.md @@ -29,7 +29,7 @@ If your machine is not very powerful, a common strategy is to only use `./x check` on your local machine and let the CI build test your changes when you push to a PR branch. -Building the compiler takes more than half an hour on my moderately powerful +Building the compiler takes more than half an hour on a moderately powerful laptop. We suggest downloading LLVM from CI so you don't have to build it from source ([see here][config]). From 4ee98f7dd2e08167c86fd698034c70c4628f70f5 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 20:19:00 +0200 Subject: [PATCH 17/68] sembr src/mir/construction.md --- .../rustc-dev-guide/src/mir/construction.md | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/construction.md b/src/doc/rustc-dev-guide/src/mir/construction.md index 8360d9ff1a8bc..c85de0240d7ee 100644 --- a/src/doc/rustc-dev-guide/src/mir/construction.md +++ b/src/doc/rustc-dev-guide/src/mir/construction.md @@ -11,13 +11,15 @@ list of items: * Drop code (the `Drop::drop` function is not called directly) * Drop implementations of types without an explicit `Drop` implementation -The lowering is triggered by calling the [`mir_built`] query. The MIR builder does +The lowering is triggered by calling the [`mir_built`] query. +The MIR builder does not actually use the HIR but operates on the [THIR] instead, processing THIR expressions recursively. The lowering creates local variables for every argument as specified in the signature. Next, it creates local variables for every binding specified (e.g. `(a, b): (i32, String)`) -produces 3 bindings, one for the argument, and two for the bindings. Next, it generates +produces 3 bindings, one for the argument, and two for the bindings. +Next, it generates field accesses that read the fields from the argument and writes the value to the binding variable. @@ -52,7 +54,8 @@ fn generate_more_mir(&mut self, block: BasicBlock) -> BlockAnd { ``` When you invoke these functions, it is common to have a local variable `block` -that is effectively a "cursor". It represents the point at which we are adding new MIR. +that is effectively a "cursor". +It represents the point at which we are adding new MIR. When you invoke `generate_more_mir`, you want to update this cursor. You can do this manually, but it's tedious: @@ -89,10 +92,13 @@ representations: We start out with lowering the function body to an `Rvalue` so we can create an assignment to `RETURN_PLACE`, This `Rvalue` lowering will in turn trigger lowering to -`Operand` for its arguments (if any). `Operand` lowering either produces a `const` -operand, or moves/copies out of a `Place`, thus triggering a `Place` lowering. An +`Operand` for its arguments (if any). +`Operand` lowering either produces a `const` +operand, or moves/copies out of a `Place`, thus triggering a `Place` lowering. +An expression being lowered to a `Place` can in turn trigger a temporary to be created -if the expression being lowered contains operations. This is where the snake bites its +if the expression being lowered contains operations. +This is where the snake bites its own tail and we need to trigger an `Rvalue` lowering for the expression to be written into the local. @@ -100,7 +106,8 @@ into the local. Operators on builtin types are not lowered to function calls (which would end up being infinite recursion calls, because the trait impls just contain the operation itself -again). Instead there are `Rvalue`s for binary and unary operators and index operations. +again). +Instead there are `Rvalue`s for binary and unary operators and index operations. These `Rvalue`s later get codegened to llvm primitive operations or llvm intrinsics. Operators on all other types get lowered to a function call to their `impl` of the @@ -118,7 +125,8 @@ In [MIR] there is no difference between method calls and function calls anymore. ## Conditions `if` conditions and `match` statements for `enum`s with variants that have no fields are -lowered to `TerminatorKind::SwitchInt`. Each possible value (so `0` and `1` for `if` +lowered to `TerminatorKind::SwitchInt`. +Each possible value (so `0` and `1` for `if` conditions) has a corresponding `BasicBlock` to which the code continues. The argument being branched on is (again) an `Operand` representing the value of the if condition. @@ -127,14 +135,14 @@ the if condition. `match` statements for `enum`s with variants that have fields are lowered to `TerminatorKind::SwitchInt`, too, but the `Operand` refers to a `Place` where the -discriminant of the value can be found. This often involves reading the discriminant -to a new temporary variable. +discriminant of the value can be found. +This often involves reading the discriminant to a new temporary variable. ## Aggregate construction Aggregate values of any kind (e.g. structs or tuples) are built via `Rvalue::Aggregate`. -All fields are -lowered to `Operator`s. This is essentially equivalent to one assignment +All fields are lowered to `Operator`s. +This is essentially equivalent to one assignment statement per aggregate field plus an assignment to the discriminant in the case of `enum`s. From 64e99647aa07619ee5edb5aa296d6480282025ba Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 23 Apr 2026 20:22:46 +0200 Subject: [PATCH 18/68] reflow --- .../rustc-dev-guide/src/mir/construction.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/construction.md b/src/doc/rustc-dev-guide/src/mir/construction.md index c85de0240d7ee..b9b5f0a346416 100644 --- a/src/doc/rustc-dev-guide/src/mir/construction.md +++ b/src/doc/rustc-dev-guide/src/mir/construction.md @@ -12,16 +12,16 @@ list of items: * Drop implementations of types without an explicit `Drop` implementation The lowering is triggered by calling the [`mir_built`] query. -The MIR builder does -not actually use the HIR but operates on the [THIR] instead, processing THIR -expressions recursively. +The MIR builder does not actually use the HIR, +but operates on the [THIR] instead, +processing THIR expressions recursively. The lowering creates local variables for every argument as specified in the signature. Next, it creates local variables for every binding specified (e.g. `(a, b): (i32, String)`) produces 3 bindings, one for the argument, and two for the bindings. -Next, it generates -field accesses that read the fields from the argument and writes the value to the binding -variable. +Next, +it generates field accesses that read the fields from the argument, +and writes the value to the binding variable. With this initialization out of the way, the lowering triggers a recursive call to a function that generates the MIR for the body (a `Block` expression) and @@ -93,10 +93,9 @@ representations: We start out with lowering the function body to an `Rvalue` so we can create an assignment to `RETURN_PLACE`, This `Rvalue` lowering will in turn trigger lowering to `Operand` for its arguments (if any). -`Operand` lowering either produces a `const` -operand, or moves/copies out of a `Place`, thus triggering a `Place` lowering. -An -expression being lowered to a `Place` can in turn trigger a temporary to be created +`Operand` lowering either produces a `const` operand, +or moves/copies out of a `Place`, thus triggering a `Place` lowering. +An expression being lowered to a `Place` can in turn trigger a temporary to be created if the expression being lowered contains operations. This is where the snake bites its own tail and we need to trigger an `Rvalue` lowering for the expression to be written From 2d1be677c27c9508bf19b553b5e44bf2211c5f02 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Fri, 24 Apr 2026 07:52:36 +0000 Subject: [PATCH 19/68] Prepare for merging from rust-lang/rust This updates the rust-version file to d493b7c5ac637ed7edf626128b9f14796a2dad20. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 1175e3372ab63..702f1a796ea3e 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -cf1817bc6ecd2d14ca492247c804bad31948dd56 +d493b7c5ac637ed7edf626128b9f14796a2dad20 From f365db21b6b8862bb21679cb40537b515f95ac9e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 24 Apr 2026 09:55:45 +0200 Subject: [PATCH 20/68] sembr src/offload/installation.md --- src/doc/rustc-dev-guide/src/offload/installation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index ac852e01f313f..bf4070caf4af9 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -1,6 +1,7 @@ # Installation -`std::offload` is partly available in nightly builds for users. For now, everyone however still needs to build rustc from source to use all features of it. +`std::offload` is partly available in nightly builds for users. +For now, everyone however still needs to build rustc from source to use all features of it. ## Build instructions From 01918c03af19e5d8c836d86c2068d2e96715a6d1 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 24 Apr 2026 10:00:40 +0200 Subject: [PATCH 21/68] use a more correct code marker --- .../src/autodiff/installation.md | 44 +++++++++---------- .../src/offload/installation.md | 10 ++--- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index 6b66a9dcb2d5f..2ea725e0df285 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -1,11 +1,11 @@ # Installation -In the near future, `std::autodiff` should become available for users via rustup. As a rustc/enzyme/autodiff contributor however, you will still need to build rustc from source. -For the meantime, you can download up-to-date builds to enable `std::autodiff` on your latest nightly toolchain, if you are using either of: -**Linux**, with `x86_64-unknown-linux-gnu` or `aarch64-unknown-linux-gnu` -**Windows**, with `x86_64-llvm-mingw` or `aarch64-llvm-mingw` +In the near future, `std::autodiff` should become available for users via rustup. As a rustc/enzyme/autodiff contributor however, you will still need to build rustc from source. +For the meantime, you can download up-to-date builds to enable `std::autodiff` on your latest nightly toolchain, if you are using either of: +**Linux**, with `x86_64-unknown-linux-gnu` or `aarch64-unknown-linux-gnu` +**Windows**, with `x86_64-llvm-mingw` or `aarch64-llvm-mingw` -You can also download slightly outdated builds for **Apple** (aarch64-apple), which should generally work for now. +You can also download slightly outdated builds for **Apple** (aarch64-apple), which should generally work for now. If you need any other platform, you can build rustc including autodiff from source. Please open an issue if you want to help enabling automatic builds for your prefered target. @@ -15,8 +15,8 @@ If you want to use `std::autodiff` and don't plan to contribute PR's to the proj For now, you'll have to manually download and copy it. 1) On our github repository, find the last merged PR: [`Repo`] -2) Scroll down to the lower end of the PR, where you'll find a rust-bors message saying `Test successful` with a `CI` link. -3) Click on the `CI` link, and grep for your target. E.g. `dist-x86_64-linux` or `dist-aarch64-llvm-mingw` and click `Load summary`. +2) Scroll down to the lower end of the PR, where you'll find a rust-bors message saying `Test successful` with a `CI` link. +3) Click on the `CI` link, and grep for your target. E.g. `dist-x86_64-linux` or `dist-aarch64-llvm-mingw` and click `Load summary`. 4) Under the `CI artifacts` section, find the `enzyme-nightly` artifact, download, and unpack it. 5) Copy the artifact (libEnzyme-22.so for linux, libEnzyme-22.dylib for apple, etc.), which should be in a folder named `enzyme-preview`, to your rust toolchain directory. E.g. for linux: `cp ~/Downloads/enzyme-nightly-x86_64-unknown-linux-gnu/enzyme-preview/lib/rustlib/x86_64-unknown-linux-gnu/lib/libEnzyme-22.so ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib` @@ -32,7 +32,7 @@ In that case you might use the following nix configuration to get a rustc that s url = "https://ci-artifacts.rust-lang.org/rustc-builds/ec818fda361ca216eb186f5cf45131bd9c776bb4/enzyme-nightly-x86_64-unknown-linux-gnu.tar.xz"; sha256 = "sha256-Rnrop44vzS+qmYNaRoMNNMFyAc3YsMnwdNGYMXpZ5VY="; }; - + rustToolchain = pkgs.symlinkJoin { name = "rust-with-enzyme"; paths = [pkgs.rust-bin.nightly.latest.default]; @@ -49,26 +49,26 @@ In that case you might use the following nix configuration to get a rustc that s ## Build instructions First you need to clone and configure the Rust repository. Based on your preferences, you might also want to `--enable-clang` or `--enable-lld`. -```bash +```console git clone git@github.com:rust-lang/rust cd rust ./configure --release-channel=nightly --enable-llvm-enzyme --enable-llvm-link-shared --enable-llvm-assertions --enable-ninja --enable-option-checking --disable-docs --set llvm.download-ci-llvm=false ``` Afterwards you can build rustc using: -```bash +```console ./x build --stage 1 library ``` Afterwards rustc toolchain link will allow you to use it through cargo: -``` +```console rustup toolchain link enzyme build/host/stage1 rustup toolchain install nightly # enables -Z unstable-options ``` You can then run our test cases: -```bash +```console ./x test --stage 1 tests/codegen-llvm/autodiff ./x test --stage 1 tests/pretty/autodiff ./x test --stage 1 tests/ui/autodiff @@ -76,27 +76,27 @@ You can then run our test cases: ./x test --stage 1 tests/ui/feature-gates/feature-gate-autodiff.rs ``` -Autodiff is still experimental, so if you want to use it in your own projects, you will need to add `lto="fat"` to your Cargo.toml -and use `RUSTFLAGS="-Zautodiff=Enable" cargo +enzyme` instead of `cargo` or `cargo +nightly`. +Autodiff is still experimental, so if you want to use it in your own projects, you will need to add `lto="fat"` to your Cargo.toml +and use `RUSTFLAGS="-Zautodiff=Enable" cargo +enzyme` instead of `cargo` or `cargo +nightly`. ## Compiler Explorer and dist builds Our compiler explorer instance can be updated to a newer rustc in a similar way. First, prepare a docker instance. -```bash +```console docker run -it ubuntu:22.04 export CC=clang CXX=clang++ apt update -apt install wget vim python3 git curl libssl-dev pkg-config lld ninja-build cmake clang build-essential +apt install wget vim python3 git curl libssl-dev pkg-config lld ninja-build cmake clang build-essential ``` Then build rustc in a slightly altered way: -```bash +```console git clone https://github.com/rust-lang/rust cd rust ./configure --release-channel=nightly --enable-llvm-enzyme --enable-llvm-link-shared --enable-llvm-assertions --enable-ninja --enable-option-checking --disable-docs --set llvm.download-ci-llvm=false ./x dist ``` We then copy the tarball to our host. The dockerid is the newest entry under `docker ps -a`. -```bash +```console docker cp :/rust/build/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz rust-nightly-x86_64-unknown-linux-gnu.tar.gz ``` Afterwards we can create a new (pre-release) tag on the EnzymeAD/rust repository and make a PR against the EnzymeAD/enzyme-explorer repository to update the tag. @@ -110,7 +110,7 @@ Following the Rust build instruction above will build LLVMEnzyme, LLDEnzyme, and We recommend that approach, if you just want to use any of them and have no experience with cmake. However, if you prefer to just build Enzyme without Rust, then these instructions might help. -```bash +```console git clone git@github.com:llvm/llvm-project cd llvm-project mkdir build @@ -121,11 +121,11 @@ ninja install ``` This gives you a working LLVM build, now we can continue with building Enzyme. Leave the `llvm-project` folder, and execute the following commands: -```bash +```console git clone git@github.com:EnzymeAD/Enzyme cd Enzyme/enzyme -mkdir build -cd build +mkdir build +cd build cmake .. -G Ninja -DLLVM_DIR=/llvm-project/build/lib/cmake/llvm/ -DLLVM_EXTERNAL_LIT=/llvm-project/llvm/utils/lit/lit.py -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=YES -DBUILD_SHARED_LIBS=ON ninja ``` diff --git a/src/doc/rustc-dev-guide/src/offload/installation.md b/src/doc/rustc-dev-guide/src/offload/installation.md index bf4070caf4af9..00233d9b02f8f 100644 --- a/src/doc/rustc-dev-guide/src/offload/installation.md +++ b/src/doc/rustc-dev-guide/src/offload/installation.md @@ -6,19 +6,19 @@ For now, everyone however still needs to build rustc from source to use all feat ## Build instructions First you need to clone and configure the Rust repository: -```bash +```console git clone git@github.com:rust-lang/rust cd rust ./configure --enable-llvm-link-shared --release-channel=nightly --enable-llvm-assertions --enable-llvm-offload --enable-llvm-enzyme --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs ``` Afterwards you can build rustc using: -```bash +```console ./x build --stage 1 library ``` Afterwards rustc toolchain link will allow you to use it through cargo: -``` +```console rustup toolchain link offload build/host/stage1 rustup toolchain install nightly # enables -Z unstable-options ``` @@ -26,7 +26,7 @@ rustup toolchain install nightly # enables -Z unstable-options ## Build instruction for LLVM itself -```bash +```console git clone git@github.com:llvm/llvm-project cd llvm-project mkdir build @@ -40,6 +40,6 @@ This gives you a working LLVM build. ## Testing run -``` +```console ./x test --stage 1 tests/codegen-llvm/gpu_offload ``` From 1f56bdefa05a337456d3d3580922673c9a8d997f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 24 Apr 2026 10:03:53 +0200 Subject: [PATCH 22/68] sembr src/autodiff/installation.md --- .../src/autodiff/installation.md | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index 2ea725e0df285..648e6c0ccef6f 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -1,17 +1,20 @@ # Installation -In the near future, `std::autodiff` should become available for users via rustup. As a rustc/enzyme/autodiff contributor however, you will still need to build rustc from source. +In the near future, `std::autodiff` should become available for users via rustup. +As a rustc/enzyme/autodiff contributor however, you will still need to build rustc from source. For the meantime, you can download up-to-date builds to enable `std::autodiff` on your latest nightly toolchain, if you are using either of: **Linux**, with `x86_64-unknown-linux-gnu` or `aarch64-unknown-linux-gnu` **Windows**, with `x86_64-llvm-mingw` or `aarch64-llvm-mingw` You can also download slightly outdated builds for **Apple** (aarch64-apple), which should generally work for now. -If you need any other platform, you can build rustc including autodiff from source. Please open an issue if you want to help enabling automatic builds for your prefered target. +If you need any other platform, you can build rustc including autodiff from source. +Please open an issue if you want to help enabling automatic builds for your prefered target. ## Installation guide -If you want to use `std::autodiff` and don't plan to contribute PR's to the project, then we recommend to just use your existing nightly installation and download the missing component. In the future, rustup will be able to do it for you. +If you want to use `std::autodiff` and don't plan to contribute PR's to the project, then we recommend to just use your existing nightly installation and download the missing component. +In the future, rustup will be able to do it for you. For now, you'll have to manually download and copy it. 1) On our github repository, find the last merged PR: [`Repo`] @@ -20,11 +23,14 @@ For now, you'll have to manually download and copy it. 4) Under the `CI artifacts` section, find the `enzyme-nightly` artifact, download, and unpack it. 5) Copy the artifact (libEnzyme-22.so for linux, libEnzyme-22.dylib for apple, etc.), which should be in a folder named `enzyme-preview`, to your rust toolchain directory. E.g. for linux: `cp ~/Downloads/enzyme-nightly-x86_64-unknown-linux-gnu/enzyme-preview/lib/rustlib/x86_64-unknown-linux-gnu/lib/libEnzyme-22.so ~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib` -Apple support was temporarily reverted, due to downstream breakages. If you want to download autodiff for apple, please look at the artifacts from this [`run`]. +Apple support was temporarily reverted, due to downstream breakages. +If you want to download autodiff for apple, please look at the artifacts from this [`run`]. ## Installation guide for Nix user. -This setup was recommended by a nix and autodiff user. It uses [`Overlay`]. Please verify for yourself if you are comfortable using that repository. +This setup was recommended by a nix and autodiff user. +It uses [`Overlay`]. +Please verify for yourself if you are comfortable using that repository. In that case you might use the following nix configuration to get a rustc that supports `std::autodiff`. ```nix { @@ -48,7 +54,8 @@ In that case you might use the following nix configuration to get a rustc that s ## Build instructions -First you need to clone and configure the Rust repository. Based on your preferences, you might also want to `--enable-clang` or `--enable-lld`. +First you need to clone and configure the Rust repository. +Based on your preferences, you might also want to `--enable-clang` or `--enable-lld`. ```console git clone git@github.com:rust-lang/rust cd rust @@ -81,7 +88,8 @@ and use `RUSTFLAGS="-Zautodiff=Enable" cargo +enzyme` instead of `cargo` or `car ## Compiler Explorer and dist builds -Our compiler explorer instance can be updated to a newer rustc in a similar way. First, prepare a docker instance. +Our compiler explorer instance can be updated to a newer rustc in a similar way. +First, prepare a docker instance. ```console docker run -it ubuntu:22.04 export CC=clang CXX=clang++ @@ -95,12 +103,15 @@ cd rust ./configure --release-channel=nightly --enable-llvm-enzyme --enable-llvm-link-shared --enable-llvm-assertions --enable-ninja --enable-option-checking --disable-docs --set llvm.download-ci-llvm=false ./x dist ``` -We then copy the tarball to our host. The dockerid is the newest entry under `docker ps -a`. +We then copy the tarball to our host. +The dockerid is the newest entry under `docker ps -a`. ```console docker cp :/rust/build/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz rust-nightly-x86_64-unknown-linux-gnu.tar.gz ``` Afterwards we can create a new (pre-release) tag on the EnzymeAD/rust repository and make a PR against the EnzymeAD/enzyme-explorer repository to update the tag. -Remember to ping `tgymnich` on the PR to run his update script. Note: We should archive EnzymeAD/rust and update the instructions here. The explorer should soon +Remember to ping `tgymnich` on the PR to run his update script. +Note: We should archive EnzymeAD/rust and update the instructions here. +The explorer should soon be able to get the rustc toolchain from the official rust servers. @@ -129,7 +140,8 @@ cd build cmake .. -G Ninja -DLLVM_DIR=/llvm-project/build/lib/cmake/llvm/ -DLLVM_EXTERNAL_LIT=/llvm-project/llvm/utils/lit/lit.py -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=YES -DBUILD_SHARED_LIBS=ON ninja ``` -This will build Enzyme, and you can find it in `Enzyme/enzyme/build/lib/Enzyme.so`. (Endings might differ based on your OS). +This will build Enzyme, and you can find it in `Enzyme/enzyme/build/lib/Enzyme.so`. +(Endings might differ based on your OS). [`Repo`]: https://github.com/rust-lang/rust/ [`run`]: https://github.com/rust-lang/rust/pull/153026#issuecomment-3950046599 From 04a5aa693224b3375c778b7c64cf134d276ff8fa Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 24 Apr 2026 10:04:43 +0200 Subject: [PATCH 23/68] sembr src/offload/internals.md --- .../rustc-dev-guide/src/offload/internals.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/offload/internals.md b/src/doc/rustc-dev-guide/src/offload/internals.md index 836fd7ad2670b..87c0fa9de3c1c 100644 --- a/src/doc/rustc-dev-guide/src/offload/internals.md +++ b/src/doc/rustc-dev-guide/src/offload/internals.md @@ -1,22 +1,29 @@ # std::offload -This module is under active development. Once upstream, it should allow Rust developers to run Rust code on GPUs. +This module is under active development. +Once upstream, it should allow Rust developers to run Rust code on GPUs. We aim to develop a `rusty` GPU programming interface, which is safe, convenient and sufficiently fast by default. -This includes automatic data movement to and from the GPU, in a efficient way. We will (later) +This includes automatic data movement to and from the GPU, in a efficient way. +We will (later) also offer more advanced, possibly unsafe, interfaces which allow a higher degree of control. The implementation is based on LLVM's "offload" project, which is already used by OpenMP to run Fortran or C++ code on GPUs. While the project is under development, users will need to call other compilers like clang to finish the compilation process. ## High-level compilation design: -We use a single-source, two-pass compilation approach. +We use a single-source, two-pass compilation approach. -First we compile all functions that should be offloaded for the device (e.g nvptx64, amdgcn-amd-amdhsa, intel in the future). Currently we require cumbersome `#cfg(target_os="")` annotations, but we intend to recognize those in the future based on our offload intrinsic. -This first compilation currently does not leverage rustc's internal Query system, so it will always recompile your kernels at the moment. This should be easy to fix, but we prioritize features and runtime performance improvements at the moment. Please reach out if you want to implement it, though! +First we compile all functions that should be offloaded for the device (e.g nvptx64, amdgcn-amd-amdhsa, intel in the future). +Currently we require cumbersome `#cfg(target_os="")` annotations, but we intend to recognize those in the future based on our offload intrinsic. +This first compilation currently does not leverage rustc's internal Query system, so it will always recompile your kernels at the moment. +This should be easy to fix, but we prioritize features and runtime performance improvements at the moment. +Please reach out if you want to implement it, though! We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), from the device, or both (e.g. `&mut [f64]`). We then launch the kernel, after which we inform the runtime to end this environment and move data back (as far as needed). -The second pass for the host will load the kernel artifacts from the previous compilation. rustc in general may not "guess" or hardcode the build directory layout, and as such it must be told the path to the kernel artifacts in the second invocation. The logic for this could be integrated into cargo, but it also only requires a trivial cargo wrapper, which we could trivially provide via crates.io till we see larger adoption. +The second pass for the host will load the kernel artifacts from the previous compilation. +rustc in general may not "guess" or hardcode the build directory layout, and as such it must be told the path to the kernel artifacts in the second invocation. +The logic for this could be integrated into cargo, but it also only requires a trivial cargo wrapper, which we could trivially provide via crates.io till we see larger adoption. It might seem tempting to think about a single-source, single pass compilation approach. However, a lot of the rustc frontend (e.g. AST) will drop any dead code (e.g. code behind an inactive `cfg`). Getting the frontend to expand and lower code for two targets naively will result in multiple definitions of the same symbol (and other issues). Trying to teach the whole rustc middle and backend to be aware that any symbol now might contain two implementations is a large undertaking, and it is questionable why we should make the whole compiler more complex, if the alternative is a ~5 line cargo wrapper. We still control the full compilation pipeline and have both host and device code available, therefore there shouldn't be a runtime performance difference between the two approaches. From b2267ae772d5c2fb4c80d46e42cdef9dbad95dc6 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 24 Apr 2026 10:08:39 +0200 Subject: [PATCH 24/68] reflow --- .../rustc-dev-guide/src/offload/internals.md | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/offload/internals.md b/src/doc/rustc-dev-guide/src/offload/internals.md index 87c0fa9de3c1c..78a1a852d21b3 100644 --- a/src/doc/rustc-dev-guide/src/offload/internals.md +++ b/src/doc/rustc-dev-guide/src/offload/internals.md @@ -4,26 +4,44 @@ This module is under active development. Once upstream, it should allow Rust developers to run Rust code on GPUs. We aim to develop a `rusty` GPU programming interface, which is safe, convenient and sufficiently fast by default. This includes automatic data movement to and from the GPU, in a efficient way. -We will (later) -also offer more advanced, possibly unsafe, interfaces which allow a higher degree of control. +We will (later) also offer more advanced, +possibly unsafe, interfaces which allow a higher degree of control. -The implementation is based on LLVM's "offload" project, which is already used by OpenMP to run Fortran or C++ code on GPUs. -While the project is under development, users will need to call other compilers like clang to finish the compilation process. +The implementation is based on LLVM's "offload" project, +which is already used by OpenMP to run Fortran or C++ code on GPUs. +While the project is under development, +users will need to call other compilers like clang to finish the compilation process. ## High-level compilation design: + We use a single-source, two-pass compilation approach. -First we compile all functions that should be offloaded for the device (e.g nvptx64, amdgcn-amd-amdhsa, intel in the future). +First we compile all functions that should be offloaded for the device +(e.g nvptx64, amdgcn-amd-amdhsa, intel in the future). Currently we require cumbersome `#cfg(target_os="")` annotations, but we intend to recognize those in the future based on our offload intrinsic. This first compilation currently does not leverage rustc's internal Query system, so it will always recompile your kernels at the moment. This should be easy to fix, but we prioritize features and runtime performance improvements at the moment. Please reach out if you want to implement it, though! -We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), from the device, or both (e.g. `&mut [f64]`). We then launch the kernel, after which we inform the runtime to end this environment and move data back (as far as needed). +We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. +On the host side, we generate calls to the openmp offload runtime, +to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). +We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), +from the device, or both (e.g. `&mut [f64]`). +We then launch the kernel, +after which we inform the runtime to end this environment and move data back (as far as needed). The second pass for the host will load the kernel artifacts from the previous compilation. -rustc in general may not "guess" or hardcode the build directory layout, and as such it must be told the path to the kernel artifacts in the second invocation. -The logic for this could be integrated into cargo, but it also only requires a trivial cargo wrapper, which we could trivially provide via crates.io till we see larger adoption. - -It might seem tempting to think about a single-source, single pass compilation approach. However, a lot of the rustc frontend (e.g. AST) will drop any dead code (e.g. code behind an inactive `cfg`). Getting the frontend to expand and lower code for two targets naively will result in multiple definitions of the same symbol (and other issues). Trying to teach the whole rustc middle and backend to be aware that any symbol now might contain two implementations is a large undertaking, and it is questionable why we should make the whole compiler more complex, if the alternative is a ~5 line cargo wrapper. We still control the full compilation pipeline and have both host and device code available, therefore there shouldn't be a runtime performance difference between the two approaches. - +rustc in general may not "guess" or hardcode the build directory layout, +and as such it must be told the path to the kernel artifacts in the second invocation. +The logic for this could be integrated into cargo, +but it also only requires a trivial cargo wrapper, +which we could trivially provide via crates.io till we see larger adoption. + +It might seem tempting to think about a single-source, single pass compilation approach. +However, a lot of the rustc frontend (e.g. AST) will drop any dead code (e.g. code behind an inactive `cfg`). +Getting the frontend to expand and lower code for two targets naively will result in multiple definitions of the same symbol (and other issues). +Trying to teach the whole rustc middle and backend to be aware that any symbol now might contain two implementations is a large undertaking, +and it is questionable why we should make the whole compiler more complex, if the alternative is a ~5 line cargo wrapper. +We still control the full compilation pipeline and have both host and device code available, +therefore there shouldn't be a runtime performance difference between the two approaches. From 183c7e5ad546b21a00ef6bf58a2be8fc77cfc053 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 24 Apr 2026 10:11:37 +0200 Subject: [PATCH 25/68] sembr src/offload/contributing.md --- src/doc/rustc-dev-guide/src/offload/contributing.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/offload/contributing.md b/src/doc/rustc-dev-guide/src/offload/contributing.md index f3a1ed2150a1b..5211bdf4303d9 100644 --- a/src/doc/rustc-dev-guide/src/offload/contributing.md +++ b/src/doc/rustc-dev-guide/src/offload/contributing.md @@ -1,8 +1,13 @@ # Contributing -Contributions are always welcome. This project is experimental, so the documentation and code are likely incomplete. Please ask on [Zulip](https://rust-lang.zulipchat.com/#narrow/channel/422870-t-compiler.2Fgpgpu-backend) (preferred) or the Rust Community Discord for help if you get stuck or if our documentation is unclear. +Contributions are always welcome. +This project is experimental, so the documentation and code are likely incomplete. +Please ask on [Zulip](https://rust-lang.zulipchat.com/#narrow/channel/422870-t-compiler.2Fgpgpu-backend) (preferred) or the Rust Community Discord for help if you get stuck or if our documentation is unclear. -We generally try to automate as much of the compilation process as possible for users. However, as a contributor it might sometimes be easier to directly rewrite and compile the LLVM-IR modules (.ll) to quickly iterate on changes, without needing to repeatedly recompile rustc. For people familiar with LLVM we therefore have the shell script below. Only when you are then happy with the IR changes you can work on updating rustc to generate the new, desired output. +We generally try to automate as much of the compilation process as possible for users. +However, as a contributor it might sometimes be easier to directly rewrite and compile the LLVM-IR modules (.ll) to quickly iterate on changes, without needing to repeatedly recompile rustc. +For people familiar with LLVM we therefore have the shell script below. +Only when you are then happy with the IR changes you can work on updating rustc to generate the new, desired output. ```sh set -e @@ -29,4 +34,6 @@ opt lib.ll -o lib.bc LIBOMPTARGET_INFO=-1 OFFLOAD_TRACK_ALLOCATION_TRACES=true ./a.out ``` -Please update the `` placeholders on the `clang-linker-wrapper` invocation. You will likely also need to adjust the library paths. See the linked usage section for details: [usage](usage.md#compile-instructions) +Please update the `` placeholders on the `clang-linker-wrapper` invocation. +You will likely also need to adjust the library paths. +See the linked usage section for details: [usage](usage.md#compile-instructions) From 4bafa9e045613afd49c882e668ff5edcf9d5270e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Fri, 24 Apr 2026 10:12:45 +0200 Subject: [PATCH 26/68] sembr src/offload/usage.md --- src/doc/rustc-dev-guide/src/offload/usage.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/offload/usage.md b/src/doc/rustc-dev-guide/src/offload/usage.md index 4d3222123aaff..c7ce2ded42e97 100644 --- a/src/doc/rustc-dev-guide/src/offload/usage.md +++ b/src/doc/rustc-dev-guide/src/offload/usage.md @@ -1,7 +1,9 @@ # Usage -This feature is work-in-progress, and not ready for usage. The instructions here are for contributors, or people interested in following the latest progress. -We currently work on launching the following Rust kernel on the GPU. To follow along, copy it to a `src/lib.rs` file. +This feature is work-in-progress, and not ready for usage. +The instructions here are for contributors, or people interested in following the latest progress. +We currently work on launching the following Rust kernel on the GPU. +To follow along, copy it to a `src/lib.rs` file. ```rust #![feature(abi_gpu_kernel)] @@ -75,9 +77,12 @@ pub extern "gpu-kernel" fn kernel_1(x: *mut [f64; 256]) { ``` ## Compile instructions -It is important to use a clang compiler build on the same llvm as rustc. Just calling clang without the full path will likely use your system clang, which probably will be incompatible. So either substitute clang/lld invocations below with absolute path, or set your `PATH` accordingly. +It is important to use a clang compiler build on the same llvm as rustc. +Just calling clang without the full path will likely use your system clang, which probably will be incompatible. +So either substitute clang/lld invocations below with absolute path, or set your `PATH` accordingly. -First we generate the device (gpu) code. Replace the target-cpu with the right code for your gpu. +First we generate the device (gpu) code. +Replace the target-cpu with the right code for your gpu. ``` RUSTFLAGS="-Ctarget-cpu=gfx90a --emit=llvm-bc,llvm-ir -Zoffload=Device -Csave-temps -Zunstable-options" cargo +offload build -Zunstable-options -r -v --target amdgcn-amd-amdhsa -Zbuild-std=core ``` @@ -94,8 +99,11 @@ While we integrated most offload steps into rustc by now, one binary invocation "clang-linker-wrapper" "--should-extract=gfx90a" "--device-compiler=amdgcn-amd-amdhsa=-g" "--device-compiler=amdgcn-amd-amdhsa=-save-temps=cwd" "--device-linker=amdgcn-amd-amdhsa=-lompdevice" "--host-triple=x86_64-unknown-linux-gnu" "--save-temps" "--linker-path=/ABSOlUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/lld/bin/ld.lld" "--hash-style=gnu" "--eh-frame-hdr" "-m" "elf_x86_64" "-pie" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "bare" "/lib/../lib64/Scrt1.o" "/lib/../lib64/crti.o" "/ABSOLUTE_PATH_TO/crtbeginS.o" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/bin/../lib/x86_64-unknown-linux-gnu" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/lib/clang/21/lib/x86_64-unknown-linux-gnu" "-L/lib/../lib64" "-L/usr/lib64" "-L/lib" "-L/usr/lib" "target//release/host.o" "-lstdc++" "-lm" "-lomp" "-lomptarget" "-L/ABSOLUTE_PATH_TO/rust/build/x86_64-unknown-linux-gnu/llvm/lib" "-lgcc_s" "-lgcc" "-lpthread" "-lc" "-lgcc_s" "-lgcc" "/ABSOLUTE_PATH_TO/crtendS.o" "/lib/../lib64/crtn.o" ``` -You can try to find the paths to those files on your system. However, I recommend to not fix the paths, but rather just re-generate them by copying a bare-mode openmp example and compiling it with your clang. By adding `-###` to your clang invocation, you can see the invidual steps. -It will show multiple steps, just look for the clang-linker-wrapper example. Make sure to still include the path to the `host.o` file, and not whatever tmp file you got when compiling your c++ example with the following call. +You can try to find the paths to those files on your system. +However, I recommend to not fix the paths, but rather just re-generate them by copying a bare-mode openmp example and compiling it with your clang. +By adding `-###` to your clang invocation, you can see the invidual steps. +It will show multiple steps, just look for the clang-linker-wrapper example. +Make sure to still include the path to the `host.o` file, and not whatever tmp file you got when compiling your c++ example with the following call. ``` myclang++ -fuse-ld=lld -O3 -fopenmp -fopenmp-offload-mandatory --offload-arch=gfx90a omp_bare.cpp -o main -### ``` From b430a1f40df15b2e6e7700884ac0eae095732ba7 Mon Sep 17 00:00:00 2001 From: Redddy Date: Fri, 24 Apr 2026 17:45:02 +0900 Subject: [PATCH 27/68] Reflect code renames and API changes in panic docs --- .../src/panic-implementation.md | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/panic-implementation.md b/src/doc/rustc-dev-guide/src/panic-implementation.md index ea71abff19998..8ce949ae2925f 100644 --- a/src/doc/rustc-dev-guide/src/panic-implementation.md +++ b/src/doc/rustc-dev-guide/src/panic-implementation.md @@ -9,7 +9,7 @@ or `std`. ### core definition of panic! -The `core` `panic!` macro eventually makes the following call (in `library/core/src/panicking.rs`): +The `core` `panic!` macro eventually makes the following call (in [`library/core/src/panicking.rs`]): ```rust // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call @@ -18,13 +18,18 @@ extern "Rust" { fn panic_impl(pi: &PanicInfo<'_>) -> !; } -let pi = PanicInfo::internal_constructor(Some(&fmt), location); +let pi = PanicInfo::new( + &fmt, + Location::caller(), + /* can_unwind */ true, + /* force_no_backtrace */ false, +); unsafe { panic_impl(&pi) } ``` Actually resolving this goes through several layers of indirection: -1. In `compiler/rustc_middle/src/middle/weak_lang_items.rs`, `panic_impl` is +1. In [`compiler/rustc_hir/src/weak_lang_items.rs`], `panic_impl` is declared as 'weak lang item', with the symbol `rust_begin_unwind`. This is used in `rustc_hir_analysis/src/collect.rs` to set the actual symbol name to `rust_begin_unwind`. @@ -33,25 +38,24 @@ Actually resolving this goes through several layers of indirection: which means that core will attempt to call a foreign symbol called `rust_begin_unwind` (to be resolved at link time) -2. In `library/std/src/panicking.rs`, we have this definition: +2. In [`library/std/src/panicking.rs`], we have this definition: ```rust -/// Entry point of panic from the core crate. -#[cfg(not(test))] +/// Entry point of panics from the core crate (`panic_impl` lang item). +#[cfg(not(any(test, doctest)))] #[panic_handler] -#[unwind(allowed)] -pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { +pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { ... } ``` -The special `panic_handler` attribute is resolved via `compiler/rustc_middle/src/middle/lang_items`. -The `extract` function converts the `panic_handler` attribute to a `panic_impl` lang item. +The special `panic_handler` attribute is resolved via [`compiler/rustc_passes/src/lang_items.rs`]. +The [`extract_ast`] function converts the `panic_handler` attribute to a `panic_impl` lang item. Now, we have a matching `panic_handler` lang item in the `std`. This function goes through the same process as the `extern { fn panic_impl }` definition in `core`, ending up with a symbol name of `rust_begin_unwind`. At link time, the symbol reference in `core` -will be resolved to the definition of `std` (the function called `begin_panic_handler` in the +will be resolved to the definition of `std` (the function called `panic_handler` in the Rust source). Thus, control flow will pass from core to std at runtime. This allows panics from `core` @@ -59,8 +63,8 @@ to go through the same infrastructure that other panics use (panic hooks, unwind ### std implementation of panic! -This is where the actual panic-related logic begins. In `library/std/src/panicking.rs`, -control passes to `rust_panic_with_hook`. This method is responsible +This is where the actual panic-related logic begins. In [`library/std/src/panicking.rs`], +control passes to `panic_with_hook`. This method is responsible for invoking the global panic hook, and checking for double panics. Finally, we call `__rust_start_panic`, which is provided by the panic runtime. @@ -111,3 +115,8 @@ the call to the user-provided `main` function is wrapped in `catch_unwind`. [runtime service]: https://github.com/rust-lang/rust/blob/HEAD/library/std/src/rt.rs +[`library/core/src/panicking.rs`]: https://doc.rust-lang.org/core/panicking/index.html +[`compiler/rustc_hir/src/weak_lang_items.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_hir/src/weak_lang_items.rs +[`library/std/src/panicking.rs`]: https://github.com/rust-lang/rust/blob/HEAD/library/std/src/panicking.rs +[`compiler/rustc_passes/src/lang_items.rs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_passes/lang_items/index.html +[`extract_ast`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_passes/lang_items/fn.extract_ast.html From 1a681b03162b141fe75eaf146e38a25e486dbe7f Mon Sep 17 00:00:00 2001 From: lapla Date: Mon, 27 Apr 2026 11:06:54 +0900 Subject: [PATCH 28/68] Use `_mcount` as the mcount symbol name on RISC-V Linux GNU targets --- .../rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs | 1 + .../src/spec/targets/riscv64a23_unknown_linux_gnu.rs | 1 + .../rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs index c11edf7c618c2..b936a2ba2570d 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { llvm_abiname: LlvmAbi::Ilp32d, max_atomic_width: Some(32), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + mcount: "\u{1}_mcount".into(), ..base::linux_gnu::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/riscv64a23_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv64a23_unknown_linux_gnu.rs index f9a7d307c74a3..74a8a88dff674 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64a23_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64a23_unknown_linux_gnu.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { llvm_abiname: LlvmAbi::Lp64d, max_atomic_width: Some(64), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + mcount: "\u{1}_mcount".into(), ..base::linux_gnu::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs index 76c9ad65700c2..f362e694db4d2 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs @@ -23,6 +23,7 @@ pub(crate) fn target() -> Target { llvm_abiname: LlvmAbi::Lp64d, max_atomic_width: Some(64), supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), + mcount: "\u{1}_mcount".into(), ..base::linux_gnu::opts() }, } From ff456498f764863997fbbfb7215215b8dc2b28bc Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 27 Apr 2026 05:12:50 +0000 Subject: [PATCH 29/68] Prepare for merging from rust-lang/rust This updates the rust-version file to ca9a134e0985765ded9cfdde4030a5df4db7e2bd. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 702f1a796ea3e..2d5b9a994c6fd 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -d493b7c5ac637ed7edf626128b9f14796a2dad20 +ca9a134e0985765ded9cfdde4030a5df4db7e2bd From d3db8df0a23945895b82979810519c0bf9c7d0b8 Mon Sep 17 00:00:00 2001 From: Redddy Date: Mon, 27 Apr 2026 15:57:38 +0900 Subject: [PATCH 30/68] Add Fuchsia to the list of notification groups --- src/doc/rustc-dev-guide/src/notification-groups/about.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc-dev-guide/src/notification-groups/about.md b/src/doc/rustc-dev-guide/src/notification-groups/about.md index 28c836dc4dba2..8d5b7d1278307 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/about.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/about.md @@ -22,6 +22,7 @@ Here's the list of the notification groups: - [Apple](./apple.md) - [ARM](./arm.md) - [Emscripten](./emscripten.md) +- [Fuchsia](./fuchsia.md) - [LoongArch](./loongarch.md) - [RISC-V](./risc-v.md) - [WASI](./wasi.md) From 9f9ad74d3de5b936c98a7c77c2b9bfab9e04aa6c Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Tue, 28 Apr 2026 17:31:16 +0800 Subject: [PATCH 31/68] Update `CodegenBackend` path --- src/doc/rustc-dev-guide/src/backend/backend-agnostic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index 2fdda4eda99a3..1fb991738a6a0 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -162,7 +162,7 @@ used for high-level codegen-driving functions like `codegen_crate` in `base.rs`. For LLVM, it is the empty `LlvmCodegenBackend`. `ExtraBackendMethods` should be implemented by the same structure that implements the `CodegenBackend` defined in -`rustc_codegen_utils/codegen_backend.rs`. +`rustc_codegen_ssa/src/traits/backend.rs`. During the traitification process, certain functions have been converted from methods of a local structure to methods of `CodegenCx` or `Builder` and a From c13cfc627efadd44664104ed5cbafb776273a745 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 15 Apr 2026 12:10:16 +0000 Subject: [PATCH 32/68] move `AstFragment::to_string` out of `ast_fragments!` --- compiler/rustc_expand/src/expand.rs | 82 +++++++++++++++++------------ compiler/rustc_expand/src/stats.rs | 4 -- 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 804d3c02b413d..861af0fd74203 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -57,12 +57,10 @@ macro_rules! ast_fragments { $(one fn $mut_visit_ast:ident; fn $visit_ast:ident; - fn $ast_to_string:path; )? $(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident($($args:tt)*); - fn $ast_to_string_elt:path; )? fn $make_ast:ident; })* @@ -166,21 +164,6 @@ macro_rules! ast_fragments { } V::Result::output() } - - pub(crate) fn to_string(&self) -> String { - match self { - AstFragment::OptExpr(Some(expr)) => pprust::expr_to_string(expr), - AstFragment::OptExpr(None) => unreachable!(), - AstFragment::MethodReceiverExpr(expr) => pprust::expr_to_string(expr), - $($(AstFragment::$Kind(ast) => $ast_to_string(ast),)?)* - $($( - AstFragment::$Kind(ast) => { - // The closure unwraps a `P` if present, or does nothing otherwise. - elems_to_string(&*ast, |ast| $ast_to_string_elt(&*ast)) - } - )?)* - } - } } impl<'a, 'b> MacResult for crate::mbe::macro_rules::ParserAnyMacro<'a, 'b> { @@ -195,98 +178,127 @@ macro_rules! ast_fragments { ast_fragments! { Expr(Box) { "expression"; - one fn visit_expr; fn visit_expr; fn pprust::expr_to_string; + one fn visit_expr; fn visit_expr; fn make_expr; } Pat(Box) { "pattern"; - one fn visit_pat; fn visit_pat; fn pprust::pat_to_string; + one fn visit_pat; fn visit_pat; fn make_pat; } Ty(Box) { "type"; - one fn visit_ty; fn visit_ty; fn pprust::ty_to_string; + one fn visit_ty; fn visit_ty; fn make_ty; } Stmts(SmallVec<[ast::Stmt; 1]>) { "statement"; - many fn flat_map_stmt; fn visit_stmt(); fn pprust::stmt_to_string; + many fn flat_map_stmt; fn visit_stmt(); fn make_stmts; } Items(SmallVec<[Box; 1]>) { "item"; - many fn flat_map_item; fn visit_item(); fn pprust::item_to_string; + many fn flat_map_item; fn visit_item(); fn make_items; } TraitItems(SmallVec<[Box; 1]>) { "trait item"; many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Trait); - fn pprust::assoc_item_to_string; fn make_trait_items; } ImplItems(SmallVec<[Box; 1]>) { "impl item"; many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: false }); - fn pprust::assoc_item_to_string; fn make_impl_items; } TraitImplItems(SmallVec<[Box; 1]>) { "impl item"; many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: true }); - fn pprust::assoc_item_to_string; fn make_trait_impl_items; } ForeignItems(SmallVec<[Box; 1]>) { "foreign item"; - many fn flat_map_foreign_item; fn visit_foreign_item(); fn pprust::foreign_item_to_string; + many fn flat_map_foreign_item; fn visit_foreign_item(); fn make_foreign_items; } Arms(SmallVec<[ast::Arm; 1]>) { "match arm"; - many fn flat_map_arm; fn visit_arm(); fn unreachable_to_string; + many fn flat_map_arm; fn visit_arm(); fn make_arms; } ExprFields(SmallVec<[ast::ExprField; 1]>) { "field expression"; - many fn flat_map_expr_field; fn visit_expr_field(); fn unreachable_to_string; + many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields; } PatFields(SmallVec<[ast::PatField; 1]>) { "field pattern"; - many fn flat_map_pat_field; fn visit_pat_field(); fn unreachable_to_string; + many fn flat_map_pat_field; fn visit_pat_field(); fn make_pat_fields; } GenericParams(SmallVec<[ast::GenericParam; 1]>) { "generic parameter"; - many fn flat_map_generic_param; fn visit_generic_param(); fn unreachable_to_string; + many fn flat_map_generic_param; fn visit_generic_param(); fn make_generic_params; } Params(SmallVec<[ast::Param; 1]>) { "function parameter"; - many fn flat_map_param; fn visit_param(); fn unreachable_to_string; + many fn flat_map_param; fn visit_param(); fn make_params; } FieldDefs(SmallVec<[ast::FieldDef; 1]>) { "field"; - many fn flat_map_field_def; fn visit_field_def(); fn unreachable_to_string; + many fn flat_map_field_def; fn visit_field_def(); fn make_field_defs; } Variants(SmallVec<[ast::Variant; 1]>) { - "variant"; many fn flat_map_variant; fn visit_variant(); fn unreachable_to_string; + "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; } WherePredicates(SmallVec<[ast::WherePredicate; 1]>) { "where predicate"; - many fn flat_map_where_predicate; fn visit_where_predicate(); fn unreachable_to_string; + many fn flat_map_where_predicate; fn visit_where_predicate(); fn make_where_predicates; } Crate(ast::Crate) { "crate"; - one fn visit_crate; fn visit_crate; fn unreachable_to_string; + one fn visit_crate; fn visit_crate; fn make_crate; } } +impl AstFragment { + pub(crate) fn to_string(&self) -> String { + match self { + AstFragment::OptExpr(Some(expr)) + | AstFragment::MethodReceiverExpr(expr) + | AstFragment::Expr(expr) => pprust::expr_to_string(expr), + AstFragment::Pat(ast) => pprust::pat_to_string(ast), + AstFragment::Ty(ast) => pprust::ty_to_string(ast), + AstFragment::Stmts(ast) => elems_to_string(ast, pprust::stmt_to_string), + AstFragment::Items(ast) => elems_to_string(ast, |ast| pprust::item_to_string(ast)), + AstFragment::TraitItems(ast) + | AstFragment::ImplItems(ast) + | AstFragment::TraitImplItems(ast) => { + elems_to_string(ast, |ast| pprust::assoc_item_to_string(ast)) + } + AstFragment::ForeignItems(ast) => { + elems_to_string(ast, |ast| pprust::foreign_item_to_string(ast)) + } + AstFragment::OptExpr(None) + | AstFragment::Crate(_) + | AstFragment::Arms(_) + | AstFragment::ExprFields(_) + | AstFragment::PatFields(_) + | AstFragment::GenericParams(_) + | AstFragment::Params(_) + | AstFragment::FieldDefs(_) + | AstFragment::Variants(_) + | AstFragment::WherePredicates(_) => unreachable!(), + } + } +} + pub enum SupportsMacroExpansion { No, Yes { supports_inner_attrs: bool }, diff --git a/compiler/rustc_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs index 0d60141f274ec..ef1668e4af25c 100644 --- a/compiler/rustc_expand/src/stats.rs +++ b/compiler/rustc_expand/src/stats.rs @@ -32,10 +32,6 @@ pub(crate) fn elems_to_string(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> Str s } -pub(crate) fn unreachable_to_string(_: &T) -> String { - unreachable!() -} - pub(crate) fn update_bang_macro_stats( ecx: &mut ExtCtxt<'_>, fragment_kind: AstFragmentKind, From 712d30f79f37d3f6520e8f0521580ff871610b81 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 15 Apr 2026 12:21:14 +0000 Subject: [PATCH 33/68] merge `$mut_visit_ast` and `$visit_ast` --- compiler/rustc_expand/src/expand.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 861af0fd74203..762f293c1e443 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -55,7 +55,6 @@ macro_rules! ast_fragments { $($Kind:ident($AstTy:ty) { $kind_name:expr; $(one - fn $mut_visit_ast:ident; fn $visit_ast:ident; )? $(many @@ -148,7 +147,7 @@ macro_rules! ast_fragments { } } AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), - $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)* + $($(AstFragment::$Kind(ast) => vis.$visit_ast(ast),)?)* $($(AstFragment::$Kind(ast) => ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast, $($args)*)),)?)* } @@ -178,17 +177,17 @@ macro_rules! ast_fragments { ast_fragments! { Expr(Box) { "expression"; - one fn visit_expr; fn visit_expr; + one fn visit_expr; fn make_expr; } Pat(Box) { "pattern"; - one fn visit_pat; fn visit_pat; + one fn visit_pat; fn make_pat; } Ty(Box) { "type"; - one fn visit_ty; fn visit_ty; + one fn visit_ty; fn make_ty; } Stmts(SmallVec<[ast::Stmt; 1]>) { @@ -262,7 +261,7 @@ ast_fragments! { } Crate(ast::Crate) { "crate"; - one fn visit_crate; fn visit_crate; + one fn visit_crate; fn make_crate; } } From 210f03a1cfb1a44a48df67ce070576e9098ae876 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 19 Apr 2026 20:53:05 +0000 Subject: [PATCH 34/68] smaller tweaks --- compiler/rustc_expand/src/expand.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 762f293c1e443..baabc6ebd75fc 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -117,21 +117,21 @@ macro_rules! ast_fragments { pub(crate) fn make_opt_expr(self) -> Option> { match self { AstFragment::OptExpr(expr) => expr, - _ => panic!("AstFragment::make_* called on the wrong kind of fragment"), + _ => panic!("AstFragment::make_opt_expr called on the wrong kind of fragment"), } } pub(crate) fn make_method_receiver_expr(self) -> Box { match self { AstFragment::MethodReceiverExpr(expr) => expr, - _ => panic!("AstFragment::make_* called on the wrong kind of fragment"), + _ => panic!("AstFragment::make_method_receiver_expr called on the wrong kind of fragment"), } } $(pub fn $make_ast(self) -> $AstTy { match self { AstFragment::$Kind(ast) => ast, - _ => panic!("AstFragment::make_* called on the wrong kind of fragment"), + _ => panic!("AstFragment::{} called on the wrong kind of fragment", stringify!($make_ast)), } })* @@ -251,7 +251,8 @@ ast_fragments! { fn make_field_defs; } Variants(SmallVec<[ast::Variant; 1]>) { - "variant"; many fn flat_map_variant; fn visit_variant(); + "variant"; + many fn flat_map_variant; fn visit_variant(); fn make_variants; } WherePredicates(SmallVec<[ast::WherePredicate; 1]>) { From 5ad560f7ec96bd42f003abe9842abe6e5643817e Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 28 Apr 2026 10:26:46 +0000 Subject: [PATCH 35/68] remove `MethodReceiverExpr` special-casing --- compiler/rustc_expand/src/base.rs | 4 ++++ compiler/rustc_expand/src/expand.rs | 19 +++++-------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index b8325f7ba3987..e50c232df2d86 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -423,6 +423,10 @@ pub trait MacResult { None } + fn make_method_receiver_expr(self: Box) -> Option> { + self.make_expr() + } + /// Creates zero or more items. fn make_items(self: Box) -> Option; 1]>> { None diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index baabc6ebd75fc..53bb7c9c03857 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -68,7 +68,6 @@ macro_rules! ast_fragments { /// Can also serve as an input and intermediate result for macro expansion operations. pub enum AstFragment { OptExpr(Option>), - MethodReceiverExpr(Box), $($Kind($AstTy),)* } @@ -76,7 +75,6 @@ macro_rules! ast_fragments { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum AstFragmentKind { OptExpr, - MethodReceiverExpr, $($Kind,)* } @@ -84,7 +82,6 @@ macro_rules! ast_fragments { pub fn name(self) -> &'static str { match self { AstFragmentKind::OptExpr => "expression", - AstFragmentKind::MethodReceiverExpr => "expression", $(AstFragmentKind::$Kind => $kind_name,)* } } @@ -93,8 +90,6 @@ macro_rules! ast_fragments { match self { AstFragmentKind::OptExpr => result.make_expr().map(Some).map(AstFragment::OptExpr), - AstFragmentKind::MethodReceiverExpr => - result.make_expr().map(AstFragment::MethodReceiverExpr), $(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)* } } @@ -121,13 +116,6 @@ macro_rules! ast_fragments { } } - pub(crate) fn make_method_receiver_expr(self) -> Box { - match self { - AstFragment::MethodReceiverExpr(expr) => expr, - _ => panic!("AstFragment::make_method_receiver_expr called on the wrong kind of fragment"), - } - } - $(pub fn $make_ast(self) -> $AstTy { match self { AstFragment::$Kind(ast) => ast, @@ -146,7 +134,6 @@ macro_rules! ast_fragments { *opt_expr = vis.filter_map_expr(expr) } } - AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ast) => vis.$visit_ast(ast),)?)* $($(AstFragment::$Kind(ast) => ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast, $($args)*)),)?)* @@ -157,7 +144,6 @@ macro_rules! ast_fragments { match self { AstFragment::OptExpr(Some(expr)) => try_visit!(visitor.visit_expr(expr)), AstFragment::OptExpr(None) => {} - AstFragment::MethodReceiverExpr(expr) => try_visit!(visitor.visit_method_receiver_expr(expr)), $($(AstFragment::$Kind(ast) => try_visit!(visitor.$visit_ast(ast)),)?)* $($(AstFragment::$Kind(ast) => walk_list!(visitor, $visit_ast_elt, &ast[..], $($args)*),)?)* } @@ -180,6 +166,11 @@ ast_fragments! { one fn visit_expr; fn make_expr; } + MethodReceiverExpr(Box) { + "expression"; + one fn visit_method_receiver_expr; + fn make_method_receiver_expr; + } Pat(Box) { "pattern"; one fn visit_pat; From b072d24e268d5d0121cfc29f3fcb0714408281e5 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Mon, 26 Jan 2026 16:58:31 -0800 Subject: [PATCH 36/68] Fix: On wasm targets, call `panic_in_cleanup` if panic occurs in cleanup Previously this was not correctly implemented. Each funclet may need its own terminate block, so this changes the `terminate_block` into a `terminate_blocks` `IndexVec` which can have a terminate_block for each funclet. We key on the first basic block of the funclet -- in particular, this is the start block for the old case of the top level terminate function. Rather than using a catchswitch/catchpad pair, I used a cleanuppad. The reason for the pair is to avoid catching foreign exceptions on MSVC. On wasm, it seems that the catchswitch/catchpad pair is optimized back into a single cleanuppad and a catch_all instruction is emitted which will catch foreign exceptions. Because the new logic is only used on wasm, it seemed better to take the simpler approach seeing as they do the same thing. --- compiler/rustc_codegen_gcc/src/builder.rs | 4 + compiler/rustc_codegen_llvm/src/builder.rs | 4 + compiler/rustc_codegen_ssa/src/mir/block.rs | 96 +++++++++++++++---- compiler/rustc_codegen_ssa/src/mir/mod.rs | 9 +- .../rustc_codegen_ssa/src/traits/builder.rs | 5 +- tests/codegen-llvm/double_panic_wasm.rs | 34 +++++++ tests/codegen-llvm/terminating-catchpad.rs | 10 ++ 7 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 tests/codegen-llvm/double_panic_wasm.rs diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 3cffd862b9b98..08964113b944a 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1657,6 +1657,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { unimplemented!(); } + fn get_funclet_cleanuppad(&self, _funclet: &Funclet) -> RValue<'gcc> { + unimplemented!(); + } + // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 056a0763087a2..9e59d7aa7c20a 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1289,6 +1289,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ret } + fn get_funclet_cleanuppad(&self, funclet: &Funclet<'ll>) -> &'ll Value { + funclet.cleanuppad() + } + // Atomic Operations fn atomic_cmpxchg( &mut self, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 06c81662d6018..2967db9df787c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -214,19 +214,18 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, mir::UnwindAction::Terminate(reason) => { - if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) { + if fx.mir[self.bb].is_cleanup && base::wants_wasm_eh(fx.cx.tcx().sess) { + // For wasm, we need to generate a nested `cleanuppad within %outer_pad` + // to catch exceptions during cleanup and call `panic_in_cleanup`. + Some(fx.terminate_block(reason, Some(self.bb))) + } else if fx.mir[self.bb].is_cleanup + && base::wants_new_eh_instructions(fx.cx.tcx().sess) + { // MSVC SEH will abort automatically if an exception tries to // propagate out from cleanup. - - // FIXME(@mirkootter): For wasm, we currently do not support terminate during - // cleanup, because this requires a few more changes: The current code - // caches the `terminate_block` for each function; funclet based code - however - - // requires a different terminate_block for each funclet - // Until this is implemented, we just do not unwind inside cleanup blocks - None } else { - Some(fx.terminate_block(reason)) + Some(fx.terminate_block(reason, None)) } } }; @@ -238,7 +237,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if let Some(unwind_block) = unwind_block { let ret_llbb = if let Some((_, target)) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -309,7 +308,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { ) -> MergingSucc { let unwind_target = match unwind { mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)), - mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)), + mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason, None)), mir::UnwindAction::Continue => None, mir::UnwindAction::Unreachable => None, }; @@ -317,7 +316,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { if operands.iter().any(|x| matches!(x, InlineAsmOperandRef::Label { .. })) { assert!(unwind_target.is_none()); let ret_llbb = if let Some(target) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -334,7 +333,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { MergingSucc::False } else if let Some(cleanup) = unwind_target { let ret_llbb = if let Some(target) = destination { - fx.llbb(target) + self.llbb_with_cleanup(fx, target) } else { fx.unreachable_block() }; @@ -1906,8 +1905,39 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } - fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock { - if let Some((cached_bb, cached_reason)) = self.terminate_block + fn terminate_block( + &mut self, + reason: UnwindTerminateReason, + outer_catchpad_bb: Option, + ) -> Bx::BasicBlock { + // mb_funclet_bb should be present if and only if the target is wasm and + // we're terminating because of an unwind in a cleanup block. In that + // case we have nested funclets and the inner catch_switch needs to know + // what outer catch_pad it is contained in. + debug_assert!( + outer_catchpad_bb.is_some() + == (base::wants_wasm_eh(self.cx.tcx().sess) + && reason == UnwindTerminateReason::InCleanup) + ); + + // When we aren't in a wasm InCleanup block, there's only one terminate + // block needed so we cache at START_BLOCK index. + let mut cache_bb = mir::START_BLOCK; + // In wasm eh InCleanup, use the outer funclet's cleanup BB as the cache + // key. + if let Some(outer_bb) = outer_catchpad_bb { + let cleanup_kinds = + self.cleanup_kinds.as_ref().expect("cleanup_kinds required for funclets"); + cache_bb = cleanup_kinds[outer_bb] + .funclet_bb(outer_bb) + .expect("funclet_bb should be in a funclet"); + + // Ensure the outer funclet is created first + if self.funclets[cache_bb].is_none() { + self.landing_pad_for(cache_bb); + } + } + if let Some((cached_bb, cached_reason)) = self.terminate_blocks[cache_bb] && reason == cached_reason { return cached_bb; @@ -1945,12 +1975,35 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // cp_terminate: // %cp = catchpad within %cs [null, i32 64, null] // ... + // + // By contrast, on WebAssembly targets, we specifically _do_ want to + // catch foreign exceptions. The situation with MSVC is a + // regrettable hack which we don't want to extend to other targets + // unless necessary. For WebAssembly, to generate catch(...) and + // catch only C++ exception instead of generating a catch_all, we + // need to call the intrinsics @llvm.wasm.get.exception and + // @llvm.wasm.get.ehselector in the catch pad. Since we don't do + // this, we generate a catch_all. We originally got this behavior + // by accident but it luckily matches our intention. llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate"); - let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); let mut cs_bx = Bx::build(self.cx, llbb); - let cs = cs_bx.catch_switch(None, None, &[cp_llbb]); + + // For wasm InCleanup blocks, our catch_switch is nested within the + // outer catchpad, so we need to provide it as the parent value to + // catch_switch. + let mut outer_cleanuppad = None; + if outer_catchpad_bb.is_some() { + // Get the outer funclet's catchpad + let outer_funclet = self.funclets[cache_bb] + .as_ref() + .expect("landing_pad_for didn't create funclet"); + outer_cleanuppad = Some(cs_bx.get_funclet_cleanuppad(outer_funclet)); + } + let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate"); + let cs = cs_bx.catch_switch(outer_cleanuppad, None, &[cp_llbb]); + drop(cs_bx); bx = Bx::build(self.cx, cp_llbb); let null = @@ -1971,13 +2024,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { // Specifying more arguments than necessary usually doesn't // hurt, but the `WasmEHPrepare` LLVM pass does not recognize - // anything other than a single `null` as a `catch (...)` block, + // anything other than a single `null` as a `catch_all` block, // leading to problems down the line during instruction // selection. &[null] as &[_] }; funclet = Some(bx.catch_pad(cs, args)); + // On wasm, if we wanted to generate a catch(...) and only catch C++ + // exceptions, we'd call @llvm.wasm.get.exception and + // @llvm.wasm.get.ehselector selectors here. We want a catch_all so + // we leave them out. This is intentionally diverging from the MSVC + // behavior. } else { llbb = Bx::append_block(self.cx, self.llfn, "terminate"); bx = Bx::build(self.cx, llbb); @@ -2003,7 +2061,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.unreachable(); - self.terminate_block = Some((llbb, reason)); + self.terminate_blocks[cache_bb] = Some((llbb, reason)); llbb } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 93da12107bab0..5c14d6f2c0930 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -90,8 +90,11 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// Cached unreachable block unreachable_block: Option, - /// Cached terminate upon unwinding block and its reason - terminate_block: Option<(Bx::BasicBlock, UnwindTerminateReason)>, + /// Cached terminate upon unwinding block and its reason. For non-wasm + /// targets, there is at most one such block per function, stored at index + /// `START_BLOCK`. For wasm targets, each funclet needs its own terminate + /// block, indexed by the cleanup block that is the funclet's head. + terminate_blocks: IndexVec>, /// A bool flag for each basic block indicating whether it is a cold block. /// A cold block is a block that is unlikely to be executed at runtime. @@ -227,7 +230,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( personality_slot: None, cached_llbbs, unreachable_block: None, - terminate_block: None, + terminate_blocks: IndexVec::from_elem(None, &mir.basic_blocks), cleanup_kinds, landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index c222aef4574bf..5092f28a33f7b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -552,12 +552,12 @@ pub trait BuilderMethods<'a, 'tcx>: fn set_personality_fn(&mut self, personality: Self::Function); - // These are used by everyone except msvc + // These are used by everyone except msvc and wasm EH fn cleanup_landing_pad(&mut self, pers_fn: Self::Function) -> (Self::Value, Self::Value); fn filter_landing_pad(&mut self, pers_fn: Self::Function); fn resume(&mut self, exn0: Self::Value, exn1: Self::Value); - // These are used only by msvc + // These are used by msvc and wasm EH fn cleanup_pad(&mut self, parent: Option, args: &[Self::Value]) -> Self::Funclet; fn cleanup_ret(&mut self, funclet: &Self::Funclet, unwind: Option); fn catch_pad(&mut self, parent: Self::Value, args: &[Self::Value]) -> Self::Funclet; @@ -567,6 +567,7 @@ pub trait BuilderMethods<'a, 'tcx>: unwind: Option, handlers: &[Self::BasicBlock], ) -> Self::Value; + fn get_funclet_cleanuppad(&self, funclet: &Self::Funclet) -> Self::Value; fn atomic_cmpxchg( &mut self, diff --git a/tests/codegen-llvm/double_panic_wasm.rs b/tests/codegen-llvm/double_panic_wasm.rs new file mode 100644 index 0000000000000..1eafe60503809 --- /dev/null +++ b/tests/codegen-llvm/double_panic_wasm.rs @@ -0,0 +1,34 @@ +//@ compile-flags: -C panic=unwind -Copt-level=0 +//@ needs-unwind +//@ only-wasm32 + +#![crate_type = "lib"] + +// Test that `panic_in_cleanup` is called on webassembly targets when a panic +// occurs in a destructor during unwinding. + +extern "Rust" { + fn may_panic(); +} + +struct PanicOnDrop; + +impl Drop for PanicOnDrop { + fn drop(&mut self) { + unsafe { may_panic() } + } +} + +// CHECK-LABEL: @double_panic +// CHECK: invoke void @may_panic() +// CHECK: invoke void @{{.+}}drop_in_place{{.+}} +// CHECK: unwind label %[[TERMINATE:.*]] +// +// CHECK: [[TERMINATE]]: +// CHECK: call void @{{.*panic_in_cleanup}} +// CHECK: unreachable +#[no_mangle] +pub fn double_panic() { + let _guard = PanicOnDrop; + unsafe { may_panic() } +} diff --git a/tests/codegen-llvm/terminating-catchpad.rs b/tests/codegen-llvm/terminating-catchpad.rs index a2ec19871d1fc..7c98ea94fdc13 100644 --- a/tests/codegen-llvm/terminating-catchpad.rs +++ b/tests/codegen-llvm/terminating-catchpad.rs @@ -9,6 +9,10 @@ // Ensure a catch-all generates: // - `catchpad ... [ptr null]` on Wasm (otherwise LLVM gets confused) // - `catchpad ... [ptr null, i32 64, ptr null]` on Windows (otherwise we catch SEH exceptions) +// +// Unlike on windows, on Wasm, we specifically do want to catch foreign +// exceptions. To catch only C++ exceptions we'd need to call +// @llvm.wasm.get.exception and @llvm.wasm.get.ehselector in the catchpad. #![feature(no_core, lang_items, rustc_attrs)] #![crate_type = "lib"] @@ -36,8 +40,14 @@ fn panic_cannot_unwind() -> ! { #[no_mangle] #[rustc_nounwind] pub fn doesnt_unwind() { + // CHECK: catchswitch within none [label %{{.*}}] unwind to caller // emscripten: %catchpad = catchpad within %catchswitch [ptr null] // wasi: %catchpad = catchpad within %catchswitch [ptr null] // seh: %catchpad = catchpad within %catchswitch [ptr null, i32 64, ptr null] + // + // We don't call these intrinsics on wasm targets so we generate a catch_all + // instruction which also picks up foreign exceptions + // NOT: @llvm.wasm.get.exception + // NOT: @llvm.wasm.get.ehselector unwinds(); } From d3992fac9491d397f8d41d7f34f98f1420d7728f Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Tue, 28 Apr 2026 18:01:12 +0000 Subject: [PATCH 37/68] Add feature gate for view_types --- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + .../feature-gates/feature-gate-view-types.rs | 16 ++++++ .../feature-gate-view-types.stderr | 49 +++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 tests/ui/feature-gates/feature-gate-view-types.rs create mode 100644 tests/ui/feature-gates/feature-gate-view-types.stderr diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3bb4bc863def2..9712879fce6f7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -723,6 +723,8 @@ declare_features! ( (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute. (unstable, used_with_arg, "1.60.0", Some(93798)), + /// Allows view types. + (unstable, view_types, "CURRENT_RUSTC_VERSION", Some(155938)), /// Target features on wasm. (unstable, wasm_target_feature, "1.30.0", Some(150260)), /// Allows use of attributes in `where` clauses. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4cacdbd3408a5..3db4921d6ec0a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2232,6 +2232,7 @@ symbols! { verbatim, version, vfp2, + view_types, vis, visible_private_types, volatile, diff --git a/tests/ui/feature-gates/feature-gate-view-types.rs b/tests/ui/feature-gates/feature-gate-view-types.rs new file mode 100644 index 0000000000000..26ca0ee4206b2 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-view-types.rs @@ -0,0 +1,16 @@ +//@ known-bug: #155938 + +struct Foo { + a: usize, + b: usize, +} + +fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { + a.a += 1; + b.b += 1; +} + +fn main() { + let mut foo = Foo { a: 0, b: 0 }; + bar(&mut foo, &mut foo); +} diff --git a/tests/ui/feature-gates/feature-gate-view-types.stderr b/tests/ui/feature-gates/feature-gate-view-types.stderr new file mode 100644 index 0000000000000..436b7ea769ed3 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-view-types.stderr @@ -0,0 +1,49 @@ +error: expected parameter name, found `{` + --> $DIR/feature-gate-view-types.rs:8:20 + | +LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { + | ^ expected parameter name + +error: expected one of `!`, `(`, `)`, `,`, `::`, or `<`, found `.` + --> $DIR/feature-gate-view-types.rs:8:19 + | +LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { + | ^ + | | + | expected one of `!`, `(`, `)`, `,`, `::`, or `<` + | help: missing `,` + +error: expected parameter name, found `{` + --> $DIR/feature-gate-view-types.rs:8:39 + | +LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { + | ^ expected parameter name + +error: expected one of `!`, `(`, `)`, `,`, `::`, or `<`, found `.` + --> $DIR/feature-gate-view-types.rs:8:38 + | +LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { + | ^ + | | + | expected one of `!`, `(`, `)`, `,`, `::`, or `<` + | help: missing `,` + +error[E0061]: this function takes 4 arguments but 2 arguments were supplied + --> $DIR/feature-gate-view-types.rs:15:5 + | +LL | bar(&mut foo, &mut foo); + | ^^^-------------------- two arguments are missing + | +note: function defined here + --> $DIR/feature-gate-view-types.rs:8:4 + | +LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { + | ^^^ ----------------- +help: provide the arguments + | +LL | bar(&mut foo, &mut foo, /* &mut Foo */, /* _ */); + | +++++++++++++++++++++++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0061`. From 72a59e73d87f7219ad4a865f0ed28e3e55060b22 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 29 Apr 2026 11:17:05 +0000 Subject: [PATCH 38/68] move fragment printing code --- compiler/rustc_expand/src/expand.rs | 32 --------------------------- compiler/rustc_expand/src/stats.rs | 34 +++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 53bb7c9c03857..7dbf6e113c53a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -258,38 +258,6 @@ ast_fragments! { } } -impl AstFragment { - pub(crate) fn to_string(&self) -> String { - match self { - AstFragment::OptExpr(Some(expr)) - | AstFragment::MethodReceiverExpr(expr) - | AstFragment::Expr(expr) => pprust::expr_to_string(expr), - AstFragment::Pat(ast) => pprust::pat_to_string(ast), - AstFragment::Ty(ast) => pprust::ty_to_string(ast), - AstFragment::Stmts(ast) => elems_to_string(ast, pprust::stmt_to_string), - AstFragment::Items(ast) => elems_to_string(ast, |ast| pprust::item_to_string(ast)), - AstFragment::TraitItems(ast) - | AstFragment::ImplItems(ast) - | AstFragment::TraitImplItems(ast) => { - elems_to_string(ast, |ast| pprust::assoc_item_to_string(ast)) - } - AstFragment::ForeignItems(ast) => { - elems_to_string(ast, |ast| pprust::foreign_item_to_string(ast)) - } - AstFragment::OptExpr(None) - | AstFragment::Crate(_) - | AstFragment::Arms(_) - | AstFragment::ExprFields(_) - | AstFragment::PatFields(_) - | AstFragment::GenericParams(_) - | AstFragment::Params(_) - | AstFragment::FieldDefs(_) - | AstFragment::Variants(_) - | AstFragment::WherePredicates(_) => unreachable!(), - } - } -} - pub enum SupportsMacroExpansion { No, Yes { supports_inner_attrs: bool }, diff --git a/compiler/rustc_expand/src/stats.rs b/compiler/rustc_expand/src/stats.rs index ef1668e4af25c..d7bd5329da8b3 100644 --- a/compiler/rustc_expand/src/stats.rs +++ b/compiler/rustc_expand/src/stats.rs @@ -32,6 +32,36 @@ pub(crate) fn elems_to_string(elems: &SmallVec<[T; 1]>, f: impl Fn(&T) -> Str s } +fn fragment_to_string(fragment: &AstFragment) -> String { + match fragment { + AstFragment::OptExpr(Some(expr)) + | AstFragment::MethodReceiverExpr(expr) + | AstFragment::Expr(expr) => pprust::expr_to_string(expr), + AstFragment::Pat(ast) => pprust::pat_to_string(ast), + AstFragment::Ty(ast) => pprust::ty_to_string(ast), + AstFragment::Stmts(ast) => elems_to_string(ast, pprust::stmt_to_string), + AstFragment::Items(ast) => elems_to_string(ast, |ast| pprust::item_to_string(ast)), + AstFragment::TraitItems(ast) + | AstFragment::ImplItems(ast) + | AstFragment::TraitImplItems(ast) => { + elems_to_string(ast, |ast| pprust::assoc_item_to_string(ast)) + } + AstFragment::ForeignItems(ast) => { + elems_to_string(ast, |ast| pprust::foreign_item_to_string(ast)) + } + AstFragment::OptExpr(None) + | AstFragment::Crate(_) + | AstFragment::Arms(_) + | AstFragment::ExprFields(_) + | AstFragment::PatFields(_) + | AstFragment::GenericParams(_) + | AstFragment::Params(_) + | AstFragment::FieldDefs(_) + | AstFragment::Variants(_) + | AstFragment::WherePredicates(_) => unreachable!(), + } +} + pub(crate) fn update_bang_macro_stats( ecx: &mut ExtCtxt<'_>, fragment_kind: AstFragmentKind, @@ -94,7 +124,7 @@ pub(crate) fn update_attr_macro_stats( let input = format!( "{}\n{}", pprust::attribute_to_string(attr), - fragment_kind.expect_from_annotatables(iter::once(item)).to_string(), + fragment_to_string(&fragment_kind.expect_from_annotatables(iter::once(item))), ); update_macro_stats(ecx, MacroKind::Attr, fragment_kind, span, path, &input, fragment); } @@ -125,7 +155,7 @@ pub(crate) fn update_macro_stats( // Measure the size of the output by pretty-printing it and counting // the lines and bytes. let name = Symbol::intern(&pprust::path_to_string(path)); - let output = fragment.to_string(); + let output = fragment_to_string(fragment); let num_lines = output.trim_end().split('\n').count(); let num_bytes = output.len(); From 77fb8d1d5c5f5e2058049b3b90abde3f8bccef4f Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Wed, 29 Apr 2026 09:03:26 +0000 Subject: [PATCH 39/68] Parse view type syntax, mark it as unstable --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_parse/src/parser/ty.rs | 21 ++++++- .../feature-gates/feature-gate-view-types.rs | 5 +- .../feature-gate-view-types.stderr | 58 +++++++------------ 4 files changed, 45 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 5831636a81b2c..3d51770f6ba79 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -510,6 +510,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { 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!(view_types, "view types 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 diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 072975f445bf4..b5151cf20ab02 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -19,7 +19,7 @@ use crate::errors::{ NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; use crate::parser::item::FrontMatterParsingMode; -use crate::parser::{FnContext, FnParseMode}; +use crate::parser::{ExpTokenPair, FnContext, FnParseMode}; use crate::{exp, maybe_recover_from_interpolated_ty_qpath}; /// Signals whether parsing a type should allow `+`. @@ -768,6 +768,25 @@ impl<'a> Parser<'a> { self.bump_with((dyn_tok, dyn_tok_sp)); } let ty = self.parse_ty_no_plus()?; + if self.token == TokenKind::Dot && self.look_ahead(1, |t| t.kind == TokenKind::OpenBrace) { + // & [mut] . { } + // ^ + // we are here + let view_start_span = self.token.span; + self.bump(); + let fields = self + .parse_delim_comma_seq( + ExpTokenPair { tok: TokenKind::OpenBrace, token_type: TokenType::OpenBrace }, + ExpTokenPair { tok: TokenKind::CloseBrace, token_type: TokenType::CloseBrace }, + |p| p.parse_ident(), + )? + .0; + // FIXME(scrabsha): actually propagate field view in the AST. + let _ = fields; + let view_end_span = self.prev_token.span; + let span = view_start_span.to(view_end_span); + self.psess.gated_spans.gate(sym::view_types, span); + } Ok(match pinned { Pinnedness::Not => TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }), Pinnedness::Pinned => TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl }), diff --git a/tests/ui/feature-gates/feature-gate-view-types.rs b/tests/ui/feature-gates/feature-gate-view-types.rs index 26ca0ee4206b2..eb0c26d61db4a 100644 --- a/tests/ui/feature-gates/feature-gate-view-types.rs +++ b/tests/ui/feature-gates/feature-gate-view-types.rs @@ -1,11 +1,11 @@ -//@ known-bug: #155938 - struct Foo { a: usize, b: usize, } fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { + //~^ ERROR view types are experimental + //~| ERROR view types are experimental a.a += 1; b.b += 1; } @@ -13,4 +13,5 @@ fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { fn main() { let mut foo = Foo { a: 0, b: 0 }; bar(&mut foo, &mut foo); + //~^ ERROR cannot borrow `foo` as mutable more than once at a time } diff --git a/tests/ui/feature-gates/feature-gate-view-types.stderr b/tests/ui/feature-gates/feature-gate-view-types.stderr index 436b7ea769ed3..661783ec59202 100644 --- a/tests/ui/feature-gates/feature-gate-view-types.stderr +++ b/tests/ui/feature-gates/feature-gate-view-types.stderr @@ -1,49 +1,33 @@ -error: expected parameter name, found `{` - --> $DIR/feature-gate-view-types.rs:8:20 +error[E0658]: view types are experimental + --> $DIR/feature-gate-view-types.rs:6:19 | LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { - | ^ expected parameter name - -error: expected one of `!`, `(`, `)`, `,`, `::`, or `<`, found `.` - --> $DIR/feature-gate-view-types.rs:8:19 + | ^^^^^^ | -LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { - | ^ - | | - | expected one of `!`, `(`, `)`, `,`, `::`, or `<` - | help: missing `,` + = note: see issue #155938 for more information + = help: add `#![feature(view_types)]` 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: expected parameter name, found `{` - --> $DIR/feature-gate-view-types.rs:8:39 +error[E0658]: view types are experimental + --> $DIR/feature-gate-view-types.rs:6:38 | LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { - | ^ expected parameter name - -error: expected one of `!`, `(`, `)`, `,`, `::`, or `<`, found `.` - --> $DIR/feature-gate-view-types.rs:8:38 + | ^^^^^^ | -LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { - | ^ - | | - | expected one of `!`, `(`, `)`, `,`, `::`, or `<` - | help: missing `,` + = note: see issue #155938 for more information + = help: add `#![feature(view_types)]` 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[E0061]: this function takes 4 arguments but 2 arguments were supplied - --> $DIR/feature-gate-view-types.rs:15:5 +error[E0499]: cannot borrow `foo` as mutable more than once at a time + --> $DIR/feature-gate-view-types.rs:15:19 | LL | bar(&mut foo, &mut foo); - | ^^^-------------------- two arguments are missing - | -note: function defined here - --> $DIR/feature-gate-view-types.rs:8:4 - | -LL | fn bar(a: &mut Foo.{ a }, b: &mut Foo.{ b }) { - | ^^^ ----------------- -help: provide the arguments - | -LL | bar(&mut foo, &mut foo, /* &mut Foo */, /* _ */); - | +++++++++++++++++++++++++ + | --- -------- ^^^^^^^^ second mutable borrow occurs here + | | | + | | first mutable borrow occurs here + | first borrow later used by call -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0061`. +Some errors have detailed explanations: E0499, E0658. +For more information about an error, try `rustc --explain E0499`. From 41796ecf1c75fc00119097b4545939f21979e01a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 29 Apr 2026 21:54:15 +0200 Subject: [PATCH 40/68] add `c_variadic_experimental_arch` feature --- compiler/rustc_abi/src/extern_abi.rs | 1 + .../rustc_ast_passes/src/ast_validation.rs | 23 ++++++--- compiler/rustc_codegen_llvm/src/va_arg.rs | 10 +++- compiler/rustc_feature/src/unstable.rs | 3 ++ compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_target/src/spec/mod.rs | 49 ++++++++++++++----- ...te-c_variadic_experimental_arch.avr.stderr | 23 +++++++++ ...c_variadic_experimental_arch.custom.stderr | 14 ++++++ ...e-c_variadic_experimental_arch.m68k.stderr | 23 +++++++++ ...c_variadic_experimental_arch.msp430.stderr | 23 +++++++++ ...variadic_experimental_arch.riscv32e.stderr | 23 +++++++++ ...ature-gate-c_variadic_experimental_arch.rs | 45 +++++++++++++++++ ...-c_variadic_experimental_arch.sparc.stderr | 23 +++++++++ 13 files changed, 242 insertions(+), 19 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.avr.stderr create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.custom.stderr create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.m68k.stderr create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.msp430.stderr create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.riscv32e.stderr create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.rs create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.sparc.stderr diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index 95ba2ee3b78f8..0a7519625efb6 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -259,6 +259,7 @@ impl StableOrd for ExternAbi { rustc_error_messages::into_diag_arg_using_display!(ExternAbi); #[cfg(feature = "nightly")] +#[derive(Debug)] pub enum CVariadicStatus { NotSupported, Stable, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 770189d8c7999..cdcef59419f6f 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -762,12 +762,23 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, FnCtxt::Free | FnCtxt::Assoc(_) => { - if !self.sess.target.supports_c_variadic_definitions() { - self.dcx().emit_err(errors::CVariadicNotSupported { - variadic_span: variadic_param.span, - target: &*self.sess.target.llvm_target, - }); - return; + match self.sess.target.supports_c_variadic_definitions() { + CVariadicStatus::NotSupported => { + self.dcx().emit_err(errors::CVariadicNotSupported { + variadic_span: variadic_param.span, + target: &*self.sess.target.llvm_target, + }); + return; + } + CVariadicStatus::Unstable { feature } if !self.features.enabled(feature) => { + let msg = + format!("C-variadic function definitions on this target are unstable"); + feature_err(&self.sess, feature, variadic_param.span, msg).emit(); + return; + } + CVariadicStatus::Unstable { .. } | CVariadicStatus::Stable => { + /* fall through */ + } } match sig.header.ext { diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index cfdd47b3a8c8c..09d3a5a3f040f 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,4 +1,4 @@ -use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size}; +use rustc_abi::{Align, BackendRepr, CVariadicStatus, Endian, HasDataLayout, Primitive, Size}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; @@ -1038,6 +1038,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( assert!(!bx.layout_of(target_ty).is_zst()); let target = &bx.cx.tcx.sess.target; + let stability = target.supports_c_variadic_definitions(); + match target.arch { Arch::X86 => emit_ptr_va_arg( bx, @@ -1094,6 +1096,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( ForceRightAdjust::Yes, ), Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => { + std::assert_matches!(stability, CVariadicStatus::Unstable { .. }); // FIXME: clang manually adjusts the alignment for this ABI. It notes: // // > To be compatible with GCC's behaviors, we force arguments with @@ -1215,10 +1218,15 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::SpirV => bug!("spirv does not support c-variadic functions"), Arch::Sparc | Arch::Avr | Arch::M68k | Arch::Msp430 => { + std::assert_matches!(stability, CVariadicStatus::Unstable { .. }); + // Clang uses the LLVM implementation for these architectures. bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx)) } + Arch::Other(ref arch) => { + std::assert_matches!(stability, CVariadicStatus::Unstable { .. }); + // Just to be safe we error out explicitly here, instead of crossing our fingers that // the default LLVM implementation has the correct behavior for this target. bug!("c-variadic functions are not currently implemented for custom target {arch}") diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 3bb4bc863def2..13ef1da6a8060 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -393,6 +393,9 @@ declare_features! ( (unstable, bpf_target_feature, "1.54.0", Some(150247)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), + /// Allows defining c-variadic functions on targets where this feature has not yet + /// undergone sufficient testing for stabilization. + (unstable, c_variadic_experimental_arch, "CURRENT_RUSTC_VERSION", Some(155973)), /// Allows defining c-variadic naked functions with any extern ABI that is allowed /// on c-variadic foreign functions. (unstable, c_variadic_naked_functions, "1.93.0", Some(148767)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4cacdbd3408a5..64a1890726309 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -548,6 +548,7 @@ symbols! { c_str_literals, c_unwind, c_variadic, + c_variadic_experimental_arch, c_variadic_naked_functions, c_void, call, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 768e43146a0c1..9da621c300d8f 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -47,7 +47,8 @@ use std::str::FromStr; use std::{fmt, io}; use rustc_abi::{ - Align, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutError, + Align, CVariadicStatus, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, + TargetDataLayoutError, }; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_error_messages::{DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; @@ -2208,10 +2209,13 @@ impl Target { Ok(dl) } - pub fn supports_c_variadic_definitions(&self) -> bool { + pub fn supports_c_variadic_definitions(&self) -> CVariadicStatus { use Arch::*; match self.arch { + // These targets just inherently do not support c-variadic definitions. + Bpf | SpirV => CVariadicStatus::NotSupported, + // The c-variadic ABI for this target may change in the future, per this comment in // clang: // @@ -2219,19 +2223,40 @@ impl Target { // > 2×XLEN-bit alignment and size at most 2×XLEN bits like `long long`, // > `unsigned long long` and `double` to have 4-byte alignment. This // > behavior may be changed when RV32E/ILP32E is ratified. - RiscV32 if self.llvm_abiname == LlvmAbi::Ilp32e => false, - - // These targets just do not support c-variadic definitions. - Bpf | SpirV => false, + RiscV32 if self.llvm_abiname == LlvmAbi::Ilp32e => { + CVariadicStatus::Unstable { feature: sym::c_variadic_experimental_arch } + } // We don't know how c-variadics work for this target. Using the default LLVM - // fallback implementation may work, but just to be safe we disallow this. - Other(_) => false, + // fallback implementation probably works, but we can't guarantee it. + Other(_) => CVariadicStatus::Unstable { feature: sym::c_variadic_experimental_arch }, - AArch64 | AmdGpu | Arm | Arm64EC | Avr | CSky | Hexagon | LoongArch32 | LoongArch64 - | M68k | Mips | Mips32r6 | Mips64 | Mips64r6 | Msp430 | Nvptx64 | PowerPC - | PowerPC64 | RiscV32 | RiscV64 | S390x | Sparc | Sparc64 | Wasm32 | Wasm64 | X86 - | X86_64 | Xtensa => true, + // These targets require more testing before we commit to c-variadic definitions + // being stable. + // + // To stabilize c-variadic functions for one of these targets, the following + // requirements must be met: + // + // - Check that `core::ffi::VaArgSafe` is (un)implemented for all the correct types. + // - Add an assembly test to `tests/assembly-llvm/c-variadic` that tests the assembly + // for all implementers of `VaArgSafe`. The generated assembly should either match + // `clang`, or we should understand and document why it deviates. + // - Ensure that `va_arg` is implemented in rustc. For stable targets we don't rely on + // the LLVM implementation, it has historically caused miscompilations. + // - The `tests/ui/c-variadic/roundtrip.rs` test must pass for the target. It may + // need slight modifications for embedded targets, that's fine. + // - Check that calling c-variadic functions defined in Rust can be called from C. + // For most targets `tests/run-make/c-link-to-rust-va-list-fn` can be used here. + // For no_std targets a manual setup may be needed. + Sparc | Avr | M68k | Msp430 => { + CVariadicStatus::Unstable { feature: sym::c_variadic_experimental_arch } + } + + AArch64 | AmdGpu | Arm | Arm64EC | CSky | Hexagon | LoongArch32 | LoongArch64 + | Mips | Mips32r6 | Mips64 | Mips64r6 | Nvptx64 | PowerPC | PowerPC64 | RiscV32 + | RiscV64 | S390x | Sparc64 | Wasm32 | Wasm64 | X86 | X86_64 | Xtensa => { + CVariadicStatus::Stable + } } } } diff --git a/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.avr.stderr b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.avr.stderr new file mode 100644 index 0000000000000..df7697845814e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.avr.stderr @@ -0,0 +1,23 @@ +error[E0658]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:39:39 + | +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:43:45 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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-c_variadic_experimental_arch.custom.stderr b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.custom.stderr new file mode 100644 index 0000000000000..fd9d7777a3886 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.custom.stderr @@ -0,0 +1,14 @@ +error: requires `va_list` lang_item + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:26:39 + | +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^ + +error: requires `va_list` lang_item + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:30:45 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.m68k.stderr b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.m68k.stderr new file mode 100644 index 0000000000000..df7697845814e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.m68k.stderr @@ -0,0 +1,23 @@ +error[E0658]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:39:39 + | +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:43:45 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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-c_variadic_experimental_arch.msp430.stderr b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.msp430.stderr new file mode 100644 index 0000000000000..df7697845814e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.msp430.stderr @@ -0,0 +1,23 @@ +error[E0658]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:39:39 + | +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:43:45 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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-c_variadic_experimental_arch.riscv32e.stderr b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.riscv32e.stderr new file mode 100644 index 0000000000000..df7697845814e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.riscv32e.stderr @@ -0,0 +1,23 @@ +error[E0658]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:39:39 + | +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:43:45 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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-c_variadic_experimental_arch.rs b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.rs new file mode 100644 index 0000000000000..98b5f063d5844 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.rs @@ -0,0 +1,45 @@ +//@ add-minicore +//@ ignore-backends: gcc +// +//@ revisions: riscv32e sparc avr m68k msp430 +// +//@[riscv32e] compile-flags: --target riscv32e-unknown-none-elf +//@[riscv32e] needs-llvm-components: riscv +// +//@[sparc] compile-flags: --target sparc-unknown-none-elf +//@[sparc] needs-llvm-components: sparc +// +//@[avr] compile-flags: --target avr-none -Ctarget-cpu=atmega328p +//@[avr] needs-llvm-components: avr +// +//@[m68k] compile-flags: --target m68k-unknown-none-elf -Ctarget-cpu=M68020 +//@[m68k] needs-llvm-components: m68k +// +//@[msp430] compile-flags: --target msp430-none-elf -Ctarget-cpu=msp430 +//@[msp430] needs-llvm-components: msp430 +#![feature(no_core, lang_items, rustc_attrs, c_variadic)] +#![crate_type = "rlib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +pub unsafe extern "C" fn test(_: i32, ap: ...) {} +//~^ ERROR C-variadic function definitions on this target are unstable + +trait Trait { + unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + //~^ ERROR C-variadic function definitions on this target are unstable +} diff --git a/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.sparc.stderr b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.sparc.stderr new file mode 100644 index 0000000000000..df7697845814e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic_experimental_arch.sparc.stderr @@ -0,0 +1,23 @@ +error[E0658]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:39:39 + | +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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]: C-variadic function definitions on this target are unstable + --> $DIR/feature-gate-c_variadic_experimental_arch.rs:43:45 + | +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^ + | + = note: see issue #155973 for more information + = help: add `#![feature(c_variadic_experimental_arch)]` 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`. From 6d79bc6f0153a9784520667e8b7ad5a9ed6ff942 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 28 Apr 2026 10:46:27 +1000 Subject: [PATCH 41/68] Move `feature*` methods from `parse` mod to `errors` mod. As the FIXME comment says, these no longer use `ParseSess` and so the `parse` mod is not a good place for them. The `errors` mod is a better home. --- compiler/rustc_ast_lowering/src/asm.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 2 +- compiler/rustc_ast_lowering/src/path.rs | 2 +- compiler/rustc_ast_lowering/src/stability.rs | 2 +- .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- .../rustc_attr_parsing/src/attributes/cfg.rs | 3 +- .../src/attributes/codegen_attrs.rs | 2 +- .../rustc_attr_parsing/src/attributes/doc.rs | 2 +- .../src/attributes/link_attrs.rs | 2 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 2 +- .../rustc_codegen_ssa/src/target_features.rs | 2 +- .../rustc_const_eval/src/check_consts/ops.rs | 2 +- compiler/rustc_expand/src/config.rs | 2 +- compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 3 +- compiler/rustc_expand/src/mbe/quoted.rs | 2 +- compiler/rustc_hir_analysis/src/check/mod.rs | 4 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../rustc_hir_analysis/src/coherence/mod.rs | 2 +- .../src/collect/resolve_bound_vars.rs | 2 +- .../rustc_hir_analysis/src/collect/type_of.rs | 2 +- .../src/hir_ty_lowering/errors.rs | 2 +- .../src/hir_ty_lowering/mod.rs | 2 +- compiler/rustc_hir_analysis/src/lib.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 5 +- compiler/rustc_hir_typeck/src/pat.rs | 2 +- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_lint/src/levels.rs | 2 +- compiler/rustc_lint/src/lints.rs | 2 +- compiler/rustc_middle/src/middle/stability.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_resolve/src/ident.rs | 2 +- compiler/rustc_resolve/src/imports.rs | 2 +- compiler/rustc_resolve/src/late.rs | 2 +- compiler/rustc_resolve/src/macros.rs | 2 +- compiler/rustc_session/src/errors.rs | 169 ++++++++++++++++- compiler/rustc_session/src/parse.rs | 176 +----------------- compiler/rustc_session/src/session.rs | 4 +- .../src/error_reporting/traits/ambiguity.rs | 2 +- .../rustc_trait_selection/src/traits/wf.rs | 2 +- .../passes/collect_intra_doc_links.rs | 2 +- 43 files changed, 216 insertions(+), 220 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 46d7beae8fe58..e8df8ce6e6dc3 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::msg; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::{Span, sym}; use rustc_target::asm; diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4e1b46d568099..86eb721ad284c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1693,7 +1693,7 @@ impl<'hir> LoweringContext<'_, 'hir> { && !self.tcx.features().coroutines() && !self.tcx.features().gen_blocks() { - rustc_session::parse::feature_err( + rustc_session::errors::feature_err( &self.tcx.sess, sym::yield_expr, span, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 6ca6bf3e1f6dc..49790c2da6de4 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -62,7 +62,7 @@ use rustc_macros::extension; use rustc_middle::hir::{self as mid_hir}; use rustc_middle::span_bug; use rustc_middle::ty::{DelegationInfo, ResolverAstLowering, TyCtxt}; -use rustc_session::parse::add_feature_diagnostics; +use rustc_session::errors::add_feature_diagnostics; use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{DUMMY_SP, DesugaringKind, Span}; use smallvec::SmallVec; diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 49c5f060e2a1e..ef60dc6fc4f1c 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -6,7 +6,7 @@ use rustc_hir::def::{DefKind, PartialRes, PerNS, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg}; use rustc_middle::{span_bug, ty}; -use rustc_session::parse::add_feature_diagnostics; +use rustc_session::errors::add_feature_diagnostics; use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use smallvec::smallvec; use tracing::{debug, instrument}; diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs index d185cc9163eb6..326576bb14881 100644 --- a/compiler/rustc_ast_lowering/src/stability.rs +++ b/compiler/rustc_ast_lowering/src/stability.rs @@ -3,7 +3,7 @@ use std::fmt; use rustc_abi::ExternAbi; use rustc_feature::Features; use rustc_session::Session; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 770189d8c7999..8ba2d955a80ad 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -30,11 +30,11 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagCtxtHandle, Diagnostic, LintBuffer}; use rustc_feature::Features; use rustc_session::Session; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES, }; -use rustc_session::parse::feature_err; use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 5831636a81b2c..54de8965e6a03 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -6,7 +6,7 @@ use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Feat use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; use rustc_session::Session; -use rustc_session::parse::{feature_err, feature_warn}; +use rustc_session::errors::{feature_err, feature_warn}; use rustc_span::{Span, Spanned, Symbol, sym}; use thin_vec::ThinVec; diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 80a1d9aa5defd..4cc07ceaf231f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -13,8 +13,9 @@ use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::{exp, parse_in}; use rustc_session::Session; use rustc_session::config::ExpectedValues; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::UNEXPECTED_CFGS; -use rustc_session::parse::{ParseSess, feature_err}; +use rustc_session::parse::ParseSess; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use thin_vec::ThinVec; diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index a20813406b024..a6cf25330b557 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,5 +1,5 @@ use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy}; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::edition::Edition::Edition2024; use super::prelude::*; diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index c0b90c2c6d97f..ecb136edad5fc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -5,7 +5,7 @@ use rustc_hir::Target; use rustc_hir::attrs::{ AttributeKind, CfgEntry, CfgHideShow, CfgInfo, DocAttribute, DocInline, HideOrShow, }; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::{Span, Symbol, edition, sym}; use thin_vec::ThinVec; diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 0ca059bf4030b..039977c74c1bb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -3,8 +3,8 @@ use rustc_feature::Features; use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; use rustc_hir::attrs::*; use rustc_session::Session; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT; -use rustc_session::parse::feature_err; use rustc_span::edition::Edition::Edition2024; use rustc_span::kw; use rustc_target::spec::{Arch, BinaryFormat}; diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 820436bb6a265..3e19f7f95470f 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -12,8 +12,8 @@ use rustc_middle::middle::codegen_fn_attrs::{ use rustc_middle::mono::Visibility; use rustc_middle::query::Providers; use rustc_middle::ty::{self as ty, TyCtxt}; +use rustc_session::errors::feature_err; use rustc_session::lint; -use rustc_session::parse::feature_err; use rustc_span::{Span, sym}; use rustc_target::spec::Os; diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 4b1b0866f2eb9..b7d09d69aaecd 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -7,8 +7,8 @@ use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::Session; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON; -use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, edit_distance, sym}; use rustc_target::spec::{Arch, SanitizerSet}; use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index d64cf8e032935..c776fa69ba5c1 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{ self, AssocContainer, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef, Param, TraitRef, Ty, suggest_constraining_type_param, }; -use rustc_session::parse::add_feature_diagnostics; +use rustc_session::errors::add_feature_diagnostics; use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::traits::call_kind::{ CallDesugaringKind, CallKind, call_kind, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index b5f85536d9cac..2b914a2664fe5 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -27,7 +27,7 @@ use rustc_hir::{ }; use rustc_parse::parser::Recovery; use rustc_session::Session; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::instrument; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index fe363e7d4a511..3629b72a93ec2 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -30,8 +30,8 @@ use rustc_parse::parser::{ RecoverColon, RecoverComma, Recovery, token_descr, }; use rustc_session::Session; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; -use rustc_session::parse::feature_err; use rustc_span::hygiene::SyntaxContext; use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym}; use smallvec::SmallVec; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index eed13a13fa911..cb22ad136645b 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -23,7 +23,8 @@ use rustc_lint_defs::builtin::{ use rustc_parse::exp; use rustc_parse::parser::{Parser, Recovery}; use rustc_session::Session; -use rustc_session::parse::{ParseSess, feature_err}; +use rustc_session::errors::feature_err; +use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol, kw, sym}; diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 92d19820848b4..443a17287d734 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -5,7 +5,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_feature::Features; use rustc_session::Session; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::edition::Edition; use rustc_span::{Ident, Span, kw, sym}; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index fcdbb11b297c1..3225e00b24b26 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -92,7 +92,7 @@ use rustc_middle::ty::{ Unnormalized, }; use rustc_middle::{bug, span_bug}; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; @@ -312,7 +312,7 @@ fn default_body_is_unstable( }); let inject_span = item_did.is_local().then(|| tcx.crate_level_attribute_injection_span()); - rustc_session::parse::add_feature_diagnostics_for_issue( + rustc_session::errors::add_feature_diagnostics_for_issue( &mut err, &tcx.sess, feature, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 05d16d2ddb49c..d0277d2e24264 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -27,7 +27,7 @@ use rustc_middle::ty::{ Unnormalized, Upcast, }; use rustc_middle::{bug, span_bug}; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::regions::{InferCtxtRegionExt, OutlivesEnvironmentBuildExt}; diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 07ad5db47b6d8..05a9f68131819 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -11,7 +11,7 @@ use rustc_hir::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, elaborate}; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::{ErrorGuaranteed, sym}; use tracing::debug; diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 3a2f3948f0abc..d8cf13db265fd 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -1272,7 +1272,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { && !self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id).is_async() && !self.tcx.features().anonymous_lifetime_in_impl_trait() { - let mut diag: rustc_errors::Diag<'_> = rustc_session::parse::feature_err( + let mut diag: rustc_errors::Diag<'_> = rustc_session::errors::feature_err( &self.tcx.sess, sym::anonymous_lifetime_in_impl_trait, lifetime_ref.ident.span, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 41b1ec91e0e75..3ac603fc715f3 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -505,7 +505,7 @@ fn infer_placeholder_type<'tcx>( fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { if !tcx.features().inherent_associated_types() { - use rustc_session::parse::feature_err; + use rustc_session::errors::feature_err; use rustc_span::sym; feature_err( &tcx.sess, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 0aa478f99200b..5e11819805e15 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -16,7 +16,7 @@ use rustc_middle::ty::{ self, AdtDef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, suggest_constraining_type_param, }; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5096e5ef76f0d..75ea42a38ff4c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -43,8 +43,8 @@ use rustc_middle::ty::{ const_lit_matches_ty, fold_regions, }; use rustc_middle::{bug, span_bug}; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; -use rustc_session::parse::feature_err; use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 0721bfcab519a..9cadaef8f886b 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -89,7 +89,7 @@ use rustc_middle::mir::interpret::GlobalId; use rustc_middle::query::Providers; use rustc_middle::ty::{Const, Ty, TyCtxt}; use rustc_middle::{middle, ty}; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits; diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 71c02dc32f6b5..664c3c457876c 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -30,8 +30,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, AdtKind, GenericArgsRef, Ty, TypeVisitableExt, Unnormalized}; use rustc_middle::{bug, span_bug}; -use rustc_session::errors::ExprParenthesesNeeded; -use rustc_session::parse::feature_err; +use rustc_session::errors::{ExprParenthesesNeeded, feature_err}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; use rustc_span::{Ident, Span, Spanned, Symbol, kw, sym}; @@ -3767,7 +3766,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block); if !self.tcx.features().offset_of_enum() { - rustc_session::parse::feature_err( + rustc_session::errors::feature_err( &self.tcx.sess, sym::offset_of_enum, ident.span, diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5a4cd6d24a8b6..d7f15222376c8 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -21,8 +21,8 @@ use rustc_infer::infer::RegionVariableOrigin; use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt, Unnormalized}; use rustc_middle::{bug, span_bug}; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym}; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 38d899853cd5b..cc05fffa1daa2 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -39,8 +39,8 @@ use rustc_resolve::{Resolver, ResolverOutputs}; use rustc_session::Session; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; +use rustc_session::errors::feature_err; use rustc_session::output::{filename_for_input, invalid_output_for_target}; -use rustc_session::parse::feature_err; use rustc_session::search_paths::PathKind; use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 2b859b65c9f8f..0c308e18b1533 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -966,7 +966,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let mut lint = Diag::new(dcx, level, msg!("unknown lint: `{$name}`")) .with_arg("name", lint_id.lint.name_lower()) .with_note(msg!("the `{$name}` lint is unstable")); - rustc_session::parse::add_feature_diagnostics_for_issue( + rustc_session::errors::add_feature_diagnostics_for_issue( &mut lint, sess, feature, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1e3ce972a3667..6d9f486f627f6 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -306,7 +306,7 @@ impl<'a> Diagnostic<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { let mut diag = Diag::new(dcx, level, "`#[track_caller]` on async functions is a no-op") .with_span_label(self.label, "this function will not propagate the caller location"); - rustc_session::parse::add_feature_diagnostics( + rustc_session::errors::add_feature_diagnostics( &mut diag, self.session, sym::async_fn_track_caller, diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index f9821c92df369..2c59517847370 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -11,9 +11,9 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, ConstStability, DefaultBodyStability, HirId, Stability}; use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic}; use rustc_session::Session; +use rustc_session::errors::feature_err_issue; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE}; use rustc_session::lint::{DeprecatedSinceKind, Level, Lint}; -use rustc_session::parse::feature_err_issue; use rustc_span::{Span, Symbol, sym}; use tracing::debug; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 831199a59a432..2817cf3020ff9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -36,12 +36,12 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt, TypingMode, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; +use rustc_session::errors::feature_err; use rustc_session::lint; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; -use rustc_session::parse::feature_err; use rustc_span::edition::Edition; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 5fae50041a02d..febdb085f7ee1 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -6,8 +6,8 @@ use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; use rustc_middle::{bug, span_bug}; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; -use rustc_session::parse::feature_err; use rustc_span::edition::Edition; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; use rustc_span::{Ident, Span, kw, sym}; diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 2695655278fb4..5a98a17add701 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -18,11 +18,11 @@ use rustc_hir::def_id::{DefId, LocalDefIdMap}; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::{TyCtxt, Visibility}; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::{ AMBIGUOUS_GLOB_REEXPORTS, EXPORTED_PRIVATE_DEPENDENCIES, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS, }; -use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::LocalExpnId; use rustc_span::{Ident, Span, Symbol, kw, sym}; diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6eb5fe17bc169..014a472c2c1bb 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -32,8 +32,8 @@ use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::ty::{AssocTag, DelegationInfo, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; +use rustc_session::errors::feature_err; use rustc_session::lint; -use rustc_session::parse::feature_err; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Spanned, Symbol, kw, respan, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index dc20d4fcf36b7..998cf27a85de1 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -23,11 +23,11 @@ use rustc_hir::{Attribute, StabilityLevel}; use rustc_middle::middle::stability; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::Session; +use rustc_session::errors::feature_err; use rustc_session::lint::builtin::{ LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNUSED_MACRO_RULES, UNUSED_MACROS, }; -use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 54f88aa22bdc4..bc4c5e98282a5 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -5,14 +5,179 @@ use rustc_ast::util::literal::LitError; use rustc_errors::codes::*; use rustc_errors::{ Diag, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level, - MultiSpan, + MultiSpan, StashKey, }; +use rustc_feature::{GateIssue, find_feature_issue}; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple}; +use crate::Session; +use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION; use crate::parse::ParseSess; +/// Construct a diagnostic for a language feature error due to the given `span`. +/// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbol`. +#[track_caller] +pub fn feature_err( + sess: &Session, + feature: Symbol, + span: impl Into, + explain: impl Into, +) -> Diag<'_> { + feature_err_issue(sess, feature, span, GateIssue::Language, explain) +} + +/// Construct a diagnostic for a feature gate error. +/// +/// This variant allows you to control whether it is a library or language feature. +/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`. +#[track_caller] +pub fn feature_err_issue( + sess: &Session, + feature: Symbol, + span: impl Into, + issue: GateIssue, + explain: impl Into, +) -> Diag<'_> { + let span = span.into(); + + // Cancel an earlier warning for this same error, if it exists. + if let Some(span) = span.primary_span() + && let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) + { + err.cancel() + } + + let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); + err +} + +/// Construct a future incompatibility diagnostic for a feature gate. +/// +/// This diagnostic is only a warning and *does not cause compilation to fail*. +#[track_caller] +pub fn feature_warn(sess: &Session, feature: Symbol, span: Span, explain: &'static str) { + feature_warn_issue(sess, feature, span, GateIssue::Language, explain); +} + +/// Construct a future incompatibility diagnostic for a feature gate. +/// +/// This diagnostic is only a warning and *does not cause compilation to fail*. +/// +/// This variant allows you to control whether it is a library or language feature. +/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`. +#[track_caller] +pub fn feature_warn_issue( + sess: &Session, + feature: Symbol, + span: Span, + issue: GateIssue, + explain: &'static str, +) { + let mut err = sess.dcx().struct_span_warn(span, explain); + add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); + + // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level + let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; + let future_incompatible = lint.future_incompatible.as_ref().unwrap(); + err.is_lint(lint.name_lower(), /* has_future_breakage */ false); + err.warn(lint.desc); + err.note(format!("for more information, see {}", future_incompatible.reason.reference())); + + // A later feature_err call can steal and cancel this warning. + err.stash(span, StashKey::EarlySyntaxWarning); +} + +/// Adds the diagnostics for a feature to an existing error. +/// Must be a language feature! +pub fn add_feature_diagnostics( + err: &mut Diag<'_, G>, + sess: &Session, + feature: Symbol, +) { + add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None); +} + +/// Adds the diagnostics for a feature to an existing error. +/// +/// This variant allows you to control whether it is a library or language feature. +/// Almost always, you want to use this for a language feature. If so, prefer +/// `add_feature_diagnostics`. +pub fn add_feature_diagnostics_for_issue( + err: &mut Diag<'_, G>, + sess: &Session, + feature: Symbol, + issue: GateIssue, + feature_from_cli: bool, + inject_span: Option, +) { + if let Some(n) = find_feature_issue(feature, issue) { + err.subdiagnostic(FeatureDiagnosticForIssue { n }); + } + + // #23973: do not suggest `#![feature(...)]` if we are in beta/stable + if sess.unstable_features.is_nightly_build() { + if feature_from_cli { + err.subdiagnostic(CliFeatureDiagnosticHelp { feature }); + } else if let Some(span) = inject_span { + err.subdiagnostic(FeatureDiagnosticSuggestion { feature, span }); + } else { + err.subdiagnostic(FeatureDiagnosticHelp { feature }); + } + if feature == sym::rustc_attrs { + // We're unlikely to stabilize something out of `rustc_attrs` + // without at least renaming it, so pointing out how old + // the compiler is will do little good. + } else if sess.opts.unstable_opts.ui_testing { + err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); + } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { + err.subdiagnostic(suggestion); + } + } +} + +/// This is only used by unstable_feature_bound as it does not have issue number information for now. +/// This is basically the same as `feature_err_issue` +/// but without the feature issue note. If we can do a lookup for issue number from feature name, +/// then we should directly use `feature_err_issue` for ambiguity error of +/// `#[unstable_feature_bound]`. +#[track_caller] +pub fn feature_err_unstable_feature_bound( + sess: &Session, + feature: Symbol, + span: impl Into, + explain: impl Into, +) -> Diag<'_> { + let span = span.into(); + + // Cancel an earlier warning for this same error, if it exists. + if let Some(span) = span.primary_span() { + if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { + err.cancel() + } + } + + let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); + + // #23973: do not suggest `#![feature(...)]` if we are in beta/stable + if sess.unstable_features.is_nightly_build() { + err.subdiagnostic(FeatureDiagnosticHelp { feature }); + + if feature == sym::rustc_attrs { + // We're unlikely to stabilize something out of `rustc_attrs` + // without at least renaming it, so pointing out how old + // the compiler is will do little good. + } else if sess.opts.unstable_opts.ui_testing { + err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); + } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { + err.subdiagnostic(suggestion); + } + } + err +} + #[derive(Diagnostic)] pub(crate) enum AppleDeploymentTarget { #[diag("failed to parse deployment target specified in {$env_var}: {$error}")] diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index c09e3eed37125..54058e9f5f046 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -1,7 +1,6 @@ //! Contains `ParseSess` which holds state living beyond what one `Parser` might. //! It also serves as an input to the parser itself. -use std::str; use std::sync::Arc; use rustc_ast::attr::AttrIdGenerator; @@ -11,21 +10,15 @@ use rustc_data_structures::sync::{AppendOnlyVec, DynSend, DynSync, Lock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::emitter::{EmitterWithNote, stderr_destination}; use rustc_errors::{ - BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle, - DiagMessage, EmissionGuarantee, Level, MultiSpan, StashKey, + BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle, Level, + MultiSpan, }; -use rustc_feature::{GateIssue, find_feature_issue}; use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnId; use rustc_span::source_map::{FilePathMapping, SourceMap}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol}; use crate::Session; -use crate::errors::{ - CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, - FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler, -}; -use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION; use crate::lint::{Lint, LintId}; /// Collected spans during parsing for places where a certain feature was @@ -78,169 +71,6 @@ impl SymbolGallery { } } -// FIXME: this function now accepts `Session` instead of `ParseSess` and should be relocated -/// Construct a diagnostic for a language feature error due to the given `span`. -/// The `feature`'s `Symbol` is the one you used in `unstable.rs` and `rustc_span::symbol`. -#[track_caller] -pub fn feature_err( - sess: &Session, - feature: Symbol, - span: impl Into, - explain: impl Into, -) -> Diag<'_> { - feature_err_issue(sess, feature, span, GateIssue::Language, explain) -} - -/// Construct a diagnostic for a feature gate error. -/// -/// This variant allows you to control whether it is a library or language feature. -/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`. -#[track_caller] -pub fn feature_err_issue( - sess: &Session, - feature: Symbol, - span: impl Into, - issue: GateIssue, - explain: impl Into, -) -> Diag<'_> { - let span = span.into(); - - // Cancel an earlier warning for this same error, if it exists. - if let Some(span) = span.primary_span() - && let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) - { - err.cancel() - } - - let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); - err -} - -/// Construct a future incompatibility diagnostic for a feature gate. -/// -/// This diagnostic is only a warning and *does not cause compilation to fail*. -#[track_caller] -pub fn feature_warn(sess: &Session, feature: Symbol, span: Span, explain: &'static str) { - feature_warn_issue(sess, feature, span, GateIssue::Language, explain); -} - -/// Construct a future incompatibility diagnostic for a feature gate. -/// -/// This diagnostic is only a warning and *does not cause compilation to fail*. -/// -/// This variant allows you to control whether it is a library or language feature. -/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`. -#[track_caller] -pub fn feature_warn_issue( - sess: &Session, - feature: Symbol, - span: Span, - issue: GateIssue, - explain: &'static str, -) { - let mut err = sess.dcx().struct_span_warn(span, explain); - add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None); - - // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level - let lint = UNSTABLE_SYNTAX_PRE_EXPANSION; - let future_incompatible = lint.future_incompatible.as_ref().unwrap(); - err.is_lint(lint.name_lower(), /* has_future_breakage */ false); - err.warn(lint.desc); - err.note(format!("for more information, see {}", future_incompatible.reason.reference())); - - // A later feature_err call can steal and cancel this warning. - err.stash(span, StashKey::EarlySyntaxWarning); -} - -/// Adds the diagnostics for a feature to an existing error. -/// Must be a language feature! -pub fn add_feature_diagnostics( - err: &mut Diag<'_, G>, - sess: &Session, - feature: Symbol, -) { - add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None); -} - -/// Adds the diagnostics for a feature to an existing error. -/// -/// This variant allows you to control whether it is a library or language feature. -/// Almost always, you want to use this for a language feature. If so, prefer -/// `add_feature_diagnostics`. -pub fn add_feature_diagnostics_for_issue( - err: &mut Diag<'_, G>, - sess: &Session, - feature: Symbol, - issue: GateIssue, - feature_from_cli: bool, - inject_span: Option, -) { - if let Some(n) = find_feature_issue(feature, issue) { - err.subdiagnostic(FeatureDiagnosticForIssue { n }); - } - - // #23973: do not suggest `#![feature(...)]` if we are in beta/stable - if sess.unstable_features.is_nightly_build() { - if feature_from_cli { - err.subdiagnostic(CliFeatureDiagnosticHelp { feature }); - } else if let Some(span) = inject_span { - err.subdiagnostic(FeatureDiagnosticSuggestion { feature, span }); - } else { - err.subdiagnostic(FeatureDiagnosticHelp { feature }); - } - if feature == sym::rustc_attrs { - // We're unlikely to stabilize something out of `rustc_attrs` - // without at least renaming it, so pointing out how old - // the compiler is will do little good. - } else if sess.opts.unstable_opts.ui_testing { - err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); - } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { - err.subdiagnostic(suggestion); - } - } -} - -/// This is only used by unstable_feature_bound as it does not have issue number information for now. -/// This is basically the same as `feature_err_issue` -/// but without the feature issue note. If we can do a lookup for issue number from feature name, -/// then we should directly use `feature_err_issue` for ambiguity error of -/// `#[unstable_feature_bound]`. -#[track_caller] -pub fn feature_err_unstable_feature_bound( - sess: &Session, - feature: Symbol, - span: impl Into, - explain: impl Into, -) -> Diag<'_> { - let span = span.into(); - - // Cancel an earlier warning for this same error, if it exists. - if let Some(span) = span.primary_span() { - if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { - err.cancel() - } - } - - let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); - - // #23973: do not suggest `#![feature(...)]` if we are in beta/stable - if sess.unstable_features.is_nightly_build() { - err.subdiagnostic(FeatureDiagnosticHelp { feature }); - - if feature == sym::rustc_attrs { - // We're unlikely to stabilize something out of `rustc_attrs` - // without at least renaming it, so pointing out how old - // the compiler is will do little good. - } else if sess.opts.unstable_opts.ui_testing { - err.subdiagnostic(SuggestUpgradeCompiler::ui_testing()); - } else if let Some(suggestion) = SuggestUpgradeCompiler::new() { - err.subdiagnostic(suggestion); - } - } - err -} - /// Info about a parsing session. pub struct ParseSess { dcx: DiagCtxt, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 623a246dc1b59..21583a5416277 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -45,7 +45,7 @@ use crate::config::{ }; use crate::filesearch::FileSearch; use crate::lint::LintId; -use crate::parse::{ParseSess, add_feature_diagnostics}; +use crate::parse::ParseSess; use crate::search_paths::SearchPath; use crate::{errors, filesearch, lint}; @@ -282,7 +282,7 @@ impl Session { if err.code.is_none() { err.code(E0658); } - add_feature_diagnostics(&mut err, self, feature); + errors::add_feature_diagnostics(&mut err, self, feature); err } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 1a308ee334d3b..108262d507ef7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -13,7 +13,7 @@ use rustc_infer::traits::{ }; use rustc_middle::ty::print::PrintPolyTraitPredicateExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _, Unnormalized}; -use rustc_session::parse::feature_err_unstable_feature_bound; +use rustc_session::errors::feature_err_unstable_feature_bound; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use tracing::{debug, instrument}; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 66a2fcaa562d1..57b14e93e277c 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{ self, GenericArgsRef, Term, TermKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; -use rustc_session::parse::feature_err; +use rustc_session::errors::feature_err; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{Span, sym}; use tracing::{debug, instrument, trace}; diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 45061a02e7525..eb1dee2e16d7b 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1492,7 +1492,7 @@ impl LinkCollector<'_, '_> { Some((sp, _)) => sp, None => item.attr_span(self.cx.tcx), }; - rustc_session::parse::feature_err( + rustc_session::errors::feature_err( self.cx.tcx.sess, sym::intra_doc_pointers, span, From ee7e4309bea4096c8169f3716fd6a0fb7f9bbbae Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 28 Apr 2026 16:48:50 +1000 Subject: [PATCH 42/68] Remove unnecessary `lift` calls. Various places where `lift` just isn't necessary. Either because we're not within a closure passed to `tls::with`, or because the type being lifted doesn't have a `'tcx` lifetime. --- compiler/rustc_middle/src/mir/pretty.rs | 5 ----- .../rustc_public/src/unstable/convert/internal.rs | 15 +++++++-------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d160aada80a83..26c4d5fbe249d 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1911,8 +1911,6 @@ fn pretty_print_const_value_tcx<'tcx>( // E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized` // to be able to destructure the tuple into `(0u8, *mut T)` (_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => { - let ct = tcx.lift(ct).unwrap(); - let ty = tcx.lift(ty).unwrap(); if let Some(contents) = tcx.try_destructure_mir_constant_for_user_output(ct, ty) { let fields: Vec<(ConstValue, Ty<'_>)> = contents.fields.to_vec(); match *ty.kind() { @@ -1937,7 +1935,6 @@ fn pretty_print_const_value_tcx<'tcx>( .variant .expect("destructed mir constant of adt without variant idx"); let variant_def = &def.variant(variant_idx); - let args = tcx.lift(args).unwrap(); let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); p.print_alloc_ids = true; p.pretty_print_value_path(variant_def.def_id, args)?; @@ -1974,7 +1971,6 @@ fn pretty_print_const_value_tcx<'tcx>( (ConstValue::Scalar(scalar), _) => { let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); p.print_alloc_ids = true; - let ty = tcx.lift(ty).unwrap(); p.pretty_print_const_scalar(scalar, ty)?; fmt.write_str(&p.into_buffer())?; return Ok(()); @@ -2000,7 +1996,6 @@ pub(crate) fn pretty_print_const_value<'tcx>( fmt: &mut Formatter<'_>, ) -> fmt::Result { ty::tls::with(|tcx| { - let ct = tcx.lift(ct).unwrap(); let ty = tcx.lift(ty).unwrap(); pretty_print_const_value_tcx(tcx, ct, ty, fmt) }) diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index dc41bb1f687fe..67099c1cf1c12 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -49,9 +49,9 @@ impl RustcInternal for DefId { fn internal<'tcx>( &self, tables: &mut Tables<'_, BridgeTys>, - tcx: impl InternalCx<'tcx>, + _tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { - tcx.lift(tables.def_ids[*self]).unwrap() + tables.def_ids[*self] } } @@ -78,7 +78,7 @@ impl RustcInternal for GenericArgKind { GenericArgKind::Type(ty) => ty.internal(tables, tcx).into(), GenericArgKind::Const(cnst) => cnst.internal(tables, tcx).into(), }; - tcx.lift(arg).unwrap() + arg } } @@ -316,11 +316,10 @@ impl RustcInternal for FnSig { .set_abi(self.abi.internal(tables, tcx)) .set_safety(self.safety.internal(tables, tcx)) .set_c_variadic(self.c_variadic); - tcx.lift(rustc_ty::FnSig { + rustc_ty::FnSig { inputs_and_output: tcx.mk_type_list(&self.inputs_and_output.internal(tables, tcx)), fn_sig_kind, - }) - .unwrap() + } } } @@ -553,9 +552,9 @@ impl RustcInternal for AllocId { fn internal<'tcx>( &self, tables: &mut Tables<'_, BridgeTys>, - tcx: impl InternalCx<'tcx>, + _tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { - tcx.lift(tables.alloc_ids[*self]).unwrap() + tables.alloc_ids[*self] } } From a762dbfe76f7bd81d6a8c6623b496329bb0b14b1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 30 Apr 2026 11:16:30 +1000 Subject: [PATCH 43/68] Make lifting infallible. Every lifting root calls `unwrap`/`expect` on the result, except for `ImmTy::fmt` but there's no good reason for that exception. Making lifting infallible is sensible because it should only fail if the wrong interner is somehow used, which indicates a major bug in rustc rather than an error condition. --- .../rustc_const_eval/src/interpret/operand.rs | 13 ++++----- compiler/rustc_macros/src/lift.rs | 17 +++++++----- compiler/rustc_middle/src/macros.rs | 4 +-- compiler/rustc_middle/src/mir/consts.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 6 ++--- .../rustc_middle/src/ty/consts/valtree.rs | 3 +-- compiler/rustc_middle/src/ty/context.rs | 27 +++++++++---------- compiler/rustc_middle/src/ty/error.rs | 17 ++++-------- compiler/rustc_middle/src/ty/generic_args.rs | 8 +++--- compiler/rustc_middle/src/ty/instance.rs | 3 +-- compiler/rustc_middle/src/ty/print/mod.rs | 2 +- compiler/rustc_middle/src/ty/print/pretty.rs | 4 +-- .../rustc_middle/src/ty/structural_impls.rs | 13 ++++----- .../src/unstable/convert/internal.rs | 17 +++++------- .../src/unstable/convert/stable/abi.rs | 2 +- .../src/unstable/convert/stable/mir.rs | 6 ++--- .../src/unstable/convert/stable/ty.rs | 6 ++--- .../src/unstable/internal_cx/mod.rs | 2 +- compiler/rustc_public/src/unstable/mod.rs | 2 +- .../rustc_public_bridge/src/context/impls.rs | 2 +- compiler/rustc_type_ir/src/binder.rs | 22 +++++++-------- compiler/rustc_type_ir/src/lift.rs | 6 ++--- compiler/rustc_type_ir/src/predicate.rs | 4 +-- compiler/rustc_type_ir/src/ty_kind.rs | 4 +-- compiler/rustc_type_ir_macros/src/lib.rs | 6 ++--- 25 files changed, 90 insertions(+), 108 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 6c9cd2e608ae1..7b2983620d420 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -216,14 +216,11 @@ impl std::fmt::Display for ImmTy<'_, Prov> { ty::tls::with(|tcx| { match self.imm { Immediate::Scalar(s) => { - if let Some(ty) = tcx.lift(self.layout.ty) { - let s = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| { - print_scalar(p, s, ty) - })?; - f.write_str(&s)?; - return Ok(()); - } - write!(f, "{:x}: {}", s, self.layout.ty) + let ty = tcx.lift(self.layout.ty); + let s = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| { + print_scalar(p, s, ty) + })?; + f.write_str(&s) } Immediate::ScalarPair(a, b) => { // FIXME(oli-obk): at least print tuples and slices nicely diff --git a/compiler/rustc_macros/src/lift.rs b/compiler/rustc_macros/src/lift.rs index 03ea396a42c75..9affbf5f1583b 100644 --- a/compiler/rustc_macros/src/lift.rs +++ b/compiler/rustc_macros/src/lift.rs @@ -34,16 +34,19 @@ pub(super) fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::To let bindings = &vi.bindings(); vi.construct(|_, index| { let bi = &bindings[index]; - quote! { __tcx.lift(#bi)? } + quote! { __tcx.lift(#bi) } }) }); s.add_impl_generic(newtcx); - s.bound_impl(quote!(::rustc_middle::ty::Lift<::rustc_middle::ty::TyCtxt<'__lifted>>), quote! { - type Lifted = #lifted; + s.bound_impl( + quote!(::rustc_middle::ty::Lift<::rustc_middle::ty::TyCtxt<'__lifted>>), + quote! { + type Lifted = #lifted; - fn lift_to_interner(self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> { - Some(match self { #body }) - } - }) + fn lift_to_interner(self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> #lifted { + match self { #body } + } + }, + ) } diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index 0ae774ebee795..3d0971ed1ffd9 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -46,8 +46,8 @@ macro_rules! TrivialLiftImpls { $( impl<'tcx> $crate::ty::Lift<$crate::ty::TyCtxt<'tcx>> for $ty { type Lifted = Self; - fn lift_to_interner(self, _: $crate::ty::TyCtxt<'tcx>) -> Option { - Some(self) + fn lift_to_interner(self, _: $crate::ty::TyCtxt<'tcx>) -> Self { + self } } )+ diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index de3ef6deca1fc..c3dd68fdd9187 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -493,7 +493,7 @@ impl<'tcx> Display for Const<'tcx> { // FIXME(valtrees): Correctly print mir constants. Const::Unevaluated(c, _ty) => { ty::tls::with(move |tcx| { - let c = tcx.lift(c).unwrap(); + let c = tcx.lift(c); // Matches `GlobalId` printing. let instance = with_no_trimmed_paths!(tcx.def_path_str_with_args(c.def, c.args)); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 26c4d5fbe249d..c59e6b6fa2151 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1165,7 +1165,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { AggregateKind::Adt(adt_did, variant, args, _user_ty, _) => { ty::tls::with(|tcx| { let variant_def = &tcx.adt_def(adt_did).variant(variant); - let args = tcx.lift(args).expect("could not lift for printing"); + let args = tcx.lift(args); let name = FmtPrinter::print_string(tcx, Namespace::ValueNS, |p| { p.print_def_path(variant_def.def_id, args) })?; @@ -1187,7 +1187,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { AggregateKind::Closure(def_id, args) | AggregateKind::CoroutineClosure(def_id, args) => ty::tls::with(|tcx| { let name = if tcx.sess.opts.unstable_opts.span_free_formats { - let args = tcx.lift(args).unwrap(); + let args = tcx.lift(args); format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),) } else { let span = tcx.def_span(def_id); @@ -1996,7 +1996,7 @@ pub(crate) fn pretty_print_const_value<'tcx>( fmt: &mut Formatter<'_>, ) -> fmt::Result { ty::tls::with(|tcx| { - let ty = tcx.lift(ty).unwrap(); + let ty = tcx.lift(ty); pretty_print_const_value_tcx(tcx, ct, ty, fmt) }) } diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 50242613b3e7f..8866860661c41 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -238,9 +238,8 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst> for Value<'tcx> { impl<'tcx> fmt::Display for Value<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(move |tcx| { - let cv = tcx.lift(*self).unwrap(); let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); - p.pretty_print_const_valtree(cv, /*print_ty*/ true)?; + p.pretty_print_const_valtree(tcx.lift(*self), /*print_ty*/ true)?; f.write_str(&p.into_buffer()) }) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index e26eb13243762..e394076ea7c5a 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -958,7 +958,7 @@ impl<'tcx> TyCtxt<'tcx> { (start, end) } - pub fn lift>>(self, value: T) -> Option { + pub fn lift>>(self, value: T) -> T::Lifted { value.lift_to_interner(self) } @@ -1689,7 +1689,8 @@ macro_rules! nop_lift { ($set:ident; $ty:ty => $lifted:ty) => { impl<'a, 'tcx> Lift> for $ty { type Lifted = $lifted; - fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { + #[track_caller] + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Self::Lifted { // Assert that the set has the right type. // Given an argument that has an interned type, the return type has the type of // the corresponding interner set. This won't actually return anything, we're @@ -1709,12 +1710,10 @@ macro_rules! nop_lift { _type_eq(&interner, &tcx.interners.$set); } - tcx.interners - .$set - .contains_pointer_to(&InternedInSet(&*self.0.0)) - // SAFETY: `self` is interned and therefore valid - // for the entire lifetime of the `TyCtxt`. - .then(|| unsafe { mem::transmute(self) }) + assert!(tcx.interners.$set.contains_pointer_to(&InternedInSet(&*self.0.0))); + // SAFETY: we just checked that `self` is interned and therefore is valid for the + // entire lifetime of the `TyCtxt`. + unsafe { mem::transmute(self) } } } }; @@ -1724,19 +1723,19 @@ macro_rules! nop_list_lift { ($set:ident; $ty:ty => $lifted:ty) => { impl<'a, 'tcx> Lift> for &'a List<$ty> { type Lifted = &'tcx List<$lifted>; - fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Self::Lifted { // Assert that the set has the right type. if false { let _x: &InternedSet<'tcx, List<$lifted>> = &tcx.interners.$set; } if self.is_empty() { - return Some(List::empty()); + return List::empty(); } - tcx.interners - .$set - .contains_pointer_to(&InternedInSet(self)) - .then(|| unsafe { mem::transmute(self) }) + assert!(tcx.interners.$set.contains_pointer_to(&InternedInSet(self))); + // SAFETY: we just checked that `self` is interned and therefore is valid for the + // entire lifetime of the `TyCtxt`. + unsafe { mem::transmute(self) } } } }; diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index a4c30f1f88434..082e5de16cf98 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -223,10 +223,8 @@ impl<'tcx> TyCtxt<'tcx> { T: Copy + for<'a, 'b> Lift, Lifted: Print<'b, FmtPrinter<'a, 'b>>>, { let mut type_limit = 50; - let regular = FmtPrinter::print_string(self, ns, |p| { - self.lift(t).expect("could not lift for printing").print(p) - }) - .expect("could not write to `String`"); + let regular = FmtPrinter::print_string(self, ns, |p| self.lift(t).print(p)) + .expect("could not write to `String`"); if regular.len() <= length_limit { return regular; } @@ -235,10 +233,7 @@ impl<'tcx> TyCtxt<'tcx> { // Look for the longest properly trimmed path that still fits in length_limit. short = with_forced_trimmed_paths!({ let mut p = FmtPrinter::new_with_limit(self, ns, Limit(type_limit)); - self.lift(t) - .expect("could not lift for printing") - .print(&mut p) - .expect("could not print type"); + self.lift(t).print(&mut p).expect("could not print type"); p.into_buffer() }); if short.len() <= length_limit || type_limit == 0 { @@ -273,10 +268,8 @@ impl<'tcx> TyCtxt<'tcx> { where T: Copy + Hash + for<'a, 'b> Lift, Lifted: Print<'b, FmtPrinter<'a, 'b>>>, { - let regular = FmtPrinter::print_string(self, namespace, |p| { - self.lift(t).expect("could not lift for printing").print(p) - }) - .expect("could not write to `String`"); + let regular = FmtPrinter::print_string(self, namespace, |p| self.lift(t).print(p)) + .expect("could not write to `String`"); if !self.sess.opts.unstable_opts.write_long_types_to_disk || self.sess.opts.verbose { return regular; diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index daeabf24d749f..c57c0851d8a49 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -320,11 +320,11 @@ impl<'tcx> GenericArg<'tcx> { impl<'a, 'tcx> Lift> for GenericArg<'a> { type Lifted = GenericArg<'tcx>; - fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Self::Lifted { match self.kind() { - GenericArgKind::Lifetime(lt) => tcx.lift(lt).map(|lt| lt.into()), - GenericArgKind::Type(ty) => tcx.lift(ty).map(|ty| ty.into()), - GenericArgKind::Const(ct) => tcx.lift(ct).map(|ct| ct.into()), + GenericArgKind::Lifetime(lt) => tcx.lift(lt).into(), + GenericArgKind::Type(ty) => tcx.lift(ty).into(), + GenericArgKind::Const(ct) => tcx.lift(ct).into(), } } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 408edf19dbf23..8166053f9bd5f 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -381,8 +381,7 @@ impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); - let instance = tcx.lift(*self).expect("could not lift for printing"); - instance.print(&mut p)?; + tcx.lift(*self).print(&mut p)?; let s = p.into_buffer(); f.write_str(&s) }) diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index d2ae226c4d8dc..d5d1c3634c7d1 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -418,7 +418,7 @@ where fn print(t: &T, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ty::tls::with(|tcx| { let mut p = FmtPrinter::new(tcx, Namespace::TypeNS); - tcx.lift(*t).expect("could not lift for printing").print(&mut p)?; + tcx.lift(*t).print(&mut p)?; fmt.write_str(&p.into_buffer())?; Ok(()) }) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f997e49868ba8..04d76e4304fe9 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2082,10 +2082,9 @@ pub(crate) fn pretty_print_const<'tcx>( print_types: bool, ) -> fmt::Result { ty::tls::with(|tcx| { - let literal = tcx.lift(c).unwrap(); let mut p = FmtPrinter::new(tcx, Namespace::ValueNS); p.print_alloc_ids = true; - p.pretty_print_const(literal, print_types)?; + p.pretty_print_const(tcx.lift(c), print_types)?; fmt.write_str(&p.into_buffer())?; Ok(()) }) @@ -3098,7 +3097,6 @@ macro_rules! forward_display_to_print { ty::tls::with(|tcx| { let mut p = FmtPrinter::new(tcx, Namespace::TypeNS); tcx.lift(*self) - .expect("could not lift for printing") .print(&mut p)?; f.write_str(&p.into_buffer())?; Ok(()) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 29b784e837954..bc53fc18b41b6 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -271,20 +271,17 @@ TrivialTypeTraversalAndLiftImpls! { impl<'tcx, T: Lift>> Lift> for Option { type Lifted = Option; - fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { - Some(match self { - Some(x) => Some(tcx.lift(x)?), - None => None, - }) + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Self::Lifted { + self.map(|x| tcx.lift(x)) } } impl<'a, 'tcx> Lift> for Term<'a> { type Lifted = ty::Term<'tcx>; - fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_interner(self, tcx: TyCtxt<'tcx>) -> Self::Lifted { match self.kind() { - TermKind::Ty(ty) => tcx.lift(ty).map(Into::into), - TermKind::Const(c) => tcx.lift(c).map(Into::into), + TermKind::Ty(ty) => tcx.lift(ty).into(), + TermKind::Const(c) => tcx.lift(c).into(), } } } diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 67099c1cf1c12..165ad737fccb8 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -101,7 +101,7 @@ impl RustcInternal for Ty { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { - tcx.lift(tables.types[*self]).unwrap() + tcx.lift(tables.types[*self]) } } @@ -112,7 +112,7 @@ impl RustcInternal for TyConst { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { - tcx.lift(tables.ty_consts[self.id]).unwrap() + tcx.lift(tables.ty_consts[self.id]) } } @@ -357,16 +357,13 @@ impl RustcInternal for MirConst { let constant = tables.mir_consts[self.id]; match constant { rustc_middle::mir::Const::Ty(ty, ct) => { - rustc_middle::mir::Const::Ty(tcx.lift(ty).unwrap(), tcx.lift(ct).unwrap()) + rustc_middle::mir::Const::Ty(tcx.lift(ty), tcx.lift(ct)) } rustc_middle::mir::Const::Unevaluated(uneval, ty) => { - rustc_middle::mir::Const::Unevaluated( - tcx.lift(uneval).unwrap(), - tcx.lift(ty).unwrap(), - ) + rustc_middle::mir::Const::Unevaluated(tcx.lift(uneval), tcx.lift(ty)) } rustc_middle::mir::Const::Val(const_val, ty) => { - rustc_middle::mir::Const::Val(tcx.lift(const_val).unwrap(), tcx.lift(ty).unwrap()) + rustc_middle::mir::Const::Val(tcx.lift(const_val), tcx.lift(ty)) } } } @@ -399,7 +396,7 @@ impl RustcInternal for Instance { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { - tcx.lift(tables.instances[self.def]).unwrap() + tcx.lift(tables.instances[self.def]) } } @@ -690,7 +687,7 @@ impl RustcInternal for Layout { tables: &mut Tables<'_, BridgeTys>, tcx: impl InternalCx<'tcx>, ) -> Self::T<'tcx> { - tcx.lift(tables.layouts[*self]).unwrap() + tcx.lift(tables.layouts[*self]) } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index d8c4cee7abbe4..56fbd8108786a 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -58,7 +58,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Layout<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - tables.layout_id(cx.lift(*self).unwrap()) + tables.layout_id(cx.lift(*self)) } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 0d04053aab76b..2bc23e2837048 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -864,7 +864,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - let id = tables.intern_mir_const(cx.lift(*self).unwrap()); + let id = tables.intern_mir_const(cx.lift(*self)); match *self { mir::Const::Ty(ty, c) => MirConst::new( crate::ty::ConstantKind::Ty(c.stable(tables, cx)), @@ -885,8 +885,8 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::Const<'tcx> { MirConst::new(ConstantKind::ZeroSized, ty, id) } mir::Const::Val(val, ty) => { - let ty = cx.lift(ty).unwrap(); - let val = cx.lift(val).unwrap(); + let ty = cx.lift(ty); + let val = cx.lift(val); let kind = ConstantKind::Allocated(alloc::new_allocation(ty, val, tables, cx)); let ty = ty.stable(tables, cx); MirConst::new(kind, ty, id) diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 4c4a51f7444bb..80453d838b880 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -408,7 +408,7 @@ impl<'tcx> Stable<'tcx> for Ty<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - tables.intern_ty(cx.lift(*self).unwrap()) + tables.intern_ty(cx.lift(*self)) } } @@ -526,7 +526,7 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - let ct = cx.lift(*self).unwrap(); + let ct = cx.lift(*self); let kind = match ct.kind() { ty::ConstKind::Value(cv) => { let const_val = cx.valtree_to_const_val(cv); @@ -967,7 +967,7 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - let def = tables.instance_def(cx.lift(*self).unwrap()); + let def = tables.instance_def(cx.lift(*self)); let kind = match self.def { ty::InstanceKind::Item(..) => crate::mir::mono::InstanceKind::Item, ty::InstanceKind::Intrinsic(..) => crate::mir::mono::InstanceKind::Intrinsic, diff --git a/compiler/rustc_public/src/unstable/internal_cx/mod.rs b/compiler/rustc_public/src/unstable/internal_cx/mod.rs index 161f2754bed87..f178ced7224c1 100644 --- a/compiler/rustc_public/src/unstable/internal_cx/mod.rs +++ b/compiler/rustc_public/src/unstable/internal_cx/mod.rs @@ -44,7 +44,7 @@ impl<'tcx> InternalCx<'tcx> for TyCtxt<'tcx> { self } - fn lift>>(self, value: T) -> Option { + fn lift>>(self, value: T) -> T::Lifted { TyCtxt::lift(self, value) } diff --git a/compiler/rustc_public/src/unstable/mod.rs b/compiler/rustc_public/src/unstable/mod.rs index ec979eef40cd1..8c20a54018553 100644 --- a/compiler/rustc_public/src/unstable/mod.rs +++ b/compiler/rustc_public/src/unstable/mod.rs @@ -26,7 +26,7 @@ mod internal_cx; pub trait InternalCx<'tcx>: Copy + Clone { fn tcx(self) -> TyCtxt<'tcx>; - fn lift>>(self, value: T) -> Option; + fn lift>>(self, value: T) -> T::Lifted; fn mk_args_from_iter(self, iter: I) -> T::Output where diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index bb504bd0017a0..260b9cd2198cf 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -53,7 +53,7 @@ impl<'tcx, B: Bridge> AllocRangeHelpers<'tcx> for CompilerCtxt<'tcx, B> { } impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { - pub fn lift>>(&self, value: T) -> Option { + pub fn lift>>(&self, value: T) -> T::Lifted { self.tcx.lift(value) } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 2b0dc221844ec..14ad3373b2c8f 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -45,11 +45,11 @@ where { type Lifted = Binder; - fn lift_to_interner(self, cx: U) -> Option { - Some(Binder { - value: self.value.lift_to_interner(cx)?, - bound_vars: self.bound_vars.lift_to_interner(cx)?, - }) + fn lift_to_interner(self, cx: U) -> Self::Lifted { + Binder { + value: self.value.lift_to_interner(cx), + bound_vars: self.bound_vars.lift_to_interner(cx), + } } } @@ -994,12 +994,12 @@ where { type Lifted = Placeholder; - fn lift_to_interner(self, cx: U) -> Option { - Some(Placeholder { + fn lift_to_interner(self, cx: U) -> Self::Lifted { + Placeholder { universe: self.universe, - bound: self.bound.lift_to_interner(cx)?, + bound: self.bound.lift_to_interner(cx), _tcx: PhantomData, - }) + } } } @@ -1190,8 +1190,8 @@ where { type Lifted = BoundTy; - fn lift_to_interner(self, cx: U) -> Option { - Some(BoundTy { var: self.var, kind: self.kind.lift_to_interner(cx)? }) + fn lift_to_interner(self, cx: U) -> Self::Lifted { + BoundTy { var: self.var, kind: self.kind.lift_to_interner(cx) } } } diff --git a/compiler/rustc_type_ir/src/lift.rs b/compiler/rustc_type_ir/src/lift.rs index e5a099d1f5042..739d3a8512329 100644 --- a/compiler/rustc_type_ir/src/lift.rs +++ b/compiler/rustc_type_ir/src/lift.rs @@ -9,13 +9,13 @@ /// It would be more efficient if `TypedArena` provided a way to /// determine whether the address is in the allocated range. /// -/// `None` is returned if the value or one of the components is not part +/// Panics if the value or one of the components is not part /// of the provided context. -/// For `Ty`, `None` can be returned if either the type interner doesn't +/// For `Ty`, this can happen if either the type interner doesn't /// contain the `TyKind` key or if the address of the interned /// pointer differs. The latter case is possible if a primitive type, /// e.g., `()` or `u8`, was interned in a different context. pub trait Lift: std::fmt::Debug { type Lifted: std::fmt::Debug; - fn lift_to_interner(self, cx: I) -> Option; + fn lift_to_interner(self, cx: I) -> Self::Lifted; } diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 0545fbfda6e41..7a1798e439d16 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -35,8 +35,8 @@ where { type Lifted = OutlivesPredicate; - fn lift_to_interner(self, cx: U) -> Option { - Some(OutlivesPredicate(self.0.lift_to_interner(cx)?, self.1.lift_to_interner(cx)?)) + fn lift_to_interner(self, cx: U) -> Self::Lifted { + OutlivesPredicate(self.0.lift_to_interner(cx), self.1.lift_to_interner(cx)) } } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index e136257a5b248..e58de0c947cd2 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -782,8 +782,8 @@ pub struct FnSigKind { impl crate::lift::Lift for FnSigKind { type Lifted = FnSigKind; - fn lift_to_interner(self, _cx: J) -> Option { - Some(FnSigKind { flags: self.flags, _marker: PhantomData }) + fn lift_to_interner(self, _cx: J) -> Self::Lifted { + FnSigKind { flags: self.flags, _marker: PhantomData } } } diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs index 8df10b6a9eccd..7ea8bd384e3cf 100644 --- a/compiler/rustc_type_ir_macros/src/lib.rs +++ b/compiler/rustc_type_ir_macros/src/lib.rs @@ -168,7 +168,7 @@ fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { wc.push(parse_quote! { #ty: ::rustc_type_ir::lift::Lift }); let bind = &bindings[index]; quote! { - #bind.lift_to_interner(interner)? + #bind.lift_to_interner(interner) } }) }); @@ -189,8 +189,8 @@ fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { fn lift_to_interner( self, interner: J, - ) -> Option { - Some(match self { #body_fold }) + ) -> Self::Lifted { + match self { #body_fold } } }, ) From ef93fbbc9d208d5dbf7658fe79d373931d0b2480 Mon Sep 17 00:00:00 2001 From: Vastargazing Date: Thu, 30 Apr 2026 10:24:54 +0300 Subject: [PATCH 44/68] tests/run-make/print-cfg: add Android target_env case Android targets must not inherit `target_env="gnu"` from the Linux GNU base. Cover this in the existing print-cfg run-make test so a future target-spec refactor cannot silently re-introduce it. --- tests/run-make/print-cfg/rmake.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/run-make/print-cfg/rmake.rs b/tests/run-make/print-cfg/rmake.rs index a5df237b7fddc..b516bb1508ec4 100644 --- a/tests/run-make/print-cfg/rmake.rs +++ b/tests/run-make/print-cfg/rmake.rs @@ -48,6 +48,12 @@ fn main() { includes: &["unix", "target_abi=\"eabihf\""], disallow: &["windows"], }); + // Regression test for #90834: Android must not have `target_env="gnu"`. + check(PrintCfg { + target: "i686-linux-android", + includes: &["unix", "target_os=\"android\""], + disallow: &["windows", "target_env=\"gnu\""], + }); } fn check(PrintCfg { target, includes, disallow }: PrintCfg) { From 09e9d3f6da4d5a499979afe6a2c0201f83ac049e Mon Sep 17 00:00:00 2001 From: cijiugechu Date: Thu, 30 Apr 2026 16:31:22 +0800 Subject: [PATCH 45/68] rustdoc: preserve parent doc cfg for macro_export macros --- src/librustdoc/clean/mod.rs | 10 ++-- src/librustdoc/clean/types.rs | 18 ++++++ src/librustdoc/passes/propagate_doc_cfg.rs | 42 ++++++++++++- src/librustdoc/visit_ast.rs | 31 ++++++---- tests/rustdoc-html/doc-cfg/decl-macro.rs | 68 ++++++++++++++++++++++ 5 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 tests/rustdoc-html/doc-cfg/decl-macro.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a4e4126c1b9b2..4b52ce0e36cec 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -95,12 +95,13 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< // This covers the case where somebody does an import which should pull in an item, // but there's already an item with the same namespace and same name. Rust gives // priority to the not-imported one, so we should, too. - items.extend(doc.items.values().flat_map(|(item, renamed, import_ids)| { + items.extend(doc.items.values().flat_map(|entry| { // First, lower everything other than glob imports. + let item = entry.item; if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) { return Vec::new(); } - let v = clean_maybe_renamed_item(cx, item, *renamed, import_ids); + let v = clean_maybe_renamed_item(cx, item, entry.renamed, &entry.import_ids); for item in &v { if let Some(name) = item.name && (cx.document_hidden() || !item.is_doc_hidden()) @@ -130,10 +131,11 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext< _ => unreachable!(), } })); - items.extend(doc.items.values().flat_map(|(item, renamed, _)| { + items.extend(doc.items.values().flat_map(|entry| { // Now we actually lower the imports, skipping everything else. + let item = entry.item; if let hir::ItemKind::Use(path, hir::UseKind::Glob) = item.kind { - clean_use_statement(item, *renamed, path, hir::UseKind::Glob, cx, &mut inserted) + clean_use_statement(item, entry.renamed, path, hir::UseKind::Glob, cx, &mut inserted) } else { // skip everything else Vec::new() diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index e81c6eae1996f..e2db913232076 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -25,6 +25,7 @@ use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, }; use rustc_session::Session; +use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{Symbol, kw, sym}; use rustc_span::{DUMMY_SP, FileName, Ident, Loc, RemapPathScopeComponents}; @@ -405,6 +406,23 @@ fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { } impl Item { + pub(crate) fn cfg_parent_ids_for_detached_item(&self, tcx: TyCtxt<'_>) -> Vec { + let Some(def_id) = self.inline_stmt_id.or(self.item_id.as_local_def_id()) else { + return Vec::new(); + }; + let mut ids = Vec::new(); + let mut next = def_id; + while let Some(parent) = tcx.opt_local_parent(next) { + if parent == CRATE_DEF_ID { + break; + } + ids.push(parent); + next = parent; + } + ids.reverse(); + ids + } + /// Returns the effective stability of the item. /// /// This method should only be called after the `propagate-stability` pass has been run. diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs index 92798bb9bb011..1334595272f60 100644 --- a/src/librustdoc/passes/propagate_doc_cfg.rs +++ b/src/librustdoc/passes/propagate_doc_cfg.rs @@ -51,12 +51,31 @@ fn add_only_cfg_attributes(attrs: &mut Vec, new_attrs: &[Attribute]) } } +/// This function goes through the attributes list (`new_attrs`) and extracts the attributes that +/// affect the cfg state propagated to detached items. +fn add_cfg_state_attributes(attrs: &mut Vec, new_attrs: &[Attribute]) { + for attr in new_attrs { + if let Attribute::Parsed(AttributeKind::Doc(d)) = attr + && (!d.cfg.is_empty() || !d.auto_cfg.is_empty() || !d.auto_cfg_change.is_empty()) + { + let mut new_attr = DocAttribute::default(); + new_attr.cfg = d.cfg.clone(); + new_attr.auto_cfg = d.auto_cfg.clone(); + new_attr.auto_cfg_change = d.auto_cfg_change.clone(); + attrs.push(Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr)))); + } else if let Attribute::Parsed(AttributeKind::CfgTrace(..)) = attr { + // If it's a `cfg()` attribute, we keep it. + attrs.push(attr.clone()); + } + } +} + impl CfgPropagator<'_, '_> { // Some items need to merge their attributes with their parents' otherwise a few of them // (mostly `cfg` ones) will be missing. fn merge_with_parent_attributes(&mut self, item: &mut Item) { let mut attrs = Vec::new(); - // We only need to merge an item attributes with its parent's in case it's an impl as an + // We need to merge an item attributes with its parent's in case it's an impl as an // impl might not be defined in the same module as the item it implements. // // Otherwise, `cfg_info` already tracks everything we need so nothing else to do! @@ -69,6 +88,27 @@ impl CfgPropagator<'_, '_> { next_def_id = parent_def_id; } } + // We also need to merge an item attributes with its parent's in case it's a macro with + // the `#[macro_export]` attribute, because it might not be defined at crate root. + if matches!(item.kind, ItemKind::MacroItem(_)) + && item.inner.attrs.other_attrs.iter().any(|attr| { + matches!( + attr, + rustc_hir::Attribute::Parsed( + rustc_hir::attrs::AttributeKind::MacroExport { .. } + ) + ) + }) + { + for parent_def_id in &item.cfg_parent_ids_for_detached_item(self.cx.tcx) { + let mut parent_attrs = Vec::new(); + add_cfg_state_attributes( + &mut parent_attrs, + load_attrs(self.cx.tcx, parent_def_id.to_def_id()), + ); + merge_attrs(self.cx.tcx, &[], Some((&parent_attrs, None)), &mut self.cfg_info); + } + } let (_, cfg) = merge_attrs( self.cx.tcx, diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 8746253d6ebba..5f119c23841e3 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -32,7 +32,7 @@ pub(crate) struct Module<'hir> { pub(crate) def_id: LocalDefId, pub(crate) renamed: Option, pub(crate) import_id: Option, - /// The key is the item `ItemId` and the value is: (item, renamed, Vec). + /// The key is the item `ItemId`. /// We use `FxIndexMap` to keep the insert order. /// /// `import_id` needs to be a `Vec` because we live in a dark world where you can have code @@ -52,10 +52,7 @@ pub(crate) struct Module<'hir> { /// So in this case, we don't want to have two items but just one with attributes from all /// non-glob imports to be merged. Glob imports attributes are always ignored, whether they're /// shadowed or not. - pub(crate) items: FxIndexMap< - (LocalDefId, Option), - (&'hir hir::Item<'hir>, Option, Vec), - >, + pub(crate) items: FxIndexMap<(LocalDefId, Option), ItemEntry<'hir>>, /// (def_id, renamed) -> (res, local_import_id) /// @@ -70,6 +67,13 @@ pub(crate) struct Module<'hir> { pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option, Option)>, } +#[derive(Debug)] +pub(crate) struct ItemEntry<'hir> { + pub(crate) item: &'hir hir::Item<'hir>, + pub(crate) renamed: Option, + pub(crate) import_ids: Vec, +} + impl Module<'_> { pub(crate) fn new( name: Symbol, @@ -172,9 +176,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { { let item = self.cx.tcx.hir_expect_item(local_def_id); let (ident, _, _) = item.expect_macro(); - top_level_module - .items - .insert((local_def_id, Some(ident.name)), (item, None, Vec::new())); + top_level_module.items.insert( + (local_def_id, Some(ident.name)), + ItemEntry { item, renamed: None, import_ids: Vec::new() }, + ); } } @@ -413,10 +418,14 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { .unwrap() .items .entry(key) - .and_modify(|v| v.2.push(import_id)) - .or_insert_with(|| (item, renamed, vec![import_id])); + .and_modify(|v| v.import_ids.push(import_id)) + .or_insert_with(|| ItemEntry { item, renamed, import_ids: vec![import_id] }); } else { - self.modules.last_mut().unwrap().items.insert(key, (item, renamed, Vec::new())); + self.modules + .last_mut() + .unwrap() + .items + .insert(key, ItemEntry { item, renamed, import_ids: Vec::new() }); } } } diff --git a/tests/rustdoc-html/doc-cfg/decl-macro.rs b/tests/rustdoc-html/doc-cfg/decl-macro.rs new file mode 100644 index 0000000000000..e97da8647a6ab --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/decl-macro.rs @@ -0,0 +1,68 @@ +// Regression test for +//@ compile-flags: --cfg feature="routing" + +#![crate_name = "foo"] +#![feature(doc_cfg)] + +#[cfg(feature = "routing")] +pub mod routing { + //@ has 'foo/macro.vpath.html' '//*[@class="stab portability"]' 'Available on crate feature routing only.' + #[macro_export] + macro_rules! vpath { + () => {}; + } +} + +#[doc(cfg(feature = "manual"))] +pub mod manual { + //@ has 'foo/macro.manual_macro.html' '//*[@class="stab portability"]' 'Available on crate feature manual only.' + #[macro_export] + macro_rules! manual_macro { + () => {}; + } +} + +#[doc(cfg(feature = "outer"))] +pub mod outer { + #[cfg(feature = "routing")] + pub mod inner { + //@ has 'foo/macro.nested_macro.html' '//*[@class="stab portability"]' 'Available on crate features outer and routing only.' + #[macro_export] + macro_rules! nested_macro { + () => {}; + } + } +} + +#[cfg(feature = "routing")] +#[doc(auto_cfg = false)] +pub mod auto_cfg_disabled { + //@ count 'foo/macro.no_auto_cfg_macro.html' '//*[@class="stab portability"]' 0 + #[macro_export] + macro_rules! no_auto_cfg_macro { + () => {}; + } +} + +#[cfg(feature = "routing")] +#[doc(auto_cfg(hide(feature = "routing")))] +pub mod auto_cfg_hidden { + //@ count 'foo/macro.hidden_cfg_macro.html' '//*[@class="stab portability"]' 0 + #[macro_export] + macro_rules! hidden_cfg_macro { + () => {}; + } +} + +#[cfg(feature = "routing")] +#[doc(auto_cfg(hide(feature = "routing")))] +pub mod auto_cfg_shown { + #[doc(auto_cfg(show(feature = "routing")))] + pub mod inner { + //@ has 'foo/macro.shown_cfg_macro.html' '//*[@class="stab portability"]' 'Available on crate feature routing only.' + #[macro_export] + macro_rules! shown_cfg_macro { + () => {}; + } + } +} From e4eb91ca0fb9b176be4e2cdd0a3a5e08eb472fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 29 Apr 2026 23:23:28 +0200 Subject: [PATCH 46/68] catch callback unwinding to finalize in-flight self-profiling events from queries fatal errors currently abort the compiler process without allocating the self-profile strings: query events aren't always correctly recorded, and will show up as in the data. catching the unwinding panic allows us to finalize the self-profiling process correctly before continuing unwinding as before. --- compiler/rustc_interface/src/passes.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 842cb1f041311..344090d1c147f 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1016,9 +1016,24 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( feed.crate_for_resolver(tcx.arena.alloc(Steal::new((krate, pre_configured_attrs)))); feed.output_filenames(Arc::new(outputs)); - let res = f(tcx); - // FIXME maybe run finish even when a fatal error occurred? or at least - // tcx.alloc_self_profile_query_strings()? + // There are two paths out of `f`. + // - Normal exit. + // - Panic, e.g. triggered by `abort_if_errors` or a fatal error. + // + // If a panic occurs, we still need to wind down the self-profiler to correctly record + // the query events that are still in flight. Otherwise, they will be invalid and will + // show up as "" in the profiling data. + let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(tcx))); + let res = match res { + Ok(res) => res, + Err(err) => { + tcx.alloc_self_profile_query_strings(); + + // Resume unwinding if a panic happened. + std::panic::resume_unwind(err); + } + }; + tcx.finish(); res }, From 74060d46ca46cf488c5f7065af7f524497bfee76 Mon Sep 17 00:00:00 2001 From: Paul Murphy Date: Thu, 30 Apr 2026 08:21:08 -0500 Subject: [PATCH 47/68] Fix ICE when using -Zinstrument-mcount and -Clinker-flavor=lld -Zinstrument-mcount passes -pg to the gnu linker command. This option is only supported by the cc frontend. This fixes https://github.com/rust-lang/rust/issues/155972 --- compiler/rustc_codegen_ssa/src/back/linker.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index eb908e19be54e..76aecb3b9e54e 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -736,13 +736,15 @@ impl<'a> Linker for GccLinker<'a> { fn enable_profiling(&mut self) { // This flag is also used when linking to choose target specific // libraries needed to enable profiling. - self.cc_arg("-pg"); - // On windows-gnu targets, libgmon also needs to be linked, and this - // requires readding libraries to satisfy its dependencies. - if self.sess.target.is_like_windows { - self.cc_arg("-lgmon"); - self.cc_arg("-lkernel32"); - self.cc_arg("-lmsvcrt"); + if !self.is_ld { + self.cc_arg("-pg"); + // On windows-gnu targets, libgmon also needs to be linked, and this + // requires readding libraries to satisfy its dependencies. + if self.sess.target.is_like_windows { + self.cc_arg("-lgmon"); + self.cc_arg("-lkernel32"); + self.cc_arg("-lmsvcrt"); + } } } From f51cf6398b8cfbff47a9b3f3b9b1c1997ddbe3cb Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 29 Apr 2026 00:11:06 +0200 Subject: [PATCH 48/68] Allow to use `Diagnostic` directly in `SharedContext::emit_lint` --- .../src/attributes/crate_level.rs | 9 +- .../attributes/diagnostic/do_not_recommend.rs | 7 +- .../src/attributes/diagnostic/mod.rs | 61 +++++------- .../src/attributes/diagnostic/on_const.rs | 5 +- .../src/attributes/diagnostic/on_move.rs | 7 +- .../attributes/diagnostic/on_unimplemented.rs | 3 +- .../src/attributes/diagnostic/on_unknown.rs | 5 +- .../attributes/diagnostic/on_unmatch_args.rs | 3 +- .../rustc_attr_parsing/src/attributes/doc.rs | 92 ++++++------------- .../src/attributes/proc_macro_attrs.rs | 3 +- compiler/rustc_attr_parsing/src/context.rs | 46 +++------- compiler/rustc_attr_parsing/src/errors.rs | 14 +-- compiler/rustc_attr_parsing/src/interface.rs | 2 +- .../rustc_attr_parsing/src/target_checking.rs | 17 ++-- compiler/rustc_hir/src/lints.rs | 2 +- compiler/rustc_interface/src/passes.rs | 10 +- 16 files changed, 95 insertions(+), 191 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 76fa2aed5c79c..070b0e385b024 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_hir::attrs::{CrateType, WindowsSubsystemKind}; use rustc_session::lint::builtin::UNKNOWN_CRATE_TYPES; use rustc_span::Symbol; @@ -61,12 +60,8 @@ impl CombineAttributeParser for CrateTypeParser { let span = n.value_span; cx.emit_lint( UNKNOWN_CRATE_TYPES, - move |dcx, level| { - UnknownCrateTypes { - sugg: candidate - .map(|s| UnknownCrateTypesSuggestion { span, snippet: s }), - } - .into_diag(dcx, level) + UnknownCrateTypes { + sugg: candidate.map(|s| UnknownCrateTypesSuggestion { span, snippet: s }), }, span, ); diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs index e7adef010aa74..4631639da5ff4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/do_not_recommend.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_feature::{AttributeTemplate, template}; use rustc_hir::Target; use rustc_hir::attrs::AttributeKind; @@ -26,7 +25,7 @@ impl SingleAttributeParser for DoNotRecommendParser { if !matches!(args, ArgParser::NoArgs) { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - |dcx, level| crate::errors::DoNotRecommendDoesNotExpectArgs.into_diag(dcx, level), + crate::errors::DoNotRecommendDoesNotExpectArgs, attr_span, ); } @@ -35,9 +34,7 @@ impl SingleAttributeParser for DoNotRecommendParser { let target_span = cx.target_span; cx.emit_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| { - IncorrectDoNotRecommendLocation { target_span }.into_diag(dcx, level) - }, + IncorrectDoNotRecommendLocation { target_span }, attr_span, ); return None; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 375eaba2844bb..816d51a6ba395 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -1,6 +1,6 @@ use std::ops::Range; -use rustc_errors::{Diagnostic, E0232}; +use rustc_errors::E0232; use rustc_hir::AttrPath; use rustc_hir::attrs::diagnostic::{ Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue, @@ -142,10 +142,7 @@ fn merge( let first_span = *first_span; cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| { - IgnoredDiagnosticOption { first_span, later_span, option_name } - .into_diag(dcx, level) - }, + IgnoredDiagnosticOption { first_span, later_span, option_name }, later_span, ); } @@ -169,19 +166,16 @@ fn parse_list<'p>( // if the user used non-metaitem syntax. See `ArgParser::from_attr_args`. cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| NonMetaItemDiagnosticAttribute.into_diag(dcx, level), + NonMetaItemDiagnosticAttribute, list.span, ); } ArgParser::NoArgs => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| { - MissingOptionsForDiagnosticAttribute { - attribute: mode.as_str(), - options: mode.expected_options(), - } - .into_diag(dcx, level) + MissingOptionsForDiagnosticAttribute { + attribute: mode.as_str(), + options: mode.expected_options(), }, span, ); @@ -189,13 +183,10 @@ fn parse_list<'p>( ArgParser::NameValue(_) => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| { - MalFormedDiagnosticAttributeLint { - attribute: mode.as_str(), - options: mode.allowed_options(), - span, - } - .into_diag(dcx, level) + MalFormedDiagnosticAttributeLint { + attribute: mode.as_str(), + options: mode.allowed_options(), + span, }, span, ); @@ -223,13 +214,10 @@ fn parse_directive_items<'p>( macro malformed() {{ cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| { - MalFormedDiagnosticAttributeLint { - attribute: mode.as_str(), - options: mode.allowed_options(), - span, - } - .into_diag(dcx, level) + MalFormedDiagnosticAttributeLint { + attribute: mode.as_str(), + options: mode.allowed_options(), + span, }, span, ); @@ -251,11 +239,11 @@ fn parse_directive_items<'p>( let first_span = $($first_span)*; cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| IgnoredDiagnosticOption { + IgnoredDiagnosticOption { first_span, later_span: span, option_name: $name, - }.into_diag(dcx, level), + }, span, ); }} @@ -285,11 +273,7 @@ fn parse_directive_items<'p>( | FormatWarning::PositionalArgument { span } | FormatWarning::IndexedArgument { span } | FormatWarning::DisallowedPlaceholder { span, .. }) = warning; - cx.emit_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - move |dcx, level| warning.into_diag(dcx, level), - span, - ); + cx.emit_lint(MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, warning, span); } f @@ -297,13 +281,10 @@ fn parse_directive_items<'p>( Err(e) => { cx.emit_lint( MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - move |dcx, level| { - WrappedParserError { - description: &e.description, - label: &e.label, - span: slice_span(input.span, e.span.clone(), is_snippet), - } - .into_diag(dcx, level) + WrappedParserError { + description: e.description, + label: e.label, + span: slice_span(input.span, e.span.clone(), is_snippet), }, input.span, ); diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs index e6c66b597ce08..e783e49ef1e83 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_hir::attrs::diagnostic::Directive; use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES; @@ -30,9 +29,7 @@ impl AttributeParser for OnConstParser { let target_span = cx.target_span; cx.emit_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| { - DiagnosticOnConstOnlyForTraitImpls { target_span }.into_diag(dcx, level) - }, + DiagnosticOnConstOnlyForTraitImpls { target_span }, span, ); return; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs index d4ef92fc9a548..260815b4718c1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_feature::template; use rustc_hir::attrs::AttributeKind; use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES; @@ -28,11 +27,7 @@ impl OnMoveParser { self.span = Some(span); if !matches!(cx.target, Target::Enum | Target::Struct | Target::Union) { - cx.emit_lint( - MISPLACED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| DiagnosticOnMoveOnlyForAdt.into_diag(dcx, level), - span, - ); + cx.emit_lint(MISPLACED_DIAGNOSTIC_ATTRIBUTES, DiagnosticOnMoveOnlyForAdt, span); return; } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs index b60d732bc6bf3..e4d01b006c226 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_hir::attrs::diagnostic::Directive; use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES; @@ -20,7 +19,7 @@ impl OnUnimplementedParser { if !matches!(cx.target, Target::Trait) { cx.emit_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| DiagnosticOnUnimplementedOnlyForTraits.into_diag(dcx, level), + DiagnosticOnUnimplementedOnlyForTraits, span, ); return; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs index 0a9aaaaa1893e..7d77497f04294 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_hir::attrs::diagnostic::Directive; use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES; @@ -32,9 +31,7 @@ impl OnUnknownParser { let target_span = cx.target_span; cx.emit_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| { - DiagnosticOnUnknownOnlyForImports { target_span }.into_diag(dcx, level) - }, + DiagnosticOnUnknownOnlyForImports { target_span }, span, ); return; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatch_args.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatch_args.rs index 3f1474ba4543e..923e5ca2b96df 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatch_args.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatch_args.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_hir::attrs::diagnostic::Directive; use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES; @@ -27,7 +26,7 @@ impl AttributeParser for OnUnmatchArgsParser { if !matches!(cx.target, Target::MacroDef) { cx.emit_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, - move |dcx, level| DiagnosticOnUnmatchArgsOnlyForMacros.into_diag(dcx, level), + DiagnosticOnUnmatchArgsOnlyForMacros, span, ); return; diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index c0b90c2c6d97f..7a2c24184982c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -1,5 +1,5 @@ use rustc_ast::ast::{AttrStyle, LitKind, MetaItemLit}; -use rustc_errors::{Applicability, Diagnostic, msg}; +use rustc_errors::{Applicability, msg}; use rustc_feature::template; use rustc_hir::Target; use rustc_hir::attrs::{ @@ -66,7 +66,7 @@ fn check_attr_crate_level(cx: &mut AcceptContext<'_, '_>, span: Span) -> bool { if cx.shared.target != Target::Crate { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| AttrCrateLevelOnly.into_diag(dcx, level), + AttrCrateLevelOnly, span, ); return false; @@ -76,20 +76,12 @@ fn check_attr_crate_level(cx: &mut AcceptContext<'_, '_>, span: Span) -> bool { // FIXME: To be removed once merged and replace with `cx.expected_name_value(span, _name)`. fn expected_name_value(cx: &mut AcceptContext<'_, '_>, span: Span, _name: Option) { - cx.emit_lint( - rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| ExpectedNameValue.into_diag(dcx, level), - span, - ); + cx.emit_lint(rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, ExpectedNameValue, span); } // FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. fn expected_no_args(cx: &mut AcceptContext<'_, '_>, span: Span) { - cx.emit_lint( - rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| ExpectedNoArgs.into_diag(dcx, level), - span, - ); + cx.emit_lint(rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, ExpectedNoArgs, span); } // FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. @@ -99,11 +91,7 @@ fn expected_string_literal( span: Span, _actual_literal: Option<&MetaItemLit>, ) { - cx.emit_lint( - rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| MalformedDoc.into_diag(dcx, level), - span, - ); + cx.emit_lint(rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, MalformedDoc, span); } fn parse_keyword_and_attribute( @@ -171,13 +159,10 @@ impl DocParser { let unused_span = path.span(); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| { - rustc_errors::lints::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: true, - } - .into_diag(dcx, level) + rustc_errors::lints::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, }, unused_span, ); @@ -197,7 +182,7 @@ impl DocParser { let span = cx.attr_span; cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| MalformedDoc.into_diag(dcx, level), + MalformedDoc, span, ); return; @@ -211,14 +196,14 @@ impl DocParser { Some(name) => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| DocTestUnknown { name }.into_diag(dcx, level), + DocTestUnknown { name }, path.span(), ); } None => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| DocTestLiteral.into_diag(dcx, level), + DocTestLiteral, path.span(), ); } @@ -250,7 +235,7 @@ impl DocParser { if let Some(first_definition) = self.attribute.aliases.get(&alias).copied() { cx.emit_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - move |dcx, level| DocAliasDuplicated { first_definition }.into_diag(dcx, level), + DocAliasDuplicated { first_definition }, span, ); } @@ -338,7 +323,7 @@ impl DocParser { let MetaItemOrLitParser::MetaItemParser(item) = meta else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level), + DocAutoCfgExpectsHideOrShow, meta.span(), ); continue; @@ -349,7 +334,7 @@ impl DocParser { _ => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| DocAutoCfgExpectsHideOrShow.into_diag(dcx, level), + DocAutoCfgExpectsHideOrShow, item.span(), ); continue; @@ -358,9 +343,7 @@ impl DocParser { let ArgParser::List(list) = item.args() else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| { - DocAutoCfgHideShowExpectsList { attr_name }.into_diag(dcx, level) - }, + DocAutoCfgHideShowExpectsList { attr_name }, item.span(), ); continue; @@ -372,10 +355,7 @@ impl DocParser { let MetaItemOrLitParser::MetaItemParser(sub_item) = item else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| { - DocAutoCfgHideShowUnexpectedItem { attr_name } - .into_diag(dcx, level) - }, + DocAutoCfgHideShowUnexpectedItem { attr_name }, item.span(), ); continue; @@ -388,7 +368,7 @@ impl DocParser { // cx.expected_identifier(sub_item.path().span()); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| MalformedDoc.into_diag(dcx, level), + MalformedDoc, sub_item.path().span(), ); continue; @@ -415,10 +395,7 @@ impl DocParser { _ => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| { - DocAutoCfgHideShowUnexpectedItem { attr_name } - .into_diag(dcx, level) - }, + DocAutoCfgHideShowUnexpectedItem { attr_name }, sub_item.span(), ); continue; @@ -433,7 +410,7 @@ impl DocParser { else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| DocAutoCfgWrongLiteral.into_diag(dcx, level), + DocAutoCfgWrongLiteral, nv.value_span, ); return; @@ -573,7 +550,7 @@ impl DocParser { let Some(list) = args.as_list() else { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| DocTestTakesList.into_diag(dcx, level), + DocTestTakesList, args.span().unwrap_or(path.span()), ); return; @@ -590,7 +567,7 @@ impl DocParser { // cx.unexpected_literal(lit.span); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - |dcx, level| MalformedDoc.into_diag(dcx, level), + MalformedDoc, lit.span, ); } @@ -601,7 +578,7 @@ impl DocParser { let span = path.span(); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| DocUnknownSpotlight { sugg_span: span }.into_diag(dcx, level), + DocUnknownSpotlight { sugg_span: span }, span, ); } @@ -614,14 +591,7 @@ impl DocParser { let span = path.span(); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| { - DocUnknownInclude { - inner, - value, - sugg: (span, Applicability::MaybeIncorrect), - } - .into_diag(dcx, level) - }, + DocUnknownInclude { inner, value, sugg: (span, Applicability::MaybeIncorrect) }, span, ); } @@ -629,9 +599,7 @@ impl DocParser { let span = path.span(); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| { - DocUnknownPasses { name, note_span: span }.into_diag(dcx, level) - }, + DocUnknownPasses { name, note_span: span }, span, ); } @@ -639,14 +607,14 @@ impl DocParser { let span = path.span(); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| DocUnknownPlugins { label_span: span }.into_diag(dcx, level), + DocUnknownPlugins { label_span: span }, span, ); } Some(name) => { cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| DocUnknownAny { name }.into_diag(dcx, level), + DocUnknownAny { name }, path.span(), ); } @@ -656,7 +624,7 @@ impl DocParser { let name = Symbol::intern(&full_name); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| DocUnknownAny { name }.into_diag(dcx, level), + DocUnknownAny { name }, path.span(), ); } @@ -670,9 +638,7 @@ impl DocParser { let span = cx.attr_span; cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - move |dcx, level| { - IllFormedAttributeInput::new(&suggestions, None, None).into_diag(dcx, level) - }, + IllFormedAttributeInput::new(&suggestions, None, None), span, ); } diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index 00af9bd38e60c..88253041ed6e0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -1,4 +1,3 @@ -use rustc_errors::Diagnostic; use rustc_session::lint::builtin::AMBIGUOUS_DERIVE_HELPERS; use super::prelude::*; @@ -124,7 +123,7 @@ fn parse_derive_like( if rustc_feature::is_builtin_attr_name(ident.name) { cx.emit_lint( AMBIGUOUS_DERIVE_HELPERS, - |dcx, level| crate::errors::AmbiguousDeriveHelpers.into_diag(dcx, level), + crate::errors::AmbiguousDeriveHelpers, ident.span, ); } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 02a7aac9659f2..9c82e6208c24c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -371,23 +371,21 @@ impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { /// Emit a lint. This method is somewhat special, since lints emitted during attribute parsing /// must be delayed until after HIR is built. This method will take care of the details of /// that. - pub(crate) fn emit_lint< - F: for<'a> Fn(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + DynSync + 'static, - >( + pub(crate) fn emit_lint( &mut self, lint: &'static Lint, - callback: F, + diagnostic: impl for<'x> Diagnostic<'x, ()> + DynSend + DynSync + 'static, span: impl Into, ) { self.emit_lint_inner( lint, - EmitAttribute(Box::new(move |dcx, level, _| callback(dcx, level))), + EmitAttribute(Box::new(move |dcx, level, _| diagnostic.into_diag(dcx, level))), span, ); } pub(crate) fn emit_lint_with_sess< - F: for<'a> Fn(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + DynSend + DynSync + 'static, @@ -418,13 +416,10 @@ impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { self.emit_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - move |dcx, level| { - rustc_errors::lints::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: false, - } - .into_diag(dcx, level) + rustc_errors::lints::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: false, }, unused_span, ) @@ -437,13 +432,10 @@ impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { ) { self.emit_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - move |dcx, level| { - rustc_errors::lints::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: true, - } - .into_diag(dcx, level) + rustc_errors::lints::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, }, unused_span, ) @@ -967,14 +959,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { let valid_without_list = self.template.word; self.emit_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - move |dcx, level| { - crate::errors::EmptyAttributeList { - attr_span: span, - attr_path: &attr_path, - valid_without_list, - } - .into_diag(dcx, level) - }, + crate::errors::EmptyAttributeList { attr_span: span, attr_path, valid_without_list }, span, ); } @@ -991,10 +976,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { let span = self.attr_span; self.emit_lint( lint, - move |dcx, level| { - crate::errors::IllFormedAttributeInput::new(&suggestions, None, help.as_deref()) - .into_diag(dcx, level) - }, + crate::errors::IllFormedAttributeInput::new(&suggestions, None, help.as_deref()), span, ); } diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs index 85304241660be..d2c9c1b1eb807 100644 --- a/compiler/rustc_attr_parsing/src/errors.rs +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -118,7 +118,7 @@ struct IllFormedAttributeInputHelp { *[other] using `{$attr_path}` with an empty list has no effect }" )] -pub(crate) struct EmptyAttributeList<'a> { +pub(crate) struct EmptyAttributeList { #[suggestion( "{$valid_without_list -> [true] remove these parentheses @@ -128,7 +128,7 @@ pub(crate) struct EmptyAttributeList<'a> { applicability = "machine-applicable" )] pub attr_span: Span, - pub attr_path: &'a str, + pub attr_path: String, pub valid_without_list: bool, } @@ -159,8 +159,8 @@ pub(crate) struct InvalidTargetLint { *[other] the `#![{$name}]` attribute can only be used at the crate root }" )] -pub(crate) struct InvalidAttrStyle<'a> { - pub name: &'a str, +pub(crate) struct InvalidAttrStyle { + pub name: String, pub is_used_as_inner: bool, #[note("this attribute does not have an `!`, which means it is applied to this {$target}")] pub target_span: Option, @@ -359,11 +359,11 @@ pub(crate) struct MalFormedDiagnosticAttributeLint { #[derive(Diagnostic)] #[diag("{$description}")] -pub(crate) struct WrappedParserError<'a> { - pub description: &'a str, +pub(crate) struct WrappedParserError { + pub description: String, #[label("{$label}")] pub span: Span, - pub label: &'a str, + pub label: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f4506d8d5fedc..567cdc7701eae 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -23,7 +23,7 @@ use crate::{OmitDoc, ShouldEmit}; pub struct EmitAttribute( pub Box< - dyn for<'a> Fn(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &Session) -> Diag<'a, ()> + DynSend + DynSync + 'static, diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index f0305c320f8f0..d05f1baf63dad 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -142,9 +142,9 @@ impl<'sess> AttributeParser<'sess> { }; let attr_span = cx.attr_span; - cx.emit_lint( + cx.emit_lint_with_sess( lint, - move |dcx, level| { + move |dcx, level, _| { InvalidTargetLint { name: name.to_string(), target: target.plural_name(), @@ -188,14 +188,11 @@ impl<'sess> AttributeParser<'sess> { cx.emit_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - move |dcx, level| { - crate::errors::InvalidAttrStyle { - name: &name, - is_used_as_inner, - target_span: (!is_used_as_inner).then_some(target_span), - target: target.name(), - } - .into_diag(dcx, level) + crate::errors::InvalidAttrStyle { + name, + is_used_as_inner, + target_span: (!is_used_as_inner).then_some(target_span), + target: target.name(), }, attr_span, ); diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 0d9fc669bda7f..fdc419c37dd34 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -18,7 +18,7 @@ pub struct DelayedLint { pub id: HirId, pub span: MultiSpan, pub callback: Box< - dyn for<'a> Fn(DiagCtxtHandle<'a>, Level, &dyn std::any::Any) -> Diag<'a, ()> + dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn std::any::Any) -> Diag<'a, ()> + DynSend + DynSync + 'static, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 38d899853cd5b..229e37cac28a7 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1025,14 +1025,14 @@ pub fn create_and_enter_global_ctxt FnOnce(TyCtxt<'tcx>) -> T>( ) } -struct DiagCallback<'a, 'tcx> { - callback: &'a Box< - dyn for<'b> Fn(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + DynSync, +struct DiagCallback<'tcx> { + callback: Box< + dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + DynSync, >, tcx: TyCtxt<'tcx>, } -impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for DiagCallback<'b, 'tcx> { +impl<'a, 'tcx> Diagnostic<'a, ()> for DiagCallback<'tcx> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { (self.callback)(dcx, level, self.tcx.sess) } @@ -1046,7 +1046,7 @@ pub fn emit_delayed_lints(tcx: TyCtxt<'_>) { lint.lint_id.lint, lint.id, lint.span.clone(), - DiagCallback { callback: &lint.callback, tcx }, + DiagCallback { callback: lint.callback, tcx }, ); } } From c5e38b741dcdd205bb36eb798590b9aacbfdb3da Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 30 Apr 2026 14:18:33 +0000 Subject: [PATCH 49/68] Pass Session to optimize_and_codegen_fat_lto This is necessary to fix incremental LTO in cg_gcc as well as to do some LTO refactorings I want to do. The actual fix for cg_gcc will be done on the cg_gcc repo to test it in CI. --- compiler/rustc_codegen_gcc/src/lib.rs | 4 ++-- compiler/rustc_codegen_llvm/src/lib.rs | 8 ++++---- compiler/rustc_codegen_ssa/src/back/write.rs | 8 ++++---- compiler/rustc_codegen_ssa/src/traits/write.rs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index d50968bad2501..4be25b3fb0934 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -430,8 +430,8 @@ impl WriteBackendMethods for GccCodegenBackend { } fn optimize_and_codegen_fat_lto( + sess: &Session, cgcx: &CodegenContext, - prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, _tm_factory: TargetMachineFactoryFn, // FIXME(bjorn3): Limit LTO exports to these symbols @@ -439,7 +439,7 @@ impl WriteBackendMethods for GccCodegenBackend { each_linked_rlib_for_lto: &[PathBuf], modules: Vec>, ) -> CompiledModule { - back::lto::run_fat(cgcx, prof, shared_emitter, each_linked_rlib_for_lto, modules) + back::lto::run_fat(cgcx, &sess.prof, shared_emitter, each_linked_rlib_for_lto, modules) } fn run_thin_lto( diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 65c70c754918d..575e37d0b171d 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -133,8 +133,8 @@ impl WriteBackendMethods for LlvmCodegenBackend { back::write::target_machine_factory(sess, optlvl, target_features) } fn optimize_and_codegen_fat_lto( + sess: &Session, cgcx: &CodegenContext, - prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], @@ -143,7 +143,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { ) -> CompiledModule { let mut module = back::lto::run_fat( cgcx, - prof, + &sess.prof, shared_emitter, tm_factory, exported_symbols_for_lto, @@ -153,9 +153,9 @@ impl WriteBackendMethods for LlvmCodegenBackend { let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); - back::lto::run_pass_manager(cgcx, prof, dcx, &mut module, false); + back::lto::run_pass_manager(cgcx, &sess.prof, dcx, &mut module, false); - back::write::codegen(cgcx, prof, shared_emitter, module, &cgcx.module_config) + back::write::codegen(cgcx, &sess.prof, shared_emitter, module, &cgcx.module_config) } fn run_thin_lto( cgcx: &CodegenContext, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index ff91a08de4de6..c48e8a58b6964 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -961,15 +961,15 @@ fn execute_copy_from_cache_work_item( } fn do_fat_lto( + sess: &Session, cgcx: &CodegenContext, - prof: &SelfProfilerRef, shared_emitter: SharedEmitter, tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], each_linked_rlib_for_lto: &[PathBuf], needs_fat_lto: Vec>, ) -> CompiledModule { - let _timer = prof.verbose_generic_activity("LLVM_fatlto"); + let _timer = sess.prof.verbose_generic_activity("LLVM_fatlto"); let dcx = DiagCtxt::new(Box::new(shared_emitter.clone())); let dcx = dcx.handle(); @@ -977,8 +977,8 @@ fn do_fat_lto( check_lto_allowed(&cgcx, dcx); B::optimize_and_codegen_fat_lto( + sess, cgcx, - prof, &shared_emitter, tm_factory, exported_symbols_for_lto, @@ -2177,8 +2177,8 @@ impl OngoingCodegen { CompiledModules { modules: vec![do_fat_lto( + sess, &cgcx, - &sess.prof, shared_emitter, tm_factory, &exported_symbols_for_lto, diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index cca6db78e381e..9b8bf138e7a14 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -30,8 +30,8 @@ pub trait WriteBackendMethods: Clone + 'static { /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations fn optimize_and_codegen_fat_lto( + sess: &Session, cgcx: &CodegenContext, - prof: &SelfProfilerRef, shared_emitter: &SharedEmitter, tm_factory: TargetMachineFactoryFn, exported_symbols_for_lto: &[String], From a0db15f7cf9ea3f21f9a6e06be1cc5ad4297ab09 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 30 Apr 2026 16:20:30 +0000 Subject: [PATCH 50/68] Prepare for merging from rust-lang/rust This updates the rust-version file to f53b654a8882fd5fc036c4ca7a4ff41ce32497a6. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 2d5b9a994c6fd..ea416dc27b52d 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -ca9a134e0985765ded9cfdde4030a5df4db7e2bd +f53b654a8882fd5fc036c4ca7a4ff41ce32497a6 From 714854d53e83edd1f76a66814c9e97720904f3dd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 18:23:50 +0200 Subject: [PATCH 51/68] sembr src/backend/backend-agnostic.md --- .../src/backend/backend-agnostic.md | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index 1fb991738a6a0..c285cd7e7e1b5 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -17,8 +17,8 @@ by Denis Merigoux, October 23rd 2018 ### State of the code before the refactoring All the code related to the compilation of MIR into LLVM IR was contained -inside the `rustc_codegen_llvm` crate. Here is the breakdown of the most -important elements: +inside the `rustc_codegen_llvm` crate. +Here is the breakdown of the most important elements: * the `back` folder (7,800 LOC) implements the mechanisms for creating the different object files and archive through LLVM, but also the communication mechanisms for parallel code generation; @@ -37,9 +37,10 @@ important elements: * the `type_.rs` (300 LOC) defines most of the type translations to LLVM IR. The goal of this refactoring is to separate inside this crate code that is -specific to the LLVM from code that can be reused for other rustc backends. For -instance, the `mir` folder is almost entirely backend-specific but it relies -heavily on other parts of the crate. The separation of the code must not affect +specific to the LLVM from code that can be reused for other rustc backends. +For instance, the `mir` folder is almost entirely backend-specific but it relies +heavily on other parts of the crate. +The separation of the code must not affect the logic of the code nor its performance. For these reasons, the separation process involves two transformations that @@ -57,13 +58,14 @@ suggestion by @eddyb). ### Generic types and structures @irinagpopa started to parametrize the types of `rustc_codegen_llvm` by a -generic `Value` type, implemented in LLVM by a reference `&'ll Value`. This +generic `Value` type, implemented in LLVM by a reference `&'ll Value`. +This work has been extended to all structures inside the `mir` folder and elsewhere, as well as for LLVM's `BasicBlock` and `Type` types. The two most important structures for the LLVM codegen are `CodegenCx` and -`Builder`. They are parametrized by multiple lifetime parameters and the type -for `Value`. +`Builder`. +They are parametrized by multiple lifetime parameters and the type for `Value`. ```rust,ignore struct CodegenCx<'ll, 'tcx> { @@ -101,7 +103,8 @@ struct LocalAnalyzer<'mir, 'a, 'tcx> { ``` However, the two most important structures `CodegenCx` and `Builder` are not -defined in the backend-agnostic code. Indeed, their content is highly specific +defined in the backend-agnostic code. +Indeed, their content is highly specific of the backend and it makes more sense to leave their definition to the backend implementor than to allow just a narrow spot via a generic field for the backend's context. @@ -111,8 +114,8 @@ backend's context. Because they have to be defined by the backend, `CodegenCx` and `Builder` will be the structures implementing all the traits defining the backend's interface. These traits are defined in the folder `rustc_codegen_ssa/traits` and all the -backend-agnostic code is parametrized by them. For instance, let us explain how -a function in `base.rs` is parametrized: +backend-agnostic code is parametrized by them. +For instance, let us explain how a function in `base.rs` is parametrized: ```rust,ignore pub fn codegen_instance<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( @@ -125,7 +128,8 @@ pub fn codegen_instance<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( In this signature, we have the two lifetime parameters explained earlier and the master type `Bx` which satisfies the trait `BuilderMethods` corresponding -to the interface satisfied by the `Builder` struct. The `BuilderMethods` +to the interface satisfied by the `Builder` struct. +The `BuilderMethods` defines an associated type `Bx::CodegenCx` that itself satisfies the `CodegenMethods` traits implemented by the struct `CodegenCx`. @@ -158,24 +162,28 @@ pub trait BuilderMethods<'a, 'tcx>: ``` Finally, a master structure implementing the `ExtraBackendMethods` trait is -used for high-level codegen-driving functions like `codegen_crate` in -`base.rs`. For LLVM, it is the empty `LlvmCodegenBackend`. +used for high-level codegen-driving functions like `codegen_crate` in `base.rs`. +For LLVM, it is the empty `LlvmCodegenBackend`. `ExtraBackendMethods` should be implemented by the same structure that implements the `CodegenBackend` defined in `rustc_codegen_ssa/src/traits/backend.rs`. During the traitification process, certain functions have been converted from methods of a local structure to methods of `CodegenCx` or `Builder` and a -corresponding `self` parameter has been added. Indeed, LLVM stores information -internally that it can access when called through its API. This information +corresponding `self` parameter has been added. +Indeed, LLVM stores information +internally that it can access when called through its API. +This information does not show up in a Rust data structure carried around when these methods are -called. However, when implementing a Rust backend for `rustc`, these methods +called. +However, when implementing a Rust backend for `rustc`, these methods will need information from `CodegenCx`, hence the additional parameter (unused in the LLVM implementation of the trait). ### State of the code after the refactoring -The traits offer an API which is very similar to the API of LLVM. This is not +The traits offer an API which is very similar to the API of LLVM. +This is not the best solution since LLVM has a very special way of doing things: when adding another backend, the traits definition might be changed in order to offer more flexibility. @@ -192,16 +200,18 @@ most important elements: * `common.rs`: 350 (BA) vs 350 (LLVM); The `debuginfo` folder has been left almost untouched by the splitting and is -specific to LLVM. Only its high-level features have been traitified. +specific to LLVM. +Only its high-level features have been traitified. -The new `traits` folder has 1500 LOC only for trait definitions. Overall, the +The new `traits` folder has 1500 LOC only for trait definitions. +Overall, the 27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new 18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized -`rustc_codegen_ssa`. We can say that this refactoring allowed the reuse of +`rustc_codegen_ssa`. +We can say that this refactoring allowed the reuse of approximately 10,000 LOC that would otherwise have had to be duplicated between the multiple backends of `rustc`. The refactored version of `rustc`'s backend introduced no regression over the test suite nor in performance benchmark, which is in coherence with the nature -of the refactoring that used only compile-time parametricity (no trait -objects). +of the refactoring that used only compile-time parametricity (no trait objects). From d2b6863037d1201ce87f1dcb741894c44ef86a57 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 19:00:07 +0200 Subject: [PATCH 52/68] "the LLVM" sounds wrong --- src/doc/rustc-dev-guide/src/backend/backend-agnostic.md | 6 +++--- src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index c285cd7e7e1b5..0e93970ab087b 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -37,7 +37,7 @@ Here is the breakdown of the most important elements: * the `type_.rs` (300 LOC) defines most of the type translations to LLVM IR. The goal of this refactoring is to separate inside this crate code that is -specific to the LLVM from code that can be reused for other rustc backends. +specific to LLVM from code that can be reused for other rustc backends. For instance, the `mir` folder is almost entirely backend-specific but it relies heavily on other parts of the crate. The separation of the code must not affect @@ -46,12 +46,12 @@ the logic of the code nor its performance. For these reasons, the separation process involves two transformations that have to be done at the same time for the resulting code to compile: -1. replace all the LLVM-specific types by generics inside function signatures +1. replace all LLVM-specific types by generics inside function signatures and structure definitions; 2. encapsulate all functions calling the LLVM FFI inside a set of traits that will define the interface between backend-agnostic code and the backend. -While the LLVM-specific code will be left in `rustc_codegen_llvm`, all the new +While LLVM-specific code will be left in `rustc_codegen_llvm`, all the new traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name suggestion by @eddyb). diff --git a/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md b/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md index e6984417086d8..bd1ec6e9b1ef9 100644 --- a/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md +++ b/src/doc/rustc-dev-guide/src/debugging-support-in-rustc.md @@ -247,7 +247,7 @@ The steps of this process are as follows: 1. LLVM needs changing. - LLVM does not emit Interface types at all, so this needs to be implemented in the LLVM first. + LLVM does not emit Interface types at all, so this needs to be implemented in LLVM first. Get sign off on LLVM maintainers that this is a good idea. From dc2397c9ceca690ae0662a1719d5e8493ad33d2d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 19:01:09 +0200 Subject: [PATCH 53/68] capitalise first word in sentence --- src/doc/rustc-dev-guide/src/backend/backend-agnostic.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index 0e93970ab087b..152feb9bdb281 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -46,10 +46,10 @@ the logic of the code nor its performance. For these reasons, the separation process involves two transformations that have to be done at the same time for the resulting code to compile: -1. replace all LLVM-specific types by generics inside function signatures - and structure definitions; -2. encapsulate all functions calling the LLVM FFI inside a set of traits that - will define the interface between backend-agnostic code and the backend. +1. Replace all LLVM-specific types by generics inside function signatures + and structure definitions +2. Encapsulate all functions calling the LLVM FFI inside a set of traits that + will define the interface between backend-agnostic code and the backend While LLVM-specific code will be left in `rustc_codegen_llvm`, all the new traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name From 80f3a50056e7cb72162d15ef20d81ad2cdd238a3 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 19:01:35 +0200 Subject: [PATCH 54/68] sembr src/asm.md --- src/doc/rustc-dev-guide/src/asm.md | 60 ++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/asm.md b/src/doc/rustc-dev-guide/src/asm.md index b5857d5465e15..ae2c653db9d41 100644 --- a/src/doc/rustc-dev-guide/src/asm.md +++ b/src/doc/rustc-dev-guide/src/asm.md @@ -3,10 +3,12 @@ ## Overview Inline assembly in rustc mostly revolves around taking an `asm!` macro invocation and plumbing it -through all of the compiler layers down to LLVM codegen. Throughout the various stages, an +through all of the compiler layers down to LLVM codegen. +Throughout the various stages, an `InlineAsm` generally consists of 3 components: -- The template string, which is stored as an array of `InlineAsmTemplatePiece`. Each piece +- The template string, which is stored as an array of `InlineAsmTemplatePiece`. + Each piece represents either a literal or a placeholder for an operand (just like format strings). ```rust @@ -16,7 +18,8 @@ represents either a literal or a placeholder for an operand (just like format st } ``` -- The list of operands to the `asm!` (`in`, `[late]out`, `in[late]out`, `sym`, `const`). These are +- The list of operands to the `asm!` (`in`, `[late]out`, `in[late]out`, `sym`, `const`). + These are represented differently at each stage of lowering, but follow a common pattern: - `in`, `out` and `inout` all have an associated register class (`reg`) or explicit register (`"eax"`). @@ -25,14 +28,17 @@ one with two separate expressions for the input and output parts. - `out` and `inout` have a `late` flag (`lateout` / `inlateout`) to indicate that the register allocator is allowed to reuse an input register for this output. - `out` and the split variant of `inout` allow `_` to be specified for an output, which means -that the output is discarded. This is used to allocate scratch registers for assembly code. +that the output is discarded. +This is used to allocate scratch registers for assembly code. - `const` refers to an anonymous constants and generally works like an inline const. - `sym` is a bit special since it only accepts a path expression, which must point to a `static` or a `fn`. -- The options set at the end of the `asm!` macro. The only ones that are of particular interest to +- The options set at the end of the `asm!` macro. + The only ones that are of particular interest to rustc are `NORETURN` which makes `asm!` return `!` instead of `()`, and `RAW` which disables format -string parsing. The remaining options are mostly passed through to LLVM with little processing. +string parsing. +The remaining options are mostly passed through to LLVM with little processing. ```rust bitflags::bitflags! { @@ -54,9 +60,11 @@ string parsing. The remaining options are mostly passed through to LLVM with lit `InlineAsm` is represented as an expression in the AST with the [`ast::InlineAsm` type][inline_asm_ast]. -The `asm!` macro is implemented in `rustc_builtin_macros` and outputs an `InlineAsm` AST node. The +The `asm!` macro is implemented in `rustc_builtin_macros` and outputs an `InlineAsm` AST node. +The template string is parsed using `fmt_macros`, positional and named operands are resolved to -explicit operand indices. Since target information is not available to macro invocations, +explicit operand indices. +Since target information is not available to macro invocations, validation of the registers and register classes is deferred to AST lowering. [inline_asm_ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.InlineAsm.html @@ -66,18 +74,23 @@ validation of the registers and register classes is deferred to AST lowering. `InlineAsm` is represented as an expression in the HIR with the [`hir::InlineAsm` type][inline_asm_hir]. AST lowering is where `InlineAsmRegOrRegClass` is converted from `Symbol`s to an actual register or -register class. If any modifiers are specified for a template string placeholder, these are -validated against the set allowed for that operand type. Finally, explicit registers for inputs and +register class. +If any modifiers are specified for a template string placeholder, these are +validated against the set allowed for that operand type. +Finally, explicit registers for inputs and outputs are checked for conflicts (same register used for different operands). [inline_asm_hir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.InlineAsm.html ## Type checking -Each register class has a whitelist of types that it may be used with. After the types of all +Each register class has a whitelist of types that it may be used with. +After the types of all operands have been determined, the `intrinsicck` pass will check that these types are in the -whitelist. It also checks that split `inout` operands have compatible types and that `const` -operands are integers or floats. Suggestions are emitted where needed if a template modifier should +whitelist. +It also checks that split `inout` operands have compatible types and that `const` +operands are integers or floats. +Suggestions are emitted where needed if a template modifier should be used for an operand based on the type that was passed into it. ## THIR @@ -107,7 +120,8 @@ multiple output places where a `Call` only has a single return place output. Operands are lowered one more time before being passed to LLVM codegen, this is represented by the [`InlineAsmOperandRef` type][inline_asm_codegen] from `rustc_codegen_ssa`. The operands are lowered to LLVM operands and constraint codes as follows: -- `out` and the output part of `inout` operands are added first, as required by LLVM. Late output +- `out` and the output part of `inout` operands are added first, as required by LLVM. + Late output operands have a `=` prefix added to their constraint code, non-late output operands have a `=&` prefix added to their constraint code. - `in` operands are added normally. @@ -119,14 +133,16 @@ The template string is converted to LLVM form: - `$` characters are escaped as `$$`. - `const` operands are converted to strings and inserted directly. - Placeholders are formatted as `${X:M}` where `X` is the operand index and `M` is the modifier -character. Modifiers are converted from the Rust form to the LLVM form. +character. +Modifiers are converted from the Rust form to the LLVM form. The various options are converted to clobber constraints or LLVM attributes, refer to the [RFC](https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir) for more details. Note that LLVM is sometimes rather picky about what types it accepts for certain constraint codes -so we sometimes need to insert conversions to/from a supported type. See the target-specific +so we sometimes need to insert conversions to/from a supported type. +See the target-specific ISelLowering.cpp files in LLVM for details of what types are supported for each register class. [inline_asm_codegen]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/enum.InlineAsmOperandRef.html @@ -134,18 +150,22 @@ ISelLowering.cpp files in LLVM for details of what types are supported for each ## Adding support for new architectures Adding inline assembly support to an architecture is mostly a matter of defining the registers and -register classes for that architecture. All the definitions for register classes are located in +register classes for that architecture. +All the definitions for register classes are located in `compiler/rustc_target/asm/`. Additionally you will need to implement lowering of these register classes to LLVM constraint codes in `compiler/rustc_codegen_llvm/asm.rs`. When adding a new architecture, make sure to cross-reference with the LLVM source code: -- LLVM has restrictions on which types can be used with a particular constraint code. Refer to the +- LLVM has restrictions on which types can be used with a particular constraint code. + Refer to the `getRegForInlineAsmConstraint` function in `lib/Target/${ARCH}/${ARCH}ISelLowering.cpp`. - LLVM reserves certain registers for its internal use, which causes them to not be saved/restored -properly around inline assembly blocks. These registers are listed in the `getReservedRegs` -function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. Any "conditionally" reserved register +properly around inline assembly blocks. +These registers are listed in the `getReservedRegs` +function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. +Any "conditionally" reserved register such as the frame/base pointer must always be treated as reserved for Rust purposes because we can't know ahead of time whether a function will require a frame/base pointer. From d6af96f447f8fde713ae14c7f10466dc0491a85c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:02:35 +0200 Subject: [PATCH 55/68] reflow --- src/doc/rustc-dev-guide/src/asm.md | 102 +++++++++++++++-------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/asm.md b/src/doc/rustc-dev-guide/src/asm.md index ae2c653db9d41..8a027e3dbee2d 100644 --- a/src/doc/rustc-dev-guide/src/asm.md +++ b/src/doc/rustc-dev-guide/src/asm.md @@ -4,12 +4,12 @@ Inline assembly in rustc mostly revolves around taking an `asm!` macro invocation and plumbing it through all of the compiler layers down to LLVM codegen. -Throughout the various stages, an -`InlineAsm` generally consists of 3 components: +Throughout the various stages, +an `InlineAsm` generally consists of 3 components: - The template string, which is stored as an array of `InlineAsmTemplatePiece`. - Each piece -represents either a literal or a placeholder for an operand (just like format strings). + Each piece represents either a literal or a placeholder for an operand + (just like format strings). ```rust pub enum InlineAsmTemplatePiece { @@ -19,26 +19,28 @@ represents either a literal or a placeholder for an operand (just like format st ``` - The list of operands to the `asm!` (`in`, `[late]out`, `in[late]out`, `sym`, `const`). - These are -represented differently at each stage of lowering, but follow a common pattern: - - `in`, `out` and `inout` all have an associated register class (`reg`) or explicit register -(`"eax"`). - - `inout` has 2 forms: one with a single expression that is both read from and written to, and -one with two separate expressions for the input and output parts. + These are represented differently at each stage of lowering, + but follow a common pattern: + - `in`, `out`, and `inout` all have an associated register class (`reg`) + or explicit register (`"eax"`). + - `inout` has 2 forms: + one with a single expression that is both read from and written to, + and one with two separate expressions for the input and output parts. - `out` and `inout` have a `late` flag (`lateout` / `inlateout`) to indicate that the register -allocator is allowed to reuse an input register for this output. - - `out` and the split variant of `inout` allow `_` to be specified for an output, which means -that the output is discarded. -This is used to allocate scratch registers for assembly code. - - `const` refers to an anonymous constants and generally works like an inline const. - - `sym` is a bit special since it only accepts a path expression, which must point to a `static` -or a `fn`. + allocator is allowed to reuse an input register for this output. + - `out` and the split variant of `inout` allow `_` to be specified for an output, + which means that the output is discarded. + This is used to allocate scratch registers for assembly code. + - `const` refers to an anonymous constants, + and generally works like an inline const. + - `sym` is a bit special since it only accepts a path expression, + which must point to a `static` or a `fn`. - The options set at the end of the `asm!` macro. The only ones that are of particular interest to -rustc are `NORETURN` which makes `asm!` return `!` instead of `()`, and `RAW` which disables format -string parsing. -The remaining options are mostly passed through to LLVM with little processing. + rustc are `NORETURN` which makes `asm!` return `!` instead of `()`, + and `RAW` which disables format string parsing. + The remaining options are mostly passed through to LLVM with little processing. ```rust bitflags::bitflags! { @@ -61,9 +63,8 @@ The remaining options are mostly passed through to LLVM with little processing. `InlineAsm` is represented as an expression in the AST with the [`ast::InlineAsm` type][inline_asm_ast]. The `asm!` macro is implemented in `rustc_builtin_macros` and outputs an `InlineAsm` AST node. -The -template string is parsed using `fmt_macros`, positional and named operands are resolved to -explicit operand indices. +The template string is parsed using `fmt_macros`, +positional and named operands are resolved to explicit operand indices. Since target information is not available to macro invocations, validation of the registers and register classes is deferred to AST lowering. @@ -85,9 +86,8 @@ outputs are checked for conflicts (same register used for different operands). ## Type checking Each register class has a whitelist of types that it may be used with. -After the types of all -operands have been determined, the `intrinsicck` pass will check that these types are in the -whitelist. +After the types of all operands have been determined, +the `intrinsicck` pass will check that these types are in the whitelist. It also checks that split `inout` operands have compatible types and that `const` operands are integers or floats. Suggestions are emitted where needed if a template modifier should @@ -98,8 +98,8 @@ be used for an operand based on the type that was passed into it. `InlineAsm` is represented as an expression in the THIR with the [`InlineAsmExpr` type][inline_asm_thir]. The only significant change compared to HIR is that `Sym` has been lowered to either a `SymFn` -whose `expr` is a `Literal` ZST of the `fn`, or a `SymStatic` which points to the `DefId` of a -`static`. +whose `expr` is a `Literal` ZST of the `fn`, +or a `SymStatic` which points to the `DefId` of a `static`. [inline_asm_thir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/thir/struct.InlineAsmExpr.html @@ -117,35 +117,36 @@ multiple output places where a `Call` only has a single return place output. ## Codegen -Operands are lowered one more time before being passed to LLVM codegen, this is represented by the [`InlineAsmOperandRef` type][inline_asm_codegen] from `rustc_codegen_ssa`. +Operands are lowered one more time before being passed to LLVM codegen. +This is represented by the [`InlineAsmOperandRef` type][inline_asm_codegen] from `rustc_codegen_ssa`. The operands are lowered to LLVM operands and constraint codes as follows: - `out` and the output part of `inout` operands are added first, as required by LLVM. - Late output -operands have a `=` prefix added to their constraint code, non-late output operands have a `=&` -prefix added to their constraint code. + Late output operands have a `=` prefix added to their constraint code, + and non-late output operands have a `=&` prefix added to their constraint code. - `in` operands are added normally. - `inout` operands are tied to the matching output operand. -- `sym` operands are passed as function pointers or pointers, using the `"s"` constraint. +- `sym` operands are passed as function pointers or pointers, + using the `"s"` constraint. - `const` operands are formatted to a string and directly inserted in the template string. The template string is converted to LLVM form: - `$` characters are escaped as `$$`. - `const` operands are converted to strings and inserted directly. -- Placeholders are formatted as `${X:M}` where `X` is the operand index and `M` is the modifier -character. -Modifiers are converted from the Rust form to the LLVM form. +- Placeholders are formatted as `${X:M}`, + where `X` is the operand index and `M` is the modifier character. + Modifiers are converted from the Rust form to the LLVM form. -The various options are converted to clobber constraints or LLVM attributes, refer to the -[RFC](https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir) -for more details. +The various options are converted to clobber constraints or LLVM attributes; +refer to the [RFC] for more details. Note that LLVM is sometimes rather picky about what types it accepts for certain constraint codes so we sometimes need to insert conversions to/from a supported type. -See the target-specific -ISelLowering.cpp files in LLVM for details of what types are supported for each register class. +See the target-specific ISelLowering.cpp files in LLVM +for details of what types are supported for each register class. [inline_asm_codegen]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/traits/enum.InlineAsmOperandRef.html +[RFC]: https://github.com/Amanieu/rfcs/blob/inline-asm/text/0000-inline-asm.md#mapping-to-llvm-ir ## Adding support for new architectures @@ -159,15 +160,16 @@ in `compiler/rustc_codegen_llvm/asm.rs`. When adding a new architecture, make sure to cross-reference with the LLVM source code: - LLVM has restrictions on which types can be used with a particular constraint code. - Refer to the -`getRegForInlineAsmConstraint` function in `lib/Target/${ARCH}/${ARCH}ISelLowering.cpp`. -- LLVM reserves certain registers for its internal use, which causes them to not be saved/restored -properly around inline assembly blocks. -These registers are listed in the `getReservedRegs` -function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. -Any "conditionally" reserved register -such as the frame/base pointer must always be treated as reserved for Rust purposes because we -can't know ahead of time whether a function will require a frame/base pointer. + Refer to the `getRegForInlineAsmConstraint` function + in `lib/Target/${ARCH}/${ARCH}ISelLowering.cpp`. +- LLVM reserves certain registers for its internal use, + which causes them to not be saved/restored properly around inline assembly blocks. + These registers are listed in the `getReservedRegs` + function in `lib/Target/${ARCH}/${ARCH}RegisterInfo.cpp`. + Any "conditionally" reserved register, + such as the frame/base pointer, + must always be treated as reserved for Rust purposes because we + can't know ahead of time whether a function will require a frame/base pointer. ## Tests From 44034eca66adef4b8cc4ae947db65e551f2557e6 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:07:22 +0200 Subject: [PATCH 56/68] inclusive wording --- src/doc/rustc-dev-guide/src/asm.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/asm.md b/src/doc/rustc-dev-guide/src/asm.md index 8a027e3dbee2d..9a87918538def 100644 --- a/src/doc/rustc-dev-guide/src/asm.md +++ b/src/doc/rustc-dev-guide/src/asm.md @@ -85,9 +85,9 @@ outputs are checked for conflicts (same register used for different operands). ## Type checking -Each register class has a whitelist of types that it may be used with. +Each register class has an allowlist of types that it may be used with. After the types of all operands have been determined, -the `intrinsicck` pass will check that these types are in the whitelist. +the `intrinsicck` pass will check that these types are in the allowlist. It also checks that split `inout` operands have compatible types and that `const` operands are integers or floats. Suggestions are emitted where needed if a template modifier should From eb8f85e7f48cb4fdcf8ea0f203736e8579c57b6d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Apr 2026 14:00:08 +0200 Subject: [PATCH 57/68] c-variadic: document `Clone` and `Drop` instances --- library/core/src/ffi/va_list.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 0a8b43622faef..fe6fae4789571 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -251,6 +251,9 @@ impl VaList<'_> { #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] impl<'f> const Clone for VaList<'f> { + /// Clone the [`VaList`], producing a second independent cursor into the variable argument list. + /// + /// Corresponds to `va_copy` in C. #[inline] // Avoid codegen when not used to help backends that don't support VaList. fn clone(&self) -> Self { // We only implement Clone and not Copy because some future target might not be able to @@ -263,8 +266,14 @@ impl<'f> const Clone for VaList<'f> { #[rustc_const_unstable(feature = "const_c_variadic", issue = "151787")] impl<'f> const Drop for VaList<'f> { + /// Drop the [`VaList`]. + /// + /// Corresponds to `va_end` in C. #[inline] // Avoid codegen when not used to help backends that don't support VaList. fn drop(&mut self) { + // Call the rust `va_end` intrinsic, which is a no-op and does not map to LLVM `va_end`. + // The rust intrinsic exists as a hook for Miri to check for UB. + // // SAFETY: this variable argument list is being dropped, so won't be read from again. unsafe { va_end(self) } } From ab19cd3dea151cf070d504c605c618c1d05b91e9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Apr 2026 14:00:51 +0200 Subject: [PATCH 58/68] c-variadic: add a `Copy` bound to `VaArgSafe` because a VaList can be cloned, the same c-variadic argument can be read twice and that is only safe if the argument type is copy --- library/core/src/ffi/va_list.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index fe6fae4789571..afd166d4931ef 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -333,7 +333,7 @@ mod sealed { // types with an alignment larger than 8, or with a non-scalar layout. Inline assembly can be used // to accept unsupported types in the meantime. #[lang = "va_arg_safe"] -pub unsafe trait VaArgSafe: sealed::Sealed {} +pub unsafe trait VaArgSafe: Copy + sealed::Sealed {} crate::cfg_select! { any(target_arch = "avr", target_arch = "msp430") => { From ba9444c4f48c56b8351eace4f1b9e93b41083aba Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 26 Apr 2026 14:01:18 +0200 Subject: [PATCH 59/68] c-variadic: test that const and mutable void and char pointers implement `VaArgSafe` --- library/core/src/ffi/va_list.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index afd166d4931ef..a27f9e2deec94 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -390,6 +390,12 @@ const _: () = { va_arg_safe_check::(); va_arg_safe_check::(); + + va_arg_safe_check::<*const crate::ffi::c_void>(); + va_arg_safe_check::<*mut crate::ffi::c_void>(); + + va_arg_safe_check::<*const crate::ffi::c_char>(); + va_arg_safe_check::<*mut crate::ffi::c_char>(); }; impl<'f> VaList<'f> { From d12e52d852ab0522bf5928b6f00a5f2f5b20019a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:28:39 +0200 Subject: [PATCH 60/68] reflow --- .../src/backend/backend-agnostic.md | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index 152feb9bdb281..566c7b04fabd7 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -38,10 +38,11 @@ Here is the breakdown of the most important elements: The goal of this refactoring is to separate inside this crate code that is specific to LLVM from code that can be reused for other rustc backends. -For instance, the `mir` folder is almost entirely backend-specific but it relies -heavily on other parts of the crate. -The separation of the code must not affect -the logic of the code nor its performance. +For instance, +the `mir` folder is almost entirely backend-specific, +but it relies heavily on other parts of the crate. +The separation of the code must not affect the logic of the code, +nor its performance. For these reasons, the separation process involves two transformations that have to be done at the same time for the resulting code to compile: @@ -102,10 +103,11 @@ struct LocalAnalyzer<'mir, 'a, 'tcx> { } ``` -However, the two most important structures `CodegenCx` and `Builder` are not -defined in the backend-agnostic code. -Indeed, their content is highly specific -of the backend and it makes more sense to leave their definition to the backend +However, the two most important structures, +`CodegenCx` and `Builder`, +are not defined in the backend-agnostic code. +Indeed, their content is highly specific to the backend, +and it makes more sense to leave their definition to the backend implementor than to allow just a narrow spot via a generic field for the backend's context. @@ -129,9 +131,9 @@ pub fn codegen_instance<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( In this signature, we have the two lifetime parameters explained earlier and the master type `Bx` which satisfies the trait `BuilderMethods` corresponding to the interface satisfied by the `Builder` struct. -The `BuilderMethods` -defines an associated type `Bx::CodegenCx` that itself satisfies the -`CodegenMethods` traits implemented by the struct `CodegenCx`. +The `BuilderMethods` defines an associated type, `Bx::CodegenCx`, +that itself satisfies the `CodegenMethods` traits implemented by the struct, +`CodegenCx`. On the trait side, here is an example with part of the definition of `BuilderMethods` in `traits/builder.rs`: @@ -183,10 +185,9 @@ in the LLVM implementation of the trait). ### State of the code after the refactoring The traits offer an API which is very similar to the API of LLVM. -This is not -the best solution since LLVM has a very special way of doing things: when -adding another backend, the traits definition might be changed in order to -offer more flexibility. +This is not the best solution since LLVM has a very special way of doing things: +when adding another backend, +the traits definition might be changed in order to offer more flexibility. However, the current separation between backend-agnostic and LLVM-specific code has allowed the reuse of a significant part of the old `rustc_codegen_llvm`. @@ -204,8 +205,8 @@ specific to LLVM. Only its high-level features have been traitified. The new `traits` folder has 1500 LOC only for trait definitions. -Overall, the -27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new +Overall, +the 27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new 18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized `rustc_codegen_ssa`. We can say that this refactoring allowed the reuse of From 958600a559a467987a9dbb508fdfb5f9bf858f5c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:31:25 +0200 Subject: [PATCH 61/68] sembr src/backend/backend-agnostic.md It has required much reflowing (and sembr tool is not fancy enough yet) --- src/doc/rustc-dev-guide/ci/sembr/src/main.rs | 2 +- .../src/backend/backend-agnostic.md | 45 +++++++------------ 2 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index a9b4d182bd400..4038f112d59fd 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -15,7 +15,7 @@ struct Cli { /// Modify files that do not comply overwrite: bool, /// Applies to lines that are to be split - #[arg(long, default_value_t = 80)] + #[arg(long, default_value_t = 100)] line_length_limit: usize, } diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index 566c7b04fabd7..ad0dbb3aa9036 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -1,15 +1,13 @@ # Backend Agnostic Codegen -[`rustc_codegen_ssa`] -provides an abstract interface for all backends to implement, +[`rustc_codegen_ssa`] provides an abstract interface for all backends to implement, namely LLVM, [Cranelift], and [GCC]. [Cranelift]: https://github.com/rust-lang/rustc_codegen_cranelift [GCC]: https://github.com/rust-lang/rustc_codegen_gcc [`rustc_codegen_ssa`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html -Below is some background information on the refactoring that created this -abstract interface. +Below is some background information on the refactoring that created this abstract interface. ## Refactoring of `rustc_codegen_llvm` by Denis Merigoux, October 23rd 2018 @@ -22,12 +20,10 @@ Here is the breakdown of the most important elements: * the `back` folder (7,800 LOC) implements the mechanisms for creating the different object files and archive through LLVM, but also the communication mechanisms for parallel code generation; -* the `debuginfo` (3,200 LOC) folder contains all code that passes debug - information down to LLVM; +* the `debuginfo` (3,200 LOC) folder contains all code that passes debug information down to LLVM; * the `llvm` (2,200 LOC) folder defines the FFI necessary to communicate with LLVM using the C++ API; -* the `mir` (4,300 LOC) folder implements the actual lowering from MIR to LLVM - IR; +* the `mir` (4,300 LOC) folder implements the actual lowering from MIR to LLVM IR; * the `base.rs` (1,300 LOC) file contains some helper functions but also the high-level code that launches the code generation and distributes the work. * the `builder.rs` (1,200 LOC) file contains all the functions generating @@ -53,19 +49,16 @@ have to be done at the same time for the resulting code to compile: will define the interface between backend-agnostic code and the backend While LLVM-specific code will be left in `rustc_codegen_llvm`, all the new -traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name -suggestion by @eddyb). +traits and backend-agnostic code will be moved in `rustc_codegen_ssa` (name suggestion by @eddyb). ### Generic types and structures @irinagpopa started to parametrize the types of `rustc_codegen_llvm` by a generic `Value` type, implemented in LLVM by a reference `&'ll Value`. -This -work has been extended to all structures inside the `mir` folder and elsewhere, +This work has been extended to all structures inside the `mir` folder and elsewhere, as well as for LLVM's `BasicBlock` and `Type` types. -The two most important structures for the LLVM codegen are `CodegenCx` and -`Builder`. +The two most important structures for the LLVM codegen are `CodegenCx` and `Builder`. They are parametrized by multiple lifetime parameters and the type for `Value`. ```rust,ignore @@ -86,10 +79,8 @@ The code in `rustc_codegen_llvm` has to deal with multiple explicit lifetime parameters, that correspond to the following: * `'tcx` is the longest lifetime, that corresponds to the original `TyCtxt` containing the program's information; -* `'a` is a short-lived reference of a `CodegenCx` or another object inside a - struct; -* `'ll` is the lifetime of references to LLVM objects such as `Value` or - `Type`. +* `'a` is a short-lived reference of a `CodegenCx` or another object inside a struct; +* `'ll` is the lifetime of references to LLVM objects such as `Value` or `Type`. Although there are already many lifetime parameters in the code, making it generic uncovered situations where the borrow-checker was passing only due to @@ -108,8 +99,7 @@ However, the two most important structures, are not defined in the backend-agnostic code. Indeed, their content is highly specific to the backend, and it makes more sense to leave their definition to the backend -implementor than to allow just a narrow spot via a generic field for the -backend's context. +implementor than to allow just a narrow spot via a generic field for the backend's context. ### Traits and interface @@ -167,16 +157,13 @@ Finally, a master structure implementing the `ExtraBackendMethods` trait is used for high-level codegen-driving functions like `codegen_crate` in `base.rs`. For LLVM, it is the empty `LlvmCodegenBackend`. `ExtraBackendMethods` should be implemented by the same structure that -implements the `CodegenBackend` defined in -`rustc_codegen_ssa/src/traits/backend.rs`. +implements the `CodegenBackend` defined in `rustc_codegen_ssa/src/traits/backend.rs`. During the traitification process, certain functions have been converted from methods of a local structure to methods of `CodegenCx` or `Builder` and a corresponding `self` parameter has been added. -Indeed, LLVM stores information -internally that it can access when called through its API. -This information -does not show up in a Rust data structure carried around when these methods are +Indeed, LLVM stores information internally that it can access when called through its API. +This information does not show up in a Rust data structure carried around when these methods are called. However, when implementing a Rust backend for `rustc`, these methods will need information from `CodegenCx`, hence the additional parameter (unused @@ -200,15 +187,13 @@ most important elements: * `builder.rs`: 1,400 (BA) vs 0 (LLVM); * `common.rs`: 350 (BA) vs 350 (LLVM); -The `debuginfo` folder has been left almost untouched by the splitting and is -specific to LLVM. +The `debuginfo` folder has been left almost untouched by the splitting and is specific to LLVM. Only its high-level features have been traitified. The new `traits` folder has 1500 LOC only for trait definitions. Overall, the 27,000 LOC-sized old `rustc_codegen_llvm` code has been split into the new -18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized -`rustc_codegen_ssa`. +18,500 LOC-sized new `rustc_codegen_llvm` and the 12,000 LOC-sized `rustc_codegen_ssa`. We can say that this refactoring allowed the reuse of approximately 10,000 LOC that would otherwise have had to be duplicated between the multiple backends of `rustc`. From ac12c696b806d7a8a5e06ca212b1469a24976ded Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 30 Apr 2026 20:32:15 +0200 Subject: [PATCH 62/68] remove custom `va_end` implementation in the LLVM backend it should use the fallback body instead --- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 3 ++- tests/codegen-llvm/c-variadic-va-end.rs | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/c-variadic-va-end.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index f4a5e8baa2a5f..aa144558211ef 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -148,8 +148,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return Ok(()); } + // va_end uses the fallback body (a no-op). sym::va_start => bx.va_start(args[0].immediate()), - sym::va_end => bx.va_end(args[0].immediate()), + sym::size_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); diff --git a/tests/codegen-llvm/c-variadic-va-end.rs b/tests/codegen-llvm/c-variadic-va-end.rs new file mode 100644 index 0000000000000..b0d7371ba01c2 --- /dev/null +++ b/tests/codegen-llvm/c-variadic-va-end.rs @@ -0,0 +1,19 @@ +//@ add-minicore +//@ compile-flags: -Copt-level=3 +#![feature(c_variadic)] +#![crate_type = "lib"] + +unsafe extern "C" { + fn g(v: *mut u8); +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn f(mut args: ...) { + // CHECK: call void @llvm.va_start + unsafe { g(&raw mut args as *mut u8) } + // We expect one call to the LLVM va_end from our desugaring of `...`. The `Drop` implementation + // should only call the rust va_end intrinsic, which is a no-op. + // + // CHECK: call void @llvm.va_end + // CHECK-NOT: call void @llvm.va_end +} From 373f5d3987658db9804e42ffdea2af5fe6afb6bb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:33:33 +0200 Subject: [PATCH 63/68] sembr src/tests/compiletest.md --- src/doc/rustc-dev-guide/src/tests/compiletest.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 53a6c0cb5737d..81084132f0838 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -349,8 +349,8 @@ If you need to work with `#![no_std]` cross-compiling tests, consult the #### Conditional assembly tests based on instruction support Tests that depend on specific assembly instructions being available can use the -`//@ needs-asm-mnemonic: ` directive. This will skip the test if the -target backend does not support the specified instruction mnemonic. +`//@ needs-asm-mnemonic: ` directive. +This will skip the test if the target backend does not support the specified instruction mnemonic. For example, a test that requires the `RET` instruction: ```rust,ignore From c4ad3037e63d302f3eecb1ffd618370bc45cfd8d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:34:39 +0200 Subject: [PATCH 64/68] sembr src/tests/directives.md --- src/doc/rustc-dev-guide/src/tests/directives.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index caa400b573135..6c8e787ddbff7 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -183,8 +183,7 @@ The following directives will check rustc build settings and target settings: - `needs-threads` — ignores if the target does not have threading support - `needs-subprocess` — ignores if the target does not have subprocess support - `needs-symlink` — ignores if the target does not support symlinks. - This can be the case on Windows if the developer did not enable privileged symlink - permissions. + This can be the case on Windows if the developer did not enable privileged symlink permissions. - `ignore-std-debug-assertions` — ignores if std was built with debug assertions. - `needs-std-debug-assertions` — ignores if std was not built with debug assertions. - `ignore-std-remap-debuginfo` — ignores if std was built with remapping of it's sources. From cd47965c28a835bb08942fa61dba96b8afe6290c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:35:27 +0200 Subject: [PATCH 65/68] sembr src/panic-implementation.md --- .../src/panic-implementation.md | 54 +++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/panic-implementation.md b/src/doc/rustc-dev-guide/src/panic-implementation.md index 8ce949ae2925f..efbbf2a2aac30 100644 --- a/src/doc/rustc-dev-guide/src/panic-implementation.md +++ b/src/doc/rustc-dev-guide/src/panic-implementation.md @@ -3,9 +3,9 @@ ## Step 1: Invocation of the `panic!` macro. There are actually two panic macros - one defined in `core`, and one defined in `std`. -This is due to the fact that code in `core` can panic. `core` is built before `std`, -but we want panics to use the same machinery at runtime, whether they originate in `core` -or `std`. +This is due to the fact that code in `core` can panic. +`core` is built before `std`, +but we want panics to use the same machinery at runtime, whether they originate in `core` or `std`. ### core definition of panic! @@ -30,8 +30,8 @@ unsafe { panic_impl(&pi) } Actually resolving this goes through several layers of indirection: 1. In [`compiler/rustc_hir/src/weak_lang_items.rs`], `panic_impl` is - declared as 'weak lang item', with the symbol `rust_begin_unwind`. This is - used in `rustc_hir_analysis/src/collect.rs` to set the actual symbol name to + declared as 'weak lang item', with the symbol `rust_begin_unwind`. + This is used in `rustc_hir_analysis/src/collect.rs` to set the actual symbol name to `rust_begin_unwind`. Note that `panic_impl` is declared in an `extern "Rust"` block, @@ -52,26 +52,33 @@ pub fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! { The special `panic_handler` attribute is resolved via [`compiler/rustc_passes/src/lang_items.rs`]. The [`extract_ast`] function converts the `panic_handler` attribute to a `panic_impl` lang item. -Now, we have a matching `panic_handler` lang item in the `std`. This function goes +Now, we have a matching `panic_handler` lang item in the `std`. +This function goes through the same process as the `extern { fn panic_impl }` definition in `core`, ending -up with a symbol name of `rust_begin_unwind`. At link time, the symbol reference in `core` +up with a symbol name of `rust_begin_unwind`. +At link time, the symbol reference in `core` will be resolved to the definition of `std` (the function called `panic_handler` in the Rust source). -Thus, control flow will pass from core to std at runtime. This allows panics from `core` +Thus, control flow will pass from core to std at runtime. +This allows panics from `core` to go through the same infrastructure that other panics use (panic hooks, unwinding, etc) ### std implementation of panic! -This is where the actual panic-related logic begins. In [`library/std/src/panicking.rs`], -control passes to `panic_with_hook`. This method is responsible -for invoking the global panic hook, and checking for double panics. Finally, +This is where the actual panic-related logic begins. +In [`library/std/src/panicking.rs`], +control passes to `panic_with_hook`. +This method is responsible for invoking the global panic hook, and checking for double panics. +Finally, we call `__rust_start_panic`, which is provided by the panic runtime. The call to `__rust_start_panic` is very weird - it is passed a `*mut &mut dyn PanicPayload`, -converted to an `usize`. Let's break this type down: +converted to an `usize`. +Let's break this type down: -1. `PanicPayload` is an internal trait. It is implemented for `PanicPayload` +1. `PanicPayload` is an internal trait. + It is implemented for `PanicPayload` (a wrapper around the user-supplied payload type), and has a method `fn take_box(&mut self) -> *mut (dyn Any + Send)`. This method takes the user-provided payload (`T: Any + Send`), @@ -81,16 +88,18 @@ boxes it, and converts the box to a raw pointer. However, this is a fat pointer (twice the size of a `usize`). To pass this to the panic runtime across an FFI boundary, we take a mutable reference *to this mutable reference* (`&mut &mut dyn PanicPayload`), and convert it to a raw -pointer (`*mut &mut dyn PanicPayload`). The outer raw pointer is a thin pointer, since it points to -a `Sized` type (a mutable reference). Therefore, we can convert this thin pointer into a `usize`, +pointer (`*mut &mut dyn PanicPayload`). +The outer raw pointer is a thin pointer, since it points to a `Sized` type (a mutable reference). +Therefore, we can convert this thin pointer into a `usize`, which is suitable for passing across an FFI boundary. -Finally, we call `__rust_start_panic` with this `usize`. We have now entered the panic runtime. +Finally, we call `__rust_start_panic` with this `usize`. +We have now entered the panic runtime. ## Step 2: The panic runtime -Rust provides two panic runtimes: `panic_abort` and `panic_unwind`. The user chooses -between them at build time via their `Cargo.toml` +Rust provides two panic runtimes: `panic_abort` and `panic_unwind`. +The user chooses between them at build time via their `Cargo.toml` `panic_abort` is extremely simple: its implementation of `__rust_start_panic` just aborts, as you would expect. @@ -99,13 +108,14 @@ as you would expect. In its implementation of `__rust_start_panic`, we take the `usize`, convert it back to a `*mut &mut dyn PanicPayload`, dereference it, and call `take_box` -on the `&mut dyn PanicPayload`. At this point, we have a raw pointer to the payload +on the `&mut dyn PanicPayload`. +At this point, we have a raw pointer to the payload itself (a `*mut (dyn Send + Any)`): that is, a raw pointer to the actual value provided by the user who called `panic!`. -At this point, the platform-independent code ends. We now call into -platform-specific unwinding logic (e.g `unwind`). This code is -responsible for unwinding the stack, running any 'landing pads' associated +At this point, the platform-independent code ends. +We now call into platform-specific unwinding logic (e.g `unwind`). +This code is responsible for unwinding the stack, running any 'landing pads' associated with each frame (currently, running destructors), and transferring control to the `catch_unwind` frame. From c0e3f05f46dbc0f5fedb95585a628140b60a61fc Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Thu, 30 Apr 2026 20:37:36 +0200 Subject: [PATCH 66/68] reflow --- .../src/panic-implementation.md | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/panic-implementation.md b/src/doc/rustc-dev-guide/src/panic-implementation.md index efbbf2a2aac30..5101556941dc3 100644 --- a/src/doc/rustc-dev-guide/src/panic-implementation.md +++ b/src/doc/rustc-dev-guide/src/panic-implementation.md @@ -79,19 +79,19 @@ Let's break this type down: 1. `PanicPayload` is an internal trait. It is implemented for `PanicPayload` -(a wrapper around the user-supplied payload type), and has a method -`fn take_box(&mut self) -> *mut (dyn Any + Send)`. -This method takes the user-provided payload (`T: Any + Send`), -boxes it, and converts the box to a raw pointer. + (a wrapper around the user-supplied payload type), and has a method + `fn take_box(&mut self) -> *mut (dyn Any + Send)`. + This method takes the user-provided payload (`T: Any + Send`), + boxes it, and converts the box to a raw pointer. 2. When we call `__rust_start_panic`, we have an `&mut dyn PanicPayload`. -However, this is a fat pointer (twice the size of a `usize`). -To pass this to the panic runtime across an FFI boundary, we take a mutable -reference *to this mutable reference* (`&mut &mut dyn PanicPayload`), and convert it to a raw -pointer (`*mut &mut dyn PanicPayload`). -The outer raw pointer is a thin pointer, since it points to a `Sized` type (a mutable reference). -Therefore, we can convert this thin pointer into a `usize`, -which is suitable for passing across an FFI boundary. + However, this is a fat pointer (twice the size of a `usize`). + To pass this to the panic runtime across an FFI boundary, we take a mutable + reference *to this mutable reference* (`&mut &mut dyn PanicPayload`), and convert it to a raw + pointer (`*mut &mut dyn PanicPayload`). + The outer raw pointer is a thin pointer, since it points to a `Sized` type (a mutable reference). + Therefore, we can convert this thin pointer into a `usize`, + which is suitable for passing across an FFI boundary. Finally, we call `__rust_start_panic` with this `usize`. We have now entered the panic runtime. @@ -123,7 +123,6 @@ Note that all panics either abort the process or get caught by some call to `cat In particular, in std's [runtime service], the call to the user-provided `main` function is wrapped in `catch_unwind`. - [runtime service]: https://github.com/rust-lang/rust/blob/HEAD/library/std/src/rt.rs [`library/core/src/panicking.rs`]: https://doc.rust-lang.org/core/panicking/index.html [`compiler/rustc_hir/src/weak_lang_items.rs`]: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_hir/src/weak_lang_items.rs From 9454be304f3dcf7af8e986909fdceaec01126079 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Apr 2026 14:40:23 +1000 Subject: [PATCH 67/68] Invert dependency between `rustc_error_messages` and `rustc_ast*`. `rustc_error_messages` currently depends on `rustc_ast`/`rustc_ast_pretty`. This is odd, because `rustc_error_messages` feels like a very low-level module but `rustc_ast`/`rustc_ast_pretty` do not. The reason is that a few AST types impl `IntoDiagArg` via pretty-printing. `rustc_error_messages` can define `IntoDiagArg` and then impl it for the AST types. But if we invert the dependency we hit a problem with the orphan rule: `rustc_ast` must impl `IntoDiagArg` for the AST types, but that requires calling pretty-printing code which is in `rustc_ast_pretty`, a downstream crate. This commit avoids this problem by just removing the `IntoDiagArg` impls for these AST types. There aren't that many of them, and we can just use `String` in the relevant error structs and use the pretty printer in the downstream crates that construct the error structs. There are plenty of existing examples where `String` is used in error structs. There is now no dependency between `rustc_ast*` and `rustc_error_messages`. --- Cargo.lock | 2 - .../rustc_ast_passes/src/ast_validation.rs | 4 +- compiler/rustc_ast_passes/src/errors.rs | 5 +- compiler/rustc_builtin_macros/src/env.rs | 5 +- compiler/rustc_builtin_macros/src/errors.rs | 6 +-- .../rustc_codegen_gcc/src/intrinsic/simd.rs | 2 +- compiler/rustc_codegen_ssa/src/errors.rs | 4 +- compiler/rustc_error_messages/Cargo.toml | 2 - .../src/diagnostic_impls.rs | 47 ----------------- compiler/rustc_expand/src/errors.rs | 5 +- compiler/rustc_expand/src/expand.rs | 6 +-- compiler/rustc_parse/src/errors.rs | 50 ++++++++++--------- compiler/rustc_parse/src/parser/expr.rs | 14 +++--- compiler/rustc_parse/src/parser/item.rs | 8 +-- .../rustc_parse/src/parser/nonterminal.rs | 5 +- compiler/rustc_parse/src/parser/pat.rs | 7 +-- 16 files changed, 64 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53cff99b4a199..cfdfb78b3d274 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3853,8 +3853,6 @@ dependencies = [ "icu_list", "icu_locale", "intl-memoizer", - "rustc_ast", - "rustc_ast_pretty", "rustc_baked_icu_data", "rustc_data_structures", "rustc_macros", diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 770189d8c7999..cc1bc5d5344a5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1117,8 +1117,8 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara dcx.emit_err(errors::OutOfOrderParams { spans: spans.clone(), sugg_span: span, - param_ord, - max_param, + param_ord: param_ord.to_string(), + max_param: max_param.to_string(), ordered_params: &ordered_params, }); } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index b3a22c0c99549..a6b75cb70a548 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -1,7 +1,6 @@ //! Errors emitted by ast_passes. use rustc_abi::ExternAbi; -use rustc_ast::ParamKindOrd; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -632,8 +631,8 @@ pub(crate) struct OutOfOrderParams<'a> { applicability = "machine-applicable" )] pub sugg_span: Span, - pub param_ord: &'a ParamKindOrd, - pub max_param: &'a ParamKindOrd, + pub param_ord: String, + pub max_param: String, pub ordered_params: &'a str, } diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs index f60113dbfc9bc..d9af43fcd1c3d 100644 --- a/compiler/rustc_builtin_macros/src/env.rs +++ b/compiler/rustc_builtin_macros/src/env.rs @@ -9,6 +9,7 @@ use std::env::VarError; use rustc_ast::token::{self, LitKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ExprKind, GenericArg, Mutability}; +use rustc_ast_pretty::pprust; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_span::edit_distance::edit_distance; use rustc_span::{Ident, Span, Symbol, kw, sym}; @@ -158,13 +159,13 @@ pub(crate) fn expand_env<'cx>( cx.dcx().emit_err(errors::EnvNotDefined::CargoEnvVar { span, var: *symbol, - var_expr: &var_expr, + var_expr: pprust::expr_to_string(&var_expr), }) } else { cx.dcx().emit_err(errors::EnvNotDefined::CustomEnvVar { span, var: *symbol, - var_expr: &var_expr, + var_expr: pprust::expr_to_string(&var_expr), }) } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index ad641beb87d98..c64d6871269a6 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -553,7 +553,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for EnvNotDefinedWithUserMessag } #[derive(Diagnostic)] -pub(crate) enum EnvNotDefined<'a> { +pub(crate) enum EnvNotDefined { #[diag("environment variable `{$var}` not defined at compile time")] #[help("`{$var}` may not be available for the current Cargo target")] #[help( @@ -563,7 +563,7 @@ pub(crate) enum EnvNotDefined<'a> { #[primary_span] span: Span, var: Symbol, - var_expr: &'a rustc_ast::Expr, + var_expr: String, }, #[diag("environment variable `{$var}` not defined at compile time")] #[help("there is a similar Cargo environment variable: `{$suggested_var}`")] @@ -579,7 +579,7 @@ pub(crate) enum EnvNotDefined<'a> { #[primary_span] span: Span, var: Symbol, - var_expr: &'a rustc_ast::Expr, + var_expr: String, }, } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index bdb8316f3965d..a32592b45e5ea 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -828,7 +828,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return_error!(InvalidMonomorphization::FloatingPointVector { span, name, - f_ty: *f, + f_ty: f.name_str().to_string(), in_ty }); } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index c3afcd6f40333..280cbb590781d 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -13,8 +13,8 @@ use rustc_errors::{ Level, msg, }; use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutError; -use rustc_middle::ty::{FloatTy, Ty}; use rustc_span::{Span, Symbol}; use crate::assert_module_sources::CguReuse; @@ -748,7 +748,7 @@ pub enum InvalidMonomorphization<'tcx> { #[primary_span] span: Span, name: Symbol, - f_ty: FloatTy, + f_ty: String, in_ty: Ty<'tcx>, }, diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml index f280aaff11260..687aff5e9229a 100644 --- a/compiler/rustc_error_messages/Cargo.toml +++ b/compiler/rustc_error_messages/Cargo.toml @@ -9,8 +9,6 @@ fluent-bundle = "0.16" icu_list = { version = "2.0", default-features = false, features = ["alloc"] } icu_locale = { version = "2.0", default-features = false } intl-memoizer = "0.5.1" -rustc_ast = { path = "../rustc_ast" } -rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_baked_icu_data = { path = "../rustc_baked_icu_data" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_error_messages/src/diagnostic_impls.rs b/compiler/rustc_error_messages/src/diagnostic_impls.rs index 3b664cce5776f..38b086eaa80d3 100644 --- a/compiler/rustc_error_messages/src/diagnostic_impls.rs +++ b/compiler/rustc_error_messages/src/diagnostic_impls.rs @@ -5,8 +5,6 @@ use std::num::ParseIntError; use std::path::{Path, PathBuf}; use std::process::ExitStatus; -use rustc_ast as ast; -use rustc_ast_pretty::pprust; use rustc_span::edition::Edition; use crate::{DiagArgValue, IntoDiagArg}; @@ -69,7 +67,6 @@ macro_rules! into_diag_arg_for_number { } into_diag_arg_using_display!( - ast::ParamKindOrd, std::io::Error, Box, std::num::NonZero, @@ -142,30 +139,6 @@ impl IntoDiagArg for PathBuf { } } -impl IntoDiagArg for ast::Expr { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self))) - } -} - -impl IntoDiagArg for ast::Path { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self))) - } -} - -impl IntoDiagArg for ast::token::Token { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(pprust::token_to_string(&self)) - } -} - -impl IntoDiagArg for ast::token::TokenKind { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(pprust::token_kind_to_string(&self)) - } -} - impl IntoDiagArg for std::ffi::CString { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned())) @@ -178,28 +151,8 @@ impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr { } } -impl IntoDiagArg for ast::Visibility { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - let s = pprust::vis_to_string(&self); - let s = s.trim_end().to_string(); - DiagArgValue::Str(Cow::Owned(s)) - } -} - impl IntoDiagArg for Backtrace { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { DiagArgValue::Str(Cow::from(self.to_string())) } } - -impl IntoDiagArg for ast::util::parser::ExprPrecedence { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Number(self as i32) - } -} - -impl IntoDiagArg for ast::FloatTy { - fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { - DiagArgValue::Str(Cow::Borrowed(self.name_str())) - } -} diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index d74b1787d83c8..a9493b8654805 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; -use rustc_ast::ast; use rustc_errors::codes::*; use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -230,7 +229,7 @@ pub(crate) struct WrongFragmentKind<'a> { #[primary_span] pub span: Span, pub kind: &'a str, - pub name: &'a ast::Path, + pub name: String, } #[derive(Diagnostic)] @@ -249,7 +248,7 @@ pub(crate) struct IncompleteParse<'a> { pub descr: String, #[label("caused by the macro expansion here")] pub label_span: Span, - pub macro_path: &'a ast::Path, + pub macro_path: String, pub kind_name: &'a str, #[note("macros cannot expand to match arms")] pub expands_to_match_arm: bool, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index fe363e7d4a511..d708caaf01318 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -713,8 +713,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { mac: &ast::MacCall, span: Span, ) -> ErrorGuaranteed { - let guar = - self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path }); + let name = pprust::path_to_string(&mac.path); + let guar = self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name }); self.cx.macro_error_and_trace_macros_diag(); guar } @@ -1218,7 +1218,7 @@ pub(crate) fn ensure_complete_parse<'a>( span: def_site_span, descr, label_span: span, - macro_path, + macro_path: pprust::path_to_string(macro_path), kind_name, expands_to_match_arm, add_semicolon, diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 1829592d6d16e..4043b9bca61c5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4,8 +4,7 @@ use std::borrow::Cow; use std::path::PathBuf; use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, Token}; -use rustc_ast::util::parser::ExprPrecedence; -use rustc_ast::{Path, Visibility}; +use rustc_ast_pretty::pprust; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, @@ -657,7 +656,7 @@ pub(crate) struct ExpectedStructField { #[primary_span] #[label("expected one of `,`, `:`, or `{\"}\"}`")] pub span: Span, - pub token: Token, + pub token: Cow<'static, str>, #[label("while parsing this struct field")] pub ident_span: Span, } @@ -875,7 +874,7 @@ pub(crate) struct ComparisonInterpretedAsGeneric { #[primary_span] #[label("not interpreted as comparison")] pub comparison: Span, - pub r#type: Path, + pub r#type: String, #[label("interpreted as generic arguments")] pub args: Span, #[subdiagnostic] @@ -897,7 +896,7 @@ pub(crate) struct ShiftInterpretedAsGeneric { #[primary_span] #[label("not interpreted as shift")] pub shift: Span, - pub r#type: Path, + pub r#type: String, #[label("interpreted as generic arguments")] pub args: Span, #[subdiagnostic] @@ -919,7 +918,7 @@ pub(crate) struct FoundExprWouldBeStmt { #[primary_span] #[label("expected expression")] pub span: Span, - pub token: Token, + pub token: Cow<'static, str>, #[subdiagnostic] pub suggestion: ExprParenthesesNeeded, } @@ -1028,7 +1027,7 @@ pub(crate) struct ParenthesesWithStructFields { applicability = "maybe-incorrect" )] pub(crate) struct BracesForStructLiteral { - pub r#type: Path, + pub r#type: String, #[suggestion_part(code = " {{ ")] pub first: Span, #[suggestion_part(code = " }}")] @@ -1041,7 +1040,7 @@ pub(crate) struct BracesForStructLiteral { applicability = "maybe-incorrect" )] pub(crate) struct NoFieldsForFnCall { - pub r#type: Path, + pub r#type: String, #[suggestion_part(code = "")] pub fields: Vec, } @@ -1511,7 +1510,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { ); diag.span(self.span); if add_token { - diag.arg("token", self.token); + diag.arg("token", pprust::token_to_string(&self.token)); } if let Some(sugg) = self.suggest_raw { @@ -1577,7 +1576,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { ); diag.span(self.span); if add_token { - diag.arg("token", self.token); + diag.arg("token", pprust::token_to_string(&self.token)); } if let Some(unexpected_token_label) = self.unexpected_token_label { @@ -2218,7 +2217,7 @@ pub(crate) struct VisibilityNotFollowedByItem { #[primary_span] #[label("the visibility")] pub span: Span, - pub vis: Visibility, + pub vis: String, } #[derive(Diagnostic)] @@ -2533,14 +2532,14 @@ pub(crate) enum UnexpectedTokenAfterStructName { #[primary_span] #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, - token: Token, + token: Cow<'static, str>, }, #[diag("expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found keyword `{$token}`")] Keyword { #[primary_span] #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, - token: Token, + token: Cow<'static, str>, }, #[diag( "expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found reserved keyword `{$token}`" @@ -2549,7 +2548,7 @@ pub(crate) enum UnexpectedTokenAfterStructName { #[primary_span] #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, - token: Token, + token: Cow<'static, str>, }, #[diag( "expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found doc comment `{$token}`" @@ -2558,7 +2557,7 @@ pub(crate) enum UnexpectedTokenAfterStructName { #[primary_span] #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, - token: Token, + token: Cow<'static, str>, }, #[diag("expected `where`, `{\"{\"}`, `(`, or `;` after struct name, found metavar")] MetaVar { @@ -2571,13 +2570,14 @@ pub(crate) enum UnexpectedTokenAfterStructName { #[primary_span] #[label("expected `where`, `{\"{\"}`, `(`, or `;` after struct name")] span: Span, - token: Token, + token: Cow<'static, str>, }, } impl UnexpectedTokenAfterStructName { - pub(crate) fn new(span: Span, token: Token) -> Self { - match TokenDescription::from_token(&token) { + pub(crate) fn new(span: Span, orig_token: Token) -> Self { + let token = pprust::token_to_string(&orig_token); + match TokenDescription::from_token(&orig_token) { Some(TokenDescription::ReservedIdentifier) => Self::ReservedIdentifier { span, token }, Some(TokenDescription::Keyword) => Self::Keyword { span, token }, Some(TokenDescription::ReservedKeyword) => Self::ReservedKeyword { span, token }, @@ -2630,13 +2630,13 @@ pub(crate) enum UnexpectedNonterminal { Ident { #[primary_span] span: Span, - token: Token, + token: Cow<'static, str>, }, #[diag("expected a lifetime, found `{$token}`")] Lifetime { #[primary_span] span: Span, - token: Token, + token: Cow<'static, str>, }, } @@ -3212,7 +3212,7 @@ pub(crate) struct UnexpectedVertVertInPattern { pub(crate) struct TrailingVertSuggestion { #[primary_span] pub span: Span, - pub token: Token, + pub token: Cow<'static, str>, } #[derive(Diagnostic)] @@ -3224,7 +3224,7 @@ pub(crate) struct TrailingVertNotAllowed { pub suggestion: TrailingVertSuggestion, #[label("while parsing this or-pattern starting here")] pub start: Option, - pub token: Token, + pub token: Cow<'static, str>, #[note("alternatives in or-patterns are separated with `|`, not `||`")] pub note_double_vert: bool, } @@ -3441,8 +3441,10 @@ pub(crate) struct UnexpectedExpressionInPattern { pub span: Span, /// Was a `RangePatternBound` expected? pub is_bound: bool, - /// The unexpected expr's precedence (used in match arm guard suggestions). - pub expr_precedence: ExprPrecedence, + /// The unexpected expr's precedence. Not used directly in the error message, but needed for + /// the stashing of this error to work correctly. We store a `u32` rather than an + /// `ExprPrecedence` to avoid having to impl `IntoDiagArg` for `ExprPrecedence`. + pub expr_precedence: u32, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d55548dd72180..35c271cb70204 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -18,6 +18,7 @@ use rustc_ast::{ FnRetTy, Guard, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, }; +use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; use rustc_literal_escaper::unescape_char; @@ -352,7 +353,7 @@ impl<'a> Parser<'a> { fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { self.dcx().emit_err(errors::FoundExprWouldBeStmt { span: self.token.span, - token: self.token, + token: pprust::token_to_string(&self.token), suggestion: ExprParenthesesNeeded::surrounding(lhs.span), }); } @@ -729,7 +730,7 @@ impl<'a> Parser<'a> { token::Lt => { self.dcx().emit_err(errors::ComparisonInterpretedAsGeneric { comparison: self.token.span, - r#type: path, + r#type: pprust::path_to_string(&path), args: args_span, suggestion: errors::ComparisonInterpretedAsGenericSugg { left: expr.span.shrink_to_lo(), @@ -739,7 +740,7 @@ impl<'a> Parser<'a> { } token::Shl => self.dcx().emit_err(errors::ShiftInterpretedAsGeneric { shift: self.token.span, - r#type: path, + r#type: pprust::path_to_string(&path), args: args_span, suggestion: errors::ShiftInterpretedAsGenericSugg { left: expr.span.shrink_to_lo(), @@ -1304,16 +1305,17 @@ impl<'a> Parser<'a> { self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")") { err.cancel(); + let type_str = pprust::path_to_string(&path); self.dcx() .create_err(errors::ParenthesesWithStructFields { span, braces_for_struct: errors::BracesForStructLiteral { first: open_paren, second: close_paren, - r#type: path.clone(), + r#type: type_str.clone(), }, no_fields_for_fn: errors::NoFieldsForFnCall { - r#type: path, + r#type: type_str, fields: fields .into_iter() .map(|field| field.span.until(field.expr.span)) @@ -4038,7 +4040,7 @@ impl<'a> Parser<'a> { return Err(this.dcx().create_err(errors::ExpectedStructField { span: this.look_ahead(1, |t| t.span), ident_span: this.token.span, - token: this.look_ahead(1, |t| *t), + token: pprust::token_to_string(&this.look_ahead(1, |t| *t)), })); } let (ident, expr) = if is_shorthand { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 96bd59e2519e1..bd45bbb6a8582 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -192,9 +192,11 @@ impl<'a> Parser<'a> { // At this point, we have failed to parse an item. if !matches!(vis.kind, VisibilityKind::Inherited) { - let mut err = this - .dcx() - .create_err(errors::VisibilityNotFollowedByItem { span: vis.span, vis }); + let vis_str = pprust::vis_to_string(&vis).trim_end().to_string(); + let mut err = this.dcx().create_err(errors::VisibilityNotFollowedByItem { + span: vis.span, + vis: vis_str, + }); if let Some((ident, _)) = this.token.ident() && !ident.is_used_keyword() && let Some((similar_kw, is_incorrect_case)) = ident diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 37b76fc26a486..6ca7f89c76776 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -1,6 +1,7 @@ use rustc_ast::token::NtExprKind::*; use rustc_ast::token::NtPatKind::*; use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, NonterminalKind, Token}; +use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_span::{Ident, kw}; @@ -176,7 +177,7 @@ impl<'a> Parser<'a> { } else { Err(self.dcx().create_err(UnexpectedNonterminal::Ident { span: self.token.span, - token: self.token, + token: pprust::token_to_string(&self.token), })) } } @@ -198,7 +199,7 @@ impl<'a> Parser<'a> { } else { Err(self.dcx().create_err(UnexpectedNonterminal::Lifetime { span: self.token.span, - token: self.token, + token: pprust::token_to_string(&self.token), })) } } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index f36127ec8f0a8..4e6d76fefd9c2 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -367,14 +367,15 @@ impl<'a> Parser<'a> { match (is_end_ahead, &self.token.kind) { (true, token::Or | token::OrOr) => { // A `|` or possibly `||` token shouldn't be here. Ban it. + let token = pprust::token_to_string(&self.token); self.dcx().emit_err(TrailingVertNotAllowed { span: self.token.span, start: lo, suggestion: TrailingVertSuggestion { span: self.prev_token.span.shrink_to_hi().with_hi(self.token.span.hi()), - token: self.token, + token: token.clone(), }, - token: self.token, + token, note_double_vert: self.token.kind == token::OrOr, }); self.bump(); @@ -502,7 +503,7 @@ impl<'a> Parser<'a> { .create_err(UnexpectedExpressionInPattern { span, is_bound, - expr_precedence: expr.precedence(), + expr_precedence: expr.precedence() as u32, }) .stash(span, StashKey::ExprInPat) .unwrap(), From 339797e8b6fe15bf7435472fa37de5a03525e9b5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 13 Apr 2026 21:14:29 +1000 Subject: [PATCH 68/68] Eliminate `rustc_lint_defs`' dependency on `rustc_ast`. It currently only depends on two things: - `rustc_ast::AttrId`: this is just a re-export of `rustc_span::AttrId`, so we can import the original instead. - `rustc_ast::AttributeExt`: needed only for the `name` and `id` methods. We can instead pass in the `name` and `id` directly. --- Cargo.lock | 1 - compiler/rustc_lint/src/levels.rs | 2 +- compiler/rustc_lint_defs/Cargo.toml | 1 - compiler/rustc_lint_defs/src/lib.rs | 11 ++++++----- compiler/rustc_mir_build/src/builder/scope.rs | 7 ++++++- src/tools/clippy/clippy_lints/src/collapsible_if.rs | 2 +- .../clippy_lints/src/returns/needless_return.rs | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfdfb78b3d274..6b5491fb754de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4184,7 +4184,6 @@ dependencies = [ name = "rustc_lint_defs" version = "0.0.0" dependencies = [ - "rustc_ast", "rustc_data_structures", "rustc_error_messages", "rustc_hir_id", diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 2b859b65c9f8f..5bb74520a10f6 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -671,7 +671,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { continue; } - let (level, lint_id) = match Level::from_attr(attr) { + let (level, lint_id) = match Level::from_attr(attr.name(), || attr.id()) { None => continue, // This is the only lint level with a `LintExpectationId` that can be created from // an attribute. diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index c8201d5ea8ccc..2ca62f7fa8cdc 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -5,7 +5,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } rustc_hir_id = { path = "../rustc_hir_id" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 0f924d425f840..aa4b731a29bbc 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,8 +1,6 @@ use std::borrow::Cow; use std::fmt::Display; -use rustc_ast::AttrId; -use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::stable_hasher::{ HashStable, HashStableContext, StableCompare, StableHasher, ToStableHashKey, @@ -12,7 +10,7 @@ use rustc_hir_id::{HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; -use rustc_span::{Ident, Symbol, sym}; +use rustc_span::{AttrId, Ident, Symbol, sym}; use serde::{Deserialize, Serialize}; pub use self::Level::*; @@ -238,8 +236,11 @@ impl Level { } /// Converts an `Attribute` to a level. - pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option)> { - attr.name().and_then(|name| Self::from_symbol(name, || Some(attr.id()))) + pub fn from_attr( + attr_name: Option, + attr_id: impl Fn() -> AttrId, + ) -> Option<(Self, Option)> { + attr_name.and_then(|name| Self::from_symbol(name, || Some(attr_id()))) } /// Converts a `Symbol` to a level. diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 91610e768d012..6baf82f1335d0 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1298,7 +1298,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { break; } - if self.tcx.hir_attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) { + if self + .tcx + .hir_attrs(id) + .iter() + .any(|attr| Level::from_attr(attr.name(), || attr.id()).is_some()) + { // This is a rare case. It's for a node path that doesn't reach the root due to an // intervening lint level attribute. This result doesn't get cached. return id; diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 52e602bbac577..8a12d1093b4b4 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -238,7 +238,7 @@ impl CollapsibleIf { }, [attr] - if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + if matches!(Level::from_attr(attr.name(), || attr.id()), Some((Level::Expect, _))) && let Some(metas) = attr.meta_item_list() && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() && let [tool, lint_name] = meta_item.path.segments.as_slice() diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index 619a70cd8dd10..b9bacc2b73a17 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -181,7 +181,7 @@ fn check_final_expr<'tcx>( match cx.tcx.hir_attrs(expr.hir_id) { [] => {}, [attr] => { - if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + if matches!(Level::from_attr(attr.name(), || attr.id()), Some((Level::Expect, _))) && let metas = attr.meta_item_list() && let Some(lst) = metas && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()