diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 7680f8f9ca09..36dbc04d0a6f 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -266,6 +266,16 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { /// Require that the user writes as where clauses on GATs the implicit /// outlives bounds involving trait parameters in trait functions and /// lifetimes passed as GAT substs. See `self-outlives-lint` test. +/// +/// This trait will be our running example. We are currently WF checking the `Item` item... +/// +/// ```rust +/// trait LendingIterator { +/// type Item<'me>; // <-- WF checking this trait item +/// +/// fn next<'a>(&'a mut self) -> Option>; +/// } +/// ``` fn check_gat_where_clauses( tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>, @@ -282,16 +292,25 @@ fn check_gat_where_clauses( return; } let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id); - let mut clauses = FxHashSet::default(); + let mut clauses: Option>> = None; // For every function in this trait... + // In our example, this would be the `next` method for item in associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn)) { + // The clauses we that we would require from this function + let mut function_clauses = FxHashSet::default(); + let id = hir::HirId::make_owner(item.def_id.expect_local()); let param_env = tcx.param_env(item.def_id.expect_local()); let sig = tcx.fn_sig(item.def_id); + // Get the signature using placeholders. In our example, this would + // convert the late-bound 'a into a free region. let sig = tcx.liberate_late_bound_regions(item.def_id, sig); + // 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(), @@ -299,11 +318,30 @@ fn check_gat_where_clauses( types: FxHashSet::default(), }; sig.output().visit_with(&mut visitor); + + // 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() { + continue; + } + + // The types we can assume to be well-formed. In our example, this + // would be &'a mut Self, from the first argument. let mut wf_tys = FxHashSet::default(); wf_tys.extend(sig.inputs()); + // For each region argument (e.g., 'a in our example), check for a + // 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 { + // Unfortunately, we have to use a new `InferCtxt` for each + // pair, because region constraints get added and solved there, + // and we need to test each pair individually. tcx.infer_ctxt().enter(|infcx| { let mut outlives_environment = OutlivesEnvironment::new(param_env); outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); @@ -328,6 +366,7 @@ fn check_gat_where_clauses( Some(tcx.lifetimes.re_root_empty), param_env, ); + // In our example, requires that Self: 'a outlives.type_must_outlive(origin, sup_type, sub_region); let errors = infcx.resolve_regions( @@ -338,14 +377,21 @@ fn check_gat_where_clauses( debug!(?errors, "errors"); + // If we were able to prove that Self: 'a without an error, + // it must be because of the implied or explicit bounds... if errors.is_empty() { debug!(?ty_idx, ?region_idx); debug!("required clause: {} must outlive {}", ty, region); + // Translate into the generic parameters of the GAT. In + // our example, the type was Self, which will also be + // Self in the GAT. let ty_param = generics.param_at(*ty_idx, tcx); let ty_param = tcx.mk_ty(ty::Param(ty::ParamTy { index: ty_param.index, name: ty_param.name, })); + // Same for the region. In our example, 'a corresponds + // to the 'me parameter. let region_param = generics.param_at(*region_idx, tcx); let region_param = tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { @@ -353,22 +399,35 @@ fn check_gat_where_clauses( index: region_param.index, name: region_param.name, })); + // The predicate we expect to see. (In our example, + // `Self: 'me`.) let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( ty_param, region_param, )); let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - clauses.insert(clause); + function_clauses.insert(clause); } }); } } + + match clauses.as_mut() { + Some(clauses) => { + clauses.drain_filter(|p| !function_clauses.contains(p)); + } + None => { + clauses = Some(function_clauses); + } + } } // If there are any missing clauses, emit an error + let mut clauses = clauses.unwrap_or_default(); debug!(?clauses); if !clauses.is_empty() { - let written_predicates: ty::GenericPredicates<'_> = tcx.predicates_of(trait_item.def_id); + let written_predicates: ty::GenericPredicates<'_> = + tcx.explicit_predicates_of(trait_item.def_id); let clauses: Vec<_> = clauses .drain_filter(|clause| { written_predicates.predicates.iter().find(|p| &p.0 == clause).is_none() @@ -402,6 +461,10 @@ fn check_gat_where_clauses( } } +/// TypeVisitor that looks for uses of GATs like +/// `>::GAT` and adds the arguments `P0..Pm` into +/// the two vectors, `regions` and `types` (depending on their kind). For each +/// parameter `Pi` also track the index `i`. struct GATSubstCollector<'tcx> { tcx: TyCtxt<'tcx>, gat: DefId, 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 eb2970522e65..4de850384e7b 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -2,6 +2,8 @@ // check-fail +use std::fmt::Debug; + // We have a `&'a self`, so we need a `Self: 'a` trait Iterable { type Item<'x>; @@ -100,4 +102,70 @@ impl Des3 for () { } */ +// Similar case to before, except with GAT. +trait NoGat<'a> { + type Bar; + fn method(&'a self) -> Self::Bar; +} + +// Lifetime is not on function; except `Self: 'a` +trait TraitLifetime<'a> { + type Bar<'b>; + //~^ Missing required bounds + fn method(&'a self) -> Self::Bar<'a>; +} + +// Like above, but we have a where clause that can prove what we want +trait TraitLifetimeWhere<'a> where Self: 'a { + type Bar<'b>; + //~^ Missing required bounds + fn method(&'a self) -> Self::Bar<'a>; +} + +// Explicit bound instead of implicit; we want to still error +trait ExplicitBound { + type Bar<'b>; + //~^ Missing required bounds + fn method<'b>(&self, token: &'b ()) -> Self::Bar<'b> where Self: 'b; +} + +// The use of the GAT here is not in the return, we don't want to error +trait NotInReturn { + type Bar<'b>; + fn method<'b>(&'b self) where Self::Bar<'b>: Debug; +} + +// We obviously error for `Iterator`, but we should also error for `Item` +trait IterableTwo { + type Item<'a>; + type Iterator<'a>: Iterator>; + //~^ Missing required bounds + fn iter<'a>(&'a self) -> Self::Iterator<'a>; +} + +// We also should report region outlives clauses +trait RegionOutlives { + type Bar<'a, 'b>; + //~^ Missing required bounds + fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y>; +} + +/* +impl Foo for () { + type Bar<'a, 'b> = &'a &'b (); + fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y> { + input + } +} +*/ + +// If there are multiple methods that return the GAT, require a set of clauses +// that can be satisfied by *all* methods +trait MultipleMethods { + type Bar<'me>; + + fn gimme<'a>(&'a self) -> Self::Bar<'a>; + fn gimme_default(&self) -> Self::Bar<'static>; +} + fn main() {} 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 3dacae65267c..acccbf24319c 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -1,5 +1,5 @@ error: Missing required bounds on Item - --> $DIR/self-outlives-lint.rs:7:5 + --> $DIR/self-outlives-lint.rs:9:5 | LL | type Item<'x>; | ^^^^^^^^^^^^^- @@ -7,7 +7,7 @@ LL | type Item<'x>; | help: add the required where clauses: `where Self: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:23:5 + --> $DIR/self-outlives-lint.rs:25:5 | LL | type Out<'x>; | ^^^^^^^^^^^^- @@ -15,7 +15,7 @@ LL | type Out<'x>; | help: add the required where clauses: `where T: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:37:5 + --> $DIR/self-outlives-lint.rs:39:5 | LL | type Out<'x>; | ^^^^^^^^^^^^- @@ -23,7 +23,7 @@ LL | type Out<'x>; | help: add the required where clauses: `where T: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:44:5 + --> $DIR/self-outlives-lint.rs:46:5 | LL | type Out<'x, 'y>; | ^^^^^^^^^^^^^^^^- @@ -31,7 +31,7 @@ LL | type Out<'x, 'y>; | help: add the required where clauses: `where U: 'y, T: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:59:5 + --> $DIR/self-outlives-lint.rs:61:5 | LL | type Out<'x, D>; | ^^^^^^^^^^^^^^^- @@ -39,7 +39,7 @@ LL | type Out<'x, D>; | help: add the required where clauses: `where D: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:75:5 + --> $DIR/self-outlives-lint.rs:77:5 | LL | type Out<'x, D>; | ^^^^^^^^^^^^^^^- @@ -47,12 +47,44 @@ LL | type Out<'x, D>; | help: add the required where clauses: `where D: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:90:5 + --> $DIR/self-outlives-lint.rs:92:5 | LL | type Out<'x, D>; | ^^^^^^^^^^^^^^^- | | | help: add the required where clauses: `where D: 'x` -error: aborting due to 7 previous errors +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:113:5 + | +LL | type Bar<'b>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'b, Self: 'a` + +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:120:5 + | +LL | type Bar<'b>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'b, Self: 'a` + +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:127:5 + | +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 + | +LL | type Iterator<'a>: Iterator>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'a` + +error: aborting due to 11 previous errors