@@ -263,9 +263,24 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
263263/// Require that the user writes where clauses on GATs for the implicit
264264/// outlives bounds involving trait parameters in trait functions and
265265/// lifetimes passed as GAT substs. See `self-outlives-lint` test.
266+ ///
267+ /// We use the following trait as an example throughout this function:
268+ /// ```rust,ignore (this code fails due to this lint)
269+ /// trait IntoIter {
270+ /// type Iter<'a>: Iterator<Item = Self::Item<'a>>;
271+ /// type Item<'a>;
272+ /// fn into_iter<'a>(&'a self) -> Self::Iter<'a>;
273+ /// }
266274/// ```
267275fn check_gat_where_clauses ( tcx : TyCtxt < ' _ > , associated_items : & [ hir:: TraitItemRef ] ) {
276+ // Associates every GAT's def_id to a list of possibly missing bounds detected by this lint.
268277 let mut required_bounds_by_item = FxHashMap :: default ( ) ;
278+
279+ // Loop over all GATs together, because if this lint suggests adding a where-clause bound
280+ // to one GAT, it might then require us to an additional bound on another GAT.
281+ // In our `IntoIter` example, we discover a missing `Self: 'a` bound on `Iter<'a>`, which
282+ // then in a second loop adds a `Self: 'a` bound to `Item` due to the relationship between
283+ // those GATs.
269284 loop {
270285 let mut should_continue = false ;
271286 for gat_item in associated_items {
@@ -276,14 +291,18 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
276291 continue ;
277292 }
278293 let gat_generics = tcx. generics_of ( gat_def_id) ;
294+ // FIXME(jackh726): we can also warn in the more general case
279295 if gat_generics. params . is_empty ( ) {
280296 continue ;
281297 }
282298
299+ // Gather the bounds with which all other items inside of this trait constrain the GAT.
300+ // This is calculated by taking the intersection of the bounds that each item
301+ // constrains the GAT with individually.
283302 let mut new_required_bounds: Option < FxHashSet < ty:: Predicate < ' _ > > > = None ;
284303 for item in associated_items {
285304 let item_def_id = item. id . def_id ;
286- // Skip our own GAT, since it would blow away the required bounds
305+ // Skip our own GAT, since it does not constrain itself at all.
287306 if item_def_id == gat_def_id {
288307 continue ;
289308 }
@@ -292,7 +311,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
292311 let param_env = tcx. param_env ( item_def_id) ;
293312
294313 let item_required_bounds = match item. kind {
314+ // In our example, this corresponds to `into_iter` method
295315 hir:: AssocItemKind :: Fn { .. } => {
316+ // For methods, we check the function signature's return type for any GATs
317+ // to constrain. In the `into_iter` case, we see that the return type
318+ // `Self::Iter<'a>` is a GAT we want to gather any potential missing bounds from.
296319 let sig: ty:: FnSig < ' _ > = tcx. liberate_late_bound_regions (
297320 item_def_id. to_def_id ( ) ,
298321 tcx. fn_sig ( item_def_id) ,
@@ -302,11 +325,14 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
302325 param_env,
303326 item_hir_id,
304327 sig. output ( ) ,
328+ // We also assume that all of the function signature's parameter types
329+ // are well formed.
305330 & sig. inputs ( ) . iter ( ) . copied ( ) . collect ( ) ,
306331 gat_def_id,
307332 gat_generics,
308333 )
309334 }
335+ // In our example, this corresponds to the `Iter` and `Item` associated types
310336 hir:: AssocItemKind :: Type => {
311337 // If our associated item is a GAT with missing bounds, add them to
312338 // the param-env here. This allows this GAT to propagate missing bounds
@@ -316,6 +342,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
316342 param_env,
317343 required_bounds_by_item. get ( & item_def_id) ,
318344 ) ;
345+ // FIXME(compiler-errors): Do we want to add a assoc ty default to the wf_tys?
319346 gather_gat_bounds (
320347 tcx,
321348 param_env,
@@ -333,9 +360,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
333360 } ;
334361
335362 if let Some ( item_required_bounds) = item_required_bounds {
336- // Take the intersection of the new_required_bounds and the item_required_bounds
337- // for this item. This is why we use an Option<_>, since we need to distinguish
338- // the empty set of bounds from the uninitialized set of bounds.
363+ // Take the intersection of the required bounds for this GAT, and
364+ // the item_required_bounds which are the ones implied by just
365+ // this item alone.
366+ // This is why we use an Option<_>, since we need to distinguish
367+ // the empty set of bounds from the _uninitialized_ set of bounds.
339368 if let Some ( new_required_bounds) = & mut new_required_bounds {
340369 new_required_bounds. retain ( |b| item_required_bounds. contains ( b) ) ;
341370 } else {
@@ -346,14 +375,17 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
346375
347376 if let Some ( new_required_bounds) = new_required_bounds {
348377 let required_bounds = required_bounds_by_item. entry ( gat_def_id) . or_default ( ) ;
349- if new_required_bounds != * required_bounds {
350- * required_bounds = new_required_bounds;
378+ if new_required_bounds. into_iter ( ) . any ( |p| required_bounds. insert ( p) ) {
351379 // Iterate until our required_bounds no longer change
352380 // Since they changed here, we should continue the loop
353381 should_continue = true ;
354382 }
355383 }
356384 }
385+ // We know that this loop will eventually halt, since we only set `should_continue` if the
386+ // `required_bounds` for this item grows. Since we are not creating any new region or type
387+ // variables, the set of all region and type bounds that we could ever insert are limited
388+ // by the number of unique types and regions we observe in a given item.
357389 if !should_continue {
358390 break ;
359391 }
@@ -422,8 +454,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
422454 }
423455}
424456
425- /// Add a new set of predicates to the caller_bounds of an existing param_env,
426- /// and normalize the param_env afterwards
457+ /// Add a new set of predicates to the caller_bounds of an existing param_env.
427458fn augment_param_env < ' tcx > (
428459 tcx : TyCtxt < ' tcx > ,
429460 param_env : ty:: ParamEnv < ' tcx > ,
@@ -444,6 +475,16 @@ fn augment_param_env<'tcx>(
444475 ty:: ParamEnv :: new ( bounds, param_env. reveal ( ) , param_env. constness ( ) )
445476}
446477
478+ /// We use the following trait as an example throughout this function.
479+ /// Specifically, let's assume that `to_check` here is the return type
480+ /// of `into_iter`, and the GAT we are checking this for is `Iter`.
481+ /// ```rust,ignore (this code fails due to this lint)
482+ /// trait IntoIter {
483+ /// type Iter<'a>: Iterator<Item = Self::Item<'a>>;
484+ /// type Item<'a>;
485+ /// fn into_iter<'a>(&'a self) -> Self::Iter<'a>;
486+ /// }
487+ /// ```
447488fn gather_gat_bounds < ' tcx , T : TypeFoldable < ' tcx > > (
448489 tcx : TyCtxt < ' tcx > ,
449490 param_env : ty:: ParamEnv < ' tcx > ,
@@ -453,13 +494,13 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
453494 gat_def_id : LocalDefId ,
454495 gat_generics : & ' tcx ty:: Generics ,
455496) -> Option < FxHashSet < ty:: Predicate < ' tcx > > > {
456- // The bounds we that we would require from this function
497+ // The bounds we that we would require from `to_check`
457498 let mut bounds = FxHashSet :: default ( ) ;
458499
459500 let ( regions, types) = GATSubstCollector :: visit ( tcx, gat_def_id. to_def_id ( ) , to_check) ;
460501
461502 // If both regions and types are empty, then this GAT isn't in the
462- // return type , and we shouldn't try to do clause analysis
503+ // set of types we are checking , and we shouldn't try to do clause analysis
463504 // (particularly, doing so would end up with an empty set of clauses,
464505 // since the current method would require none, and we take the
465506 // intersection of requirements of all methods)
@@ -474,18 +515,18 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
474515 if let ty:: ReStatic = * * region_a {
475516 continue ;
476517 }
477- // For each region argument (e.g., 'a in our example), check for a
478- // relationship to the type arguments (e.g., Self). If there is an
518+ // For each region argument (e.g., `'a` in our example), check for a
519+ // relationship to the type arguments (e.g., ` Self` ). If there is an
479520 // outlives relationship (`Self: 'a`), then we want to ensure that is
480521 // reflected in a where clause on the GAT itself.
481522 for ( ty, ty_idx) in & types {
482- // In our example, requires that Self: 'a
523+ // In our example, requires that ` Self: 'a`
483524 if ty_known_to_outlive ( tcx, item_hir, param_env, & wf_tys, * ty, * region_a) {
484525 debug ! ( ?ty_idx, ?region_a_idx) ;
485526 debug ! ( "required clause: {} must outlive {}" , ty, region_a) ;
486527 // Translate into the generic parameters of the GAT. In
487- // our example, the type was Self, which will also be
488- // Self in the GAT.
528+ // our example, the type was ` Self` , which will also be
529+ // ` Self` in the GAT.
489530 let ty_param = gat_generics. param_at ( * ty_idx, tcx) ;
490531 let ty_param = tcx
491532 . mk_ty ( ty:: Param ( ty:: ParamTy { index : ty_param. index , name : ty_param. name } ) ) ;
@@ -507,11 +548,13 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
507548 }
508549 }
509550
510- // For each region argument (e.g., 'a in our example), also check for a
511- // relationship to the other region arguments. If there is an
512- // outlives relationship, then we want to ensure that is
513- // reflected in a where clause on the GAT itself.
551+ // For each region argument (e.g., `'a` in our example), also check for a
552+ // relationship to the other region arguments. If there is an outlives
553+ // relationship, then we want to ensure that is reflected in the where clause
554+ // on the GAT itself.
514555 for ( region_b, region_b_idx) in & regions {
556+ // Again, skip `'static` because it outlives everything. Also, we trivially
557+ // know that a region outlives itself.
515558 if ty:: ReStatic == * * region_b || region_a == region_b {
516559 continue ;
517560 }
0 commit comments