1- use rustc_data_structures:: fx:: FxIndexSet ;
1+ use std:: cell:: LazyCell ;
2+
3+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap , FxIndexSet } ;
24use rustc_data_structures:: unord:: UnordSet ;
35use rustc_errors:: { Applicability , LintDiagnostic } ;
46use rustc_hir as hir;
57use rustc_hir:: def:: DefKind ;
68use rustc_hir:: def_id:: { DefId , LocalDefId } ;
9+ use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
10+ use rustc_infer:: infer:: TyCtxtInferExt ;
711use rustc_macros:: LintDiagnostic ;
8- use rustc_middle:: bug;
912use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
13+ use rustc_middle:: ty:: relate:: {
14+ structurally_relate_consts, structurally_relate_tys, Relate , RelateResult , TypeRelation ,
15+ } ;
1016use rustc_middle:: ty:: {
1117 self , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitableExt , TypeVisitor ,
1218} ;
19+ use rustc_middle:: { bug, span_bug} ;
1320use 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 ;
1524
1625use crate :: { fluent_generated as fluent, LateContext , LateLintPass } ;
1726
@@ -122,38 +131,86 @@ impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
122131 }
123132}
124133
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+
125144fn check_fn ( tcx : TyCtxt < ' _ > , parent_def_id : LocalDefId ) {
126145 let sig = tcx. fn_sig ( parent_def_id) . instantiate_identity ( ) ;
127146
128- let mut in_scope_parameters = FxIndexSet :: default ( ) ;
147+ let mut in_scope_parameters = FxIndexMap :: default ( ) ;
129148 // Populate the in_scope_parameters list first with all of the generics in scope
130149 let mut current_def_id = Some ( parent_def_id. to_def_id ( ) ) ;
131150 while let Some ( def_id) = current_def_id {
132151 let generics = tcx. generics_of ( def_id) ;
133152 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 ) ) ;
135154 }
136155 current_def_id = generics. parent ;
137156 }
138157
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+
139169 // Then visit the signature to walk through all the binders (incl. the late-bound
140170 // vars on the function itself, which we need to count too).
141171 sig. visit_with ( & mut VisitOpaqueTypes {
142172 tcx,
143173 parent_def_id,
144174 in_scope_parameters,
145175 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+ } ) ,
146196 } ) ;
147197}
148198
149- struct VisitOpaqueTypes < ' tcx > {
199+ struct VisitOpaqueTypes < ' tcx , VarFn , OutlivesFn > {
150200 tcx : TyCtxt < ' tcx > ,
151201 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 > ,
153205 seen : FxIndexSet < LocalDefId > ,
154206}
155207
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+ {
157214 fn visit_binder < T : TypeVisitable < TyCtxt < ' tcx > > > (
158215 & mut self ,
159216 t : & ty:: Binder < ' tcx , T > ,
@@ -166,8 +223,8 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
166223 ty:: BoundVariableKind :: Region ( ty:: BoundRegionKind :: BrNamed ( def_id, ..) )
167224 | ty:: BoundVariableKind :: Ty ( ty:: BoundTyKind :: Param ( def_id, _) ) => {
168225 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 ) ;
171228 }
172229 _ => {
173230 self . tcx . dcx ( ) . span_delayed_bug (
@@ -212,6 +269,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
212269 {
213270 // Compute the set of args that are captured by the opaque...
214271 let mut captured = FxIndexSet :: default ( ) ;
272+ let mut captured_regions = FxIndexSet :: default ( ) ;
215273 let variances = self . tcx . variances_of ( opaque_def_id) ;
216274 let mut current_def_id = Some ( opaque_def_id. to_def_id ( ) ) ;
217275 while let Some ( def_id) = current_def_id {
@@ -221,25 +279,60 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
221279 if variances[ param. index as usize ] != ty:: Invariant {
222280 continue ;
223281 }
282+
283+ let arg = opaque_ty. args [ param. index as usize ] ;
224284 // We need to turn all `ty::Param`/`ConstKind::Param` and
225285 // `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 ( ) ) ;
231289 }
232290 current_def_id = generics. parent ;
233291 }
234292
235293 // Compute the set of in scope params that are not captured. Get their spans,
236294 // 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
238296 . in_scope_parameters
239297 . 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+ } )
242333 . collect ( ) ;
334+ // We don't care to warn on these args.
335+ uncaptured_args. retain ( |arg| !covariant_long_args. contains ( arg) ) ;
243336
244337 let opaque_span = self . tcx . def_span ( opaque_def_id) ;
245338 let new_capture_rules =
@@ -249,7 +342,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
249342 // `use<>` syntax on it, and we're < edition 2024, then warn the user.
250343 if !new_capture_rules
251344 && !opaque. bounds . iter ( ) . any ( |bound| matches ! ( bound, hir:: GenericBound :: Use ( ..) ) )
252- && !uncaptured_spans . is_empty ( )
345+ && !uncaptured_args . is_empty ( )
253346 {
254347 let suggestion = if let Ok ( snippet) =
255348 self . tcx . sess . source_map ( ) . span_to_snippet ( opaque_span)
@@ -277,6 +370,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
277370 None
278371 } ;
279372
373+ let uncaptured_spans: Vec < _ > = uncaptured_args
374+ . into_iter ( )
375+ . map ( |( def_id, _) | self . tcx . def_span ( def_id) )
376+ . collect ( ) ;
377+
280378 self . tcx . emit_node_span_lint (
281379 IMPL_TRAIT_OVERCAPTURES ,
282380 self . tcx . local_def_id_to_hir_id ( opaque_def_id) ,
@@ -329,7 +427,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
329427 if self
330428 . in_scope_parameters
331429 . iter ( )
332- . all ( |def_id| explicitly_captured. contains ( def_id) )
430+ . all ( |( def_id, _ ) | explicitly_captured. contains ( def_id) )
333431 {
334432 self . tcx . emit_node_span_lint (
335433 IMPL_TRAIT_REDUNDANT_CAPTURES ,
@@ -398,7 +496,11 @@ fn extract_def_id_from_arg<'tcx>(
398496 ty:: ReBound (
399497 _,
400498 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,
402504 _ => unreachable ! ( ) ,
403505 } ,
404506 ty:: GenericArgKind :: Type ( ty) => {
@@ -415,3 +517,106 @@ fn extract_def_id_from_arg<'tcx>(
415517 }
416518 }
417519}
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