1
- use rustc_data_structures:: fx:: FxIndexSet ;
1
+ use std:: cell:: LazyCell ;
2
+
3
+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap , FxIndexSet } ;
2
4
use rustc_data_structures:: unord:: UnordSet ;
3
5
use rustc_errors:: { Applicability , LintDiagnostic } ;
4
6
use rustc_hir as hir;
5
7
use rustc_hir:: def:: DefKind ;
6
8
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
9
+ use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
10
+ use rustc_infer:: infer:: TyCtxtInferExt ;
7
11
use rustc_macros:: LintDiagnostic ;
8
- use rustc_middle:: bug;
9
12
use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
13
+ use rustc_middle:: ty:: relate:: {
14
+ structurally_relate_consts, structurally_relate_tys, Relate , RelateResult , TypeRelation ,
15
+ } ;
10
16
use rustc_middle:: ty:: {
11
17
self , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitableExt , TypeVisitor ,
12
18
} ;
19
+ use rustc_middle:: { bug, span_bug} ;
13
20
use rustc_session:: { declare_lint, declare_lint_pass} ;
14
- use rustc_span:: Span ;
21
+ use rustc_span:: { Span , Symbol } ;
22
+ use rustc_trait_selection:: traits:: outlives_bounds:: InferCtxtExt ;
23
+ use rustc_trait_selection:: traits:: ObligationCtxt ;
15
24
16
25
use crate :: { fluent_generated as fluent, LateContext , LateLintPass } ;
17
26
@@ -122,38 +131,86 @@ impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
122
131
}
123
132
}
124
133
134
+ #[ derive( PartialEq , Eq , Hash , Debug , Copy , Clone ) ]
135
+ enum ParamKind {
136
+ // Early-bound var.
137
+ Early ( Symbol , u32 ) ,
138
+ // Late-bound var on function, not within a binder. We can capture these.
139
+ Free ( DefId , Symbol ) ,
140
+ // Late-bound var in a binder. We can't capture these yet.
141
+ Late ,
142
+ }
143
+
125
144
fn check_fn ( tcx : TyCtxt < ' _ > , parent_def_id : LocalDefId ) {
126
145
let sig = tcx. fn_sig ( parent_def_id) . instantiate_identity ( ) ;
127
146
128
- let mut in_scope_parameters = FxIndexSet :: default ( ) ;
147
+ let mut in_scope_parameters = FxIndexMap :: default ( ) ;
129
148
// Populate the in_scope_parameters list first with all of the generics in scope
130
149
let mut current_def_id = Some ( parent_def_id. to_def_id ( ) ) ;
131
150
while let Some ( def_id) = current_def_id {
132
151
let generics = tcx. generics_of ( def_id) ;
133
152
for param in & generics. own_params {
134
- in_scope_parameters. insert ( param. def_id ) ;
153
+ in_scope_parameters. insert ( param. def_id , ParamKind :: Early ( param . name , param . index ) ) ;
135
154
}
136
155
current_def_id = generics. parent ;
137
156
}
138
157
158
+ for bound_var in sig. bound_vars ( ) {
159
+ let ty:: BoundVariableKind :: Region ( ty:: BoundRegionKind :: BrNamed ( def_id, name) ) = bound_var
160
+ else {
161
+ span_bug ! ( tcx. def_span( parent_def_id) , "unexpected non-lifetime binder on fn sig" ) ;
162
+ } ;
163
+
164
+ in_scope_parameters. insert ( def_id, ParamKind :: Free ( def_id, name) ) ;
165
+ }
166
+
167
+ let sig = tcx. liberate_late_bound_regions ( parent_def_id. to_def_id ( ) , sig) ;
168
+
139
169
// Then visit the signature to walk through all the binders (incl. the late-bound
140
170
// vars on the function itself, which we need to count too).
141
171
sig. visit_with ( & mut VisitOpaqueTypes {
142
172
tcx,
143
173
parent_def_id,
144
174
in_scope_parameters,
145
175
seen : Default :: default ( ) ,
176
+ // Lazily compute these two, since they're likely a bit expensive.
177
+ variances : LazyCell :: new ( || {
178
+ let mut functional_variances = FunctionalVariances {
179
+ tcx : tcx,
180
+ variances : FxHashMap :: default ( ) ,
181
+ ambient_variance : ty:: Covariant ,
182
+ generics : tcx. generics_of ( parent_def_id) ,
183
+ } ;
184
+ let _ = functional_variances. relate ( sig, sig) ;
185
+ functional_variances. variances
186
+ } ) ,
187
+ outlives_env : LazyCell :: new ( || {
188
+ let param_env = tcx. param_env ( parent_def_id) ;
189
+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
190
+ let ocx = ObligationCtxt :: new ( & infcx) ;
191
+ let assumed_wf_tys = ocx. assumed_wf_types ( param_env, parent_def_id) . unwrap_or_default ( ) ;
192
+ let implied_bounds =
193
+ infcx. implied_bounds_tys_compat ( param_env, parent_def_id, & assumed_wf_tys, false ) ;
194
+ OutlivesEnvironment :: with_bounds ( param_env, implied_bounds)
195
+ } ) ,
146
196
} ) ;
147
197
}
148
198
149
- struct VisitOpaqueTypes < ' tcx > {
199
+ struct VisitOpaqueTypes < ' tcx , VarFn , OutlivesFn > {
150
200
tcx : TyCtxt < ' tcx > ,
151
201
parent_def_id : LocalDefId ,
152
- in_scope_parameters : FxIndexSet < DefId > ,
202
+ in_scope_parameters : FxIndexMap < DefId , ParamKind > ,
203
+ variances : LazyCell < FxHashMap < DefId , ty:: Variance > , VarFn > ,
204
+ outlives_env : LazyCell < OutlivesEnvironment < ' tcx > , OutlivesFn > ,
153
205
seen : FxIndexSet < LocalDefId > ,
154
206
}
155
207
156
- impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for VisitOpaqueTypes < ' tcx > {
208
+ impl < ' tcx , VarFn , OutlivesFn > TypeVisitor < TyCtxt < ' tcx > >
209
+ for VisitOpaqueTypes < ' tcx , VarFn , OutlivesFn >
210
+ where
211
+ VarFn : FnOnce ( ) -> FxHashMap < DefId , ty:: Variance > ,
212
+ OutlivesFn : FnOnce ( ) -> OutlivesEnvironment < ' tcx > ,
213
+ {
157
214
fn visit_binder < T : TypeVisitable < TyCtxt < ' tcx > > > (
158
215
& mut self ,
159
216
t : & ty:: Binder < ' tcx , T > ,
@@ -166,8 +223,8 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
166
223
ty:: BoundVariableKind :: Region ( ty:: BoundRegionKind :: BrNamed ( def_id, ..) )
167
224
| ty:: BoundVariableKind :: Ty ( ty:: BoundTyKind :: Param ( def_id, _) ) => {
168
225
added. push ( def_id) ;
169
- let unique = self . in_scope_parameters . insert ( def_id) ;
170
- assert ! ( unique) ;
226
+ let unique = self . in_scope_parameters . insert ( def_id, ParamKind :: Late ) ;
227
+ assert_eq ! ( unique, None ) ;
171
228
}
172
229
_ => {
173
230
self . tcx . dcx ( ) . span_delayed_bug (
@@ -212,6 +269,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
212
269
{
213
270
// Compute the set of args that are captured by the opaque...
214
271
let mut captured = FxIndexSet :: default ( ) ;
272
+ let mut captured_regions = FxIndexSet :: default ( ) ;
215
273
let variances = self . tcx . variances_of ( opaque_def_id) ;
216
274
let mut current_def_id = Some ( opaque_def_id. to_def_id ( ) ) ;
217
275
while let Some ( def_id) = current_def_id {
@@ -221,25 +279,60 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
221
279
if variances[ param. index as usize ] != ty:: Invariant {
222
280
continue ;
223
281
}
282
+
283
+ let arg = opaque_ty. args [ param. index as usize ] ;
224
284
// We need to turn all `ty::Param`/`ConstKind::Param` and
225
285
// `ReEarlyParam`/`ReBound` into def ids.
226
- captured. insert ( extract_def_id_from_arg (
227
- self . tcx ,
228
- generics,
229
- opaque_ty. args [ param. index as usize ] ,
230
- ) ) ;
286
+ captured. insert ( extract_def_id_from_arg ( self . tcx , generics, arg) ) ;
287
+
288
+ captured_regions. extend ( arg. as_region ( ) ) ;
231
289
}
232
290
current_def_id = generics. parent ;
233
291
}
234
292
235
293
// Compute the set of in scope params that are not captured. Get their spans,
236
294
// since that's all we really care about them for emitting the diagnostic.
237
- let uncaptured_spans : Vec < _ > = self
295
+ let mut uncaptured_args : FxIndexSet < _ > = self
238
296
. in_scope_parameters
239
297
. iter ( )
240
- . filter ( |def_id| !captured. contains ( * def_id) )
241
- . map ( |def_id| self . tcx . def_span ( def_id) )
298
+ . filter ( |& ( def_id, _) | !captured. contains ( def_id) )
299
+ . collect ( ) ;
300
+
301
+ // These are args that we know are likely fine to "overcapture", since they can be
302
+ // contravariantly shortened to one of the already-captured lifetimes that they
303
+ // outlive.
304
+ let covariant_long_args: FxIndexSet < _ > = uncaptured_args
305
+ . iter ( )
306
+ . copied ( )
307
+ . filter ( |& ( def_id, kind) | {
308
+ let Some ( ty:: Bivariant | ty:: Contravariant ) = self . variances . get ( def_id) else {
309
+ return false ;
310
+ } ;
311
+ let DefKind :: LifetimeParam = self . tcx . def_kind ( def_id) else {
312
+ return false ;
313
+ } ;
314
+ let uncaptured = match * kind {
315
+ ParamKind :: Early ( name, index) => ty:: Region :: new_early_param (
316
+ self . tcx ,
317
+ ty:: EarlyParamRegion { name, index } ,
318
+ ) ,
319
+ ParamKind :: Free ( def_id, name) => ty:: Region :: new_late_param (
320
+ self . tcx ,
321
+ self . parent_def_id . to_def_id ( ) ,
322
+ ty:: BoundRegionKind :: BrNamed ( def_id, name) ,
323
+ ) ,
324
+ ParamKind :: Late => return false ,
325
+ } ;
326
+ // Does this region outlive any captured region?
327
+ captured_regions. iter ( ) . any ( |r| {
328
+ self . outlives_env
329
+ . free_region_map ( )
330
+ . sub_free_regions ( self . tcx , * r, uncaptured)
331
+ } )
332
+ } )
242
333
. collect ( ) ;
334
+ // We don't care to warn on these args.
335
+ uncaptured_args. retain ( |arg| !covariant_long_args. contains ( arg) ) ;
243
336
244
337
let opaque_span = self . tcx . def_span ( opaque_def_id) ;
245
338
let new_capture_rules =
@@ -249,7 +342,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
249
342
// `use<>` syntax on it, and we're < edition 2024, then warn the user.
250
343
if !new_capture_rules
251
344
&& !opaque. bounds . iter ( ) . any ( |bound| matches ! ( bound, hir:: GenericBound :: Use ( ..) ) )
252
- && !uncaptured_spans . is_empty ( )
345
+ && !uncaptured_args . is_empty ( )
253
346
{
254
347
let suggestion = if let Ok ( snippet) =
255
348
self . tcx . sess . source_map ( ) . span_to_snippet ( opaque_span)
@@ -277,6 +370,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
277
370
None
278
371
} ;
279
372
373
+ let uncaptured_spans: Vec < _ > = uncaptured_args
374
+ . into_iter ( )
375
+ . map ( |( def_id, _) | self . tcx . def_span ( def_id) )
376
+ . collect ( ) ;
377
+
280
378
self . tcx . emit_node_span_lint (
281
379
IMPL_TRAIT_OVERCAPTURES ,
282
380
self . tcx . local_def_id_to_hir_id ( opaque_def_id) ,
@@ -329,7 +427,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
329
427
if self
330
428
. in_scope_parameters
331
429
. iter ( )
332
- . all ( |def_id| explicitly_captured. contains ( def_id) )
430
+ . all ( |( def_id, _ ) | explicitly_captured. contains ( def_id) )
333
431
{
334
432
self . tcx . emit_node_span_lint (
335
433
IMPL_TRAIT_REDUNDANT_CAPTURES ,
@@ -398,7 +496,11 @@ fn extract_def_id_from_arg<'tcx>(
398
496
ty:: ReBound (
399
497
_,
400
498
ty:: BoundRegion { kind : ty:: BoundRegionKind :: BrNamed ( def_id, ..) , .. } ,
401
- ) => def_id,
499
+ )
500
+ | ty:: ReLateParam ( ty:: LateParamRegion {
501
+ scope : _,
502
+ bound_region : ty:: BoundRegionKind :: BrNamed ( def_id, ..) ,
503
+ } ) => def_id,
402
504
_ => unreachable ! ( ) ,
403
505
} ,
404
506
ty:: GenericArgKind :: Type ( ty) => {
@@ -415,3 +517,106 @@ fn extract_def_id_from_arg<'tcx>(
415
517
}
416
518
}
417
519
}
520
+
521
+ /// Computes the variances of regions that appear in the type, but considering
522
+ /// late-bound regions too, which don't have their variance computed usually.
523
+ ///
524
+ /// Like generalization, this is a unary operation implemented on top of the binary
525
+ /// relation infrastructure, mostly because it's much easier to have the relation
526
+ /// track the variance for you, rather than having to do it yourself.
527
+ struct FunctionalVariances < ' tcx > {
528
+ tcx : TyCtxt < ' tcx > ,
529
+ variances : FxHashMap < DefId , ty:: Variance > ,
530
+ ambient_variance : ty:: Variance ,
531
+ generics : & ' tcx ty:: Generics ,
532
+ }
533
+
534
+ impl < ' tcx > TypeRelation < TyCtxt < ' tcx > > for FunctionalVariances < ' tcx > {
535
+ fn cx ( & self ) -> TyCtxt < ' tcx > {
536
+ self . tcx
537
+ }
538
+
539
+ fn relate_with_variance < T : ty:: relate:: Relate < TyCtxt < ' tcx > > > (
540
+ & mut self ,
541
+ variance : rustc_type_ir:: Variance ,
542
+ _: ty:: VarianceDiagInfo < TyCtxt < ' tcx > > ,
543
+ a : T ,
544
+ b : T ,
545
+ ) -> RelateResult < ' tcx , T > {
546
+ let old_variance = self . ambient_variance ;
547
+ self . ambient_variance = self . ambient_variance . xform ( variance) ;
548
+ self . relate ( a, b) ?;
549
+ self . ambient_variance = old_variance;
550
+ Ok ( a)
551
+ }
552
+
553
+ fn tys ( & mut self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> RelateResult < ' tcx , Ty < ' tcx > > {
554
+ structurally_relate_tys ( self , a, b) ?;
555
+ Ok ( a)
556
+ }
557
+
558
+ fn regions (
559
+ & mut self ,
560
+ a : ty:: Region < ' tcx > ,
561
+ _: ty:: Region < ' tcx > ,
562
+ ) -> RelateResult < ' tcx , ty:: Region < ' tcx > > {
563
+ let def_id = match * a {
564
+ ty:: ReEarlyParam ( ebr) => self . generics . region_param ( ebr, self . tcx ) . def_id ,
565
+ ty:: ReBound (
566
+ _,
567
+ ty:: BoundRegion { kind : ty:: BoundRegionKind :: BrNamed ( def_id, ..) , .. } ,
568
+ )
569
+ | ty:: ReLateParam ( ty:: LateParamRegion {
570
+ scope : _,
571
+ bound_region : ty:: BoundRegionKind :: BrNamed ( def_id, ..) ,
572
+ } ) => def_id,
573
+ _ => {
574
+ return Ok ( a) ;
575
+ }
576
+ } ;
577
+
578
+ if let Some ( variance) = self . variances . get_mut ( & def_id) {
579
+ * variance = unify ( * variance, self . ambient_variance ) ;
580
+ } else {
581
+ self . variances . insert ( def_id, self . ambient_variance ) ;
582
+ }
583
+
584
+ Ok ( a)
585
+ }
586
+
587
+ fn consts (
588
+ & mut self ,
589
+ a : ty:: Const < ' tcx > ,
590
+ b : ty:: Const < ' tcx > ,
591
+ ) -> RelateResult < ' tcx , ty:: Const < ' tcx > > {
592
+ structurally_relate_consts ( self , a, b) ?;
593
+ Ok ( a)
594
+ }
595
+
596
+ fn binders < T > (
597
+ & mut self ,
598
+ a : ty:: Binder < ' tcx , T > ,
599
+ b : ty:: Binder < ' tcx , T > ,
600
+ ) -> RelateResult < ' tcx , ty:: Binder < ' tcx , T > >
601
+ where
602
+ T : Relate < TyCtxt < ' tcx > > ,
603
+ {
604
+ self . relate ( a. skip_binder ( ) , b. skip_binder ( ) ) ?;
605
+ Ok ( a)
606
+ }
607
+ }
608
+
609
+ /// What is the variance that satisfies the two variances?
610
+ fn unify ( a : ty:: Variance , b : ty:: Variance ) -> ty:: Variance {
611
+ match ( a, b) {
612
+ // Bivariance is lattice bottom.
613
+ ( ty:: Bivariant , other) | ( other, ty:: Bivariant ) => other,
614
+ // Invariant is lattice top.
615
+ ( ty:: Invariant , _) | ( _, ty:: Invariant ) => ty:: Invariant ,
616
+ // If type is required to be covariant and contravariant, then it's invariant.
617
+ ( ty:: Contravariant , ty:: Covariant ) | ( ty:: Covariant , ty:: Contravariant ) => ty:: Invariant ,
618
+ // Otherwise, co + co = co, contra + contra = contra.
619
+ ( ty:: Contravariant , ty:: Contravariant ) => ty:: Contravariant ,
620
+ ( ty:: Covariant , ty:: Covariant ) => ty:: Covariant ,
621
+ }
622
+ }
0 commit comments