Skip to content

Commit 5a7a850

Browse files
committed
move leak-check to during coherence, candidate eval
In particular, it no longer occurs during the subtyping check. This is important for enabling lazy normalization, because the subtyping check will be producing sub-obligations that could affect its results. Consider an example like for<'a> fn(<&'a as Mirror>::Item) = fn(&'b u8) where `<T as Mirror>::Item = T` for all `T`. We will wish to produce a new subobligation like <'!1 as Mirror>::Item = &'b u8 This will, after being solved, ultimately yield a constraint that `'!1 = 'b` which will fail. But with the leak-check being performed on subtyping, there is no opportunity to normalize `<'!1 as Mirror>::Item` (unless we invoke that normalization directly from within subtyping, and I would prefer that subtyping and unification are distinct operations rather than part of the trait solving stack). The reason to keep the leak check during coherence and trait evaluation is partly for backwards compatibility. The coherence change permits impls for `fn(T)` and `fn(&T)` to co-exist, and the trait evaluation change means that we can distinguish those two cases without ambiguity errors. It also avoids recreating #57639, where we were incorrectly choosing a where clause that would have failed the leak check over the impl which succeeds. The other reason to keep the leak check in those places is that I think it is actually close to the model we want. To the point, I think the trait solver ought to have the job of "breaking down" higher-ranked region obligation like ``!1: '2` into into region obligations that operate on things in the root universe, at which point they should be handed off to polonius. The leak check isn't *really* doing that -- these obligations are still handed to the region solver to process -- but if/when we do adopt that model, the decision to pass/fail would be happening in roughly this part of the code. This change had somewhat more side-effects than I anticipated. It seems like there are cases where the leak-check was not being enforced during method proving and trait selection. I haven't quite tracked this down but I think it ought to be documented, so that we know what precisely we are committing to. One surprising test was `issue-30786.rs`. The behavior there seems a bit "fishy" to me, but the problem is not related to the leak check change as far as I can tell, but more to do with the closure signature inference code and perhaps the associated type projection, which together seem to be conspiring to produce an unexpected signature. Nonetheless, it is an example of where changing the leak-check can have some unexpected consequences: we're now failing to resolve a method earlier than we were, which suggests we might change some method resolutions that would have been ambiguous to be successful. TODO: * figure out remainig test failures * add new coherence tests for the patterns we ARE disallowing
1 parent f2cf994 commit 5a7a850

File tree

60 files changed

+511
-576
lines changed

Some content is hidden

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

60 files changed

+511
-576
lines changed

src/librustc_infer/infer/higher_ranked/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> {
3030

3131
let span = self.trace.cause.span;
3232

33-
self.infcx.commit_if_ok(|snapshot| {
33+
self.infcx.commit_if_ok(|_| {
3434
// First, we instantiate each bound region in the supertype with a
3535
// fresh placeholder region.
3636
let (b_prime, _) = self.infcx.replace_bound_vars_with_placeholders(b);
@@ -48,8 +48,6 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> {
4848
// Compare types now that bound regions have been replaced.
4949
let result = self.sub(a_is_expected).relate(&a_prime, &b_prime)?;
5050

51-
self.infcx.leak_check(!a_is_expected, snapshot)?;
52-
5351
debug!("higher_ranked_sub: OK result={:?}", result);
5452

5553
Ok(ty::Binder::bind(result))
@@ -75,7 +73,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
7573
where
7674
T: TypeFoldable<'tcx>,
7775
{
78-
let next_universe = self.create_next_universe();
76+
// Figure out what the next universe will be, but don't actually create
77+
// it until after we've done the substitution (in particular there may
78+
// be no bound variables). This is a performance optimization, since the
79+
// leak check for example can be skipped if no new universes are created
80+
// (i.e., if there are no placeholders).
81+
let next_universe = self.universe().next_universe();
7982

8083
let fld_r = |br| {
8184
self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion {
@@ -103,6 +106,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
103106

104107
let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c);
105108

109+
// If there were higher-ranked regions to replace, then actually create
110+
// the next universe (this avoids needlessly creating universes).
111+
if !map.is_empty() {
112+
let n_u = self.create_next_universe();
113+
assert_eq!(n_u, next_universe);
114+
}
115+
106116
debug!(
107117
"replace_bound_vars_with_placeholders(\
108118
next_universe={:?}, \

src/librustc_infer/infer/region_constraints/leak_check.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
286286
placeholder: ty::PlaceholderRegion,
287287
other_region: ty::Region<'tcx>,
288288
) -> TypeError<'tcx> {
289+
debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
289290
if self.overly_polymorphic {
290291
return TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region);
291292
} else {

src/librustc_trait_selection/traits/coherence.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,13 @@ fn overlap<'cx, 'tcx>(
120120
debug!("overlap(a_def_id={:?}, b_def_id={:?})", a_def_id, b_def_id);
121121

122122
selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
123-
overlap_within_probe(selcx, a_def_id, b_def_id, snapshot)
123+
overlap_within_probe(selcx, skip_leak_check, a_def_id, b_def_id, snapshot)
124124
})
125125
}
126126

127127
fn overlap_within_probe(
128128
selcx: &mut SelectionContext<'cx, 'tcx>,
129+
skip_leak_check: SkipLeakCheck,
129130
a_def_id: DefId,
130131
b_def_id: DefId,
131132
snapshot: &CombinedSnapshot<'_, 'tcx>,
@@ -180,6 +181,13 @@ fn overlap_within_probe(
180181
return None;
181182
}
182183

184+
if !skip_leak_check.is_yes() {
185+
if let Err(_) = infcx.leak_check(true, snapshot) {
186+
debug!("overlap: leak check failed");
187+
return None;
188+
}
189+
}
190+
183191
let impl_header = selcx.infcx().resolve_vars_if_possible(&a_impl_header);
184192
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
185193
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);

src/librustc_trait_selection/traits/project.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
298298
fn fold<T: TypeFoldable<'tcx>>(&mut self, value: &T) -> T {
299299
let value = self.selcx.infcx().resolve_vars_if_possible(value);
300300

301-
if !value.has_projections() {
302-
value
303-
} else {
304-
value.fold_with(self)
305-
}
301+
if !value.has_projections() { value } else { value.fold_with(self) }
306302
}
307303
}
308304

src/librustc_trait_selection/traits/select/mod.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
347347
) -> Result<EvaluationResult, OverflowError> {
348348
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
349349
let result = op(self)?;
350+
351+
match self.infcx.leak_check(true, snapshot) {
352+
Ok(()) => {}
353+
Err(_) => return Ok(EvaluatedToErr),
354+
}
355+
350356
match self.infcx.region_constraints_added_in_snapshot(snapshot) {
351357
None => Ok(result),
352358
Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)),
@@ -2402,11 +2408,7 @@ impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> {
24022408
}
24032409

24042410
fn depth(&self) -> usize {
2405-
if let Some(head) = self.head {
2406-
head.depth
2407-
} else {
2408-
0
2409-
}
2411+
if let Some(head) = self.head { head.depth } else { 0 }
24102412
}
24112413
}
24122414

src/test/ui/associated-types/associated-types-eq-hr.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ LL | where T : for<'x,'y> TheTrait<(&'x isize, &'y isize), A = &'y isize>
7474
| ------------- required by this bound in `tuple_two`
7575
...
7676
LL | tuple_two::<Tuple>();
77-
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'y, found concrete lifetime
77+
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'x, found concrete lifetime
7878

7979
error[E0277]: the trait bound `for<'x, 'y> Tuple: TheTrait<(&'x isize, &'y isize)>` is not satisfied
8080
--> $DIR/associated-types-eq-hr.rs:107:18

src/test/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
error[E0623]: lifetime mismatch
2-
--> $DIR/project-fn-ret-invariant.rs:53:21
2+
--> $DIR/project-fn-ret-invariant.rs:54:22
33
|
4-
LL | fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
5-
| -------- --------------------
6-
| |
7-
| this parameter and the return type are declared with different lifetimes...
8-
LL | let a = bar(foo, y);
9-
| ^ ...but data from `x` is returned here
4+
LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
5+
| -------- --------------------
6+
| |
7+
| this parameter and the return type are declared with different lifetimes...
8+
LL | let a = bar(foo, y);
9+
| ^ ...but data from `x` is returned here
1010

1111
error[E0623]: lifetime mismatch
12-
--> $DIR/project-fn-ret-invariant.rs:54:21
12+
--> $DIR/project-fn-ret-invariant.rs:56:9
1313
|
14-
LL | fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
15-
| -------- --------------------
16-
| |
17-
| this parameter and the return type are declared with different lifetimes...
18-
LL | let a = bar(foo, y);
19-
LL | let b = bar(foo, x);
20-
| ^ ...but data from `y` is returned here
14+
LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
15+
| -------- --------------------
16+
| |
17+
| this parameter and the return type are declared with different lifetimes...
18+
...
19+
LL | (a, b)
20+
| ^ ...but data from `x` is returned here
2121

2222
error: aborting due to 2 previous errors
2323

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: fatal error triggered by #[rustc_error]
2-
--> $DIR/project-fn-ret-invariant.rs:59:1
2+
--> $DIR/project-fn-ret-invariant.rs:60:1
33
|
4-
LL | fn main() { }
5-
| ^^^^^^^^^^^^^
4+
LL | fn main() {}
5+
| ^^^^^^^^^^^^
66

77
error: aborting due to previous error
88

src/test/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
error[E0623]: lifetime mismatch
2-
--> $DIR/project-fn-ret-invariant.rs:39:19
2+
--> $DIR/project-fn-ret-invariant.rs:40:20
33
|
4-
LL | fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
5-
| -------- --------------------
6-
| |
7-
| this parameter and the return type are declared with different lifetimes...
4+
LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
5+
| -------- --------------------
6+
| |
7+
| this parameter and the return type are declared with different lifetimes...
88
...
9-
LL | let b = bar(f, y);
10-
| ^ ...but data from `x` is returned here
9+
LL | let b = bar(f, y);
10+
| ^ ...but data from `x` is returned here
1111

1212
error: aborting due to previous error
1313

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,61 @@
11
#![feature(unboxed_closures)]
22
#![feature(rustc_attrs)]
3-
43
// Test for projection cache. We should be able to project distinct
54
// lifetimes from `foo` as we reinstantiate it multiple times, but not
65
// if we do it just once. In this variant, the region `'a` is used in
76
// an invariant position, which affects the results.
87

98
// revisions: ok oneuse transmute krisskross
10-
119
#![allow(dead_code, unused_variables)]
1210

1311
use std::marker::PhantomData;
1412

1513
struct Type<'a> {
1614
// Invariant
17-
data: PhantomData<fn(&'a u32) -> &'a u32>
15+
data: PhantomData<fn(&'a u32) -> &'a u32>,
1816
}
1917

20-
fn foo<'a>() -> Type<'a> { loop { } }
18+
fn foo<'a>() -> Type<'a> {
19+
loop {}
20+
}
2121

2222
fn bar<T>(t: T, x: T::Output) -> T::Output
23-
where T: FnOnce<()>
23+
where
24+
T: FnOnce<()>,
2425
{
2526
t()
2627
}
2728

2829
#[cfg(ok)] // two instantiations: OK
29-
fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
30+
fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
3031
let a = bar(foo, x);
3132
let b = bar(foo, y);
3233
(a, b)
3334
}
3435

3536
#[cfg(oneuse)] // one instantiation: BAD
36-
fn baz<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
37-
let f = foo; // <-- No consistent type can be inferred for `f` here.
38-
let a = bar(f, x);
39-
let b = bar(f, y); //[oneuse]~ ERROR lifetime mismatch [E0623]
40-
(a, b)
37+
fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
38+
let f = foo; // <-- No consistent type can be inferred for `f` here.
39+
let a = bar(f, x);
40+
let b = bar(f, y); //[oneuse]~ ERROR lifetime mismatch [E0623]
41+
(a, b)
4142
}
4243

4344
#[cfg(transmute)] // one instantiations: BAD
44-
fn baz<'a,'b>(x: Type<'a>) -> Type<'static> {
45-
// Cannot instantiate `foo` with any lifetime other than `'a`,
46-
// since it is provided as input.
45+
fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> {
46+
// Cannot instantiate `foo` with any lifetime other than `'a`,
47+
// since it is provided as input.
4748

48-
bar(foo, x) //[transmute]~ ERROR E0495
49+
bar(foo, x) //[transmute]~ ERROR E0495
4950
}
5051

5152
#[cfg(krisskross)] // two instantiations, mixing and matching: BAD
52-
fn transmute<'a,'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
53-
let a = bar(foo, y); //[krisskross]~ ERROR E0623
54-
let b = bar(foo, x); //[krisskross]~ ERROR E0623
55-
(a, b)
53+
fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
54+
let a = bar(foo, y); //[krisskross]~ ERROR E0623
55+
let b = bar(foo, x);
56+
(a, b) //[krisskross]~ ERROR E0623
5657
}
5758

5859
#[rustc_error]
59-
fn main() { }
60+
fn main() {}
6061
//[ok]~^ ERROR fatal error triggered by #[rustc_error]

src/test/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
1-
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
2-
--> $DIR/project-fn-ret-invariant.rs:48:4
1+
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
2+
--> $DIR/project-fn-ret-invariant.rs:49:9
33
|
4-
LL | bar(foo, x)
5-
| ^^^^^^^^^^^
4+
LL | bar(foo, x)
5+
| ^^^
66
|
7-
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 44:8...
8-
--> $DIR/project-fn-ret-invariant.rs:44:8
7+
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 45:8...
8+
--> $DIR/project-fn-ret-invariant.rs:45:8
99
|
10-
LL | fn baz<'a,'b>(x: Type<'a>) -> Type<'static> {
10+
LL | fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> {
1111
| ^^
1212
note: ...so that the expression is assignable
13-
--> $DIR/project-fn-ret-invariant.rs:48:13
13+
--> $DIR/project-fn-ret-invariant.rs:49:14
1414
|
15-
LL | bar(foo, x)
16-
| ^
15+
LL | bar(foo, x)
16+
| ^
1717
= note: expected `Type<'_>`
1818
found `Type<'a>`
1919
= note: but, the lifetime must be valid for the static lifetime...
2020
note: ...so that the expression is assignable
21-
--> $DIR/project-fn-ret-invariant.rs:48:4
21+
--> $DIR/project-fn-ret-invariant.rs:49:5
2222
|
23-
LL | bar(foo, x)
24-
| ^^^^^^^^^^^
23+
LL | bar(foo, x)
24+
| ^^^^^^^^^^^
2525
= note: expected `Type<'static>`
2626
found `Type<'_>`
2727

src/test/ui/closure-expected-type/expect-fn-supply-fn.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
fn with_closure_expecting_fn_with_free_region<F>(_: F)
2-
where F: for<'a> FnOnce(fn(&'a u32), &i32)
2+
where
3+
F: for<'a> FnOnce(fn(&'a u32), &i32),
34
{
45
}
56

67
fn with_closure_expecting_fn_with_bound_region<F>(_: F)
7-
where F: FnOnce(fn(&u32), &i32)
8+
where
9+
F: FnOnce(fn(&u32), &i32),
810
{
911
}
1012

@@ -28,14 +30,14 @@ fn expect_free_supply_bound() {
2830
// Here, we are given a function whose region is bound at closure level,
2931
// but we expect one bound in the argument. Error results.
3032
with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
31-
//~^ ERROR type mismatch
33+
//~^ ERROR mismatched types
3234
}
3335

3436
fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) {
3537
// Here, we are given a `fn(&u32)` but we expect a `fn(&'x
3638
// u32)`. In principle, this could be ok, but we demand equality.
3739
with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
38-
//~^ ERROR type mismatch
40+
//~^ ERROR mismatched types
3941
}
4042

4143
fn expect_bound_supply_free_from_closure() {
@@ -44,16 +46,15 @@ fn expect_bound_supply_free_from_closure() {
4446
// the argument level.
4547
type Foo<'a> = fn(&'a u32);
4648
with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| {
47-
//~^ ERROR type mismatch
49+
//~^ ERROR mismatched types
4850
});
4951
}
5052

5153
fn expect_bound_supply_bound<'x>(x: &'x u32) {
5254
// No error in this case. The supplied type supplies the bound
5355
// regions, and hence we are able to figure out the type of `y`
5456
// from the expected type
55-
with_closure_expecting_fn_with_bound_region(|x: for<'z> fn(&'z u32), y| {
56-
});
57+
with_closure_expecting_fn_with_bound_region(|x: for<'z> fn(&'z u32), y| {});
5758
}
5859

59-
fn main() { }
60+
fn main() {}

0 commit comments

Comments
 (0)