Skip to content

Commit 427dcbc

Browse files
committed
reliably detect unnecessary lifetime annotations
register lifetime annotations from closure signature and UFCS calls under `ConstraintCategory::TypeAnnotation` and make sure we don't report type annotation errors unless thy're the only thing to blame.
1 parent 801821d commit 427dcbc

File tree

62 files changed

+297
-297
lines changed

Some content is hidden

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

62 files changed

+297
-297
lines changed

compiler/rustc_borrowck/src/region_infer/mod.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18751875
/// `results`. The paths are stored as a series of
18761876
/// `ConstraintIndex` values -- in other words, a list of *edges*.
18771877
///
1878+
/// We have a special handling for `TypeAnnotation` constraints in that we don't report a path
1879+
/// with such constraint unless we're sure that no other path exists.
1880+
/// This enables us to say that a lifetime annotation is unnecessarily restrictive if it
1881+
/// appears in the constraint path, and thus we can safely suggest removing it.
1882+
///
18781883
/// Returns: a series of constraints as well as the region `R`
18791884
/// that passed the target test.
18801885
pub(crate) fn find_constraint_paths_between_regions(
@@ -1888,10 +1893,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
18881893
// Use a deque so that we do a breadth-first search. We will
18891894
// stop at the first match, which ought to be the shortest
18901895
// path (fewest constraints).
1891-
let mut deque = VecDeque::new();
1892-
deque.push_back(from_region);
1896+
let mut deque_p0 = VecDeque::new(); // Higher priority queue.
1897+
let mut deque_p1 = VecDeque::new(); // Lower priority queue. See method docs.
1898+
deque_p0.push_back(from_region);
18931899

1894-
while let Some(r) = deque.pop_front() {
1900+
while let Some(r) = deque_p0.pop_front().or_else(|| deque_p1.pop_front()) {
18951901
debug!(
18961902
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
18971903
from_region,
@@ -1939,8 +1945,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19391945
debug_assert_eq!(constraint.sup, r);
19401946
let sub_region = constraint.sub;
19411947
if let Trace::NotVisited = context[sub_region] {
1948+
let constraint_category = constraint.category;
19421949
context[sub_region] = Trace::FromOutlivesConstraint(constraint);
1943-
deque.push_back(sub_region);
1950+
match constraint_category {
1951+
ConstraintCategory::TypeAnnotation => deque_p1.push_back(sub_region),
1952+
// FIXME A `ClosureBounds` constraint can be mapped to `TypeAnnotation`
1953+
// later. It should be treated as such here but we're ingoring that.
1954+
_ => deque_p0.push_back(sub_region),
1955+
}
19441956
}
19451957
};
19461958

compiler/rustc_borrowck/src/type_check/input_output.rs

+3
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
101101
// argument N is stored in local N+2.
102102
let local = Local::new(argument_index + 2);
103103
let mir_input_ty = body.local_decls[local].ty;
104+
// FIXME span should point to the type annotation, not the argument.
104105
let mir_input_span = body.local_decls[local].source_info.span;
105106

106107
// If the user explicitly annotated the input types, enforce those.
108+
let user_provided_input_ty =
109+
self.extract_annotations(user_provided_input_ty, mir_input_span);
107110
let user_provided_input_ty =
108111
self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
109112

compiler/rustc_borrowck/src/type_check/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
494494
ty::Variance::Invariant,
495495
user_ty,
496496
Locations::All(*span),
497-
ConstraintCategory::TypeAnnotation,
497+
ConstraintCategory::Boring,
498498
) {
499499
span_mirbug!(
500500
self,
@@ -1076,6 +1076,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
10761076
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
10771077
let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
10781078
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
1079+
let annotation = self.extract_annotations(annotation, span);
10791080
match annotation {
10801081
UserType::Ty(mut ty) => {
10811082
ty = self.normalize(ty, Locations::All(span));

compiler/rustc_borrowck/src/type_check/relate_tys.rs

+24-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_infer::traits::ObligationCause;
44
use rustc_middle::mir::ConstraintCategory;
55
use rustc_middle::ty::error::TypeError;
66
use rustc_middle::ty::relate::TypeRelation;
7-
use rustc_middle::ty::{self, Const, Ty};
7+
use rustc_middle::ty::{self, Const, Ty, TypeFoldable};
88
use rustc_span::Span;
99
use rustc_trait_selection::traits::query::Fallible;
1010

@@ -55,6 +55,29 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
5555
.relate(a, b)?;
5656
Ok(())
5757
}
58+
59+
/// Given a user annotation, `val`, this registers the minimum necessary region constraints
60+
/// under the category `ConstraintCategory::TypeAnnotation` and replaces annotated lifetimes
61+
/// with fresh inference vars.
62+
pub(super) fn extract_annotations<T: TypeFoldable<'tcx>>(&mut self, val: T, span: Span) -> T {
63+
let tcx = self.infcx.tcx;
64+
let mut relate = NllTypeRelatingDelegate::new(
65+
self,
66+
Locations::All(span),
67+
ConstraintCategory::TypeAnnotation,
68+
UniverseInfo::other(),
69+
);
70+
tcx.fold_regions(val, |region, _| match region.kind() {
71+
ty::ReVar(_) => region,
72+
ty::ReFree(_) | ty::ReEarlyBound(_) | ty::ReStatic => {
73+
let var = relate.next_existential_region_var(false);
74+
relate.push_outlives(region, var, ty::VarianceDiagInfo::default());
75+
relate.push_outlives(var, region, ty::VarianceDiagInfo::default());
76+
var
77+
}
78+
_ => bug!("unexpected region in type annotation {:?}", region),
79+
})
80+
}
5881
}
5982

6083
struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> {

src/test/ui/borrowck/borrowck-loan-of-static-data-issue-27616.stderr

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ error[E0506]: cannot assign to `*s` because it is borrowed
22
--> $DIR/borrowck-loan-of-static-data-issue-27616.rs:16:5
33
|
44
LL | let alias: &'static mut String = s;
5-
| ------------------- - borrow of `*s` occurs here
6-
| |
7-
| type annotation requires that `*s` is borrowed for `'static`
5+
| - borrow of `*s` occurs here
86
...
97
LL | *s = String::new();
108
| ^^ assignment to borrowed `*s` occurs here
9+
...
10+
LL | println!("{}", inner);
11+
| ----- borrow later used here
1112

1213
error: aborting due to previous error
1314

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {});
88
| ^
99
| |
1010
| has type `fn(&'1 u32)`
11-
| requires that `'1` must outlive `'x`
11+
| type annotation requires that `'1` must outlive `'x`
1212

1313
error: lifetime may not live long enough
1414
--> $DIR/expect-fn-supply-fn.rs:16:49
@@ -17,7 +17,7 @@ LL | fn expect_free_supply_free_from_fn<'x>(x: &'x u32) {
1717
| -- lifetime `'x` defined here
1818
...
1919
LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {});
20-
| ^ requires that `'x` must outlive `'static`
20+
| ^ type annotation requires that `'x` must outlive `'static`
2121

2222
error[E0308]: mismatched types
2323
--> $DIR/expect-fn-supply-fn.rs:32:49

src/test/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | fn expect_bound_supply_named<'x>() {
77
LL | closure_expecting_bound(|x: &'x u32| {
88
| ^ - let's call the lifetime of this reference `'1`
99
| |
10-
| requires that `'1` must outlive `'x`
10+
| type annotation requires that `'1` must outlive `'x`
1111

1212
error: lifetime may not live long enough
1313
--> $DIR/expect-region-supply-region-2.rs:14:30
@@ -16,7 +16,7 @@ LL | fn expect_bound_supply_named<'x>() {
1616
| -- lifetime `'x` defined here
1717
...
1818
LL | closure_expecting_bound(|x: &'x u32| {
19-
| ^ requires that `'x` must outlive `'static`
19+
| ^ type annotation requires that `'x` must outlive `'static`
2020

2121
error: aborting due to 2 previous errors
2222

src/test/ui/fn/implied-bounds-unnorm-associated-type-2.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,9 @@ LL | fn g<'a, 'b>() {
66
| |
77
| lifetime `'a` defined here
88
LL | f::<'a, 'b>(());
9-
| ^^^^^^^^^^^^^^^ requires that `'b` must outlive `'a`
9+
| ^^^^^^^^^^^ type annotation requires that `'b` must outlive `'a`
1010
|
1111
= help: consider adding the following bound: `'b: 'a`
12-
= note: requirement occurs because of a function pointer to `f`
13-
= note: the function `f` is invariant over the parameter `'a`
14-
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
1512

1613
error: aborting due to previous error
1714

src/test/ui/higher-rank-trait-bounds/hrtb-cache-issue-54302.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: implementation of `Deserialize` is not general enough
44
LL | assert_deserialize_owned::<&'static str>();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough
66
|
7-
= note: `&'static str` must implement `Deserialize<'0>`, for any lifetime `'0`...
7+
= note: `&str` must implement `Deserialize<'0>`, for any lifetime `'0`...
88
= note: ...but `&str` actually implements `Deserialize<'1>`, for some specific lifetime `'1`
99

1010
error: aborting due to previous error

src/test/ui/higher-rank-trait-bounds/hrtb-just-for-static.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ error: lifetime may not live long enough
1313
LL | fn give_some<'a>() {
1414
| -- lifetime `'a` defined here
1515
LL | want_hrtb::<&'a u32>()
16-
| ^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
16+
| ^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
1717

1818
error: implementation of `Foo` is not general enough
1919
--> $DIR/hrtb-just-for-static.rs:30:5

src/test/ui/hr-subtype/hr-subtype.free_inv_x_vs_free_inv_y.stderr

+3-9
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,30 @@ LL | fn subtype<'x, 'y: 'x, 'z: 'y>() {
66
| |
77
| lifetime `'x` defined here
88
LL | gimme::<$t2>(None::<$t1>);
9-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'x` must outlive `'y`
9+
| ^^^^^^^^^^^^ type annotation requires that `'x` must outlive `'y`
1010
...
1111
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
1212
LL | | fn(Inv<'y>)) }
1313
| |______________- in this macro invocation
1414
|
1515
= help: consider adding the following bound: `'x: 'y`
16-
= note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant
17-
= note: the struct `Inv<'a>` is invariant over the parameter `'a`
18-
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
1916
= note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
2017

2118
error: lifetime may not live long enough
22-
--> $DIR/hr-subtype.rs:54:13
19+
--> $DIR/hr-subtype.rs:54:26
2320
|
2421
LL | fn supertype<'x, 'y: 'x, 'z: 'y>() {
2522
| -- -- lifetime `'y` defined here
2623
| |
2724
| lifetime `'x` defined here
2825
LL | gimme::<$t1>(None::<$t2>);
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'x` must outlive `'y`
26+
| ^^^^^^^^^^^ type annotation requires that `'x` must outlive `'y`
3027
...
3128
LL | / check! { free_inv_x_vs_free_inv_y: (fn(Inv<'x>),
3229
LL | | fn(Inv<'y>)) }
3330
| |______________- in this macro invocation
3431
|
3532
= help: consider adding the following bound: `'x: 'y`
36-
= note: requirement occurs because of the type `Inv<'_>`, which makes the generic argument `'_` invariant
37-
= note: the struct `Inv<'a>` is invariant over the parameter `'a`
38-
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
3933
= note: this error originates in the macro `check` (in Nightly builds, run with -Z macro-backtrace for more info)
4034

4135
error: aborting due to 2 previous errors

src/test/ui/hr-subtype/hr-subtype.free_x_vs_free_y.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
error: lifetime may not live long enough
2-
--> $DIR/hr-subtype.rs:54:13
2+
--> $DIR/hr-subtype.rs:54:26
33
|
44
LL | fn supertype<'x, 'y: 'x, 'z: 'y>() {
55
| -- -- lifetime `'y` defined here
66
| |
77
| lifetime `'x` defined here
88
LL | gimme::<$t1>(None::<$t2>);
9-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'x` must outlive `'y`
9+
| ^^^^^^^^^^^ type annotation requires that `'x` must outlive `'y`
1010
...
1111
LL | / check! { free_x_vs_free_y: (fn(&'x u32),
1212
LL | | fn(&'y u32)) }

src/test/ui/inline-const/const-expr-lifetime-err.stderr

+3-4
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ LL | fn foo<'a>() {
55
| -- lifetime `'a` defined here
66
LL | let y = ();
77
LL | equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW });
8-
| ------------------^^-
9-
| | |
10-
| | borrowed value does not live long enough
11-
| argument requires that `y` is borrowed for `'a`
8+
| ^^ ----------------------- type annotation requires that `y` is borrowed for `'a`
9+
| |
10+
| borrowed value does not live long enough
1211
LL |
1312
LL | }
1413
| - `y` dropped here while still borrowed

src/test/ui/issues/issue-26217.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: lifetime may not live long enough
44
LL | fn bar<'a>() {
55
| -- lifetime `'a` defined here
66
LL | foo::<&'a i32>();
7-
| ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
7+
| ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
88

99
error: aborting due to previous error
1010

src/test/ui/issues/issue-54302.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: implementation of `Deserialize` is not general enough
44
LL | assert_deserialize_owned::<&'static str>();
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough
66
|
7-
= note: `&'static str` must implement `Deserialize<'0>`, for any lifetime `'0`...
7+
= note: `&str` must implement `Deserialize<'0>`, for any lifetime `'0`...
88
= note: ...but `&str` actually implements `Deserialize<'1>`, for some specific lifetime `'1`
99

1010
error: aborting due to previous error

src/test/ui/issues/issue-54943.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | fn boo<'a>() {
55
| -- lifetime `'a` defined here
66
...
77
LL | let x = foo::<&'a u32>();
8-
| ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
8+
| ^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
99

1010
error: aborting due to previous error
1111

src/test/ui/lub-glb/empty-binders-err.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ where
3333
{
3434

3535
let _: fn(&'lower ()) = match v {
36-
//~^ ERROR lifetime may not live long enough
3736
true => lt_in_fn::<'a>(),
37+
//~^ ERROR lifetime may not live long enough
3838
false => lt_in_fn::<'b>(),
3939
};
4040
}
@@ -46,8 +46,8 @@ where
4646

4747
{
4848
let _: Contra<'lower> = match v {
49-
//~^ ERROR lifetime may not live long enough
5049
true => lt_in_contra::<'a>(),
50+
//~^ ERROR lifetime may not live long enough
5151
false => lt_in_contra::<'b>(),
5252
};
5353
}

src/test/ui/lub-glb/empty-binders-err.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,28 @@ help: the following changes may resolve your lifetime errors
3030
= help: add bound `'b: 'upper`
3131

3232
error: lifetime may not live long enough
33-
--> $DIR/empty-binders-err.rs:35:12
33+
--> $DIR/empty-binders-err.rs:36:17
3434
|
3535
LL | fn contra_fn<'a, 'b, 'lower>(v: bool)
3636
| -- ------ lifetime `'lower` defined here
3737
| |
3838
| lifetime `'a` defined here
3939
...
40-
LL | let _: fn(&'lower ()) = match v {
41-
| ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
40+
LL | true => lt_in_fn::<'a>(),
41+
| ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
4242
|
4343
= help: consider adding the following bound: `'lower: 'a`
4444

4545
error: lifetime may not live long enough
46-
--> $DIR/empty-binders-err.rs:48:12
46+
--> $DIR/empty-binders-err.rs:49:17
4747
|
4848
LL | fn contra_struct<'a, 'b, 'lower>(v: bool)
4949
| -- ------ lifetime `'lower` defined here
5050
| |
5151
| lifetime `'a` defined here
5252
...
53-
LL | let _: Contra<'lower> = match v {
54-
| ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
53+
LL | true => lt_in_contra::<'a>(),
54+
| ^^^^^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
5555
|
5656
= help: consider adding the following bound: `'lower: 'a`
5757

src/test/ui/nll/closure-requirements/propagate-multiple-requirements.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ pub fn dangle() -> &'static [i32] {
99
let other_local_arr = [0, 2, 4];
1010
let local_arr = other_local_arr;
1111
let mut out: &mut &'static [i32] = &mut (&[1] as _);
12-
once(|mut z: &[i32], mut out_val: &mut &[i32]| {
12+
once(|mut z: &[i32], mut out_val: &mut &[i32]| { //~ ERROR
1313
// We unfortunately point to the first use in the closure in the error
1414
// message
15-
z = &local_arr; //~ ERROR
15+
z = &local_arr;
1616
*out_val = &local_arr;
1717
}, &[] as &[_], &mut *out);
1818
*out
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
error[E0597]: `local_arr` does not live long enough
2-
--> $DIR/propagate-multiple-requirements.rs:15:14
1+
error[E0373]: closure may outlive the current function, but it borrows `local_arr`, which is owned by the current function
2+
--> $DIR/propagate-multiple-requirements.rs:12:10
33
|
4-
LL | let mut out: &mut &'static [i32] = &mut (&[1] as _);
5-
| ------------------- type annotation requires that `local_arr` is borrowed for `'static`
64
LL | once(|mut z: &[i32], mut out_val: &mut &[i32]| {
7-
| ----------------------------------------- value captured here
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `local_arr`
86
...
97
LL | z = &local_arr;
10-
| ^^^^^^^^^ borrowed value does not live long enough
11-
...
12-
LL | }
13-
| - `local_arr` dropped here while still borrowed
8+
| --------- `local_arr` is borrowed here
9+
|
10+
note: closure is returned here
11+
--> $DIR/propagate-multiple-requirements.rs:18:5
12+
|
13+
LL | *out
14+
| ^^^^
15+
help: to force the closure to take ownership of `local_arr` (and any other referenced variables), use the `move` keyword
16+
|
17+
LL | once(move |mut z: &[i32], mut out_val: &mut &[i32]| {
18+
| ++++
1419

1520
error: aborting due to previous error
1621

17-
For more information about this error, try `rustc --explain E0597`.
22+
For more information about this error, try `rustc --explain E0373`.

0 commit comments

Comments
 (0)