From 5b01968b730028459b462d67c183c3886df0d2a9 Mon Sep 17 00:00:00 2001 From: AayushMainali-Github Date: Wed, 29 Jul 2026 14:25:30 +0000 Subject: [PATCH] resolve: suggest usize for array-length consts --- .../rustc_resolve/src/diagnostics/impls.rs | 2 + compiler/rustc_resolve/src/diagnostics/mod.rs | 3 +- compiler/rustc_resolve/src/ident.rs | 8 +- compiler/rustc_resolve/src/late.rs | 93 ++++++++++++++----- compiler/rustc_resolve/src/lib.rs | 2 + .../ui/consts/non-const-value-in-const.stderr | 2 +- .../consts/suggest-usize-for-array-length.rs | 10 ++ .../suggest-usize-for-array-length.stderr | 27 ++++++ .../parser/recover/array-type-no-semi.stderr | 2 +- tests/ui/repeat-expr/repeat_count.stderr | 2 +- 10 files changed, 122 insertions(+), 29 deletions(-) create mode 100644 tests/ui/consts/suggest-usize-for-array-length.rs create mode 100644 tests/ui/consts/suggest-usize-for-array-length.stderr diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 5ff31f5d90834..1c8f573913eb1 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1175,6 +1175,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestion, current, type_span, + type_name, } => { // let foo =... // ^^^ given this Span @@ -1216,6 +1217,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestion, current, type_span, + type_name, }), Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), None, diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index cadfab22c8862..c039d77b768d0 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -298,8 +298,9 @@ pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { #[suggestion_part(code = "{suggestion} ")] pub(crate) span: Span, pub(crate) suggestion: &'a str, - #[suggestion_part(code = ": /* Type */")] + #[suggestion_part(code = ": {type_name}")] pub(crate) type_span: Option, + pub(crate) type_name: &'a str, pub(crate) current: &'a str, } diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 1c94779a6009a..90cad8dd6efd3 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1511,7 +1511,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { res_err = Some((span, CannotCaptureDynamicEnvironmentInFnItem)); } } - RibKind::ConstantItem(_, item) => { + RibKind::ConstantItem(_, item, type_suggestion) => { // Still doesn't deal with upvars if let Some(span) = finalize { let (span, resolution_error) = match item { @@ -1540,6 +1540,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestion: "const", current: "let", type_span, + type_name: type_suggestion.as_str(), }, ) } @@ -1550,6 +1551,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestion: "let", current: kind.as_str(), type_span: None, + type_name: type_suggestion.as_str(), }, ), }; @@ -1610,7 +1612,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - RibKind::ConstantItem(trivial, _) => { + RibKind::ConstantItem(trivial, _, _) => { if let ConstantHasGenerics::No(cause) = trivial && !matches!(res, Res::SelfTyAlias { .. }) { @@ -1704,7 +1706,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - RibKind::ConstantItem(trivial, _) => { + RibKind::ConstantItem(trivial, _, _) => { if let ConstantHasGenerics::No(cause) = trivial { if let Some(span) = finalize { let error = match cause { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 535d11d00d718..8eceede045dbd 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -78,9 +78,30 @@ enum AnonConstKind { EnumDiscriminant, FieldDefaultValue, InlineConst, + /// Array type length, e.g. `[T; N]`. + ArrayLength, ConstArg(IsRepeatExpr), } +/// Type ascription to suggest when turning a `let` binding into a `const` +/// because it was used in a constant context without an existing type annotation. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum NonConstantTypeSuggestion { + /// Suggest `: /* Type */`. + Placeholder, + /// Suggest `: usize` (array lengths and repeat counts). + Usize, +} + +impl NonConstantTypeSuggestion { + pub(crate) fn as_str(self) -> &'static str { + match self { + Self::Placeholder => "/* Type */", + Self::Usize => "usize", + } + } +} + impl PatternSource { fn descr(self) -> &'static str { match self { @@ -214,7 +235,10 @@ pub(crate) enum RibKind<'ra> { /// /// The item may reference generic parameters in trivial constant expressions. /// All other constants aren't allowed to use generic params at all. - ConstantItem(ConstantHasGenerics, Option<(Ident, ConstantItemKind)>), + /// + /// The third field is used when suggesting that a `let` binding used in this + /// constant context should become a `const`, to pick a sensible type ascription. + ConstantItem(ConstantHasGenerics, Option<(Ident, ConstantItemKind)>, NonConstantTypeSuggestion), /// We passed through a module item. Module(LocalModule<'ra>), @@ -1038,7 +1062,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc } TyKind::Array(element_ty, length) => { self.visit_ty(element_ty); - self.resolve_anon_const(length, AnonConstKind::ConstArg(IsRepeatExpr::No)); + self.resolve_anon_const(length, AnonConstKind::ArrayLength); } _ => visit::walk_ty(self, ty), } @@ -3021,6 +3045,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { IsRepeatExpr::No, ConstantHasGenerics::Yes, Some((ConstBlockItem::IDENT, ConstantItemKind::Const)), + NonConstantTypeSuggestion::Placeholder, |this| this.resolve_labeled_block(None, block.id, block), ) }, @@ -3295,21 +3320,30 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { is_repeat: IsRepeatExpr, may_use_generics: ConstantHasGenerics, item: Option<(Ident, ConstantItemKind)>, + type_suggestion: NonConstantTypeSuggestion, f: impl FnOnce(&mut Self), ) { let f = |this: &mut Self| { - this.with_rib(ValueNS, RibKind::ConstantItem(may_use_generics, item), |this| { - this.with_rib( - TypeNS, - RibKind::ConstantItem( - may_use_generics.force_yes_if(is_repeat == IsRepeatExpr::Yes), - item, - ), - |this| { - this.with_label_rib(RibKind::ConstantItem(may_use_generics, item), f); - }, - ) - }) + this.with_rib( + ValueNS, + RibKind::ConstantItem(may_use_generics, item, type_suggestion), + |this| { + this.with_rib( + TypeNS, + RibKind::ConstantItem( + may_use_generics.force_yes_if(is_repeat == IsRepeatExpr::Yes), + item, + type_suggestion, + ), + |this| { + this.with_label_rib( + RibKind::ConstantItem(may_use_generics, item, type_suggestion), + f, + ); + }, + ) + }, + ) }; if let ConstantHasGenerics::No(cause) = may_use_generics { @@ -3864,9 +3898,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { fn resolve_static_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) { self.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { - this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| { - this.visit_expr(expr) - }); + this.with_constant_rib( + IsRepeatExpr::No, + ConstantHasGenerics::Yes, + item, + NonConstantTypeSuggestion::Placeholder, + |this| this.visit_expr(expr), + ); }) } @@ -3877,9 +3915,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ) { if let Some(body) = body { self.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { - this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| { - this.visit_expr(body) - }) + this.with_constant_rib( + IsRepeatExpr::No, + ConstantHasGenerics::Yes, + item, + NonConstantTypeSuggestion::Placeholder, + |this| this.visit_expr(body), + ) }) } } @@ -5152,13 +5194,20 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { _ => IsRepeatExpr::No, }; + let type_suggestion = match anon_const_kind { + AnonConstKind::ArrayLength | AnonConstKind::ConstArg(IsRepeatExpr::Yes) => { + NonConstantTypeSuggestion::Usize + } + _ => NonConstantTypeSuggestion::Placeholder, + }; + let may_use_generics = match anon_const_kind { AnonConstKind::EnumDiscriminant => { ConstantHasGenerics::No(NoConstantGenericsReason::IsEnumDiscriminant) } AnonConstKind::FieldDefaultValue => ConstantHasGenerics::Yes, AnonConstKind::InlineConst => ConstantHasGenerics::Yes, - AnonConstKind::ConstArg(_) => { + AnonConstKind::ArrayLength | AnonConstKind::ConstArg(_) => { if self.r.features.generic_const_exprs() || self.r.features.min_generic_const_args() || is_trivial_const_arg @@ -5170,7 +5219,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } }; - self.with_constant_rib(is_repeat_expr, may_use_generics, None, |this| { + self.with_constant_rib(is_repeat_expr, may_use_generics, None, type_suggestion, |this| { this.with_lifetime_rib(LifetimeRibKind::elided(LifetimeRes::Infer), |this| { resolve_expr(this); }); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2dbf32dc20288..ef11292572422 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -282,6 +282,8 @@ enum ResolutionError<'ra> { suggestion: &'static str, current: &'static str, type_span: Option, + /// Type ascription to suggest when `type_span` is present (e.g. `usize` or `/* Type */`). + type_name: &'static str, }, /// Error E0530: `X` bindings cannot shadow `Y`s. BindingShadowsSomethingUnacceptable { diff --git a/tests/ui/consts/non-const-value-in-const.stderr b/tests/ui/consts/non-const-value-in-const.stderr index 201c310843b38..67478a4f84727 100644 --- a/tests/ui/consts/non-const-value-in-const.stderr +++ b/tests/ui/consts/non-const-value-in-const.stderr @@ -19,7 +19,7 @@ LL | let _ = [0; x]; help: consider using `const` instead of `let` | LL - let x = 5; -LL + const x: /* Type */ = 5; +LL + const x: usize = 5; | error: aborting due to 2 previous errors diff --git a/tests/ui/consts/suggest-usize-for-array-length.rs b/tests/ui/consts/suggest-usize-for-array-length.rs new file mode 100644 index 0000000000000..97b401862b1ed --- /dev/null +++ b/tests/ui/consts/suggest-usize-for-array-length.rs @@ -0,0 +1,10 @@ +//! Regression test for #159487: when suggesting `const` for a value used as an +//! array length or repeat count, prefer `usize` over `/* Type */`. + +fn main() { + let length = 3; + let values: [i32; length] = [0; length]; + //~^ ERROR attempt to use a non-constant value in a constant + //~| ERROR attempt to use a non-constant value in a constant + println!("{}", values.len()); +} diff --git a/tests/ui/consts/suggest-usize-for-array-length.stderr b/tests/ui/consts/suggest-usize-for-array-length.stderr new file mode 100644 index 0000000000000..0d8d6ef7c8b9c --- /dev/null +++ b/tests/ui/consts/suggest-usize-for-array-length.stderr @@ -0,0 +1,27 @@ +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/suggest-usize-for-array-length.rs:6:23 + | +LL | let values: [i32; length] = [0; length]; + | ^^^^^^ non-constant value + | +help: consider using `const` instead of `let` + | +LL - let length = 3; +LL + const length: usize = 3; + | + +error[E0435]: attempt to use a non-constant value in a constant + --> $DIR/suggest-usize-for-array-length.rs:6:37 + | +LL | let values: [i32; length] = [0; length]; + | ^^^^^^ non-constant value + | +help: consider using `const` instead of `let` + | +LL - let length = 3; +LL + const length: usize = 3; + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0435`. diff --git a/tests/ui/parser/recover/array-type-no-semi.stderr b/tests/ui/parser/recover/array-type-no-semi.stderr index 0af085140223e..01fcc3766f635 100644 --- a/tests/ui/parser/recover/array-type-no-semi.stderr +++ b/tests/ui/parser/recover/array-type-no-semi.stderr @@ -68,7 +68,7 @@ LL | let c: [i32, x]; help: consider using `const` instead of `let` | LL - let x = 5; -LL + const x: /* Type */ = 5; +LL + const x: usize = 5; | error[E0423]: cannot find value `i32` in this scope diff --git a/tests/ui/repeat-expr/repeat_count.stderr b/tests/ui/repeat-expr/repeat_count.stderr index e2cecf9973b8b..91d6a5d79d0ac 100644 --- a/tests/ui/repeat-expr/repeat_count.stderr +++ b/tests/ui/repeat-expr/repeat_count.stderr @@ -7,7 +7,7 @@ LL | let a = [0; n]; help: consider using `const` instead of `let` | LL - let n = 1; -LL + const n: /* Type */ = 1; +LL + const n: usize = 1; | error[E0308]: mismatched types