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
5 changes: 5 additions & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,11 @@ impl<'tcx> rustc_type_ir::inherent::ParamEnv<TyCtxt<'tcx>> for ParamEnv<'tcx> {
fn caller_bounds(self) -> impl inherent::SliceLike<Item = ty::Clause<'tcx>> {
self.caller_bounds()
}

#[inline]
fn empty() -> Self {
Self::empty()
}
}

impl<'tcx> ParamEnv<'tcx> {
Expand Down
57 changes: 48 additions & 9 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1282,19 +1282,58 @@ where
Ok(())
}

// Try to evaluate a const, or return `None` if the const is too generic.
// This doesn't mean the const isn't evaluatable, though, and should be treated
// as an ambiguity rather than no-solution.
// Try to evaluate a const, returning `(Option<Const>, Certainty)`, this returns
// `NoSolution` when concrete const fails to prove well-formedness in empty env.
//
// `(None, Maybe(_))` means we couldn't fully check const's well-formedness, and
// bailed without evaluation.
//
// `(None, Certainty::Yes)` means const is too generic, and doesn't indicate the
// const isn't evaluatable, should be treated as an ambiguity rather than no-solution.
pub(super) fn evaluate_const(
&mut self,
param_env: I::ParamEnv,
alias_const: ty::AliasConst<I>,
) -> Result<Option<I::Const>, RerunNonErased> {
) -> Result<(Option<I::Const>, Certainty), NoSolutionOrRerunNonErased> {
if self.typing_mode().is_erased_not_coherence() {
match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {}
}
let cx = self.cx();

// Checking in empty env to be sure const is WF without relying on assumptions from env,
// preventing ill-formed consts from reaching CTFE.
//
// For example without this check const with `[u8]: Sized` bound could get to be evaluated
// in environment with the same assumption, pass and ICE later in CTFE.
let certainty = if alias_const.has_non_region_infer()
|| alias_const.has_non_region_param()
|| alias_const.has_non_region_placeholders()
{
// Skip proving for not fully concrete consts as they either won't reach CTFE or
// can't depend on generics even if they have them to pass CTFE.
Comment on lines +1312 to +1313

@zedddie zedddie Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not super sure about this comment

View changes since the review

Certainty::Yes
}
// We do this only for GCA consts. Doing this check for GCE introduces cycles with anon
// consts in impl blocks.
else if cx.features().generic_const_args() {
// FIXME(zedddie): we should check this in `fully_monomorphized()` Typing mode.
let goal = Goal::new(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we definitely want a nice comment explaining why we do this :3

cx,
ParamEnv::empty(),
ty::ClauseKind::WellFormed(alias_const.to_const(cx, ty::IsRigid::Yes).into()),
);
self.add_goal(GoalSource::AliasWellFormed, goal)?;
let certainty = self.try_evaluate_added_goals()?;

if matches!(certainty, Certainty::Maybe(_)) {
return Ok((None, certainty));
}
certainty
} else {
Certainty::Yes
};

Ok(self.delegate.evaluate_const(param_env, alias_const))
Ok((self.delegate.evaluate_const(param_env, alias_const), certainty))
}

pub(super) fn evaluate_const_and_instantiate_projection_term(
Expand All @@ -1305,11 +1344,11 @@ where
alias_const: ty::AliasConst<I>,
) -> QueryResultOrRerunNonErased<I> {
match self.evaluate_const(param_env, alias_const)? {
Some(evaluated) => {
(Some(evaluated), _) => {
self.eq(param_env, expected_term, evaluated.into())?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
None if self.cx().features().generic_const_args() => {
(None, certainty) if self.cx().features().generic_const_args() => {
// HACK(khyperia): calling `resolve_vars_if_possible` here shouldn't be necessary,
// `try_evaluate_const` calls `resolve_vars_if_possible` already. However, we want
// to check `has_non_region_infer` against the type with vars resolved (i.e. check
Expand All @@ -1331,10 +1370,10 @@ where
projection_term.to_term(self.cx(), ty::IsRigid::Yes),
expected_term,
)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
self.evaluate_added_goals_and_make_canonical_response(certainty)
}
}
None => {
(None, _) => {
// Legacy behavior: always treat as ambiguous
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
Expand Down
14 changes: 10 additions & 4 deletions compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,19 @@ where
// error in that case is unnecessary noise. This may change in the future once
// evaluation failures are allowed to impact selection, e.g. generic const
// expressions in impl headers or `where`-clauses.
//
// `evaluate_const` itself can return `NoSolution` if alias' WF doesn't hold in
// empty environment.

// FIXME(generic_const_exprs): Implement handling for generic
// const expressions here.
if let Some(_normalized) = self.evaluate_const(param_env, alias_const)? {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
match self.evaluate_const(param_env, alias_const)? {
(None, certainty) => self.evaluate_added_goals_and_make_canonical_response(
certainty.and(Certainty::AMBIGUOUS),
),
(Some(_normalized), _) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_type_ir/src/const_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_type_ir_macros::{
GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic,
};

use crate::inherent::*;
use crate::{self as ty, AliasConst, BoundVarIndexKind, Interner};

/// Represents a constant in Rust.
Expand Down Expand Up @@ -84,6 +85,10 @@ impl<I: Interner> AliasConst<I> {
AliasConst { kind, args, _use_alias_new_instead: () }
}

pub fn to_const(self, interner: I, is_rigid: ty::IsRigid) -> I::Const {
I::Const::new_alias(interner, is_rigid, self)
}

pub fn type_of(self, interner: I) -> ty::Unnormalized<I, I::Ty> {
let def_id = match self.kind {
ty::AliasConstKind::Projection { def_id } => def_id.into(),
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_type_ir/src/inherent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,14 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
#[rust_analyzer::prefer_underscore_import]
pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
fn caller_bounds(self) -> impl SliceLike<Item = I::Clause>;

/// Construct a trait environment suitable for contexts where there are
/// no where-clauses in scope. In the majority of cases it is incorrect
/// to use an empty environment. See the [dev guide section][param_env_guide]
/// for information on what a `ParamEnv` is and how to acquire one.
///
/// [param_env_guide]: https://rustc-dev-guide.rust-lang.org/typing_parameter_envs.html
fn empty() -> Self;
}

#[rust_analyzer::prefer_underscore_import]
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/const-generics/gca/impl-block-type-const-type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/156780>.
//@ compile-flags: -Znext-solver=globally
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![feature(generic_const_items)]
#![feature(macroless_generic_const_args)]

const ADD1<const N:usize>: usize = N + 1;
type const A<const N:usize>: usize = ADD1::<N>;
impl [(); A::<1f64>] {} //~ ERROR: type mismatch resolving `A<1f64> == _` [E0271]
//~| ERROR: cannot define inherent `impl` for primitive types [E0390]
//~^^ ERROR: the type `[(); A::<1f64>]` is not well-formed
fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0271]: type mismatch resolving `A<1f64> == _`
--> $DIR/impl-block-type-const-type-mismatch.rs:10:1
|
LL | impl [(); A::<1f64>] {}
| ^^^^^^^^^^^^^^^^^^^^^^^ types differ

error: the type `[(); A::<1f64>]` is not well-formed
--> $DIR/impl-block-type-const-type-mismatch.rs:10:6
|
LL | impl [(); A::<1f64>] {}
| ^^^^^^^^^^^^^^^

error[E0390]: cannot define inherent `impl` for primitive types
--> $DIR/impl-block-type-const-type-mismatch.rs:10:1
|
LL | impl [(); A::<1f64>] {}
| ^^^^^^^^^^^^^^^^^^^^
|
= help: consider using an extension trait instead

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0271, E0390.
For more information about an error, try `rustc --explain E0271`.
12 changes: 12 additions & 0 deletions tests/ui/const-generics/gca/rp-type-const-type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/154805>.
//@ compile-flags: -Znext-solver=globally
#![feature(generic_const_items,min_generic_const_args)]
#![feature(macroless_generic_const_args, generic_const_args)]
const ADD1<const N: usize>: usize = N + 1;
fn a() -> [usize; ADD1::<b"">] {} //~ ERROR: type mismatch resolving `ADD1<*b""> == _` [E0271]
//~| ERROR: the type `[usize; ADD1::<*b"">]` is not well-formed
//~| ERROR: mismatched types [E0308]
//~| ERROR: type mismatch resolving `ADD1<*b""> == _` [E0271]


fn main() {}
32 changes: 32 additions & 0 deletions tests/ui/const-generics/gca/rp-type-const-type-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
error[E0271]: type mismatch resolving `ADD1<*b""> == _`
--> $DIR/rp-type-const-type-mismatch.rs:6:11
|
LL | fn a() -> [usize; ADD1::<b"">] {}
| ^^^^^^^^^^^^^^^^^^^^ types differ

error: the type `[usize; ADD1::<*b"">]` is not well-formed
--> $DIR/rp-type-const-type-mismatch.rs:6:11
|
LL | fn a() -> [usize; ADD1::<b"">] {}
| ^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/rp-type-const-type-mismatch.rs:6:11
|
LL | fn a() -> [usize; ADD1::<b"">] {}
| - ^^^^^^^^^^^^^^^^^^^^ expected `[usize; _]`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0271]: type mismatch resolving `ADD1<*b""> == _`
--> $DIR/rp-type-const-type-mismatch.rs:6:11
|
LL | fn a() -> [usize; ADD1::<b"">] {}
| ^^^^^^^^^^^^^^^^^^^^ types differ
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0271, E0308.
For more information about an error, try `rustc --explain E0271`.
11 changes: 11 additions & 0 deletions tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/154805>.
//@ compile-flags: -Znext-solver=globally
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]
#![feature(generic_const_items)]

const ADD1<const N: usize>: usize = N + 1;
type const ONE: usize = ADD1::<core::direct_const_arg!(b"")>; //~ ERROR type mismatch resolving
//~| ERROR the constant `*b""` is not of type `usize`
//~| ERROR the constant `ADD1::<*b"">` is not of type `usize`
fn main() {}
27 changes: 27 additions & 0 deletions tests/ui/const-generics/gca/type-const-arg-type-mismatch-1.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0271]: type mismatch resolving `ADD1<*b""> == _`
--> $DIR/type-const-arg-type-mismatch-1.rs:8:1
|
LL | type const ONE: usize = ADD1::<core::direct_const_arg!(b"")>;
| ^^^^^^^^^^^^^^^^^^^^^ types differ

error: the constant `*b""` is not of type `usize`
--> $DIR/type-const-arg-type-mismatch-1.rs:8:1
|
LL | type const ONE: usize = ADD1::<core::direct_const_arg!(b"")>;
| ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `[u8; 0]`
|
note: required by a const generic parameter in `ADD1`
--> $DIR/type-const-arg-type-mismatch-1.rs:7:12
|
LL | const ADD1<const N: usize>: usize = N + 1;
| ^^^^^^^^^^^^^^ required by this const generic parameter in `ADD1`

error: the constant `ADD1::<*b"">` is not of type `usize`
--> $DIR/type-const-arg-type-mismatch-1.rs:8:1
|
LL | type const ONE: usize = ADD1::<core::direct_const_arg!(b"")>;
| ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found a different `usize`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0271`.
12 changes: 12 additions & 0 deletions tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/154805>.
//@ compile-flags: -Znext-solver=globally
#![feature(min_generic_const_args)]
#![feature(macroless_generic_const_args)]
#![feature(generic_const_args)]
#![feature(generic_const_items)]

const ADD1<const N: usize>: usize = N + 1;
type const ONE: usize = ADD1::<b"">; //~ ERROR type mismatch resolving
//~| ERROR the constant `*b""` is not of type `usize`
//~| ERROR the constant `ADD1::<*b"">` is not of type `usize`
fn main() {}
27 changes: 27 additions & 0 deletions tests/ui/const-generics/gca/type-const-arg-type-mismatch-2.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
error[E0271]: type mismatch resolving `ADD1<*b""> == _`
--> $DIR/type-const-arg-type-mismatch-2.rs:9:1
|
LL | type const ONE: usize = ADD1::<b"">;
| ^^^^^^^^^^^^^^^^^^^^^ types differ

error: the constant `*b""` is not of type `usize`
--> $DIR/type-const-arg-type-mismatch-2.rs:9:1
|
LL | type const ONE: usize = ADD1::<b"">;
| ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `[u8; 0]`
|
note: required by a const generic parameter in `ADD1`
--> $DIR/type-const-arg-type-mismatch-2.rs:8:12
|
LL | const ADD1<const N: usize>: usize = N + 1;
| ^^^^^^^^^^^^^^ required by this const generic parameter in `ADD1`

error: the constant `ADD1::<*b"">` is not of type `usize`
--> $DIR/type-const-arg-type-mismatch-2.rs:9:1
|
LL | type const ONE: usize = ADD1::<b"">;
| ^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found a different `usize`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0271`.
Loading