Skip to content

Commit 8e64ba8

Browse files
committed
extract constrain_anon_types to the InferCtxt
No funtional change.
1 parent e96f4be commit 8e64ba8

File tree

4 files changed

+289
-206
lines changed

4 files changed

+289
-206
lines changed

src/librustc/infer/anon_types/mod.rs

Lines changed: 261 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
// except according to those terms.
1010

1111
use hir::def_id::DefId;
12-
use infer::{InferCtxt, InferOk, TypeVariableOrigin};
12+
use infer::{self, InferCtxt, InferOk, TypeVariableOrigin};
13+
use infer::outlives::free_region_map::FreeRegionRelations;
1314
use syntax::ast;
1415
use traits::{self, PredicateObligation};
1516
use ty::{self, Ty};
1617
use ty::fold::{BottomUpFolder, TypeFoldable};
18+
use ty::outlives::Component;
1719
use ty::subst::Substs;
1820
use util::nodemap::DefIdMap;
1921

@@ -115,6 +117,264 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
115117
obligations: instantiator.obligations,
116118
}
117119
}
120+
121+
/// Given the map `anon_types` containing the existential `impl
122+
/// Trait` types whose underlying, hidden types are being
123+
/// inferred, this method adds constraints to the regions
124+
/// appearing in those underlying hidden types to ensure that they
125+
/// at least do not refer to random scopes within the current
126+
/// function. These constraints are not (quite) sufficient to
127+
/// guarantee that the regions are actually legal values; that
128+
/// final condition is imposed after region inference is done.
129+
///
130+
/// # The Problem
131+
///
132+
/// Let's work through an example to explain how it works. Assume
133+
/// the current function is as follows:
134+
///
135+
/// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>)
136+
///
137+
/// Here, we have two `impl Trait` types whose values are being
138+
/// inferred (the `impl Bar<'a>` and the `impl
139+
/// Bar<'b>`). Conceptually, this is sugar for a setup where we
140+
/// define underlying abstract types (`Foo1`, `Foo2`) and then, in
141+
/// the return type of `foo`, we *reference* those definitions:
142+
///
143+
/// abstract type Foo1<'x>: Bar<'x>;
144+
/// abstract type Foo2<'x>: Bar<'x>;
145+
/// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. }
146+
/// // ^^^^ ^^
147+
/// // | |
148+
/// // | substs
149+
/// // def_id
150+
///
151+
/// As indicating in the comments above, each of those references
152+
/// is (in the compiler) basically a substitution (`substs`)
153+
/// applied to the type of a suitable `def_id` (which identifies
154+
/// `Foo1` or `Foo2`).
155+
///
156+
/// Now, at this point in compilation, what we have done is to
157+
/// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with
158+
/// fresh inference variables C1 and C2. We wish to use the values
159+
/// of these variables to infer the underlying types of `Foo1` and
160+
/// `Foo2`. That is, this gives rise to higher-order (pattern) unification
161+
/// constraints like:
162+
///
163+
/// for<'a> (Foo1<'a> = C1)
164+
/// for<'b> (Foo1<'b> = C2)
165+
///
166+
/// For these equation to be satisfiable, the types `C1` and `C2`
167+
/// can only refer to a limited set of regions. For example, `C1`
168+
/// can only refer to `'static` and `'a`, and `C2` can only refer
169+
/// to `'static` and `'b`. The job of this function is to impose that
170+
/// constraint.
171+
///
172+
/// Up to this point, C1 and C2 are basically just random type
173+
/// inference variables, and hence they may contain arbitrary
174+
/// regions. In fact, it is fairly likely that they do! Consider
175+
/// this possible definition of `foo`:
176+
///
177+
/// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) {
178+
/// (&*x, &*y)
179+
/// }
180+
///
181+
/// Here, the values for the concrete types of the two impl
182+
/// traits will include inference variables:
183+
///
184+
/// &'0 i32
185+
/// &'1 i32
186+
///
187+
/// Ordinarily, the subtyping rules would ensure that these are
188+
/// sufficiently large. But since `impl Bar<'a>` isn't a specific
189+
/// type per se, we don't get such constraints by default. This
190+
/// is where this function comes into play. It adds extra
191+
/// constraints to ensure that all the regions which appear in the
192+
/// inferred type are regions that could validly appear.
193+
///
194+
/// This is actually a bit of a tricky constraint in general. We
195+
/// want to say that each variable (e.g., `'0``) can only take on
196+
/// values that were supplied as arguments to the abstract type
197+
/// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in
198+
/// scope. We don't have a constraint quite of this kind in the current
199+
/// region checker.
200+
///
201+
/// # The Solution
202+
///
203+
/// We make use of the constraint that we *do* have in the `<=`
204+
/// relation. To do that, we find the "minimum" of all the
205+
/// arguments that appear in the substs: that is, some region
206+
/// which is less than all the others. In the case of `Foo1<'a>`,
207+
/// that would be `'a` (it's the only choice, after all). Then we
208+
/// apply that as a least bound to the variables (e.g., `'a <=
209+
/// '0`).
210+
///
211+
/// In some cases, there is no minimum. Consider this example:
212+
///
213+
/// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... }
214+
///
215+
/// Here we would report an error, because `'a` and `'b` have no
216+
/// relation to one another.
217+
///
218+
/// # The `free_region_relations` parameter
219+
///
220+
/// The `free_region_relations` argument is used to find the
221+
/// "minimum" of the regions supplied to a given abstract type.
222+
/// It must be a relation that can answer whether `'a <= 'b`,
223+
/// where `'a` and `'b` are regions that appear in the "substs"
224+
/// for the abstract type references (the `<'a>` in `Foo1<'a>`).
225+
///
226+
/// Note that we do not impose the constraints based on the
227+
/// generic regions from the `Foo1` definition (e.g., `'x`). This
228+
/// is because the constraints we are imposing here is basically
229+
/// the concern of the one generating the constraining type C1,
230+
/// which is the current function. It also means that we can
231+
/// take "implied bounds" into account in some cases:
232+
///
233+
/// trait SomeTrait<'a, 'b> { }
234+
/// fn foo<'a, 'b>(_: &'a &'b u32) -> impl SomeTrait<'a, 'b> { .. }
235+
///
236+
/// Here, the fact that `'b: 'a` is known only because of the
237+
/// implied bounds from the `&'a &'b u32` parameter, and is not
238+
/// "inherent" to the abstract type definition.
239+
///
240+
/// # Parameters
241+
///
242+
/// - `anon_types` -- the map produced by `instantiate_anon_types`
243+
/// - `free_region_relations` -- something that can be used to relate
244+
/// the free regions (`'a`) that appear in the impl trait.
245+
pub fn constrain_anon_types<FRR: FreeRegionRelations<'tcx>>(
246+
&self,
247+
anon_types: &AnonTypeMap<'tcx>,
248+
free_region_relations: &FRR,
249+
) {
250+
debug!("constrain_anon_types()");
251+
252+
for (&def_id, anon_defn) in anon_types {
253+
self.constrain_anon_type(def_id, anon_defn, free_region_relations);
254+
}
255+
}
256+
257+
fn constrain_anon_type<FRR: FreeRegionRelations<'tcx>>(
258+
&self,
259+
def_id: DefId,
260+
anon_defn: &AnonTypeDecl<'tcx>,
261+
free_region_relations: &FRR,
262+
) {
263+
debug!("constrain_anon_type()");
264+
debug!("constrain_anon_type: def_id={:?}", def_id);
265+
debug!("constrain_anon_type: anon_defn={:#?}", anon_defn);
266+
267+
let concrete_ty = self.resolve_type_vars_if_possible(&anon_defn.concrete_ty);
268+
269+
debug!("constrain_anon_type: concrete_ty={:?}", concrete_ty);
270+
271+
let abstract_type_generics = self.tcx.generics_of(def_id);
272+
273+
let span = self.tcx.def_span(def_id);
274+
275+
// If there are required region bounds, we can just skip
276+
// ahead. There will already be a registered region
277+
// obligation related `concrete_ty` to those regions.
278+
if anon_defn.has_required_region_bounds {
279+
return;
280+
}
281+
282+
// There were no `required_region_bounds`,
283+
// so we have to search for a `least_region`.
284+
// Go through all the regions used as arguments to the
285+
// abstract type. These are the parameters to the abstract
286+
// type; so in our example above, `substs` would contain
287+
// `['a]` for the first impl trait and `'b` for the
288+
// second.
289+
let mut least_region = None;
290+
for region_def in &abstract_type_generics.regions {
291+
// Find the index of this region in the list of substitutions.
292+
let index = region_def.index as usize;
293+
294+
// Get the value supplied for this region from the substs.
295+
let subst_arg = anon_defn.substs[index].as_region().unwrap();
296+
297+
// Compute the least upper bound of it with the other regions.
298+
debug!("constrain_anon_types: least_region={:?}", least_region);
299+
debug!("constrain_anon_types: subst_arg={:?}", subst_arg);
300+
match least_region {
301+
None => least_region = Some(subst_arg),
302+
Some(lr) => {
303+
if free_region_relations.sub_free_regions(lr, subst_arg) {
304+
// keep the current least region
305+
} else if free_region_relations.sub_free_regions(subst_arg, lr) {
306+
// switch to `subst_arg`
307+
least_region = Some(subst_arg);
308+
} else {
309+
// There are two regions (`lr` and
310+
// `subst_arg`) which are not relatable. We can't
311+
// find a best choice.
312+
self.tcx
313+
.sess
314+
.struct_span_err(span, "ambiguous lifetime bound in `impl Trait`")
315+
.span_label(
316+
span,
317+
format!("neither `{}` nor `{}` outlives the other", lr, subst_arg),
318+
)
319+
.emit();
320+
321+
least_region = Some(self.tcx.mk_region(ty::ReEmpty));
322+
break;
323+
}
324+
}
325+
}
326+
}
327+
328+
let least_region = least_region.unwrap_or(self.tcx.types.re_static);
329+
debug!("constrain_anon_types: least_region={:?}", least_region);
330+
331+
// Require that the type `concrete_ty` outlives
332+
// `least_region`, modulo any type parameters that appear
333+
// in the type, which we ignore. This is because impl
334+
// trait values are assumed to capture all the in-scope
335+
// type parameters. This little loop here just invokes
336+
// `outlives` repeatedly, draining all the nested
337+
// obligations that result.
338+
let mut types = vec![concrete_ty];
339+
let bound_region = |r| self.sub_regions(infer::CallReturn(span), least_region, r);
340+
while let Some(ty) = types.pop() {
341+
let mut components = self.tcx.outlives_components(ty);
342+
while let Some(component) = components.pop() {
343+
match component {
344+
Component::Region(r) => {
345+
bound_region(r);
346+
}
347+
348+
Component::Param(_) => {
349+
// ignore type parameters like `T`, they are captured
350+
// implicitly by the `impl Trait`
351+
}
352+
353+
Component::UnresolvedInferenceVariable(_) => {
354+
// we should get an error that more type
355+
// annotations are needed in this case
356+
self.tcx
357+
.sess
358+
.delay_span_bug(span, "unresolved inf var in anon");
359+
}
360+
361+
Component::Projection(ty::ProjectionTy {
362+
substs,
363+
item_def_id: _,
364+
}) => {
365+
for r in substs.regions() {
366+
bound_region(r);
367+
}
368+
types.extend(substs.types());
369+
}
370+
371+
Component::EscapingProjection(more_components) => {
372+
components.extend(more_components);
373+
}
374+
}
375+
}
376+
}
377+
}
118378
}
119379

120380
struct Instantiator<'a, 'gcx: 'tcx, 'tcx: 'a> {

src/librustc/infer/outlives/free_region_map.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,6 @@ impl<'tcx> FreeRegionMap<'tcx> {
3838
}
3939
}
4040

41-
/// Tests whether `r_a <= r_b`. Both must be free regions or
42-
/// `'static`.
43-
pub fn sub_free_regions<'a, 'gcx>(&self,
44-
r_a: Region<'tcx>,
45-
r_b: Region<'tcx>)
46-
-> bool {
47-
assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
48-
if let ty::ReStatic = r_b {
49-
true // `'a <= 'static` is just always true, and not stored in the relation explicitly
50-
} else {
51-
r_a == r_b || self.relation.contains(&r_a, &r_b)
52-
}
53-
}
54-
5541
/// Compute the least-upper-bound of two free regions. In some
5642
/// cases, this is more conservative than necessary, in order to
5743
/// avoid making arbitrary choices. See
@@ -75,6 +61,29 @@ impl<'tcx> FreeRegionMap<'tcx> {
7561
}
7662
}
7763

64+
/// The NLL region handling code represents free region relations in a
65+
/// slightly different way; this trait allows functions to be abstract
66+
/// over which version is in use.
67+
pub trait FreeRegionRelations<'tcx> {
68+
/// Tests whether `r_a <= r_b`. Both must be free regions or
69+
/// `'static`.
70+
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
71+
}
72+
73+
impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
74+
fn sub_free_regions(&self,
75+
r_a: Region<'tcx>,
76+
r_b: Region<'tcx>)
77+
-> bool {
78+
assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
79+
if let ty::ReStatic = r_b {
80+
true // `'a <= 'static` is just always true, and not stored in the relation explicitly
81+
} else {
82+
r_a == r_b || self.relation.contains(&r_a, &r_b)
83+
}
84+
}
85+
}
86+
7887
fn is_free(r: Region) -> bool {
7988
match *r {
8089
ty::ReEarlyBound(_) | ty::ReFree(_) => true,

src/librustc/middle/free_region.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//! `TransitiveRelation` type and use that to decide when one free
1616
//! region outlives another and so forth.
1717
18-
use infer::outlives::free_region_map::FreeRegionMap;
18+
use infer::outlives::free_region_map::{FreeRegionMap, FreeRegionRelations};
1919
use hir::def_id::DefId;
2020
use middle::region;
2121
use ty::{self, TyCtxt, Region};

0 commit comments

Comments
 (0)