From e842e2dc6c394c969fba58d2c94cb0ebc8f99d0f Mon Sep 17 00:00:00 2001 From: lapla Date: Thu, 23 Apr 2026 03:41:37 +0900 Subject: [PATCH 1/5] Register `ConstArgHasType` obligation when normalizing trait projection consts --- .../src/solve/normalizes_to/mod.rs | 16 ++++++++-- .../src/traits/project.rs | 14 +++++++- ...e-const-value-type-mismatch.current.stderr | 32 +++++++++++++++++++ ...type-const-value-type-mismatch.next.stderr | 30 +++++++++++++++++ .../mgca/type-const-value-type-mismatch.rs | 29 +++++++++++++++++ 5 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 tests/ui/const-generics/mgca/type-const-value-type-mismatch.current.stderr create mode 100644 tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr create mode 100644 tests/ui/const-generics/mgca/type-const-value-type-mismatch.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index e9a4d7e5919ad..7862f7c6a4d6b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -409,10 +409,22 @@ where ty::AliasTermKind::ProjectionConst { .. } if cx.is_type_const(target_item_def_id.into()) => { - cx.const_of_item(target_item_def_id.into()) + let term: I::Term = cx + .const_of_item(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() - .into() + .into(); + if let Some(ct) = term.as_const() { + let expected_ty = cx + .type_of(target_item_def_id.into()) + .instantiate(cx, target_args) + .skip_norm_wip(); + ecx.add_goal( + GoalSource::Misc, + goal.with(cx, ty::ClauseKind::ConstArgHasType(ct, expected_ty)), + ); + } + term } ty::AliasTermKind::ProjectionConst { .. } => { let uv = ty::UnevaluatedConst::new( diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e2458a2392f5f..39a03a4c1ff2e 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2046,7 +2046,19 @@ fn confirm_impl_candidate<'cx, 'tcx>( Progress { term: err, obligations: nested } } else { assoc_term_own_obligations(selcx, obligation, &mut nested); - Progress { term: term.instantiate(tcx, args).skip_norm_wip(), obligations: nested } + let instantiated_term: Term<'tcx> = term.instantiate(tcx, args).skip_norm_wip(); + if let Some(ct) = instantiated_term.as_const() { + let expected_ty = + tcx.type_of(assoc_term.item.def_id).instantiate(tcx, args).skip_norm_wip(); + nested.push(Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + ty::ClauseKind::ConstArgHasType(ct, expected_ty), + )); + } + Progress { term: instantiated_term, obligations: nested } }; Ok(Projected::Progress(progress)) } diff --git a/tests/ui/const-generics/mgca/type-const-value-type-mismatch.current.stderr b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.current.stderr new file mode 100644 index 0000000000000..7ce0513c91d13 --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.current.stderr @@ -0,0 +1,32 @@ +error[E0053]: method `arr` has an incompatible type for trait + --> $DIR/type-const-value-type-mismatch.rs:22:5 + | +LL | fn arr() -> [u8; const { Self::LEN }] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a size of 0, found one with a size of const { Self::LEN } + | +note: type in trait + --> $DIR/type-const-value-type-mismatch.rs:15:5 + | +LL | fn arr() -> [u8; Self::LEN]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: expected signature `fn() -> [u8; 0]` + found signature `fn() -> [u8; const { Self::LEN }]` + +error: the constant `0` is not of type `usize` + --> $DIR/type-const-value-type-mismatch.rs:19:5 + | +LL | type const LEN: usize = 0u8; + | ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `u8` + +error[E0308]: mismatched types + --> $DIR/type-const-value-type-mismatch.rs:22:17 + | +LL | fn arr() -> [u8; const { Self::LEN }] {} + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `[u8; const { Self::LEN }]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0053, E0308. +For more information about an error, try `rustc --explain E0053`. diff --git a/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr new file mode 100644 index 0000000000000..77885cbcfecde --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr @@ -0,0 +1,30 @@ +error[E0271]: type mismatch resolving `::LEN normalizes-to 0` + --> $DIR/type-const-value-type-mismatch.rs:22:5 + | +LL | fn arr() -> [u8; const { Self::LEN }] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + +error: the constant `0` is not of type `usize` + --> $DIR/type-const-value-type-mismatch.rs:19:5 + | +LL | type const LEN: usize = 0u8; + | ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `u8` + +error[E0284]: type annotations needed: cannot normalize `::arr::{constant#0}` + --> $DIR/type-const-value-type-mismatch.rs:22:17 + | +LL | fn arr() -> [u8; const { Self::LEN }] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `::arr::{constant#0}` + +error[E0308]: mismatched types + --> $DIR/type-const-value-type-mismatch.rs:22:17 + | +LL | fn arr() -> [u8; const { Self::LEN }] {} + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `[u8; _]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0271, E0284, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/mgca/type-const-value-type-mismatch.rs b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.rs new file mode 100644 index 0000000000000..273e7e340fef1 --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.rs @@ -0,0 +1,29 @@ +// Regression test for https://github.com/rust-lang/rust/issues/152962 + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ compile-flags: -Zvalidate-mir + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +pub struct A; + +pub trait Array { + type const LEN: usize; + fn arr() -> [u8; Self::LEN]; +} + +impl Array for A { + type const LEN: usize = 0u8; + //~^ ERROR the constant `0` is not of type `usize` + + fn arr() -> [u8; const { Self::LEN }] {} + //~^ ERROR mismatched types [E0308] + //[current]~| ERROR method `arr` has an incompatible type for trait [E0053] + //[next]~| ERROR type annotations needed + //[next]~| ERROR type mismatch resolving `::LEN normalizes-to 0` [E0271] +} + +fn main() {} From 06b66a91f2e8caffa1a3f55dc6fdda58c8ffab45 Mon Sep 17 00:00:00 2001 From: lapla Date: Thu, 23 Apr 2026 03:41:53 +0900 Subject: [PATCH 2/5] Register `ConstArgHasType` obligation when normalizing inherent projection consts --- .../src/solve/normalizes_to/inherent.rs | 13 +++++++++- .../src/traits/project.rs | 11 +++++++++ ...nherent-value-type-mismatch.current.stderr | 17 +++++++++++++ ...t-inherent-value-type-mismatch.next.stderr | 24 +++++++++++++++++++ ...type-const-inherent-value-type-mismatch.rs | 22 +++++++++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.current.stderr create mode 100644 tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr create mode 100644 tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 2f44cc42a5d71..2f89802503767 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -5,6 +5,7 @@ //! 2. equate the self type, and //! 3. instantiate and register where clauses. +use rustc_type_ir::inherent::*; use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, Unnormalized}; @@ -54,7 +55,7 @@ where .map(|pred| goal.with(cx, pred)), ); - let normalized = match inherent.kind(cx) { + let normalized: I::Term = match inherent.kind(cx) { ty::AliasTermKind::InherentTy { def_id } => { cx.type_of(def_id.into()).instantiate(cx, inherent_args).skip_norm_wip().into() } @@ -74,6 +75,16 @@ where } kind => panic!("expected inherent alias, found {kind:?}"), }; + + if let Some(ct) = normalized.as_const() { + let expected_ty = + cx.type_of(inherent.def_id()).instantiate(cx, inherent_args).skip_norm_wip(); + self.add_goal( + GoalSource::Misc, + goal.with(cx, ty::ClauseKind::ConstArgHasType(ct, expected_ty)), + ); + } + self.instantiate_normalizes_to_term(goal, normalized); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 39a03a4c1ff2e..6e233497991f0 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -551,6 +551,17 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( tcx.const_of_item(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip().into() }; + if let Some(ct) = term.as_const() { + let expected_ty = tcx.type_of(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip(); + obligations.push(Obligation::with_depth( + tcx, + cause.clone(), + depth + 1, + param_env, + ty::ClauseKind::ConstArgHasType(ct, expected_ty), + )); + } + let mut term = selcx.infcx.resolve_vars_if_possible(term); if term.has_aliases() { term = diff --git a/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.current.stderr b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.current.stderr new file mode 100644 index 0000000000000..bf34bcaf9fc9c --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.current.stderr @@ -0,0 +1,17 @@ +error: the constant `"this isn't a usize"` is not of type `usize` + --> $DIR/type-const-inherent-value-type-mismatch.rs:14:5 + | +LL | type const N: usize = "this isn't a usize"; + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&'static str` + +error[E0308]: mismatched types + --> $DIR/type-const-inherent-value-type-mismatch.rs:18:11 + | +LL | fn f() -> [u8; const { Struct::N }] {} + | - ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `[u8; const { Struct::N }]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr new file mode 100644 index 0000000000000..5d0b81f4a3e0d --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr @@ -0,0 +1,24 @@ +error[E0284]: type annotations needed: cannot normalize `f::{constant#0}` + --> $DIR/type-const-inherent-value-type-mismatch.rs:18:11 + | +LL | fn f() -> [u8; const { Struct::N }] {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `f::{constant#0}` + +error: the constant `"this isn't a usize"` is not of type `usize` + --> $DIR/type-const-inherent-value-type-mismatch.rs:14:5 + | +LL | type const N: usize = "this isn't a usize"; + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&'static str` + +error[E0308]: mismatched types + --> $DIR/type-const-inherent-value-type-mismatch.rs:18:11 + | +LL | fn f() -> [u8; const { Struct::N }] {} + | - ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `[u8; _]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0284, E0308. +For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.rs b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.rs new file mode 100644 index 0000000000000..1f00b4410c4d7 --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.rs @@ -0,0 +1,22 @@ +// Regression test for https://github.com/rust-lang/rust/issues/152962 + +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ compile-flags: -Zvalidate-mir + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +struct Struct; + +impl Struct { + type const N: usize = "this isn't a usize"; + //~^ ERROR the constant `"this isn't a usize"` is not of type `usize` +} + +fn f() -> [u8; const { Struct::N }] {} +//~^ ERROR mismatched types [E0308] +//[next]~| ERROR type annotations needed + +fn main() {} From 4dc5162b1b1ce52b33fc0a6bf7a71883c9f709d1 Mon Sep 17 00:00:00 2001 From: lapla Date: Thu, 23 Apr 2026 03:42:03 +0900 Subject: [PATCH 3/5] Add test that free type const value type mismatch does not ICE --- .../mgca/type-const-free-value-type-mismatch.rs | 13 +++++++++++++ .../type-const-free-value-type-mismatch.stderr | 15 +++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs create mode 100644 tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.stderr diff --git a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs new file mode 100644 index 0000000000000..5e9147b9a7b91 --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs @@ -0,0 +1,13 @@ +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +//@ compile-flags: -Zvalidate-mir +//@ normalize-stderr: "\d+-byte" -> "$$BYTE-byte" + +type const N: usize = "this isn't a usize"; +//~^ ERROR the constant `"this isn't a usize"` is not of type `usize` + +fn f() -> [u8; const { N }] {} +//~^ ERROR transmuting from + +fn main() {} diff --git a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.stderr b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.stderr new file mode 100644 index 0000000000000..20ffb7b335a55 --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.stderr @@ -0,0 +1,15 @@ +error: the constant `"this isn't a usize"` is not of type `usize` + --> $DIR/type-const-free-value-type-mismatch.rs:7:1 + | +LL | type const N: usize = "this isn't a usize"; + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&'static str` + +error[E0080]: transmuting from $BYTE-byte type to $BYTE-byte type: `&str` -> `usize` + --> $DIR/type-const-free-value-type-mismatch.rs:10:24 + | +LL | fn f() -> [u8; const { N }] {} + | ^ evaluation of `f::{constant#0}` failed here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. From ef946ef7cf23e09d500883cdd3ebf1ceb40ae338 Mon Sep 17 00:00:00 2001 From: lapla Date: Thu, 23 Apr 2026 03:42:14 +0900 Subject: [PATCH 4/5] Extract shared helper for registering `ConstArgHasType` during const projection normalization --- .../src/solve/normalizes_to/inherent.rs | 12 +--- .../src/solve/normalizes_to/mod.rs | 40 ++++++----- .../src/traits/project.rs | 66 +++++++++++++------ 3 files changed, 71 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 2f89802503767..f83099b68cad8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -5,7 +5,6 @@ //! 2. equate the self type, and //! 3. instantiate and register where clauses. -use rustc_type_ir::inherent::*; use rustc_type_ir::solve::QueryResultOrRerunNonErased; use rustc_type_ir::{self as ty, Interner, Unnormalized}; @@ -76,16 +75,7 @@ where kind => panic!("expected inherent alias, found {kind:?}"), }; - if let Some(ct) = normalized.as_const() { - let expected_ty = - cx.type_of(inherent.def_id()).instantiate(cx, inherent_args).skip_norm_wip(); - self.add_goal( - GoalSource::Misc, - goal.with(cx, ty::ClauseKind::ConstArgHasType(ct, expected_ty)), - ); - } - - self.instantiate_normalizes_to_term(goal, normalized); + self.instantiate_normalizes_to_term_with_type_check(goal, normalized); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 7862f7c6a4d6b..de16d8070c88f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -130,6 +130,28 @@ where .expect("expected goal term to be fully unconstrained"); } + /// Like `instantiate_normalizes_to_term`, but also registers a + /// `ConstArgHasType` goal when the term is a const. This ensures that + /// the const value's type matches the type of the alias it was + /// normalized from, preventing ICEs from type mismatches. + pub fn instantiate_normalizes_to_term_with_type_check( + &mut self, + goal: Goal>, + term: I::Term, + ) { + if let Some(ct) = term.as_const() { + let cx = self.cx(); + let alias = goal.predicate.alias; + let expected_ty = + cx.type_of(alias.def_id()).instantiate(cx, alias.args).skip_norm_wip(); + self.add_goal( + GoalSource::Misc, + goal.with(cx, ty::ClauseKind::ConstArgHasType(ct, expected_ty)), + ); + } + self.instantiate_normalizes_to_term(goal, term); + } + /// Unlike `instantiate_normalizes_to_term` this instantiates the expected term /// with a rigid alias. Using this is pretty much always wrong. pub fn structurally_instantiate_normalizes_to_term( @@ -409,22 +431,10 @@ where ty::AliasTermKind::ProjectionConst { .. } if cx.is_type_const(target_item_def_id.into()) => { - let term: I::Term = cx - .const_of_item(target_item_def_id.into()) + cx.const_of_item(target_item_def_id.into()) .instantiate(cx, target_args) .skip_norm_wip() - .into(); - if let Some(ct) = term.as_const() { - let expected_ty = cx - .type_of(target_item_def_id.into()) - .instantiate(cx, target_args) - .skip_norm_wip(); - ecx.add_goal( - GoalSource::Misc, - goal.with(cx, ty::ClauseKind::ConstArgHasType(ct, expected_ty)), - ); - } - term + .into() } ty::AliasTermKind::ProjectionConst { .. } => { let uv = ty::UnevaluatedConst::new( @@ -436,7 +446,7 @@ where kind => panic!("expected projection, found {kind:?}"), }; - ecx.instantiate_normalizes_to_term(goal, term); + ecx.instantiate_normalizes_to_term_with_type_check(goal, term); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into) }) } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 6e233497991f0..38501cabe3d15 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -5,6 +5,7 @@ use std::ops::ControlFlow; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::resolve::OpportunisticRegionResolver; @@ -484,6 +485,30 @@ fn normalize_to_error<'a, 'tcx>( Normalized { value: new_value, obligations } } +/// When normalizing a const alias, register a `ConstArgHasType` obligation +/// to ensure the const value's type matches the declared type. +fn push_const_arg_has_type_obligation<'tcx>( + tcx: TyCtxt<'tcx>, + obligations: &mut PredicateObligations<'tcx>, + cause: &ObligationCause<'tcx>, + depth: usize, + param_env: ty::ParamEnv<'tcx>, + term: Term<'tcx>, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, +) { + if let Some(ct) = term.as_const() { + let expected_ty = tcx.type_of(def_id).instantiate(tcx, args).skip_norm_wip(); + obligations.push(Obligation::with_depth( + tcx, + cause.clone(), + depth, + param_env, + ty::ClauseKind::ConstArgHasType(ct, expected_ty), + )); + } +} + /// Confirm and normalize the given inherent projection. // FIXME(mgca): While this supports constants, it is only used for types by default right now #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] @@ -551,16 +576,16 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( tcx.const_of_item(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip().into() }; - if let Some(ct) = term.as_const() { - let expected_ty = tcx.type_of(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip(); - obligations.push(Obligation::with_depth( - tcx, - cause.clone(), - depth + 1, - param_env, - ty::ClauseKind::ConstArgHasType(ct, expected_ty), - )); - } + push_const_arg_has_type_obligation( + tcx, + obligations, + &cause, + depth + 1, + param_env, + term, + alias_term.def_id(), + args, + ); let mut term = selcx.infcx.resolve_vars_if_possible(term); if term.has_aliases() { @@ -2058,17 +2083,16 @@ fn confirm_impl_candidate<'cx, 'tcx>( } else { assoc_term_own_obligations(selcx, obligation, &mut nested); let instantiated_term: Term<'tcx> = term.instantiate(tcx, args).skip_norm_wip(); - if let Some(ct) = instantiated_term.as_const() { - let expected_ty = - tcx.type_of(assoc_term.item.def_id).instantiate(tcx, args).skip_norm_wip(); - nested.push(Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - ty::ClauseKind::ConstArgHasType(ct, expected_ty), - )); - } + push_const_arg_has_type_obligation( + tcx, + &mut nested, + &obligation.cause, + obligation.recursion_depth + 1, + obligation.param_env, + instantiated_term, + assoc_term.item.def_id, + args, + ); Progress { term: instantiated_term, obligations: nested } }; Ok(Projected::Progress(progress)) From cb7ca451114494d669c8edb15cf11b4fbab10a13 Mon Sep 17 00:00:00 2001 From: lapla Date: Sat, 9 May 2026 14:14:48 +0900 Subject: [PATCH 5/5] Register `ConstArgHasType` when normalizing free const aliases --- .../src/solve/normalizes_to/free_alias.rs | 2 +- .../src/traits/normalize.rs | 15 +++++++++++- .../src/traits/query/normalize.rs | 14 ++++++++++- .../src/normalize_erasing_regions.rs | 4 ++-- .../type-const-free-anon-const-mismatch.rs | 12 ++++++++++ ...type-const-free-anon-const-mismatch.stderr | 15 ++++++++++++ ...t-free-value-type-mismatch.current.stderr} | 4 ++-- ...const-free-value-type-mismatch.next.stderr | 24 +++++++++++++++++++ .../type-const-free-value-type-mismatch.rs | 7 +++++- .../mgca/type_const-mismatched-types.rs | 3 ++- .../mgca/type_const-mismatched-types.stderr | 10 ++++++-- 11 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.rs create mode 100644 tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr rename tests/ui/const-generics/mgca/{type-const-free-value-type-mismatch.stderr => type-const-free-value-type-mismatch.current.stderr} (81%) create mode 100644 tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index d68c7dd11d1d9..7430fcd70a1f1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -49,7 +49,7 @@ where kind => panic!("expected free alias, found {kind:?}"), }; - self.instantiate_normalizes_to_term(goal, actual); + self.instantiate_normalizes_to_term_with_type_check(goal, actual); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 84dbd53de83f2..c10c082fc5b1a 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -340,7 +340,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { }), ); self.depth += 1; - let res = if free.kind(infcx.tcx).is_type() { + let res: ty::Term<'tcx> = if free.kind(infcx.tcx).is_type() { infcx .tcx .type_of(free.def_id()) @@ -357,6 +357,19 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { .fold_with(self) .into() }; + // When normalizing a free const alias, register a `ConstArgHasType` + // obligation to ensure the const value's type matches the declared type. + if let Some(ct) = res.as_const() { + let expected_ty = + infcx.tcx.type_of(free.def_id()).instantiate(infcx.tcx, free.args).skip_norm_wip(); + self.obligations.push(Obligation::with_depth( + infcx.tcx, + self.cause.clone(), + self.depth, + self.param_env, + ty::ClauseKind::ConstArgHasType(ct, expected_ty), + )); + } self.depth -= 1; res } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index e8c5d10c78ce3..21c6652540bbd 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -5,7 +5,7 @@ use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def::DefKind; -use rustc_infer::traits::PredicateObligations; +use rustc_infer::traits::{Obligation, PredicateObligations}; use rustc_macros::extension; pub use rustc_middle::traits::query::NormalizationResult; use rustc_middle::ty::{ @@ -374,6 +374,18 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { } else { result.normalized_term }; + // When normalizing a const alias, register a `ConstArgHasType` obligation + // to ensure the const value's type matches the declared type. + if let Some(ct) = res.as_const() { + let expected_ty = + tcx.type_of(term.def_id()).instantiate(tcx, term.args).skip_norm_wip(); + self.obligations.push(Obligation::new( + tcx, + self.cause.clone(), + self.param_env, + ty::ClauseKind::ConstArgHasType(ct, expected_ty), + )); + } // `tcx.normalize_canonicalized_projection` may normalize to a type that // still has unevaluated consts, so keep normalizing here if that's the case. // Similarly, `tcx.normalize_canonicalized_free_alias` will only unwrap one layer diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index d3a2c4d20f95d..d41d2cf27f624 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -63,11 +63,11 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable> + Par fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { match p.kind().skip_binder() { ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => false, + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) => false, ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::Clause(ty::ClauseKind::UnstableFeature(_)) | ty::PredicateKind::NormalizesTo(..) | ty::PredicateKind::AliasRelate(..) diff --git a/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.rs b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.rs new file mode 100644 index 0000000000000..906bd5e7ef912 --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Zvalidate-mir -Znext-solver + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +type const X: usize = const { N }; +//~^ ERROR type annotations needed + +type const N: usize = "this isn't a usize"; +//~^ ERROR the constant `"this isn't a usize"` is not of type `usize` + +fn main() {} diff --git a/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr new file mode 100644 index 0000000000000..0718b739cad0e --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr @@ -0,0 +1,15 @@ +error[E0284]: type annotations needed: cannot normalize `X::{constant#0}` + --> $DIR/type-const-free-anon-const-mismatch.rs:6:1 + | +LL | type const X: usize = const { N }; + | ^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}` + +error: the constant `"this isn't a usize"` is not of type `usize` + --> $DIR/type-const-free-anon-const-mismatch.rs:9:1 + | +LL | type const N: usize = "this isn't a usize"; + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&'static str` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.stderr b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.current.stderr similarity index 81% rename from tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.stderr rename to tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.current.stderr index 20ffb7b335a55..a734b4062b2a6 100644 --- a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.stderr +++ b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.current.stderr @@ -1,11 +1,11 @@ error: the constant `"this isn't a usize"` is not of type `usize` - --> $DIR/type-const-free-value-type-mismatch.rs:7:1 + --> $DIR/type-const-free-value-type-mismatch.rs:10:1 | LL | type const N: usize = "this isn't a usize"; | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&'static str` error[E0080]: transmuting from $BYTE-byte type to $BYTE-byte type: `&str` -> `usize` - --> $DIR/type-const-free-value-type-mismatch.rs:10:24 + --> $DIR/type-const-free-value-type-mismatch.rs:13:24 | LL | fn f() -> [u8; const { N }] {} | ^ evaluation of `f::{constant#0}` failed here diff --git a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr new file mode 100644 index 0000000000000..2fbd629878bfe --- /dev/null +++ b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr @@ -0,0 +1,24 @@ +error: the constant `"this isn't a usize"` is not of type `usize` + --> $DIR/type-const-free-value-type-mismatch.rs:10:1 + | +LL | type const N: usize = "this isn't a usize"; + | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `&'static str` + +error[E0284]: type annotations needed: cannot normalize `f::{constant#0}` + --> $DIR/type-const-free-value-type-mismatch.rs:13:11 + | +LL | fn f() -> [u8; const { N }] {} + | ^^^^^^^^^^^^^^^^^ cannot normalize `f::{constant#0}` + +error[E0308]: mismatched types + --> $DIR/type-const-free-value-type-mismatch.rs:13:11 + | +LL | fn f() -> [u8; const { N }] {} + | - ^^^^^^^^^^^^^^^^^ expected `[u8; _]`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0284, E0308. +For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs index 5e9147b9a7b91..1b66a5d54f7e6 100644 --- a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs +++ b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.rs @@ -1,6 +1,9 @@ #![feature(min_generic_const_args)] #![expect(incomplete_features)] +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver //@ compile-flags: -Zvalidate-mir //@ normalize-stderr: "\d+-byte" -> "$$BYTE-byte" @@ -8,6 +11,8 @@ type const N: usize = "this isn't a usize"; //~^ ERROR the constant `"this isn't a usize"` is not of type `usize` fn f() -> [u8; const { N }] {} -//~^ ERROR transmuting from +//[current]~^ ERROR transmuting from +//[next]~^^ ERROR type annotations needed +//[next]~| ERROR mismatched types [E0308] fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-mismatched-types.rs b/tests/ui/const-generics/mgca/type_const-mismatched-types.rs index b17a5b2b978a6..74f6aa5a2ddf2 100644 --- a/tests/ui/const-generics/mgca/type_const-mismatched-types.rs +++ b/tests/ui/const-generics/mgca/type_const-mismatched-types.rs @@ -5,7 +5,8 @@ type const FREE: u32 = 5_usize; //~^ ERROR the constant `5` is not of type `u32` type const FREE2: isize = FREE; -//~^ ERROR the constant `5` is not of type `isize` +//~^ ERROR the constant `5` is not of type `u32` +//~| ERROR the constant `5` is not of type `isize` trait Tr { type const N: usize; diff --git a/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr b/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr index e2c916cf6d05a..df728d8065923 100644 --- a/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr +++ b/tests/ui/const-generics/mgca/type_const-mismatched-types.stderr @@ -4,6 +4,12 @@ error: the constant `5` is not of type `u32` LL | type const FREE: u32 = 5_usize; | ^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `usize` +error: the constant `5` is not of type `u32` + --> $DIR/type_const-mismatched-types.rs:7:1 + | +LL | type const FREE2: isize = FREE; + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found `usize` + error: the constant `5` is not of type `isize` --> $DIR/type_const-mismatched-types.rs:7:1 | @@ -11,10 +17,10 @@ LL | type const FREE2: isize = FREE; | ^^^^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `usize` error: the constant `false` is not of type `usize` - --> $DIR/type_const-mismatched-types.rs:15:5 + --> $DIR/type_const-mismatched-types.rs:16:5 | LL | type const N: usize = false; | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors