diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 3f5d4383838a..06bfc427bbab 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -263,7 +263,7 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_gat_where_clauses(tcx, trait_item, encl_trait_def_id); } -/// Require that the user writes as where clauses on GATs the implicit +/// Require that the user writes where clauses on GATs for the implicit /// outlives bounds involving trait parameters in trait functions and /// lifetimes passed as GAT substs. See `self-outlives-lint` test. /// @@ -288,6 +288,7 @@ fn check_gat_where_clauses( } let generics: &ty::Generics = tcx.generics_of(trait_item.def_id); // If the current associated type doesn't have any (own) params, it's not a GAT + // FIXME(jackh726): we can also warn in the more general case if generics.params.len() == 0 { return; } @@ -311,20 +312,15 @@ fn check_gat_where_clauses( // Collect the arguments that are given to this GAT in the return type // of the function signature. In our example, the GAT in the return // type is `::Item<'a>`, so 'a and Self are arguments. - let mut visitor = GATSubstCollector { - tcx, - gat: trait_item.def_id.to_def_id(), - regions: FxHashSet::default(), - types: FxHashSet::default(), - }; - sig.output().visit_with(&mut visitor); + let (regions, types) = + GATSubstCollector::visit(tcx, trait_item.def_id.to_def_id(), sig.output()); // If both regions and types are empty, then this GAT isn't in the // return type, and we shouldn't try to do clause analysis // (particularly, doing so would end up with an empty set of clauses, // since the current method would require none, and we take the // intersection of requirements of all methods) - if visitor.types.is_empty() && visitor.regions.is_empty() { + if types.is_empty() && regions.is_empty() { continue; } @@ -337,8 +333,8 @@ fn check_gat_where_clauses( // relationship to the type arguments (e.g., Self). If there is an // outlives relationship (`Self: 'a`), then we want to ensure that is // reflected in a where clause on the GAT itself. - for (region, region_idx) in &visitor.regions { - for (ty, ty_idx) in &visitor.types { + for (region, region_idx) in ®ions { + for (ty, ty_idx) in &types { // In our example, requires that Self: 'a if ty_known_to_outlive(tcx, id, param_env, &wf_tys, *ty, *region) { debug!(?ty_idx, ?region_idx); @@ -376,8 +372,8 @@ fn check_gat_where_clauses( // relationship to the other region arguments. If there is an // outlives relationship, then we want to ensure that is // reflected in a where clause on the GAT itself. - for (region_a, region_a_idx) in &visitor.regions { - for (region_b, region_b_idx) in &visitor.regions { + for (region_a, region_a_idx) in ®ions { + for (region_b, region_b_idx) in ®ions { if region_a == region_b { continue; } @@ -412,6 +408,16 @@ fn check_gat_where_clauses( } } + // Imagine we have: + // ``` + // trait Foo { + // type Bar<'me>; + // fn gimme(&self) -> Self::Bar<'_>; + // fn gimme_default(&self) -> Self::Bar<'static>; + // } + // ``` + // We only want to require clauses on `Bar` that we can prove from *all* functions (in this + // case, `'me` can be `static` from `gimme_default`) match clauses.as_mut() { Some(clauses) => { clauses.drain_filter(|p| !function_clauses.contains(p)); @@ -428,12 +434,14 @@ fn check_gat_where_clauses( if !clauses.is_empty() { let written_predicates: ty::GenericPredicates<'_> = tcx.explicit_predicates_of(trait_item.def_id); - let clauses: Vec<_> = clauses + let mut clauses: Vec<_> = clauses .drain_filter(|clause| { written_predicates.predicates.iter().find(|p| &p.0 == clause).is_none() }) .map(|clause| format!("{}", clause)) .collect(); + // We sort so that order is predictable + clauses.sort(); if !clauses.is_empty() { let mut err = tcx.sess.struct_span_err( trait_item.span, @@ -461,6 +469,8 @@ fn check_gat_where_clauses( } } +// FIXME(jackh726): refactor some of the shared logic between the two functions below + /// Given a known `param_env` and a set of well formed types, can we prove that /// `ty` outlives `region`. fn ty_known_to_outlive<'tcx>( @@ -564,6 +574,23 @@ struct GATSubstCollector<'tcx> { types: FxHashSet<(Ty<'tcx>, usize)>, } +impl<'tcx> GATSubstCollector<'tcx> { + fn visit>( + tcx: TyCtxt<'tcx>, + gat: DefId, + t: T, + ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) { + let mut visitor = GATSubstCollector { + tcx, + gat, + regions: FxHashSet::default(), + types: FxHashSet::default(), + }; + t.visit_with(&mut visitor); + (visitor.regions, visitor.types) + } +} + impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { type BreakTy = !; diff --git a/src/test/ui/generic-associated-types/self-outlives-lint.rs b/src/test/ui/generic-associated-types/self-outlives-lint.rs index 4de850384e7b..af90d158855d 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -109,6 +109,7 @@ trait NoGat<'a> { } // Lifetime is not on function; except `Self: 'a` +// FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one trait TraitLifetime<'a> { type Bar<'b>; //~^ Missing required bounds @@ -116,6 +117,7 @@ trait TraitLifetime<'a> { } // Like above, but we have a where clause that can prove what we want +// FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one trait TraitLifetimeWhere<'a> where Self: 'a { type Bar<'b>; //~^ Missing required bounds diff --git a/src/test/ui/generic-associated-types/self-outlives-lint.stderr b/src/test/ui/generic-associated-types/self-outlives-lint.stderr index df858c27cd3c..bf85780f69f7 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -28,7 +28,7 @@ error: Missing required bounds on Out LL | type Out<'x, 'y>; | ^^^^^^^^^^^^^^^^- | | - | help: add the required where clauses: `where U: 'y, T: 'x` + | help: add the required where clauses: `where T: 'x, U: 'y` error: Missing required bounds on Out --> $DIR/self-outlives-lint.rs:61:5 @@ -55,23 +55,23 @@ LL | type Out<'x, D>; | help: add the required where clauses: `where D: 'x` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:113:5 + --> $DIR/self-outlives-lint.rs:114:5 | LL | type Bar<'b>; | ^^^^^^^^^^^^- | | - | help: add the required where clauses: `where Self: 'b, Self: 'a` + | help: add the required where clauses: `where Self: 'a, Self: 'b` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:120:5 + --> $DIR/self-outlives-lint.rs:122:5 | LL | type Bar<'b>; | ^^^^^^^^^^^^- | | - | help: add the required where clauses: `where Self: 'b, Self: 'a` + | help: add the required where clauses: `where Self: 'a, Self: 'b` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:127:5 + --> $DIR/self-outlives-lint.rs:129:5 | LL | type Bar<'b>; | ^^^^^^^^^^^^- @@ -79,7 +79,7 @@ LL | type Bar<'b>; | help: add the required where clauses: `where Self: 'b` error: Missing required bounds on Iterator - --> $DIR/self-outlives-lint.rs:141:5 + --> $DIR/self-outlives-lint.rs:143:5 | LL | type Iterator<'a>: Iterator>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -87,7 +87,7 @@ LL | type Iterator<'a>: Iterator>; | help: add the required where clauses: `where Self: 'a` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:148:5 + --> $DIR/self-outlives-lint.rs:150:5 | LL | type Bar<'a, 'b>; | ^^^^^^^^^^^^^^^^-