@@ -263,9 +263,24 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
263
263
/// Require that the user writes where clauses on GATs for the implicit
264
264
/// outlives bounds involving trait parameters in trait functions and
265
265
/// 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
+ /// }
266
274
/// ```
267
275
fn 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.
268
277
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.
269
284
loop {
270
285
let mut should_continue = false ;
271
286
for gat_item in associated_items {
@@ -276,14 +291,18 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
276
291
continue ;
277
292
}
278
293
let gat_generics = tcx. generics_of ( gat_def_id) ;
294
+ // FIXME(jackh726): we can also warn in the more general case
279
295
if gat_generics. params . is_empty ( ) {
280
296
continue ;
281
297
}
282
298
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.
283
302
let mut new_required_bounds: Option < FxHashSet < ty:: Predicate < ' _ > > > = None ;
284
303
for item in associated_items {
285
304
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.
287
306
if item_def_id == gat_def_id {
288
307
continue ;
289
308
}
@@ -292,7 +311,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
292
311
let param_env = tcx. param_env ( item_def_id) ;
293
312
294
313
let item_required_bounds = match item. kind {
314
+ // In our example, this corresponds to `into_iter` method
295
315
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.
296
319
let sig: ty:: FnSig < ' _ > = tcx. liberate_late_bound_regions (
297
320
item_def_id. to_def_id ( ) ,
298
321
tcx. fn_sig ( item_def_id) ,
@@ -302,11 +325,14 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
302
325
param_env,
303
326
item_hir_id,
304
327
sig. output ( ) ,
328
+ // We also assume that all of the function signature's parameter types
329
+ // are well formed.
305
330
& sig. inputs ( ) . iter ( ) . copied ( ) . collect ( ) ,
306
331
gat_def_id,
307
332
gat_generics,
308
333
)
309
334
}
335
+ // In our example, this corresponds to the `Iter` and `Item` associated types
310
336
hir:: AssocItemKind :: Type => {
311
337
// If our associated item is a GAT with missing bounds, add them to
312
338
// 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
316
342
param_env,
317
343
required_bounds_by_item. get ( & item_def_id) ,
318
344
) ;
345
+ // FIXME(compiler-errors): Do we want to add a assoc ty default to the wf_tys?
319
346
gather_gat_bounds (
320
347
tcx,
321
348
param_env,
@@ -333,9 +360,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
333
360
} ;
334
361
335
362
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.
339
368
if let Some ( new_required_bounds) = & mut new_required_bounds {
340
369
new_required_bounds. retain ( |b| item_required_bounds. contains ( b) ) ;
341
370
} else {
@@ -346,14 +375,17 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
346
375
347
376
if let Some ( new_required_bounds) = new_required_bounds {
348
377
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) ) {
351
379
// Iterate until our required_bounds no longer change
352
380
// Since they changed here, we should continue the loop
353
381
should_continue = true ;
354
382
}
355
383
}
356
384
}
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.
357
389
if !should_continue {
358
390
break ;
359
391
}
@@ -422,8 +454,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
422
454
}
423
455
}
424
456
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.
427
458
fn augment_param_env < ' tcx > (
428
459
tcx : TyCtxt < ' tcx > ,
429
460
param_env : ty:: ParamEnv < ' tcx > ,
@@ -444,6 +475,16 @@ fn augment_param_env<'tcx>(
444
475
ty:: ParamEnv :: new ( bounds, param_env. reveal ( ) , param_env. constness ( ) )
445
476
}
446
477
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
+ /// ```
447
488
fn gather_gat_bounds < ' tcx , T : TypeFoldable < ' tcx > > (
448
489
tcx : TyCtxt < ' tcx > ,
449
490
param_env : ty:: ParamEnv < ' tcx > ,
@@ -453,13 +494,13 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
453
494
gat_def_id : LocalDefId ,
454
495
gat_generics : & ' tcx ty:: Generics ,
455
496
) -> 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`
457
498
let mut bounds = FxHashSet :: default ( ) ;
458
499
459
500
let ( regions, types) = GATSubstCollector :: visit ( tcx, gat_def_id. to_def_id ( ) , to_check) ;
460
501
461
502
// 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
463
504
// (particularly, doing so would end up with an empty set of clauses,
464
505
// since the current method would require none, and we take the
465
506
// intersection of requirements of all methods)
@@ -474,18 +515,18 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
474
515
if let ty:: ReStatic = * * region_a {
475
516
continue ;
476
517
}
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
479
520
// outlives relationship (`Self: 'a`), then we want to ensure that is
480
521
// reflected in a where clause on the GAT itself.
481
522
for ( ty, ty_idx) in & types {
482
- // In our example, requires that Self: 'a
523
+ // In our example, requires that ` Self: 'a`
483
524
if ty_known_to_outlive ( tcx, item_hir, param_env, & wf_tys, * ty, * region_a) {
484
525
debug ! ( ?ty_idx, ?region_a_idx) ;
485
526
debug ! ( "required clause: {} must outlive {}" , ty, region_a) ;
486
527
// 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.
489
530
let ty_param = gat_generics. param_at ( * ty_idx, tcx) ;
490
531
let ty_param = tcx
491
532
. 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>>(
507
548
}
508
549
}
509
550
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.
514
555
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.
515
558
if ty:: ReStatic == * * region_b || region_a == region_b {
516
559
continue ;
517
560
}
0 commit comments