1- use rustc_data_structures:: fx:: FxIndexSet ;
1+ use std:: assert_matches:: debug_assert_matches;
2+ use std:: cell:: LazyCell ;
3+
4+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap , FxIndexSet } ;
25use rustc_data_structures:: unord:: UnordSet ;
36use rustc_errors:: { Applicability , LintDiagnostic } ;
47use rustc_hir as hir;
58use rustc_hir:: def:: DefKind ;
69use rustc_hir:: def_id:: { DefId , LocalDefId } ;
10+ use rustc_infer:: infer:: outlives:: env:: OutlivesEnvironment ;
11+ use rustc_infer:: infer:: TyCtxtInferExt ;
712use rustc_macros:: LintDiagnostic ;
8- use rustc_middle:: bug;
913use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
14+ use rustc_middle:: ty:: relate:: {
15+ structurally_relate_consts, structurally_relate_tys, Relate , RelateResult , TypeRelation ,
16+ } ;
1017use rustc_middle:: ty:: {
1118 self , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitableExt , TypeVisitor ,
1219} ;
20+ use rustc_middle:: { bug, span_bug} ;
1321use rustc_session:: lint:: FutureIncompatibilityReason ;
1422use rustc_session:: { declare_lint, declare_lint_pass} ;
1523use rustc_span:: edition:: Edition ;
16- use rustc_span:: Span ;
24+ use rustc_span:: { Span , Symbol } ;
25+ use rustc_trait_selection:: traits:: outlives_bounds:: InferCtxtExt ;
26+ use rustc_trait_selection:: traits:: ObligationCtxt ;
1727
1828use crate :: { fluent_generated as fluent, LateContext , LateLintPass } ;
1929
@@ -119,42 +129,87 @@ impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures {
119129 }
120130}
121131
132+ #[ derive( PartialEq , Eq , Hash , Debug , Copy , Clone ) ]
133+ enum ParamKind {
134+ // Early-bound var.
135+ Early ( Symbol , u32 ) ,
136+ // Late-bound var on function, not within a binder. We can capture these.
137+ Free ( DefId , Symbol ) ,
138+ // Late-bound var in a binder. We can't capture these yet.
139+ Late ,
140+ }
141+
122142fn check_fn ( tcx : TyCtxt < ' _ > , parent_def_id : LocalDefId ) {
123143 let sig = tcx. fn_sig ( parent_def_id) . instantiate_identity ( ) ;
124144
125- let mut in_scope_parameters = FxIndexSet :: default ( ) ;
145+ let mut in_scope_parameters = FxIndexMap :: default ( ) ;
126146 // Populate the in_scope_parameters list first with all of the generics in scope
127147 let mut current_def_id = Some ( parent_def_id. to_def_id ( ) ) ;
128148 while let Some ( def_id) = current_def_id {
129149 let generics = tcx. generics_of ( def_id) ;
130150 for param in & generics. own_params {
131- in_scope_parameters. insert ( param. def_id ) ;
151+ in_scope_parameters. insert ( param. def_id , ParamKind :: Early ( param . name , param . index ) ) ;
132152 }
133153 current_def_id = generics. parent ;
134154 }
135155
156+ for bound_var in sig. bound_vars ( ) {
157+ let ty:: BoundVariableKind :: Region ( ty:: BoundRegionKind :: BrNamed ( def_id, name) ) = bound_var
158+ else {
159+ span_bug ! ( tcx. def_span( parent_def_id) , "unexpected non-lifetime binder on fn sig" ) ;
160+ } ;
161+
162+ in_scope_parameters. insert ( def_id, ParamKind :: Free ( def_id, name) ) ;
163+ }
164+
165+ let sig = tcx. liberate_late_bound_regions ( parent_def_id. to_def_id ( ) , sig) ;
166+
136167 // Then visit the signature to walk through all the binders (incl. the late-bound
137168 // vars on the function itself, which we need to count too).
138169 sig. visit_with ( & mut VisitOpaqueTypes {
139170 tcx,
140171 parent_def_id,
141172 in_scope_parameters,
142173 seen : Default :: default ( ) ,
174+ // Lazily compute these two, since they're likely a bit expensive.
175+ variances : LazyCell :: new ( || {
176+ let mut functional_variances = FunctionalVariances {
177+ tcx : tcx,
178+ variances : FxHashMap :: default ( ) ,
179+ ambient_variance : ty:: Covariant ,
180+ generics : tcx. generics_of ( parent_def_id) ,
181+ } ;
182+ functional_variances. relate ( sig, sig) . unwrap ( ) ;
183+ functional_variances. variances
184+ } ) ,
185+ outlives_env : LazyCell :: new ( || {
186+ let param_env = tcx. param_env ( parent_def_id) ;
187+ let infcx = tcx. infer_ctxt ( ) . build ( ) ;
188+ let ocx = ObligationCtxt :: new ( & infcx) ;
189+ let assumed_wf_tys = ocx. assumed_wf_types ( param_env, parent_def_id) . unwrap_or_default ( ) ;
190+ let implied_bounds =
191+ infcx. implied_bounds_tys_compat ( param_env, parent_def_id, & assumed_wf_tys, false ) ;
192+ OutlivesEnvironment :: with_bounds ( param_env, implied_bounds)
193+ } ) ,
143194 } ) ;
144195}
145196
146- struct VisitOpaqueTypes < ' tcx > {
197+ struct VisitOpaqueTypes < ' tcx , VarFn , OutlivesFn > {
147198 tcx : TyCtxt < ' tcx > ,
148199 parent_def_id : LocalDefId ,
149- in_scope_parameters : FxIndexSet < DefId > ,
200+ in_scope_parameters : FxIndexMap < DefId , ParamKind > ,
201+ variances : LazyCell < FxHashMap < DefId , ty:: Variance > , VarFn > ,
202+ outlives_env : LazyCell < OutlivesEnvironment < ' tcx > , OutlivesFn > ,
150203 seen : FxIndexSet < LocalDefId > ,
151204}
152205
153- impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for VisitOpaqueTypes < ' tcx > {
154- fn visit_binder < T : TypeVisitable < TyCtxt < ' tcx > > > (
155- & mut self ,
156- t : & ty:: Binder < ' tcx , T > ,
157- ) -> Self :: Result {
206+ impl < ' tcx , VarFn , OutlivesFn > TypeVisitor < TyCtxt < ' tcx > >
207+ for VisitOpaqueTypes < ' tcx , VarFn , OutlivesFn >
208+ where
209+ VarFn : FnOnce ( ) -> FxHashMap < DefId , ty:: Variance > ,
210+ OutlivesFn : FnOnce ( ) -> OutlivesEnvironment < ' tcx > ,
211+ {
212+ fn visit_binder < T : TypeVisitable < TyCtxt < ' tcx > > > ( & mut self , t : & ty:: Binder < ' tcx , T > ) {
158213 // When we get into a binder, we need to add its own bound vars to the scope.
159214 let mut added = vec ! [ ] ;
160215 for arg in t. bound_vars ( ) {
@@ -163,8 +218,8 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
163218 ty:: BoundVariableKind :: Region ( ty:: BoundRegionKind :: BrNamed ( def_id, ..) )
164219 | ty:: BoundVariableKind :: Ty ( ty:: BoundTyKind :: Param ( def_id, _) ) => {
165220 added. push ( def_id) ;
166- let unique = self . in_scope_parameters . insert ( def_id) ;
167- assert ! ( unique) ;
221+ let unique = self . in_scope_parameters . insert ( def_id, ParamKind :: Late ) ;
222+ assert_eq ! ( unique, None ) ;
168223 }
169224 _ => {
170225 self . tcx . dcx ( ) . span_delayed_bug (
@@ -184,7 +239,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
184239 }
185240 }
186241
187- fn visit_ty ( & mut self , t : Ty < ' tcx > ) -> Self :: Result {
242+ fn visit_ty ( & mut self , t : Ty < ' tcx > ) {
188243 if !t. has_aliases ( ) {
189244 return ;
190245 }
@@ -207,89 +262,126 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
207262 && let hir:: OpaqueTyOrigin :: FnReturn ( parent_def_id) = opaque. origin
208263 && parent_def_id == self . parent_def_id
209264 {
210- // Compute the set of args that are captured by the opaque...
211- let mut captured = FxIndexSet :: default ( ) ;
212- let variances = self . tcx . variances_of ( opaque_def_id) ;
213- let mut current_def_id = Some ( opaque_def_id. to_def_id ( ) ) ;
214- while let Some ( def_id) = current_def_id {
215- let generics = self . tcx . generics_of ( def_id) ;
216- for param in & generics. own_params {
217- // A param is captured if it's invariant.
218- if variances[ param. index as usize ] != ty:: Invariant {
219- continue ;
220- }
221- // We need to turn all `ty::Param`/`ConstKind::Param` and
222- // `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- ) ) ;
228- }
229- current_def_id = generics. parent ;
230- }
231-
232- // Compute the set of in scope params that are not captured. Get their spans,
233- // since that's all we really care about them for emitting the diagnostic.
234- let uncaptured_spans: Vec < _ > = self
235- . in_scope_parameters
236- . iter ( )
237- . filter ( |def_id| !captured. contains ( * def_id) )
238- . map ( |def_id| self . tcx . def_span ( def_id) )
239- . collect ( ) ;
240-
241265 let opaque_span = self . tcx . def_span ( opaque_def_id) ;
242266 let new_capture_rules =
243267 opaque_span. at_least_rust_2024 ( ) || self . tcx . features ( ) . lifetime_capture_rules_2024 ;
244-
245- // If we have uncaptured args, and if the opaque doesn't already have
246- // `use<>` syntax on it, and we're < edition 2024, then warn the user.
247268 if !new_capture_rules
248269 && !opaque. bounds . iter ( ) . any ( |bound| matches ! ( bound, hir:: GenericBound :: Use ( ..) ) )
249- && !uncaptured_spans. is_empty ( )
250270 {
251- let suggestion = if let Ok ( snippet) =
252- self . tcx . sess . source_map ( ) . span_to_snippet ( opaque_span)
253- && snippet. starts_with ( "impl " )
254- {
255- let ( lifetimes, others) : ( Vec < _ > , Vec < _ > ) = captured
256- . into_iter ( )
257- . partition ( |def_id| self . tcx . def_kind ( * def_id) == DefKind :: LifetimeParam ) ;
258- // Take all lifetime params first, then all others (ty/ct).
259- let generics: Vec < _ > = lifetimes
260- . into_iter ( )
261- . chain ( others)
262- . map ( |def_id| self . tcx . item_name ( def_id) . to_string ( ) )
263- . collect ( ) ;
264- // Make sure that we're not trying to name any APITs
265- if generics. iter ( ) . all ( |name| !name. starts_with ( "impl " ) ) {
266- Some ( (
267- format ! ( " + use<{}>" , generics. join( ", " ) ) ,
268- opaque_span. shrink_to_hi ( ) ,
269- ) )
271+ // Compute the set of args that are captured by the opaque...
272+ let mut captured = FxIndexSet :: default ( ) ;
273+ let mut captured_regions = FxIndexSet :: default ( ) ;
274+ let variances = self . tcx . variances_of ( opaque_def_id) ;
275+ let mut current_def_id = Some ( opaque_def_id. to_def_id ( ) ) ;
276+ while let Some ( def_id) = current_def_id {
277+ let generics = self . tcx . generics_of ( def_id) ;
278+ for param in & generics. own_params {
279+ // A param is captured if it's invariant.
280+ if variances[ param. index as usize ] != ty:: Invariant {
281+ continue ;
282+ }
283+
284+ let arg = opaque_ty. args [ param. index as usize ] ;
285+ // We need to turn all `ty::Param`/`ConstKind::Param` and
286+ // `ReEarlyParam`/`ReBound` into def ids.
287+ captured. insert ( extract_def_id_from_arg ( self . tcx , generics, arg) ) ;
288+
289+ captured_regions. extend ( arg. as_region ( ) ) ;
290+ }
291+ current_def_id = generics. parent ;
292+ }
293+
294+ // Compute the set of in scope params that are not captured.
295+ let mut uncaptured_args: FxIndexSet < _ > = self
296+ . in_scope_parameters
297+ . iter ( )
298+ . filter ( |& ( def_id, _) | !captured. contains ( def_id) )
299+ . collect ( ) ;
300+ // Remove the set of lifetimes that are in-scope that outlive some other captured
301+ // lifetime and are contravariant (i.e. covariant in argument position).
302+ uncaptured_args. retain ( |& ( def_id, kind) | {
303+ let Some ( ty:: Bivariant | ty:: Contravariant ) = self . variances . get ( def_id) else {
304+ // Keep all covariant/invariant args. Also if variance is `None`,
305+ // then that means it's either not a lifetime, or it didn't show up
306+ // anywhere in the signature.
307+ return true ;
308+ } ;
309+ // We only computed variance of lifetimes...
310+ debug_assert_matches ! ( self . tcx. def_kind( def_id) , DefKind :: LifetimeParam ) ;
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+ // Totally ignore late bound args from binders.
322+ ParamKind :: Late => return true ,
323+ } ;
324+ // Does this region outlive any captured region?
325+ !captured_regions. iter ( ) . any ( |r| {
326+ self . outlives_env
327+ . free_region_map ( )
328+ . sub_free_regions ( self . tcx , * r, uncaptured)
329+ } )
330+ } ) ;
331+
332+ // If we have uncaptured args, and if the opaque doesn't already have
333+ // `use<>` syntax on it, and we're < edition 2024, then warn the user.
334+ if !uncaptured_args. is_empty ( ) {
335+ let suggestion = if let Ok ( snippet) =
336+ self . tcx . sess . source_map ( ) . span_to_snippet ( opaque_span)
337+ && snippet. starts_with ( "impl " )
338+ {
339+ let ( lifetimes, others) : ( Vec < _ > , Vec < _ > ) =
340+ captured. into_iter ( ) . partition ( |def_id| {
341+ self . tcx . def_kind ( * def_id) == DefKind :: LifetimeParam
342+ } ) ;
343+ // Take all lifetime params first, then all others (ty/ct).
344+ let generics: Vec < _ > = lifetimes
345+ . into_iter ( )
346+ . chain ( others)
347+ . map ( |def_id| self . tcx . item_name ( def_id) . to_string ( ) )
348+ . collect ( ) ;
349+ // Make sure that we're not trying to name any APITs
350+ if generics. iter ( ) . all ( |name| !name. starts_with ( "impl " ) ) {
351+ Some ( (
352+ format ! ( " + use<{}>" , generics. join( ", " ) ) ,
353+ opaque_span. shrink_to_hi ( ) ,
354+ ) )
355+ } else {
356+ None
357+ }
270358 } else {
271359 None
272- }
273- } else {
274- None
275- } ;
276-
277- self . tcx . emit_node_span_lint (
278- IMPL_TRAIT_OVERCAPTURES ,
279- self . tcx . local_def_id_to_hir_id ( opaque_def_id) ,
280- opaque_span,
281- ImplTraitOvercapturesLint {
282- self_ty : t,
283- num_captured : uncaptured_spans. len ( ) ,
284- uncaptured_spans,
285- suggestion,
286- } ,
287- ) ;
360+ } ;
361+
362+ let uncaptured_spans: Vec < _ > = uncaptured_args
363+ . into_iter ( )
364+ . map ( |( def_id, _) | self . tcx . def_span ( def_id) )
365+ . collect ( ) ;
366+
367+ self . tcx . emit_node_span_lint (
368+ IMPL_TRAIT_OVERCAPTURES ,
369+ self . tcx . local_def_id_to_hir_id ( opaque_def_id) ,
370+ opaque_span,
371+ ImplTraitOvercapturesLint {
372+ self_ty : t,
373+ num_captured : uncaptured_spans. len ( ) ,
374+ uncaptured_spans,
375+ suggestion,
376+ } ,
377+ ) ;
378+ }
288379 }
380+
289381 // Otherwise, if we are edition 2024, have `use<>` syntax, and
290382 // have no uncaptured args, then we should warn to the user that
291383 // it's redundant to capture all args explicitly.
292- else if new_capture_rules
384+ if new_capture_rules
293385 && let Some ( ( captured_args, capturing_span) ) =
294386 opaque. bounds . iter ( ) . find_map ( |bound| match * bound {
295387 hir:: GenericBound :: Use ( a, s) => Some ( ( a, s) ) ,
@@ -327,7 +419,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
327419 if self
328420 . in_scope_parameters
329421 . iter ( )
330- . all ( |def_id| explicitly_captured. contains ( def_id) )
422+ . all ( |( def_id, _ ) | explicitly_captured. contains ( def_id) )
331423 {
332424 self . tcx . emit_node_span_lint (
333425 IMPL_TRAIT_REDUNDANT_CAPTURES ,
@@ -396,7 +488,11 @@ fn extract_def_id_from_arg<'tcx>(
396488 ty:: ReBound (
397489 _,
398490 ty:: BoundRegion { kind : ty:: BoundRegionKind :: BrNamed ( def_id, ..) , .. } ,
399- ) => def_id,
491+ )
492+ | ty:: ReLateParam ( ty:: LateParamRegion {
493+ scope : _,
494+ bound_region : ty:: BoundRegionKind :: BrNamed ( def_id, ..) ,
495+ } ) => def_id,
400496 _ => unreachable ! ( ) ,
401497 } ,
402498 ty:: GenericArgKind :: Type ( ty) => {
@@ -413,3 +509,106 @@ fn extract_def_id_from_arg<'tcx>(
413509 }
414510 }
415511}
512+
513+ /// Computes the variances of regions that appear in the type, but considering
514+ /// late-bound regions too, which don't have their variance computed usually.
515+ ///
516+ /// Like generalization, this is a unary operation implemented on top of the binary
517+ /// relation infrastructure, mostly because it's much easier to have the relation
518+ /// track the variance for you, rather than having to do it yourself.
519+ struct FunctionalVariances < ' tcx > {
520+ tcx : TyCtxt < ' tcx > ,
521+ variances : FxHashMap < DefId , ty:: Variance > ,
522+ ambient_variance : ty:: Variance ,
523+ generics : & ' tcx ty:: Generics ,
524+ }
525+
526+ impl < ' tcx > TypeRelation < TyCtxt < ' tcx > > for FunctionalVariances < ' tcx > {
527+ fn cx ( & self ) -> TyCtxt < ' tcx > {
528+ self . tcx
529+ }
530+
531+ fn relate_with_variance < T : ty:: relate:: Relate < TyCtxt < ' tcx > > > (
532+ & mut self ,
533+ variance : rustc_type_ir:: Variance ,
534+ _: ty:: VarianceDiagInfo < TyCtxt < ' tcx > > ,
535+ a : T ,
536+ b : T ,
537+ ) -> RelateResult < ' tcx , T > {
538+ let old_variance = self . ambient_variance ;
539+ self . ambient_variance = self . ambient_variance . xform ( variance) ;
540+ self . relate ( a, b) . unwrap ( ) ;
541+ self . ambient_variance = old_variance;
542+ Ok ( a)
543+ }
544+
545+ fn tys ( & mut self , a : Ty < ' tcx > , b : Ty < ' tcx > ) -> RelateResult < ' tcx , Ty < ' tcx > > {
546+ structurally_relate_tys ( self , a, b) . unwrap ( ) ;
547+ Ok ( a)
548+ }
549+
550+ fn regions (
551+ & mut self ,
552+ a : ty:: Region < ' tcx > ,
553+ _: ty:: Region < ' tcx > ,
554+ ) -> RelateResult < ' tcx , ty:: Region < ' tcx > > {
555+ let def_id = match * a {
556+ ty:: ReEarlyParam ( ebr) => self . generics . region_param ( ebr, self . tcx ) . def_id ,
557+ ty:: ReBound (
558+ _,
559+ ty:: BoundRegion { kind : ty:: BoundRegionKind :: BrNamed ( def_id, ..) , .. } ,
560+ )
561+ | ty:: ReLateParam ( ty:: LateParamRegion {
562+ scope : _,
563+ bound_region : ty:: BoundRegionKind :: BrNamed ( def_id, ..) ,
564+ } ) => def_id,
565+ _ => {
566+ return Ok ( a) ;
567+ }
568+ } ;
569+
570+ if let Some ( variance) = self . variances . get_mut ( & def_id) {
571+ * variance = unify ( * variance, self . ambient_variance ) ;
572+ } else {
573+ self . variances . insert ( def_id, self . ambient_variance ) ;
574+ }
575+
576+ Ok ( a)
577+ }
578+
579+ fn consts (
580+ & mut self ,
581+ a : ty:: Const < ' tcx > ,
582+ b : ty:: Const < ' tcx > ,
583+ ) -> RelateResult < ' tcx , ty:: Const < ' tcx > > {
584+ structurally_relate_consts ( self , a, b) . unwrap ( ) ;
585+ Ok ( a)
586+ }
587+
588+ fn binders < T > (
589+ & mut self ,
590+ a : ty:: Binder < ' tcx , T > ,
591+ b : ty:: Binder < ' tcx , T > ,
592+ ) -> RelateResult < ' tcx , ty:: Binder < ' tcx , T > >
593+ where
594+ T : Relate < TyCtxt < ' tcx > > ,
595+ {
596+ self . relate ( a. skip_binder ( ) , b. skip_binder ( ) ) . unwrap ( ) ;
597+ Ok ( a)
598+ }
599+ }
600+
601+ /// What is the variance that satisfies the two variances?
602+ fn unify ( a : ty:: Variance , b : ty:: Variance ) -> ty:: Variance {
603+ match ( a, b) {
604+ // Bivariance is lattice bottom.
605+ ( ty:: Bivariant , other) | ( other, ty:: Bivariant ) => other,
606+ // Invariant is lattice top.
607+ ( ty:: Invariant , _) | ( _, ty:: Invariant ) => ty:: Invariant ,
608+ // If type is required to be covariant and contravariant, then it's invariant.
609+ ( ty:: Contravariant , ty:: Covariant ) | ( ty:: Covariant , ty:: Contravariant ) => ty:: Invariant ,
610+ // Otherwise, co + co = co, contra + contra = contra.
611+ ( ty:: Contravariant , ty:: Contravariant ) => ty:: Contravariant ,
612+ ( ty:: Covariant , ty:: Covariant ) => ty:: Covariant ,
613+ }
614+ }
0 commit comments