diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index dc9784594c21..0ab9855d3250 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -288,7 +288,6 @@ fn check_gat_where_clauses( associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn)) { let id = hir::HirId::make_owner(item.def_id.expect_local()); - let span = DUMMY_SP; let param_env = tcx.param_env(item.def_id.expect_local()); let sig = tcx.fn_sig(item.def_id); @@ -308,7 +307,7 @@ fn check_gat_where_clauses( for (ty, ty_idx) in &visitor.types { tcx.infer_ctxt().enter(|infcx| { let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, span); + outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); outlives_environment.save_implied_bounds(id); let region_bound_pairs = outlives_environment.region_bound_pairs_map().get(&id).unwrap(); @@ -349,7 +348,6 @@ fn check_gat_where_clauses( name: ty_param.name, })); let region_param = generics.param_at(*region_idx, tcx); - // Then create a clause that is required on the GAT let region_param = tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { def_id: region_param.def_id, @@ -372,13 +370,35 @@ fn check_gat_where_clauses( debug!(?clauses); if !clauses.is_empty() { let written_predicates: ty::GenericPredicates<'_> = tcx.predicates_of(trait_item.def_id); - for clause in clauses { - let found = written_predicates.predicates.iter().find(|p| p.0 == clause).is_some(); - debug!(?clause, ?found); - let mut error = tcx - .sess - .struct_span_err(trait_item.generics.span, &format!("Missing bound: {}", clause)); - error.emit(); + let clauses: Vec<_> = clauses + .drain_filter(|clause| { + written_predicates.predicates.iter().find(|p| &p.0 == clause).is_none() + }) + .map(|clause| format!("{}", clause)) + .collect(); + if !clauses.is_empty() { + let mut err = tcx.sess.struct_span_err( + trait_item.span, + &format!("Missing required bounds on {}", trait_item.ident), + ); + + let suggestion = format!( + "{} {}", + if !trait_item.generics.where_clause.predicates.is_empty() { + "," + } else { + " where" + }, + clauses.join(", "), + ); + err.span_suggestion( + trait_item.generics.where_clause.tail_span_for_suggestion(), + "add the required where clauses", + suggestion, + Applicability::MachineApplicable, + ); + + err.emit() } } } diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 971776c882a1..50afdc23631b 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -69,6 +69,7 @@ This API is completely unstable and subject to change. #![feature(never_type)] #![feature(slice_partition_dedup)] #![feature(control_flow_enum)] +#![feature(hash_drain_filter)] #![recursion_limit = "256"] #[macro_use] 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 7a1fb51eafa0..76273c5249ec 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -2,9 +2,10 @@ // check-fail +// We have a `&'a self`, so we need a `Self: 'a` trait Iterable { type Item<'x>; - //~^ Missing bound + //~^ Missing required bounds fn iter<'a>(&'a self) -> Self::Item<'a>; } @@ -17,9 +18,10 @@ impl Iterable for T { } */ +// We have a `&'a T`, so we need a `T: 'x` trait Deserializer { type Out<'x>; - //~^ Missing bound + //~^ Missing required bounds fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>; } @@ -30,19 +32,21 @@ impl Deserializer for () { } */ +// We have a `&'b T` and a `'b: 'a`, so it is implied that `T: 'a`. Therefore, we need a `T: 'x` trait Deserializer2 { type Out<'x>; - //~^ Missing bound - fn deserialize2<'a, 'b: 'a>(&self, input: &'a T, input2: &'b T) -> Self::Out<'a>; + //~^ Missing required bounds + fn deserialize2<'a, 'b: 'a>(&self, input1: &'b T) -> Self::Out<'a>; } +// We have a `&'a T` and a `&'b U`, so we need a `T: 'x` and a `U: 'y` trait Deserializer3 { type Out<'x, 'y>; - //~^ Missing bound - //~^^ Missing bound + //~^ Missing required bounds fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>; } +// `T` is a param on the function, so it can't be named by the associated type trait Deserializer4 { type Out<'x>; fn deserialize<'a, T>(&self, input: &'a T) -> Self::Out<'a>; @@ -50,36 +54,41 @@ trait Deserializer4 { struct Wrap(T); +// Even though we might theoretically want `D: 'x`, because we pass `Wrap` and +// we see `&'z Wrap`, we are conservative and only add bounds for direct params trait Des { type Out<'x, D>; fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, Wrap>; } /* impl Des for () { - type Out<'x, D> = &'x D; + type Out<'x, D> = &'x D; // Not okay fn des<'a, T>(&self, data: &'a Wrap) -> Self::Out<'a, Wrap> { data } } */ +// We have `T` and `'z` as GAT substs. Because of `&'z Wrap`, there is an +// implied bound that `T: 'z`, so we require `D: 'x` trait Des2 { type Out<'x, D>; - //~^ Missing bound + //~^ Missing required bounds fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, T>; } /* impl Des2 for () { type Out<'x, D> = &'x D; fn des<'a, T>(&self, data: &'a Wrap) -> Self::Out<'a, T> { - data + &data.0 } } */ +// We see `&'z T`, so we require `D: 'x` trait Des3 { type Out<'x, D>; - //~^ Missing bound + //~^ Missing required bounds fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>; } /* 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 42af6d25a238..077c64421a8c 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -1,44 +1,50 @@ -error: Missing bound: Self: 'x - --> $DIR/self-outlives-lint.rs:6:14 +error: Missing required bounds on Item + --> $DIR/self-outlives-lint.rs:7:5 | LL | type Item<'x>; - | ^^^^ + | ^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'x` -error: Missing bound: T: 'x - --> $DIR/self-outlives-lint.rs:21:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:23:5 | LL | type Out<'x>; - | ^^^^ + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where T: 'x` -error: Missing bound: T: 'x - --> $DIR/self-outlives-lint.rs:34:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:37:5 | LL | type Out<'x>; - | ^^^^ + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where T: 'x` -error: Missing bound: U: 'y - --> $DIR/self-outlives-lint.rs:40:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:44:5 | LL | type Out<'x, 'y>; - | ^^^^^^^^ + | ^^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where U: 'y, T: 'x` -error: Missing bound: T: 'x - --> $DIR/self-outlives-lint.rs:40:13 - | -LL | type Out<'x, 'y>; - | ^^^^^^^^ - -error: Missing bound: D: 'x - --> $DIR/self-outlives-lint.rs:67:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:75:5 | LL | type Out<'x, D>; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where D: 'x` -error: Missing bound: D: 'x - --> $DIR/self-outlives-lint.rs:81:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:90:5 | LL | type Out<'x, D>; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where D: 'x` -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors