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
4 changes: 3 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1508,7 +1508,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
} else if self.tcx.features().generic_const_exprs() {
rustc_trait_selection::traits::evaluate_const(&self.infcx, ct, self.param_env)
rustc_trait_selection::traits::evaluate_const(&self.infcx, ct, self.param_env, |ty| {
self.normalize(sp, ty)
})
} else {
ct
}
Expand Down
7 changes: 5 additions & 2 deletions compiler/rustc_next_trait_solver/src/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ 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(
fn evaluate_const<E>(
&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
13 changes: 8 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 @@ -1407,19 +1407,22 @@ 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))
let delegate = self.delegate;
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
20 changes: 14 additions & 6 deletions compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,19 +303,27 @@ 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>(
&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_with_fallible_normalization(
&self.0,
ct,
param_env,
normalize_ty,
) {
Ok(ct) => ct.map(Some),
Err(EvaluateConstErr::EvaluationFailure(e)) => {
Ok(Some(ty::Const::new_error(self.tcx, e)))
}
Err(
EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
) => None,
) => Ok(None),
}
}

Expand Down
20 changes: 17 additions & 3 deletions compiler/rustc_trait_selection/src/traits/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::diagnostics::UnableToConstructConstantValue;
use crate::infer::TypeFreshener;
use crate::infer::region_constraints::{ConstraintKind, RegionConstraintData};
use crate::regions::OutlivesEnvironmentBuildExt;
use crate::traits::normalize::normalize_with_depth_to;
use crate::traits::project::ProjectAndUnifyResult;

// FIXME(twk): this is obviously not nice to duplicate like that
Expand Down Expand Up @@ -851,10 +852,23 @@ impl<'tcx> AutoTraitFinder<'tcx> {
};
}
ty::PredicateKind::ConstEquate(c1, c2) => {
let evaluate = |c: ty::Const<'tcx>| {
let mut 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| {
normalize_with_depth_to(
selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
ty,
&mut PredicateObligations::new(),
)
},
);

if let Err(EvaluateConstErr::InvalidConstParamTy(_)) = ct {
let span = alias_const.kind.def_span(self.tcx);
Expand Down
48 changes: 41 additions & 7 deletions compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@
//! `thir_abstract_const` which can then be checked for structural equality with other
//! generic constants mentioned in the `caller_bounds` of the current environment.

use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::InferCtxt;
use rustc_middle::bug;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor};
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor, Unnormalized,
};
use rustc_span::{DUMMY_SP, Span};
use tracing::{debug, instrument};

use super::EvaluateConstErr;
use crate::error_reporting::InferCtxtErrorExt;
use crate::traits::ObligationCtxt;

/// Check if a given constant can be evaluated.
Expand Down Expand Up @@ -67,7 +71,12 @@ 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_with_fallible_normalization(
infcx,
unexpanded_ct,
param_env,
|ty| normalize_evaluated_const_ty(infcx, param_env, span, ty),
) {
Err(EvaluateConstErr::HasGenericsOrInfers) => {
Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug(
span,
Expand All @@ -78,7 +87,8 @@ pub fn is_const_evaluatable<'tcx>(
EvaluateConstErr::EvaluationFailure(e)
| EvaluateConstErr::InvalidConstParamTy(e),
) => Err(NotConstEvaluatable::Error(e)),
Ok(_) => Ok(()),
Ok(Ok(_)) => Ok(()),
Ok(Err(e)) => Err(NotConstEvaluatable::Error(e)),
}
}
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
Expand All @@ -87,8 +97,15 @@ pub fn is_const_evaluatable<'tcx>(
// This is a sanity check to make sure that non-generics consts are checked to
// be evaluatable in case they aren't cchecked elsewhere. This will NOT error
// if the const uses generics, as desired.
crate::traits::evaluate_const(infcx, unexpanded_ct, param_env);
Ok(())
match crate::traits::evaluate_const_with_fallible_normalization(
infcx,
unexpanded_ct,
param_env,
|ty| normalize_evaluated_const_ty(infcx, param_env, span, ty),
) {
Ok(_) => Ok(()),
Err(e) => Err(NotConstEvaluatable::Error(e)),
}
} else {
let alias_const = match unexpanded_ct.kind() {
ty::ConstKind::Alias(_, alias_const) => alias_const,
Expand All @@ -98,7 +115,12 @@ 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_with_fallible_normalization(
infcx,
unexpanded_ct,
param_env,
|ty| normalize_evaluated_const_ty(infcx, param_env, span, ty),
) {
// 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 Expand Up @@ -145,11 +167,23 @@ pub fn is_const_evaluatable<'tcx>(
Err(
EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e),
) => Err(NotConstEvaluatable::Error(e)),
Ok(_) => Ok(()),
Ok(Ok(_)) => Ok(()),
Ok(Err(e)) => Err(NotConstEvaluatable::Error(e)),
}
}
}

fn normalize_evaluated_const_ty<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
ty: Unnormalized<'tcx, Ty<'tcx>>,
) -> Result<Ty<'tcx>, ErrorGuaranteed> {
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
ocx.deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty)
.map_err(|errors| infcx.err_ctxt().report_fulfillment_errors(errors))
}

#[instrument(skip(infcx, tcx), level = "debug")]
fn satisfied_from_param_env<'tcx>(
tcx: TyCtxt<'tcx>,
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_trait_selection/src/traits/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,16 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
self.selcx.infcx,
c,
obligation.param_env,
|ty| {
normalize_with_depth_to(
&mut self.selcx,
obligation.param_env,
obligation.cause.clone(),
obligation.recursion_depth + 1,
ty,
&mut PredicateObligations::new(),
)
},
) {
Ok(val) => Ok(val),
e @ Err(EvaluateConstErr::HasGenericsOrInfers) => {
Expand Down Expand Up @@ -799,6 +809,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
obligation,
inf_ok.into_obligations(),
)),

Err(err) => {
ProcessResult::Error(FulfillmentErrorCode::ConstEquate(
ExpectedFound::new(c1, c2),
Expand Down
81 changes: 73 additions & 8 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,20 @@ pub fn normalize_param_env_or_error<'tcx>(
&& matches!(alias_const.kind, ty::AliasConstKind::Anon { .. })
{
let infcx = self.0.infer_ctxt().build(TypingMode::non_body_analysis());
let c = evaluate_const(&infcx, c, ty::ParamEnv::empty());
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let cause = ObligationCause::dummy();
let c = match evaluate_const_with_fallible_normalization(
&infcx,
c,
ty::ParamEnv::empty(),
|ty| ocx.deeply_normalize(&cause, ty::ParamEnv::empty(), ty),
) {
Ok(c) => c,
Err(errors) => ty::Const::new_error(
self.0,
infcx.err_ctxt().report_fulfillment_errors(errors),
),
};
// We should never wind up with any `infcx` local state when normalizing anon consts
// under min const generics.
assert!(!c.has_infer() && !c.has_placeholders());
Expand Down Expand Up @@ -537,6 +550,11 @@ pub enum EvaluateConstErr {
EvaluationFailure(ErrorGuaranteed),
}

enum EvaluatedConst<'tcx> {
Const(ty::Const<'tcx>),
ValTree { value: ty::ValTree<'tcx>, ty: Unnormalized<'tcx, Ty<'tcx>> },
}

// FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine
// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own
// normalization scheme
Expand All @@ -550,8 +568,9 @@ pub fn evaluate_const<'tcx>(
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
normalize_ty: impl FnOnce(Unnormalized<'tcx, Ty<'tcx>>) -> Ty<'tcx>,
) -> ty::Const<'tcx> {
match try_evaluate_const(infcx, ct, param_env) {
match try_evaluate_const(infcx, ct, param_env, normalize_ty) {
Ok(ct) => ct,
Err(EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e)) => {
ty::Const::new_error(infcx.tcx, e)
Expand All @@ -560,26 +579,72 @@ pub fn evaluate_const<'tcx>(
}
}

pub(crate) fn evaluate_const_with_fallible_normalization<'tcx, E>(
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
normalize_ty: impl FnOnce(Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
) -> Result<ty::Const<'tcx>, E> {
match try_evaluate_const_with_fallible_normalization(infcx, ct, param_env, normalize_ty) {
Ok(ct) => ct,
Err(EvaluateConstErr::EvaluationFailure(e) | EvaluateConstErr::InvalidConstParamTy(e)) => {
Ok(ty::Const::new_error(infcx.tcx, e))
}
Err(EvaluateConstErr::HasGenericsOrInfers) => Ok(ct),
}
}

// FIXME(BoxyUwU): Private this once we `generic_const_exprs` isn't doing its own normalization routine
// FIXME(generic_const_exprs): Consider accepting a `ty::AliasConst` when we are not rolling our own
// normalization scheme
/// Evaluates a type system constant making sure to not allow constants that depend on generic parameters
/// or inference variables to succeed in evaluating.
/// Evaluates a type system constant making sure to not allow constants that depend on generic
/// parameters or inference variables to succeed in evaluating.
///
/// If evaluation succeeds, `normalize_ty` normalizes the type before it is attached to the
/// resulting `ConstKind::Value`.
///
/// 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>(
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
normalize_ty: impl FnOnce(Unnormalized<'tcx, Ty<'tcx>>) -> Ty<'tcx>,
) -> Result<ty::Const<'tcx>, EvaluateConstErr> {
match try_evaluate_const_inner(infcx, ct, param_env)? {
EvaluatedConst::Const(ct) => Ok(ct),
EvaluatedConst::ValTree { value, ty } => {
Ok(ty::Const::new_value(infcx.tcx, value, normalize_ty(ty)))
}
}
}

pub(crate) fn try_evaluate_const_with_fallible_normalization<'tcx, E>(
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
normalize_ty: impl FnOnce(Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
) -> Result<Result<ty::Const<'tcx>, E>, EvaluateConstErr> {
match try_evaluate_const_inner(infcx, ct, param_env)? {
EvaluatedConst::Const(ct) => Ok(Ok(ct)),
EvaluatedConst::ValTree { value, ty } => {
Ok(normalize_ty(ty).map(|ty| ty::Const::new_value(infcx.tcx, value, ty)))
}
}
}

#[instrument(name = "try_evaluate_const", level = "debug", skip(infcx))]
fn try_evaluate_const_inner<'tcx>(
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<EvaluatedConst<'tcx>, EvaluateConstErr> {
let tcx = infcx.tcx;
let ct = infcx.resolve_vars_if_possible(ct);
debug!(?ct);

match ct.kind() {
ty::ConstKind::Value(..) => Ok(ct),
ty::ConstKind::Value(..) => Ok(EvaluatedConst::Const(ct)),
ty::ConstKind::Error(e) => Err(EvaluateConstErr::EvaluationFailure(e)),
ty::ConstKind::Param(_)
| ty::ConstKind::Infer(_)
Expand Down Expand Up @@ -711,8 +776,8 @@ pub fn try_evaluate_const<'tcx>(
// where it gets used as a const generic.
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()))
Ok(Ok(value)) => {
Ok(EvaluatedConst::ValTree { value, ty: alias_const.type_of(tcx) })
}
Ok(Err(_)) => {
let e = tcx.dcx().delayed_bug(
Expand Down
Loading
Loading