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
11 changes: 9 additions & 2 deletions compiler/rustc_next_trait_solver/src/delegate.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fmt::Debug;
use std::ops::Deref;

use rustc_type_ir::solve::{
Expand Down Expand Up @@ -36,11 +37,17 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
// FIXME: Uplift the leak check into this crate.
fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>;

fn evaluate_const(
/// Evaluate a const, normalizing the type of the resulting value with `normalize_ty`.
/// Returns `Ok(None)` if the const is too generic, and `Err(_)` only if `normalize_ty`
/// failed.
fn evaluate_const<E: Debug>(
&self,
param_env: <Self::Interner as Interner>::ParamEnv,
alias_const: ty::AliasConst<Self::Interner>,
) -> Option<<Self::Interner as Interner>::Const>;
normalize_ty: impl FnOnce(
ty::Unnormalized<Self::Interner, <Self::Interner as Interner>::Ty>,
) -> Result<<Self::Interner as Interner>::Ty, E>,
) -> Result<Option<<Self::Interner as Interner>::Const>, E>;

// FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`!
fn well_formed_goals(
Expand Down
12 changes: 7 additions & 5 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,19 +1403,21 @@ 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 and normalize the type of the resulting value, 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.
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>, NoSolutionOrRerunNonErased> {
if self.typing_mode().is_erased_not_coherence() {
match self.opaque_accesses.rerun_always(RerunReason::EvaluateConst)? {}
}

Ok(self.delegate.evaluate_const(param_env, alias_const))
self.delegate.evaluate_const(param_env, alias_const, |ty| {
self.normalize(GoalSource::Misc, param_env, ty)
})
}

pub(super) fn evaluate_const_and_instantiate_projection_term(
Expand Down
17 changes: 11 additions & 6 deletions compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use std::mem;
use std::ops::Deref;

Expand Down Expand Up @@ -319,19 +320,23 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
}

fn evaluate_const(
fn evaluate_const<E: Debug>(
&self,
param_env: ty::ParamEnv<'tcx>,
alias_const: ty::AliasConst<'tcx>,
) -> Option<ty::Const<'tcx>> {
normalize_ty: impl FnOnce(ty::Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
) -> Result<Option<ty::Const<'tcx>>, E> {
let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);

match crate::traits::try_evaluate_const(&self.0, ct, param_env) {
Ok(ct) => Some(ct),
Err(EvaluateConstErr::EvaluationFailure(e)) => Some(ty::Const::new_error(self.tcx, e)),
match crate::traits::try_evaluate_const(&self.0, ct, param_env, normalize_ty) {
Ok(ct) => Ok(Some(ct)),
Err(EvaluateConstErr::EvaluationFailure(e)) => {
Ok(Some(ty::Const::new_error(self.tcx, e)))
}
Err(
EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
) => None,
) => Ok(None),
Err(EvaluateConstErr::FailedNormalization(e)) => Err(e),
}
}

Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_trait_selection/src/traits/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -853,8 +853,12 @@ impl<'tcx> AutoTraitFinder<'tcx> {
ty::PredicateKind::ConstEquate(c1, c2) => {
let evaluate = |c: ty::Const<'tcx>| {
if let ty::ConstKind::Alias(_, alias_const) = c.kind() {
let ct =
super::try_evaluate_const(selcx.infcx, c, obligation.param_env);
let ct = super::try_evaluate_const(
selcx.infcx,
c,
obligation.param_env,
|ty| Ok::<_, !>(ty.skip_norm_wip()),
);

if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct {
let span = alias_const.kind.def_span(self.tcx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ pub fn is_const_evaluatable<'tcx>(
tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported");
}
ty::ConstKind::Alias(_, _) => {
match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) {
match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env, |ty| {
Ok::<_, !>(ty.skip_norm_wip())
}) {
Err(EvaluateConstErr::HasGenericsOrInfers) => {
Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug(
span,
Expand Down Expand Up @@ -98,7 +100,9 @@ pub fn is_const_evaluatable<'tcx>(
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
};

match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env) {
match crate::traits::try_evaluate_const(infcx, unexpanded_ct, param_env, |ty| {
Ok::<_, !>(ty.skip_norm_wip())
}) {
// If we're evaluating a generic foreign constant, under a nightly compiler while
// the current crate does not enable `feature(generic_const_exprs)`, abort
// compilation with a useful error.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_trait_selection/src/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
self.selcx.infcx,
c,
obligation.param_env,
|ty| Ok::<_, !>(ty.skip_norm_wip()),
) {
Ok(val) => Ok(val),
e @ Err(EvaluateConstErr::HasGenericsOrInfers) => {
Expand Down
16 changes: 10 additions & 6 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ pub fn normalize_param_env_or_error<'tcx>(
}

#[derive(Debug)]
pub enum EvaluateConstErr {
pub enum EvaluateConstErr<E> {
/// The constant being evaluated was either a generic parameter or inference variable, *or*,
/// some alias const with either generic parameters or inference variables in its
/// generic arguments.
Expand All @@ -585,6 +585,7 @@ pub enum EvaluateConstErr {
/// CTFE failed to evaluate the constant in some unrecoverable way (e.g. encountered a `panic!`).
/// This is also used when the constant was already tainted by error.
EvaluationFailure(ErrorGuaranteed),
FailedNormalization(E),
}

// FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine
Expand All @@ -601,7 +602,7 @@ pub fn evaluate_const<'tcx>(
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> ty::Const<'tcx> {
match try_evaluate_const(infcx, ct, param_env) {
match try_evaluate_const(infcx, ct, param_env, |v| Ok::<_, !>(v.skip_norm_wip())) {
Ok(ct) => ct,
Err(EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e)) => {
ty::Const::new_error(infcx.tcx, e)
Expand All @@ -618,12 +619,13 @@ pub fn evaluate_const<'tcx>(
///
/// You should not call this function unless you are implementing normalization itself. Prefer to use
/// `normalize_erasing_regions` or the `normalize` functions on `ObligationCtxt`/`FnCtxt`/`InferCtxt`.
#[instrument(level = "debug", skip(infcx), ret)]
pub fn try_evaluate_const<'tcx>(
#[instrument(level = "debug", skip(infcx, normalize_ty), ret)]
pub fn try_evaluate_const<'tcx, E: Debug>(
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<ty::Const<'tcx>, EvaluateConstErr> {
normalize_ty: impl FnOnce(Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
) -> Result<ty::Const<'tcx>, EvaluateConstErr<E>> {
let tcx = infcx.tcx;
let ct = infcx.resolve_vars_if_possible(ct);
debug!(?ct);
Expand Down Expand Up @@ -762,7 +764,9 @@ pub fn try_evaluate_const<'tcx>(
let span = alias_const.kind.def_span(tcx);
match tcx.const_eval_resolve_for_typeck(typing_env, erased_alias_const, span) {
Ok(Ok(val)) => {
Ok(ty::Const::new_value(tcx, val, alias_const.type_of(tcx).skip_norm_wip()))
let ty = normalize_ty(alias_const.type_of(tcx))
.map_err(EvaluateConstErr::FailedNormalization)?;
Ok(ty::Const::new_value(tcx, val, ty))
}
Ok(Err(_)) => {
let e = tcx.dcx().delayed_bug(
Expand Down
11 changes: 6 additions & 5 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -921,11 +921,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {

let evaluate = |c: ty::Const<'tcx>| {
if let ty::ConstKind::Alias(_, _) = c.kind() {
match crate::traits::try_evaluate_const(self.infcx, c, obligation.param_env)
{
Ok(val) => Ok(val),
Err(e) => Err(e),
}
crate::traits::try_evaluate_const(
self.infcx,
c,
obligation.param_env,
|v| Ok::<_, !>(v.skip_norm_wip()),
)
} else {
Ok(c)
}
Expand Down
26 changes: 26 additions & 0 deletions tests/ui/traits/next-solver/adt-const-param-projection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//@ revisions: old next
//@[next] compile-flags: -Znext-solver
//@ build-pass
//@ compile-flags: --crate-type=lib
//@ edition: 2015

// Regression test for https://github.com/rust-lang/rust/issues/156294.
// We used to not normalize the type we get back from const evaluation, so the value of
// `EMPTY_MATRIX` had the type `<Type as Trait>::Matrix` instead of `[usize; 1]`. Nobody
// normalized it later on either, so we ended up ICEing when mangling the symbol name of
// `Walk::<EMPTY_MATRIX>::new`.

#![feature(adt_const_params)]

pub const EMPTY_MATRIX: <Type as Trait>::Matrix = [1];
pub struct Walk<const REMAINING: <Type as Trait>::Matrix>;
impl Walk<EMPTY_MATRIX> {
pub fn new() {}
}
pub enum Type {}
pub trait Trait {
type Matrix;
}
impl Trait for Type {
type Matrix = [usize; 1];
}
34 changes: 34 additions & 0 deletions tests/ui/traits/next-solver/normalize-const-item-type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//@ compile-flags: -Znext-solver
#![feature(generic_const_items)]
#![feature(min_generic_const_args)]
#![feature(generic_const_args)]

use std::marker::PhantomData;

trait Project1<'a> {
type Assoc1;
}

impl<'a, T> Project1<'a> for T {
type Assoc1 = ();
}

trait Project2 {
type Assoc2;
}

impl<T: Project1<'static, Assoc1 = ()>> Project2 for PhantomData<T> {
type Assoc2 = usize;
}

const N<T>: <PhantomData::<T> as Project2>::Assoc2 = 2_usize;

fn func(_: [(); core::direct_const_arg!(N::<u32>)])
//~^ ERROR: type mismatch resolving `N<u32> == _` [E0271]
//~| ERROR: the type `[(); N::<u32>]` is not well-formed
//~| ERROR: type mismatch resolving `N<u32> == _` [E0271]
where
for<'a> u32: Project1<'a>,
{}

fn main() {}
23 changes: 23 additions & 0 deletions tests/ui/traits/next-solver/normalize-const-item-type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error[E0271]: type mismatch resolving `N<u32> == _`
--> $DIR/normalize-const-item-type.rs:26:12
|
LL | fn func(_: [(); core::direct_const_arg!(N::<u32>)])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ

error: the type `[(); N::<u32>]` is not well-formed
--> $DIR/normalize-const-item-type.rs:26:12
|
LL | fn func(_: [(); core::direct_const_arg!(N::<u32>)])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0271]: type mismatch resolving `N<u32> == _`
--> $DIR/normalize-const-item-type.rs:26:12
|
LL | fn func(_: [(); core::direct_const_arg!(N::<u32>)])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 3 previous errors

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