From 8ef6b7a417b2b7c22362aeedfecf932ac0935ea4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 13 Sep 2023 07:37:39 +0200 Subject: [PATCH] restore the old logic adjusting ty::UnevaluatedConst before evaluation --- compiler/rustc_middle/src/mir/mod.rs | 9 +++--- compiler/rustc_middle/src/ty/consts.rs | 26 ++------------- compiler/rustc_middle/src/ty/consts/kind.rs | 35 ++++++++++++++++++++- 3 files changed, 41 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d56f2246c99..c5f0280e617 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2336,18 +2336,19 @@ impl<'tcx> ConstantKind<'tcx> { param_env: ty::ParamEnv<'tcx>, span: Option, ) -> Result, ErrorHandled> { - let uneval = match self { + let (uneval, param_env) = match self { 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. - uv.expand() + let (param_env, uneval) = uneval.prepare_for_eval(tcx, param_env); + (uneval.expand(), param_env) } else { // It's already a valtree, or an error. let val = c.eval(tcx, param_env, span)?; 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), }; // FIXME: We might want to have a `try_eval`-like function on `Unevaluated` diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 86d1e463ed1..629f9f8cd7d 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -276,6 +276,7 @@ impl<'tcx> Const<'tcx> { } /// Returns the evaluated constant + #[inline] pub fn eval( self, tcx: TyCtxt<'tcx>, @@ -285,32 +286,9 @@ impl<'tcx> Const<'tcx> { assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}"); match self.kind() { 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 // `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 // evaluate the const. let c = tcx.const_eval_resolve_for_typeck(param_env, unevaluated, span)?; diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs index 17fbbd7c6b7..a0a7331a37e 100644 --- a/compiler/rustc_middle/src/ty/consts/kind.rs +++ b/compiler/rustc_middle/src/ty/consts/kind.rs @@ -2,7 +2,7 @@ use super::Const; use crate::mir; use crate::ty::abstract_const::CastKind; 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_hir::def_id::DefId; use rustc_macros::HashStable; @@ -26,6 +26,39 @@ impl<'tcx> UnevaluatedConst<'tcx> { pub fn expand(self) -> mir::UnevaluatedConst<'tcx> { 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> {