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 @@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,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()
}
Expand All @@ -74,7 +74,8 @@ where
}
kind => panic!("expected inherent alias, found {kind:?}"),
};
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 @@ -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<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 @@ -424,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)
})
}
Expand Down
15 changes: 14 additions & 1 deletion compiler/rustc_trait_selection/src/traits/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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
}
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
14 changes: 13 additions & 1 deletion compiler/rustc_trait_selection/src/traits/query/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_traits/src/normalize_erasing_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<TyCtxt<'tcx>> + 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(..)
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
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: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:13: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,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`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#![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"

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 }] {}
//[current]~^ ERROR transmuting from
//[next]~^^ ERROR type annotations needed
//[next]~| ERROR mismatched types [E0308]

fn main() {}
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; _]`, 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() {}
Loading
Loading