Skip to content

Commit 5cff88f

Browse files
committed
add a higher-ranked match routine
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.
1 parent b40529c commit 5cff88f

File tree

2 files changed

+177
-0
lines changed

2 files changed

+177
-0
lines changed

src/librustc/infer/higher_ranked/mod.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use super::{CombinedSnapshot,
1515
InferCtxt,
1616
LateBoundRegion,
1717
HigherRankedType,
18+
SubregionOrigin,
1819
SkolemizationMap};
1920
use super::combine::CombineFields;
2021
use super::region_inference::{TaintDirections};
@@ -25,6 +26,19 @@ use ty::relate::{Relate, RelateResult, TypeRelation};
2526
use syntax::codemap::Span;
2627
use util::nodemap::{FnvHashMap, FnvHashSet};
2728

29+
pub struct HrMatchResult<U> {
30+
pub value: U,
31+
32+
/// Normally, when we do a higher-ranked match operation, we
33+
/// expect all higher-ranked regions to be constrained as part of
34+
/// the match operation. However, in the transition period for
35+
/// #32330, it can happen that we sometimes have unconstrained
36+
/// regions that get instantiated with fresh variables. In that
37+
/// case, we collect the set of unconstrained bound regions here
38+
/// and replace them with fresh variables.
39+
pub unconstrained_regions: Vec<ty::BoundRegion>,
40+
}
41+
2842
impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
2943
pub fn higher_ranked_sub<T>(&self, a: &Binder<T>, b: &Binder<T>)
3044
-> RelateResult<'tcx, Binder<T>>
@@ -79,6 +93,134 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
7993
});
8094
}
8195

96+
/// The value consists of a pair `(t, u)` where `t` is the
97+
/// *matcher* and `u` is a *value*. The idea is to find a
98+
/// substitution `S` such that `S(t) == b`, and then return
99+
/// `S(u)`. In other words, find values for the late-bound regions
100+
/// in `a` that can make `t == b` and then replace the LBR in `u`
101+
/// with those values.
102+
///
103+
/// This routine is (as of this writing) used in trait matching,
104+
/// particularly projection.
105+
///
106+
/// NB. It should not happen that there are LBR appearing in `U`
107+
/// that do not appear in `T`. If that happens, those regions are
108+
/// unconstrained, and this routine replaces them with `'static`.
109+
pub fn higher_ranked_match<T, U>(&self,
110+
span: Span,
111+
a_pair: &Binder<(T, U)>,
112+
b_match: &T)
113+
-> RelateResult<'tcx, HrMatchResult<U>>
114+
where T: Relate<'tcx>,
115+
U: TypeFoldable<'tcx>
116+
{
117+
debug!("higher_ranked_match(a={:?}, b={:?})",
118+
a_pair, b_match);
119+
120+
// Start a snapshot so we can examine "all bindings that were
121+
// created as part of this type comparison".
122+
return self.infcx.commit_if_ok(|snapshot| {
123+
// First, we instantiate each bound region in the matcher
124+
// with a skolemized region.
125+
let ((a_match, a_value), skol_map) =
126+
self.infcx.skolemize_late_bound_regions(a_pair, snapshot);
127+
128+
debug!("higher_ranked_match: a_match={:?}", a_match);
129+
debug!("higher_ranked_match: skol_map={:?}", skol_map);
130+
131+
// Equate types now that bound regions have been replaced.
132+
try!(self.equate().relate(&a_match, &b_match));
133+
134+
// Map each skolemized region to a vector of other regions that it
135+
// must be equated with. (Note that this vector may include other
136+
// skolemized regions from `skol_map`.)
137+
let skol_resolution_map: FnvHashMap<_, _> =
138+
skol_map
139+
.iter()
140+
.map(|(&br, &skol)| {
141+
let tainted_regions =
142+
self.infcx.tainted_regions(snapshot,
143+
skol,
144+
TaintDirections::incoming()); // [1]
145+
146+
// [1] this routine executes after the skolemized
147+
// regions have been *equated* with something
148+
// else, so examining the incoming edges ought to
149+
// be enough to collect all constraints
150+
151+
(skol, (br, tainted_regions))
152+
})
153+
.collect();
154+
155+
// For each skolemized region, pick a representative -- which can
156+
// be any region from the sets above, except for other members of
157+
// `skol_map`. There should always be a representative if things
158+
// are properly well-formed.
159+
let mut unconstrained_regions = vec![];
160+
let skol_representatives: FnvHashMap<_, _> =
161+
skol_resolution_map
162+
.iter()
163+
.map(|(&skol, &(br, ref regions))| {
164+
let representative =
165+
regions.iter()
166+
.filter(|r| !skol_resolution_map.contains_key(r))
167+
.cloned()
168+
.next()
169+
.unwrap_or_else(|| { // [1]
170+
unconstrained_regions.push(br);
171+
self.infcx.next_region_var(
172+
LateBoundRegion(span, br, HigherRankedType))
173+
});
174+
175+
// [1] There should always be a representative,
176+
// unless the higher-ranked region did not appear
177+
// in the values being matched. We should reject
178+
// as ill-formed cases that can lead to this, but
179+
// right now we sometimes issue warnings (see
180+
// #32330).
181+
182+
(skol, representative)
183+
})
184+
.collect();
185+
186+
// Equate all the members of each skolemization set with the
187+
// representative.
188+
for (skol, &(_br, ref regions)) in &skol_resolution_map {
189+
let representative = &skol_representatives[skol];
190+
debug!("higher_ranked_match: \
191+
skol={:?} representative={:?} regions={:?}",
192+
skol, representative, regions);
193+
for region in regions.iter()
194+
.filter(|&r| !skol_resolution_map.contains_key(r))
195+
.filter(|&r| r != representative)
196+
{
197+
let origin = SubregionOrigin::Subtype(self.trace.clone());
198+
self.infcx.region_vars.make_eqregion(origin,
199+
*representative,
200+
*region);
201+
}
202+
}
203+
204+
// Replace the skolemized regions appearing in value with
205+
// their representatives
206+
let a_value =
207+
fold_regions_in(
208+
self.tcx(),
209+
&a_value,
210+
|r, _| skol_representatives.get(&r).cloned().unwrap_or(r));
211+
212+
debug!("higher_ranked_match: value={:?}", a_value);
213+
214+
// We are now done with these skolemized variables.
215+
self.infcx.pop_skolemized(skol_map, snapshot);
216+
217+
Ok(HrMatchResult {
218+
value: a_value,
219+
unconstrained_regions: unconstrained_regions,
220+
})
221+
});
222+
}
223+
82224
pub fn higher_ranked_lub<T>(&self, a: &Binder<T>, b: &Binder<T>)
83225
-> RelateResult<'tcx, Binder<T>>
84226
where T: Relate<'tcx>

src/librustc/infer/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use syntax::errors::DiagnosticBuilder;
4545
use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
4646

4747
use self::combine::CombineFields;
48+
use self::higher_ranked::HrMatchResult;
4849
use self::region_inference::{RegionVarBindings, RegionSnapshot};
4950
use self::unify_key::ToType;
5051

@@ -63,6 +64,7 @@ pub mod sub;
6364
pub mod type_variable;
6465
pub mod unify_key;
6566

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

1581+
/// Given a higher-ranked projection predicate like:
1582+
///
1583+
/// for<'a> <T as Fn<&'a u32>>::Output = &'a u32
1584+
///
1585+
/// and a target trait-ref like:
1586+
///
1587+
/// <T as Fn<&'x u32>>
1588+
///
1589+
/// find a substitution `S` for the higher-ranked regions (here,
1590+
/// `['a => 'x]`) such that the predicate matches the trait-ref,
1591+
/// and then return the value (here, `&'a u32`) but with the
1592+
/// substitution applied (hence, `&'x u32`).
1593+
///
1594+
/// See `higher_ranked_match` in `higher_ranked/mod.rs` for more
1595+
/// details.
1596+
pub fn match_poly_projection_predicate(&self,
1597+
origin: TypeOrigin,
1598+
match_a: ty::PolyProjectionPredicate<'tcx>,
1599+
match_b: ty::TraitRef<'tcx>)
1600+
-> RelateResult<HrMatchResult<Ty<'tcx>>>
1601+
{
1602+
let span = origin.span();
1603+
let match_trait_ref = match_a.skip_binder().projection_ty.trait_ref;
1604+
let trace = TypeTrace {
1605+
origin: origin,
1606+
values: TraitRefs(ExpectedFound::new(true, match_trait_ref, match_b))
1607+
};
1608+
1609+
let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref, p.ty));
1610+
self.combine_fields(true, trace)
1611+
.higher_ranked_match(span, &match_pair, &match_b)
1612+
}
1613+
15791614
/// See `verify_generic_bound` method in `region_inference`
15801615
pub fn verify_generic_bound(&self,
15811616
origin: SubregionOrigin<'tcx>,

0 commit comments

Comments
 (0)