restore the old logic adjusting ty::UnevaluatedConst before evaluation

This commit is contained in:
Ralf Jung 2023-09-13 07:37:39 +02:00
parent 11a4a24d8e
commit 8ef6b7a417
3 changed files with 41 additions and 29 deletions

View File

@ -2336,18 +2336,19 @@ impl<'tcx> ConstantKind<'tcx> {
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
span: Option<Span>, span: Option<Span>,
) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> { ) -> Result<interpret::ConstValue<'tcx>, ErrorHandled> {
let uneval = match self { let (uneval, param_env) = match self {
ConstantKind::Ty(c) => { ConstantKind::Ty(c) => {
if let ty::ConstKind::Unevaluated(uv) = c.kind() { if let ty::ConstKind::Unevaluated(uneval) = c.kind() {
// Avoid the round-trip via valtree, evaluate directly to ConstValue. // Avoid the round-trip via valtree, evaluate directly to ConstValue.
uv.expand() let (param_env, uneval) = uneval.prepare_for_eval(tcx, param_env);
(uneval.expand(), param_env)
} else { } else {
// It's already a valtree, or an error. // It's already a valtree, or an error.
let val = c.eval(tcx, param_env, span)?; let val = c.eval(tcx, param_env, span)?;
return Ok(tcx.valtree_to_const_val((self.ty(), val))); return Ok(tcx.valtree_to_const_val((self.ty(), val)));
} }
} }
ConstantKind::Unevaluated(uneval, _) => uneval, ConstantKind::Unevaluated(uneval, _) => (uneval, param_env),
ConstantKind::Val(val, _) => return Ok(val), ConstantKind::Val(val, _) => return Ok(val),
}; };
// FIXME: We might want to have a `try_eval`-like function on `Unevaluated` // FIXME: We might want to have a `try_eval`-like function on `Unevaluated`

View File

@ -276,6 +276,7 @@ impl<'tcx> Const<'tcx> {
} }
/// Returns the evaluated constant /// Returns the evaluated constant
#[inline]
pub fn eval( pub fn eval(
self, self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
@ -285,32 +286,9 @@ impl<'tcx> Const<'tcx> {
assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
match self.kind() { match self.kind() {
ConstKind::Unevaluated(unevaluated) => { ConstKind::Unevaluated(unevaluated) => {
// HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
// also does later, but we want to do it before checking for
// inference variables.
// Note that we erase regions *before* calling `with_reveal_all_normalized`,
// so that we don't try to invoke this query with
// any region variables.
// HACK(eddyb) when the query key would contain inference variables,
// attempt using identity args and `ParamEnv` instead, that will succeed
// when the expression doesn't depend on any parameters.
// FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
// we can call `infcx.const_eval_resolve` which handles inference variables.
let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
def: unevaluated.def,
args: GenericArgs::identity_for_item(tcx, unevaluated.def),
})
} else {
tcx.erase_regions(param_env)
.with_reveal_all_normalized(tcx)
.and(tcx.erase_regions(unevaluated))
};
// FIXME(eddyb) maybe the `const_eval_*` methods should take // FIXME(eddyb) maybe the `const_eval_*` methods should take
// `ty::ParamEnvAnd` instead of having them separate. // `ty::ParamEnvAnd` instead of having them separate.
let (param_env, unevaluated) = param_env_and.into_parts(); let (param_env, unevaluated) = unevaluated.prepare_for_eval(tcx, param_env);
// try to resolve e.g. associated constants to their definition on an impl, and then // try to resolve e.g. associated constants to their definition on an impl, and then
// evaluate the const. // evaluate the const.
let c = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)?; let c = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)?;

View File

@ -2,7 +2,7 @@ use super::Const;
use crate::mir; use crate::mir;
use crate::ty::abstract_const::CastKind; use crate::ty::abstract_const::CastKind;
use crate::ty::GenericArgsRef; use crate::ty::GenericArgsRef;
use crate::ty::{self, List, Ty}; use crate::ty::{self, visit::TypeVisitableExt as _, List, Ty, TyCtxt};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_macros::HashStable; use rustc_macros::HashStable;
@ -26,6 +26,39 @@ impl<'tcx> UnevaluatedConst<'tcx> {
pub fn expand(self) -> mir::UnevaluatedConst<'tcx> { pub fn expand(self) -> mir::UnevaluatedConst<'tcx> {
mir::UnevaluatedConst { def: self.def, args: self.args, promoted: None } mir::UnevaluatedConst { def: self.def, args: self.args, promoted: None }
} }
/// FIXME(RalfJung): I cannot explain what this does or why it makes sense, but not doing this
/// hurts performance.
#[inline]
pub(crate) fn prepare_for_eval(
self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> (ty::ParamEnv<'tcx>, Self) {
// HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
// also does later, but we want to do it before checking for
// inference variables.
// Note that we erase regions *before* calling `with_reveal_all_normalized`,
// so that we don't try to invoke this query with
// any region variables.
// HACK(eddyb) when the query key would contain inference variables,
// attempt using identity args and `ParamEnv` instead, that will succeed
// when the expression doesn't depend on any parameters.
// FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
// we can call `infcx.const_eval_resolve` which handles inference variables.
if (param_env, self).has_non_region_infer() {
(
tcx.param_env(self.def),
ty::UnevaluatedConst {
def: self.def,
args: ty::GenericArgs::identity_for_item(tcx, self.def),
},
)
} else {
(tcx.erase_regions(param_env).with_reveal_all_normalized(tcx), tcx.erase_regions(self))
}
}
} }
impl<'tcx> UnevaluatedConst<'tcx> { impl<'tcx> UnevaluatedConst<'tcx> {