Skip to content

Commit 8503b3f

Browse files
committed
Auto merge of #46319 - nikomatsakis:nll-master-to-rust-master-2, r=pnkfelix
NLL: improve inference with flow results, represent regions with bitsets, and more This PR begins with a number of edits to the NLL code and then includes a large number of smaller refactorings (these refactorings ought not to change behavior). There are a lot of commits here, but each is individually simple. The goal is to land everything up to but not including the changes to how we handle closures, which are conceptually more complex. The NLL specific changes are as follows (in order of appearance): **Modify the region inferencer's approach to free regions.** Previously, for each free region (lifetime parameter) `'a`, it would compute the set of other free regions that `'a` outlives (e.g., if we have `where 'a: 'b`, then this set would be `{'a, 'b}`). Then it would mark those free regions as "constants" and report an error if inference tried to extend `'a` to include any other region (e.g., `'c`) that is not in that outlives set. In this way, the value of `'a` would never grow beyond the maximum that could type check. The new approach is to allow `'a` to grow larger. Then, after the fact, we check over the value of `'a` and see what other free regions it is required to outlive, and we check that those outlives relationships are justified by the where clauses in scope etc. **Modify constraint generation to consider maybe-init.** When we have a "drop-live" variable `x` (i.e., a variable that will be dropped but will not be otherwise used), we now consider whether `x` is "maybe initialized" at that point. If not, then we know the drop is a no-op, and we can allow its regions to be dead. Due to limitations in the fragment code, this currently only works at the level of entire variables. **Change representation of regions to use a `BitMatrix`.** We used to use a `BTreeSet`, which was rather silly. We now use a MxN matrix of bits, where `M` is the number of variables and `N` is the number of possible elements in each set (size of the CFG + number of free regions). The remaining commits (starting from extract the `implied_bounds` code into a helper function ") are all "no-op" refactorings, I believe. ~~One concern I have is with the commit "with -Zverbose, print all details of closure substs"; this commit seems to include some "internal" stuff in the mir-dump files, such as internal interner numbers, that I fear may vary by platform. Annoying. I guess we will see.~~ (I removed this commit.) As for reviewer, @arielb1 has been reviewing the PRs, and they are certainly welcome to review this one too. But I figured it'd maybe be good to have more people taking a look and being familiar with this code, so I'll "nominate" @pnkfelix . r? @pnkfelix
2 parents 226656f + a6adc74 commit 8503b3f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3289
-2115
lines changed

src/librustc/infer/mod.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub use ty::IntVarValue;
1818
pub use self::freshen::TypeFreshener;
1919

2020
use hir::def_id::DefId;
21-
use middle::free_region::{FreeRegionMap, RegionRelations};
21+
use middle::free_region::RegionRelations;
2222
use middle::region;
2323
use middle::lang_items;
2424
use mir::tcx::PlaceTy;
@@ -44,6 +44,7 @@ use self::higher_ranked::HrMatchResult;
4444
use self::region_constraints::{RegionConstraintCollector, RegionSnapshot};
4545
use self::region_constraints::{GenericKind, VerifyBound, RegionConstraintData, VarOrigins};
4646
use self::lexical_region_resolve::LexicalRegionResolutions;
47+
use self::outlives::env::OutlivesEnvironment;
4748
use self::type_variable::TypeVariableOrigin;
4849
use self::unify_key::ToType;
4950

@@ -58,15 +59,13 @@ pub mod lattice;
5859
mod lub;
5960
pub mod region_constraints;
6061
mod lexical_region_resolve;
61-
mod outlives;
62+
pub mod outlives;
6263
pub mod resolve;
6364
mod freshen;
6465
mod sub;
6566
pub mod type_variable;
6667
pub mod unify_key;
6768

68-
pub use self::outlives::env::OutlivesEnvironment;
69-
7069
#[must_use]
7170
pub struct InferOk<'tcx, T> {
7271
pub value: T,
@@ -1157,15 +1156,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
11571156
pub fn resolve_regions_and_report_errors(&self,
11581157
region_context: DefId,
11591158
region_map: &region::ScopeTree,
1160-
free_regions: &FreeRegionMap<'tcx>) {
1159+
outlives_env: &OutlivesEnvironment<'tcx>) {
11611160
assert!(self.is_tainted_by_errors() || self.region_obligations.borrow().is_empty(),
11621161
"region_obligations not empty: {:#?}",
11631162
self.region_obligations.borrow());
11641163

11651164
let region_rels = &RegionRelations::new(self.tcx,
11661165
region_context,
11671166
region_map,
1168-
free_regions);
1167+
outlives_env.free_region_map());
11691168
let (var_origins, data) = self.region_constraints.borrow_mut()
11701169
.take()
11711170
.expect("regions already resolved")

src/librustc/infer/outlives/bounds.rs

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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+
/// Outlives bounds are relationships between generic parameters,
20+
/// whether they both be regions (`'a: 'b`) or whether types are
21+
/// involved (`T: 'a`). These relationships can be extracted from the
22+
/// full set of predicates we understand or also from types (in which
23+
/// case they are called implied bounds). They are fed to the
24+
/// `OutlivesEnv` which in turn is supplied to the region checker and
25+
/// other parts of the inference system.
26+
#[derive(Debug)]
27+
pub enum OutlivesBound<'tcx> {
28+
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
29+
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
30+
RegionSubProjection(ty::Region<'tcx>, ty::ProjectionTy<'tcx>),
31+
}
32+
33+
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
34+
/// Implied bounds are region relationships that we deduce
35+
/// automatically. The idea is that (e.g.) a caller must check that a
36+
/// function's argument types are well-formed immediately before
37+
/// calling that fn, and hence the *callee* can assume that its
38+
/// argument types are well-formed. This may imply certain relationships
39+
/// between generic parameters. For example:
40+
///
41+
/// fn foo<'a,T>(x: &'a T)
42+
///
43+
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
44+
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
45+
///
46+
/// # Parameters
47+
///
48+
/// - `param_env`, the where-clauses in scope
49+
/// - `body_id`, the body-id to use when normalizing assoc types.
50+
/// Note that this may cause outlives obligations to be injected
51+
/// into the inference context with this body-id.
52+
/// - `ty`, the type that we are supposed to assume is WF.
53+
/// - `span`, a span to use when normalizing, hopefully not important,
54+
/// might be useful if a `bug!` occurs.
55+
pub fn implied_outlives_bounds(
56+
&self,
57+
param_env: ty::ParamEnv<'tcx>,
58+
body_id: ast::NodeId,
59+
ty: Ty<'tcx>,
60+
span: Span,
61+
) -> Vec<OutlivesBound<'tcx>> {
62+
let tcx = self.tcx;
63+
64+
// Sometimes when we ask what it takes for T: WF, we get back that
65+
// U: WF is required; in that case, we push U onto this stack and
66+
// process it next. Currently (at least) these resulting
67+
// predicates are always guaranteed to be a subset of the original
68+
// type, so we need not fear non-termination.
69+
let mut wf_types = vec![ty];
70+
71+
let mut implied_bounds = vec![];
72+
73+
let mut fulfill_cx = FulfillmentContext::new();
74+
75+
while let Some(ty) = wf_types.pop() {
76+
// Compute the obligations for `ty` to be well-formed. If `ty` is
77+
// an unresolved inference variable, just substituted an empty set
78+
// -- because the return type here is going to be things we *add*
79+
// to the environment, it's always ok for this set to be smaller
80+
// than the ultimate set. (Note: normally there won't be
81+
// unresolved inference variables here anyway, but there might be
82+
// during typeck under some circumstances.)
83+
let obligations = wf::obligations(self, param_env, body_id, ty, span).unwrap_or(vec![]);
84+
85+
// NB: All of these predicates *ought* to be easily proven
86+
// true. In fact, their correctness is (mostly) implied by
87+
// other parts of the program. However, in #42552, we had
88+
// an annoying scenario where:
89+
//
90+
// - Some `T::Foo` gets normalized, resulting in a
91+
// variable `_1` and a `T: Trait<Foo=_1>` constraint
92+
// (not sure why it couldn't immediately get
93+
// solved). This result of `_1` got cached.
94+
// - These obligations were dropped on the floor here,
95+
// rather than being registered.
96+
// - Then later we would get a request to normalize
97+
// `T::Foo` which would result in `_1` being used from
98+
// the cache, but hence without the `T: Trait<Foo=_1>`
99+
// constraint. As a result, `_1` never gets resolved,
100+
// and we get an ICE (in dropck).
101+
//
102+
// Therefore, we register any predicates involving
103+
// inference variables. We restrict ourselves to those
104+
// involving inference variables both for efficiency and
105+
// to avoids duplicate errors that otherwise show up.
106+
fulfill_cx.register_predicate_obligations(
107+
self,
108+
obligations
109+
.iter()
110+
.filter(|o| o.predicate.has_infer_types())
111+
.cloned(),
112+
);
113+
114+
// From the full set of obligations, just filter down to the
115+
// region relationships.
116+
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
117+
assert!(!obligation.has_escaping_regions());
118+
match obligation.predicate {
119+
ty::Predicate::Trait(..) |
120+
ty::Predicate::Equate(..) |
121+
ty::Predicate::Subtype(..) |
122+
ty::Predicate::Projection(..) |
123+
ty::Predicate::ClosureKind(..) |
124+
ty::Predicate::ObjectSafe(..) |
125+
ty::Predicate::ConstEvaluatable(..) => vec![],
126+
127+
ty::Predicate::WellFormed(subty) => {
128+
wf_types.push(subty);
129+
vec![]
130+
}
131+
132+
ty::Predicate::RegionOutlives(ref data) => match data.no_late_bound_regions() {
133+
None => vec![],
134+
Some(ty::OutlivesPredicate(r_a, r_b)) => {
135+
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
136+
}
137+
},
138+
139+
ty::Predicate::TypeOutlives(ref data) => match data.no_late_bound_regions() {
140+
None => vec![],
141+
Some(ty::OutlivesPredicate(ty_a, r_b)) => {
142+
let ty_a = self.resolve_type_vars_if_possible(&ty_a);
143+
let components = tcx.outlives_components(ty_a);
144+
Self::implied_bounds_from_components(r_b, components)
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<OutlivesBound<'tcx>> {
169+
sup_components
170+
.into_iter()
171+
.flat_map(|component| {
172+
match component {
173+
Component::Region(r) =>
174+
vec![OutlivesBound::RegionSubRegion(sub_region, r)],
175+
Component::Param(p) =>
176+
vec![OutlivesBound::RegionSubParam(sub_region, p)],
177+
Component::Projection(p) =>
178+
vec![OutlivesBound::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+
}
196+
197+
pub fn explicit_outlives_bounds<'tcx>(
198+
param_env: ty::ParamEnv<'tcx>,
199+
) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx {
200+
debug!("explicit_outlives_bounds()");
201+
param_env
202+
.caller_bounds
203+
.into_iter()
204+
.filter_map(move |predicate| match predicate {
205+
ty::Predicate::Projection(..) |
206+
ty::Predicate::Trait(..) |
207+
ty::Predicate::Equate(..) |
208+
ty::Predicate::Subtype(..) |
209+
ty::Predicate::WellFormed(..) |
210+
ty::Predicate::ObjectSafe(..) |
211+
ty::Predicate::ClosureKind(..) |
212+
ty::Predicate::TypeOutlives(..) |
213+
ty::Predicate::ConstEvaluatable(..) => None,
214+
ty::Predicate::RegionOutlives(ref data) => data.no_late_bound_regions().map(
215+
|ty::OutlivesPredicate(r_a, r_b)| OutlivesBound::RegionSubRegion(r_b, r_a),
216+
),
217+
})
218+
}

0 commit comments

Comments
 (0)