Skip to content

Commit

Permalink
add a higher-ranked match routine
Browse files Browse the repository at this point in the history
Currently, when projecting out of a higher-ranked where-clause, we
instantiate all higher-ranked regions with lifetime variables. This is
unnecessary since the language rules ought to guarantee (modulo #32330)
that each of those higher-ranked regions is equated with some regions
from the input types. This routine figures out what those regions are
and just uses them. Also, since #32330 is not fully fixed, it detects
when we may have unconstrained variables and indicates that in its
return value.
  • Loading branch information
nikomatsakis committed May 31, 2016
1 parent b40529c commit 5cff88f
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 0 deletions.
142 changes: 142 additions & 0 deletions src/librustc/infer/higher_ranked/mod.rs
Expand Up @@ -15,6 +15,7 @@ use super::{CombinedSnapshot,
InferCtxt,
LateBoundRegion,
HigherRankedType,
SubregionOrigin,
SkolemizationMap};
use super::combine::CombineFields;
use super::region_inference::{TaintDirections};
Expand All @@ -25,6 +26,19 @@ use ty::relate::{Relate, RelateResult, TypeRelation};
use syntax::codemap::Span;
use util::nodemap::{FnvHashMap, FnvHashSet};

pub struct HrMatchResult<U> {
pub value: U,

/// Normally, when we do a higher-ranked match operation, we
/// expect all higher-ranked regions to be constrained as part of
/// the match operation. However, in the transition period for
/// #32330, it can happen that we sometimes have unconstrained
/// regions that get instantiated with fresh variables. In that
/// case, we collect the set of unconstrained bound regions here
/// and replace them with fresh variables.
pub unconstrained_regions: Vec<ty::BoundRegion>,
}

impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
pub fn higher_ranked_sub<T>(&self, a: &Binder<T>, b: &Binder<T>)
-> RelateResult<'tcx, Binder<T>>
Expand Down Expand Up @@ -79,6 +93,134 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
});
}

/// The value consists of a pair `(t, u)` where `t` is the
/// *matcher* and `u` is a *value*. The idea is to find a
/// substitution `S` such that `S(t) == b`, and then return
/// `S(u)`. In other words, find values for the late-bound regions
/// in `a` that can make `t == b` and then replace the LBR in `u`
/// with those values.
///
/// This routine is (as of this writing) used in trait matching,
/// particularly projection.
///
/// NB. It should not happen that there are LBR appearing in `U`
/// that do not appear in `T`. If that happens, those regions are
/// unconstrained, and this routine replaces them with `'static`.
pub fn higher_ranked_match<T, U>(&self,
span: Span,
a_pair: &Binder<(T, U)>,
b_match: &T)
-> RelateResult<'tcx, HrMatchResult<U>>
where T: Relate<'tcx>,
U: TypeFoldable<'tcx>
{
debug!("higher_ranked_match(a={:?}, b={:?})",
a_pair, b_match);

// Start a snapshot so we can examine "all bindings that were
// 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, snapshot);

debug!("higher_ranked_match: a_match={:?}", a_match);
debug!("higher_ranked_match: skol_map={:?}", skol_map);

// Equate types now that bound regions have been replaced.
try!(self.equate().relate(&a_match, &b_match));

// Map each skolemized 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`.)
let skol_resolution_map: FnvHashMap<_, _> =
skol_map
.iter()
.map(|(&br, &skol)| {
let tainted_regions =
self.infcx.tainted_regions(snapshot,
skol,
TaintDirections::incoming()); // [1]

// [1] this routine executes after the skolemized
// regions have been *equated* with something
// else, so examining the incoming edges ought to
// be enough to collect all constraints

(skol, (br, tainted_regions))
})
.collect();

// For each skolemized 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
// are properly well-formed.
let mut unconstrained_regions = vec![];
let skol_representatives: FnvHashMap<_, _> =
skol_resolution_map
.iter()
.map(|(&skol, &(br, ref regions))| {
let representative =
regions.iter()
.filter(|r| !skol_resolution_map.contains_key(r))
.cloned()
.next()
.unwrap_or_else(|| { // [1]
unconstrained_regions.push(br);
self.infcx.next_region_var(
LateBoundRegion(span, br, HigherRankedType))
});

// [1] There should always be a representative,
// unless the higher-ranked region did not appear
// in the values being matched. We should reject
// as ill-formed cases that can lead to this, but
// right now we sometimes issue warnings (see
// #32330).

(skol, representative)
})
.collect();

// Equate all the members of each skolemization set with the
// representative.
for (skol, &(_br, ref regions)) in &skol_resolution_map {
let representative = &skol_representatives[skol];
debug!("higher_ranked_match: \
skol={:?} representative={:?} regions={:?}",
skol, representative, regions);
for region in regions.iter()
.filter(|&r| !skol_resolution_map.contains_key(r))
.filter(|&r| r != representative)
{
let origin = SubregionOrigin::Subtype(self.trace.clone());
self.infcx.region_vars.make_eqregion(origin,
*representative,
*region);
}
}

// Replace the skolemized regions appearing in value with
// their representatives
let a_value =
fold_regions_in(
self.tcx(),
&a_value,
|r, _| skol_representatives.get(&r).cloned().unwrap_or(r));

debug!("higher_ranked_match: value={:?}", a_value);

// We are now done with these skolemized variables.
self.infcx.pop_skolemized(skol_map, snapshot);

Ok(HrMatchResult {
value: a_value,
unconstrained_regions: unconstrained_regions,
})
});
}

pub fn higher_ranked_lub<T>(&self, a: &Binder<T>, b: &Binder<T>)
-> RelateResult<'tcx, Binder<T>>
where T: Relate<'tcx>
Expand Down
35 changes: 35 additions & 0 deletions src/librustc/infer/mod.rs
Expand Up @@ -45,6 +45,7 @@ use syntax::errors::DiagnosticBuilder;
use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};

use self::combine::CombineFields;
use self::higher_ranked::HrMatchResult;
use self::region_inference::{RegionVarBindings, RegionSnapshot};
use self::unify_key::ToType;

Expand All @@ -63,6 +64,7 @@ pub mod sub;
pub mod type_variable;
pub mod unify_key;

#[must_use]
pub struct InferOk<'tcx, T> {
pub value: T,
pub obligations: PredicateObligations<'tcx>,
Expand Down Expand Up @@ -1576,6 +1578,39 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|br| self.next_region_var(LateBoundRegion(span, br, lbrct)))
}

/// Given a higher-ranked projection predicate like:
///
/// for<'a> <T as Fn<&'a u32>>::Output = &'a u32
///
/// and a target trait-ref like:
///
/// <T as Fn<&'x u32>>
///
/// find a substitution `S` for the higher-ranked regions (here,
/// `['a => 'x]`) such that the predicate matches the trait-ref,
/// and then return the value (here, `&'a u32`) but with the
/// substitution applied (hence, `&'x u32`).
///
/// See `higher_ranked_match` in `higher_ranked/mod.rs` for more
/// details.
pub fn match_poly_projection_predicate(&self,
origin: TypeOrigin,
match_a: ty::PolyProjectionPredicate<'tcx>,
match_b: ty::TraitRef<'tcx>)
-> RelateResult<HrMatchResult<Ty<'tcx>>>
{
let span = origin.span();
let match_trait_ref = match_a.skip_binder().projection_ty.trait_ref;
let trace = TypeTrace {
origin: origin,
values: TraitRefs(ExpectedFound::new(true, match_trait_ref, match_b))
};

let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref, p.ty));
self.combine_fields(true, trace)
.higher_ranked_match(span, &match_pair, &match_b)
}

/// See `verify_generic_bound` method in `region_inference`
pub fn verify_generic_bound(&self,
origin: SubregionOrigin<'tcx>,
Expand Down

0 comments on commit 5cff88f

Please sign in to comment.