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
This commit is contained in:
commit
d0f8e2913a
69 changed files with 4820 additions and 3601 deletions
|
|
@ -27,7 +27,6 @@ trait A<'a> {
|
|||
|
||||
impl<'a> A<'a> for B {
|
||||
fn foo<F>(&mut self, f: F) //~ ERROR impl has stricter
|
||||
//~^ WARNING future release
|
||||
where F: fmt::Debug + 'static,
|
||||
{
|
||||
self.list.push(Box::new(f));
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ fn main() {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.use_x.nll.0.mir
|
||||
// | '_#0r: {bb0[0], bb0[1], '_#0r}
|
||||
// | '_#1r: {bb0[0], bb0[1], '_#0r, '_#1r}
|
||||
// | '_#2r: {bb0[0], bb0[1], '_#2r}
|
||||
// ...
|
||||
// fn use_x(_1: &'_#0r mut i32, _2: &'_#1r u32, _3: &'_#0r u32, _4: &'_#2r u32) -> bool {
|
||||
// | '_#0r: {bb0[0], bb0[1], '_#0r, '_#1r, '_#2r, '_#3r}
|
||||
// | '_#1r: {bb0[0], bb0[1], '_#1r}
|
||||
// | '_#2r: {bb0[0], bb0[1], '_#1r, '_#2r}
|
||||
// | '_#3r: {bb0[0], bb0[1], '_#3r}
|
||||
// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {
|
||||
// END rustc.use_x.nll.0.mir
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ fn main() {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#5r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// | '_#6r: {bb0[6], bb0[7], bb0[8], bb0[9], bb0[10], bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// ...
|
||||
// | '_#7r: {bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// | '_#8r: {bb0[11], bb0[12], bb0[13], bb0[14]}
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// let _2: &'_#5r mut i32;
|
||||
// let _2: &'_#6r mut i32;
|
||||
// ...
|
||||
// let _4: &'_#7r mut i32;
|
||||
// let _4: &'_#8r mut i32;
|
||||
// END rustc.main.nll.0.mir
|
||||
|
|
|
|||
|
|
@ -31,15 +31,15 @@ fn main() {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#0r: {bb1[1], bb2[0], bb2[1]}
|
||||
// | '_#1r: {bb1[1], bb2[0], bb2[1]}
|
||||
// | '_#2r: {bb1[1], bb2[0], bb2[1]}
|
||||
// ...
|
||||
// let _2: &'_#1r usize;
|
||||
// let _2: &'_#2r usize;
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// bb1: {
|
||||
// | Live variables at bb1[0]: [_1, _3]
|
||||
// _2 = &'_#0r _1[_3];
|
||||
// _2 = &'_#1r _1[_3];
|
||||
// | Live variables at bb1[1]: [_2]
|
||||
// switchInt(const true) -> [0u8: bb3, otherwise: bb2];
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -44,5 +44,5 @@ unsafe impl<#[may_dangle] T> Drop for Wrap<T> {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]}
|
||||
// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1]}
|
||||
// END rustc.main.nll.0.mir
|
||||
|
|
|
|||
|
|
@ -46,5 +46,5 @@ impl<T> Drop for Wrap<T> {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#4r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]}
|
||||
// | '_#5r: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb3[1], bb3[2], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb7[2], bb8[0]}
|
||||
// END rustc.main.nll.0.mir
|
||||
|
|
|
|||
|
|
@ -36,14 +36,14 @@ fn main() {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#0r: {bb1[1], bb2[0], bb2[1]}
|
||||
// | '_#1r: {bb1[1], bb2[0], bb2[1]}
|
||||
// ...
|
||||
// | '_#2r: {bb7[2], bb7[3], bb7[4]}
|
||||
// | '_#3r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]}
|
||||
// | '_#3r: {bb7[2], bb7[3], bb7[4]}
|
||||
// | '_#4r: {bb1[1], bb2[0], bb2[1], bb7[2], bb7[3], bb7[4]}
|
||||
// ...
|
||||
// let mut _2: &'_#3r usize;
|
||||
// let mut _2: &'_#4r usize;
|
||||
// ...
|
||||
// _2 = &'_#0r _1[_3];
|
||||
// _2 = &'_#1r _1[_3];
|
||||
// ...
|
||||
// _2 = &'_#2r (*_11);
|
||||
// _2 = &'_#3r (*_11);
|
||||
// END rustc.main.nll.0.mir
|
||||
|
|
|
|||
|
|
@ -32,16 +32,16 @@ fn main() {
|
|||
|
||||
// END RUST SOURCE
|
||||
// START rustc.main.nll.0.mir
|
||||
// | '_#0r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]}
|
||||
// | '_#1r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]}
|
||||
// | '_#2r: {bb1[5], bb1[6], bb2[0], bb2[1]}
|
||||
// | '_#2r: {bb1[1], bb1[2], bb1[3], bb1[4], bb1[5], bb1[6], bb2[0], bb2[1]}
|
||||
// | '_#3r: {bb1[5], bb1[6], bb2[0], bb2[1]}
|
||||
// END rustc.main.nll.0.mir
|
||||
// START rustc.main.nll.0.mir
|
||||
// let _2: &'_#1r usize;
|
||||
// let _2: &'_#2r usize;
|
||||
// ...
|
||||
// let _6: &'_#2r usize;
|
||||
// let _6: &'_#3r usize;
|
||||
// ...
|
||||
// _2 = &'_#0r _1[_3];
|
||||
// _2 = &'_#1r _1[_3];
|
||||
// ...
|
||||
// _7 = _2;
|
||||
// ...
|
||||
|
|
|
|||
44
src/test/run-pass/implied-bounds-closure-arg-outlives.rs
Normal file
44
src/test/run-pass/implied-bounds-closure-arg-outlives.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test that we are able to handle the relationships between free
|
||||
// regions bound in a closure callback.
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyCx<'short, 'long: 'short> {
|
||||
short: &'short u32,
|
||||
long: &'long u32,
|
||||
}
|
||||
|
||||
impl<'short, 'long> MyCx<'short, 'long> {
|
||||
fn short(self) -> &'short u32 { self.short }
|
||||
fn long(self) -> &'long u32 { self.long }
|
||||
fn set_short(&mut self, v: &'short u32) { self.short = v; }
|
||||
}
|
||||
|
||||
fn with<F, R>(op: F) -> R
|
||||
where
|
||||
F: for<'short, 'long> FnOnce(MyCx<'short, 'long>) -> R,
|
||||
{
|
||||
op(MyCx {
|
||||
short: &22,
|
||||
long: &22,
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
with(|mut cx| {
|
||||
// For this to type-check, we need to be able to deduce that
|
||||
// the lifetime of `l` can be `'short`, even though it has
|
||||
// input from `'long`.
|
||||
let l = if true { cx.long() } else { cx.short() };
|
||||
cx.set_short(l);
|
||||
});
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ impl<'a,'tcx> Foo<'a,'tcx> {
|
|||
// inferring `'_2` to be `'static` in this case, because
|
||||
// it is created outside the closure but then related to
|
||||
// regions bound by the closure itself. See the
|
||||
// `region_inference.rs` file (and the `givens` field, in
|
||||
// `region_constraints.rs` file (and the `givens` field, in
|
||||
// particular) for more details.
|
||||
this.foo()
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
error: impl has stricter requirements than trait
|
||||
error[E0276]: impl has stricter requirements than trait
|
||||
--> $DIR/proj-outlives-region.rs:19:5
|
||||
|
|
||||
14 | fn foo() where T: 'a;
|
||||
|
|
@ -6,10 +6,6 @@ error: impl has stricter requirements than trait
|
|||
...
|
||||
19 | fn foo() where U: 'a { } //~ ERROR E0276
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `U: 'a`
|
||||
|
|
||||
= note: #[deny(extra_requirement_in_impl)] on by default
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #37166 <https://github.com/rust-lang/rust/issues/37166>
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
error: impl has stricter requirements than trait
|
||||
error[E0276]: impl has stricter requirements than trait
|
||||
--> $DIR/region-unrelated.rs:19:5
|
||||
|
|
||||
14 | fn foo() where T: 'a;
|
||||
|
|
@ -6,10 +6,6 @@ error: impl has stricter requirements than trait
|
|||
...
|
||||
19 | fn foo() where V: 'a { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ impl has extra requirement `V: 'a`
|
||||
|
|
||||
= note: #[deny(extra_requirement_in_impl)] on by default
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #37166 <https://github.com/rust-lang/rust/issues/37166>
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
53
src/test/ui/nll/get_default.rs
Normal file
53
src/test/ui/nll/get_default.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Basic test for free regions in the NLL code. This test ought to
|
||||
// report an error due to a reborrowing constraint. Right now, we get
|
||||
// a variety of errors from the older, AST-based machinery (notably
|
||||
// borrowck), and then we get the NLL error at the end.
|
||||
|
||||
// compile-flags:-Znll -Zborrowck-mir
|
||||
|
||||
struct Map {
|
||||
}
|
||||
|
||||
impl Map {
|
||||
fn get(&self) -> Option<&String> { None }
|
||||
fn set(&mut self, v: String) { }
|
||||
}
|
||||
|
||||
fn ok(map: &mut Map) -> &String {
|
||||
loop {
|
||||
match map.get() {
|
||||
Some(v) => {
|
||||
return v;
|
||||
}
|
||||
None => {
|
||||
map.set(String::new()); // Just AST errors here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn err(map: &mut Map) -> &String {
|
||||
loop {
|
||||
match map.get() {
|
||||
Some(v) => {
|
||||
map.set(String::new()); // Both AST and MIR error here
|
||||
return v;
|
||||
}
|
||||
None => {
|
||||
map.set(String::new()); // Just AST errors here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
47
src/test/ui/nll/get_default.stderr
Normal file
47
src/test/ui/nll/get_default.stderr
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:33:17
|
||||
|
|
||||
28 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
33 | map.set(String::new()); // Just AST errors here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
37 | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:43:17
|
||||
|
|
||||
41 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
42 | Some(v) => {
|
||||
43 | map.set(String::new()); // Both AST and MIR error here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
51 | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable (Ast)
|
||||
--> $DIR/get_default.rs:47:17
|
||||
|
|
||||
41 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
...
|
||||
47 | map.set(String::new()); // Just AST errors here
|
||||
| ^^^ mutable borrow occurs here
|
||||
...
|
||||
51 | }
|
||||
| - immutable borrow ends here
|
||||
|
||||
error[E0502]: cannot borrow `(*map)` as mutable because it is also borrowed as immutable (Mir)
|
||||
--> $DIR/get_default.rs:43:17
|
||||
|
|
||||
41 | match map.get() {
|
||||
| --- immutable borrow occurs here
|
||||
42 | Some(v) => {
|
||||
43 | map.set(String::new()); // Both AST and MIR error here
|
||||
| ^^^ mutable borrow occurs here
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue