From 4882893863fabec457aeea337eb654d479c96cca Mon Sep 17 00:00:00 2001 From: IsaiahCoroama Date: Mon, 13 Jul 2026 21:07:56 -0500 Subject: [PATCH] Fix where-bound suggestion for legacy const generics Point the suggestion at the const argument's span instead of the whole obligation, and parenthesize the snippet when the const expression needs it (e.g. `(N + 1) as usize`) so the suggested bound parses and compiles. Also add regression tests for legacy const generic where-bound suggestions. --- compiler/rustc_ast_lowering/src/expr.rs | 2 +- .../traits/fulfillment_errors.rs | 20 ++- .../auxiliary/legacy_const_generics_bounds.rs | 32 +++++ ...gacy-const-generics-bound-suggestion.fixed | 86 +++++++++++ .../legacy-const-generics-bound-suggestion.rs | 86 +++++++++++ ...acy-const-generics-bound-suggestion.stderr | 134 ++++++++++++++++++ 6 files changed, 355 insertions(+), 5 deletions(-) create mode 100644 tests/ui/const-generics/generic_const_exprs/auxiliary/legacy_const_generics_bounds.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.fixed create mode 100644 tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 4ed23e032d234..312722b680bff 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -597,7 +597,7 @@ impl<'hir> LoweringContext<'_, 'hir> { for (idx, arg) in args.iter().cloned().enumerate() { if legacy_args_idx.contains(&idx) { let node_id = self.next_node_id(); - self.create_def(node_id, None, DefKind::AnonConst, f.span); + self.create_def(node_id, None, DefKind::AnonConst, arg.span); let const_value = if let ControlFlow::Break(span) = WillCreateDefIdsVisitor.visit_expr(&arg) { Box::new(Expr { 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 b38c933151eb2..c7791b9deee29 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 @@ -16,7 +16,7 @@ use rustc_errors::{ use rustc_hir::attrs::diagnostic::CustomDiagnostic; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, LangItem, Node, find_attr}; +use rustc_hir::{self as hir, LangItem, Node, expr_needs_parens, find_attr}; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_infer::traits::ImplSource; use rustc_infer::traits::solve::Goal; @@ -3798,13 +3798,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::ConstKind::Alias(_, alias_const) => { let mut err = self.dcx().struct_span_err(span, "unconstrained generic constant"); - let const_span = alias_const.kind.def_span(self.tcx); + let const_span = alias_const.kind.def_span(self.tcx); let const_ty = alias_const.type_of(self.tcx).skip_norm_wip(); - let cast = if const_ty != self.tcx.types.usize { " as usize" } else { "" }; + let msg = "try adding a `where` bound"; if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(const_span) { - let code = format!("[(); {snippet}{cast}]:"); + let code = if const_ty == self.tcx.types.usize { + format!("[(); {snippet}]:") + } else if let ty::AliasConstKind::Anon { def_id } = alias_const.kind + && let Some(local_def_id) = def_id.as_local() + && let Some(local_body) = self.tcx.hir_maybe_body_owned_by(local_def_id) + && expr_needs_parens(local_body.value) + { + format!("[(); ({snippet}) as usize]:") + } else { + format!("[(); {snippet} as usize]:") + }; + let suggestion_def_id = if let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. @@ -3814,6 +3825,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } else { Some(obligation.cause.body_def_id) }; + if let Some(suggestion_def_id) = suggestion_def_id && let Some(generics) = self.tcx.hir_get_generics(suggestion_def_id) { diff --git a/tests/ui/const-generics/generic_const_exprs/auxiliary/legacy_const_generics_bounds.rs b/tests/ui/const-generics/generic_const_exprs/auxiliary/legacy_const_generics_bounds.rs new file mode 100644 index 0000000000000..821ed040e1dbc --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/auxiliary/legacy_const_generics_bounds.rs @@ -0,0 +1,32 @@ +#![allow(internal_features)] +#![feature(rustc_attrs)] + +// Signed Primitive Integers + +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_i8() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_i16() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_i32() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_i64() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_i128() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_isize() {} + +// Unsigned Primitive Integers + +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_u8() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_u16() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_u32() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_u64() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_u128() {} +#[rustc_legacy_const_generics(0)] +pub fn arg0_as_usize() {} diff --git a/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.fixed b/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.fixed new file mode 100644 index 0000000000000..835b0de582922 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.fixed @@ -0,0 +1,86 @@ +//@ run-rustfix +//@ aux-crate: legacy_const_generics_bounds=legacy_const_generics_bounds.rs +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +// Signed Primitive Integers + +fn invoke_arg0_as_i8() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i8(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i16() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i16(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i32() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i32(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i64() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i64(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i128() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i128(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_isize() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_isize(N + 1); + //~^ ERROR unconstrained generic constant +} + +// Unsigned Primitive Integers + +fn invoke_arg0_as_u8() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u8(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u16() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u16(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u32() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u32(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u64() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u64(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u128() where [(); (N + 1) as usize]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u128(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_usize() where [(); N + 1]: { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_usize(N + 1); + //~^ ERROR unconstrained generic constant +} + +fn main() { + invoke_arg0_as_i8::<0>(); + invoke_arg0_as_i16::<0>(); + invoke_arg0_as_i32::<0>(); + invoke_arg0_as_i64::<0>(); + invoke_arg0_as_i128::<0>(); + invoke_arg0_as_isize::<0>(); + + invoke_arg0_as_u8::<0>(); + invoke_arg0_as_u16::<0>(); + invoke_arg0_as_u32::<0>(); + invoke_arg0_as_u64::<0>(); + invoke_arg0_as_u128::<0>(); + invoke_arg0_as_usize::<0>(); +} diff --git a/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.rs b/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.rs new file mode 100644 index 0000000000000..485a24783c8cd --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.rs @@ -0,0 +1,86 @@ +//@ run-rustfix +//@ aux-crate: legacy_const_generics_bounds=legacy_const_generics_bounds.rs +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +// Signed Primitive Integers + +fn invoke_arg0_as_i8() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i8(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i16() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i16(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i32() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i32(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i64() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i64(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_i128() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_i128(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_isize() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_isize(N + 1); + //~^ ERROR unconstrained generic constant +} + +// Unsigned Primitive Integers + +fn invoke_arg0_as_u8() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u8(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u16() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u16(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u32() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u32(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u64() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u64(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_u128() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_u128(N + 1); + //~^ ERROR unconstrained generic constant +} +fn invoke_arg0_as_usize() { + //~^ HELP try adding a `where` bound + legacy_const_generics_bounds::arg0_as_usize(N + 1); + //~^ ERROR unconstrained generic constant +} + +fn main() { + invoke_arg0_as_i8::<0>(); + invoke_arg0_as_i16::<0>(); + invoke_arg0_as_i32::<0>(); + invoke_arg0_as_i64::<0>(); + invoke_arg0_as_i128::<0>(); + invoke_arg0_as_isize::<0>(); + + invoke_arg0_as_u8::<0>(); + invoke_arg0_as_u16::<0>(); + invoke_arg0_as_u32::<0>(); + invoke_arg0_as_u64::<0>(); + invoke_arg0_as_u128::<0>(); + invoke_arg0_as_usize::<0>(); +} diff --git a/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.stderr b/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.stderr new file mode 100644 index 0000000000000..2c3b641540efb --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/legacy-const-generics-bound-suggestion.stderr @@ -0,0 +1,134 @@ +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:10:46 + | +LL | legacy_const_generics_bounds::arg0_as_i8(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_i8() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:15:47 + | +LL | legacy_const_generics_bounds::arg0_as_i16(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_i16() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:20:47 + | +LL | legacy_const_generics_bounds::arg0_as_i32(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_i32() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:25:47 + | +LL | legacy_const_generics_bounds::arg0_as_i64(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_i64() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:30:48 + | +LL | legacy_const_generics_bounds::arg0_as_i128(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_i128() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:35:49 + | +LL | legacy_const_generics_bounds::arg0_as_isize(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_isize() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:43:46 + | +LL | legacy_const_generics_bounds::arg0_as_u8(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_u8() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:48:47 + | +LL | legacy_const_generics_bounds::arg0_as_u16(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_u16() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:53:47 + | +LL | legacy_const_generics_bounds::arg0_as_u32(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_u32() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:58:47 + | +LL | legacy_const_generics_bounds::arg0_as_u64(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_u64() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:63:48 + | +LL | legacy_const_generics_bounds::arg0_as_u128(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_u128() where [(); (N + 1) as usize]: { + | +++++++++++++++++++++++++++++ + +error: unconstrained generic constant + --> $DIR/legacy-const-generics-bound-suggestion.rs:68:49 + | +LL | legacy_const_generics_bounds::arg0_as_usize(N + 1); + | ^^^^^ + | +help: try adding a `where` bound + | +LL | fn invoke_arg0_as_usize() where [(); N + 1]: { + | ++++++++++++++++++ + +error: aborting due to 12 previous errors +