From 70dd3bceca234ba2957f14d4083ea29ce29f1872 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:46:21 +0200 Subject: [PATCH 01/25] Remove rustc_on_unimplemented's `append_const_msg` --- .../src/attributes/diagnostic/mod.rs | 24 ++++++-------- compiler/rustc_hir/src/attrs/diagnostic.rs | 16 ---------- compiler/rustc_span/src/symbol.rs | 1 - .../traits/fulfillment_errors.rs | 31 +++++-------------- library/core/src/cmp.rs | 10 +++--- library/core/src/iter/range.rs | 2 +- library/core/src/marker.rs | 2 +- library/core/src/ops/arith.rs | 8 ++--- tests/auxiliary/minicore.rs | 2 +- 9 files changed, 27 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 8abcaeb5fbf53..e63baf77c0852 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -3,8 +3,8 @@ use std::ops::Range; use rustc_errors::E0232; use rustc_hir::AttrPath; use rustc_hir::attrs::diagnostic::{ - AppendConstMessage, Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, - Name, NameValue, OnUnimplementedCondition, Piece, Predicate, + Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue, + OnUnimplementedCondition, Piece, Predicate, }; use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_macros::Diagnostic; @@ -92,7 +92,6 @@ fn parse_directive_items<'p, S: Stage>( let mut notes = ThinVec::new(); let mut parent_label = None; let mut subcommands = ThinVec::new(); - let mut append_const_msg = None; for item in items { let span = item.span(); @@ -131,7 +130,6 @@ fn parse_directive_items<'p, S: Stage>( let Some(ret) = (||{ Some($($code)*) })() else { - malformed!() }; ret @@ -159,8 +157,13 @@ fn parse_directive_items<'p, S: Stage>( let item: &MetaItemParser = or_malformed!(item.meta_item()?); let name = or_malformed!(item.ident()?).name; - // Some things like `message = "message"` must have a value. - // But with things like `append_const_msg` that is optional. + // Currently, as of April 2026, all arguments of all diagnostic attrs + // must have a value, like `message = "message"`. Thus in a well-formed + // diagnostic attribute this is never `None`. + // + // But we don't assert its presence yet because we don't want to mention it + // if someone does something like `#[diagnostic::on_unimplemented(doesnt_exist)]`. + // That happens in the big `match` below. let value: Option = match item.args().name_value() { Some(nv) => Some(or_malformed!(nv.value_as_ident()?)), None => None, @@ -223,14 +226,6 @@ fn parse_directive_items<'p, S: Stage>( let value = or_malformed!(value?); notes.push(parse_format(value)) } - - (Mode::RustcOnUnimplemented, sym::append_const_msg) => { - append_const_msg = if let Some(msg) = value { - Some(AppendConstMessage::Custom(msg.name, item.span())) - } else { - Some(AppendConstMessage::Default) - } - } (Mode::RustcOnUnimplemented, sym::parent_label) => { let value = or_malformed!(value?); if parent_label.is_none() { @@ -290,7 +285,6 @@ fn parse_directive_items<'p, S: Stage>( label, notes, parent_label, - append_const_msg, }) } diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs index 7c66b3f844691..c700ca142759e 100644 --- a/compiler/rustc_hir/src/attrs/diagnostic.rs +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -19,7 +19,6 @@ pub struct Directive { pub label: Option<(Span, FormatString)>, pub notes: ThinVec, pub parent_label: Option, - pub append_const_msg: Option, } impl Directive { @@ -63,7 +62,6 @@ impl Directive { let mut label = None; let mut notes = Vec::new(); let mut parent_label = None; - let mut append_const_msg = None; info!( "evaluate_directive({:?}, trait_ref={:?}, options={:?}, args ={:?})", self, trait_name, condition_options, args @@ -91,8 +89,6 @@ impl Directive { if let Some(ref parent_label_) = command.parent_label { parent_label = Some(parent_label_.clone()); } - - append_const_msg = command.append_const_msg; } OnUnimplementedNote { @@ -100,7 +96,6 @@ impl Directive { message: message.map(|m| m.1.format(args)), notes: notes.into_iter().map(|n| n.format(args)).collect(), parent_label: parent_label.map(|e_s| e_s.format(args)), - append_const_msg, } } } @@ -111,17 +106,6 @@ pub struct OnUnimplementedNote { pub label: Option, pub notes: Vec, pub parent_label: Option, - // If none, should fall back to a generic message - pub append_const_msg: Option, -} - -/// Append a message for `[const] Trait` errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] -#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] -pub enum AppendConstMessage { - #[default] - Default, - Custom(Symbol, Span), } /// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7b359dcd6b252..a35813abdc210 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -399,7 +399,6 @@ symbols! { anon_assoc, anonymous_lifetime_in_impl_trait, any, - append_const_msg, apx_target_feature, arbitrary_enum_discriminant, arbitrary_self_types, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d6cfc993c8b83..3aa32a2feb4d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -14,7 +14,7 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, msg, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::diagnostic::{AppendConstMessage, OnUnimplementedNote}; +use rustc_hir::attrs::diagnostic::OnUnimplementedNote; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node, find_attr}; @@ -193,7 +193,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label, notes, parent_label, - append_const_msg, } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file); let have_alt_message = message.is_some() || label.is_some(); @@ -210,7 +209,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let question_mark_message = "the question mark operation (`?`) implicitly \ performs a conversion on the error value \ using the `From` trait"; - let (message, notes, append_const_msg) = if is_try_conversion { + let (message, notes) = if is_try_conversion { let ty = self.tcx.short_string( main_trait_predicate.skip_binder().self_ty(), &mut long_ty_file, @@ -219,7 +218,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ( Some(format!("`?` couldn't convert the error to `{ty}`")), vec![question_mark_message.to_owned()], - Some(AppendConstMessage::Default), ) } else if is_question_mark { let main_trait_predicate = @@ -233,17 +231,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { not satisfied", )), vec![question_mark_message.to_owned()], - Some(AppendConstMessage::Default), ) } else { - (message, notes, append_const_msg) + (message, notes) }; let default_err_msg = || self.get_standard_error_message( main_trait_predicate, message, None, - append_const_msg, post_message, &mut long_ty_file, ); @@ -859,7 +855,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref, None, Some(predicate.constness()), - None, String::new(), &mut file, ); @@ -919,7 +914,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label, notes, parent_label, - append_const_msg: _, } = note; if let Some(message) = message { @@ -2838,27 +2832,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option, predicate_constness: Option, - append_const_msg: Option, post_message: String, long_ty_path: &mut Option, ) -> String { message .and_then(|cannot_do_this| { - match (predicate_constness, append_const_msg) { + match predicate_constness { // do nothing if predicate is not const - (None, _) => Some(cannot_do_this), + None => Some(cannot_do_this), // suggested using default post message - ( - Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), - Some(AppendConstMessage::Default), - ) => Some(format!("{cannot_do_this} in const contexts")), - // overridden post message - ( - Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), - Some(AppendConstMessage::Custom(custom_msg, _)), - ) => Some(format!("{cannot_do_this}{custom_msg}")), - // fallback to generic message - (Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), None) => None, + Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe) => { + Some(format!("{cannot_do_this} in const contexts")) + } } }) .unwrap_or_else(|| { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 49d7487c2803b..2051a806af642 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -241,10 +241,9 @@ use crate::ops::ControlFlow; #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "==")] #[doc(alias = "!=")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "can't compare `{Self}` with `{Rhs}`", - label = "no implementation for `{Self} == {Rhs}`", - append_const_msg + label = "no implementation for `{Self} == {Rhs}`" )] #[rustc_diagnostic_item = "PartialEq"] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] @@ -1356,10 +1355,9 @@ pub macro Ord($item:item) { #[doc(alias = "<")] #[doc(alias = "<=")] #[doc(alias = ">=")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "can't compare `{Self}` with `{Rhs}`", - label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`", - append_const_msg + label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`" )] #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 951bb5d0029f6..695e1f27eff41 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -21,7 +21,7 @@ unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u6 /// The *successor* operation moves towards values that compare greater. /// The *predecessor* operation moves towards values that compare lesser. #[rustc_diagnostic_item = "range_step"] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "`std::ops::Range<{Self}>` is not an iterator", label = "`Range<{Self}>` is not an iterator", note = "`Range` only implements `Iterator` for select types in the standard library, \ diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index c79e8fc4060c1..3ca6466d122a4 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1054,7 +1054,7 @@ marker_impls! { #[unstable(feature = "const_destruct", issue = "133214")] #[rustc_const_unstable(feature = "const_destruct", issue = "133214")] #[lang = "destruct"] -#[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] +#[diagnostic::on_unimplemented(message = "can't drop `{Self}`")] #[rustc_deny_explicit_impl] #[rustc_dyn_incompatible_trait] pub const trait Destruct: PointeeSized {} diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index aec52424af3c4..afb814dbc6b33 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -70,8 +70,7 @@ on(all(Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), on(all(Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), message = "cannot add `{Rhs}` to `{Self}`", - label = "no implementation for `{Self} + {Rhs}`", - append_const_msg + label = "no implementation for `{Self} + {Rhs}`" )] #[doc(alias = "+")] pub const trait Add { @@ -181,10 +180,9 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 #[lang = "sub"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "143802")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "cannot subtract `{Rhs}` from `{Self}`", - label = "no implementation for `{Self} - {Rhs}`", - append_const_msg + label = "no implementation for `{Self} - {Rhs}`" )] #[doc(alias = "-")] pub const trait Sub { diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index d84357edd0423..5c6eb54832437 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -63,7 +63,7 @@ pub trait MetaSized: PointeeSized {} pub trait Sized: MetaSized {} #[lang = "destruct"] -#[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] +#[diagnostic::on_unimplemented(message = "can't drop `{Self}`")] pub trait Destruct: PointeeSized {} #[lang = "legacy_receiver"] From 8c043676a112155d824c9748534639704c145652 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:21:46 +0200 Subject: [PATCH 02/25] Refactor `get_standard_error_message` --- .../traits/fulfillment_errors.rs | 54 +++++++------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 3aa32a2feb4d4..855cce9b32b9b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -196,6 +196,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file); let have_alt_message = message.is_some() || label.is_some(); + + let message = message.unwrap_or_else(|| self.get_standard_error_message( + main_trait_predicate, + None, + post_message, + &mut long_ty_file, + )); let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id()); let is_question_mark = matches!( root_obligation.cause.code().peel_derives(), @@ -216,7 +223,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); // We have a `-> Result<_, E1>` and `gives_E2()?`. ( - Some(format!("`?` couldn't convert the error to `{ty}`")), + format!("`?` couldn't convert the error to `{ty}`"), vec![question_mark_message.to_owned()], ) } else if is_question_mark { @@ -226,24 +233,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // trait object: `-> Result<_, Box` and `gives_E()?` when // `E: Error` isn't met. ( - Some(format!( + format!( "`?` couldn't convert the error: `{main_trait_predicate}` is \ not satisfied", - )), + ), vec![question_mark_message.to_owned()], ) } else { (message, notes) }; - let default_err_msg = || self.get_standard_error_message( - main_trait_predicate, - message, - None, - post_message, - &mut long_ty_file, - ); - let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item( main_trait_predicate.def_id(), LangItem::TransmuteTrait, @@ -267,7 +266,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } GetSafeTransmuteErrorAndReason::Default => { - (default_err_msg(), None) + (message, None) } GetSafeTransmuteErrorAndReason::Error { err_msg, @@ -275,7 +274,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } => (err_msg, safe_transmute_explanation), } } else { - (default_err_msg(), None) + (message, None) }; let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); @@ -853,7 +852,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let err_msg = self.get_standard_error_message( trait_ref, - None, Some(predicate.constness()), String::new(), &mut file, @@ -2830,31 +2828,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn get_standard_error_message( &self, trait_predicate: ty::PolyTraitPredicate<'tcx>, - message: Option, predicate_constness: Option, post_message: String, long_ty_path: &mut Option, ) -> String { - message - .and_then(|cannot_do_this| { - match predicate_constness { - // do nothing if predicate is not const - None => Some(cannot_do_this), - // suggested using default post message - Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe) => { - Some(format!("{cannot_do_this} in const contexts")) - } - } - }) - .unwrap_or_else(|| { - format!( - "the trait bound `{}` is not satisfied{post_message}", - self.tcx.short_string( - trait_predicate.print_with_bound_constness(predicate_constness), - long_ty_path, - ), - ) - }) + format!( + "the trait bound `{}` is not satisfied{post_message}", + self.tcx.short_string( + trait_predicate.print_with_bound_constness(predicate_constness), + long_ty_path, + ), + ) } fn select_transmute_obligation_for_reporting( From b6bdfedad65384dedcdb94953384c1f52893ce80 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:34:40 +0200 Subject: [PATCH 03/25] Avoid needless clone. --- .../src/error_reporting/traits/fulfillment_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 855cce9b32b9b..5013d87493841 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -388,7 +388,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! - err.span_label(span, s.as_str().to_owned()); + err.span_label(span, s); if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point From 62db1ebca3c1f40ef42bd62283e060f3c8b88192 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 31 Mar 2026 09:54:34 +0800 Subject: [PATCH 04/25] Improve shadowed private field diagnostics --- compiler/rustc_hir_typeck/src/op.rs | 27 +++++ .../traits/fulfillment_errors.rs | 24 +++++ .../src/error_reporting/traits/suggestions.rs | 63 +++++++++++ ...vate-field-deref-confusion-issue-149546.rs | 81 ++++++++++++++ ...-field-deref-confusion-issue-149546.stderr | 100 ++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 tests/ui/privacy/private-field-deref-confusion-issue-149546.rs create mode 100644 tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index cf61728f7c2a3..f21db0bf85550 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -249,6 +249,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty_var, Some(lhs_expr), |err, ty| { + self.err_ctxt().note_field_shadowed_by_private_candidate( + err, + rhs_expr.hir_id, + self.param_env, + ); if let Op::BinOp(binop) = op && binop.node == hir::BinOpKind::Eq { @@ -331,6 +336,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr.span, format!("cannot use `{}` on type `{}`", s, lhs_ty_str), ); + let err_ctxt = self.err_ctxt(); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + lhs_expr.hir_id, + self.param_env, + ); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + rhs_expr.hir_id, + self.param_env, + ); self.note_unmet_impls_on_type(&mut err, &errors, false); (err, None) } @@ -391,6 +407,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(lhs_expr.span, lhs_ty_str.clone()); err.span_label(rhs_expr.span, rhs_ty_str); } + let err_ctxt = self.err_ctxt(); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + lhs_expr.hir_id, + self.param_env, + ); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + rhs_expr.hir_id, + self.param_env, + ); let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty); self.note_unmet_impls_on_type(&mut err, &errors, suggest_derive); (err, output_def_id) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d0358b03af197..fe3c7b8797b01 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -559,6 +559,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } + self.note_shadowed_private_fields_in_binop( + &mut err, + &obligation, + obligation.param_env, + ); self.try_to_add_help_message( &root_obligation, &obligation, @@ -3226,6 +3231,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_shadowed_inherent_method(err, obligation, trait_predicate); } + fn note_shadowed_private_fields_in_binop( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + if self.typeck_results.is_none() { + return; + } + + let ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } = obligation.cause.code() + else { + return; + }; + + self.note_field_shadowed_by_private_candidate(err, *lhs_hir_id, param_env); + self.note_field_shadowed_by_private_candidate(err, *rhs_hir_id, param_env); + } + fn add_help_message_for_fn_trait( &self, trait_pred: ty::PolyTraitPredicate<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 2d9574ea8c546..01a1d7f42cdfc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -242,6 +242,69 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( } impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn note_field_shadowed_by_private_candidate( + &self, + err: &mut Diag<'_>, + hir_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + ) { + let Some(typeck_results) = &self.typeck_results else { + return; + }; + let Node::Expr(expr) = self.tcx.hir_node(hir_id) else { + return; + }; + let hir::ExprKind::Field(base_expr, field_ident) = expr.kind else { + return; + }; + + let Some(base_ty) = typeck_results.expr_ty_opt(base_expr) else { + return; + }; + let base_ty = self.resolve_vars_if_possible(base_ty); + if base_ty.references_error() { + return; + } + + let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id); + let mut private_candidate = None; + + for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) { + let ty::Adt(base_def, args) = deref_base_ty.kind() else { + continue; + }; + + if base_def.is_enum() { + continue; + } + + let (adjusted_ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field_ident, base_def.did(), fn_body_hir_id); + + let Some((_, field_def)) = + base_def.non_enum_variant().fields.iter_enumerated().find(|(_, field)| { + field.ident(self.tcx).normalize_to_macros_2_0() == adjusted_ident + }) + else { + continue; + }; + + if field_def.vis.is_accessible_from(def_scope, self.tcx) { + let accessible_field_ty = field_def.ty(self.tcx, args); + if let Some((private_base_ty, private_field_ty)) = private_candidate + && !self.can_eq(param_env, private_field_ty, accessible_field_ty) + { + err.note(format!( + "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}`, but it is private" + )); + } + return; + } + + private_candidate.get_or_insert((deref_base_ty, field_def.ty(self.tcx, args))); + } + } + pub fn suggest_restricting_param_bound( &self, err: &mut Diag<'_>, diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs new file mode 100644 index 0000000000000..ee76ef7a5ed01 --- /dev/null +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -0,0 +1,81 @@ +// Regression test for issue #149546. +// Field lookup still resolves to the public field on the Deref target, but +// follow-up diagnostics should explain that the original type has a same-named +// private field with a different type. + +mod structs { + pub struct A { + field: usize, + b: B, + } + + pub struct B { + pub field: bool, + } + + impl std::ops::Deref for A { + type Target = B; + + fn deref(&self) -> &Self::Target { + &self.b + } + } +} + +use structs::A; + +fn by_value(a: A) { + a.field + 5; + //~^ ERROR cannot add `{integer}` to `bool` + //~| NOTE bool + //~| NOTE {integer} + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn by_ref(a: &A) { + a.field + 5; + //~^ ERROR cannot add `{integer}` to `bool` + //~| NOTE bool + //~| NOTE {integer} + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_by_value(a: A) { + 5 + a.field; + //~^ ERROR cannot add `bool` to `{integer}` + //~| NOTE no implementation for `{integer} + bool` + //~| HELP the trait `Add` is not implemented for `{integer}` + //~| HELP the following other types implement trait `Add`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_by_ref(a: &A) { + 5 + a.field; + //~^ ERROR cannot add `bool` to `{integer}` + //~| NOTE no implementation for `{integer} + bool` + //~| HELP the trait `Add` is not implemented for `{integer}` + //~| HELP the following other types implement trait `Add`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_assign_op_by_value(a: A) { + let mut n = 5; + n += a.field; + //~^ ERROR cannot add-assign `bool` to `{integer}` + //~| NOTE no implementation for `{integer} += bool` + //~| HELP the trait `AddAssign` is not implemented for `{integer}` + //~| HELP the following other types implement trait `AddAssign`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_assign_op_by_ref(a: &A) { + let mut n = 5; + n += a.field; + //~^ ERROR cannot add-assign `bool` to `{integer}` + //~| NOTE no implementation for `{integer} += bool` + //~| HELP the trait `AddAssign` is not implemented for `{integer}` + //~| HELP the following other types implement trait `AddAssign`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn main() {} diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr new file mode 100644 index 0000000000000..db0bc632c0acc --- /dev/null +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -0,0 +1,100 @@ +error[E0369]: cannot add `{integer}` to `bool` + --> $DIR/private-field-deref-confusion-issue-149546.rs:28:13 + | +LL | a.field + 5; + | ------- ^ - {integer} + | | + | bool + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error[E0369]: cannot add `{integer}` to `bool` + --> $DIR/private-field-deref-confusion-issue-149546.rs:36:13 + | +LL | a.field + 5; + | ------- ^ - {integer} + | | + | bool + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error[E0277]: cannot add `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:44:7 + | +LL | 5 + a.field; + | ^ no implementation for `{integer} + bool` + | + = help: the trait `Add` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `Add`: + `&f128` implements `Add` + `&f128` implements `Add` + `&f16` implements `Add` + `&f16` implements `Add` + `&f32` implements `Add` + `&f32` implements `Add` + `&f64` implements `Add` + `&f64` implements `Add` + and 56 others + +error[E0277]: cannot add `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:53:7 + | +LL | 5 + a.field; + | ^ no implementation for `{integer} + bool` + | + = help: the trait `Add` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `Add`: + `&f128` implements `Add` + `&f128` implements `Add` + `&f16` implements `Add` + `&f16` implements `Add` + `&f32` implements `Add` + `&f32` implements `Add` + `&f64` implements `Add` + `&f64` implements `Add` + and 56 others + +error[E0277]: cannot add-assign `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:63:7 + | +LL | n += a.field; + | ^^ no implementation for `{integer} += bool` + | + = help: the trait `AddAssign` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `AddAssign`: + `f128` implements `AddAssign<&f128>` + `f128` implements `AddAssign` + `f16` implements `AddAssign<&f16>` + `f16` implements `AddAssign` + `f32` implements `AddAssign<&f32>` + `f32` implements `AddAssign` + `f64` implements `AddAssign<&f64>` + `f64` implements `AddAssign` + and 24 others + +error[E0277]: cannot add-assign `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:73:7 + | +LL | n += a.field; + | ^^ no implementation for `{integer} += bool` + | + = help: the trait `AddAssign` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `AddAssign`: + `f128` implements `AddAssign<&f128>` + `f128` implements `AddAssign` + `f16` implements `AddAssign<&f16>` + `f16` implements `AddAssign` + `f32` implements `AddAssign<&f32>` + `f32` implements `AddAssign` + `f64` implements `AddAssign<&f64>` + `f64` implements `AddAssign` + and 24 others + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0369. +For more information about an error, try `rustc --explain E0277`. From 257d65531477cd48e89131161b184465753a5443 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 31 Mar 2026 10:39:24 +0800 Subject: [PATCH 05/25] extend note for private field to more diagnostics --- .../src/error_reporting/infer/mod.rs | 4 ++ .../traits/fulfillment_errors.rs | 23 +------ .../src/error_reporting/traits/suggestions.rs | 49 +++++++++++++++ ...vate-field-deref-confusion-issue-149546.rs | 41 ++++++++++++ ...-field-deref-confusion-issue-149546.stderr | 62 ++++++++++++++++--- 5 files changed, 150 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index d9ea2e0057895..f7486a23bf731 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1528,6 +1528,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label_or_note(span, terr.to_string(self.tcx)); } + if let Some(param_env) = param_env { + self.note_field_shadowed_by_private_candidate_in_cause(diag, cause, param_env); + } + if self.check_and_note_conflicting_crates(diag, terr) { return; } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index fe3c7b8797b01..3ff08bbaf9dc2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -559,9 +559,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - self.note_shadowed_private_fields_in_binop( + self.note_field_shadowed_by_private_candidate_in_cause( &mut err, - &obligation, + &obligation.cause, obligation.param_env, ); self.try_to_add_help_message( @@ -3231,25 +3231,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_shadowed_inherent_method(err, obligation, trait_predicate); } - fn note_shadowed_private_fields_in_binop( - &self, - err: &mut Diag<'_>, - obligation: &PredicateObligation<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) { - if self.typeck_results.is_none() { - return; - } - - let ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } = obligation.cause.code() - else { - return; - }; - - self.note_field_shadowed_by_private_candidate(err, *lhs_hir_id, param_env); - self.note_field_shadowed_by_private_candidate(err, *rhs_hir_id, param_env); - } - fn add_help_message_for_fn_trait( &self, trait_pred: ty::PolyTraitPredicate<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 01a1d7f42cdfc..95dfe6ffbcf35 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -8,6 +8,7 @@ use itertools::{EitherOrBoth, Itertools}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, @@ -242,6 +243,54 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( } impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn note_field_shadowed_by_private_candidate_in_cause( + &self, + err: &mut Diag<'_>, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + let mut hir_ids = UnordMap::default(); + // Walk the parent chain so we can recover + // the source expression from whichever layer carries them. + let mut next_code = Some(cause.code()); + while let Some(cause_code) = next_code { + match cause_code { + ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => { + hir_ids.insert(lhs_hir_id.local_id.as_u32(), *lhs_hir_id); + hir_ids.insert(rhs_hir_id.local_id.as_u32(), *rhs_hir_id); + } + ObligationCauseCode::FunctionArg { arg_hir_id, .. } + | ObligationCauseCode::ReturnValue(arg_hir_id) + | ObligationCauseCode::AwaitableExpr(arg_hir_id) + | ObligationCauseCode::BlockTailExpression(arg_hir_id, _) + | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => { + hir_ids.insert(arg_hir_id.local_id.as_u32(), *arg_hir_id); + } + ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => { + hir_ids.insert(hir_id.local_id.as_u32(), *hir_id); + } + _ => {} + } + next_code = cause_code.parent(); + } + + if cause.span != DUMMY_SP + && hir_ids.is_empty() + && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id) + { + let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); + expr_finder.visit_body(body); + if let Some(expr) = expr_finder.result { + hir_ids.insert(expr.hir_id.local_id.as_u32(), expr.hir_id); + } + } + + let hir_ids = hir_ids.into_sorted_stable_ord(); + for (_, hir_id) in hir_ids { + self.note_field_shadowed_by_private_candidate(err, hir_id, param_env); + } + } + pub fn note_field_shadowed_by_private_candidate( &self, err: &mut Diag<'_>, diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs index ee76ef7a5ed01..44981a801afa3 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -24,6 +24,24 @@ mod structs { use structs::A; +fn takes_usize(_: usize) {} +//~^ NOTE function defined here + +trait Marker {} + +impl Marker for usize {} +//~^ HELP the trait `Marker` is implemented for `usize` + +struct Wrapper(i32); + +impl std::ops::Add for Wrapper { +//~^ NOTE required for `Wrapper` to implement `Add` +//~| NOTE unsatisfied trait bound introduced here + type Output = (); + + fn add(self, _: T) {} +} + fn by_value(a: A) { a.field + 5; //~^ ERROR cannot add `{integer}` to `bool` @@ -78,4 +96,27 @@ fn rhs_assign_op_by_ref(a: &A) { //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } +fn rhs_nested_obligation(a: A) { + Wrapper(5) + a.field; + //~^ ERROR the trait bound `bool: Marker` is not satisfied + //~| NOTE the trait `Marker` is not implemented for `bool` + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn function_arg(a: A) { + takes_usize(a.field); + //~^ ERROR mismatched types + //~| NOTE expected `usize`, found `bool` + //~| NOTE arguments to this function are incorrect + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn return_value(a: &A) -> usize { +//~^ NOTE expected `usize` because of return type + a.field + //~^ ERROR mismatched types + //~| NOTE expected `usize`, found `bool` + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + fn main() {} diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr index db0bc632c0acc..511d4a3d9075b 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:28:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:46:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -9,7 +9,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:36:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:54:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -19,7 +19,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:44:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:62:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -38,7 +38,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:53:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:71:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -57,7 +57,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:63:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:81:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -76,7 +76,7 @@ LL | n += a.field; and 24 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:73:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:91:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -94,7 +94,53 @@ LL | n += a.field; `f64` implements `AddAssign` and 24 others -error: aborting due to 6 previous errors +error[E0277]: the trait bound `bool: Marker` is not satisfied + --> $DIR/private-field-deref-confusion-issue-149546.rs:100:16 + | +LL | Wrapper(5) + a.field; + | ^ the trait `Marker` is not implemented for `bool` + | + = note: there is a field `field` on `A` with type `usize`, but it is private +help: the trait `Marker` is implemented for `usize` + --> $DIR/private-field-deref-confusion-issue-149546.rs:32:1 + | +LL | impl Marker for usize {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required for `Wrapper` to implement `Add` + --> $DIR/private-field-deref-confusion-issue-149546.rs:37:17 + | +LL | impl std::ops::Add for Wrapper { + | ------ ^^^^^^^^^^^^^^^^ ^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error[E0308]: mismatched types + --> $DIR/private-field-deref-confusion-issue-149546.rs:107:17 + | +LL | takes_usize(a.field); + | ----------- ^^^^^^^ expected `usize`, found `bool` + | | + | arguments to this function are incorrect + | + = note: there is a field `field` on `A` with type `usize`, but it is private +note: function defined here + --> $DIR/private-field-deref-confusion-issue-149546.rs:27:4 + | +LL | fn takes_usize(_: usize) {} + | ^^^^^^^^^^^ -------- + +error[E0308]: mismatched types + --> $DIR/private-field-deref-confusion-issue-149546.rs:116:5 + | +LL | fn return_value(a: &A) -> usize { + | ----- expected `usize` because of return type +LL | +LL | a.field + | ^^^^^^^ expected `usize`, found `bool` + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0277, E0369. +Some errors have detailed explanations: E0277, E0308, E0369. For more information about an error, try `rustc --explain E0277`. From 86f99d2d4dd83e85a2d2cf5328cca4f95c0106d5 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 31 Mar 2026 11:40:36 +0800 Subject: [PATCH 06/25] extend note for private field to method call --- .../rustc_hir_typeck/src/method/suggest.rs | 7 +++ .../src/error_reporting/traits/suggestions.rs | 1 - ...vate-field-deref-confusion-issue-149546.rs | 23 ++++++++-- ...-field-deref-confusion-issue-149546.stderr | 46 +++++++++++++------ 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c5b3d7065fa92..85bcc2745254d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1214,6 +1214,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unsatisfied_predicates, ) }; + if let SelfSource::MethodCall(rcvr_expr) = source { + self.err_ctxt().note_field_shadowed_by_private_candidate( + &mut err, + rcvr_expr.hir_id, + self.param_env, + ); + } self.set_label_for_method_error( &mut err, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 95dfe6ffbcf35..a1a4cc1bc129c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -275,7 +275,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if cause.span != DUMMY_SP - && hir_ids.is_empty() && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id) { let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs index 44981a801afa3..e9013990d74cb 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -1,4 +1,3 @@ -// Regression test for issue #149546. // Field lookup still resolves to the public field on the Deref target, but // follow-up diagnostics should explain that the original type has a same-named // private field with a different type. @@ -35,8 +34,8 @@ impl Marker for usize {} struct Wrapper(i32); impl std::ops::Add for Wrapper { -//~^ NOTE required for `Wrapper` to implement `Add` -//~| NOTE unsatisfied trait bound introduced here + //~^ NOTE required for `Wrapper` to implement `Add` + //~| NOTE unsatisfied trait bound introduced here type Output = (); fn add(self, _: T) {} @@ -103,6 +102,22 @@ fn rhs_nested_obligation(a: A) { //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } +fn method_call(a: A) { + a.field.count_ones(); + //~^ ERROR no method named `count_ones` found for type `bool` in the current scope + //~| NOTE method not found in `bool` + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn type_mismatch(a: A) { + let value: usize = a.field; + //~^ ERROR mismatched types + //~| NOTE expected `usize`, found `bool` + //~| NOTE expected due to this + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private + eprintln!("value: {value}"); +} + fn function_arg(a: A) { takes_usize(a.field); //~^ ERROR mismatched types @@ -112,7 +127,7 @@ fn function_arg(a: A) { } fn return_value(a: &A) -> usize { -//~^ NOTE expected `usize` because of return type + //~^ NOTE expected `usize` because of return type a.field //~^ ERROR mismatched types //~| NOTE expected `usize`, found `bool` diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr index 511d4a3d9075b..d10d9a771b079 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:46:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:45:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -9,7 +9,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:54:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:53:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -19,7 +19,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:62:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:61:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -38,7 +38,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:71:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:70:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -57,7 +57,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:81:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:80:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -76,7 +76,7 @@ LL | n += a.field; and 24 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:91:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:90:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -95,27 +95,45 @@ LL | n += a.field; and 24 others error[E0277]: the trait bound `bool: Marker` is not satisfied - --> $DIR/private-field-deref-confusion-issue-149546.rs:100:16 + --> $DIR/private-field-deref-confusion-issue-149546.rs:99:16 | LL | Wrapper(5) + a.field; | ^ the trait `Marker` is not implemented for `bool` | = note: there is a field `field` on `A` with type `usize`, but it is private help: the trait `Marker` is implemented for `usize` - --> $DIR/private-field-deref-confusion-issue-149546.rs:32:1 + --> $DIR/private-field-deref-confusion-issue-149546.rs:31:1 | LL | impl Marker for usize {} | ^^^^^^^^^^^^^^^^^^^^^ note: required for `Wrapper` to implement `Add` - --> $DIR/private-field-deref-confusion-issue-149546.rs:37:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:36:17 | LL | impl std::ops::Add for Wrapper { | ------ ^^^^^^^^^^^^^^^^ ^^^^^^^ | | | unsatisfied trait bound introduced here +error[E0599]: no method named `count_ones` found for type `bool` in the current scope + --> $DIR/private-field-deref-confusion-issue-149546.rs:106:13 + | +LL | a.field.count_ones(); + | ^^^^^^^^^^ method not found in `bool` + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error[E0308]: mismatched types + --> $DIR/private-field-deref-confusion-issue-149546.rs:113:24 + | +LL | let value: usize = a.field; + | ----- ^^^^^^^ expected `usize`, found `bool` + | | + | expected due to this + | + = note: there is a field `field` on `A` with type `usize`, but it is private + error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:107:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:122:17 | LL | takes_usize(a.field); | ----------- ^^^^^^^ expected `usize`, found `bool` @@ -124,13 +142,13 @@ LL | takes_usize(a.field); | = note: there is a field `field` on `A` with type `usize`, but it is private note: function defined here - --> $DIR/private-field-deref-confusion-issue-149546.rs:27:4 + --> $DIR/private-field-deref-confusion-issue-149546.rs:26:4 | LL | fn takes_usize(_: usize) {} | ^^^^^^^^^^^ -------- error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:116:5 + --> $DIR/private-field-deref-confusion-issue-149546.rs:131:5 | LL | fn return_value(a: &A) -> usize { | ----- expected `usize` because of return type @@ -140,7 +158,7 @@ LL | a.field | = note: there is a field `field` on `A` with type `usize`, but it is private -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors -Some errors have detailed explanations: E0277, E0308, E0369. +Some errors have detailed explanations: E0277, E0308, E0369, E0599. For more information about an error, try `rustc --explain E0277`. From ce46df2fca06b65e213f10054965ccbde13dc6df Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 2 Apr 2026 17:42:27 +0800 Subject: [PATCH 07/25] Sort shadowed field notes by source order --- .../src/error_reporting/traits/suggestions.rs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index a1a4cc1bc129c..e855d51aac1ca 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -8,7 +8,6 @@ use itertools::{EitherOrBoth, Itertools}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, @@ -249,25 +248,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) { - let mut hir_ids = UnordMap::default(); + let mut hir_ids = Vec::new(); + let mut push_hir_id = |hir_id| { + if !hir_ids.contains(&hir_id) { + hir_ids.push(hir_id); + } + }; // Walk the parent chain so we can recover // the source expression from whichever layer carries them. let mut next_code = Some(cause.code()); while let Some(cause_code) = next_code { match cause_code { ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => { - hir_ids.insert(lhs_hir_id.local_id.as_u32(), *lhs_hir_id); - hir_ids.insert(rhs_hir_id.local_id.as_u32(), *rhs_hir_id); + push_hir_id(*lhs_hir_id); + push_hir_id(*rhs_hir_id); } ObligationCauseCode::FunctionArg { arg_hir_id, .. } | ObligationCauseCode::ReturnValue(arg_hir_id) | ObligationCauseCode::AwaitableExpr(arg_hir_id) | ObligationCauseCode::BlockTailExpression(arg_hir_id, _) | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => { - hir_ids.insert(arg_hir_id.local_id.as_u32(), *arg_hir_id); + push_hir_id(*arg_hir_id); } ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => { - hir_ids.insert(hir_id.local_id.as_u32(), *hir_id); + push_hir_id(*hir_id); } _ => {} } @@ -280,12 +284,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); expr_finder.visit_body(body); if let Some(expr) = expr_finder.result { - hir_ids.insert(expr.hir_id.local_id.as_u32(), expr.hir_id); + push_hir_id(expr.hir_id); } } - let hir_ids = hir_ids.into_sorted_stable_ord(); - for (_, hir_id) in hir_ids { + let source_map = self.tcx.sess.source_map(); + hir_ids.sort_by_cached_key(|hir_id| { + let span = self.tcx.hir_span(*hir_id); + let lo = source_map.lookup_byte_offset(span.lo()); + let hi = source_map.lookup_byte_offset(span.hi()); + (lo.sf.name.prefer_remapped_unconditionally().to_string(), lo.pos.0, hi.pos.0) + }); + + for hir_id in hir_ids { self.note_field_shadowed_by_private_candidate(err, hir_id, param_env); } } From 98d259beaf8909341ea697396b2db397fd644c4f Mon Sep 17 00:00:00 2001 From: zedddie Date: Sun, 8 Mar 2026 18:33:22 +0100 Subject: [PATCH 08/25] add min_adt_const_params gate tests --- .../const_param_ty-on-adt-without-adt-gate.rs | 17 +++++++++ ...st_param_ty-on-adt-without-adt-gate.stderr | 8 +++++ .../min_adt_const_params-gate-fail.rs | 35 +++++++++++++++++++ .../min_adt_const_params-gate-fail.stderr | 8 +++++ .../min_adt_const_params-gate.rs | 18 ++++++++++ .../type-field-more-visible-than-type.rs | 12 +++++++ 6 files changed, 98 insertions(+) create mode 100644 tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.rs create mode 100644 tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.stderr create mode 100644 tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.rs create mode 100644 tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.stderr create mode 100644 tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate.rs create mode 100644 tests/ui/const-generics/min_adt_const_params/type-field-more-visible-than-type.rs diff --git a/tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.rs b/tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.rs new file mode 100644 index 0000000000000..9b5c62c412995 --- /dev/null +++ b/tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.rs @@ -0,0 +1,17 @@ +//! Ensure we enforce `min_adt_const_params` rules on any adt `ConstParamTy_` +//! implementation unless `adt_const_params` feature is used. +#![allow(incomplete_features)] +#![feature(const_param_ty_trait)] + +use std::marker::ConstParamTy_; + +#[derive(PartialEq, Eq)] +pub struct Fumo { + cirno: i32, + pub(crate) reimu: i32 +} + +impl ConstParamTy_ for Fumo {} + //~^ ERROR: the trait `ConstParamTy` may not be implemented for this struct + +fn main() {} diff --git a/tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.stderr b/tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.stderr new file mode 100644 index 0000000000000..784c6f0937d8a --- /dev/null +++ b/tests/ui/const-generics/min_adt_const_params/const_param_ty-on-adt-without-adt-gate.stderr @@ -0,0 +1,8 @@ +error: the trait `ConstParamTy` may not be implemented for this struct + --> $DIR/const_param_ty-on-adt-without-adt-gate.rs:14:24 + | +LL | impl ConstParamTy_ for Fumo {} + | ^^^^ struct fields are less visible than the struct + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.rs b/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.rs new file mode 100644 index 0000000000000..bb18a3314f4c6 --- /dev/null +++ b/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.rs @@ -0,0 +1,35 @@ +//! Ensure min_adt_const_params enforce +//! struct's visibility on its fields +#![allow(incomplete_features)] +#![feature(min_adt_const_params)] +#![feature(const_param_ty_trait)] + +use std::marker::ConstParamTy_; + +#[derive(PartialEq, Eq)] +pub struct Meowl { + pub public: i32, + private: i32 +} + +#[derive(PartialEq, Eq)] +pub struct Meowl2 { + pub a: i32, + pub b: i32 +} + +#[derive(PartialEq, Eq)] +pub(crate) struct Meowl3 { + pub(crate) a: i32, + pub b: i32 +} + +impl ConstParamTy_ for Meowl {} + //~^ ERROR the trait `ConstParamTy` may not be implemented for this struct +impl ConstParamTy_ for Meowl2 {} +impl ConstParamTy_ for Meowl3 {} + +fn something() {} +fn something2() {} + +fn main() {} diff --git a/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.stderr b/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.stderr new file mode 100644 index 0000000000000..fec610f061c20 --- /dev/null +++ b/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate-fail.stderr @@ -0,0 +1,8 @@ +error: the trait `ConstParamTy` may not be implemented for this struct + --> $DIR/min_adt_const_params-gate-fail.rs:27:24 + | +LL | impl ConstParamTy_ for Meowl {} + | ^^^^^ struct fields are less visible than the struct + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate.rs b/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate.rs new file mode 100644 index 0000000000000..e33f5b0fac99b --- /dev/null +++ b/tests/ui/const-generics/min_adt_const_params/min_adt_const_params-gate.rs @@ -0,0 +1,18 @@ +// gate-test-min_adt_const_params +//@run-pass +#![feature(min_adt_const_params, const_param_ty_trait)] +#![allow(incomplete_features, dead_code)] + +use std::marker::ConstParamTy_; + +#[derive(PartialEq, Eq)] +pub struct Meowl { + pub public: i32, + pub also_public: i32 +} + +impl ConstParamTy_ for Meowl {} + +fn meoow() {} + +fn main() {} diff --git a/tests/ui/const-generics/min_adt_const_params/type-field-more-visible-than-type.rs b/tests/ui/const-generics/min_adt_const_params/type-field-more-visible-than-type.rs new file mode 100644 index 0000000000000..cedec86675f3e --- /dev/null +++ b/tests/ui/const-generics/min_adt_const_params/type-field-more-visible-than-type.rs @@ -0,0 +1,12 @@ +//@run-pass +#![feature(min_adt_const_params)] + +use std::marker::ConstParamTy; + +#[derive(ConstParamTy, Eq, PartialEq)] +#[allow(dead_code)] +struct Foo { + pub field: u32, +} + +fn main() {} From 72fbd1c0b53852bbed7e6ab0d36496182c84dce5 Mon Sep 17 00:00:00 2001 From: zedddie Date: Mon, 9 Mar 2026 03:19:55 +0100 Subject: [PATCH 09/25] implement `min_adt_const_params` feature --- compiler/rustc_feature/src/unstable.rs | 3 +++ .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/coherence/builtin.rs | 20 +++++++++++++++++++ compiler/rustc_hir_analysis/src/errors.rs | 8 ++++++++ .../rustc_resolve/src/late/diagnostics.rs | 3 ++- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/marker.rs | 2 +- 7 files changed, 36 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index a559211e5d467..5b4ef693e5ee4 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -577,6 +577,9 @@ declare_features! ( (unstable, marker_trait_attr, "1.30.0", Some(29864)), /// Enable mgca `type const` syntax before expansion. (incomplete, mgca_type_const_syntax, "1.95.0", Some(132980)), + /// Allows additional const parameter types, such as [u8; 10] or user defined types. + /// User defined types must not have fields more private than the type itself. + (unstable, min_adt_const_params, "CURRENT_RUSTC_VERSION", Some(154042)), /// Enables the generic const args MVP (only bare paths, not arbitrary computation). (incomplete, min_generic_const_args, "1.84.0", Some(132980)), /// A minimal, sound subset of specialization intended to be used by the diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 96d0a56f901ad..52cb061177c1f 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -842,7 +842,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er let span = tcx.def_span(param.def_id); let def_id = param.def_id.expect_local(); - if tcx.features().adt_const_params() { + if tcx.features().adt_const_params() || tcx.features().min_adt_const_params() { enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| { wfcx.register_bound( ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)), diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index abd5c024ef79c..61453e5328d5f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -184,6 +184,26 @@ fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), E return Ok(()); } + if !tcx.features().adt_const_params() { + match *self_type.kind() { + ty::Adt(adt, _) if adt.is_struct() => { + let struct_vis = tcx.visibility(adt.did()); + for variant in adt.variants() { + for field in &variant.fields { + if !field.vis.is_at_least(struct_vis, tcx) { + let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; + return Err(tcx + .dcx() + .emit_err(errors::ConstParamTyFieldVisMismatch { span })); + } + } + } + } + + _ => {} + } + } + let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { Ok(()) => Ok(()), diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index fcd4cb938bf73..c55b9e384c55b 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -317,6 +317,14 @@ pub(crate) struct ConstParamTyImplOnNonAdt { pub span: Span, } +#[derive(Diagnostic)] +#[diag("the trait `ConstParamTy` may not be implemented for this struct")] +pub(crate) struct ConstParamTyFieldVisMismatch { + #[primary_span] + #[label("struct fields are less visible than the struct")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("at least one trait is required for an object type", code = E0224)] pub(crate) struct TraitObjectDeclaredWithNoTraits { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index cf048231bd607..8512a4b93e863 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1773,7 +1773,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the // benefits of including them here outweighs the small number of false positives. Some(Res::Def(DefKind::Struct | DefKind::Enum, _)) - if self.r.tcx.features().adt_const_params() => + if self.r.tcx.features().adt_const_params() + || self.r.tcx.features().min_adt_const_params() => { Applicability::MaybeIncorrect } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 738c9b975fd00..5ca5d79bd940e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1241,6 +1241,7 @@ symbols! { meta_sized, metadata_type, mgca_type_const_syntax, + min_adt_const_params, min_const_fn, min_const_generics, min_const_unsafe_fn, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index c79e8fc4060c1..688371ff808ab 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1088,7 +1088,7 @@ pub trait ConstParamTy_: StructuralPartialEq + Eq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] #[allow_internal_unstable(const_param_ty_trait)] -#[unstable(feature = "adt_const_params", issue = "95174")] +#[unstable(feature = "min_adt_const_params", issue = "154042", implied_by = "adt_const_params")] pub macro ConstParamTy($item:item) { /* compiler built-in */ } From 46b05279dd1c6c82ab1e850220fffb04531b9030 Mon Sep 17 00:00:00 2001 From: fru1tworld Date: Thu, 2 Apr 2026 20:15:33 +0900 Subject: [PATCH 10/25] Increase span_look_ahead limit to handle whitespace --- .../rustc_resolve/src/late/diagnostics.rs | 8 ++++-- tests/ui/generics/wrong-number-of-args.stderr | 12 ++++----- ...6-trailing-comma-in-lifetime-suggestion.rs | 18 +++++++++++++ ...ailing-comma-in-lifetime-suggestion.stderr | 25 +++++++++++++++++++ 4 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.rs create mode 100644 tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index cf048231bd607..3be653f272d9c 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -4064,6 +4064,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }; let mut spans_suggs: Vec<_> = Vec::new(); + let source_map = self.r.tcx.sess.source_map(); let build_sugg = |lt: MissingLifetime| match lt.kind { MissingLifetimeKind::Underscore => { debug_assert_eq!(lt.count, 1); @@ -4074,9 +4075,12 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { (lt.span.shrink_to_hi(), format!("{existing_name} ")) } MissingLifetimeKind::Comma => { - let sugg: String = std::iter::repeat_n([existing_name.as_str(), ", "], lt.count) - .flatten() + let sugg: String = std::iter::repeat_n(existing_name.as_str(), lt.count) + .intersperse(", ") .collect(); + let is_empty_brackets = + source_map.span_look_ahead(lt.span, ">", Some(50)).is_some(); + let sugg = if is_empty_brackets { sugg } else { format!("{sugg}, ") }; (lt.span.shrink_to_hi(), sugg) } MissingLifetimeKind::Brackets => { diff --git a/tests/ui/generics/wrong-number-of-args.stderr b/tests/ui/generics/wrong-number-of-args.stderr index 554d017d67e33..953cd273eb764 100644 --- a/tests/ui/generics/wrong-number-of-args.stderr +++ b/tests/ui/generics/wrong-number-of-args.stderr @@ -28,8 +28,8 @@ LL | type E = Ty<>; | help: consider introducing a named lifetime parameter | -LL | type E<'a> = Ty<'a, >; - | ++++ +++ +LL | type E<'a> = Ty<'a>; + | ++++ ++ error[E0106]: missing lifetime specifier --> $DIR/wrong-number-of-args.rs:120:22 @@ -55,12 +55,12 @@ LL | type F = Box>; | help: consider making the bound lifetime-generic with a new `'a` lifetime | -LL | type F = Box GenericLifetime<'a, >>; - | +++++++ +++ +LL | type F = Box GenericLifetime<'a>>; + | +++++++ ++ help: consider introducing a named lifetime parameter | -LL | type F<'a> = Box>; - | ++++ +++ +LL | type F<'a> = Box>; + | ++++ ++ error[E0106]: missing lifetime specifier --> $DIR/wrong-number-of-args.rs:163:43 diff --git a/tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.rs b/tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.rs new file mode 100644 index 0000000000000..acf93ae51cc4d --- /dev/null +++ b/tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.rs @@ -0,0 +1,18 @@ +// Regression test for . +// +// When suggesting lifetime parameters for empty angle brackets like `Foo<>`, +// the suggestion should not include a trailing comma (e.g., `Foo<'a>` not `Foo<'a, >`). +// When there are other generic arguments like `Foo`, the trailing comma is needed +// (e.g., `Foo<'a, T>`). + +#![crate_type = "lib"] + +struct Foo<'a>(&'a ()); + +type A = Foo<>; +//~^ ERROR missing lifetime specifier [E0106] + +struct Bar<'a, T>(&'a T); + +type B = Bar; +//~^ ERROR missing lifetime specifier [E0106] diff --git a/tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.stderr b/tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.stderr new file mode 100644 index 0000000000000..f189c259d65f9 --- /dev/null +++ b/tests/ui/lifetimes/E0106-trailing-comma-in-lifetime-suggestion.stderr @@ -0,0 +1,25 @@ +error[E0106]: missing lifetime specifier + --> $DIR/E0106-trailing-comma-in-lifetime-suggestion.rs:12:13 + | +LL | type A = Foo<>; + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type A<'a> = Foo<'a>; + | ++++ ++ + +error[E0106]: missing lifetime specifier + --> $DIR/E0106-trailing-comma-in-lifetime-suggestion.rs:17:13 + | +LL | type B = Bar; + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +LL | type B<'a> = Bar<'a, u8>; + | ++++ +++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0106`. From ae899cc4e2bf91e553ddb369b83deb6425500ff4 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 2 Apr 2026 17:57:33 +0800 Subject: [PATCH 11/25] Clarify private field autoderef notes --- .../src/error_reporting/traits/suggestions.rs | 125 +++++++++-- ...vate-field-deref-confusion-issue-149546.rs | 51 +---- ...-field-deref-confusion-issue-149546.stderr | 203 +++++++++++++++--- 3 files changed, 285 insertions(+), 94 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index e855d51aac1ca..4912d5f4582e7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -22,8 +22,10 @@ use rustc_hir::{ expr_needs_parens, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_infer::traits::ImplSource; use rustc_middle::middle::privacy::Level; use rustc_middle::traits::IsConstable; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::{ PrintPolyTraitPredicateExt as _, PrintPolyTraitRefExt, PrintTraitPredicateExt as _, @@ -49,7 +51,7 @@ use crate::error_reporting::TypeErrCtxt; use crate::errors; use crate::infer::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt}; +use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt, SelectionContext}; #[derive(Debug)] pub enum CoroutineInteriorOrUpvar { @@ -248,46 +250,44 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) { - let mut hir_ids = Vec::new(); - let mut push_hir_id = |hir_id| { - if !hir_ids.contains(&hir_id) { - hir_ids.push(hir_id); - } - }; + let mut hir_ids = FxHashSet::default(); // Walk the parent chain so we can recover // the source expression from whichever layer carries them. let mut next_code = Some(cause.code()); while let Some(cause_code) = next_code { match cause_code { ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => { - push_hir_id(*lhs_hir_id); - push_hir_id(*rhs_hir_id); + hir_ids.insert(*lhs_hir_id); + hir_ids.insert(*rhs_hir_id); } ObligationCauseCode::FunctionArg { arg_hir_id, .. } | ObligationCauseCode::ReturnValue(arg_hir_id) | ObligationCauseCode::AwaitableExpr(arg_hir_id) | ObligationCauseCode::BlockTailExpression(arg_hir_id, _) | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => { - push_hir_id(*arg_hir_id); + hir_ids.insert(*arg_hir_id); } ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => { - push_hir_id(*hir_id); + hir_ids.insert(*hir_id); } _ => {} } next_code = cause_code.parent(); } - if cause.span != DUMMY_SP + if !cause.span.is_dummy() && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id) { let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); expr_finder.visit_body(body); if let Some(expr) = expr_finder.result { - push_hir_id(expr.hir_id); + hir_ids.insert(expr.hir_id); } } + // we will sort immediately by source order before emitting any diagnostics + #[allow(rustc::potential_query_instability)] + let mut hir_ids: Vec<_> = hir_ids.into_iter().collect(); let source_map = self.tcx.sess.source_map(); hir_ids.sort_by_cached_key(|hir_id| { let span = self.tcx.hir_span(*hir_id); @@ -326,7 +326,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id); - let mut private_candidate = None; + let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None; for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) { let ty::Adt(base_def, args) = deref_base_ty.kind() else { @@ -347,20 +347,107 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { else { continue; }; + let field_span = self + .tcx + .def_ident_span(field_def.did) + .unwrap_or_else(|| self.tcx.def_span(field_def.did)); if field_def.vis.is_accessible_from(def_scope, self.tcx) { let accessible_field_ty = field_def.ty(self.tcx, args); - if let Some((private_base_ty, private_field_ty)) = private_candidate + if let Some((private_base_ty, private_field_ty, private_field_span)) = + private_candidate && !self.can_eq(param_env, private_field_ty, accessible_field_ty) { - err.note(format!( - "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}`, but it is private" - )); + let private_struct_span = match private_base_ty.kind() { + ty::Adt(private_base_def, _) => self + .tcx + .def_ident_span(private_base_def.did()) + .unwrap_or_else(|| self.tcx.def_span(private_base_def.did())), + _ => DUMMY_SP, + }; + let accessible_struct_span = self + .tcx + .def_ident_span(base_def.did()) + .unwrap_or_else(|| self.tcx.def_span(base_def.did())); + let deref_impl_span = (typeck_results + .expr_adjustments(base_expr) + .iter() + .filter(|adj| { + matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_))) + }) + .count() + == 1) + .then(|| { + self.probe(|_| { + let deref_trait_did = + self.tcx.require_lang_item(LangItem::Deref, DUMMY_SP); + let trait_ref = + ty::TraitRef::new(self.tcx, deref_trait_did, [private_base_ty]); + let obligation: Obligation<'tcx, ty::Predicate<'tcx>> = + Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + trait_ref, + ); + let Ok(Some(ImplSource::UserDefined(impl_data))) = + SelectionContext::new(self) + .select(&obligation.with(self.tcx, trait_ref)) + else { + return None; + }; + Some(self.tcx.def_span(impl_data.impl_def_id)) + }) + }) + .flatten(); + + let mut note_spans: MultiSpan = private_struct_span.into(); + if private_struct_span != DUMMY_SP { + note_spans.push_span_label(private_struct_span, "in this struct"); + } + if private_field_span != DUMMY_SP { + note_spans.push_span_label( + private_field_span, + "if this field wasn't private, it would be accessible", + ); + } + if accessible_struct_span != DUMMY_SP { + note_spans.push_span_label( + accessible_struct_span, + "this struct is accessible through auto-deref", + ); + } + if field_span != DUMMY_SP { + note_spans + .push_span_label(field_span, "this is the field that was accessed"); + } + if let Some(deref_impl_span) = deref_impl_span + && deref_impl_span != DUMMY_SP + { + note_spans.push_span_label( + deref_impl_span, + "the field was accessed through this `Deref`", + ); + } + + err.span_note( + note_spans, + format!( + "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}` but it is private; `{field_ident}` from `{deref_base_ty}` was accessed through auto-deref instead" + ), + ); } + + // we finally get to the accessible field, + // so we can return early without checking the rest of the autoderef candidates return; } - private_candidate.get_or_insert((deref_base_ty, field_def.ty(self.tcx, args))); + private_candidate.get_or_insert(( + deref_base_ty, + field_def.ty(self.tcx, args), + field_span, + )); } } diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs index e9013990d74cb..ec26c879d4eb3 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -1,6 +1,7 @@ // Field lookup still resolves to the public field on the Deref target, but // follow-up diagnostics should explain that the original type has a same-named // private field with a different type. +//@ dont-require-annotations: ERROR mod structs { pub struct A { @@ -24,18 +25,14 @@ mod structs { use structs::A; fn takes_usize(_: usize) {} -//~^ NOTE function defined here trait Marker {} impl Marker for usize {} -//~^ HELP the trait `Marker` is implemented for `usize` struct Wrapper(i32); impl std::ops::Add for Wrapper { - //~^ NOTE required for `Wrapper` to implement `Add` - //~| NOTE unsatisfied trait bound introduced here type Output = (); fn add(self, _: T) {} @@ -43,95 +40,49 @@ impl std::ops::Add for Wrapper { fn by_value(a: A) { a.field + 5; - //~^ ERROR cannot add `{integer}` to `bool` - //~| NOTE bool - //~| NOTE {integer} - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn by_ref(a: &A) { a.field + 5; - //~^ ERROR cannot add `{integer}` to `bool` - //~| NOTE bool - //~| NOTE {integer} - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_by_value(a: A) { 5 + a.field; - //~^ ERROR cannot add `bool` to `{integer}` - //~| NOTE no implementation for `{integer} + bool` - //~| HELP the trait `Add` is not implemented for `{integer}` - //~| HELP the following other types implement trait `Add`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_by_ref(a: &A) { 5 + a.field; - //~^ ERROR cannot add `bool` to `{integer}` - //~| NOTE no implementation for `{integer} + bool` - //~| HELP the trait `Add` is not implemented for `{integer}` - //~| HELP the following other types implement trait `Add`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_assign_op_by_value(a: A) { let mut n = 5; n += a.field; - //~^ ERROR cannot add-assign `bool` to `{integer}` - //~| NOTE no implementation for `{integer} += bool` - //~| HELP the trait `AddAssign` is not implemented for `{integer}` - //~| HELP the following other types implement trait `AddAssign`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_assign_op_by_ref(a: &A) { let mut n = 5; n += a.field; - //~^ ERROR cannot add-assign `bool` to `{integer}` - //~| NOTE no implementation for `{integer} += bool` - //~| HELP the trait `AddAssign` is not implemented for `{integer}` - //~| HELP the following other types implement trait `AddAssign`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_nested_obligation(a: A) { Wrapper(5) + a.field; - //~^ ERROR the trait bound `bool: Marker` is not satisfied - //~| NOTE the trait `Marker` is not implemented for `bool` - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn method_call(a: A) { a.field.count_ones(); - //~^ ERROR no method named `count_ones` found for type `bool` in the current scope - //~| NOTE method not found in `bool` - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn type_mismatch(a: A) { let value: usize = a.field; - //~^ ERROR mismatched types - //~| NOTE expected `usize`, found `bool` - //~| NOTE expected due to this - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private eprintln!("value: {value}"); } fn function_arg(a: A) { takes_usize(a.field); - //~^ ERROR mismatched types - //~| NOTE expected `usize`, found `bool` - //~| NOTE arguments to this function are incorrect - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn return_value(a: &A) -> usize { - //~^ NOTE expected `usize` because of return type a.field - //~^ ERROR mismatched types - //~| NOTE expected `usize`, found `bool` - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn main() {} diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr index d10d9a771b079..9f2bb523913c8 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -1,31 +1,73 @@ error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:45:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:42:13 | LL | a.field + 5; | ------- ^ - {integer} | | | bool | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:53:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:46:13 | LL | a.field + 5; | ------- ^ - {integer} | | | bool | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:61:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:50:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` | = help: the trait `Add` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `Add`: `&f128` implements `Add` `&f128` implements `Add` @@ -38,13 +80,27 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:70:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:54:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` | = help: the trait `Add` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `Add`: `&f128` implements `Add` `&f128` implements `Add` @@ -57,13 +113,27 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:80:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:59:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` | = help: the trait `AddAssign` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `AddAssign`: `f128` implements `AddAssign<&f128>` `f128` implements `AddAssign` @@ -76,13 +146,27 @@ LL | n += a.field; and 24 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:90:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:64:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` | = help: the trait `AddAssign` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `AddAssign`: `f128` implements `AddAssign<&f128>` `f128` implements `AddAssign` @@ -95,19 +179,33 @@ LL | n += a.field; and 24 others error[E0277]: the trait bound `bool: Marker` is not satisfied - --> $DIR/private-field-deref-confusion-issue-149546.rs:99:16 + --> $DIR/private-field-deref-confusion-issue-149546.rs:68:16 | LL | Wrapper(5) + a.field; | ^ the trait `Marker` is not implemented for `bool` | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` help: the trait `Marker` is implemented for `usize` --> $DIR/private-field-deref-confusion-issue-149546.rs:31:1 | LL | impl Marker for usize {} | ^^^^^^^^^^^^^^^^^^^^^ note: required for `Wrapper` to implement `Add` - --> $DIR/private-field-deref-confusion-issue-149546.rs:36:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:35:17 | LL | impl std::ops::Add for Wrapper { | ------ ^^^^^^^^^^^^^^^^ ^^^^^^^ @@ -115,48 +213,103 @@ LL | impl std::ops::Add for Wrapper { | unsatisfied trait bound introduced here error[E0599]: no method named `count_ones` found for type `bool` in the current scope - --> $DIR/private-field-deref-confusion-issue-149546.rs:106:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:72:13 | LL | a.field.count_ones(); | ^^^^^^^^^^ method not found in `bool` | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:113:24 + --> $DIR/private-field-deref-confusion-issue-149546.rs:76:24 | LL | let value: usize = a.field; | ----- ^^^^^^^ expected `usize`, found `bool` | | | expected due to this | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:122:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:81:17 | LL | takes_usize(a.field); | ----------- ^^^^^^^ expected `usize`, found `bool` | | | arguments to this function are incorrect | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` note: function defined here - --> $DIR/private-field-deref-confusion-issue-149546.rs:26:4 + --> $DIR/private-field-deref-confusion-issue-149546.rs:27:4 | LL | fn takes_usize(_: usize) {} | ^^^^^^^^^^^ -------- error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:131:5 + --> $DIR/private-field-deref-confusion-issue-149546.rs:85:5 | LL | fn return_value(a: &A) -> usize { | ----- expected `usize` because of return type -LL | LL | a.field | ^^^^^^^ expected `usize`, found `bool` | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error: aborting due to 11 previous errors From 707c0d0b5e1a20eec7cd062715d786c6bddcc733 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Fri, 3 Apr 2026 17:08:16 +0700 Subject: [PATCH 12/25] Add comment to borrow-checker As requested by @lcnr --- compiler/rustc_borrowck/src/type_check/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 47f14dc3df62e..cf69dda7b2710 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -447,6 +447,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tcx = self.infcx.tcx; for proj in &user_ty.projs { + // Necessary for non-trivial patterns whose user-type annotation is an opaque type, + // e.g. `let (_a,): Tait = whatever`, see #105897 if !self.infcx.next_trait_solver() && let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() { From a2f7f3c1ebb7a8e820140fac88bff10c5605e5b2 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 19 Feb 2026 11:03:14 +0000 Subject: [PATCH 13/25] ty_utils: lower tuples to `ScalableVector` repr Instead of just using regular struct lowering for these types, which results in an incorrect ABI (e.g. returning indirectly), use `BackendRepr::ScalableVector` which will lower to the correct type and be passed in registers. This also enables some simplifications for generating alloca of scalable vectors and greater re-use of `scalable_vector_parts`. A LLVM codegen test demonstrating the changed IR this generates is included in the next commit alongside some intrinsics that make these tuples usable. --- compiler/rustc_abi/src/layout.rs | 21 ++++++--- compiler/rustc_abi/src/lib.rs | 33 ++++++++++++-- compiler/rustc_codegen_gcc/src/builder.rs | 7 +-- compiler/rustc_codegen_llvm/src/builder.rs | 18 +++----- compiler/rustc_codegen_llvm/src/type_of.rs | 44 ++++++++++++++++++- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 4 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 15 +++---- .../rustc_codegen_ssa/src/traits/builder.rs | 2 +- compiler/rustc_middle/src/ty/sty.rs | 24 +++++++--- compiler/rustc_public/src/abi.rs | 5 +++ .../src/unstable/convert/stable/abi.rs | 25 +++++++++-- compiler/rustc_ty_utils/src/layout.rs | 32 ++++++-------- 12 files changed, 161 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index ca6128b6f1be4..cca1d499088f4 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -10,8 +10,8 @@ use tracing::{debug, trace}; use crate::{ AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer, - LayoutData, Niche, NonZeroUsize, Primitive, ReprOptions, Scalar, Size, StructKind, TagEncoding, - TargetDataLayout, Variants, WrappingRange, + LayoutData, Niche, NonZeroUsize, NumScalableVectors, Primitive, ReprOptions, Scalar, Size, + StructKind, TagEncoding, TargetDataLayout, Variants, WrappingRange, }; mod coroutine; @@ -204,13 +204,19 @@ impl LayoutCalculator { &self, element: F, count: u64, + number_of_vectors: NumScalableVectors, ) -> LayoutCalculatorResult where FieldIdx: Idx, VariantIdx: Idx, F: AsRef> + fmt::Debug, { - vector_type_layout(SimdVectorKind::Scalable, self.cx.data_layout(), element, count) + vector_type_layout( + SimdVectorKind::Scalable(number_of_vectors), + self.cx.data_layout(), + element, + count, + ) } pub fn simd_type( @@ -1526,7 +1532,7 @@ impl LayoutCalculator { enum SimdVectorKind { /// `#[rustc_scalable_vector]` - Scalable, + Scalable(NumScalableVectors), /// `#[repr(simd, packed)]` PackedFixed, /// `#[repr(simd)]` @@ -1559,9 +1565,10 @@ where let size = elt.size.checked_mul(count, dl).ok_or_else(|| LayoutCalculatorError::SizeOverflow)?; let (repr, align) = match kind { - SimdVectorKind::Scalable => { - (BackendRepr::SimdScalableVector { element, count }, dl.llvmlike_vector_align(size)) - } + SimdVectorKind::Scalable(number_of_vectors) => ( + BackendRepr::SimdScalableVector { element, count, number_of_vectors }, + dl.llvmlike_vector_align(size), + ), // Non-power-of-two vectors have padding up to the next power-of-two. // If we're a packed repr, remove the padding while keeping the alignment as close // to a vector as possible. diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 42e272fdafb5d..f148b776852aa 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1696,6 +1696,28 @@ impl AddressSpace { pub const ZERO: Self = AddressSpace(0); } +/// How many scalable vectors are in a `BackendRepr::ScalableVector`? +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "nightly", derive(HashStable_Generic))] +pub struct NumScalableVectors(pub u8); + +impl NumScalableVectors { + /// Returns a `NumScalableVector` for a non-tuple scalable vector (e.g. a single vector). + pub fn for_non_tuple() -> Self { + NumScalableVectors(1) + } + + // Returns `NumScalableVectors` for values of two through eight, which are a valid number of + // fields for a tuple of scalable vectors to have. `1` is a valid value of `NumScalableVectors` + // but not for a tuple which would have a field count. + pub fn from_field_count(count: usize) -> Option { + match count { + 2..8 => Some(NumScalableVectors(count as u8)), + _ => None, + } + } +} + /// The way we represent values to the backend /// /// Previously this was conflated with the "ABI" a type is given, as in the platform-specific ABI. @@ -1714,6 +1736,7 @@ pub enum BackendRepr { SimdScalableVector { element: Scalar, count: u64, + number_of_vectors: NumScalableVectors, }, SimdVector { element: Scalar, @@ -1820,8 +1843,12 @@ impl BackendRepr { BackendRepr::SimdVector { element: element.to_union(), count } } BackendRepr::Memory { .. } => BackendRepr::Memory { sized: true }, - BackendRepr::SimdScalableVector { element, count } => { - BackendRepr::SimdScalableVector { element: element.to_union(), count } + BackendRepr::SimdScalableVector { element, count, number_of_vectors } => { + BackendRepr::SimdScalableVector { + element: element.to_union(), + count, + number_of_vectors, + } } } } @@ -2161,7 +2188,7 @@ impl LayoutData { } /// Returns `true` if the size of the type is only known at runtime. - pub fn is_runtime_sized(&self) -> bool { + pub fn is_scalable_vector(&self) -> bool { matches!(self.backend_repr, BackendRepr::SimdScalableVector { .. }) } diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 1d6d77604411c..3cffd862b9b98 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -24,7 +24,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ - FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, LayoutOfHelpers, + FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, HasTypingEnv, LayoutError, + LayoutOfHelpers, TyAndLayout, }; use rustc_middle::ty::{self, AtomicOrdering, Instance, Ty, TyCtxt}; use rustc_span::Span; @@ -943,8 +944,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { .get_address(self.location) } - fn scalable_alloca(&mut self, _elt: u64, _align: Align, _element_ty: Ty<'_>) -> RValue<'gcc> { - todo!() + fn alloca_with_ty(&mut self, ty: TyAndLayout<'tcx>) -> RValue<'gcc> { + self.alloca(ty.layout.size, ty.layout.align.abi) } fn load(&mut self, pointee_ty: Type<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 4e79e4fafac52..056a0763087a2 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -7,8 +7,7 @@ pub(crate) mod autodiff; pub(crate) mod gpu_offload; use libc::{c_char, c_uint}; -use rustc_abi as abi; -use rustc_abi::{Align, Size, WrappingRange}; +use rustc_abi::{self as abi, Align, Size, WrappingRange}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -616,21 +615,14 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - fn scalable_alloca(&mut self, elt: u64, align: Align, element_ty: Ty<'_>) -> Self::Value { + fn alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> Self::Value { let mut bx = Builder::with_cx(self.cx); bx.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) }); - let llvm_ty = match element_ty.kind() { - ty::Bool => bx.type_i1(), - ty::Int(int_ty) => self.cx.type_int_from_ty(*int_ty), - ty::Uint(uint_ty) => self.cx.type_uint_from_ty(*uint_ty), - ty::Float(float_ty) => self.cx.type_float_from_ty(*float_ty), - _ => unreachable!("scalable vectors can only contain a bool, int, uint or float"), - }; + let scalable_vector_ty = layout.llvm_type(self.cx); unsafe { - let ty = llvm::LLVMScalableVectorType(llvm_ty, elt.try_into().unwrap()); - let alloca = llvm::LLVMBuildAlloca(&bx.llbuilder, ty, UNNAMED); - llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); + let alloca = llvm::LLVMBuildAlloca(&bx.llbuilder, scalable_vector_ty, UNNAMED); + llvm::LLVMSetAlignment(alloca, layout.align.abi.bytes() as c_uint); alloca } } diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index e586ed0dd6b07..6d0490e4a1f79 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -24,14 +24,54 @@ fn uncached_llvm_type<'a, 'tcx>( let element = layout.scalar_llvm_type_at(cx, element); return cx.type_vector(element, count); } - BackendRepr::SimdScalableVector { ref element, count } => { + BackendRepr::SimdScalableVector { ref element, count, number_of_vectors } => { let element = if element.is_bool() { cx.type_i1() } else { layout.scalar_llvm_type_at(cx, *element) }; - return cx.type_scalable_vector(element, count); + let vector_type = cx.type_scalable_vector(element, count); + return match number_of_vectors.0 { + 1 => vector_type, + 2 => cx.type_struct(&[vector_type, vector_type], false), + 3 => cx.type_struct(&[vector_type, vector_type, vector_type], false), + 4 => cx.type_struct(&[vector_type, vector_type, vector_type, vector_type], false), + 5 => cx.type_struct( + &[vector_type, vector_type, vector_type, vector_type, vector_type], + false, + ), + 6 => cx.type_struct( + &[vector_type, vector_type, vector_type, vector_type, vector_type, vector_type], + false, + ), + 7 => cx.type_struct( + &[ + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + ], + false, + ), + 8 => cx.type_struct( + &[ + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + vector_type, + ], + false, + ), + _ => bug!("`#[rustc_scalable_vector]` tuple struct with too many fields"), + }; } BackendRepr::Memory { .. } | BackendRepr::ScalarPair(..) => {} } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 2f93f688c316d..60ab13dbc6f76 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -438,8 +438,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if operand.layout.ty.is_scalable_vector() && bx.sess().target.arch == rustc_target::spec::Arch::AArch64 { - let (count, element_ty) = - operand.layout.ty.scalable_vector_element_count_and_type(bx.tcx()); + let (count, element_ty, _) = + operand.layout.ty.scalable_vector_parts(bx.tcx()).unwrap(); // i.e. `` when `N != 16` if element_ty.is_bool() && count != 16 { return; diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index d62e622b6fed3..53518fd816f31 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -1,3 +1,5 @@ +use std::ops::Deref as _; + use rustc_abi::{ Align, BackendRepr, FieldIdx, FieldsShape, Size, TagEncoding, VariantIdx, Variants, }; @@ -109,8 +111,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { bx: &mut Bx, layout: TyAndLayout<'tcx>, ) -> Self { - if layout.is_runtime_sized() { - Self::alloca_runtime_sized(bx, layout) + if layout.deref().is_scalable_vector() { + Self::alloca_scalable(bx, layout) } else { Self::alloca_size(bx, layout.size, layout) } @@ -151,16 +153,11 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { } } - fn alloca_runtime_sized>( + fn alloca_scalable>( bx: &mut Bx, layout: TyAndLayout<'tcx>, ) -> Self { - let (element_count, ty) = layout.ty.scalable_vector_element_count_and_type(bx.tcx()); - PlaceValue::new_sized( - bx.scalable_alloca(element_count as u64, layout.align.abi, ty), - layout.align.abi, - ) - .with_type(layout) + PlaceValue::new_sized(bx.alloca_with_ty(layout), layout.align.abi).with_type(layout) } } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 2076e2bb26274..c222aef4574bf 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -235,7 +235,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn to_immediate_scalar(&mut self, val: Self::Value, scalar: Scalar) -> Self::Value; fn alloca(&mut self, size: Size, align: Align) -> Self::Value; - fn scalable_alloca(&mut self, elt: u64, align: Align, element_ty: Ty<'_>) -> Self::Value; + fn alloca_with_ty(&mut self, layout: TyAndLayout<'tcx>) -> Self::Value; fn load(&mut self, ty: Self::Type, ptr: Self::Value, align: Align) -> Self::Value; fn volatile_load(&mut self, ty: Self::Type, ptr: Self::Value) -> Self::Value; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 0d047b348d9e1..621ceeffac658 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -7,7 +7,7 @@ use std::debug_assert_matches; use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; -use rustc_abi::{FIRST_VARIANT, FieldIdx, ScalableElt, VariantIdx}; +use rustc_abi::{FIRST_VARIANT, FieldIdx, NumScalableVectors, ScalableElt, VariantIdx}; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::LangItem; @@ -1261,17 +1261,27 @@ impl<'tcx> Ty<'tcx> { } } - pub fn scalable_vector_element_count_and_type(self, tcx: TyCtxt<'tcx>) -> (u16, Ty<'tcx>) { + pub fn scalable_vector_parts( + self, + tcx: TyCtxt<'tcx>, + ) -> Option<(u16, Ty<'tcx>, NumScalableVectors)> { let Adt(def, args) = self.kind() else { - bug!("`scalable_vector_size_and_type` called on invalid type") + return None; }; - let Some(ScalableElt::ElementCount(element_count)) = def.repr().scalable else { - bug!("`scalable_vector_size_and_type` called on non-scalable vector type"); + let (num_vectors, vec_def) = match def.repr().scalable? { + ScalableElt::ElementCount(_) => (NumScalableVectors::for_non_tuple(), *def), + ScalableElt::Container => ( + NumScalableVectors::from_field_count(def.non_enum_variant().fields.len())?, + def.non_enum_variant().fields[FieldIdx::ZERO].ty(tcx, args).ty_adt_def()?, + ), }; - let variant = def.non_enum_variant(); + let Some(ScalableElt::ElementCount(element_count)) = vec_def.repr().scalable else { + return None; + }; + let variant = vec_def.non_enum_variant(); assert_eq!(variant.fields.len(), 1); let field_ty = variant.fields[FieldIdx::ZERO].ty(tcx, args); - (element_count, field_ty) + Some((element_count, field_ty, num_vectors)) } pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) { diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index 1403e57a7e6a9..4a780d652df81 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -232,6 +232,10 @@ pub enum TagEncoding { }, } +/// How many scalable vectors are in a `ValueAbi::ScalableVector`? +#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] +pub struct NumScalableVectors(pub(crate) u8); + /// Describes how values of the type are passed by target ABIs, /// in terms of categories of C types there are ABI rules for. #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] @@ -245,6 +249,7 @@ pub enum ValueAbi { ScalableVector { element: Scalar, count: u64, + number_of_vectors: NumScalableVectors, }, Aggregate { /// If true, the size is exact, otherwise it's only a lower bound. diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index b3edc6194c307..d8c4cee7abbe4 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -10,8 +10,9 @@ use rustc_target::callconv; use crate::abi::{ AddressSpace, ArgAbi, CallConvention, FieldsShape, FloatLength, FnAbi, IntegerLength, - IntegerType, Layout, LayoutShape, PassMode, Primitive, ReprFlags, ReprOptions, Scalar, - TagEncoding, TyAndLayout, ValueAbi, VariantFields, VariantsShape, WrappingRange, + IntegerType, Layout, LayoutShape, NumScalableVectors, PassMode, Primitive, ReprFlags, + ReprOptions, Scalar, TagEncoding, TyAndLayout, ValueAbi, VariantFields, VariantsShape, + WrappingRange, }; use crate::compiler_interface::BridgeTys; use crate::target::MachineSize as Size; @@ -249,6 +250,18 @@ impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding { } } +impl<'tcx> Stable<'tcx> for rustc_abi::NumScalableVectors { + type T = NumScalableVectors; + + fn stable<'cx>( + &self, + _tables: &mut Tables<'cx, BridgeTys>, + _cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + NumScalableVectors(self.0) + } +} + impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { type T = ValueAbi; @@ -265,8 +278,12 @@ impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { rustc_abi::BackendRepr::SimdVector { element, count } => { ValueAbi::Vector { element: element.stable(tables, cx), count } } - rustc_abi::BackendRepr::SimdScalableVector { element, count } => { - ValueAbi::ScalableVector { element: element.stable(tables, cx), count } + rustc_abi::BackendRepr::SimdScalableVector { element, count, number_of_vectors } => { + ValueAbi::ScalableVector { + element: element.stable(tables, cx), + count, + number_of_vectors: number_of_vectors.stable(tables, cx), + } } rustc_abi::BackendRepr::Memory { sized } => ValueAbi::Aggregate { sized }, } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 391f50edf23fa..136df923ee47a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -4,8 +4,8 @@ use rustc_abi::Integer::{I8, I32}; use rustc_abi::Primitive::{self, Float, Int, Pointer}; use rustc_abi::{ AddressSpace, BackendRepr, FIRST_VARIANT, FieldIdx, FieldsShape, HasDataLayout, Layout, - LayoutCalculatorError, LayoutData, Niche, ReprOptions, ScalableElt, Scalar, Size, StructKind, - TagEncoding, VariantIdx, Variants, WrappingRange, + LayoutCalculatorError, LayoutData, Niche, ReprOptions, Scalar, Size, StructKind, TagEncoding, + VariantIdx, Variants, WrappingRange, }; use rustc_hashes::Hash64; use rustc_hir as hir; @@ -572,30 +572,26 @@ fn layout_of_uncached<'tcx>( // ```rust (ignore, example) // #[rustc_scalable_vector(3)] // struct svuint32_t(u32); + // + // #[rustc_scalable_vector] + // struct svuint32x2_t(svuint32_t, svuint32_t); // ``` - ty::Adt(def, args) - if matches!(def.repr().scalable, Some(ScalableElt::ElementCount(..))) => - { - let Some(element_ty) = def - .is_struct() - .then(|| &def.variant(FIRST_VARIANT).fields) - .filter(|fields| fields.len() == 1) - .map(|fields| fields[FieldIdx::ZERO].ty(tcx, args)) + ty::Adt(def, _args) if def.repr().scalable() => { + let Some((element_count, element_ty, number_of_vectors)) = + ty.scalable_vector_parts(tcx) else { let guar = tcx .dcx() - .delayed_bug("#[rustc_scalable_vector] was applied to an invalid type"); - return Err(error(cx, LayoutError::ReferencesError(guar))); - }; - let Some(ScalableElt::ElementCount(element_count)) = def.repr().scalable else { - let guar = tcx - .dcx() - .delayed_bug("#[rustc_scalable_vector] was applied to an invalid type"); + .delayed_bug("`#[rustc_scalable_vector]` was applied to an invalid type"); return Err(error(cx, LayoutError::ReferencesError(guar))); }; let element_layout = cx.layout_of(element_ty)?; - map_layout(cx.calc.scalable_vector_type(element_layout, element_count as u64))? + map_layout(cx.calc.scalable_vector_type( + element_layout, + element_count as u64, + number_of_vectors, + ))? } // SIMD vector types. From 4fbcb031de05becc6cf2984446d0ce9972376757 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 19 Feb 2026 11:03:14 +0000 Subject: [PATCH 14/25] cg_llvm: `sve_tuple_{create,get,set}` intrinsics Clang changed to representing tuples of scalable vectors as structs rather than as wide vectors (that is, scalable vector types where the `N` part of the `` type was multiplied by the number of vectors). rustc mirrored this in the initial implementation of scalable vectors. Earlier versions of our patches used the wide vector representation and our intrinsic patches used the legacy `llvm.aarch64.sve.tuple.{create,get,set}{2,3,4}` intrinsics for creating these tuples/getting/setting the vectors, which were only supported due to LLVM's `AutoUpgrade` pass converting these intrinsics into `llvm.vector.insert`. `AutoUpgrade` only supports these legacy intrinsics with the wide vector representation. With the current struct representation, Clang has special handling in codegen for generating `insertvalue`/`extractvalue` instructions for these operations, which must be replicated by rustc's codegen for our intrinsics to use. This patch implements new intrinsics in `core::intrinsics::scalable` (mirroring the structure of `core::intrinsics::simd`) which rustc lowers to the appropriate `insertvalue`/`extractvalue` instructions. --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 112 +++++++++++++++++- .../rustc_hir_analysis/src/check/intrinsic.rs | 6 + compiler/rustc_span/src/symbol.rs | 5 + .../src/intrinsics/{simd.rs => simd/mod.rs} | 2 + library/core/src/intrinsics/simd/scalable.rs | 71 +++++++++++ .../scalable-vectors/tuple-intrinsics.rs | 100 ++++++++++++++++ .../simd/masked-load-store-check-fail.stderr | 4 +- triagebot.toml | 2 +- 8 files changed, 298 insertions(+), 4 deletions(-) rename library/core/src/intrinsics/{simd.rs => simd/mod.rs} (99%) create mode 100644 library/core/src/intrinsics/simd/scalable.rs create mode 100644 tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 39bf4c10dab18..ad2c23c99820b 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -3,7 +3,8 @@ use std::ffi::c_uint; use std::{assert_matches, ptr}; use rustc_abi::{ - Align, BackendRepr, ExternAbi, Float, HasDataLayout, Primitive, Size, WrappingRange, + Align, BackendRepr, ExternAbi, Float, HasDataLayout, NumScalableVectors, Primitive, Size, + WrappingRange, }; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -605,6 +606,115 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.pointercast(val, self.type_ptr()) } + sym::sve_tuple_create2 => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + let tuple_ty = self.layout_of(fn_args.type_at(1)); + assert_matches!( + tuple_ty.backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(2), + .. + } + ); + let ret = self.const_poison(self.backend_type(tuple_ty)); + let ret = self.insert_value(ret, args[0].immediate(), 0); + self.insert_value(ret, args[1].immediate(), 1) + } + + sym::sve_tuple_create3 => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + let tuple_ty = self.layout_of(fn_args.type_at(1)); + assert_matches!( + tuple_ty.backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(3), + .. + } + ); + let ret = self.const_poison(self.backend_type(tuple_ty)); + let ret = self.insert_value(ret, args[0].immediate(), 0); + let ret = self.insert_value(ret, args[1].immediate(), 1); + self.insert_value(ret, args[2].immediate(), 2) + } + + sym::sve_tuple_create4 => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + let tuple_ty = self.layout_of(fn_args.type_at(1)); + assert_matches!( + tuple_ty.backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(4), + .. + } + ); + let ret = self.const_poison(self.backend_type(tuple_ty)); + let ret = self.insert_value(ret, args[0].immediate(), 0); + let ret = self.insert_value(ret, args[1].immediate(), 1); + let ret = self.insert_value(ret, args[2].immediate(), 2); + self.insert_value(ret, args[3].immediate(), 3) + } + + sym::sve_tuple_get => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8), + .. + } + ); + assert_matches!( + self.layout_of(fn_args.type_at(1)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + self.extract_value( + args[0].immediate(), + fn_args.const_at(2).to_leaf().to_i32() as u64, + ) + } + + sym::sve_tuple_set => { + assert_matches!( + self.layout_of(fn_args.type_at(0)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(2 | 3 | 4 | 5 | 6 | 7 | 8), + .. + } + ); + assert_matches!( + self.layout_of(fn_args.type_at(1)).backend_repr, + BackendRepr::SimdScalableVector { + number_of_vectors: NumScalableVectors(1), + .. + } + ); + self.insert_value( + args[0].immediate(), + args[1].immediate(), + fn_args.const_at(2).to_leaf().to_i32() as u64, + ) + } + _ if name.as_str().starts_with("simd_") => { // Unpack non-power-of-2 #[repr(packed, simd)] arguments. // This gives them the expected layout of a regular #[repr(simd)] vector. diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index b1dc593331c6c..ca57921089fae 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -783,6 +783,12 @@ pub(crate) fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_const_generic => (2, 1, vec![param(0), param(0)], param(1)), + sym::sve_tuple_create2 => (2, 0, vec![param(0), param(0)], param(1)), + sym::sve_tuple_create3 => (2, 0, vec![param(0), param(0), param(0)], param(1)), + sym::sve_tuple_create4 => (2, 0, vec![param(0), param(0), param(0), param(0)], param(1)), + sym::sve_tuple_get => (2, 1, vec![param(0)], param(1)), + sym::sve_tuple_set => (2, 1, vec![param(0), param(1)], param(0)), + sym::atomic_cxchg | sym::atomic_cxchgweak => ( 1, 2, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 53e2527057bc2..8b57421f2045c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1979,6 +1979,11 @@ symbols! { suggestion, super_let, supertrait_item_shadowing, + sve_tuple_create2, + sve_tuple_create3, + sve_tuple_create4, + sve_tuple_get, + sve_tuple_set, sym, sync, synthetic, diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd/mod.rs similarity index 99% rename from library/core/src/intrinsics/simd.rs rename to library/core/src/intrinsics/simd/mod.rs index ae86690dc418d..084d8a3f1f247 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd/mod.rs @@ -2,6 +2,8 @@ //! //! In this module, a "vector" is any `repr(simd)` type. +pub mod scalable; + use crate::marker::ConstParamTy; /// Inserts an element into a vector, returning the updated vector. diff --git a/library/core/src/intrinsics/simd/scalable.rs b/library/core/src/intrinsics/simd/scalable.rs new file mode 100644 index 0000000000000..5e7b64f18e533 --- /dev/null +++ b/library/core/src/intrinsics/simd/scalable.rs @@ -0,0 +1,71 @@ +//! Scalable vector compiler intrinsics. +//! +//! In this module, a "vector" is any `#[rustc_scalable_vector]`-annotated type. + +/// Create a tuple of two vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svcreate2*` builtins. +#[cfg(target_arch = "aarch64")] +#[rustc_nounwind] +#[rustc_intrinsic] +pub unsafe fn sve_tuple_create2(x0: SVec, x1: SVec) -> SVecTup; + +/// Create a tuple of three vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svcreate3*` builtins. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn sve_tuple_create3(x0: SVec, x1: SVec, x2: SVec) -> SVecTup; + +/// Create a tuple of four vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svcreate4*` builtins. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn sve_tuple_create4(x0: SVec, x1: SVec, x2: SVec, x3: SVec) -> SVecTup; + +/// Get one vector from a tuple of vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svget*` builtins. +/// +/// # Safety +/// +/// `IDX` must be in-bounds of the tuple. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn sve_tuple_get(tuple: SVecTup) -> SVec; + +/// Change one vector in a tuple of vectors. +/// +/// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a +/// scalable vector (`#[rustc_scalable_vector(N)]`). `SVecTup` must be a tuple of vectors of +/// type `SVec`. +/// +/// Corresponds to Clang's `__builtin_sve_svset*` builtins. +/// +/// # Safety +/// +/// `IDX` must be in-bounds of the tuple. +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn sve_tuple_set(tuple: SVecTup, x: SVec) -> SVecTup; diff --git a/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs new file mode 100644 index 0000000000000..e19fc40cb9d67 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs @@ -0,0 +1,100 @@ +//@ build-pass +//@ only-aarch64 +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(abi_unadjusted, core_intrinsics, link_llvm_intrinsics, rustc_attrs)] + +// Tests that tuples of scalable vectors are passed as immediates and that the intrinsics for +// creating/getting/setting tuples of scalable vectors generate the correct assembly + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +pub struct svfloat32_t(f32); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +pub struct svfloat32x2_t(svfloat32_t, svfloat32_t); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +pub struct svfloat32x3_t(svfloat32_t, svfloat32_t, svfloat32_t); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +pub struct svfloat32x4_t(svfloat32_t, svfloat32_t, svfloat32_t, svfloat32_t); + +#[inline(never)] +#[target_feature(enable = "sve")] +pub fn svdup_n_f32(op: f32) -> svfloat32_t { + extern "C" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4f32")] + fn _svdup_n_f32(op: f32) -> svfloat32_t; + } + unsafe { _svdup_n_f32(op) } +} + +// CHECK: define { , } @svcreate2_f32( %x0, %x1) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svcreate2_f32(x0: svfloat32_t, x1: svfloat32_t) -> svfloat32x2_t { + // CHECK: %1 = insertvalue { , } poison, %x0, 0 + // CHECK-NEXT: %2 = insertvalue { , } %1, %x1, 1 + unsafe { std::intrinsics::simd::scalable::sve_tuple_create2(x0, x1) } +} + +// CHECK: define { , , } @svcreate3_f32( %x0, %x1, %x2) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svcreate3_f32(x0: svfloat32_t, x1: svfloat32_t, x2: svfloat32_t) -> svfloat32x3_t { + // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate3_f32 + // CHECK: %1 = insertvalue { , , } poison, %x0, 0 + // CHECK-NEXT: %2 = insertvalue { , , } %1, %x1, 1 + // CHECK-NEXT: %3 = insertvalue { , , } %2, %x2, 2 + unsafe { std::intrinsics::simd::scalable::sve_tuple_create3(x0, x1, x2) } +} + +// CHECK: define { , , , } @svcreate4_f32( %x0, %x1, %x2, %x3) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svcreate4_f32( + x0: svfloat32_t, + x1: svfloat32_t, + x2: svfloat32_t, + x3: svfloat32_t, +) -> svfloat32x4_t { + // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate4_f32 + // CHECK: %1 = insertvalue { , , , } poison, %x0, 0 + // CHECK-NEXT: %2 = insertvalue { , , , } %1, %x1, 1 + // CHECK-NEXT: %3 = insertvalue { , , , } %2, %x2, 2 + // CHECK-NEXT: %4 = insertvalue { , , , } %3, %x3, 3 + unsafe { std::intrinsics::simd::scalable::sve_tuple_create4(x0, x1, x2, x3) } +} + +// CHECK: define @svget2_f32({ , } %tup) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svget2_f32(tup: svfloat32x2_t) -> svfloat32_t { + // CHECK: %1 = extractvalue { , } %tup, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_get::<_, _, { IDX }>(tup) } +} + +// CHECK: define { , } @svset2_f32({ , } %tup, %x) +#[no_mangle] +#[target_feature(enable = "sve")] +pub fn svset2_f32(tup: svfloat32x2_t, x: svfloat32_t) -> svfloat32x2_t { + // CHECK: %1 = insertvalue { , } %tup, %x, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_set::<_, _, { IDX }>(tup, x) } +} + +// This function exists only so there are calls to the generic functions +#[target_feature(enable = "sve")] +pub fn test() { + let x = svdup_n_f32(2f32); + let tup = svcreate2_f32(x, x); + let x = svget2_f32::<0>(tup); + let tup = svset2_f32::<0>(tup, x); +} diff --git a/tests/ui/simd/masked-load-store-check-fail.stderr b/tests/ui/simd/masked-load-store-check-fail.stderr index 4e63d04a3b158..037855c8ec9ca 100644 --- a/tests/ui/simd/masked-load-store-check-fail.stderr +++ b/tests/ui/simd/masked-load-store-check-fail.stderr @@ -21,7 +21,7 @@ LL | | Simd::([9; 4]), LL | | ); | |_________^ note: function defined here - --> $SRC_DIR/core/src/intrinsics/simd.rs:LL:COL + --> $SRC_DIR/core/src/intrinsics/simd/mod.rs:LL:COL error[E0308]: mismatched types --> $DIR/masked-load-store-check-fail.rs:25:13 @@ -46,7 +46,7 @@ LL | | default, LL | | ); | |_________^ note: function defined here - --> $SRC_DIR/core/src/intrinsics/simd.rs:LL:COL + --> $SRC_DIR/core/src/intrinsics/simd/mod.rs:LL:COL error: aborting due to 2 previous errors diff --git a/triagebot.toml b/triagebot.toml index f99d700310dfe..719fe2a75c82b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1077,7 +1077,7 @@ cc = ["@Amanieu", "@folkertdev", "@sayantn"] message = "Some changes occurred in `std_detect`" cc = ["@Amanieu", "@folkertdev", "@sayantn"] -[mentions."library/core/src/intrinsics/simd.rs"] +[mentions."library/core/src/intrinsics/simd/mod.rs"] message = """ Some changes occurred to the platform-builtins intrinsics. Make sure the LLVM backend as well as portable-simd gets adapted for the changes. From a24ee0329e0e2c810f9468285505809be75d847b Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 26 Feb 2026 15:35:20 +0000 Subject: [PATCH 15/25] cg_llvm/debuginfo: scalable vectors Generate debuginfo for scalable vectors, following the structure that Clang generates for scalable vectors. --- compiler/rustc_codegen_gcc/src/common.rs | 4 + compiler/rustc_codegen_llvm/src/common.rs | 4 + .../src/debuginfo/dwarf_const.rs | 8 + .../src/debuginfo/metadata.rs | 120 +++++++++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 17 ++ .../rustc_codegen_ssa/src/traits/consts.rs | 1 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 36 ++++- .../scalable-vectors/debuginfo-tuples-x2.rs | 150 ++++++++++++++++++ .../scalable-vectors/debuginfo-tuples-x3.rs | 150 ++++++++++++++++++ .../scalable-vectors/debuginfo-tuples-x4.rs | 150 ++++++++++++++++++ .../scalable-vectors/debuginfo.rs | 124 +++++++++++++++ 11 files changed, 758 insertions(+), 6 deletions(-) create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs create mode 100644 tests/codegen-llvm/scalable-vectors/debuginfo.rs diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 9e548ac0a8b01..dd0064d34bc4a 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -145,6 +145,10 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i64(&self, i: i64) -> RValue<'gcc> { + self.const_int(self.type_i64(), i) + } + fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> { self.gcc_int(typ, int) } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index a134e97cc8915..dadf8e9e7d5fa 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -159,6 +159,10 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_int(self.type_i32(), i as i64) } + fn const_i64(&self, i: i64) -> &'ll Value { + self.const_int(self.type_i64(), i as i64) + } + fn const_int(&self, t: &'ll Type, i: i64) -> &'ll Value { debug_assert!( self.type_kind(t) == TypeKind::Integer, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs index 52d04625749b9..1172660af4a29 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/dwarf_const.rs @@ -35,6 +35,14 @@ declare_constant!(DW_OP_plus_uconst: u64); /// Double-checked by a static assertion in `RustWrapper.cpp`. #[allow(non_upper_case_globals)] pub(crate) const DW_OP_LLVM_fragment: u64 = 0x1000; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_constu: u64 = 0x10; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_minus: u64 = 0x1c; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_mul: u64 = 0x1e; +#[allow(non_upper_case_globals)] +pub(crate) const DW_OP_bregx: u64 = 0x92; // It describes the actual value of a source variable which might not exist in registers or in memory. #[allow(non_upper_case_globals)] pub(crate) const DW_OP_stack_value: u64 = 0x9f; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 04c0b6953290c..25307823b37c7 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use std::{iter, ptr}; use libc::{c_longlong, c_uint}; -use rustc_abi::{Align, Size}; +use rustc_abi::{Align, Layout, NumScalableVectors, Size}; use rustc_codegen_ssa::debuginfo::type_names::{VTableNameKind, cpp_like_debuginfo}; use rustc_codegen_ssa::traits::*; use rustc_hir::def::{CtorKind, DefKind}; @@ -16,12 +16,12 @@ use rustc_middle::ty::layout::{ HasTypingEnv, LayoutOf, TyAndLayout, WIDE_PTR_ADDR, WIDE_PTR_EXTRA, }; use rustc_middle::ty::{ - self, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility, + self, AdtDef, AdtKind, CoroutineArgsExt, ExistentialTraitRef, Instance, Ty, TyCtxt, Visibility, }; use rustc_session::config::{self, DebugInfo, Lto}; use rustc_span::{DUMMY_SP, FileName, RemapPathScopeComponents, SourceFile, Span, Symbol, hygiene}; use rustc_symbol_mangling::typeid_for_trait_ref; -use rustc_target::spec::DebuginfoKind; +use rustc_target::spec::{Arch, DebuginfoKind}; use smallvec::smallvec; use tracing::{debug, instrument}; @@ -33,7 +33,7 @@ use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_na use super::utils::{DIB, debug_context, get_namespace_for_item, is_node_local_to_unit}; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::metadata::type_map::build_type_with_children; -use crate::debuginfo::utils::{WidePtrKind, wide_pointer_kind}; +use crate::debuginfo::utils::{WidePtrKind, create_DIArray, wide_pointer_kind}; use crate::debuginfo::{DIBuilderExt, dwarf_const}; use crate::llvm::debuginfo::{ DIBasicType, DIBuilder, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, @@ -1039,6 +1039,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( span: Span, ) -> DINodeCreationResult<'ll> { let struct_type = unique_type_id.expect_ty(); + let ty::Adt(adt_def, _) = struct_type.kind() else { bug!("build_struct_type_di_node() called with non-struct-type: {:?}", struct_type); }; @@ -1051,6 +1052,21 @@ fn build_struct_type_di_node<'ll, 'tcx>( } else { None }; + let name = compute_debuginfo_type_name(cx.tcx, struct_type, false); + + if struct_type.is_scalable_vector() { + let parts = struct_type.scalable_vector_parts(cx.tcx).unwrap(); + return build_scalable_vector_di_node( + cx, + unique_type_id, + name, + *adt_def, + parts, + struct_type_and_layout.layout, + def_location, + containing_scope, + ); + } type_map::build_type_with_children( cx, @@ -1058,7 +1074,7 @@ fn build_struct_type_di_node<'ll, 'tcx>( cx, Stub::Struct, unique_type_id, - &compute_debuginfo_type_name(cx.tcx, struct_type, false), + &name, def_location, size_and_align_of(struct_type_and_layout), Some(containing_scope), @@ -1101,6 +1117,100 @@ fn build_struct_type_di_node<'ll, 'tcx>( ) } +/// Generate debuginfo for a `#[rustc_scalable_vector]` type. +/// +/// Debuginfo for a scalable vector uses a derived type based on a composite type. The composite +/// type has the `DIFlagVector` flag set and is based on the element type of the scalable vector. +/// The composite type has a subrange from 0 to an expression that calculates the number of +/// elements in the vector. +/// +/// ```text,ignore +/// !1 = !DIDerivedType(tag: DW_TAG_typedef, name: "svint16_t", ..., baseType: !2, ...) +/// !2 = !DICompositeType(tag: DW_TAG_array_type, baseType: !3, ..., flags: DIFlagVector, elements: !4) +/// !3 = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) +/// !4 = !{!5} +/// !5 = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) +/// ``` +/// +/// See the `CodegenType::CreateType(const BuiltinType *BT)` implementation in Clang for how this +/// is generated for C and C++. +fn build_scalable_vector_di_node<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + unique_type_id: UniqueTypeId<'tcx>, + name: String, + adt_def: AdtDef<'tcx>, + (element_count, element_ty, number_of_vectors): (u16, Ty<'tcx>, NumScalableVectors), + layout: Layout<'tcx>, + def_location: Option>, + containing_scope: &'ll DIScope, +) -> DINodeCreationResult<'ll> { + use dwarf_const::{DW_OP_bregx, DW_OP_constu, DW_OP_minus, DW_OP_mul}; + assert!(adt_def.repr().scalable()); + // This logic is specific to AArch64 for the moment, but can be extended for other architectures + // later. + assert_matches!(cx.tcx.sess.target.arch, Arch::AArch64); + + let (file_metadata, line_number) = if let Some(def_location) = def_location { + (def_location.0, def_location.1) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + + let (bitstride, element_di_node) = if element_ty.is_bool() { + (Some(llvm::LLVMValueAsMetadata(cx.const_i64(1))), type_di_node(cx, cx.tcx.types.u8)) + } else { + (None, type_di_node(cx, element_ty)) + }; + + let number_of_elements: u64 = (element_count as u64) * (number_of_vectors.0 as u64); + let number_of_elements_per_vg = number_of_elements / 2; + let mut expr = smallvec::SmallVec::<[u64; 9]>::new(); + // `($number_of_elements_per_vector_granule * (value_of_register(AArch64::VG) + 0)) - 1` + expr.push(DW_OP_constu); // Push a constant onto the stack + expr.push(number_of_elements_per_vg); + expr.push(DW_OP_bregx); // Push the value of a register + offset on to the stack + expr.push(/* AArch64::VG */ 46u64); + expr.push(0u64); + expr.push(DW_OP_mul); // Multiply top two values on stack + expr.push(DW_OP_constu); // Push a constant onto the stack + expr.push(1u64); + expr.push(DW_OP_minus); // Subtract top two values on stack + + let di_builder = DIB(cx); + let metadata = unsafe { + let upper = llvm::LLVMDIBuilderCreateExpression(di_builder, expr.as_ptr(), expr.len()); + let subrange = llvm::LLVMRustDIGetOrCreateSubrange( + di_builder, + /* CountNode */ None, + llvm::LLVMValueAsMetadata(cx.const_i64(0)), + upper, + /* Stride */ None, + ); + let subscripts = create_DIArray(di_builder, &[Some(subrange)]); + let vector_ty = llvm::LLVMRustDICreateVectorType( + di_builder, + /* Size */ 0, + layout.align.bits() as u32, + element_di_node, + subscripts, + bitstride, + ); + llvm::LLVMDIBuilderCreateTypedef( + di_builder, + vector_ty, + name.as_ptr(), + name.len(), + file_metadata, + line_number, + Some(containing_scope), + layout.align.bits() as u32, + ) + }; + + debug_context(cx).type_map.insert(unique_type_id, metadata); + DINodeCreationResult { di_node: metadata, already_stored_in_typemap: true } +} + //=----------------------------------------------------------------------------- // Tuples //=----------------------------------------------------------------------------- diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7355d11367920..0ad74c9ca43a7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2302,6 +2302,23 @@ unsafe extern "C" { Params: Option<&'a DIArray>, ); + pub(crate) fn LLVMRustDIGetOrCreateSubrange<'a>( + Builder: &DIBuilder<'a>, + CountNode: Option<&'a Metadata>, + LB: &'a Metadata, + UB: &'a Metadata, + Stride: Option<&'a Metadata>, + ) -> &'a Metadata; + + pub(crate) fn LLVMRustDICreateVectorType<'a>( + Builder: &DIBuilder<'a>, + Size: u64, + AlignInBits: u32, + Type: &'a DIType, + Subscripts: &'a DIArray, + BitStride: Option<&'a Metadata>, + ) -> &'a Metadata; + pub(crate) fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>( Location: &'a DILocation, BD: c_uint, diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 4178a9742e268..22784a8868ab5 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -20,6 +20,7 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_i8(&self, i: i8) -> Self::Value; fn const_i16(&self, i: i16) -> Self::Value; fn const_i32(&self, i: i32) -> Self::Value; + fn const_i64(&self, i: i64) -> Self::Value; fn const_int(&self, t: Self::Type, i: i64) -> Self::Value; fn const_u8(&self, i: u8) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 63ff0b2a0a0df..f0cda4493c851 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -70,6 +70,10 @@ using namespace llvm::object; // This opcode is an LLVM detail that could hypothetically change (?), so // verify that the hard-coded value in `dwarf_const.rs` still agrees with LLVM. static_assert(dwarf::DW_OP_LLVM_fragment == 0x1000); +static_assert(dwarf::DW_OP_constu == 0x10); +static_assert(dwarf::DW_OP_minus == 0x1c); +static_assert(dwarf::DW_OP_mul == 0x1e); +static_assert(dwarf::DW_OP_bregx == 0x92); static_assert(dwarf::DW_OP_stack_value == 0x9f); static LLVM_THREAD_LOCAL char *LastError; @@ -731,7 +735,7 @@ extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, } template DIT *unwrapDIPtr(LLVMMetadataRef Ref) { - return (DIT *)(Ref ? unwrap(Ref) : nullptr); + return (DIT *)(Ref ? unwrap(Ref) : nullptr); } #define DIDescriptor DIScope @@ -1207,6 +1211,36 @@ extern "C" void LLVMRustDICompositeTypeReplaceArrays( DINodeArray(unwrap(Params))); } +// LLVM's C FFI bindings don't expose the overload of `GetOrCreateSubrange` +// which takes a metadata node as the upper bound. +extern "C" LLVMMetadataRef +LLVMRustDIGetOrCreateSubrange(LLVMDIBuilderRef Builder, + LLVMMetadataRef CountNode, LLVMMetadataRef LB, + LLVMMetadataRef UB, LLVMMetadataRef Stride) { + return wrap(unwrap(Builder)->getOrCreateSubrange( + unwrapDI(CountNode), unwrapDI(LB), + unwrapDI(UB), unwrapDI(Stride))); +} + +// LLVM's CI FFI bindings don't expose the `BitStride` parameter of +// `createVectorType`. +extern "C" LLVMMetadataRef +LLVMRustDICreateVectorType(LLVMDIBuilderRef Builder, uint64_t Size, + uint32_t AlignInBits, LLVMMetadataRef Type, + LLVMMetadataRef Subscripts, + LLVMMetadataRef BitStride) { +#if LLVM_VERSION_GE(22, 0) + return wrap(unwrap(Builder)->createVectorType( + Size, AlignInBits, unwrapDI(Type), + DINodeArray(unwrapDI(Subscripts)), + unwrapDI(BitStride))); +#else + return wrap(unwrap(Builder)->createVectorType( + Size, AlignInBits, unwrapDI(Type), + DINodeArray(unwrapDI(Subscripts)))); +#endif +} + extern "C" LLVMMetadataRef LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location, unsigned BD) { diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs new file mode 100644 index 0000000000000..1aa6562c471e4 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x2.rs @@ -0,0 +1,150 @@ +//@ only-aarch64 +//@ only-linux +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint8x2_t(svint8_t, svint8_t); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint8x2_t(svuint8_t, svuint8_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint16x2_t(svint16_t, svint16_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint16x2_t(svuint16_t, svuint16_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint32x2_t(svint32_t, svint32_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint32x2_t(svuint32_t, svuint32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint64x2_t(svint64_t, svint64_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint64x2_t(svuint64_t, svuint64_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat32x2_t(svfloat32_t, svfloat32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat64x2_t(svfloat64_t, svfloat64_t); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svint8x2_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x2:[0-9]+]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS8x2]] = !{![[REALELTS8x2:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8x2]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 16, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s8: svint8x2_t; + + // CHECK-DAG: name: "svuint8x2_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x2]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + let u8: svuint8x2_t; + + // CHECK-DAG: name: "svint16x2_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x2:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16x2]] = !{![[REALELTS16x2:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16x2]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 8, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16x2_t; + + // CHECK-DAG: name: "svuint16x2_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x2]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16x2_t; + + // CHECK-DAG: name: "svint32x2_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x2:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32x2]] = !{![[REALELTS32x2:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32x2]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32x2_t; + + // CHECK-DAG: name: "svuint32x2_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x2]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32x2_t; + + // CHECK-DAG: name: "svint64x2_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x2_64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS1x2_64]] = !{![[REALELTS1x2_64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS1x2_64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 2, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64x2_t; + + // CHECK-DAG: name: "svuint64x2_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x2_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64x2_t; + + // CHECK: name: "svfloat32x2_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x2]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32x2_t; + + // CHECK: name: "svfloat64x2_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x2_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64x2_t; +} diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs new file mode 100644 index 0000000000000..89f76087bb4a7 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x3.rs @@ -0,0 +1,150 @@ +//@ only-aarch64 +//@ only-linux +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint8x3_t(svint8_t, svint8_t, svint8_t); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint8x3_t(svuint8_t, svuint8_t, svuint8_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint16x3_t(svint16_t, svint16_t, svint16_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint16x3_t(svuint16_t, svuint16_t, svuint16_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint32x3_t(svint32_t, svint32_t, svint32_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint32x3_t(svuint32_t, svuint32_t, svuint32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint64x3_t(svint64_t, svint64_t, svint64_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint64x3_t(svuint64_t, svuint64_t, svuint64_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat32x3_t(svfloat32_t, svfloat32_t, svfloat32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat64x3_t(svfloat64_t, svfloat64_t, svfloat64_t); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svint8x3_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x3:[0-9]+]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS8x3]] = !{![[REALELTS8x3:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8x3]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 24, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s8: svint8x3_t; + + // CHECK-DAG: name: "svuint8x3_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x3]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + let u8: svuint8x3_t; + + // CHECK-DAG: name: "svint16x3_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x3:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16x3]] = !{![[REALELTS16x3:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16x3]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 12, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16x3_t; + + // CHECK-DAG: name: "svuint16x3_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x3]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16x3_t; + + // CHECK-DAG: name: "svint32x3_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x3:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32x3]] = !{![[REALELTS32x3:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32x3]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 6, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32x3_t; + + // CHECK-DAG: name: "svuint32x3_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x3]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32x3_t; + + // CHECK-DAG: name: "svint64x3_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x3_64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS1x3_64]] = !{![[REALELTS1x3_64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS1x3_64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 3, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64x3_t; + + // CHECK-DAG: name: "svuint64x3_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x3_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64x3_t; + + // CHECK: name: "svfloat32x3_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x3]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32x3_t; + + // CHECK: name: "svfloat64x3_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x3_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64x3_t; +} diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs new file mode 100644 index 0000000000000..0daa3f8747d54 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo-tuples-x4.rs @@ -0,0 +1,150 @@ +//@ only-aarch64 +//@ only-linux +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint8x4_t(svint8_t, svint8_t, svint8_t, svint8_t); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint8x4_t(svuint8_t, svuint8_t, svuint8_t, svuint8_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint16x4_t(svint16_t, svint16_t, svint16_t, svint16_t); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint16x4_t(svuint16_t, svuint16_t, svuint16_t, svuint16_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint32x4_t(svint32_t, svint32_t, svint32_t, svint32_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint32x4_t(svuint32_t, svuint32_t, svuint32_t, svuint32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svint64x4_t(svint64_t, svint64_t, svint64_t, svint64_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svuint64x4_t(svuint64_t, svuint64_t, svuint64_t, svuint64_t); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat32x4_t(svfloat32_t, svfloat32_t, svfloat32_t, svfloat32_t); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[rustc_scalable_vector] +#[allow(non_camel_case_types)] +struct svfloat64x4_t(svfloat64_t, svfloat64_t, svfloat64_t, svfloat64_t); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svint8x4_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x4:[0-9]+]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS8x4]] = !{![[REALELTS8x4:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8x4]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 32, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s8: svint8x4_t; + + // CHECK-DAG: name: "svuint8x4_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8x4]]) + // CHECK-DAG: ![[ELTTY8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + let u8: svuint8x4_t; + + // CHECK-DAG: name: "svint16x4_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x4:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16x4]] = !{![[REALELTS16x4:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16x4]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 16, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16x4_t; + + // CHECK-DAG: name: "svuint16x4_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16x4]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16x4_t; + + // CHECK-DAG: name: "svint32x4_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x4:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32x4]] = !{![[REALELTS32x4:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32x4]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 8, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32x4_t; + + // CHECK-DAG: name: "svuint32x4_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x4]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32x4_t; + + // CHECK-DAG: name: "svint64x4_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x4_64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS1x4_64]] = !{![[REALELTS1x4_64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS1x4_64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64x4_t; + + // CHECK-DAG: name: "svuint64x4_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x4_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64x4_t; + + // CHECK: name: "svfloat32x4_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32x4]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32x4_t; + + // CHECK: name: "svfloat64x4_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS1x4_64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64x4_t; +} diff --git a/tests/codegen-llvm/scalable-vectors/debuginfo.rs b/tests/codegen-llvm/scalable-vectors/debuginfo.rs new file mode 100644 index 0000000000000..8b2724f06f3e0 --- /dev/null +++ b/tests/codegen-llvm/scalable-vectors/debuginfo.rs @@ -0,0 +1,124 @@ +// ignore-tidy-linelength +//@ only-aarch64 +//@ only-linux +//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 +//@ revisions: POST-LLVM-22 PRE-LLVM-22 +//@ [PRE-LLVM-22] max-llvm-major-version: 21 +//@ [POST-LLVM-22] min-llvm-version: 22 + +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features)] +#![feature(rustc_attrs)] + +// Test that we generate the correct debuginfo for scalable vector types. + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svbool_t(bool); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svint8_t(i8); + +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +struct svuint8_t(u8); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svint16_t(i16); + +#[rustc_scalable_vector(8)] +#[allow(non_camel_case_types)] +struct svuint16_t(u16); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svint32_t(i32); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svuint32_t(u32); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svint64_t(i64); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svuint64_t(u64); + +#[rustc_scalable_vector(4)] +#[allow(non_camel_case_types)] +struct svfloat32_t(f32); + +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +struct svfloat64_t(f64); + +#[target_feature(enable = "sve")] +pub fn locals() { + // CHECK-DAG: name: "svbool_t",{{.*}}, baseType: ![[CT1:[0-9]+]] + // PRE-LLVM-22-DAG: ![[CT1]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYU8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8:[0-9]+]]) + // POST-LLVM-22-DAG: ![[CT1]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYU8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8:[0-9]+]], bitStride: i64 1) + // CHECK-DAG: ![[ELTTYU8]] = !DIBasicType(name: "u8", size: 8, encoding: DW_ATE_unsigned) + // CHECK-DAG: ![[ELTS8]] = !{![[REALELTS8:[0-9]+]]} + // CHECK-DAG: ![[REALELTS8]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 8, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let b8: svbool_t; + + // CHECK-DAG: name: "svint8_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // CHECK-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYS8:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8:[0-9]+]]) + // CHECK-DAG: ![[ELTTYS8]] = !DIBasicType(name: "i8", size: 8, encoding: DW_ATE_signed) + let s8: svint8_t; + + // PRE-LLVM-22-DAG: name: "svuint8_t",{{.*}}, baseType: ![[CT1:[0-9]+]] + // POST-LLVM-22-DAG: name: "svuint8_t",{{.*}}, baseType: ![[CT8:[0-9]+]] + // POST-LLVM-22-DAG: ![[CT8]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTYU8]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS8]]) + let u8: svuint8_t; + + // CHECK-DAG: name: "svint16_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16:[0-9]+]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "i16", size: 16, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS16]] = !{![[REALELTS16:[0-9]+]]} + // CHECK-DAG: ![[REALELTS16]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 4, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s16: svint16_t; + + // CHECK-DAG: name: "svuint16_t",{{.*}}, baseType: ![[CT16:[0-9]+]] + // CHECK-DAG: ![[CT16]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY16:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS16]]) + // CHECK-DAG: ![[ELTTY16]] = !DIBasicType(name: "u16", size: 16, encoding: DW_ATE_unsigned) + let u16: svuint16_t; + + // CHECK-DAG: name: "svint32_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32:[0-9]+]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "i32", size: 32, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS32]] = !{![[REALELTS32:[0-9]+]]} + // CHECK-DAG: ![[REALELTS32]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 2, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s32: svint32_t; + + // CHECK-DAG: name: "svuint32_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "u32", size: 32, encoding: DW_ATE_unsigned) + let u32: svuint32_t; + + // CHECK-DAG: name: "svint64_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS64:[0-9]+]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "i64", size: 64, encoding: DW_ATE_signed) + // CHECK-DAG: ![[ELTS64]] = !{![[REALELTS64:[0-9]+]]} + // CHECK-DAG: ![[REALELTS64]] = !DISubrange(lowerBound: 0, upperBound: !DIExpression(DW_OP_constu, 1, DW_OP_bregx, 46, 0, DW_OP_mul, DW_OP_constu, 1, DW_OP_minus)) + let s64: svint64_t; + + // CHECK-DAG: name: "svuint64_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "u64", size: 64, encoding: DW_ATE_unsigned) + let u64: svuint64_t; + + // CHECK: name: "svfloat32_t",{{.*}}, baseType: ![[CT32:[0-9]+]] + // CHECK-DAG: ![[CT32]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY32:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS32]]) + // CHECK-DAG: ![[ELTTY32]] = !DIBasicType(name: "f32", size: 32, encoding: DW_ATE_float) + let f32: svfloat32_t; + + // CHECK: name: "svfloat64_t",{{.*}}, baseType: ![[CT64:[0-9]+]] + // CHECK-DAG: ![[CT64]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[ELTTY64:[0-9]+]],{{.*}}, flags: DIFlagVector, elements: ![[ELTS64]]) + // CHECK-DAG: ![[ELTTY64]] = !DIBasicType(name: "f64", size: 64, encoding: DW_ATE_float) + let f64: svfloat64_t; +} From 957320cdb124fe82b1c35c57c819df7bb331de4b Mon Sep 17 00:00:00 2001 From: David Wood Date: Sat, 28 Feb 2026 16:48:59 +0000 Subject: [PATCH 16/25] cg_llvm: `sve_cast` intrinsic Abstract over the existing `simd_cast` intrinsic to implement a new `sve_cast` intrinsic - this is better than allowing scalable vectors to be used with all of the generic `simd_*` intrinsics. --- .../src/debuginfo/metadata.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 203 ++++++++++-------- .../rustc_hir_analysis/src/check/intrinsic.rs | 1 + compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/simd/scalable.rs | 22 ++ tests/ui/scalable-vectors/cast-intrinsic.rs | 65 ++++++ 6 files changed, 204 insertions(+), 90 deletions(-) create mode 100644 tests/ui/scalable-vectors/cast-intrinsic.rs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 25307823b37c7..c91d3ec63a028 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; use std::path::PathBuf; use std::sync::Arc; -use std::{iter, ptr}; +use std::{assert_matches, iter, ptr}; use libc::{c_longlong, c_uint}; use rustc_abi::{Align, Layout, NumScalableVectors, Size}; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ad2c23c99820b..3e600914d6f42 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -606,6 +606,27 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.pointercast(val, self.type_ptr()) } + sym::sve_cast => { + let Some((in_cnt, in_elem, in_num_vecs)) = + args[0].layout.ty.scalable_vector_parts(self.cx.tcx) + else { + bug!("input parameter to `sve_cast` was not scalable vector"); + }; + let out_layout = self.layout_of(fn_args.type_at(1)); + let Some((out_cnt, out_elem, out_num_vecs)) = + out_layout.ty.scalable_vector_parts(self.cx.tcx) + else { + bug!("output parameter to `sve_cast` was not scalable vector"); + }; + assert_eq!(in_cnt, out_cnt); + assert_eq!(in_num_vecs, out_num_vecs); + let out_llty = self.backend_type(out_layout); + match simd_cast(self, sym::simd_cast, args, out_llty, in_elem, out_elem) { + Some(val) => val, + _ => bug!("could not cast scalable vectors"), + } + } + sym::sve_tuple_create2 => { assert_matches!( self.layout_of(fn_args.type_at(0)).backend_repr, @@ -2772,96 +2793,17 @@ fn generic_simd_intrinsic<'ll, 'tcx>( out_len } ); - // casting cares about nominal type, not just structural type - if in_elem == out_elem { - return Ok(args[0].immediate()); - } - - #[derive(Copy, Clone)] - enum Sign { - Unsigned, - Signed, - } - use Sign::*; - - enum Style { - Float, - Int(Sign), - Unsupported, - } - - let (in_style, in_width) = match in_elem.kind() { - // vectors of pointer-sized integers should've been - // disallowed before here, so this unwrap is safe. - ty::Int(i) => ( - Style::Int(Signed), - i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Uint(u) => ( - Style::Int(Unsigned), - u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Float(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0), - }; - let (out_style, out_width) = match out_elem.kind() { - ty::Int(i) => ( - Style::Int(Signed), - i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Uint(u) => ( - Style::Int(Unsigned), - u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), - ), - ty::Float(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0), - }; - - match (in_style, out_style) { - (Style::Int(sign), Style::Int(_)) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => match sign { - Sign::Signed => bx.sext(args[0].immediate(), llret_ty), - Sign::Unsigned => bx.zext(args[0].immediate(), llret_ty), - }, - }); - } - (Style::Int(Sign::Signed), Style::Float) => { - return Ok(bx.sitofp(args[0].immediate(), llret_ty)); - } - (Style::Int(Sign::Unsigned), Style::Float) => { - return Ok(bx.uitofp(args[0].immediate(), llret_ty)); - } - (Style::Float, Style::Int(sign)) => { - return Ok(match (sign, name == sym::simd_as) { - (Sign::Unsigned, false) => bx.fptoui(args[0].immediate(), llret_ty), - (Sign::Signed, false) => bx.fptosi(args[0].immediate(), llret_ty), - (_, true) => bx.cast_float_to_int( - matches!(sign, Sign::Signed), - args[0].immediate(), - llret_ty, - ), - }); - } - (Style::Float, Style::Float) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => bx.fpext(args[0].immediate(), llret_ty), - }); - } - _ => { /* Unsupported. Fallthrough. */ } + match simd_cast(bx, name, args, llret_ty, in_elem, out_elem) { + Some(val) => return Ok(val), + None => return_error!(InvalidMonomorphization::UnsupportedCast { + span, + name, + in_ty, + in_elem, + ret_ty, + out_elem + }), } - return_error!(InvalidMonomorphization::UnsupportedCast { - span, - name, - in_ty, - in_elem, - ret_ty, - out_elem - }); } macro_rules! arith_binary { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { @@ -3035,3 +2977,86 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } + +/// Implementation of `core::intrinsics::simd_cast`, re-used by `core::scalable::sve_cast`. +fn simd_cast<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + name: Symbol, + args: &[OperandRef<'tcx, &'ll Value>], + llret_ty: &'ll Type, + in_elem: Ty<'tcx>, + out_elem: Ty<'tcx>, +) -> Option<&'ll Value> { + // Casting cares about nominal type, not just structural type + if in_elem == out_elem { + return Some(args[0].immediate()); + } + + #[derive(Copy, Clone)] + enum Sign { + Unsigned, + Signed, + } + use Sign::*; + + enum Style { + Float, + Int(Sign), + Unsupported, + } + + let (in_style, in_width) = match in_elem.kind() { + // vectors of pointer-sized integers should've been + // disallowed before here, so this unwrap is safe. + ty::Int(i) => ( + Style::Int(Signed), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(Unsigned), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Float(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0), + }; + let (out_style, out_width) = match out_elem.kind() { + ty::Int(i) => ( + Style::Int(Signed), + i.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Uint(u) => ( + Style::Int(Unsigned), + u.normalize(bx.tcx().sess.target.pointer_width).bit_width().unwrap(), + ), + ty::Float(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0), + }; + + match (in_style, out_style) { + (Style::Int(sign), Style::Int(_)) => Some(match in_width.cmp(&out_width) { + Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => match sign { + Sign::Signed => bx.sext(args[0].immediate(), llret_ty), + Sign::Unsigned => bx.zext(args[0].immediate(), llret_ty), + }, + }), + (Style::Int(Sign::Signed), Style::Float) => Some(bx.sitofp(args[0].immediate(), llret_ty)), + (Style::Int(Sign::Unsigned), Style::Float) => { + Some(bx.uitofp(args[0].immediate(), llret_ty)) + } + (Style::Float, Style::Int(sign)) => Some(match (sign, name == sym::simd_as) { + (Sign::Unsigned, false) => bx.fptoui(args[0].immediate(), llret_ty), + (Sign::Signed, false) => bx.fptosi(args[0].immediate(), llret_ty), + (_, true) => { + bx.cast_float_to_int(matches!(sign, Sign::Signed), args[0].immediate(), llret_ty) + } + }), + (Style::Float, Style::Float) => Some(match in_width.cmp(&out_width) { + Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => bx.fpext(args[0].immediate(), llret_ty), + }), + _ => None, + } +} diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index ca57921089fae..58454cfc489c6 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -783,6 +783,7 @@ pub(crate) fn check_intrinsic_type( sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)), sym::simd_shuffle_const_generic => (2, 1, vec![param(0), param(0)], param(1)), + sym::sve_cast => (2, 0, vec![param(0)], param(1)), sym::sve_tuple_create2 => (2, 0, vec![param(0), param(0)], param(1)), sym::sve_tuple_create3 => (2, 0, vec![param(0), param(0), param(0)], param(1)), sym::sve_tuple_create4 => (2, 0, vec![param(0), param(0), param(0), param(0)], param(1)), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8b57421f2045c..b7ad81c15f1cd 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1979,6 +1979,7 @@ symbols! { suggestion, super_let, supertrait_item_shadowing, + sve_cast, sve_tuple_create2, sve_tuple_create3, sve_tuple_create4, diff --git a/library/core/src/intrinsics/simd/scalable.rs b/library/core/src/intrinsics/simd/scalable.rs index 5e7b64f18e533..f33831a30c070 100644 --- a/library/core/src/intrinsics/simd/scalable.rs +++ b/library/core/src/intrinsics/simd/scalable.rs @@ -2,6 +2,28 @@ //! //! In this module, a "vector" is any `#[rustc_scalable_vector]`-annotated type. +/// Numerically casts a vector, elementwise. +/// +/// `T` and `U` must be vectors of integers or floats, and must have the same length. +/// +/// When casting floats to integers, the result is truncated. Out-of-bounds result lead to UB. +/// When casting integers to floats, the result is rounded. +/// Otherwise, truncates or extends the value, maintaining the sign for signed integers. +/// +/// # Safety +/// Casting from integer types is always safe. +/// Casting between two float types is also always safe. +/// +/// Casting floats to integers truncates, following the same rules as `to_int_unchecked`. +/// Specifically, each element must: +/// * Not be `NaN` +/// * Not be infinite +/// * Be representable in the return type, after truncating off its fractional part +#[cfg(target_arch = "aarch64")] +#[rustc_intrinsic] +#[rustc_nounwind] +pub unsafe fn sve_cast(x: T) -> U; + /// Create a tuple of two vectors. /// /// `SVecTup` must be a scalable vector tuple (`#[rustc_scalable_vector]`) and `SVec` must be a diff --git a/tests/ui/scalable-vectors/cast-intrinsic.rs b/tests/ui/scalable-vectors/cast-intrinsic.rs new file mode 100644 index 0000000000000..f2157d8bcc14b --- /dev/null +++ b/tests/ui/scalable-vectors/cast-intrinsic.rs @@ -0,0 +1,65 @@ +//@ check-pass +//@ only-aarch64 +#![crate_type = "lib"] +#![allow(incomplete_features, internal_features, improper_ctypes)] +#![feature(abi_unadjusted, core_intrinsics, link_llvm_intrinsics, rustc_attrs)] + +use std::intrinsics::simd::scalable::sve_cast; + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(16)] +#[allow(non_camel_case_types)] +pub struct svbool_t(bool); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +pub struct svbool2_t(bool); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +pub struct svint64_t(i64); + +#[derive(Copy, Clone)] +#[rustc_scalable_vector(2)] +#[allow(non_camel_case_types)] +pub struct nxv2i16(i16); + +pub trait SveInto: Sized { + unsafe fn sve_into(self) -> T; +} + +impl SveInto for svbool_t { + #[target_feature(enable = "sve")] + unsafe fn sve_into(self) -> svbool2_t { + unsafe extern "C" { + #[cfg_attr( + target_arch = "aarch64", + link_name = concat!("llvm.aarch64.sve.convert.from.svbool.nxv2i1") + )] + fn convert_from_svbool(b: svbool_t) -> svbool2_t; + } + unsafe { convert_from_svbool(self) } + } +} + +#[target_feature(enable = "sve")] +pub unsafe fn svld1sh_gather_s64offset_s64( + pg: svbool_t, + base: *const i16, + offsets: svint64_t, +) -> svint64_t { + unsafe extern "unadjusted" { + #[cfg_attr( + target_arch = "aarch64", + link_name = "llvm.aarch64.sve.ld1.gather.nxv2i16" + )] + fn _svld1sh_gather_s64offset_s64( + pg: svbool2_t, + base: *const i16, + offsets: svint64_t, + ) -> nxv2i16; + } + sve_cast(_svld1sh_gather_s64offset_s64(pg.sve_into(), base, offsets)) +} From c3d9f996c462b99ad29f20ff064eb49a622d755c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Apr 2026 14:22:24 +1100 Subject: [PATCH 17/25] Remove an unused `StableHash` impl. --- compiler/rustc_middle/src/ich/impls_syntax.rs | 52 +------------------ 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/compiler/rustc_middle/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs index 1063b37be87e8..d932bfdee6ef6 100644 --- a/compiler/rustc_middle/src/ich/impls_syntax.rs +++ b/compiler/rustc_middle/src/ich/impls_syntax.rs @@ -4,7 +4,7 @@ use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir as hir; -use rustc_span::{SourceFile, Symbol, sym}; +use rustc_span::{Symbol, sym}; use smallvec::SmallVec; use super::StableHashingContext; @@ -54,56 +54,6 @@ fn is_ignored_attr(name: Symbol) -> bool { IGNORED_ATTRIBUTES.contains(&name) } -impl<'a> HashStable> for SourceFile { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let SourceFile { - name: _, // We hash the smaller stable_id instead of this - stable_id, - cnum, - // Do not hash the source as it is not encoded - src: _, - ref src_hash, - // Already includes src_hash, this is redundant - checksum_hash: _, - external_src: _, - start_pos: _, - normalized_source_len: _, - unnormalized_source_len: _, - lines: _, - ref multibyte_chars, - ref normalized_pos, - } = *self; - - stable_id.hash_stable(hcx, hasher); - - src_hash.hash_stable(hcx, hasher); - - { - // We are always in `Lines` form by the time we reach here. - assert!(self.lines.read().is_lines()); - let lines = self.lines(); - // We only hash the relative position within this source_file - lines.len().hash_stable(hcx, hasher); - for &line in lines.iter() { - line.hash_stable(hcx, hasher); - } - } - - // We only hash the relative position within this source_file - multibyte_chars.len().hash_stable(hcx, hasher); - for &char_pos in multibyte_chars.iter() { - char_pos.hash_stable(hcx, hasher); - } - - normalized_pos.len().hash_stable(hcx, hasher); - for &char_pos in normalized_pos.iter() { - char_pos.hash_stable(hcx, hasher); - } - - cnum.hash_stable(hcx, hasher); - } -} - impl<'tcx> HashStable> for rustc_feature::Features { fn hash_stable(&self, hcx: &mut StableHashingContext<'tcx>, hasher: &mut StableHasher) { // Unfortunately we cannot exhaustively list fields here, since the From d6b280675ea24753fe5ed701c8ad119459a94a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jalil=20David=20Salam=C3=A9=20Messina?= Date: Tue, 9 Jul 2024 19:21:54 +0200 Subject: [PATCH 18/25] feat(core): impl Step for NonZero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement Step for NonZero unsigned integers as discussed in [libs-team#130][1]. [1]: https://github.com/rust-lang/libs-team/issues/130 `step_nonzero_impls` was adapted from `step_integer_impls` and the tests were adapted from the step tests. Signed-off-by: Jalil David Salamé Messina --- library/core/src/iter/range.rs | 146 ++++++++++++++++++++++++-- library/coretests/tests/iter/range.rs | 139 ++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 10 deletions(-) diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 951bb5d0029f6..adc561879ebd3 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -15,6 +15,7 @@ macro_rules! unsafe_impl_trusted_step { )*}; } unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize Ipv4Addr Ipv6Addr]; +unsafe_impl_trusted_step![NonZero NonZero NonZero NonZero NonZero NonZero]; /// Objects that have a notion of *successor* and *predecessor* operations. /// @@ -255,10 +256,8 @@ macro_rules! step_identical_methods { macro_rules! step_integer_impls { { - narrower than or same width as usize: - $( [ $u_narrower:ident $i_narrower:ident ] ),+; - wider than usize: - $( [ $u_wider:ident $i_wider:ident ] ),+; + [ $( [ $u_narrower:ident $i_narrower:ident ] ),+ ] <= usize < + [ $( [ $u_wider:ident $i_wider:ident ] ),+ ] } => { $( #[allow(unreachable_patterns)] @@ -437,20 +436,138 @@ macro_rules! step_integer_impls { #[cfg(target_pointer_width = "64")] step_integer_impls! { - narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize]; - wider than usize: [u128 i128]; + [ [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize] ] <= usize < [ [u128 i128] ] } #[cfg(target_pointer_width = "32")] step_integer_impls! { - narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize]; - wider than usize: [u64 i64], [u128 i128]; + [ [u8 i8], [u16 i16], [u32 i32], [usize isize] ] <= usize < [ [u64 i64], [u128 i128] ] } #[cfg(target_pointer_width = "16")] step_integer_impls! { - narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize]; - wider than usize: [u32 i32], [u64 i64], [u128 i128]; + [ [u8 i8], [u16 i16], [usize isize] ] <= usize < [ [u32 i32], [u64 i64], [u128 i128] ] +} + +// These are still macro-generated because the integer literals resolve to different types. +macro_rules! step_nonzero_identical_methods { + ($int:ident) => { + #[inline] + unsafe fn forward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start + n` doesn't overflow. + unsafe { Self::new_unchecked(start.get().unchecked_add(n as $int)) } + } + + #[inline] + unsafe fn backward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start - n` doesn't overflow or hit zero. + unsafe { Self::new_unchecked(start.get().unchecked_sub(n as $int)) } + } + + #[inline] + #[allow(arithmetic_overflow)] + #[rustc_inherit_overflow_checks] + fn forward(start: Self, n: usize) -> Self { + // In debug builds, trigger a panic on overflow. + // This should optimize completely out in release builds. + if Self::forward_checked(start, n).is_none() { + let _ = $int::MAX + 1; + } + // Do saturating math (wrapping math causes UB if it wraps to Zero) + start.saturating_add(n as $int) + } + + #[inline] + #[allow(arithmetic_overflow)] + #[rustc_inherit_overflow_checks] + fn backward(start: Self, n: usize) -> Self { + // In debug builds, trigger a panic on overflow. + // This should optimize completely out in release builds. + if Self::backward_checked(start, n).is_none() { + let _ = $int::MIN - 1; + } + // Do saturating math (wrapping math causes UB if it wraps to Zero) + Self::new(start.get().saturating_sub(n as $int)).unwrap_or(Self::MIN) + } + + #[inline] + fn steps_between(start: &Self, end: &Self) -> (usize, Option) { + if *start <= *end { + #[allow(irrefutable_let_patterns, reason = "happens on usize or narrower")] + if let Ok(steps) = usize::try_from(end.get() - start.get()) { + (steps, Some(steps)) + } else { + (usize::MAX, None) + } + } else { + (0, None) + } + } + }; +} + +macro_rules! step_nonzero_impls { + { + [$( $narrower:ident ),+] <= usize < [$( $wider:ident ),+] + } => { + $( + #[allow(unreachable_patterns)] + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + impl Step for NonZero<$narrower> { + step_nonzero_identical_methods!($narrower); + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + match $narrower::try_from(n) { + Ok(n) => start.checked_add(n), + Err(_) => None, // if n is out of range, `unsigned_start + n` is too + } + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + match $narrower::try_from(n) { + // *_sub() is not implemented on NonZero + Ok(n) => start.get().checked_sub(n).and_then(Self::new), + Err(_) => None, // if n is out of range, `unsigned_start - n` is too + } + } + } + )+ + + $( + #[allow(unreachable_patterns)] + #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + impl Step for NonZero<$wider> { + step_nonzero_identical_methods!($wider); + + #[inline] + fn forward_checked(start: Self, n: usize) -> Option { + start.checked_add(n as $wider) + } + + #[inline] + fn backward_checked(start: Self, n: usize) -> Option { + start.get().checked_sub(n as $wider).and_then(Self::new) + } + } + )+ + }; +} + +#[cfg(target_pointer_width = "64")] +step_nonzero_impls! { + [u8, u16, u32, u64, usize] <= usize < [u128] +} + +#[cfg(target_pointer_width = "32")] +step_nonzero_impls! { + [u8, u16, u32, usize] <= usize < [u64, u128] +} + +#[cfg(target_pointer_width = "16")] +step_nonzero_impls! { + [u8, u16, usize] <= usize < [u32, u64, u128] } #[unstable(feature = "step_trait", issue = "42168")] @@ -944,6 +1061,7 @@ impl Iterator for ops::Range { range_exact_iter_impl! { usize u8 u16 isize i8 i16 + NonZero NonZero NonZero // These are incorrect per the reasoning above, // but removing them would be a breaking change as they were stabilized in Rust 1.0.0. @@ -956,22 +1074,30 @@ range_exact_iter_impl! { unsafe_range_trusted_random_access_impl! { usize u8 u16 isize i8 i16 + NonZero NonZero NonZero } #[cfg(target_pointer_width = "32")] unsafe_range_trusted_random_access_impl! { u32 i32 + NonZero } #[cfg(target_pointer_width = "64")] unsafe_range_trusted_random_access_impl! { u32 i32 u64 i64 + NonZero + NonZero } range_incl_exact_iter_impl! { u8 i8 + NonZero + // Since RangeInclusive> can only be 1..=uN::MAX the length of this range is always + // <= uN::MAX, so they are always valid ExactSizeIterator unlike the ranges that include zero. + NonZero NonZero // These are incorrect per the reasoning above, // but removing them would be a breaking change as they were stabilized in Rust 1.26.0. diff --git a/library/coretests/tests/iter/range.rs b/library/coretests/tests/iter/range.rs index d5d2b8bf2b047..4a00e6f96bda8 100644 --- a/library/coretests/tests/iter/range.rs +++ b/library/coretests/tests/iter/range.rs @@ -502,3 +502,142 @@ fn test_double_ended_range() { panic!("unreachable"); } } + +macro_rules! nz { + (NonZero<$type:ident>($val:literal)) => { + ::core::num::NonZero::<$type>::new($val).unwrap() + }; + (NonZero<$type:ident>::MAX) => { + ::core::num::NonZero::<$type>::new($type::MAX).unwrap() + }; +} + +macro_rules! nonzero_array { + (NonZero<$type:ident>[$($val:literal),*]) => { + [$(nz!(NonZero<$type>($val))),*] + } +} + +macro_rules! nonzero_range { + (NonZero<$type:ident>($($left:literal)?..$($right:literal)?)) => { + nz!(NonZero<$type>($($left)?))..nz!(NonZero<$type>($($right)?)) + }; + (NonZero<$type:ident>($($left:literal)?..MAX)) => { + nz!(NonZero<$type>($($left)?))..nz!(NonZero<$type>::MAX) + }; + (NonZero<$type:ident>($($left:literal)?..=$right:literal)) => { + nz!(NonZero<$type>($($left)?))..=nz!(NonZero<$type>($right)) + }; + (NonZero<$type:ident>($($left:literal)?..=MAX)) => { + nz!(NonZero<$type>($($left)?))..=nz!(NonZero<$type>::MAX) + }; +} + +#[test] +fn test_nonzero_range() { + assert_eq!( + nonzero_range!(NonZero(1..=21)).step_by(5).collect::>(), + nonzero_array!(NonZero[1, 6, 11, 16, 21]) + ); + assert_eq!( + nonzero_range!(NonZero(1..=20)).step_by(5).collect::>(), + nonzero_array!(NonZero[1, 6, 11, 16]) + ); + assert_eq!( + nonzero_range!(NonZero(1..20)).step_by(5).collect::>(), + nonzero_array!(NonZero[1, 6, 11, 16]) + ); + assert_eq!( + nonzero_range!(NonZero(1..21)).rev().step_by(5).collect::>(), + nonzero_array!(NonZero[20, 15, 10, 5]) + ); + assert_eq!( + nonzero_range!(NonZero(1..21)).rev().step_by(6).collect::>(), + nonzero_array!(NonZero[20, 14, 8, 2]) + ); + assert_eq!( + nonzero_range!(NonZero(200..255)).step_by(50).collect::>(), + nonzero_array!(NonZero[200, 250]) + ); + assert_eq!( + nonzero_range!(NonZero(200..5)).step_by(1).collect::>(), + nonzero_array!(NonZero[]) + ); + assert_eq!( + nonzero_range!(NonZero(200..200)).step_by(1).collect::>(), + nonzero_array!(NonZero[]) + ); + + assert_eq!(nonzero_range!(NonZero(10..20)).step_by(1).size_hint(), (10, Some(10))); + assert_eq!(nonzero_range!(NonZero(10..20)).step_by(5).size_hint(), (2, Some(2))); + assert_eq!(nonzero_range!(NonZero(1..21)).rev().step_by(5).size_hint(), (4, Some(4))); + assert_eq!(nonzero_range!(NonZero(1..21)).rev().step_by(6).size_hint(), (4, Some(4))); + assert_eq!(nonzero_range!(NonZero(20..1)).step_by(1).size_hint(), (0, Some(0))); + assert_eq!(nonzero_range!(NonZero(20..20)).step_by(1).size_hint(), (0, Some(0))); + + // ExactSizeIterator + assert_eq!(nonzero_range!(NonZero(1..=MAX)).step_by(1).len(), usize::from(u8::MAX)); + assert_eq!(nonzero_range!(NonZero(1..=MAX)).step_by(1).len(), usize::from(u16::MAX)); + assert_eq!(nonzero_range!(NonZero(1..=MAX)).step_by(1).len(), usize::MAX); + + // Limits (next) + let mut range = nonzero_range!(NonZero(254..=MAX)); + assert_eq!(range.next(), Some(nz!(NonZero(254)))); + assert_eq!(range.next(), Some(nz!(NonZero(255)))); + assert_eq!(range.next(), None); + + let mut range = nonzero_range!(NonZero(65534..=MAX)); + assert_eq!(range.next(), Some(nz!(NonZero(65534)))); + assert_eq!(range.next(), Some(nz!(NonZero(65535)))); + assert_eq!(range.next(), None); + + // Limits (size_hint, exclusive range) + assert_eq!( + nonzero_range!(NonZero(1..MAX)).step_by(1).size_hint(), + (u8::MAX as usize - 1, Some(u8::MAX as usize - 1)) + ); + assert_eq!( + nonzero_range!(NonZero(1..MAX)).step_by(1).size_hint(), + (u16::MAX as usize - 1, Some(u16::MAX as usize - 1)) + ); + #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] + assert_eq!( + nonzero_range!(NonZero(1..MAX)).step_by(1).size_hint(), + (u32::MAX as usize - 1, Some(u32::MAX as usize - 1)) + ); + #[cfg(target_pointer_width = "64")] + assert_eq!( + nonzero_range!(NonZero(1..MAX)).step_by(1).size_hint(), + (u64::MAX as usize - 1, Some(u64::MAX as usize - 1)) + ); + assert_eq!(nonzero_range!(NonZero(1..MAX)).step_by(1).size_hint(), (usize::MAX, None)); + assert_eq!( + nonzero_range!(NonZero(1..MAX)).step_by(1).size_hint(), + (usize::MAX - 1, Some(usize::MAX - 1)) + ); + + // Limits (size_hint, inclusive range) + assert_eq!( + nonzero_range!(NonZero(1..=MAX)).step_by(1).size_hint(), + (u8::MAX as usize, Some(u8::MAX as usize)) + ); + assert_eq!( + nonzero_range!(NonZero(1..=MAX)).step_by(1).size_hint(), + (u16::MAX as usize, Some(u16::MAX as usize)) + ); + #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] + assert_eq!( + nonzero_range!(NonZero(1..=MAX)).step_by(1).size_hint(), + (u32::MAX as usize, Some(u32::MAX as usize)) + ); + #[cfg(target_pointer_width = "64")] + assert_eq!( + nonzero_range!(NonZero(1..=MAX)).step_by(1).size_hint(), + (u64::MAX as usize, Some(u64::MAX as usize)) + ); + assert_eq!(nonzero_range!(NonZero(1..=MAX)).step_by(1).size_hint(), (usize::MAX, None)); + assert_eq!( + nonzero_range!(NonZero(1..=MAX)).step_by(1).size_hint(), + (usize::MAX, Some(usize::MAX)) + ); +} From 2bf76b4388e95f335abad1cf3fef6d9351bde48e Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Fri, 3 Apr 2026 17:06:50 +0100 Subject: [PATCH 19/25] Add a regression test for an escaping late-bound region ICE during canonicalisation --- ...te-bound-region-in-canonical-ice-113870.rs | 24 +++++++++ ...ound-region-in-canonical-ice-113870.stderr | 52 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.stderr diff --git a/tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.rs b/tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.rs new file mode 100644 index 0000000000000..6a75bf6ca4b73 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.rs @@ -0,0 +1,24 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/113870 + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +const fn allow<'b, 'b>() -> usize +//~^ ERROR the name `'b` is already used for a generic parameter in this item's generic parameters +where + for<'b> [u8; foo::<'a, 'b>()]: Sized, + //~^ ERROR lifetime name `'b` shadows a lifetime name that is already in scope + //~| ERROR use of undeclared lifetime name `'a` + //~| ERROR cannot capture late-bound lifetime in constant +{ + 4 +} + +const fn foo<'a, 'b>() -> usize +where + &'a (): Sized, + &'b (): Sized, +{ + 4 +} +//~^ ERROR `main` function not found in crate diff --git a/tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.stderr b/tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.stderr new file mode 100644 index 0000000000000..f036a2a341e3c --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/escaping-late-bound-region-in-canonical-ice-113870.stderr @@ -0,0 +1,52 @@ +error[E0403]: the name `'b` is already used for a generic parameter in this item's generic parameters + --> $DIR/escaping-late-bound-region-in-canonical-ice-113870.rs:6:20 + | +LL | const fn allow<'b, 'b>() -> usize + | -- ^^ already used + | | + | first use of `'b` + +error[E0496]: lifetime name `'b` shadows a lifetime name that is already in scope + --> $DIR/escaping-late-bound-region-in-canonical-ice-113870.rs:9:9 + | +LL | const fn allow<'b, 'b>() -> usize + | -- first declared here +... +LL | for<'b> [u8; foo::<'a, 'b>()]: Sized, + | ^^ lifetime `'b` already in scope + +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/escaping-late-bound-region-in-canonical-ice-113870.rs:9:24 + | +LL | for<'b> [u8; foo::<'a, 'b>()]: Sized, + | ^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | for<'a, 'b> [u8; foo::<'a, 'b>()]: Sized, + | +++ +help: consider introducing lifetime `'a` here + | +LL | const fn allow<'a, 'b, 'b>() -> usize + | +++ + +error[E0601]: `main` function not found in crate `escaping_late_bound_region_in_canonical_ice_113870` + --> $DIR/escaping-late-bound-region-in-canonical-ice-113870.rs:23:2 + | +LL | } + | ^ consider adding a `main` function to `$DIR/escaping-late-bound-region-in-canonical-ice-113870.rs` + +error: cannot capture late-bound lifetime in constant + --> $DIR/escaping-late-bound-region-in-canonical-ice-113870.rs:9:28 + | +LL | const fn allow<'b, 'b>() -> usize + | -- lifetime defined here +... +LL | for<'b> [u8; foo::<'a, 'b>()]: Sized, + | ^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0261, E0403, E0496, E0601. +For more information about an error, try `rustc --explain E0261`. From 6dd94d6074217654b5756af214693bcc5cc32cfa Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Fri, 3 Apr 2026 17:22:16 +0100 Subject: [PATCH 20/25] Add a regression test for an index-out-of-bounds ICE on `partial_cmp` --- ...out-of-bounds-on-partial-cmp-ice-114056.rs | 12 +++++++++ ...of-bounds-on-partial-cmp-ice-114056.stderr | 27 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.rs create mode 100644 tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.stderr diff --git a/tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.rs b/tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.rs new file mode 100644 index 0000000000000..73230055908d1 --- /dev/null +++ b/tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.rs @@ -0,0 +1,12 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/114056 + +struct P(Q); + +impl P { + fn foo(&self) { + self.partial_cmp(()) + //~^ ERROR the method `partial_cmp` exists for reference `&P`, but its trait bounds were not satisfied + } +} + +fn main() {} diff --git a/tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.stderr b/tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.stderr new file mode 100644 index 0000000000000..47e0d2d90357a --- /dev/null +++ b/tests/ui/typeck/index-out-of-bounds-on-partial-cmp-ice-114056.stderr @@ -0,0 +1,27 @@ +error[E0599]: the method `partial_cmp` exists for reference `&P`, but its trait bounds were not satisfied + --> $DIR/index-out-of-bounds-on-partial-cmp-ice-114056.rs:7:14 + | +LL | struct P(Q); + | ----------- doesn't satisfy `P: Iterator` or `P: PartialOrd<_>` +... +LL | self.partial_cmp(()) + | ^^^^^^^^^^^ method cannot be called on `&P` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `P: PartialOrd<_>` + which is required by `&P: PartialOrd<&_>` + `&P: Iterator` + which is required by `&mut &P: Iterator` + `P: Iterator` + which is required by `&mut P: Iterator` +note: the trait `Iterator` must be implemented + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL +help: consider annotating `P` with `#[derive(PartialEq, PartialOrd)]` + | +LL + #[derive(PartialEq, PartialOrd)] +LL | struct P(Q); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. From fedb021f7e4ed581d0a091fd23322c71ed9dd03f Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Fri, 3 Apr 2026 17:27:57 +0100 Subject: [PATCH 21/25] Add a regression test for an ICE when using `impl Trait` with a const generic bound --- ...pper-impl-trait-with-const-bound-118278.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/ice-wrapper-impl-trait-with-const-bound-118278.rs diff --git a/tests/ui/const-generics/generic_const_exprs/ice-wrapper-impl-trait-with-const-bound-118278.rs b/tests/ui/const-generics/generic_const_exprs/ice-wrapper-impl-trait-with-const-bound-118278.rs new file mode 100644 index 0000000000000..02dff02430989 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/ice-wrapper-impl-trait-with-const-bound-118278.rs @@ -0,0 +1,28 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/118278 + +//@ check-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +pub trait Foo { + const SIZE: usize; +} + +impl Foo for u64 { + const SIZE: usize = 8; +} + +pub struct Wrapper +where + T: Foo, + [(); T::SIZE]:, +{ + pub t: T, +} + +pub fn bar() -> Wrapper { + Wrapper { t: 10 } +} + +fn main() {} From 13373707aefd4cf5685b3bd9987afa6a1b07c170 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 25 Mar 2026 16:59:54 +0100 Subject: [PATCH 22/25] Remove `BuiltinLintDiag::SingleUseLifetime` --- compiler/rustc_lint/src/early/diagnostics.rs | 31 ------------ compiler/rustc_lint/src/lints.rs | 24 ---------- compiler/rustc_lint_defs/src/lib.rs | 12 ----- compiler/rustc_resolve/src/errors.rs | 24 ++++++++++ .../rustc_resolve/src/late/diagnostics.rs | 47 +++++++++++++++---- 5 files changed, 63 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 75824670d2496..dbf96a1c40492 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -8,7 +8,6 @@ use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; -use tracing::debug; use crate::lints; @@ -87,36 +86,6 @@ impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> { } .into_diag(dcx, level) } - BuiltinLintDiag::SingleUseLifetime { - param_span, - use_span, - elidable, - deletion_span, - ident, - } => { - debug!(?param_span, ?use_span, ?deletion_span); - let suggestion = if let Some(deletion_span) = deletion_span { - let (use_span, replace_lt) = if elidable { - let use_span = - self.sess.source_map().span_extend_while_whitespace(use_span); - (use_span, String::new()) - } else { - (use_span, "'_".to_owned()) - }; - debug!(?deletion_span, ?use_span); - - // issue 107998 for the case such as a wrong function pointer type - // `deletion_span` is empty and there is no need to report lifetime uses here - let deletion_span = - if deletion_span.is_empty() { None } else { Some(deletion_span) }; - Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt }) - } else { - None - }; - - lints::SingleUseLifetime { suggestion, param_span, use_span, ident } - .into_diag(dcx, level) - } BuiltinLintDiag::NamedArgumentUsedPositionally { position_sp_to_replace, position_sp_for_msg, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b77225db4f0eb..14dfcca0c4669 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3081,30 +3081,6 @@ pub(crate) enum UnusedImportsSugg { }, } -#[derive(Diagnostic)] -#[diag("lifetime parameter `{$ident}` only used once")] -pub(crate) struct SingleUseLifetime { - #[label("this lifetime...")] - pub param_span: Span, - #[label("...is used only here")] - pub use_span: Span, - #[subdiagnostic] - pub suggestion: Option, - - pub ident: Ident, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] -pub(crate) struct SingleUseLifetimeSugg { - #[suggestion_part(code = "")] - pub deletion_span: Option, - #[suggestion_part(code = "{replace_lt}")] - pub use_span: Span, - - pub replace_lt: String, -} - #[derive(Diagnostic)] #[diag("named argument `{$named_arg_name}` is not used by name")] pub(crate) struct NamedArgumentUsedPositionally { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 71b507f67e8f2..32e88976f589c 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -665,18 +665,6 @@ pub enum BuiltinLintDiag { test_module_span: Option, span_snippets: Vec, }, - SingleUseLifetime { - /// Span of the parameter which declares this lifetime. - param_span: Span, - /// Span of the code that should be removed when eliding this lifetime. - /// This span should include leading or trailing comma. - deletion_span: Option, - /// Span of the single use, or None if the lifetime is never used. - /// If true, the lifetime will be fully elided. - use_span: Span, - elidable: bool, - ident: Ident, - }, NamedArgumentUsedPositionally { /// Span where the named argument is used by position and will be replaced with the named /// argument name diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 43b006d72e500..5f3079cdba0de 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1697,3 +1697,27 @@ pub(crate) struct AssociatedConstElidedLifetime { #[note("cannot automatically infer `'static` because of other lifetimes in scope")] pub lifetimes_in_scope: MultiSpan, } + +#[derive(Diagnostic)] +#[diag("lifetime parameter `{$ident}` only used once")] +pub(crate) struct SingleUseLifetime { + #[label("this lifetime...")] + pub param_span: Span, + #[label("...is used only here")] + pub use_span: Span, + #[subdiagnostic] + pub suggestion: Option, + + pub ident: Ident, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] +pub(crate) struct SingleUseLifetimeSugg { + #[suggestion_part(code = "")] + pub deletion_span: Option, + #[suggestion_part(code = "{replace_lt}")] + pub use_span: Span, + + pub replace_lt: String, +} diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 7539e3c4f499f..3128e67724ca8 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -14,7 +14,7 @@ use rustc_ast_pretty::pprust::{path_to_string, where_bound_predicate_to_string}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize, + Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize, struct_span_code_err, }; use rustc_hir as hir; @@ -3647,16 +3647,47 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let deletion_span = if param.bounds.is_empty() { deletion_span() } else { None }; - self.r.lint_buffer.buffer_lint( + let tcx = self.r.tcx; + let param_ident = param.ident; + let sugg_data = deletion_span.map(|deletion_span| { + if elidable { + let use_span = + tcx.sess.source_map().span_extend_while_whitespace(use_span); + (deletion_span, use_span, String::new()) + } else { + (deletion_span, use_span, "'_".to_owned()) + } + }); + self.r.lint_buffer.dyn_buffer_lint( lint::builtin::SINGLE_USE_LIFETIMES, param.id, param.ident.span, - lint::BuiltinLintDiag::SingleUseLifetime { - param_span: param.ident.span, - use_span, - elidable, - deletion_span, - ident: param.ident, + move |dcx, level| { + let suggestion = + sugg_data.map(|(deletion_span, use_span, replace_lt)| { + // issue 107998 for the case such as a wrong function pointer type + // `deletion_span` is empty and there is no need to report lifetime uses here + let deletion_span = if deletion_span.is_empty() { + None + } else { + Some(deletion_span) + }; + errors::SingleUseLifetimeSugg { + deletion_span, + use_span, + replace_lt, + } + }); + let param_span = param_ident.span; + debug!(?param_span, ?use_span, ?deletion_span); + + errors::SingleUseLifetime { + suggestion, + param_span, + use_span, + ident: param_ident, + } + .into_diag(dcx, level) }, ); } From 99f9ba0a33ff2ee7165df72bf7615e4a985ac6e4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 25 Mar 2026 17:36:26 +0100 Subject: [PATCH 23/25] Remove `BuiltinLintDiag::AbsPathWithModule` --- compiler/rustc_lint/src/early/diagnostics.rs | 22 --------------- compiler/rustc_lint/src/lints.rs | 19 ------------- compiler/rustc_lint_defs/src/lib.rs | 1 - compiler/rustc_resolve/src/diagnostics.rs | 28 ++++++++++++++++---- compiler/rustc_resolve/src/errors.rs | 19 +++++++++++++ 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index dbf96a1c40492..19dc1c40e6d34 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -24,28 +24,6 @@ pub struct DecorateBuiltinLint<'sess, 'tcx> { impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { match self.diagnostic { - BuiltinLintDiag::AbsPathWithModule(mod_span) => { - let (replacement, applicability) = - match self.sess.source_map().span_to_snippet(mod_span) { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = - if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) - } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - lints::AbsPathWithModule { - sugg: lints::AbsPathWithModuleSugg { - span: mod_span, - applicability, - replacement, - }, - } - .into_diag(dcx, level) - } BuiltinLintDiag::ElidedLifetimesInPaths( n, path_span, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 14dfcca0c4669..5819f2bc151f9 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3011,25 +3011,6 @@ pub(crate) struct IllFormedAttributeInput { pub docs: &'static str, } -#[derive(Diagnostic)] -#[diag( - "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" -)] -pub(crate) struct AbsPathWithModule { - #[subdiagnostic] - pub sugg: AbsPathWithModuleSugg, -} - -#[derive(Subdiagnostic)] -#[suggestion("use `crate`", code = "{replacement}")] -pub(crate) struct AbsPathWithModuleSugg { - #[primary_span] - pub span: Span, - #[applicability] - pub applicability: Applicability, - pub replacement: String, -} - #[derive(Diagnostic)] #[diag("hidden lifetime parameters in types are deprecated")] pub(crate) struct ElidedLifetimesInPaths { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 32e88976f589c..7e51029b02bb6 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -656,7 +656,6 @@ pub enum DeprecatedSinceKind { // becomes hacky (and it gets allocated). #[derive(Debug)] pub enum BuiltinLintDiag { - AbsPathWithModule(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), UnusedImports { remove_whole_use: bool, diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index cab37f5c0faa6..74f258b42b001 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, MultiSpan, SuggestionStyle, + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, struct_span_code_err, }; use rustc_feature::BUILTIN_ATTRIBUTES; @@ -23,7 +23,6 @@ use rustc_hir::{PrimTy, Stability, StabilityLevel, find_attr}; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, @@ -510,12 +509,31 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; } - let diag = BuiltinLintDiag::AbsPathWithModule(root_span); - self.lint_buffer.buffer_lint( + let (replacement, applicability) = + match self.tcx.sess.source_map().span_to_snippet(root_span) { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; + self.lint_buffer.dyn_buffer_lint( ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, node_id, root_span, - diag, + move |dcx, level| { + errors::AbsPathWithModule { + sugg: errors::AbsPathWithModuleSugg { + span: root_span, + applicability, + replacement, + }, + } + .into_diag(dcx, level) + }, ); } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 5f3079cdba0de..305231a3ea0d9 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1721,3 +1721,22 @@ pub(crate) struct SingleUseLifetimeSugg { pub replace_lt: String, } + +#[derive(Diagnostic)] +#[diag( + "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" +)] +pub(crate) struct AbsPathWithModule { + #[subdiagnostic] + pub sugg: AbsPathWithModuleSugg, +} + +#[derive(Subdiagnostic)] +#[suggestion("use `crate`", code = "{replacement}")] +pub(crate) struct AbsPathWithModuleSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, + pub replacement: String, +} From 6f751ec1824d98e7b7537d0878849df155ce6116 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 25 Mar 2026 23:25:45 +0100 Subject: [PATCH 24/25] Make `DecorateDiagCompat::Dynamic` take an `Any` reference allowing to downcast to `Session` --- compiler/rustc_errors/src/decorate_diag.rs | 28 ++++++- compiler/rustc_errors/src/diagnostic.rs | 11 --- compiler/rustc_lint/src/early.rs | 10 ++- compiler/rustc_lint/src/early/diagnostics.rs | 15 ++++ compiler/rustc_resolve/src/diagnostics.rs | 30 ++++---- .../rustc_resolve/src/late/diagnostics.rs | 73 +++++++++---------- compiler/rustc_session/src/parse.rs | 2 +- 7 files changed, 102 insertions(+), 67 deletions(-) diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs index 61dd8f0493f91..3215914698896 100644 --- a/compiler/rustc_errors/src/decorate_diag.rs +++ b/compiler/rustc_errors/src/decorate_diag.rs @@ -1,3 +1,5 @@ +use std::any::Any; + /// This module provides types and traits for buffering lints until later in compilation. use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::FxIndexMap; @@ -10,9 +12,11 @@ use crate::{Diag, DiagCtxtHandle, Diagnostic, Level}; /// We can't implement `Diagnostic` for `BuiltinLintDiag`, because decorating some of its /// variants requires types we don't have yet. So, handle that case separately. pub enum DecorateDiagCompat { + /// The third argument of the closure is a `Session`. However, due to the dependency tree, + /// we don't have access to `rustc_session` here, so we downcast it when needed. Dynamic( Box< - dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()> + DynSync + DynSend + 'static, @@ -30,7 +34,7 @@ impl std::fmt::Debug for DecorateDiagCompat { impl Diagnostic<'a, ()> + DynSync + DynSend + 'static> From for DecorateDiagCompat { #[inline] fn from(d: D) -> Self { - Self::Dynamic(Box::new(|dcx, level| d.into_diag(dcx, level))) + Self::Dynamic(Box::new(|dcx, level, _| d.into_diag(dcx, level))) } } @@ -97,6 +101,26 @@ impl LintBuffer { node_id: NodeId, span: impl Into, callback: F, + ) { + self.add_early_lint(BufferedEarlyLint { + lint_id: LintId::of(lint), + node_id, + span: Some(span.into()), + diagnostic: DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))), + }); + } + + pub fn dyn_buffer_lint_any< + F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()> + + DynSend + + DynSync + + 'static, + >( + &mut self, + lint: &'static Lint, + node_id: NodeId, + span: impl Into, + callback: F, ) { self.add_early_lint(BufferedEarlyLint { lint_id: LintId::of(lint), diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 9525a45d55f1b..7acf95d77d40e 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -7,7 +7,6 @@ use std::panic; use std::path::PathBuf; use std::thread::panicking; -use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg}; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_macros::{Decodable, Encodable}; @@ -119,16 +118,6 @@ where } } -impl<'a> Diagnostic<'a, ()> - for Box< - dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSync + DynSend + 'static, - > -{ - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { - self(dcx, level) - } -} - /// Type used to emit diagnostic through a closure instead of implementing the `Diagnostic` trait. pub struct DiagDecorator)>(pub F); diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 7d9c2e8327b91..635185be1cb76 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -15,9 +15,9 @@ use rustc_session::lint::LintPass; use rustc_span::{DUMMY_SP, Ident, Span}; use tracing::debug; -use crate::DecorateBuiltinLint; use crate::context::{EarlyContext, LintContext, LintStore}; use crate::passes::{EarlyLintPass, EarlyLintPassObject}; +use crate::{DecorateBuiltinLint, DiagAndSess}; pub(super) mod diagnostics; @@ -49,8 +49,12 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> { }, ); } - DecorateDiagCompat::Dynamic(d) => { - self.context.opt_span_lint(lint_id.lint, span, d); + DecorateDiagCompat::Dynamic(callback) => { + self.context.opt_span_lint( + lint_id.lint, + span, + DiagAndSess { callback, sess: self.context.sess() }, + ); } } } diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 19dc1c40e6d34..aee16526dfc0b 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -1,5 +1,7 @@ +use std::any::Any; use std::borrow::Cow; +use rustc_data_structures::sync::DynSend; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level, elided_lifetime_in_path_suggestion, @@ -13,6 +15,19 @@ use crate::lints; mod check_cfg; +pub struct DiagAndSess<'sess> { + pub callback: Box< + dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + 'static, + >, + pub sess: &'sess Session, +} + +impl<'a> Diagnostic<'a, ()> for DiagAndSess<'_> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + (self.callback)(dcx, level, self.sess) + } +} + /// This is a diagnostic struct that will decorate a `BuiltinLintDiag` /// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed. pub struct DecorateBuiltinLint<'sess, 'tcx> { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 74f258b42b001..9e0f04e82b472 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -509,22 +509,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; } - let (replacement, applicability) = - match self.tcx.sess.source_map().span_to_snippet(root_span) { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) - } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - self.lint_buffer.dyn_buffer_lint( + self.lint_buffer.dyn_buffer_lint_any( ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, node_id, root_span, - move |dcx, level| { + move |dcx, level, sess| { + let (replacement, applicability) = match sess + .downcast_ref::() + .expect("expected a `Session`") + .source_map() + .span_to_snippet(root_span) + { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; errors::AbsPathWithModule { sugg: errors::AbsPathWithModuleSugg { span: root_span, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 3128e67724ca8..5bd7ea502dbad 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3641,49 +3641,48 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { match use_set { Some(LifetimeUseSet::Many) => {} Some(LifetimeUseSet::One { use_span, use_ctxt }) => { - debug!(?param.ident, ?param.ident.span, ?use_span); - - let elidable = matches!(use_ctxt, LifetimeCtxt::Ref); + let param_ident = param.ident; let deletion_span = if param.bounds.is_empty() { deletion_span() } else { None }; - - let tcx = self.r.tcx; - let param_ident = param.ident; - let sugg_data = deletion_span.map(|deletion_span| { - if elidable { - let use_span = - tcx.sess.source_map().span_extend_while_whitespace(use_span); - (deletion_span, use_span, String::new()) - } else { - (deletion_span, use_span, "'_".to_owned()) - } - }); - self.r.lint_buffer.dyn_buffer_lint( + self.r.lint_buffer.dyn_buffer_lint_any( lint::builtin::SINGLE_USE_LIFETIMES, param.id, - param.ident.span, - move |dcx, level| { - let suggestion = - sugg_data.map(|(deletion_span, use_span, replace_lt)| { - // issue 107998 for the case such as a wrong function pointer type - // `deletion_span` is empty and there is no need to report lifetime uses here - let deletion_span = if deletion_span.is_empty() { - None - } else { - Some(deletion_span) - }; - errors::SingleUseLifetimeSugg { - deletion_span, - use_span, - replace_lt, - } - }); - let param_span = param_ident.span; - debug!(?param_span, ?use_span, ?deletion_span); - + param_ident.span, + move |dcx, level, sess| { + debug!(?param_ident, ?param_ident.span, ?use_span); + + let elidable = matches!(use_ctxt, LifetimeCtxt::Ref); + let suggestion = if let Some(deletion_span) = deletion_span { + let (use_span, replace_lt) = if elidable { + let use_span = sess + .downcast_ref::() + .expect("expected a `Session`") + .source_map() + .span_extend_while_whitespace(use_span); + (use_span, String::new()) + } else { + (use_span, "'_".to_owned()) + }; + debug!(?deletion_span, ?use_span); + + // issue 107998 for the case such as a wrong function pointer type + // `deletion_span` is empty and there is no need to report lifetime uses here + let deletion_span = if deletion_span.is_empty() { + None + } else { + Some(deletion_span) + }; + Some(errors::SingleUseLifetimeSugg { + deletion_span, + use_span, + replace_lt, + }) + } else { + None + }; errors::SingleUseLifetime { suggestion, - param_span, + param_span: param_ident.span, use_span, ident: param_ident, } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 65a15dba42873..8251050b6aead 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -344,7 +344,7 @@ impl ParseSess { lint, Some(span.into()), node_id, - DecorateDiagCompat::Dynamic(Box::new(callback)), + DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))), ) } From df848e78d9b2ef68b08d46aa0aea440df92d530a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 3 Apr 2026 21:48:20 +0200 Subject: [PATCH 25/25] Fix merge conflict --- compiler/rustc_lint/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index e4106cbdfb797..c76fafcde2ee1 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -129,7 +129,7 @@ use unused::*; #[rustfmt::skip] pub use builtin::{MissingDoc, SoftLints}; pub use context::{EarlyContext, LateContext, LintContext, LintStore}; -pub use early::diagnostics::{DecorateAttrLint, DecorateBuiltinLint}; +pub use early::diagnostics::{DecorateAttrLint, DecorateBuiltinLint, DiagAndSess}; pub use early::{EarlyCheckNode, check_ast_node}; pub use late::{check_crate, late_lint_mod, unerased_lint_store}; pub use levels::LintLevelsBuilder;