mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
rename skolemized to placeholder
This commit is contained in:
parent
a96510d066
commit
4cd4eae435
@ -131,7 +131,7 @@ for ty::RegionKind {
|
||||
}
|
||||
ty::ReLateBound(..) |
|
||||
ty::ReVar(..) |
|
||||
ty::ReSkolemized(..) => {
|
||||
ty::RePlaceholder(..) => {
|
||||
bug!("StableHasher: unexpected region {:?}", *self)
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx>
|
||||
ty::ReEarlyBound(..)
|
||||
| ty::ReFree(_)
|
||||
| ty::ReScope(_)
|
||||
| ty::ReSkolemized(..)
|
||||
| ty::RePlaceholder(..)
|
||||
| ty::ReEmpty
|
||||
| ty::ReErased => {
|
||||
if self.canonicalize_region_mode.other_free_regions {
|
||||
|
@ -458,9 +458,10 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
|
||||
return Ok(r);
|
||||
}
|
||||
|
||||
// Always make a fresh region variable for skolemized regions;
|
||||
// the higher-ranked decision procedures rely on this.
|
||||
ty::ReSkolemized(..) => { }
|
||||
// Always make a fresh region variable for placeholder
|
||||
// regions; the higher-ranked decision procedures rely on
|
||||
// this.
|
||||
ty::RePlaceholder(..) => { }
|
||||
|
||||
// For anything else, we make a region variable, unless we
|
||||
// are *equating*, in which case it's just wasteful.
|
||||
|
@ -142,12 +142,12 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
||||
|
||||
ty::ReEmpty => ("the empty lifetime".to_owned(), None),
|
||||
|
||||
// FIXME(#13998) ReSkolemized should probably print like
|
||||
// FIXME(#13998) RePlaceholder should probably print like
|
||||
// ReFree rather than dumping Debug output on the user.
|
||||
//
|
||||
// We shouldn't really be having unification failures with ReVar
|
||||
// and ReLateBound though.
|
||||
ty::ReSkolemized(..) | ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
|
||||
ty::RePlaceholder(..) | ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
|
||||
(format!("lifetime {:?}", region), None)
|
||||
}
|
||||
|
||||
|
@ -107,7 +107,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
|
||||
ty::ReFree(_) |
|
||||
ty::ReScope(_) |
|
||||
ty::ReVar(_) |
|
||||
ty::ReSkolemized(..) |
|
||||
ty::RePlaceholder(..) |
|
||||
ty::ReEmpty |
|
||||
ty::ReErased => {
|
||||
// replace all free regions with 'erased
|
||||
|
@ -72,11 +72,11 @@ the same lifetime, but not the reverse.
|
||||
Here is the algorithm we use to perform the subtyping check:
|
||||
|
||||
1. Replace all bound regions in the subtype with new variables
|
||||
2. Replace all bound regions in the supertype with skolemized
|
||||
equivalents. A "skolemized" region is just a new fresh region
|
||||
2. Replace all bound regions in the supertype with placeholder
|
||||
equivalents. A "placeholder" region is just a new fresh region
|
||||
name.
|
||||
3. Check that the parameter and return types match as normal
|
||||
4. Ensure that no skolemized regions 'leak' into region variables
|
||||
4. Ensure that no placeholder regions 'leak' into region variables
|
||||
visible from "the outside"
|
||||
|
||||
Let's walk through some examples and see how this algorithm plays out.
|
||||
@ -95,7 +95,7 @@ like so:
|
||||
Here the upper case `&A` indicates a *region variable*, that is, a
|
||||
region whose value is being inferred by the system. I also replaced
|
||||
`&b` with `&x`---I'll use letters late in the alphabet (`x`, `y`, `z`)
|
||||
to indicate skolemized region names. We can assume they don't appear
|
||||
to indicate placeholder region names. We can assume they don't appear
|
||||
elsewhere. Note that neither the sub- nor the supertype bind any
|
||||
region names anymore (as indicated by the absence of `<` and `>`).
|
||||
|
||||
@ -181,15 +181,15 @@ the first example, you had two functions:
|
||||
for<'a> fn(&'a T) <: for<'b> fn(&'b T)
|
||||
|
||||
and hence `&A` and `&x` were created "together". In general, the
|
||||
intention of the skolemized names is that they are supposed to be
|
||||
intention of the placeholder names is that they are supposed to be
|
||||
fresh names that could never be equal to anything from the outside.
|
||||
But when inference comes into play, we might not be respecting this
|
||||
rule.
|
||||
|
||||
So the way we solve this is to add a fourth step that examines the
|
||||
constraints that refer to skolemized names. Basically, consider a
|
||||
constraints that refer to placeholder names. Basically, consider a
|
||||
non-directed version of the constraint graph. Let `Tainted(x)` be the
|
||||
set of all things reachable from a skolemized variable `x`.
|
||||
set of all things reachable from a placeholder variable `x`.
|
||||
`Tainted(x)` should not contain any regions that existed before the
|
||||
step at which the skolemization was performed. So this case here
|
||||
would fail because `&x` was created alone, but is relatable to `&A`.
|
||||
|
@ -15,7 +15,7 @@ use super::{CombinedSnapshot,
|
||||
InferCtxt,
|
||||
HigherRankedType,
|
||||
SubregionOrigin,
|
||||
SkolemizationMap};
|
||||
PlaceholderMap};
|
||||
use super::combine::CombineFields;
|
||||
use super::region_constraints::{TaintDirections};
|
||||
|
||||
@ -61,8 +61,8 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
|
||||
// Second, we instantiate each bound region in the supertype with a
|
||||
// fresh concrete region.
|
||||
let (b_prime, skol_map) =
|
||||
self.infcx.skolemize_late_bound_regions(b);
|
||||
let (b_prime, placeholder_map) =
|
||||
self.infcx.replace_late_bound_regions_with_placeholders(b);
|
||||
|
||||
debug!("a_prime={:?}", a_prime);
|
||||
debug!("b_prime={:?}", b_prime);
|
||||
@ -71,12 +71,12 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
let result = self.sub(a_is_expected).relate(&a_prime, &b_prime)?;
|
||||
|
||||
// Presuming type comparison succeeds, we need to check
|
||||
// that the skolemized regions do not "leak".
|
||||
self.infcx.leak_check(!a_is_expected, span, &skol_map, snapshot)?;
|
||||
// that the placeholder regions do not "leak".
|
||||
self.infcx.leak_check(!a_is_expected, span, &placeholder_map, snapshot)?;
|
||||
|
||||
// We are finished with the skolemized regions now so pop
|
||||
// We are finished with the placeholder regions now so pop
|
||||
// them off.
|
||||
self.infcx.pop_skolemized(skol_map, snapshot);
|
||||
self.infcx.pop_placeholders(placeholder_map, snapshot);
|
||||
|
||||
debug!("higher_ranked_sub: OK result={:?}", result);
|
||||
|
||||
@ -112,21 +112,21 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
// created as part of this type comparison".
|
||||
return self.infcx.commit_if_ok(|snapshot| {
|
||||
// First, we instantiate each bound region in the matcher
|
||||
// with a skolemized region.
|
||||
let ((a_match, a_value), skol_map) =
|
||||
self.infcx.skolemize_late_bound_regions(a_pair);
|
||||
// with a placeholder region.
|
||||
let ((a_match, a_value), placeholder_map) =
|
||||
self.infcx.replace_late_bound_regions_with_placeholders(a_pair);
|
||||
|
||||
debug!("higher_ranked_match: a_match={:?}", a_match);
|
||||
debug!("higher_ranked_match: skol_map={:?}", skol_map);
|
||||
debug!("higher_ranked_match: placeholder_map={:?}", placeholder_map);
|
||||
|
||||
// Equate types now that bound regions have been replaced.
|
||||
self.equate(a_is_expected).relate(&a_match, &b_match)?;
|
||||
|
||||
// Map each skolemized region to a vector of other regions that it
|
||||
// Map each placeholder region to a vector of other regions that it
|
||||
// must be equated with. (Note that this vector may include other
|
||||
// skolemized regions from `skol_map`.)
|
||||
// placeholder regions from `placeholder_map`.)
|
||||
let skol_resolution_map: FxHashMap<_, _> =
|
||||
skol_map
|
||||
placeholder_map
|
||||
.iter()
|
||||
.map(|(&br, &skol)| {
|
||||
let tainted_regions =
|
||||
@ -134,7 +134,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
skol,
|
||||
TaintDirections::incoming()); // [1]
|
||||
|
||||
// [1] this routine executes after the skolemized
|
||||
// [1] this routine executes after the placeholder
|
||||
// regions have been *equated* with something
|
||||
// else, so examining the incoming edges ought to
|
||||
// be enough to collect all constraints
|
||||
@ -143,9 +143,9 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
// For each skolemized region, pick a representative -- which can
|
||||
// For each placeholder region, pick a representative -- which can
|
||||
// be any region from the sets above, except for other members of
|
||||
// `skol_map`. There should always be a representative if things
|
||||
// `placeholder_map`. There should always be a representative if things
|
||||
// are properly well-formed.
|
||||
let skol_representatives: FxHashMap<_, _> =
|
||||
skol_resolution_map
|
||||
@ -184,7 +184,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the skolemized regions appearing in value with
|
||||
// Replace the placeholder regions appearing in value with
|
||||
// their representatives
|
||||
let a_value =
|
||||
fold_regions_in(
|
||||
@ -194,8 +194,8 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
|
||||
|
||||
debug!("higher_ranked_match: value={:?}", a_value);
|
||||
|
||||
// We are now done with these skolemized variables.
|
||||
self.infcx.pop_skolemized(skol_map, snapshot);
|
||||
// We are now done with these placeholder variables.
|
||||
self.infcx.pop_placeholders(placeholder_map, snapshot);
|
||||
|
||||
Ok(HrMatchResult { value: a_value })
|
||||
});
|
||||
@ -500,7 +500,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
* started. This is used in the sub/lub/glb computations. The
|
||||
* idea here is that when we are computing lub/glb of two
|
||||
* regions, we sometimes create intermediate region variables.
|
||||
* Those region variables may touch some of the skolemized or
|
||||
* Those region variables may touch some of the placeholder or
|
||||
* other "forbidden" regions we created to replace bound
|
||||
* regions, but they don't really represent an "external"
|
||||
* constraint.
|
||||
@ -527,10 +527,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
* we're not careful, it will succeed.
|
||||
*
|
||||
* The reason is that when we walk through the subtyping
|
||||
* algorithm, we begin by replacing `'a` with a skolemized
|
||||
* algorithm, we begin by replacing `'a` with a placeholder
|
||||
* variable `'1`. We then have `fn(_#0t) <: fn(&'1 int)`. This
|
||||
* can be made true by unifying `_#0t` with `&'1 int`. In the
|
||||
* process, we create a fresh variable for the skolemized
|
||||
* process, we create a fresh variable for the placeholder
|
||||
* region, `'$2`, and hence we have that `_#0t == &'$2
|
||||
* int`. However, because `'$2` was created during the sub
|
||||
* computation, if we're not careful we will erroneously
|
||||
@ -568,14 +568,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
region_vars
|
||||
}
|
||||
|
||||
/// Replace all regions bound by `binder` with skolemized regions and
|
||||
/// Replace all regions bound by `binder` with placeholder regions and
|
||||
/// return a map indicating which bound-region was replaced with what
|
||||
/// skolemized region. This is the first step of checking subtyping
|
||||
/// placeholder region. This is the first step of checking subtyping
|
||||
/// when higher-ranked things are involved.
|
||||
///
|
||||
/// **Important:** you must call this function from within a snapshot.
|
||||
/// Moreover, before committing the snapshot, you must eventually call
|
||||
/// either `plug_leaks` or `pop_skolemized` to remove the skolemized
|
||||
/// either `plug_leaks` or `pop_placeholders` to remove the placeholder
|
||||
/// regions. If you rollback the snapshot (or are using a probe), then
|
||||
/// the pop occurs as part of the rollback, so an explicit call is not
|
||||
/// needed (but is also permitted).
|
||||
@ -584,14 +584,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
/// the [rustc guide].
|
||||
///
|
||||
/// [rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/traits/hrtb.html
|
||||
pub fn skolemize_late_bound_regions<T>(&self,
|
||||
pub fn replace_late_bound_regions_with_placeholders<T>(&self,
|
||||
binder: &ty::Binder<T>)
|
||||
-> (T, SkolemizationMap<'tcx>)
|
||||
-> (T, PlaceholderMap<'tcx>)
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| {
|
||||
self.universe.set(self.universe().subuniverse());
|
||||
self.tcx.mk_region(ty::ReSkolemized(self.universe(), br))
|
||||
self.tcx.mk_region(ty::RePlaceholder(self.universe(), br))
|
||||
});
|
||||
|
||||
debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})",
|
||||
@ -603,19 +603,19 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
/// Searches the region constraints created since `snapshot` was started
|
||||
/// and checks to determine whether any of the skolemized regions created
|
||||
/// in `skol_map` would "escape" -- meaning that they are related to
|
||||
/// and checks to determine whether any of the placeholder regions created
|
||||
/// in `placeholder_map` would "escape" -- meaning that they are related to
|
||||
/// other regions in some way. If so, the higher-ranked subtyping doesn't
|
||||
/// hold. See `README.md` for more details.
|
||||
pub fn leak_check(&self,
|
||||
overly_polymorphic: bool,
|
||||
_span: Span,
|
||||
skol_map: &SkolemizationMap<'tcx>,
|
||||
placeholder_map: &PlaceholderMap<'tcx>,
|
||||
snapshot: &CombinedSnapshot<'a, 'tcx>)
|
||||
-> RelateResult<'tcx, ()>
|
||||
{
|
||||
debug!("leak_check: skol_map={:?}",
|
||||
skol_map);
|
||||
debug!("leak_check: placeholder_map={:?}",
|
||||
placeholder_map);
|
||||
|
||||
// If the user gave `-Zno-leak-check`, then skip the leak
|
||||
// check completely. This is wildly unsound and also not
|
||||
@ -630,14 +630,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
let new_vars = self.region_vars_confined_to_snapshot(snapshot);
|
||||
for (&skol_br, &skol) in skol_map {
|
||||
// The inputs to a skolemized variable can only
|
||||
for (&skol_br, &skol) in placeholder_map {
|
||||
// The inputs to a placeholder variable can only
|
||||
// be itself or other new variables.
|
||||
let incoming_taints = self.tainted_regions(snapshot,
|
||||
skol,
|
||||
TaintDirections::both());
|
||||
for &tainted_region in &incoming_taints {
|
||||
// Each skolemized should only be relatable to itself
|
||||
// Each placeholder should only be relatable to itself
|
||||
// or new variables:
|
||||
match *tainted_region {
|
||||
ty::ReVar(vid) => {
|
||||
@ -668,9 +668,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This code converts from skolemized regions back to late-bound
|
||||
/// This code converts from placeholder regions back to late-bound
|
||||
/// regions. It works by replacing each region in the taint set of a
|
||||
/// skolemized region with a bound-region. The bound region will be bound
|
||||
/// placeholder region with a bound-region. The bound region will be bound
|
||||
/// by the outer-most binder in `value`; the caller must ensure that there is
|
||||
/// such a binder and it is the right place.
|
||||
///
|
||||
@ -687,7 +687,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
/// where A : Clone
|
||||
/// { ... }
|
||||
///
|
||||
/// Here we will have replaced `'a` with a skolemized region
|
||||
/// Here we will have replaced `'a` with a placeholder region
|
||||
/// `'0`. This means that our substitution will be `{A=>&'0
|
||||
/// int, R=>&'0 int}`.
|
||||
///
|
||||
@ -697,25 +697,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
/// to the depth of the predicate, in this case 1, so that the final
|
||||
/// predicate is `for<'a> &'a int : Clone`.
|
||||
pub fn plug_leaks<T>(&self,
|
||||
skol_map: SkolemizationMap<'tcx>,
|
||||
placeholder_map: PlaceholderMap<'tcx>,
|
||||
snapshot: &CombinedSnapshot<'a, 'tcx>,
|
||||
value: T) -> T
|
||||
where T : TypeFoldable<'tcx>
|
||||
{
|
||||
debug!("plug_leaks(skol_map={:?}, value={:?})",
|
||||
skol_map,
|
||||
debug!("plug_leaks(placeholder_map={:?}, value={:?})",
|
||||
placeholder_map,
|
||||
value);
|
||||
|
||||
if skol_map.is_empty() {
|
||||
if placeholder_map.is_empty() {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Compute a mapping from the "taint set" of each skolemized
|
||||
// Compute a mapping from the "taint set" of each placeholder
|
||||
// region back to the `ty::BoundRegion` that it originally
|
||||
// represented. Because `leak_check` passed, we know that
|
||||
// these taint sets are mutually disjoint.
|
||||
let inv_skol_map: FxHashMap<ty::Region<'tcx>, ty::BoundRegion> =
|
||||
skol_map
|
||||
let inv_placeholder_map: FxHashMap<ty::Region<'tcx>, ty::BoundRegion> =
|
||||
placeholder_map
|
||||
.iter()
|
||||
.flat_map(|(&skol_br, &skol)| {
|
||||
self.tainted_regions(snapshot, skol, TaintDirections::both())
|
||||
@ -724,8 +724,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
debug!("plug_leaks: inv_skol_map={:?}",
|
||||
inv_skol_map);
|
||||
debug!("plug_leaks: inv_placeholder_map={:?}",
|
||||
inv_placeholder_map);
|
||||
|
||||
// Remove any instantiated type variables from `value`; those can hide
|
||||
// references to regions from the `fold_regions` code below.
|
||||
@ -737,25 +737,25 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
// responsible for ensuring that (a) `value` contains at least one
|
||||
// binder and (b) that binder is the one we want to use.
|
||||
let result = self.tcx.fold_regions(&value, &mut false, |r, current_depth| {
|
||||
match inv_skol_map.get(&r) {
|
||||
match inv_placeholder_map.get(&r) {
|
||||
None => r,
|
||||
Some(br) => {
|
||||
// It is the responsibility of the caller to ensure
|
||||
// that each skolemized region appears within a
|
||||
// that each placeholder region appears within a
|
||||
// binder. In practice, this routine is only used by
|
||||
// trait checking, and all of the skolemized regions
|
||||
// trait checking, and all of the placeholder regions
|
||||
// appear inside predicates, which always have
|
||||
// binders, so this assert is satisfied.
|
||||
assert!(current_depth > ty::INNERMOST);
|
||||
|
||||
// since leak-check passed, this skolemized region
|
||||
// since leak-check passed, this placeholder region
|
||||
// should only have incoming edges from variables
|
||||
// (which ought not to escape the snapshot, but we
|
||||
// don't check that) or itself
|
||||
assert!(
|
||||
match *r {
|
||||
ty::ReVar(_) => true,
|
||||
ty::ReSkolemized(_, ref br1) => br == br1,
|
||||
ty::RePlaceholder(_, ref br1) => br == br1,
|
||||
_ => false,
|
||||
},
|
||||
"leak-check would have us replace {:?} with {:?}",
|
||||
@ -769,31 +769,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
}
|
||||
});
|
||||
|
||||
self.pop_skolemized(skol_map, snapshot);
|
||||
self.pop_placeholders(placeholder_map, snapshot);
|
||||
|
||||
debug!("plug_leaks: result={:?}", result);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Pops the skolemized regions found in `skol_map` from the region
|
||||
/// inference context. Whenever you create skolemized regions via
|
||||
/// `skolemize_late_bound_regions`, they must be popped before you
|
||||
/// Pops the placeholder regions found in `placeholder_map` from the region
|
||||
/// inference context. Whenever you create placeholder regions via
|
||||
/// `replace_late_bound_regions_with_placeholders`, they must be popped before you
|
||||
/// commit the enclosing snapshot (if you do not commit, e.g. within a
|
||||
/// probe or as a result of an error, then this is not necessary, as
|
||||
/// popping happens as part of the rollback).
|
||||
///
|
||||
/// Note: popping also occurs implicitly as part of `leak_check`.
|
||||
pub fn pop_skolemized(&self,
|
||||
skol_map: SkolemizationMap<'tcx>,
|
||||
snapshot: &CombinedSnapshot<'a, 'tcx>) {
|
||||
debug!("pop_skolemized({:?})", skol_map);
|
||||
let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect();
|
||||
pub fn pop_placeholders(
|
||||
&self,
|
||||
placeholder_map: PlaceholderMap<'tcx>,
|
||||
snapshot: &CombinedSnapshot<'a, 'tcx>,
|
||||
) {
|
||||
debug!("pop_placeholders({:?})", placeholder_map);
|
||||
let skol_regions: FxHashSet<_> = placeholder_map.values().cloned().collect();
|
||||
self.borrow_region_constraints()
|
||||
.pop_skolemized(self.universe(), &skol_regions, &snapshot.region_constraints_snapshot);
|
||||
.pop_placeholders(
|
||||
self.universe(),
|
||||
&skol_regions,
|
||||
&snapshot.region_constraints_snapshot,
|
||||
);
|
||||
self.universe.set(snapshot.universe);
|
||||
if !skol_map.is_empty() {
|
||||
self.projection_cache.borrow_mut().rollback_skolemized(
|
||||
if !placeholder_map.is_empty() {
|
||||
self.projection_cache.borrow_mut().rollback_placeholder(
|
||||
&snapshot.projection_cache_snapshot);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use std::u32;
|
||||
use ty::fold::TypeFoldable;
|
||||
use ty::{self, Ty, TyCtxt};
|
||||
use ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic};
|
||||
use ty::{ReLateBound, ReScope, ReSkolemized, ReVar};
|
||||
use ty::{ReLateBound, ReScope, RePlaceholder, ReVar};
|
||||
use ty::{Region, RegionVid};
|
||||
|
||||
mod graphviz;
|
||||
@ -341,7 +341,7 @@ impl<'cx, 'gcx, 'tcx> LexicalResolver<'cx, 'gcx, 'tcx> {
|
||||
|
||||
// For these types, we cannot define any additional
|
||||
// relationship:
|
||||
(&ReSkolemized(..), _) | (_, &ReSkolemized(..)) => if a == b {
|
||||
(&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => if a == b {
|
||||
a
|
||||
} else {
|
||||
tcx.types.re_static
|
||||
|
@ -229,9 +229,10 @@ pub struct InferCtxt<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
||||
universe: Cell<ty::UniverseIndex>,
|
||||
}
|
||||
|
||||
/// A map returned by `skolemize_late_bound_regions()` indicating the skolemized
|
||||
/// region that each late-bound region was replaced with.
|
||||
pub type SkolemizationMap<'tcx> = BTreeMap<ty::BoundRegion, ty::Region<'tcx>>;
|
||||
/// A map returned by `replace_late_bound_regions_with_placeholders()`
|
||||
/// indicating the placeholder region that each late-bound region was
|
||||
/// replaced with.
|
||||
pub type PlaceholderMap<'tcx> = BTreeMap<ty::BoundRegion, ty::Region<'tcx>>;
|
||||
|
||||
/// See `error_reporting` module for more details
|
||||
#[derive(Clone, Debug)]
|
||||
@ -913,13 +914,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
a,
|
||||
b,
|
||||
},
|
||||
skol_map,
|
||||
) = self.skolemize_late_bound_regions(predicate);
|
||||
placeholder_map,
|
||||
) = self.replace_late_bound_regions_with_placeholders(predicate);
|
||||
|
||||
let cause_span = cause.span;
|
||||
let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?;
|
||||
self.leak_check(false, cause_span, &skol_map, snapshot)?;
|
||||
self.pop_skolemized(skol_map, snapshot);
|
||||
self.leak_check(false, cause_span, &placeholder_map, snapshot)?;
|
||||
self.pop_placeholders(placeholder_map, snapshot);
|
||||
Ok(ok.unit())
|
||||
}))
|
||||
}
|
||||
@ -930,14 +931,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
||||
predicate: &ty::PolyRegionOutlivesPredicate<'tcx>,
|
||||
) -> UnitResult<'tcx> {
|
||||
self.commit_if_ok(|snapshot| {
|
||||
let (ty::OutlivesPredicate(r_a, r_b), skol_map) =
|
||||
self.skolemize_late_bound_regions(predicate);
|
||||
let (ty::OutlivesPredicate(r_a, r_b), placeholder_map) =
|
||||
self.replace_late_bound_regions_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.leak_check(false, cause.span, &skol_map, snapshot)?;
|
||||
Ok(self.pop_skolemized(skol_map, snapshot))
|
||||
self.leak_check(false, cause.span, &placeholder_map, snapshot)?;
|
||||
Ok(self.pop_placeholders(placeholder_map, snapshot))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -306,9 +306,9 @@ pub struct RegionSnapshot {
|
||||
any_unifications: bool,
|
||||
}
|
||||
|
||||
/// When working with skolemized regions, we often wish to find all of
|
||||
/// the regions that are either reachable from a skolemized region, or
|
||||
/// which can reach a skolemized region, or both. We call such regions
|
||||
/// When working with placeholder regions, we often wish to find all of
|
||||
/// the regions that are either reachable from a placeholder region, or
|
||||
/// which can reach a placeholder region, or both. We call such regions
|
||||
/// *tained* regions. This struct allows you to decide what set of
|
||||
/// tainted regions you want.
|
||||
#[derive(Debug)]
|
||||
@ -527,23 +527,23 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
|
||||
self.var_infos[vid].origin
|
||||
}
|
||||
|
||||
/// Removes all the edges to/from the skolemized regions that are
|
||||
/// Removes all the edges to/from the placeholder regions that are
|
||||
/// in `skols`. This is used after a higher-ranked operation
|
||||
/// completes to remove all trace of the skolemized regions
|
||||
/// completes to remove all trace of the placeholder regions
|
||||
/// created in that time.
|
||||
pub fn pop_skolemized(
|
||||
pub fn pop_placeholders(
|
||||
&mut self,
|
||||
skolemization_count: ty::UniverseIndex,
|
||||
skols: &FxHashSet<ty::Region<'tcx>>,
|
||||
snapshot: &RegionSnapshot,
|
||||
) {
|
||||
debug!("pop_skolemized_regions(skols={:?})", skols);
|
||||
debug!("pop_placeholders(skols={:?})", skols);
|
||||
|
||||
assert!(self.in_snapshot());
|
||||
assert!(self.undo_log[snapshot.length] == OpenSnapshot);
|
||||
assert!(
|
||||
skolemization_count.as_usize() >= skols.len(),
|
||||
"popping more skolemized variables than actually exist, \
|
||||
"popping more placeholder variables than actually exist, \
|
||||
sc now = {:?}, skols.len = {:?}",
|
||||
skolemization_count,
|
||||
skols.len()
|
||||
@ -555,7 +555,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
|
||||
debug_assert! {
|
||||
skols.iter()
|
||||
.all(|&k| match *k {
|
||||
ty::ReSkolemized(universe, _) =>
|
||||
ty::RePlaceholder(universe, _) =>
|
||||
universe >= first_to_pop &&
|
||||
universe < last_to_pop,
|
||||
_ =>
|
||||
@ -860,7 +860,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
|
||||
ty::ReErased |
|
||||
ty::ReFree(..) |
|
||||
ty::ReEarlyBound(..) => ty::UniverseIndex::ROOT,
|
||||
ty::ReSkolemized(universe, _) => universe,
|
||||
ty::RePlaceholder(universe, _) => universe,
|
||||
ty::ReClosureBound(vid) |
|
||||
ty::ReVar(vid) => self.var_universe(vid),
|
||||
ty::ReLateBound(..) =>
|
||||
@ -886,7 +886,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
|
||||
/// relations are considered. For example, one can say that only
|
||||
/// "incoming" edges to `r0` are desired, in which case one will
|
||||
/// get the set of regions `{r|r <= r0}`. This is used when
|
||||
/// checking whether skolemized regions are being improperly
|
||||
/// checking whether placeholder regions are being improperly
|
||||
/// related to other regions.
|
||||
pub fn tainted(
|
||||
&self,
|
||||
|
@ -117,7 +117,7 @@ fn overlap<'cx, 'gcx, 'tcx>(selcx: &mut SelectionContext<'cx, 'gcx, 'tcx>,
|
||||
{
|
||||
debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id);
|
||||
|
||||
// For the purposes of this check, we don't bring any skolemized
|
||||
// For the purposes of this check, we don't bring any placeholder
|
||||
// types into scope; instead, we replace the generic types with
|
||||
// fresh type variables, and hence we do our evaluations in an
|
||||
// empty environment.
|
||||
|
@ -206,15 +206,15 @@ pub fn poly_project_and_unify_type<'cx, 'gcx, 'tcx>(
|
||||
|
||||
let infcx = selcx.infcx();
|
||||
infcx.commit_if_ok(|snapshot| {
|
||||
let (skol_predicate, skol_map) =
|
||||
infcx.skolemize_late_bound_regions(&obligation.predicate);
|
||||
let (skol_predicate, placeholder_map) =
|
||||
infcx.replace_late_bound_regions_with_placeholders(&obligation.predicate);
|
||||
|
||||
let skol_obligation = obligation.with(skol_predicate);
|
||||
let r = match project_and_unify_type(selcx, &skol_obligation) {
|
||||
Ok(result) => {
|
||||
let span = obligation.cause.span;
|
||||
match infcx.leak_check(false, span, &skol_map, snapshot) {
|
||||
Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, result)),
|
||||
match infcx.leak_check(false, span, &placeholder_map, snapshot) {
|
||||
Ok(()) => Ok(infcx.plug_leaks(placeholder_map, snapshot, result)),
|
||||
Err(e) => {
|
||||
debug!("poly_project_and_unify_type: leak check encountered error {:?}", e);
|
||||
Err(MismatchedProjectionTypes { err: e })
|
||||
@ -1571,11 +1571,11 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
|
||||
|
||||
// # Cache
|
||||
|
||||
/// The projection cache. Unlike the standard caches, this can
|
||||
/// include infcx-dependent type variables - therefore, we have to roll
|
||||
/// the cache back each time we roll a snapshot back, to avoid assumptions
|
||||
/// on yet-unresolved inference variables. Types with skolemized regions
|
||||
/// also have to be removed when the respective snapshot ends.
|
||||
/// The projection cache. Unlike the standard caches, this can include
|
||||
/// infcx-dependent type variables - therefore, we have to roll the
|
||||
/// cache back each time we roll a snapshot back, to avoid assumptions
|
||||
/// on yet-unresolved inference variables. Types with placeholder
|
||||
/// regions also have to be removed when the respective snapshot ends.
|
||||
///
|
||||
/// Because of that, projection cache entries can be "stranded" and left
|
||||
/// inaccessible when type variables inside the key are resolved. We make no
|
||||
@ -1661,7 +1661,7 @@ impl<'tcx> ProjectionCache<'tcx> {
|
||||
self.map.rollback_to(&snapshot.snapshot);
|
||||
}
|
||||
|
||||
pub fn rollback_skolemized(&mut self, snapshot: &ProjectionCacheSnapshot) {
|
||||
pub fn rollback_placeholder(&mut self, snapshot: &ProjectionCacheSnapshot) {
|
||||
self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_skol());
|
||||
}
|
||||
|
||||
|
@ -62,11 +62,11 @@ use util::nodemap::{FxHashMap, FxHashSet};
|
||||
pub struct SelectionContext<'cx, 'gcx: 'cx + 'tcx, 'tcx: 'cx> {
|
||||
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
|
||||
|
||||
/// Freshener used specifically for skolemizing entries on the
|
||||
/// obligation stack. This ensures that all entries on the stack
|
||||
/// at one time will have the same set of skolemized entries,
|
||||
/// which is important for checking for trait bounds that
|
||||
/// recursively require themselves.
|
||||
/// Freshener used specifically for entries on the obligation
|
||||
/// stack. This ensures that all entries on the stack at one time
|
||||
/// will have the same set of placeholder entries, which is
|
||||
/// important for checking for trait bounds that recursively
|
||||
/// require themselves.
|
||||
freshener: TypeFreshener<'cx, 'gcx, 'tcx>,
|
||||
|
||||
/// If true, indicates that the evaluation should be conservative
|
||||
@ -159,7 +159,7 @@ impl IntercrateAmbiguityCause {
|
||||
struct TraitObligationStack<'prev, 'tcx: 'prev> {
|
||||
obligation: &'prev TraitObligation<'tcx>,
|
||||
|
||||
/// Trait ref from `obligation` but skolemized with the
|
||||
/// Trait ref from `obligation` but "freshened" with the
|
||||
/// selection-context's freshener. Used to check for recursion.
|
||||
fresh_trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
|
||||
@ -1008,11 +1008,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
// `Send`.
|
||||
//
|
||||
// Note that we do this comparison using the `fresh_trait_ref`
|
||||
// fields. Because these have all been skolemized using
|
||||
// fields. Because these have all been freshened using
|
||||
// `self.freshener`, we can be sure that (a) this will not
|
||||
// affect the inferencer state and (b) that if we see two
|
||||
// skolemized types with the same index, they refer to the
|
||||
// same unbound type variable.
|
||||
// fresh regions with the same index, they refer to the same
|
||||
// unbound type variable.
|
||||
if let Some(rec_index) = stack.iter()
|
||||
.skip(1) // skip top-most frame
|
||||
.position(|prev| stack.obligation.param_env == prev.obligation.param_env &&
|
||||
@ -1191,9 +1191,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
// Check the cache. Note that we skolemize the trait-ref
|
||||
// separately rather than using `stack.fresh_trait_ref` -- this
|
||||
// is because we want the unbound variables to be replaced
|
||||
// with fresh skolemized types starting from index 0.
|
||||
// separately rather than using `stack.fresh_trait_ref` --
|
||||
// this is because we want the unbound variables to be
|
||||
// replaced with fresh types starting from index 0.
|
||||
let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate.clone());
|
||||
debug!(
|
||||
"candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})",
|
||||
@ -1687,12 +1687,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
) -> bool {
|
||||
let poly_trait_predicate = self.infcx()
|
||||
.resolve_type_vars_if_possible(&obligation.predicate);
|
||||
let (skol_trait_predicate, skol_map) = self.infcx()
|
||||
.skolemize_late_bound_regions(&poly_trait_predicate);
|
||||
let (skol_trait_predicate, placeholder_map) = self.infcx()
|
||||
.replace_late_bound_regions_with_placeholders(&poly_trait_predicate);
|
||||
debug!(
|
||||
"match_projection_obligation_against_definition_bounds: \
|
||||
skol_trait_predicate={:?} skol_map={:?}",
|
||||
skol_trait_predicate, skol_map
|
||||
skol_trait_predicate={:?} placeholder_map={:?}",
|
||||
skol_trait_predicate, placeholder_map
|
||||
);
|
||||
|
||||
let (def_id, substs) = match skol_trait_predicate.trait_ref.self_ty().sty {
|
||||
@ -1729,7 +1729,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
obligation,
|
||||
bound.clone(),
|
||||
skol_trait_predicate.trait_ref.clone(),
|
||||
&skol_map,
|
||||
&placeholder_map,
|
||||
snapshot,
|
||||
)
|
||||
})
|
||||
@ -1748,11 +1748,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
obligation,
|
||||
bound,
|
||||
skol_trait_predicate.trait_ref.clone(),
|
||||
&skol_map,
|
||||
&placeholder_map,
|
||||
snapshot,
|
||||
);
|
||||
|
||||
self.infcx.pop_skolemized(skol_map, snapshot);
|
||||
self.infcx.pop_placeholders(placeholder_map, snapshot);
|
||||
|
||||
assert!(result);
|
||||
true
|
||||
@ -1765,7 +1765,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
obligation: &TraitObligation<'tcx>,
|
||||
trait_bound: ty::PolyTraitRef<'tcx>,
|
||||
skol_trait_ref: ty::TraitRef<'tcx>,
|
||||
skol_map: &infer::SkolemizationMap<'tcx>,
|
||||
placeholder_map: &infer::PlaceholderMap<'tcx>,
|
||||
snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
|
||||
) -> bool {
|
||||
debug_assert!(!skol_trait_ref.has_escaping_regions());
|
||||
@ -1778,7 +1778,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
}
|
||||
|
||||
self.infcx
|
||||
.leak_check(false, obligation.cause.span, skol_map, snapshot)
|
||||
.leak_check(false, obligation.cause.span, placeholder_map, snapshot)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
@ -1981,12 +1981,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
obligation.predicate.skip_binder().trait_ref.self_ty(),
|
||||
|impl_def_id| {
|
||||
self.probe(|this, snapshot| /* [1] */
|
||||
if let Ok(skol_map) = this.match_impl(impl_def_id, obligation, snapshot) {
|
||||
if let Ok(placeholder_map) = this.match_impl(impl_def_id, obligation, snapshot) {
|
||||
candidates.vec.push(ImplCandidate(impl_def_id));
|
||||
|
||||
// NB: we can safely drop the skol map
|
||||
// NB: we can safely drop the placeholder map
|
||||
// since we are in a probe [1]
|
||||
mem::drop(skol_map);
|
||||
mem::drop(placeholder_map);
|
||||
}
|
||||
);
|
||||
},
|
||||
@ -2630,7 +2630,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
//
|
||||
// The strategy is to:
|
||||
//
|
||||
// 1. Instantiate those regions to skolemized regions (e.g.,
|
||||
// 1. Instantiate those regions to placeholder regions (e.g.,
|
||||
// `for<'a> &'a int` becomes `&0 int`.
|
||||
// 2. Produce something like `&'0 int : Copy`
|
||||
// 3. Re-bind the regions back to `for<'a> &'a int : Copy`
|
||||
@ -2643,7 +2643,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
let ty: ty::Binder<Ty<'tcx>> = ty::Binder::bind(ty); // <----/
|
||||
|
||||
self.in_snapshot(|this, snapshot| {
|
||||
let (skol_ty, skol_map) = this.infcx().skolemize_late_bound_regions(&ty);
|
||||
let (skol_ty, placeholder_map) = this.infcx()
|
||||
.replace_late_bound_regions_with_placeholders(&ty);
|
||||
let Normalized {
|
||||
value: normalized_ty,
|
||||
mut obligations,
|
||||
@ -2663,7 +2664,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
&[],
|
||||
);
|
||||
obligations.push(skol_obligation);
|
||||
this.infcx().plug_leaks(skol_map, snapshot, obligations)
|
||||
this.infcx()
|
||||
.plug_leaks(placeholder_map, snapshot, obligations)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
@ -2870,7 +2872,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
|
||||
let trait_obligations: Vec<PredicateObligation<'_>> = self.in_snapshot(|this, snapshot| {
|
||||
let poly_trait_ref = obligation.predicate.to_poly_trait_ref();
|
||||
let (trait_ref, skol_map) = this.infcx().skolemize_late_bound_regions(&poly_trait_ref);
|
||||
let (trait_ref, placeholder_map) = this.infcx()
|
||||
.replace_late_bound_regions_with_placeholders(&poly_trait_ref);
|
||||
let cause = obligation.derived_cause(ImplDerivedObligation);
|
||||
this.impl_or_trait_obligations(
|
||||
cause,
|
||||
@ -2878,7 +2881,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
obligation.param_env,
|
||||
trait_def_id,
|
||||
&trait_ref.substs,
|
||||
skol_map,
|
||||
placeholder_map,
|
||||
snapshot,
|
||||
)
|
||||
});
|
||||
@ -2905,7 +2908,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
// First, create the substitutions by matching the impl again,
|
||||
// this time not in a probe.
|
||||
self.in_snapshot(|this, snapshot| {
|
||||
let (substs, skol_map) = this.rematch_impl(impl_def_id, obligation, snapshot);
|
||||
let (substs, placeholder_map) = this.rematch_impl(impl_def_id, obligation, snapshot);
|
||||
debug!("confirm_impl_candidate substs={:?}", substs);
|
||||
let cause = obligation.derived_cause(ImplDerivedObligation);
|
||||
this.vtable_impl(
|
||||
@ -2914,7 +2917,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
cause,
|
||||
obligation.recursion_depth + 1,
|
||||
obligation.param_env,
|
||||
skol_map,
|
||||
placeholder_map,
|
||||
snapshot,
|
||||
)
|
||||
})
|
||||
@ -2927,12 +2930,12 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
cause: ObligationCause<'tcx>,
|
||||
recursion_depth: usize,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
skol_map: infer::SkolemizationMap<'tcx>,
|
||||
placeholder_map: infer::PlaceholderMap<'tcx>,
|
||||
snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
|
||||
) -> VtableImplData<'tcx, PredicateObligation<'tcx>> {
|
||||
debug!(
|
||||
"vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={}, skol_map={:?})",
|
||||
impl_def_id, substs, recursion_depth, skol_map
|
||||
"vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={}, placeholder_map={:?})",
|
||||
impl_def_id, substs, recursion_depth, placeholder_map
|
||||
);
|
||||
|
||||
let mut impl_obligations = self.impl_or_trait_obligations(
|
||||
@ -2941,7 +2944,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
param_env,
|
||||
impl_def_id,
|
||||
&substs.value,
|
||||
skol_map,
|
||||
placeholder_map,
|
||||
snapshot,
|
||||
);
|
||||
|
||||
@ -3465,10 +3468,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
|
||||
) -> (
|
||||
Normalized<'tcx, &'tcx Substs<'tcx>>,
|
||||
infer::SkolemizationMap<'tcx>,
|
||||
infer::PlaceholderMap<'tcx>,
|
||||
) {
|
||||
match self.match_impl(impl_def_id, obligation, snapshot) {
|
||||
Ok((substs, skol_map)) => (substs, skol_map),
|
||||
Ok((substs, placeholder_map)) => (substs, placeholder_map),
|
||||
Err(()) => {
|
||||
bug!(
|
||||
"Impl {:?} was matchable against {:?} but now is not",
|
||||
@ -3487,7 +3490,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
) -> Result<
|
||||
(
|
||||
Normalized<'tcx, &'tcx Substs<'tcx>>,
|
||||
infer::SkolemizationMap<'tcx>,
|
||||
infer::PlaceholderMap<'tcx>,
|
||||
),
|
||||
(),
|
||||
> {
|
||||
@ -3500,8 +3503,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let (skol_obligation, skol_map) = self.infcx()
|
||||
.skolemize_late_bound_regions(&obligation.predicate);
|
||||
let (skol_obligation, placeholder_map) = self.infcx()
|
||||
.replace_late_bound_regions_with_placeholders(&obligation.predicate);
|
||||
let skol_obligation_trait_ref = skol_obligation.trait_ref;
|
||||
|
||||
let impl_substs = self.infcx
|
||||
@ -3532,8 +3535,9 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
.map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?;
|
||||
nested_obligations.extend(obligations);
|
||||
|
||||
if let Err(e) = self.infcx
|
||||
.leak_check(false, obligation.cause.span, &skol_map, snapshot)
|
||||
if let Err(e) =
|
||||
self.infcx
|
||||
.leak_check(false, obligation.cause.span, &placeholder_map, snapshot)
|
||||
{
|
||||
debug!("match_impl: failed leak check due to `{}`", e);
|
||||
return Err(());
|
||||
@ -3545,7 +3549,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
value: impl_substs,
|
||||
obligations: nested_obligations,
|
||||
},
|
||||
skol_map,
|
||||
placeholder_map,
|
||||
))
|
||||
}
|
||||
|
||||
@ -3692,7 +3696,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
def_id: DefId, // of impl or trait
|
||||
substs: &Substs<'tcx>, // for impl or trait
|
||||
skol_map: infer::SkolemizationMap<'tcx>,
|
||||
placeholder_map: infer::PlaceholderMap<'tcx>,
|
||||
snapshot: &infer::CombinedSnapshot<'cx, 'tcx>,
|
||||
) -> Vec<PredicateObligation<'tcx>> {
|
||||
debug!("impl_or_trait_obligations(def_id={:?})", def_id);
|
||||
@ -3755,7 +3759,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
|
||||
let mut seen = FxHashSet();
|
||||
predicates.retain(|i| seen.insert(i.clone()));
|
||||
}
|
||||
self.infcx().plug_leaks(skol_map, snapshot, predicates)
|
||||
self.infcx()
|
||||
.plug_leaks(placeholder_map, snapshot, predicates)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ pub(super) fn specializes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
return false;
|
||||
}
|
||||
|
||||
// create a parameter environment corresponding to a (skolemized) instantiation of impl1
|
||||
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
|
||||
let penv = tcx.param_env(impl1_def_id);
|
||||
let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
|
||||
|
||||
|
@ -218,9 +218,9 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
|
||||
ty::Infer(ty::IntVar(_)) => "integral variable".to_string(),
|
||||
ty::Infer(ty::FloatVar(_)) => "floating-point variable".to_string(),
|
||||
ty::Infer(ty::CanonicalTy(_)) |
|
||||
ty::Infer(ty::FreshTy(_)) => "skolemized type".to_string(),
|
||||
ty::Infer(ty::FreshIntTy(_)) => "skolemized integral type".to_string(),
|
||||
ty::Infer(ty::FreshFloatTy(_)) => "skolemized floating-point type".to_string(),
|
||||
ty::Infer(ty::FreshTy(_)) => "fresh type".to_string(),
|
||||
ty::Infer(ty::FreshIntTy(_)) => "fresh integral type".to_string(),
|
||||
ty::Infer(ty::FreshFloatTy(_)) => "fresh floating-point type".to_string(),
|
||||
ty::Projection(_) => "associated type".to_string(),
|
||||
ty::UnnormalizedProjection(_) => "non-normalized associated type".to_string(),
|
||||
ty::Param(ref p) => {
|
||||
|
@ -667,12 +667,14 @@ pub fn shift_regions<'a, 'gcx, 'tcx, T>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
/// we already use the term "free region". It refers to the regions that we use to represent bound
|
||||
/// regions on a fn definition while we are typechecking its body.
|
||||
///
|
||||
/// To clarify, conceptually there is no particular difference between an "escaping" region and a
|
||||
/// "free" region. However, there is a big difference in practice. Basically, when "entering" a
|
||||
/// binding level, one is generally required to do some sort of processing to a bound region, such
|
||||
/// as replacing it with a fresh/skolemized region, or making an entry in the environment to
|
||||
/// represent the scope to which it is attached, etc. An escaping region represents a bound region
|
||||
/// for which this processing has not yet been done.
|
||||
/// To clarify, conceptually there is no particular difference between
|
||||
/// an "escaping" region and a "free" region. However, there is a big
|
||||
/// difference in practice. Basically, when "entering" a binding
|
||||
/// level, one is generally required to do some sort of processing to
|
||||
/// a bound region, such as replacing it with a fresh/placeholder
|
||||
/// region, or making an entry in the environment to represent the
|
||||
/// scope to which it is attached, etc. An escaping region represents
|
||||
/// a bound region for which this processing has not yet been done.
|
||||
struct HasEscapingRegionsVisitor {
|
||||
/// Anything bound by `outer_index` or "above" is escaping
|
||||
outer_index: ty::DebruijnIndex,
|
||||
|
@ -1485,10 +1485,10 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
|
||||
/// the body of `bar`, we refer to `T` as a type, we aren't referring
|
||||
/// to any type in particular, but rather a kind of "fresh" type that
|
||||
/// is distinct from all other types we have actually declared. This
|
||||
/// is called a **skolemized** type, and we use universes to talk
|
||||
/// is called a **placeholder** type, and we use universes to talk
|
||||
/// about this. In other words, a type name in universe 0 always
|
||||
/// corresponds to some "ground" type that the user declared, but a
|
||||
/// type name in a non-zero universe is a skolemized type -- an
|
||||
/// type name in a non-zero universe is a placeholder type -- an
|
||||
/// idealized representative of "types in general" that we use for
|
||||
/// checking generic functions.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
|
||||
|
@ -709,7 +709,7 @@ impl<'a, 'gcx, 'tcx> ExistentialTraitRef<'tcx> {
|
||||
/// Object types don't have a self-type specified. Therefore, when
|
||||
/// we convert the principal trait-ref into a normal trait-ref,
|
||||
/// you must give *some* self-type. A common choice is `mk_err()`
|
||||
/// or some skolemized type.
|
||||
/// or some placeholder type.
|
||||
pub fn with_self_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, self_ty: Ty<'tcx>)
|
||||
-> ty::TraitRef<'tcx> {
|
||||
// otherwise the escaping regions would be captured by the binder
|
||||
@ -732,7 +732,7 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
|
||||
/// Object types don't have a self-type specified. Therefore, when
|
||||
/// we convert the principal trait-ref into a normal trait-ref,
|
||||
/// you must give *some* self-type. A common choice is `mk_err()`
|
||||
/// or some skolemized type.
|
||||
/// or some placeholder type.
|
||||
pub fn with_self_ty(&self, tcx: TyCtxt<'_, '_, 'tcx>,
|
||||
self_ty: Ty<'tcx>)
|
||||
-> ty::PolyTraitRef<'tcx> {
|
||||
@ -1066,10 +1066,10 @@ pub type Region<'tcx> = &'tcx RegionKind;
|
||||
///
|
||||
/// Unlike Param-s, bound regions are not supposed to exist "in the wild"
|
||||
/// outside their binder, e.g. in types passed to type inference, and
|
||||
/// should first be substituted (by skolemized regions, free regions,
|
||||
/// should first be substituted (by placeholder regions, free regions,
|
||||
/// or region variables).
|
||||
///
|
||||
/// ## Skolemized and Free Regions
|
||||
/// ## Placeholder and Free Regions
|
||||
///
|
||||
/// One often wants to work with bound regions without knowing their precise
|
||||
/// identity. For example, when checking a function, the lifetime of a borrow
|
||||
@ -1078,11 +1078,11 @@ pub type Region<'tcx> = &'tcx RegionKind;
|
||||
/// assumed without being checked.
|
||||
///
|
||||
/// The process of doing that is called "skolemization". The bound regions
|
||||
/// are replaced by skolemized markers, which don't satisfy any relation
|
||||
/// are replaced by placeholder markers, which don't satisfy any relation
|
||||
/// not explicitly provided.
|
||||
///
|
||||
/// There are 2 kinds of skolemized regions in rustc: `ReFree` and
|
||||
/// `ReSkolemized`. When checking an item's body, `ReFree` is supposed
|
||||
/// There are 2 kinds of placeholder regions in rustc: `ReFree` and
|
||||
/// `RePlaceholder`. When checking an item's body, `ReFree` is supposed
|
||||
/// to be used. These also support explicit bounds: both the internally-stored
|
||||
/// *scope*, which the region is assumed to outlive, as well as other
|
||||
/// relations stored in the `FreeRegionMap`. Note that these relations
|
||||
@ -1091,14 +1091,14 @@ pub type Region<'tcx> = &'tcx RegionKind;
|
||||
///
|
||||
/// When working with higher-ranked types, some region relations aren't
|
||||
/// yet known, so you can't just call `resolve_regions_and_report_errors`.
|
||||
/// `ReSkolemized` is designed for this purpose. In these contexts,
|
||||
/// `RePlaceholder` is designed for this purpose. In these contexts,
|
||||
/// there's also the risk that some inference variable laying around will
|
||||
/// get unified with your skolemized region: if you want to check whether
|
||||
/// get unified with your placeholder region: if you want to check whether
|
||||
/// `for<'a> Foo<'_>: 'a`, and you substitute your bound region `'a`
|
||||
/// with a skolemized region `'%a`, the variable `'_` would just be
|
||||
/// instantiated to the skolemized region `'%a`, which is wrong because
|
||||
/// with a placeholder region `'%a`, the variable `'_` would just be
|
||||
/// instantiated to the placeholder region `'%a`, which is wrong because
|
||||
/// the inference variable is supposed to satisfy the relation
|
||||
/// *for every value of the skolemized region*. To ensure that doesn't
|
||||
/// *for every value of the placeholder region*. To ensure that doesn't
|
||||
/// happen, you can use `leak_check`. This is more clearly explained
|
||||
/// by the [rustc guide].
|
||||
///
|
||||
@ -1132,9 +1132,9 @@ pub enum RegionKind {
|
||||
/// A region variable. Should not exist after typeck.
|
||||
ReVar(RegionVid),
|
||||
|
||||
/// A skolemized region - basically the higher-ranked version of ReFree.
|
||||
/// A placeholder region - basically the higher-ranked version of ReFree.
|
||||
/// Should not exist after typeck.
|
||||
ReSkolemized(ty::UniverseIndex, BoundRegion),
|
||||
RePlaceholder(ty::UniverseIndex, BoundRegion),
|
||||
|
||||
/// Empty lifetime is for data that is never accessed.
|
||||
/// Bottom in the region lattice. We treat ReEmpty somewhat
|
||||
@ -1338,7 +1338,7 @@ impl RegionKind {
|
||||
RegionKind::ReScope(..) => false,
|
||||
RegionKind::ReStatic => true,
|
||||
RegionKind::ReVar(..) => false,
|
||||
RegionKind::ReSkolemized(_, br) => br.is_named(),
|
||||
RegionKind::RePlaceholder(_, br) => br.is_named(),
|
||||
RegionKind::ReEmpty => false,
|
||||
RegionKind::ReErased => false,
|
||||
RegionKind::ReClosureBound(..) => false,
|
||||
@ -1410,7 +1410,7 @@ impl RegionKind {
|
||||
flags = flags | TypeFlags::HAS_FREE_REGIONS;
|
||||
flags = flags | TypeFlags::HAS_RE_INFER;
|
||||
}
|
||||
ty::ReSkolemized(..) => {
|
||||
ty::RePlaceholder(..) => {
|
||||
flags = flags | TypeFlags::HAS_FREE_REGIONS;
|
||||
flags = flags | TypeFlags::HAS_RE_SKOL;
|
||||
}
|
||||
|
@ -520,7 +520,7 @@ pub fn object_region_bounds<'a, 'gcx, 'tcx>(
|
||||
{
|
||||
// Since we don't actually *know* the self type for an object,
|
||||
// this "open(err)" serves as a kind of dummy standin -- basically
|
||||
// a skolemized type.
|
||||
// a placeholder type.
|
||||
let open_ty = tcx.mk_infer(ty::FreshTy(0));
|
||||
|
||||
let predicates = existential_predicates.iter().filter_map(|predicate| {
|
||||
|
@ -803,7 +803,7 @@ define_print! {
|
||||
}
|
||||
ty::ReLateBound(_, br) |
|
||||
ty::ReFree(ty::FreeRegion { bound_region: br, .. }) |
|
||||
ty::ReSkolemized(_, br) => {
|
||||
ty::RePlaceholder(_, br) => {
|
||||
write!(f, "{}", br)
|
||||
}
|
||||
ty::ReScope(scope) if cx.identify_regions => {
|
||||
@ -872,8 +872,8 @@ define_print! {
|
||||
write!(f, "'?{}", c.index())
|
||||
}
|
||||
|
||||
ty::ReSkolemized(universe, ref bound_region) => {
|
||||
write!(f, "ReSkolemized({:?}, {:?})", universe, bound_region)
|
||||
ty::RePlaceholder(universe, ref bound_region) => {
|
||||
write!(f, "RePlaceholder({:?}, {:?})", universe, bound_region)
|
||||
}
|
||||
|
||||
ty::ReEmpty => write!(f, "ReEmpty"),
|
||||
|
@ -426,7 +426,7 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
|
||||
// These cannot exist in borrowck
|
||||
RegionKind::ReVar(..) |
|
||||
RegionKind::ReCanonical(..) |
|
||||
RegionKind::ReSkolemized(..) |
|
||||
RegionKind::RePlaceholder(..) |
|
||||
RegionKind::ReClosureBound(..) |
|
||||
RegionKind::ReErased => span_bug!(borrow_span,
|
||||
"unexpected region in borrowck {:?}",
|
||||
|
@ -368,7 +368,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
|
||||
ty::ReClosureBound(..) |
|
||||
ty::ReLateBound(..) |
|
||||
ty::ReVar(..) |
|
||||
ty::ReSkolemized(..) |
|
||||
ty::RePlaceholder(..) |
|
||||
ty::ReErased => {
|
||||
span_bug!(
|
||||
cmt.span,
|
||||
|
@ -1780,7 +1780,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
|
||||
// lifetimes without names with the value `'0`.
|
||||
match ty.sty {
|
||||
ty::TyKind::Ref(ty::RegionKind::ReLateBound(_, br), _, _)
|
||||
| ty::TyKind::Ref(ty::RegionKind::ReSkolemized(_, br), _, _) => {
|
||||
| ty::TyKind::Ref(ty::RegionKind::RePlaceholder(_, br), _, _) => {
|
||||
with_highlight_region_for_bound_region(*br, counter, || format!("{}", ty))
|
||||
}
|
||||
_ => format!("{}", ty),
|
||||
@ -1792,7 +1792,7 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
|
||||
fn get_region_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String {
|
||||
match ty.sty {
|
||||
ty::TyKind::Ref(region, _, _) => match region {
|
||||
ty::RegionKind::ReLateBound(_, br) | ty::RegionKind::ReSkolemized(_, br) => {
|
||||
ty::RegionKind::ReLateBound(_, br) | ty::RegionKind::RePlaceholder(_, br) => {
|
||||
with_highlight_region_for_bound_region(*br, counter, || format!("{}", region))
|
||||
}
|
||||
_ => format!("{}", region),
|
||||
|
@ -274,7 +274,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
ty::ReLateBound(..)
|
||||
| ty::ReScope(..)
|
||||
| ty::ReVar(..)
|
||||
| ty::ReSkolemized(..)
|
||||
| ty::RePlaceholder(..)
|
||||
| ty::ReEmpty
|
||||
| ty::ReErased
|
||||
| ty::ReClosureBound(..)
|
||||
|
@ -128,14 +128,14 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
// We create a mapping `dummy_substs` that maps from the impl type
|
||||
// parameters to fresh types and regions. For type parameters,
|
||||
// this is the identity transform, but we could as well use any
|
||||
// skolemized types. For regions, we convert from bound to free
|
||||
// placeholder types. For regions, we convert from bound to free
|
||||
// regions (Note: but only early-bound regions, i.e., those
|
||||
// declared on the impl or used in type parameter bounds).
|
||||
//
|
||||
// impl_to_skol_substs = {'i => 'i0, U => U0, N => N0 }
|
||||
//
|
||||
// Now we can apply skol_substs to the type of the impl method
|
||||
// to yield a new function type in terms of our fresh, skolemized
|
||||
// to yield a new function type in terms of our fresh, placeholder
|
||||
// types:
|
||||
//
|
||||
// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
|
||||
@ -163,15 +163,15 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
// We do this by creating a parameter environment which contains a
|
||||
// substitution corresponding to impl_to_skol_substs. We then build
|
||||
// trait_to_skol_substs and use it to convert the predicates contained
|
||||
// in the trait_m.generics to the skolemized form.
|
||||
// in the trait_m.generics to the placeholder form.
|
||||
//
|
||||
// Finally we register each of these predicates as an obligation in
|
||||
// a fresh FulfillmentCtxt, and invoke select_all_or_error.
|
||||
|
||||
// Create mapping from impl to skolemized.
|
||||
// Create mapping from impl to placeholder.
|
||||
let impl_to_skol_substs = Substs::identity_for_item(tcx, impl_m.def_id);
|
||||
|
||||
// Create mapping from trait to skolemized.
|
||||
// Create mapping from trait to placeholder.
|
||||
let trait_to_skol_substs = impl_to_skol_substs.rebase_onto(tcx,
|
||||
impl_m.container.id(),
|
||||
trait_to_impl_substs);
|
||||
@ -212,7 +212,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
hybrid_preds.predicates.extend(
|
||||
trait_m_predicates.instantiate_own(tcx, trait_to_skol_substs).predicates);
|
||||
|
||||
// Construct trait parameter environment and then shift it into the skolemized viewpoint.
|
||||
// Construct trait parameter environment and then shift it into the placeholder viewpoint.
|
||||
// The key step here is to update the caller_bounds's predicates to be
|
||||
// the new hybrid bounds we computed.
|
||||
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_node_id);
|
||||
@ -259,7 +259,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
// any associated types appearing in the fn arguments or return
|
||||
// type.
|
||||
|
||||
// Compute skolemized form of impl and trait method tys.
|
||||
// Compute placeholder form of impl and trait method tys.
|
||||
let tcx = infcx.tcx;
|
||||
|
||||
let (impl_sig, _) =
|
||||
@ -894,7 +894,7 @@ pub fn compare_const_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
// method.
|
||||
let impl_c_node_id = tcx.hir.as_local_node_id(impl_c.def_id).unwrap();
|
||||
|
||||
// Compute skolemized form of impl and trait const tys.
|
||||
// Compute placeholder form of impl and trait const tys.
|
||||
let impl_ty = tcx.type_of(impl_c.def_id);
|
||||
let trait_ty = tcx.type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs);
|
||||
let mut cause = ObligationCause::misc(impl_c_span, impl_c_node_id);
|
||||
|
@ -170,7 +170,7 @@ fn is_free_region<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, region: Region<'_>) -> bool
|
||||
| RegionKind::ReCanonical(..)
|
||||
| RegionKind::ReScope(..)
|
||||
| RegionKind::ReVar(..)
|
||||
| RegionKind::ReSkolemized(..)
|
||||
| RegionKind::RePlaceholder(..)
|
||||
| RegionKind::ReFree(..) => {
|
||||
bug!("unexpected region in outlives inference: {:?}", region);
|
||||
}
|
||||
|
@ -431,7 +431,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
ty::ReClosureBound(..) |
|
||||
ty::ReScope(..) |
|
||||
ty::ReVar(..) |
|
||||
ty::ReSkolemized(..) |
|
||||
ty::RePlaceholder(..) |
|
||||
ty::ReEmpty |
|
||||
ty::ReErased => {
|
||||
// We don't expect to see anything but 'static or bound
|
||||
|
@ -1258,7 +1258,7 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
|
||||
ty::ReFree(..) |
|
||||
ty::ReScope(..) |
|
||||
ty::ReVar(..) |
|
||||
ty::ReSkolemized(..) |
|
||||
ty::RePlaceholder(..) |
|
||||
ty::ReEmpty |
|
||||
ty::ReClosureBound(_) |
|
||||
ty::ReCanonical(_) |
|
||||
|
@ -10,8 +10,8 @@
|
||||
|
||||
#![allow(dead_code)]
|
||||
// Regression test for #37154: the problem here was that the cache
|
||||
// results in a false error because it was caching skolemized results
|
||||
// even after those skolemized regions had been popped.
|
||||
// results in a false error because it was caching placeholder results
|
||||
// even after those placeholder regions had been popped.
|
||||
|
||||
trait Foo {
|
||||
fn method(&self) {}
|
||||
|
Loading…
Reference in New Issue
Block a user