diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6b905f67e68..9dae978dcde 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1252,16 +1252,16 @@ pub fn set_tainted_by_errors(&self) { self.tainted_by_errors_flag.set(true) } - /// Process the region constraints and report any errors that + /// Process the region constraints and return any any errors that /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. - pub fn resolve_regions_and_report_errors( + pub fn resolve_regions( &self, region_context: DefId, outlives_env: &OutlivesEnvironment<'tcx>, mode: RegionckMode, - ) { + ) -> Vec> { let (var_infos, data) = { let mut inner = self.inner.borrow_mut(); let inner = &mut *inner; @@ -1287,6 +1287,21 @@ pub fn resolve_regions_and_report_errors( let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); assert!(old_value.is_none()); + errors + } + + /// Process the region constraints and report any errors that + /// result. After this, no more unification operations should be + /// done -- or the compiler will panic -- but it is legal to use + /// `resolve_vars_if_possible` as well as `fully_resolve`. + pub fn resolve_regions_and_report_errors( + &self, + region_context: DefId, + outlives_env: &OutlivesEnvironment<'tcx>, + mode: RegionckMode, + ) { + let errors = self.resolve_regions(region_context, outlives_env, mode); + if !self.is_tainted_by_errors() { // As a heuristic, just skip reporting region errors // altogether if other errors have been reported while diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 230a576046a..86d4e4d2b11 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -103,7 +103,7 @@ macro_rules! ignore_err { }; } -trait OutlivesEnvironmentExt<'tcx> { +pub(crate) trait OutlivesEnvironmentExt<'tcx> { fn add_implied_bounds( &mut self, infcx: &InferCtxt<'a, 'tcx>, diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 30aab38b1eb..06bfc427bba 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1,3 +1,4 @@ +use crate::check::regionck::OutlivesEnvironmentExt; use crate::check::{FnCtxt, Inherited}; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; @@ -11,16 +12,21 @@ use rustc_hir::itemlikevisit::ParItemLikeVisitor; use rustc_hir::lang_items::LangItem; use rustc_hir::ItemKind; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::outlives::obligations::TypeOutlives; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::{self, RegionckMode, SubregionOrigin}; use rustc_middle::hir::map as hir_map; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ - self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, + self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor, + WithConstness, }; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; +use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc}; use std::convert::TryInto; @@ -253,6 +259,364 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { .emit(); } } + + check_gat_where_clauses(tcx, trait_item, encl_trait_def_id); +} + +/// 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. +/// +/// 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<'_>, + encl_trait_def_id: DefId, +) { + let item = tcx.associated_item(trait_item.def_id); + // If the current trait item isn't a type, it isn't a GAT + if !matches!(item.kind, ty::AssocKind::Type) { + return; + } + 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; + } + let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id); + 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 (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 types.is_empty() && 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 ®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); + 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 { + def_id: region_param.def_id, + 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)); + function_clauses.insert(clause); + } + } + } + + // For each region argument (e.g., 'a in our example), also check for a + // 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 ®ions { + for (region_b, region_b_idx) in ®ions { + if region_a == region_b { + continue; + } + + if region_known_to_outlive(tcx, id, param_env, &wf_tys, *region_a, *region_b) { + debug!(?region_a_idx, ?region_b_idx); + debug!("required clause: {} must outlive {}", region_a, region_b); + // Translate into the generic parameters of the GAT. + let region_a_param = generics.param_at(*region_a_idx, tcx); + let region_a_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_a_param.def_id, + index: region_a_param.index, + name: region_a_param.name, + })); + // Same for the region. + let region_b_param = generics.param_at(*region_b_idx, tcx); + let region_b_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_b_param.def_id, + index: region_b_param.index, + name: region_b_param.name, + })); + // The predicate we expect to see. + let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( + region_a_param, + region_b_param, + )); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + function_clauses.insert(clause); + } + } + } + + // 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)); + } + 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.explicit_predicates_of(trait_item.def_id); + 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, + &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() + } + } +} + +// 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>( + tcx: TyCtxt<'tcx>, + id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxHashSet>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, +) -> bool { + // Unfortunately, we have to use a new `InferCtxt` each call, because + // region constraints get added and solved there and we need to test each + // call 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); + outlives_environment.save_implied_bounds(id); + let region_bound_pairs = outlives_environment.region_bound_pairs_map().get(&id).unwrap(); + + let cause = ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); + + let sup_type = ty; + let sub_region = region; + + let origin = SubregionOrigin::from_obligation_cause(&cause, || { + infer::RelateParamBound(cause.span, sup_type, None) + }); + + let outlives = &mut TypeOutlives::new( + &infcx, + tcx, + ®ion_bound_pairs, + Some(infcx.tcx.lifetimes.re_root_empty), + param_env, + ); + outlives.type_must_outlive(origin, sup_type, sub_region); + + let errors = infcx.resolve_regions( + id.expect_owner().to_def_id(), + &outlives_environment, + RegionckMode::default(), + ); + + debug!(?errors, "errors"); + + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() + }) +} + +fn region_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxHashSet>, + region_a: ty::Region<'tcx>, + region_b: ty::Region<'tcx>, +) -> bool { + // Unfortunately, we have to use a new `InferCtxt` each call, because + // region constraints get added and solved there and we need to test each + // call 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); + outlives_environment.save_implied_bounds(id); + + let cause = ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); + + let origin = SubregionOrigin::from_obligation_cause(&cause, || { + infer::RelateRegionParamBound(cause.span) + }); + + use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; + (&infcx).push_sub_region_constraint(origin, region_a, region_b); + + let errors = infcx.resolve_regions( + id.expect_owner().to_def_id(), + &outlives_environment, + RegionckMode::default(), + ); + + debug!(?errors, "errors"); + + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() + }) +} + +/// 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, + // Which region appears and which parameter index its subsituted for + regions: FxHashSet<(ty::Region<'tcx>, usize)>, + // Which params appears and which parameter index its subsituted for + 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 = !; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + match t.kind() { + ty::Projection(p) if p.item_def_id == self.gat => { + for (idx, subst) in p.substs.iter().enumerate() { + match subst.unpack() { + GenericArgKind::Lifetime(lt) => { + self.regions.insert((lt, idx)); + } + GenericArgKind::Type(t) => { + self.types.insert((t, idx)); + } + _ => {} + } + } + } + _ => {} + } + t.super_visit_with(self) + } + + fn tcx_for_anon_const_substs(&self) -> Option> { + Some(self.tcx) + } } fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 017e7ad8ca7..f90cfb88491 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -70,6 +70,7 @@ #![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/collections-project-default.rs b/src/test/ui/generic-associated-types/collections-project-default.rs index 0944bf110c1..5b94cdee7c9 100644 --- a/src/test/ui/generic-associated-types/collections-project-default.rs +++ b/src/test/ui/generic-associated-types/collections-project-default.rs @@ -8,7 +8,7 @@ // check that we don't normalize with trait defaults. trait Collection { - type Iter<'iter>: Iterator where T: 'iter; + type Iter<'iter>: Iterator where T: 'iter, Self: 'iter; type Family: CollectionFamily; // Test associated type defaults with parameters type Sibling: Collection = diff --git a/src/test/ui/generic-associated-types/collections.rs b/src/test/ui/generic-associated-types/collections.rs index f14c6dac1b1..b0f2fb3f567 100644 --- a/src/test/ui/generic-associated-types/collections.rs +++ b/src/test/ui/generic-associated-types/collections.rs @@ -8,7 +8,7 @@ // run-pass trait Collection { - type Iter<'iter>: Iterator where T: 'iter; + type Iter<'iter>: Iterator where T: 'iter, Self: 'iter; type Family: CollectionFamily; // Test associated type defaults with parameters type Sibling: Collection = diff --git a/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs b/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs index 5d3a3a89352..d7c4dbda264 100644 --- a/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs +++ b/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs @@ -3,7 +3,7 @@ #![feature(generic_associated_types)] pub trait X { - type Y<'a>; + type Y<'a> where Self: 'a; fn m(&self) -> Self::Y<'_>; } diff --git a/src/test/ui/generic-associated-types/issue-70303.rs b/src/test/ui/generic-associated-types/issue-70303.rs index d238f53bde7..568996e1a17 100644 --- a/src/test/ui/generic-associated-types/issue-70303.rs +++ b/src/test/ui/generic-associated-types/issue-70303.rs @@ -3,7 +3,7 @@ #![feature(generic_associated_types)] trait Document { - type Cursor<'a>: DocCursor<'a>; + type Cursor<'a>: DocCursor<'a> where Self: 'a; fn cursor(&self) -> Self::Cursor<'_>; } diff --git a/src/test/ui/generic-associated-types/issue-76535.rs b/src/test/ui/generic-associated-types/issue-76535.rs index 1dad856d5a3..20c6924afa6 100644 --- a/src/test/ui/generic-associated-types/issue-76535.rs +++ b/src/test/ui/generic-associated-types/issue-76535.rs @@ -3,7 +3,7 @@ pub trait SubTrait {} pub trait SuperTrait { - type SubType<'a>: SubTrait; + type SubType<'a>: SubTrait where Self: 'a; fn get_sub<'a>(&'a mut self) -> Self::SubType<'a>; } diff --git a/src/test/ui/generic-associated-types/issue-76535.stderr b/src/test/ui/generic-associated-types/issue-76535.stderr index 0a7eb5dde60..64eeec1b2fc 100644 --- a/src/test/ui/generic-associated-types/issue-76535.stderr +++ b/src/test/ui/generic-associated-types/issue-76535.stderr @@ -7,7 +7,7 @@ LL | let sub: Box> = Box::new(SuperStruc note: associated type defined here, with 1 lifetime parameter: `'a` --> $DIR/issue-76535.rs:6:10 | -LL | type SubType<'a>: SubTrait; +LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ -- help: add missing lifetime argument | @@ -25,7 +25,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | pub trait SuperTrait { | ---------- this trait cannot be made into an object... -LL | type SubType<'a>: SubTrait; +LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ ...because it contains the generic associated type `SubType` = help: consider moving `SubType` to another trait @@ -40,7 +40,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | pub trait SuperTrait { | ---------- this trait cannot be made into an object... -LL | type SubType<'a>: SubTrait; +LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ ...because it contains the generic associated type `SubType` = help: consider moving `SubType` to another trait = note: required because of the requirements on the impl of `CoerceUnsized>>>` for `Box` diff --git a/src/test/ui/generic-associated-types/issue-79422.rs b/src/test/ui/generic-associated-types/issue-79422.rs index 7f0ac348358..47ef38ff45d 100644 --- a/src/test/ui/generic-associated-types/issue-79422.rs +++ b/src/test/ui/generic-associated-types/issue-79422.rs @@ -17,12 +17,12 @@ fn t(&'a self) -> &'a T { } trait MapLike { - type VRefCont<'a>: RefCont<'a, V>; + type VRefCont<'a>: RefCont<'a, V> where Self: 'a; fn get<'a>(&'a self, key: &K) -> Option>; } impl MapLike for std::collections::BTreeMap { - type VRefCont<'a> = &'a V; + type VRefCont<'a> where Self: 'a = &'a V; fn get<'a>(&'a self, key: &K) -> Option<&'a V> { std::collections::BTreeMap::get(self, key) } diff --git a/src/test/ui/generic-associated-types/issue-79422.stderr b/src/test/ui/generic-associated-types/issue-79422.stderr index b6f856a97e7..8b6f9b866e5 100644 --- a/src/test/ui/generic-associated-types/issue-79422.stderr +++ b/src/test/ui/generic-associated-types/issue-79422.stderr @@ -7,7 +7,7 @@ LL | as Box>>; note: associated type defined here, with 1 lifetime parameter: `'a` --> $DIR/issue-79422.rs:20:10 | -LL | type VRefCont<'a>: RefCont<'a, V>; +LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ -- help: add missing lifetime argument | @@ -25,7 +25,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | trait MapLike { | ------- this trait cannot be made into an object... -LL | type VRefCont<'a>: RefCont<'a, V>; +LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait @@ -40,7 +40,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | trait MapLike { | ------- this trait cannot be made into an object... -LL | type VRefCont<'a>: RefCont<'a, V>; +LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait = note: required because of the requirements on the impl of `CoerceUnsized + 'static)>>>` for `Box>` diff --git a/src/test/ui/generic-associated-types/issue-86787.rs b/src/test/ui/generic-associated-types/issue-86787.rs index f1f05ea6627..0f62f83e256 100644 --- a/src/test/ui/generic-associated-types/issue-86787.rs +++ b/src/test/ui/generic-associated-types/issue-86787.rs @@ -9,6 +9,7 @@ enum Either { pub trait HasChildrenOf { type T; type TRef<'a>; + //~^ Missing required bounds fn ref_children<'a>(&'a self) -> Vec>; fn take_children(self) -> Vec; @@ -20,9 +21,9 @@ impl HasChildrenOf for Either Right: HasChildrenOf, { type T = Either; + // We used to error below because the where clause doesn't match the trait. + // Now, we error early on the trait itself. type TRef<'a> - //~^ `impl` associated type signature - //~^^ `impl` associated type signature where ::T: 'a, ::T: 'a diff --git a/src/test/ui/generic-associated-types/issue-86787.stderr b/src/test/ui/generic-associated-types/issue-86787.stderr index 648eff77d73..87dcd875de7 100644 --- a/src/test/ui/generic-associated-types/issue-86787.stderr +++ b/src/test/ui/generic-associated-types/issue-86787.stderr @@ -1,32 +1,10 @@ -error: `impl` associated type signature for `TRef` doesn't match `trait` associated type signature - --> $DIR/issue-86787.rs:23:5 +error: Missing required bounds on TRef + --> $DIR/issue-86787.rs:11:5 | -LL | type TRef<'a>; - | -------------- expected -... -LL | / type TRef<'a> -LL | | -LL | | -LL | | where -LL | | ::T: 'a, -LL | | ::T: 'a -LL | | = Either<&'a Left::T, &'a Right::T>; - | |________________________________________^ found +LL | type TRef<'a>; + | ^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'a` -error: `impl` associated type signature for `TRef` doesn't match `trait` associated type signature - --> $DIR/issue-86787.rs:23:5 - | -LL | type TRef<'a>; - | -------------- expected -... -LL | / type TRef<'a> -LL | | -LL | | -LL | | where -LL | | ::T: 'a, -LL | | ::T: 'a -LL | | = Either<&'a Left::T, &'a Right::T>; - | |________________________________________^ found - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/issue-88287.rs b/src/test/ui/generic-associated-types/issue-88287.rs index 2e65af594a6..df5586ed422 100644 --- a/src/test/ui/generic-associated-types/issue-88287.rs +++ b/src/test/ui/generic-associated-types/issue-88287.rs @@ -13,7 +13,8 @@ trait SearchableResource { trait SearchableResourceExt: SearchableResource { type Future<'f, A: 'f + ?Sized, B: 'f>: Future, ()>> + 'f where - A: SearchableResource; + A: SearchableResource, + Self: 'f; fn search<'c>(&'c self, client: &'c ()) -> Self::Future<'c, Self, Criteria>; } @@ -29,6 +30,7 @@ impl SearchableResourceExt for T type Future<'f, A, B: 'f> where A: SearchableResource + ?Sized + 'f, + Self: 'f, = SearchFutureTy<'f, A, B>; fn search<'c>(&'c self, _client: &'c ()) -> Self::Future<'c, Self, Criteria> { diff --git a/src/test/ui/generic-associated-types/issue-88360.rs b/src/test/ui/generic-associated-types/issue-88360.rs index 06af3f5ec96..8ee98201aba 100644 --- a/src/test/ui/generic-associated-types/issue-88360.rs +++ b/src/test/ui/generic-associated-types/issue-88360.rs @@ -1,13 +1,14 @@ #![feature(generic_associated_types)] trait GatTrait { - type Gat<'a>; + type Gat<'a> where Self: 'a; fn test(&self) -> Self::Gat<'_>; } trait SuperTrait where + Self: 'static, for<'a> Self: GatTrait = &'a T>, { fn copy(&self) -> Self::Gat<'_> where T: Copy { diff --git a/src/test/ui/generic-associated-types/issue-88360.stderr b/src/test/ui/generic-associated-types/issue-88360.stderr index cfbf3aaa4e6..5f769d799fa 100644 --- a/src/test/ui/generic-associated-types/issue-88360.stderr +++ b/src/test/ui/generic-associated-types/issue-88360.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-88360.rs:14:9 + --> $DIR/issue-88360.rs:15:9 | LL | trait SuperTrait | - this type parameter diff --git a/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs b/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs index b976ee3261f..bcbcfc18996 100644 --- a/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs +++ b/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs @@ -1,7 +1,7 @@ #![feature(generic_associated_types)] pub trait X { - type Y<'a>; + type Y<'a> where Self: 'a; fn m(&self) -> Self::Y<'_>; } diff --git a/src/test/ui/generic-associated-types/self-outlives-lint.rs b/src/test/ui/generic-associated-types/self-outlives-lint.rs new file mode 100644 index 00000000000..af90d158855 --- /dev/null +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -0,0 +1,173 @@ +#![feature(generic_associated_types)] + +// check-fail + +use std::fmt::Debug; + +// We have a `&'a self`, so we need a `Self: 'a` +trait Iterable { + type Item<'x>; + //~^ Missing required bounds + fn iter<'a>(&'a self) -> Self::Item<'a>; +} + +/* +impl Iterable for T { + type Item<'a> = &'a T; + fn iter<'a>(&'a self) -> Self::Item<'a> { + self + } +} +*/ + +// We have a `&'a T`, so we need a `T: 'x` +trait Deserializer { + type Out<'x>; + //~^ Missing required bounds + fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>; +} + +/* +impl Deserializer for () { + type Out<'a> = &'a T; + fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a> { input } +} +*/ + +// 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 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 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>; +} + +struct Wrap(T); + +// We pass `Wrap` and we see `&'z Wrap`, so we require `D: 'x` +trait Des { + type Out<'x, D>; + //~^ Missing required bounds + fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, Wrap>; +} +/* +impl Des for () { + 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 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.0 + } +} +*/ + +// We see `&'z T`, so we require `D: 'x` +trait Des3 { + type Out<'x, D>; + //~^ Missing required bounds + fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>; +} +/* +impl Des3 for () { + type Out<'x, D> = &'x D; + fn des<'a, T>(&self, data: &'a T) -> Self::Out<'a, T> { + data + } +} +*/ + +// 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` +// 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 + fn method(&'a self) -> Self::Bar<'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 + 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 new file mode 100644 index 00000000000..bf85780f69f --- /dev/null +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -0,0 +1,98 @@ +error: Missing required bounds on Item + --> $DIR/self-outlives-lint.rs:9:5 + | +LL | type Item<'x>; + | ^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'x` + +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:25:5 + | +LL | type Out<'x>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where T: 'x` + +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:39:5 + | +LL | type Out<'x>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where T: 'x` + +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:46:5 + | +LL | type Out<'x, 'y>; + | ^^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where T: 'x, U: 'y` + +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:61:5 + | +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:77:5 + | +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:92:5 + | +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:114:5 + | +LL | type Bar<'b>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'a, Self: 'b` + +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:122:5 + | +LL | type Bar<'b>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'a, Self: 'b` + +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:129: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:143:5 + | +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:150:5 + | +LL | type Bar<'a, 'b>; + | ^^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where 'a: 'b` + +error: aborting due to 12 previous errors + diff --git a/src/test/ui/generic-associated-types/streaming_iterator.rs b/src/test/ui/generic-associated-types/streaming_iterator.rs index 2feff9f4c6f..f83d4d7b68e 100644 --- a/src/test/ui/generic-associated-types/streaming_iterator.rs +++ b/src/test/ui/generic-associated-types/streaming_iterator.rs @@ -5,12 +5,12 @@ use std::fmt::Display; trait StreamingIterator { - type Item<'a>; + type Item<'a> where Self: 'a; // Applying the lifetime parameter `'a` to `Self::Item` inside the trait. fn next<'a>(&'a mut self) -> Option>; } -struct Foo { +struct Foo { // Applying a concrete lifetime to the constructor outside the trait. bar: ::Item<'static>, } @@ -30,7 +30,7 @@ struct StreamEnumerate { } impl StreamingIterator for StreamEnumerate { - type Item<'a> = (usize, I::Item<'a>); + type Item<'a> where Self: 'a = (usize, I::Item<'a>); fn next<'a>(&'a mut self) -> Option> { match self.iter.next() { None => None, @@ -44,7 +44,7 @@ fn next<'a>(&'a mut self) -> Option> { } impl StreamingIterator for I { - type Item<'a> = ::Item; + type Item<'a> where Self: 'a = ::Item; fn next(&mut self) -> Option<::Item<'_>> { Iterator::next(self) } diff --git a/src/test/ui/generic-associated-types/variance_constraints.rs b/src/test/ui/generic-associated-types/variance_constraints.rs index 7bc250ee87b..7d0f7638ac8 100644 --- a/src/test/ui/generic-associated-types/variance_constraints.rs +++ b/src/test/ui/generic-associated-types/variance_constraints.rs @@ -3,7 +3,7 @@ #![feature(generic_associated_types)] trait A { - type B<'a>; + type B<'a> where Self: 'a; fn make_b<'a>(&'a self) -> Self::B<'a>; }