Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce enter_forall to supercede instantiate_binder_with_placeholders #120544

Merged
merged 3 commits into from
Feb 8, 2024
Merged
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
19 changes: 10 additions & 9 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,21 +1032,22 @@ impl<'tcx> InferCtxt<'tcx> {
_ => {}
}

let ty::SubtypePredicate { a_is_expected, a, b } =
self.instantiate_binder_with_placeholders(predicate);

Ok(self.at(cause, param_env).sub_exp(DefineOpaqueTypes::No, a_is_expected, a, b))
self.enter_forall(predicate, |ty::SubtypePredicate { a_is_expected, a, b }| {
Ok(self.at(cause, param_env).sub_exp(DefineOpaqueTypes::No, a_is_expected, a, b))
})
}

pub fn region_outlives_predicate(
&self,
cause: &traits::ObligationCause<'tcx>,
predicate: ty::PolyRegionOutlivesPredicate<'tcx>,
) {
let ty::OutlivesPredicate(r_a, r_b) = self.instantiate_binder_with_placeholders(predicate);
let origin =
SubregionOrigin::from_obligation_cause(cause, || RelateRegionParamBound(cause.span));
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
RelateRegionParamBound(cause.span)
});
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
})
}

/// Number of type variables created so far.
Expand Down Expand Up @@ -1455,7 +1456,7 @@ impl<'tcx> InferCtxt<'tcx> {
// Use this method if you'd like to find some substitution of the binder's
// variables (e.g. during a method call). If there isn't a [`BoundRegionConversionTime`]
// that corresponds to your use case, consider whether or not you should
// use [`InferCtxt::instantiate_binder_with_placeholders`] instead.
// use [`InferCtxt::enter_forall`] instead.
pub fn instantiate_binder_with_fresh_vars<T>(
&self,
span: Span,
Expand Down
64 changes: 46 additions & 18 deletions compiler/rustc_infer/src/infer/relate/higher_ranked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,25 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> {
// First, we instantiate each bound region in the supertype with a
// fresh placeholder region. Note that this automatically creates
// a new universe if needed.
let sup_prime = self.infcx.instantiate_binder_with_placeholders(sup);
self.infcx.enter_forall(sup, |sup_prime| {
// Next, we instantiate each bound region in the subtype
// with a fresh region variable. These region variables --
// but no other preexisting region variables -- can name
// the placeholders.
let sub_prime =
self.infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, sub);
debug!("a_prime={:?}", sub_prime);
debug!("b_prime={:?}", sup_prime);

// Next, we instantiate each bound region in the subtype
// with a fresh region variable. These region variables --
// but no other preexisting region variables -- can name
// the placeholders.
let sub_prime = self.infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, sub);

debug!("a_prime={:?}", sub_prime);
debug!("b_prime={:?}", sup_prime);

// Compare types now that bound regions have been replaced.
let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime)?;

debug!("OK result={result:?}");
// NOTE: returning the result here would be dangerous as it contains
// placeholders which **must not** be named afterwards.
Ok(())
// Compare types now that bound regions have been replaced.
let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime);
if result.is_ok() {
debug!("OK result={result:?}");
}
// NOTE: returning the result here would be dangerous as it contains
// placeholders which **must not** be named afterwards.
result.map(|_| ())
})
}
}

Expand All @@ -68,9 +69,11 @@ impl<'tcx> InferCtxt<'tcx> {
/// This is the first step of checking subtyping when higher-ranked things are involved.
/// For more details visit the relevant sections of the [rustc dev guide].
///
/// `fn enter_forall` should be preferred over this method.
///
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
#[instrument(level = "debug", skip(self), ret)]
pub fn instantiate_binder_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T
pub fn enter_forall_and_leak_universe<T>(&self, binder: ty::Binder<'tcx, T>) -> T
where
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
{
Expand Down Expand Up @@ -106,6 +109,31 @@ impl<'tcx> InferCtxt<'tcx> {
self.tcx.replace_bound_vars_uncached(binder, delegate)
}

/// Replaces all bound variables (lifetimes, types, and constants) bound by
/// `binder` with placeholder variables in a new universe and then calls the
/// closure `f` with the instantiated value. The new placeholders can only be
/// named by inference variables created inside of the closure `f` or afterwards.
///
/// This is the first step of checking subtyping when higher-ranked things are involved.
/// For more details visit the relevant sections of the [rustc dev guide].
///
/// This method should be preferred over `fn enter_forall_and_leak_universe`.
///
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
#[instrument(level = "debug", skip(self, f))]
pub fn enter_forall<T, U>(&self, forall: ty::Binder<'tcx, T>, f: impl FnOnce(T) -> U) -> U
where
T: TypeFoldable<TyCtxt<'tcx>> + Copy,
{
// FIXME: currently we do nothing to prevent placeholders with the new universe being
// used after exiting `f`. For example region subtyping can result in outlives constraints
// that name placeholders created in this function. Nested goals from type relations can
// also contain placeholders created by this function.
let value = self.enter_forall_and_leak_universe(forall);
debug!("?value");
f(value)
}

/// See [RegionConstraintCollector::leak_check][1]. We only check placeholder
/// leaking into `outer_universe`, i.e. placeholders which cannot be named by that
/// universe.
Expand Down
97 changes: 50 additions & 47 deletions compiler/rustc_infer/src/infer/relate/nll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,52 +261,55 @@ where
Ok(a)
}

#[instrument(skip(self), level = "debug")]
fn instantiate_binder_with_placeholders<T>(&mut self, binder: ty::Binder<'tcx, T>) -> T
fn enter_forall<T, U>(
&mut self,
binder: ty::Binder<'tcx, T>,
f: impl FnOnce(&mut Self, T) -> U,
) -> U
where
T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy,
{
if let Some(inner) = binder.no_bound_vars() {
return inner;
}

let mut next_region = {
let nll_delegate = &mut self.delegate;
let mut lazy_universe = None;
let value = if let Some(inner) = binder.no_bound_vars() {
inner
} else {
let mut next_region = {
let nll_delegate = &mut self.delegate;
let mut lazy_universe = None;

move |br: ty::BoundRegion| {
// The first time this closure is called, create a
// new universe for the placeholders we will make
// from here out.
let universe = lazy_universe.unwrap_or_else(|| {
let universe = nll_delegate.create_next_universe();
lazy_universe = Some(universe);
universe
});

let placeholder = ty::PlaceholderRegion { universe, bound: br };
debug!(?placeholder);
let placeholder_reg = nll_delegate.next_placeholder_region(placeholder);
debug!(?placeholder_reg);

placeholder_reg
}
};

move |br: ty::BoundRegion| {
// The first time this closure is called, create a
// new universe for the placeholders we will make
// from here out.
let universe = lazy_universe.unwrap_or_else(|| {
let universe = nll_delegate.create_next_universe();
lazy_universe = Some(universe);
universe
});

let placeholder = ty::PlaceholderRegion { universe, bound: br };
debug!(?placeholder);
let placeholder_reg = nll_delegate.next_placeholder_region(placeholder);
debug!(?placeholder_reg);

placeholder_reg
}
};
let delegate = FnMutDelegate {
regions: &mut next_region,
types: &mut |_bound_ty: ty::BoundTy| {
unreachable!("we only replace regions in nll_relate, not types")
},
consts: &mut |_bound_var: ty::BoundVar, _ty| {
unreachable!("we only replace regions in nll_relate, not consts")
},
};

let delegate = FnMutDelegate {
regions: &mut next_region,
types: &mut |_bound_ty: ty::BoundTy| {
unreachable!("we only replace regions in nll_relate, not types")
},
consts: &mut |_bound_var: ty::BoundVar, _ty| {
unreachable!("we only replace regions in nll_relate, not consts")
},
self.infcx.tcx.replace_bound_vars_uncached(binder, delegate)
};

let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate);
debug!(?replaced);

replaced
debug!(?value);
f(self, value)
}

#[instrument(skip(self), level = "debug")]
Expand Down Expand Up @@ -630,10 +633,10 @@ where

// Note: the order here is important. Create the placeholders first, otherwise
// we assign the wrong universe to the existential!
let b_replaced = self.instantiate_binder_with_placeholders(b);
let a_replaced = self.instantiate_binder_with_existentials(a);

self.relate(a_replaced, b_replaced)?;
self.enter_forall(b, |this, b| {
let a = this.instantiate_binder_with_existentials(a);
this.relate(a, b)
})?;

self.ambient_variance = variance;
}
Expand All @@ -650,10 +653,10 @@ where
let variance =
std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);

let a_replaced = self.instantiate_binder_with_placeholders(a);
let b_replaced = self.instantiate_binder_with_existentials(b);

self.relate(a_replaced, b_replaced)?;
self.enter_forall(a, |this, a| {
let b = this.instantiate_binder_with_existentials(b);
this.relate(a, b)
})?;

self.ambient_variance = variance;
}
Expand Down
Loading
Loading