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