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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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/19] 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 ae899cc4e2bf91e553ddb369b83deb6425500ff4 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 2 Apr 2026 17:57:33 +0800 Subject: [PATCH 10/19] 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 11/19] 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 12/19] 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 13/19] 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 14/19] 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 15/19] 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 16/19] 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 2bf76b4388e95f335abad1cf3fef6d9351bde48e Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Fri, 3 Apr 2026 17:06:50 +0100 Subject: [PATCH 17/19] 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 18/19] 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 19/19] 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() {}