@@ -82,17 +82,28 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
82
82
83
83
let infcx = & tcx. infer_ctxt ( ) . build ( ) ;
84
84
let ocx = ObligationCtxt :: new ( infcx) ;
85
+
86
+ // Compute the implied outlives bounds for the item. This ensures that we treat
87
+ // a signature with an argument like `&'a &'b ()` as implicitly having `'b: 'a`.
85
88
let Ok ( assumed_wf_types) = ocx. assumed_wf_types ( param_env, owner_id. def_id ) else {
86
89
return ;
87
90
} ;
88
-
89
91
let implied_bounds = infcx. implied_bounds_tys ( param_env, owner_id. def_id , assumed_wf_types) ;
90
92
let outlives_env = & OutlivesEnvironment :: with_bounds ( param_env, implied_bounds) ;
91
93
94
+ // The ordering of this lifetime map is a bit subtle.
95
+ //
96
+ // Specifically, we want to find a "candidate" lifetime that precedes a "victim" lifetime,
97
+ // where we can prove that `'candidate = 'victim`.
98
+ //
99
+ // `'static` must come first in this list because we can never replace `'static` with
100
+ // something else, but if we find some lifetime `'a` where `'a = 'static`, we want to
101
+ // suggest replacing `'a` with `'static`.
92
102
let mut lifetimes = vec ! [ tcx. lifetimes. re_static] ;
93
103
lifetimes. extend (
94
104
ty:: GenericArgs :: identity_for_item ( tcx, owner_id) . iter ( ) . filter_map ( |arg| arg. as_region ( ) ) ,
95
105
) ;
106
+ // If we are in a function, add its late-bound lifetimes too.
96
107
if matches ! ( def_kind, DefKind :: Fn | DefKind :: AssocFn ) {
97
108
for var in tcx. fn_sig ( owner_id) . instantiate_identity ( ) . bound_vars ( ) {
98
109
let ty:: BoundVariableKind :: Region ( kind) = var else { continue } ;
@@ -101,20 +112,23 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
101
112
}
102
113
103
114
// Keep track of lifetimes which have already been replaced with other lifetimes.
115
+ // This makes sure that if `'a = 'b = 'c`, we don't say `'c` should be replaced by
116
+ // both `'a` and `'b`.
104
117
let mut shadowed = FxHashSet :: default ( ) ;
105
118
106
119
for ( idx, & candidate) in lifetimes. iter ( ) . enumerate ( ) {
120
+ // Don't suggest removing a lifetime twice.
107
121
if shadowed. contains ( & candidate) {
108
- // Don't suggest removing a lifetime twice.
109
122
continue ;
110
123
}
111
124
125
+ // Can't rename a named lifetime named `'_` without ambiguity.
112
126
if !candidate. has_name ( ) {
113
- // Can't rename a named lifetime with `'_` without ambiguity.
114
127
continue ;
115
128
}
116
129
117
130
for & victim in & lifetimes[ ( idx + 1 ) ..] {
131
+ // We only care about lifetimes that are "real", i.e. that have a def-id.
118
132
let ( ty:: ReEarlyParam ( ty:: EarlyParamRegion { def_id, .. } )
119
133
| ty:: ReLateParam ( ty:: LateParamRegion {
120
134
bound_region : ty:: BoundRegionKind :: BrNamed ( def_id, _) ,
@@ -124,15 +138,21 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
124
138
continue ;
125
139
} ;
126
140
141
+ // Do not rename lifetimes not local to this item since they'll overlap
142
+ // with the lint running on the parent. We still want to consider parent
143
+ // lifetimes which make child lifetimes redundant, otherwise we would
144
+ // have truncated the `identity_for_item` args above.
127
145
if tcx. parent ( def_id) != owner_id. to_def_id ( ) {
128
- // Do not rename generics not local to this item since
129
- // they'll overlap with this lint running on the parent.
130
146
continue ;
131
147
}
132
148
133
149
let infcx = infcx. fork ( ) ;
150
+
151
+ // Require that `'candidate = 'victim`
134
152
infcx. sub_regions ( SubregionOrigin :: RelateRegionParamBound ( DUMMY_SP ) , candidate, victim) ;
135
153
infcx. sub_regions ( SubregionOrigin :: RelateRegionParamBound ( DUMMY_SP ) , victim, candidate) ;
154
+
155
+ // If there are no lifetime errors, then we have proven that `'candidate = 'victim`!
136
156
if infcx. resolve_regions ( outlives_env) . is_empty ( ) {
137
157
shadowed. insert ( victim) ;
138
158
tcx. emit_spanned_lint (
@@ -150,6 +170,8 @@ fn check<'tcx>(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, owner_id: hir::
150
170
#[ diag( lint_redundant_lifetime_args) ]
151
171
#[ note]
152
172
struct RedundantLifetimeArgsLint < ' tcx > {
153
- candidate : ty :: Region < ' tcx > ,
173
+ /// The lifetime we have found to be redundant.
154
174
victim : ty:: Region < ' tcx > ,
175
+ // The lifetime we can replace the victim with.
176
+ candidate : ty:: Region < ' tcx > ,
155
177
}
0 commit comments