Skip to content

Commit aa51603

Browse files
committed
extract the implied_bounds code into a helper function
1 parent 22b3175 commit aa51603

File tree

3 files changed

+201
-172
lines changed

3 files changed

+201
-172
lines changed

src/librustc/infer/outlives/env.rs

Lines changed: 3 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@
1010

1111
use middle::free_region::FreeRegionMap;
1212
use infer::{InferCtxt, GenericKind};
13-
use traits::FulfillmentContext;
14-
use ty::{self, Ty, TypeFoldable};
15-
use ty::outlives::Component;
16-
use ty::wf;
13+
use infer::outlives::implied_bounds::ImpliedBound;
14+
use ty::{self, Ty};
1715

1816
use syntax::ast;
1917
use syntax_pos::Span;
@@ -44,24 +42,6 @@ pub struct OutlivesEnvironment<'tcx> {
4442
region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
4543
}
4644

47-
/// Implied bounds are region relationships that we deduce
48-
/// automatically. The idea is that (e.g.) a caller must check that a
49-
/// function's argument types are well-formed immediately before
50-
/// calling that fn, and hence the *callee* can assume that its
51-
/// argument types are well-formed. This may imply certain relationships
52-
/// between generic parameters. For example:
53-
///
54-
/// fn foo<'a,T>(x: &'a T)
55-
///
56-
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
57-
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
58-
#[derive(Debug)]
59-
enum ImpliedBound<'tcx> {
60-
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
61-
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
62-
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
63-
}
64-
6545
impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
6646
pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
6747
let mut free_region_map = FreeRegionMap::new();
@@ -163,7 +143,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
163143
for &ty in fn_sig_tys {
164144
let ty = infcx.resolve_type_vars_if_possible(&ty);
165145
debug!("add_implied_bounds: ty = {}", ty);
166-
let implied_bounds = self.implied_bounds(infcx, body_id, ty, span);
146+
let implied_bounds = infcx.implied_bounds(self.param_env, body_id, ty, span);
167147

168148
// But also record other relationships, such as `T:'x`,
169149
// that don't go into the free-region-map but which we use
@@ -203,153 +183,4 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> OutlivesEnvironment<'tcx> {
203183
}
204184
}
205185
}
206-
207-
/// Compute the implied bounds that a callee/impl can assume based on
208-
/// the fact that caller/projector has ensured that `ty` is WF. See
209-
/// the `ImpliedBound` type for more details.
210-
fn implied_bounds(
211-
&mut self,
212-
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
213-
body_id: ast::NodeId,
214-
ty: Ty<'tcx>,
215-
span: Span,
216-
) -> Vec<ImpliedBound<'tcx>> {
217-
let tcx = infcx.tcx;
218-
219-
// Sometimes when we ask what it takes for T: WF, we get back that
220-
// U: WF is required; in that case, we push U onto this stack and
221-
// process it next. Currently (at least) these resulting
222-
// predicates are always guaranteed to be a subset of the original
223-
// type, so we need not fear non-termination.
224-
let mut wf_types = vec![ty];
225-
226-
let mut implied_bounds = vec![];
227-
228-
let mut fulfill_cx = FulfillmentContext::new();
229-
230-
while let Some(ty) = wf_types.pop() {
231-
// Compute the obligations for `ty` to be well-formed. If `ty` is
232-
// an unresolved inference variable, just substituted an empty set
233-
// -- because the return type here is going to be things we *add*
234-
// to the environment, it's always ok for this set to be smaller
235-
// than the ultimate set. (Note: normally there won't be
236-
// unresolved inference variables here anyway, but there might be
237-
// during typeck under some circumstances.)
238-
let obligations =
239-
wf::obligations(infcx, self.param_env, body_id, ty, span).unwrap_or(vec![]);
240-
241-
// NB: All of these predicates *ought* to be easily proven
242-
// true. In fact, their correctness is (mostly) implied by
243-
// other parts of the program. However, in #42552, we had
244-
// an annoying scenario where:
245-
//
246-
// - Some `T::Foo` gets normalized, resulting in a
247-
// variable `_1` and a `T: Trait<Foo=_1>` constraint
248-
// (not sure why it couldn't immediately get
249-
// solved). This result of `_1` got cached.
250-
// - These obligations were dropped on the floor here,
251-
// rather than being registered.
252-
// - Then later we would get a request to normalize
253-
// `T::Foo` which would result in `_1` being used from
254-
// the cache, but hence without the `T: Trait<Foo=_1>`
255-
// constraint. As a result, `_1` never gets resolved,
256-
// and we get an ICE (in dropck).
257-
//
258-
// Therefore, we register any predicates involving
259-
// inference variables. We restrict ourselves to those
260-
// involving inference variables both for efficiency and
261-
// to avoids duplicate errors that otherwise show up.
262-
fulfill_cx.register_predicate_obligations(
263-
infcx,
264-
obligations
265-
.iter()
266-
.filter(|o| o.predicate.has_infer_types())
267-
.cloned());
268-
269-
// From the full set of obligations, just filter down to the
270-
// region relationships.
271-
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
272-
assert!(!obligation.has_escaping_regions());
273-
match obligation.predicate {
274-
ty::Predicate::Trait(..) |
275-
ty::Predicate::Equate(..) |
276-
ty::Predicate::Subtype(..) |
277-
ty::Predicate::Projection(..) |
278-
ty::Predicate::ClosureKind(..) |
279-
ty::Predicate::ObjectSafe(..) |
280-
ty::Predicate::ConstEvaluatable(..) => vec![],
281-
282-
ty::Predicate::WellFormed(subty) => {
283-
wf_types.push(subty);
284-
vec![]
285-
}
286-
287-
ty::Predicate::RegionOutlives(ref data) => {
288-
match tcx.no_late_bound_regions(data) {
289-
None => vec![],
290-
Some(ty::OutlivesPredicate(r_a, r_b)) => {
291-
vec![ImpliedBound::RegionSubRegion(r_b, r_a)]
292-
}
293-
}
294-
}
295-
296-
ty::Predicate::TypeOutlives(ref data) => {
297-
match tcx.no_late_bound_regions(data) {
298-
None => vec![],
299-
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
300-
let ty_a = infcx.resolve_type_vars_if_possible(&ty_a);
301-
let components = tcx.outlives_components(ty_a);
302-
self.implied_bounds_from_components(r_b, components)
303-
}
304-
}
305-
}
306-
}
307-
}));
308-
}
309-
310-
// Ensure that those obligations that we had to solve
311-
// get solved *here*.
312-
match fulfill_cx.select_all_or_error(infcx) {
313-
Ok(()) => (),
314-
Err(errors) => infcx.report_fulfillment_errors(&errors, None),
315-
}
316-
317-
implied_bounds
318-
}
319-
320-
/// When we have an implied bound that `T: 'a`, we can further break
321-
/// this down to determine what relationships would have to hold for
322-
/// `T: 'a` to hold. We get to assume that the caller has validated
323-
/// those relationships.
324-
fn implied_bounds_from_components(
325-
&self,
326-
sub_region: ty::Region<'tcx>,
327-
sup_components: Vec<Component<'tcx>>,
328-
) -> Vec<ImpliedBound<'tcx>> {
329-
sup_components
330-
.into_iter()
331-
.flat_map(|component| {
332-
match component {
333-
Component::Region(r) =>
334-
vec![ImpliedBound::RegionSubRegion(sub_region, r)],
335-
Component::Param(p) =>
336-
vec![ImpliedBound::RegionSubParam(sub_region, p)],
337-
Component::Projection(p) =>
338-
vec![ImpliedBound::RegionSubProjection(sub_region, p)],
339-
Component::EscapingProjection(_) =>
340-
// If the projection has escaping regions, don't
341-
// try to infer any implied bounds even for its
342-
// free components. This is conservative, because
343-
// the caller will still have to prove that those
344-
// free components outlive `sub_region`. But the
345-
// idea is that the WAY that the caller proves
346-
// that may change in the future and we want to
347-
// give ourselves room to get smarter here.
348-
vec![],
349-
Component::UnresolvedInferenceVariable(..) =>
350-
vec![],
351-
}
352-
})
353-
.collect()
354-
}
355186
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use infer::InferCtxt;
12+
use syntax::ast;
13+
use syntax::codemap::Span;
14+
use traits::FulfillmentContext;
15+
use ty::{self, Ty, TypeFoldable};
16+
use ty::outlives::Component;
17+
use ty::wf;
18+
19+
/// Implied bounds are region relationships that we deduce
20+
/// automatically. The idea is that (e.g.) a caller must check that a
21+
/// function's argument types are well-formed immediately before
22+
/// calling that fn, and hence the *callee* can assume that its
23+
/// argument types are well-formed. This may imply certain relationships
24+
/// between generic parameters. For example:
25+
///
26+
/// fn foo<'a,T>(x: &'a T)
27+
///
28+
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
29+
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
30+
#[derive(Debug)]
31+
pub enum ImpliedBound<'tcx> {
32+
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
33+
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
34+
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
35+
}
36+
37+
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
38+
/// Compute the implied bounds that a callee/impl can assume based on
39+
/// the fact that caller/projector has ensured that `ty` is WF. See
40+
/// the `ImpliedBound` type for more details.
41+
///
42+
/// # Parameters
43+
///
44+
/// - `param_env`, the where-clauses in scope
45+
/// - `body_id`, the body-id to use when normalizing assoc types.
46+
/// Note that this may cause outlives obligations to be injected
47+
/// into the inference context with this body-id.
48+
/// - `ty`, the type that we are supposed to assume is WF.
49+
/// - `span`, a span to use when normalizing, hopefully not important,
50+
/// might be useful if a `bug!` occurs.
51+
pub fn implied_bounds(
52+
&self,
53+
param_env: ty::ParamEnv<'tcx>,
54+
body_id: ast::NodeId,
55+
ty: Ty<'tcx>,
56+
span: Span,
57+
) -> Vec<ImpliedBound<'tcx>> {
58+
let tcx = self.tcx;
59+
60+
// Sometimes when we ask what it takes for T: WF, we get back that
61+
// U: WF is required; in that case, we push U onto this stack and
62+
// process it next. Currently (at least) these resulting
63+
// predicates are always guaranteed to be a subset of the original
64+
// type, so we need not fear non-termination.
65+
let mut wf_types = vec![ty];
66+
67+
let mut implied_bounds = vec![];
68+
69+
let mut fulfill_cx = FulfillmentContext::new();
70+
71+
while let Some(ty) = wf_types.pop() {
72+
// Compute the obligations for `ty` to be well-formed. If `ty` is
73+
// an unresolved inference variable, just substituted an empty set
74+
// -- because the return type here is going to be things we *add*
75+
// to the environment, it's always ok for this set to be smaller
76+
// than the ultimate set. (Note: normally there won't be
77+
// unresolved inference variables here anyway, but there might be
78+
// during typeck under some circumstances.)
79+
let obligations =
80+
wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]);
81+
82+
// NB: All of these predicates *ought* to be easily proven
83+
// true. In fact, their correctness is (mostly) implied by
84+
// other parts of the program. However, in #42552, we had
85+
// an annoying scenario where:
86+
//
87+
// - Some `T::Foo` gets normalized, resulting in a
88+
// variable `_1` and a `T: Trait<Foo=_1>` constraint
89+
// (not sure why it couldn't immediately get
90+
// solved). This result of `_1` got cached.
91+
// - These obligations were dropped on the floor here,
92+
// rather than being registered.
93+
// - Then later we would get a request to normalize
94+
// `T::Foo` which would result in `_1` being used from
95+
// the cache, but hence without the `T: Trait<Foo=_1>`
96+
// constraint. As a result, `_1` never gets resolved,
97+
// and we get an ICE (in dropck).
98+
//
99+
// Therefore, we register any predicates involving
100+
// inference variables. We restrict ourselves to those
101+
// involving inference variables both for efficiency and
102+
// to avoids duplicate errors that otherwise show up.
103+
fulfill_cx.register_predicate_obligations(
104+
self,
105+
obligations
106+
.iter()
107+
.filter(|o| o.predicate.has_infer_types())
108+
.cloned());
109+
110+
// From the full set of obligations, just filter down to the
111+
// region relationships.
112+
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
113+
assert!(!obligation.has_escaping_regions());
114+
match obligation.predicate {
115+
ty::Predicate::Trait(..) |
116+
ty::Predicate::Equate(..) |
117+
ty::Predicate::Subtype(..) |
118+
ty::Predicate::Projection(..) |
119+
ty::Predicate::ClosureKind(..) |
120+
ty::Predicate::ObjectSafe(..) |
121+
ty::Predicate::ConstEvaluatable(..) => vec![],
122+
123+
ty::Predicate::WellFormed(subty) => {
124+
wf_types.push(subty);
125+
vec![]
126+
}
127+
128+
ty::Predicate::RegionOutlives(ref data) => {
129+
match tcx.no_late_bound_regions(data) {
130+
None => vec![],
131+
Some(ty::OutlivesPredicate(r_a, r_b)) => {
132+
vec![ImpliedBound::RegionSubRegion(r_b, r_a)]
133+
}
134+
}
135+
}
136+
137+
ty::Predicate::TypeOutlives(ref data) => {
138+
match tcx.no_late_bound_regions(data) {
139+
None => vec![],
140+
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
141+
let ty_a = self.resolve_type_vars_if_possible(&ty_a);
142+
let components = tcx.outlives_components(ty_a);
143+
Self::implied_bounds_from_components(r_b, components)
144+
}
145+
}
146+
}
147+
}
148+
}));
149+
}
150+
151+
// Ensure that those obligations that we had to solve
152+
// get solved *here*.
153+
match fulfill_cx.select_all_or_error(self) {
154+
Ok(()) => (),
155+
Err(errors) => self.report_fulfillment_errors(&errors, None),
156+
}
157+
158+
implied_bounds
159+
}
160+
161+
/// When we have an implied bound that `T: 'a`, we can further break
162+
/// this down to determine what relationships would have to hold for
163+
/// `T: 'a` to hold. We get to assume that the caller has validated
164+
/// those relationships.
165+
fn implied_bounds_from_components(
166+
sub_region: ty::Region<'tcx>,
167+
sup_components: Vec<Component<'tcx>>,
168+
) -> Vec<ImpliedBound<'tcx>> {
169+
sup_components
170+
.into_iter()
171+
.flat_map(|component| {
172+
match component {
173+
Component::Region(r) =>
174+
vec![ImpliedBound::RegionSubRegion(sub_region, r)],
175+
Component::Param(p) =>
176+
vec![ImpliedBound::RegionSubParam(sub_region, p)],
177+
Component::Projection(p) =>
178+
vec![ImpliedBound::RegionSubProjection(sub_region, p)],
179+
Component::EscapingProjection(_) =>
180+
// If the projection has escaping regions, don't
181+
// try to infer any implied bounds even for its
182+
// free components. This is conservative, because
183+
// the caller will still have to prove that those
184+
// free components outlive `sub_region`. But the
185+
// idea is that the WAY that the caller proves
186+
// that may change in the future and we want to
187+
// give ourselves room to get smarter here.
188+
vec![],
189+
Component::UnresolvedInferenceVariable(..) =>
190+
vec![],
191+
}
192+
})
193+
.collect()
194+
}
195+
}

0 commit comments

Comments
 (0)