@@ -3,7 +3,7 @@ use crate::check::{FnCtxt, Inherited};
3
3
use crate :: constrained_generic_params:: { identify_constrained_generic_params, Parameter } ;
4
4
5
5
use rustc_ast as ast;
6
- use rustc_data_structures:: fx:: FxHashSet ;
6
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
7
7
use rustc_errors:: { struct_span_err, Applicability , DiagnosticBuilder } ;
8
8
use rustc_hir as hir;
9
9
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
@@ -258,145 +258,131 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
258
258
. emit ( ) ;
259
259
}
260
260
}
261
-
262
- check_gat_where_clauses ( tcx, trait_item, encl_trait_def_id) ;
263
261
}
264
262
265
263
/// Require that the user writes where clauses on GATs for the implicit
266
264
/// outlives bounds involving trait parameters in trait functions and
267
265
/// lifetimes passed as GAT substs. See `self-outlives-lint` test.
268
- ///
269
- /// This trait will be our running example. We are currently WF checking the `Item` item...
270
- ///
271
- /// ```rust
272
- /// trait LendingIterator {
273
- /// type Item<'me>; // <-- WF checking this trait item
274
- ///
275
- /// fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
276
- /// }
277
266
/// ```
278
- fn check_gat_where_clauses ( tcx : TyCtxt < ' _ > , gat_hir : & hir:: TraitItem < ' _ > , gat_def_id : DefId ) {
279
- let gat_item = tcx. associated_item ( gat_def_id) ;
280
- let gat_def_id = gat_hir. def_id ;
281
- // If the current trait item isn't a type, it isn't a GAT
282
- if !matches ! ( gat_item. kind, ty:: AssocKind :: Type ) {
283
- return ;
284
- }
285
- let gat_generics: & ty:: Generics = tcx. generics_of ( gat_def_id) ;
286
- // If the current associated type doesn't have any (own) params, it's not a GAT
287
- // FIXME(jackh726): we can also warn in the more general case
288
- if gat_generics. params . len ( ) == 0 {
289
- return ;
290
- }
291
- let associated_items: & ty:: AssocItems < ' _ > = tcx. associated_items ( gat_def_id) ;
292
- let mut clauses: Option < FxHashSet < ty:: Predicate < ' _ > > > = None ;
293
- // For every function in this trait...
294
- // In our example, this would be the `next` method
295
- for item in
296
- associated_items. in_definition_order ( ) . filter ( |item| matches ! ( item. kind, ty:: AssocKind :: Fn ) )
297
- {
298
- let item_hir_id = hir:: HirId :: make_owner ( item. def_id . expect_local ( ) ) ;
299
- let param_env = tcx. param_env ( item. def_id . expect_local ( ) ) ;
267
+ fn check_gat_where_clauses ( tcx : TyCtxt < ' _ > , associated_items : & [ hir:: TraitItemRef ] ) {
268
+ let mut required_bounds_by_item = FxHashMap :: default ( ) ;
269
+
270
+ for gat_item in associated_items {
271
+ let gat_def_id = gat_item. id . def_id ;
272
+ let gat_item = tcx. associated_item ( gat_def_id) ;
273
+ // If this item is not an assoc ty, or has no substs, then it's not a GAT
274
+ if gat_item. kind != ty:: AssocKind :: Type {
275
+ continue ;
276
+ }
277
+ let gat_generics = tcx. generics_of ( gat_def_id) ;
278
+ if gat_generics. params . is_empty ( ) {
279
+ continue ;
280
+ }
300
281
301
- // Get the signature using placeholders. In our example, this would
302
- // convert the late-bound 'a into a free region.
303
- let sig = tcx. liberate_late_bound_regions ( item. def_id , tcx. fn_sig ( item. def_id ) ) ;
282
+ let mut new_required_bounds: Option < FxHashSet < ty:: Predicate < ' _ > > > = None ;
283
+ for item in associated_items {
284
+ if !matches ! ( & item. kind, hir:: AssocItemKind :: Fn { .. } ) {
285
+ // FIXME: next commit will add items...
286
+ continue ;
287
+ }
304
288
305
- // The types we can assume to be well-formed. In our example, this
306
- // would be &'a mut Self, from the first argument.
307
- let mut wf_tys = FxHashSet :: default ( ) ;
308
- wf_tys. extend ( sig. inputs ( ) ) ;
289
+ let item_def_id = item. id . def_id ;
290
+ // Skip our own GAT, since it would blow away the required bounds
291
+ if item_def_id == gat_def_id {
292
+ continue ;
293
+ }
309
294
310
- // The clauses we that we would require from this function
311
- let function_clauses = gather_gat_bounds (
312
- tcx,
313
- param_env,
314
- item_hir_id,
315
- sig. output ( ) ,
316
- & wf_tys,
317
- gat_def_id,
318
- gat_generics,
319
- ) ;
295
+ let item_hir_id = item. id . hir_id ( ) ;
296
+ let param_env = tcx. param_env ( item_def_id) ;
320
297
321
- if let Some ( function_clauses) = function_clauses {
322
- // Imagine we have:
323
- // ```
324
- // trait Foo {
325
- // type Bar<'me>;
326
- // fn gimme(&self) -> Self::Bar<'_>;
327
- // fn gimme_default(&self) -> Self::Bar<'static>;
328
- // }
329
- // ```
330
- // We only want to require clauses on `Bar` that we can prove from *all* functions (in this
331
- // case, `'me` can be `static` from `gimme_default`)
332
- match clauses. as_mut ( ) {
333
- Some ( clauses) => {
334
- clauses. drain_filter ( |p| !function_clauses. contains ( p) ) ;
335
- }
336
- None => {
337
- clauses = Some ( function_clauses) ;
298
+ // Get the signature using placeholders. In our example, this would
299
+ // convert the late-bound 'a into a free region.
300
+ let sig = tcx. liberate_late_bound_regions (
301
+ item_def_id. to_def_id ( ) ,
302
+ tcx. fn_sig ( item_def_id. to_def_id ( ) ) ,
303
+ ) ;
304
+
305
+ // The types we can assume to be well-formed. In our example, this
306
+ // would be &'a mut Self, from the first argument.
307
+ let mut wf_tys = FxHashSet :: default ( ) ;
308
+ wf_tys. extend ( sig. inputs ( ) ) ;
309
+
310
+ // The clauses we that we would require from this function
311
+ let item_required_bounds = gather_gat_bounds (
312
+ tcx,
313
+ param_env,
314
+ item_hir_id,
315
+ sig. output ( ) ,
316
+ & wf_tys,
317
+ gat_def_id,
318
+ gat_generics,
319
+ ) ;
320
+
321
+ if let Some ( item_required_bounds) = item_required_bounds {
322
+ // Take the intersection of the new_required_bounds and the item_required_bounds
323
+ // for this item. This is why we use an Option<_>, since we need to distinguish
324
+ // the empty set of bounds from the uninitialized set of bounds.
325
+ if let Some ( new_required_bounds) = & mut new_required_bounds {
326
+ new_required_bounds. retain ( |b| item_required_bounds. contains ( b) ) ;
327
+ } else {
328
+ new_required_bounds = Some ( item_required_bounds) ;
338
329
}
339
330
}
340
331
}
332
+
333
+ if let Some ( required_bounds) = new_required_bounds {
334
+ required_bounds_by_item. insert ( gat_def_id, required_bounds) ;
335
+ }
341
336
}
342
337
343
- // If there are any clauses that aren't provable, emit an error
344
- let clauses = clauses. unwrap_or_default ( ) ;
345
- debug ! ( ?clauses) ;
346
- if !clauses. is_empty ( ) {
338
+ for ( gat_def_id, required_bounds) in required_bounds_by_item {
339
+ let gat_item_hir = tcx. hir ( ) . expect_trait_item ( gat_def_id) ;
340
+ debug ! ( ?required_bounds) ;
347
341
let param_env = tcx. param_env ( gat_def_id) ;
342
+ let gat_hir = gat_item_hir. hir_id ( ) ;
348
343
349
- let mut clauses : Vec < _ > = clauses
344
+ let mut unsatisfied_bounds : Vec < _ > = required_bounds
350
345
. into_iter ( )
351
346
. filter ( |clause| match clause. kind ( ) . skip_binder ( ) {
352
347
ty:: PredicateKind :: RegionOutlives ( ty:: OutlivesPredicate ( a, b) ) => {
353
- !region_known_to_outlive (
354
- tcx,
355
- gat_hir. hir_id ( ) ,
356
- param_env,
357
- & FxHashSet :: default ( ) ,
358
- a,
359
- b,
360
- )
348
+ !region_known_to_outlive ( tcx, gat_hir, param_env, & FxHashSet :: default ( ) , a, b)
361
349
}
362
350
ty:: PredicateKind :: TypeOutlives ( ty:: OutlivesPredicate ( a, b) ) => {
363
- !ty_known_to_outlive (
364
- tcx,
365
- gat_hir. hir_id ( ) ,
366
- param_env,
367
- & FxHashSet :: default ( ) ,
368
- a,
369
- b,
370
- )
351
+ !ty_known_to_outlive ( tcx, gat_hir, param_env, & FxHashSet :: default ( ) , a, b)
371
352
}
372
353
_ => bug ! ( "Unexpected PredicateKind" ) ,
373
354
} )
374
- . map ( |clause| format ! ( "{}" , clause) )
355
+ . map ( |clause| clause. to_string ( ) )
375
356
. collect ( ) ;
376
357
377
358
// We sort so that order is predictable
378
- clauses . sort ( ) ;
359
+ unsatisfied_bounds . sort ( ) ;
379
360
380
- if !clauses . is_empty ( ) {
381
- let plural = if clauses . len ( ) > 1 { "s" } else { "" } ;
361
+ if !unsatisfied_bounds . is_empty ( ) {
362
+ let plural = if unsatisfied_bounds . len ( ) > 1 { "s" } else { "" } ;
382
363
let mut err = tcx. sess . struct_span_err (
383
- gat_hir . span ,
384
- & format ! ( "missing required bound{} on `{}`" , plural, gat_hir . ident) ,
364
+ gat_item_hir . span ,
365
+ & format ! ( "missing required bound{} on `{}`" , plural, gat_item_hir . ident) ,
385
366
) ;
386
367
387
368
let suggestion = format ! (
388
369
"{} {}" ,
389
- if !gat_hir. generics. where_clause. predicates. is_empty( ) { "," } else { " where" } ,
390
- clauses. join( ", " ) ,
370
+ if !gat_item_hir. generics. where_clause. predicates. is_empty( ) {
371
+ ","
372
+ } else {
373
+ " where"
374
+ } ,
375
+ unsatisfied_bounds. join( ", " ) ,
391
376
) ;
392
377
err. span_suggestion (
393
- gat_hir . generics . where_clause . tail_span_for_suggestion ( ) ,
378
+ gat_item_hir . generics . where_clause . tail_span_for_suggestion ( ) ,
394
379
& format ! ( "add the required where clause{}" , plural) ,
395
380
suggestion,
396
381
Applicability :: MachineApplicable ,
397
382
) ;
398
383
399
- let bound = if clauses. len ( ) > 1 { "these bounds are" } else { "this bound is" } ;
384
+ let bound =
385
+ if unsatisfied_bounds. len ( ) > 1 { "these bounds are" } else { "this bound is" } ;
400
386
err. note ( & format ! (
401
387
"{} currently required to ensure that impls have maximum flexibility" ,
402
388
bound
@@ -1025,6 +1011,11 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
1025
1011
1026
1012
FxHashSet :: default ( )
1027
1013
} ) ;
1014
+
1015
+ // Only check traits, don't check trait aliases
1016
+ if let hir:: ItemKind :: Trait ( _, _, _, _, items) = item. kind {
1017
+ check_gat_where_clauses ( tcx, items) ;
1018
+ }
1028
1019
}
1029
1020
1030
1021
/// Checks all associated type defaults of trait `trait_def_id`.
0 commit comments