Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ where
.skip_norm_wip()
.into()
};
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)
}
}
24 changes: 23 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,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<I, NormalizesTo<I>>,
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(
Expand Down Expand Up @@ -393,7 +415,7 @@ where
kind => panic!("expected projection, found {kind:?}"),
};

ecx.instantiate_normalizes_to_term(
ecx.instantiate_normalizes_to_term_with_type_check(
goal,
term.instantiate(cx, target_args).skip_norm_wip(),
);
Expand Down
49 changes: 48 additions & 1 deletion compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))]
Expand Down Expand Up @@ -551,6 +576,17 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
tcx.const_of_item(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip().into()
};

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() {
term =
Expand Down Expand Up @@ -2046,7 +2082,18 @@ 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();
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))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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; const { Struct::N }]`, 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`.
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
error[E0271]: type mismatch resolving `<A as Array>::LEN normalizes-to 0`
--> $DIR/type-const-value-type-mismatch.rs:22:5
|
LL | fn arr() -> [u8; const { Self::LEN }] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
|
note: the requirement `<A as Array>::LEN normalizes-to 0` appears on the `impl`'s associated function `arr` but not on the corresponding trait's associated function
--> $DIR/type-const-value-type-mismatch.rs:15:8
|
LL | pub trait Array {
| ----- in this trait
LL | type const LEN: usize;
LL | fn arr() -> [u8; Self::LEN];
| ^^^ this trait's associated function doesn't have the requirement `<A as Array>::LEN normalizes-to 0`

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 `<A as Array>::arr::{constant#0}`
--> $DIR/type-const-value-type-mismatch.rs:22:17
|
LL | fn arr() -> [u8; const { Self::LEN }] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `<A as Array>::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; const { Self::LEN }]`, 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`.
29 changes: 29 additions & 0 deletions tests/ui/const-generics/mgca/type-const-value-type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -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 `<A as Array>::LEN normalizes-to 0` [E0271]
}

fn main() {}
Loading