1
Fork 0

don't evaluate with escaping bound vars

This commit is contained in:
lcnr 2022-07-04 18:38:35 +02:00
parent 01adb7e98d
commit d15b00af48
4 changed files with 57 additions and 19 deletions

View file

@ -743,9 +743,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
} }
} }
} }
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
if self.tcx().lazy_normalization() =>
{
assert_eq!(promoted, None); assert_eq!(promoted, None);
let substs = self.relate_with_variance( let substs = self.relate_with_variance(
ty::Variance::Invariant, ty::Variance::Invariant,
@ -967,9 +965,7 @@ impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
} }
} }
} }
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) => {
if self.tcx().lazy_normalization() =>
{
assert_eq!(promoted, None); assert_eq!(promoted, None);
let substs = self.relate_with_variance( let substs = self.relate_with_variance(
ty::Variance::Invariant, ty::Variance::Invariant,

View file

@ -180,6 +180,7 @@ impl<'tcx> ConstKind<'tcx> {
param_env: ParamEnv<'tcx>, param_env: ParamEnv<'tcx>,
eval_mode: EvalMode, eval_mode: EvalMode,
) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> { ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
if let ConstKind::Unevaluated(unevaluated) = self { if let ConstKind::Unevaluated(unevaluated) = self {
use crate::mir::interpret::ErrorHandled; use crate::mir::interpret::ErrorHandled;

View file

@ -635,13 +635,18 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
if self.selcx.tcx().lazy_normalization() { let tcx = self.selcx.tcx();
if tcx.lazy_normalization() {
constant constant
} else { } else {
let constant = constant.super_fold_with(self); let constant = constant.super_fold_with(self);
debug!(?constant); debug!(?constant, ?self.param_env);
debug!("self.param_env: {:?}", self.param_env); with_replaced_escaping_bound_vars(
constant.eval(self.selcx.tcx(), self.param_env) self.selcx.infcx(),
&mut self.universes,
constant,
|constant| constant.eval(tcx, self.param_env),
)
} }
} }
@ -671,6 +676,41 @@ pub struct BoundVarReplacer<'me, 'tcx> {
universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
} }
/// Executes `f` on `value` after replacing all escaping bound variables with placeholders
/// and then replaces these placeholders with the original bound variables in the result.
///
/// In most places, bound variables should be replaced right when entering a binder, making
/// this function unnecessary. However, normalization currently does not do that, so we have
/// to do this lazily.
///
/// You should not add any additional uses of this function, at least not without first
/// discussing it with t-types.
///
/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during
/// normalization as well, at which point this function will be unnecessary and can be removed.
pub fn with_replaced_escaping_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>, R: TypeFoldable<'tcx>>(
infcx: &'a InferCtxt<'a, 'tcx>,
universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
value: T,
f: impl FnOnce(T) -> R,
) -> R {
if value.has_escaping_bound_vars() {
let (value, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value);
let result = f(value);
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
universe_indices,
result,
)
} else {
f(value)
}
}
impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
/// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
/// use a binding level above `universe_indices.len()`, we fail. /// use a binding level above `universe_indices.len()`, we fail.

View file

@ -6,7 +6,7 @@ use crate::infer::at::At;
use crate::infer::canonical::OriginalQueryValues; use crate::infer::canonical::OriginalQueryValues;
use crate::infer::{InferCtxt, InferOk}; use crate::infer::{InferCtxt, InferOk};
use crate::traits::error_reporting::InferCtxtExt; use crate::traits::error_reporting::InferCtxtExt;
use crate::traits::project::needs_normalization; use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::sso::SsoHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::stack::ensure_sufficient_stack;
@ -283,11 +283,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
let tcx = self.infcx.tcx; let tcx = self.infcx.tcx;
let infcx = self.infcx; let infcx = self.infcx;
let (data, mapped_regions, mapped_types, mapped_consts) = let (data, mapped_regions, mapped_types, mapped_consts) =
crate::traits::project::BoundVarReplacer::replace_bound_vars( BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
infcx,
&mut self.universes,
data,
);
let data = data.try_fold_with(self)?; let data = data.try_fold_with(self)?;
let mut orig_values = OriginalQueryValues::default(); let mut orig_values = OriginalQueryValues::default();
@ -313,8 +309,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
debug!("QueryNormalizer: result = {:#?}", result); debug!("QueryNormalizer: result = {:#?}", result);
debug!("QueryNormalizer: obligations = {:#?}", obligations); debug!("QueryNormalizer: obligations = {:#?}", obligations);
self.obligations.extend(obligations); self.obligations.extend(obligations);
let res = PlaceholderReplacer::replace_placeholders(
let res = crate::traits::project::PlaceholderReplacer::replace_placeholders(
infcx, infcx,
mapped_regions, mapped_regions,
mapped_types, mapped_types,
@ -343,7 +338,13 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
constant: ty::Const<'tcx>, constant: ty::Const<'tcx>,
) -> Result<ty::Const<'tcx>, Self::Error> { ) -> Result<ty::Const<'tcx>, Self::Error> {
let constant = constant.try_super_fold_with(self)?; let constant = constant.try_super_fold_with(self)?;
Ok(constant.eval(self.infcx.tcx, self.param_env)) debug!(?constant, ?self.param_env);
Ok(crate::traits::project::with_replaced_escaping_bound_vars(
self.infcx,
&mut self.universes,
constant,
|constant| constant.eval(self.infcx.tcx, self.param_env),
))
} }
fn try_fold_mir_const( fn try_fold_mir_const(