Skip to content

Commit d0f8e29

Browse files
committed
Auto merge of #45825 - nikomatsakis:nll-factor-region-inference, r=arielb1
integrate MIR type-checker with NLL inference This branch refactors NLL type inference so that it uses the MIR type-checker to gather constraints. Along the way, it also refactors how region constraints are gathered in the normal inference context mildly. The new setup is like this: - What used to be `region_inference` is split into two parts: - `region_constraints`, which just collects up sets of constraints - `lexical_region_resolve`, which does the iterative, lexical region resolution - When `resolve_regions_and_report_errors` is invoked, the inference engine converts the constraints into final values. - In the MIR type checker, however, we do not invoke this method, but instead periodically take the region constraints and package them up for the NLL solver to use later. - This allows us to track when and where those constraints were incurred. - We also remove the central fulfillment context from the MIR type checker, instead instantiating new fulfillment contexts at each point. This allows us to capture the set of obligations that occurred at a particular point, and also to ensure that if the same obligation arises at two points, we will enforce the region constraints at both locations. - The MIR type checker is also enhanced to instantiate late-bound-regions with fresh variables and handle a few other corner cases that arose. - I also extracted some of the 'outlives' logic from the regionck, which will be needed later (see future work) to handle the type-outlives relationships. One concern I have with this branch: since the MIR type checker is used even without the `-Znll` switch, I'm not sure if it will impact performance. One simple fix here would be to only enable the MIR type-checker if debug-assertions are enabled, since it just serves to validate the MIR. Longer term I hope to address this by improving the interface to the trait solver to be more query-based (ongoing work). There is plenty of future work left. Here are two things that leap to mind: - **Type-region outlives.** Currently, the NLL solver will ICE if it is required to handle a constraint like `T: 'a`. Fixing this will require a small amount of refactoring to extract the implied bounds code. I plan to follow a file-up bug on this (hopefully with mentoring instructions). - **Testing.** It's a good idea to enumerate some of the tricky scenarios that need testing, but I think it'd be nice to try and parallelize some of the actual test writing (and resulting bug fixing): - Same obligation occurring at two points. - Well-formedness and trait obligations of various kinds (which are not all processed by the current MIR type-checker). - More tests for how subtyping and region inferencing interact. - More suggestions welcome! r? @arielb1
2 parents 58d8761 + 8c109f5 commit d0f8e29

Some content is hidden

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

69 files changed

+4835
-3616
lines changed

src/librustc/infer/README.md

+213-225
Large diffs are not rendered by default.

src/librustc/infer/equate.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
104104
a,
105105
b);
106106
let origin = Subtype(self.fields.trace.clone());
107-
self.fields.infcx.region_vars.make_eqregion(origin, a, b);
107+
self.fields.infcx.borrow_region_constraints()
108+
.make_eqregion(origin, a, b);
108109
Ok(a)
109110
}
110111

src/librustc/infer/error_reporting/different_lifetimes.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
use hir;
1414
use infer::InferCtxt;
1515
use ty::{self, Region};
16-
use infer::region_inference::RegionResolutionError::*;
17-
use infer::region_inference::RegionResolutionError;
16+
use infer::lexical_region_resolve::RegionResolutionError::*;
17+
use infer::lexical_region_resolve::RegionResolutionError;
1818
use hir::map as hir_map;
1919
use middle::resolve_lifetime as rl;
2020
use hir::intravisit::{self, Visitor, NestedVisitorMap};

src/librustc/infer/error_reporting/mod.rs

+37-39
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@
5757
5858
use infer;
5959
use super::{InferCtxt, TypeTrace, SubregionOrigin, RegionVariableOrigin, ValuePairs};
60-
use super::region_inference::{RegionResolutionError, ConcreteFailure, SubSupConflict,
61-
GenericBoundFailure, GenericKind};
60+
use super::region_constraints::GenericKind;
61+
use super::lexical_region_resolve::RegionResolutionError;
6262

6363
use std::fmt;
6464
use hir;
@@ -177,13 +177,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
177177

178178
ty::ReEarlyBound(_) |
179179
ty::ReFree(_) => {
180-
let scope = match *region {
181-
ty::ReEarlyBound(ref br) => {
182-
self.parent_def_id(br.def_id).unwrap()
183-
}
184-
ty::ReFree(ref fr) => fr.scope,
185-
_ => bug!()
186-
};
180+
let scope = region.free_region_binding_scope(self);
187181
let prefix = match *region {
188182
ty::ReEarlyBound(ref br) => {
189183
format!("the lifetime {} as defined on", br.name)
@@ -293,33 +287,37 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
293287
debug!("report_region_errors: error = {:?}", error);
294288

295289
if !self.try_report_named_anon_conflict(&error) &&
296-
!self.try_report_anon_anon_conflict(&error) {
297-
298-
match error.clone() {
299-
// These errors could indicate all manner of different
300-
// problems with many different solutions. Rather
301-
// than generate a "one size fits all" error, what we
302-
// attempt to do is go through a number of specific
303-
// scenarios and try to find the best way to present
304-
// the error. If all of these fails, we fall back to a rather
305-
// general bit of code that displays the error information
306-
ConcreteFailure(origin, sub, sup) => {
307-
self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit();
308-
}
309-
310-
GenericBoundFailure(kind, param_ty, sub) => {
311-
self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub);
312-
}
313-
314-
SubSupConflict(var_origin, sub_origin, sub_r, sup_origin, sup_r) => {
290+
!self.try_report_anon_anon_conflict(&error)
291+
{
292+
match error.clone() {
293+
// These errors could indicate all manner of different
294+
// problems with many different solutions. Rather
295+
// than generate a "one size fits all" error, what we
296+
// attempt to do is go through a number of specific
297+
// scenarios and try to find the best way to present
298+
// the error. If all of these fails, we fall back to a rather
299+
// general bit of code that displays the error information
300+
RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
301+
self.report_concrete_failure(region_scope_tree, origin, sub, sup).emit();
302+
}
303+
304+
RegionResolutionError::GenericBoundFailure(kind, param_ty, sub) => {
305+
self.report_generic_bound_failure(region_scope_tree, kind, param_ty, sub);
306+
}
307+
308+
RegionResolutionError::SubSupConflict(var_origin,
309+
sub_origin,
310+
sub_r,
311+
sup_origin,
312+
sup_r) => {
315313
self.report_sub_sup_conflict(region_scope_tree,
316314
var_origin,
317315
sub_origin,
318316
sub_r,
319317
sup_origin,
320318
sup_r);
321-
}
322-
}
319+
}
320+
}
323321
}
324322
}
325323
}
@@ -351,9 +349,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
351349
// the only thing in the list.
352350

353351
let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
354-
ConcreteFailure(..) => false,
355-
SubSupConflict(..) => false,
356-
GenericBoundFailure(..) => true,
352+
RegionResolutionError::GenericBoundFailure(..) => true,
353+
RegionResolutionError::ConcreteFailure(..) |
354+
RegionResolutionError::SubSupConflict(..) => false,
357355
};
358356

359357

@@ -365,9 +363,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
365363

366364
// sort the errors by span, for better error message stability.
367365
errors.sort_by_key(|u| match *u {
368-
ConcreteFailure(ref sro, _, _) => sro.span(),
369-
GenericBoundFailure(ref sro, _, _) => sro.span(),
370-
SubSupConflict(ref rvo, _, _, _, _) => rvo.span(),
366+
RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
367+
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
368+
RegionResolutionError::SubSupConflict(ref rvo, _, _, _, _) => rvo.span(),
371369
});
372370
errors
373371
}
@@ -880,14 +878,13 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
880878
};
881879

882880
if let SubregionOrigin::CompareImplMethodObligation {
883-
span, item_name, impl_item_def_id, trait_item_def_id, lint_id
881+
span, item_name, impl_item_def_id, trait_item_def_id,
884882
} = origin {
885883
self.report_extra_impl_obligation(span,
886884
item_name,
887885
impl_item_def_id,
888886
trait_item_def_id,
889-
&format!("`{}: {}`", bound_kind, sub),
890-
lint_id)
887+
&format!("`{}: {}`", bound_kind, sub))
891888
.emit();
892889
return;
893890
}
@@ -1026,6 +1023,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10261023
let var_name = self.tcx.hir.name(var_node_id);
10271024
format!(" for capture of `{}` by closure", var_name)
10281025
}
1026+
infer::NLL(..) => bug!("NLL variable found in lexical phase"),
10291027
};
10301028

10311029
struct_span_err!(self.tcx.sess, var_origin.span(), E0495,

src/librustc/infer/error_reporting/named_anon_conflict.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
//! Error Reporting for Anonymous Region Lifetime Errors
1212
//! where one region is named and the other is anonymous.
1313
use infer::InferCtxt;
14-
use infer::region_inference::RegionResolutionError::*;
15-
use infer::region_inference::RegionResolutionError;
14+
use infer::lexical_region_resolve::RegionResolutionError::*;
15+
use infer::lexical_region_resolve::RegionResolutionError;
1616
use ty;
1717

1818
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {

src/librustc/infer/error_reporting/note.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -445,14 +445,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
445445
infer::CompareImplMethodObligation { span,
446446
item_name,
447447
impl_item_def_id,
448-
trait_item_def_id,
449-
lint_id } => {
448+
trait_item_def_id } => {
450449
self.report_extra_impl_obligation(span,
451450
item_name,
452451
impl_item_def_id,
453452
trait_item_def_id,
454-
&format!("`{}: {}`", sup, sub),
455-
lint_id)
453+
&format!("`{}: {}`", sup, sub))
456454
}
457455
}
458456
}

src/librustc/infer/fudge.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
7878
self.type_variables.borrow_mut().types_created_since_snapshot(
7979
&snapshot.type_snapshot);
8080
let region_vars =
81-
self.region_vars.vars_created_since_snapshot(
82-
&snapshot.region_vars_snapshot);
81+
self.borrow_region_constraints().vars_created_since_snapshot(
82+
&snapshot.region_constraints_snapshot);
8383

8484
Ok((type_variables, region_vars, value))
8585
}

src/librustc/infer/glb.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl<'combine, 'infcx, 'gcx, 'tcx> TypeRelation<'infcx, 'gcx, 'tcx>
6767
b);
6868

6969
let origin = Subtype(self.fields.trace.clone());
70-
Ok(self.fields.infcx.region_vars.glb_regions(origin, a, b))
70+
Ok(self.fields.infcx.borrow_region_constraints().glb_regions(self.tcx(), origin, a, b))
7171
}
7272

7373
fn binders<T>(&mut self, a: &ty::Binder<T>, b: &ty::Binder<T>)

src/librustc/infer/higher_ranked/mod.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use super::{CombinedSnapshot,
1717
SubregionOrigin,
1818
SkolemizationMap};
1919
use super::combine::CombineFields;
20-
use super::region_inference::{TaintDirections};
20+
use super::region_constraints::{TaintDirections};
2121

2222
use ty::{self, TyCtxt, Binder, TypeFoldable};
2323
use ty::error::TypeError;
@@ -176,9 +176,10 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
176176
.filter(|&r| r != representative)
177177
{
178178
let origin = SubregionOrigin::Subtype(self.trace.clone());
179-
self.infcx.region_vars.make_eqregion(origin,
180-
*representative,
181-
*region);
179+
self.infcx.borrow_region_constraints()
180+
.make_eqregion(origin,
181+
*representative,
182+
*region);
182183
}
183184
}
184185

@@ -427,7 +428,7 @@ impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
427428
fn fresh_bound_variable<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
428429
debruijn: ty::DebruijnIndex)
429430
-> ty::Region<'tcx> {
430-
infcx.region_vars.new_bound(debruijn)
431+
infcx.borrow_region_constraints().new_bound(infcx.tcx, debruijn)
431432
}
432433
}
433434
}
@@ -481,7 +482,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
481482
r: ty::Region<'tcx>,
482483
directions: TaintDirections)
483484
-> FxHashSet<ty::Region<'tcx>> {
484-
self.region_vars.tainted(&snapshot.region_vars_snapshot, r, directions)
485+
self.borrow_region_constraints().tainted(
486+
self.tcx,
487+
&snapshot.region_constraints_snapshot,
488+
r,
489+
directions)
485490
}
486491

487492
fn region_vars_confined_to_snapshot(&self,
@@ -539,7 +544,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
539544
*/
540545

541546
let mut region_vars =
542-
self.region_vars.vars_created_since_snapshot(&snapshot.region_vars_snapshot);
547+
self.borrow_region_constraints().vars_created_since_snapshot(
548+
&snapshot.region_constraints_snapshot);
543549

544550
let escaping_types =
545551
self.type_variables.borrow_mut().types_escaping_snapshot(&snapshot.type_snapshot);
@@ -581,7 +587,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
581587
where T : TypeFoldable<'tcx>
582588
{
583589
let (result, map) = self.tcx.replace_late_bound_regions(binder, |br| {
584-
self.region_vars.push_skolemized(br, &snapshot.region_vars_snapshot)
590+
self.borrow_region_constraints()
591+
.push_skolemized(self.tcx, br, &snapshot.region_constraints_snapshot)
585592
});
586593

587594
debug!("skolemize_bound_regions(binder={:?}, result={:?}, map={:?})",
@@ -766,7 +773,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
766773
{
767774
debug!("pop_skolemized({:?})", skol_map);
768775
let skol_regions: FxHashSet<_> = skol_map.values().cloned().collect();
769-
self.region_vars.pop_skolemized(&skol_regions, &snapshot.region_vars_snapshot);
776+
self.borrow_region_constraints()
777+
.pop_skolemized(self.tcx, &skol_regions, &snapshot.region_constraints_snapshot);
770778
if !skol_map.is_empty() {
771779
self.projection_cache.borrow_mut().rollback_skolemized(
772780
&snapshot.projection_cache_snapshot);

src/librustc/infer/region_inference/README.md src/librustc/infer/lexical_region_resolve/README.md

+10-69
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1-
Region inference
1+
# Region inference
22

3-
# Terminology
3+
## Terminology
44

55
Note that we use the terms region and lifetime interchangeably.
66

7-
# Introduction
7+
## Introduction
8+
9+
See the [general inference README](../README.md) for an overview of
10+
how lexical-region-solving fits into the bigger picture.
811

912
Region inference uses a somewhat more involved algorithm than type
1013
inference. It is not the most efficient thing ever written though it
@@ -16,63 +19,6 @@ it's worth spending more time on a more involved analysis. Moreover,
1619
regions are a simpler case than types: they don't have aggregate
1720
structure, for example.
1821

19-
Unlike normal type inference, which is similar in spirit to H-M and thus
20-
works progressively, the region type inference works by accumulating
21-
constraints over the course of a function. Finally, at the end of
22-
processing a function, we process and solve the constraints all at
23-
once.
24-
25-
The constraints are always of one of three possible forms:
26-
27-
- `ConstrainVarSubVar(Ri, Rj)` states that region variable Ri must be
28-
a subregion of Rj
29-
- `ConstrainRegSubVar(R, Ri)` states that the concrete region R (which
30-
must not be a variable) must be a subregion of the variable Ri
31-
- `ConstrainVarSubReg(Ri, R)` states the variable Ri shoudl be less
32-
than the concrete region R. This is kind of deprecated and ought to
33-
be replaced with a verify (they essentially play the same role).
34-
35-
In addition to constraints, we also gather up a set of "verifys"
36-
(what, you don't think Verify is a noun? Get used to it my
37-
friend!). These represent relations that must hold but which don't
38-
influence inference proper. These take the form of:
39-
40-
- `VerifyRegSubReg(Ri, Rj)` indicates that Ri <= Rj must hold,
41-
where Rj is not an inference variable (and Ri may or may not contain
42-
one). This doesn't influence inference because we will already have
43-
inferred Ri to be as small as possible, so then we just test whether
44-
that result was less than Rj or not.
45-
- `VerifyGenericBound(R, Vb)` is a more complex expression which tests
46-
that the region R must satisfy the bound `Vb`. The bounds themselves
47-
may have structure like "must outlive one of the following regions"
48-
or "must outlive ALL of the following regions. These bounds arise
49-
from constraints like `T: 'a` -- if we know that `T: 'b` and `T: 'c`
50-
(say, from where clauses), then we can conclude that `T: 'a` if `'b:
51-
'a` *or* `'c: 'a`.
52-
53-
# Building up the constraints
54-
55-
Variables and constraints are created using the following methods:
56-
57-
- `new_region_var()` creates a new, unconstrained region variable;
58-
- `make_subregion(Ri, Rj)` states that Ri is a subregion of Rj
59-
- `lub_regions(Ri, Rj) -> Rk` returns a region Rk which is
60-
the smallest region that is greater than both Ri and Rj
61-
- `glb_regions(Ri, Rj) -> Rk` returns a region Rk which is
62-
the greatest region that is smaller than both Ri and Rj
63-
64-
The actual region resolution algorithm is not entirely
65-
obvious, though it is also not overly complex.
66-
67-
## Snapshotting
68-
69-
It is also permitted to try (and rollback) changes to the graph. This
70-
is done by invoking `start_snapshot()`, which returns a value. Then
71-
later you can call `rollback_to()` which undoes the work.
72-
Alternatively, you can call `commit()` which ends all snapshots.
73-
Snapshots can be recursive---so you can start a snapshot when another
74-
is in progress, but only the root snapshot can "commit".
75-
7622
## The problem
7723

7824
Basically our input is a directed graph where nodes can be divided
@@ -109,9 +55,9 @@ step where we walk over the verify bounds and check that they are
10955
satisfied. These bounds represent the "maximal" values that a region
11056
variable can take on, basically.
11157

112-
# The Region Hierarchy
58+
## The Region Hierarchy
11359

114-
## Without closures
60+
### Without closures
11561

11662
Let's first consider the region hierarchy without thinking about
11763
closures, because they add a lot of complications. The region
@@ -141,7 +87,7 @@ Within that, there are sublifetimes for the assignment pattern and
14187
also the expression `x + y`. The expression itself has sublifetimes
14288
for evaluating `x` and `y`.
14389

144-
## Function calls
90+
#s## Function calls
14591

14692
Function calls are a bit tricky. I will describe how we handle them
14793
*now* and then a bit about how we can improve them (Issue #6268).
@@ -259,7 +205,7 @@ there is a reference created whose lifetime does not enclose
259205
the borrow expression, we must issue sufficient restrictions to ensure
260206
that the pointee remains valid.
261207

262-
## Modeling closures
208+
### Modeling closures
263209

264210
Integrating closures properly into the model is a bit of
265211
work-in-progress. In an ideal world, we would model closures as
@@ -314,8 +260,3 @@ handling of closures, there are no known cases where this leads to a
314260
type-checking accepting incorrect code (though it sometimes rejects
315261
what might be considered correct code; see rust-lang/rust#22557), but
316262
it still doesn't feel like the right approach.
317-
318-
### Skolemization
319-
320-
For a discussion on skolemization and higher-ranked subtyping, please
321-
see the module `middle::infer::higher_ranked::doc`.

0 commit comments

Comments
 (0)