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

Lines changed: 16 additions & 4 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 24 additions & 1 deletion
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

Lines changed: 4 additions & 3 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 1 addition & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

0 commit comments

Comments
 (0)