Auto merge of #89970 - jackh726:gats_diagnostics, r=nikomatsakis

Implementation of GATs outlives lint

See #87479 for background. Closes #87479

The basic premise of this lint/error is to require the user to write where clauses on a GAT when those bounds can be implied or proven from any function on the trait returning that GAT.

## Intuitive Explanation (Attempt) ##
Let's take this trait definition as an example:
```rust
trait Iterable {
    type Item<'x>;
    fn iter<'a>(&'a self) -> Self::Item<'a>;
}
```
Let's focus on the `iter` function. The first thing to realize is that we know that `Self: 'a` because of `&'a self`. If an impl wants `Self::Item` to contain any data with references, then those references must be derived from `&'a self`. Thus, they must live only as long as `'a`. Furthermore, because of the `Self: 'a` implied bound, they must live only as long as `Self`. Since it's `'a` is used in place of `'x`, it is reasonable to assume that any value of `Self::Item<'x>`, and thus `'x`, will only be able to live as long as `Self`. Therefore, we require this bound on `Item` in the trait.

As another example:
```rust
trait Deserializer<T> {
    type Out<'x>;
    fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>;
}
```
The intuition is similar here, except rather than a `Self: 'a` implied bound, we have a `T: 'a` implied bound. Thus, the data on `Self::Out<'a>` is derived from `&'a T`, and thus it is reasonable to expect that the lifetime `'x` will always be less than `T`.

## Implementation Algorithm ##
* Given a GAT `<P0 as Trait<P1..Pi>>::G<Pi...Pn>` declared as `trait T<A1..Ai> for A0 { type G<Ai...An>; }` used in return type of one associated function `F`
* Given env `E` (including implied bounds) for `F`
* For each lifetime parameter `'a` in `P0...Pn`:
    * For each other type parameter `Pi != 'a` in `P0...Pn`: // FIXME: this include of lifetime parameters too
        * If `E => (P: 'a)`:
            * Require where clause `Ai: 'a`

## Follow-up questions ##
* What should we do when we don't pass params exactly?
For this example:
```rust
trait Des {
    type Out<'x, D>;
    fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, Wrap<T>>;
}
```
Should we be requiring a `D: 'x` clause? We pass `Wrap<T>` as `D` and `'z` as `'x`, and should be able to prove that `Wrap<T>: 'z`.

r? `@nikomatsakis`
This commit is contained in:
bors 2021-11-06 04:15:22 +00:00
commit 9d39f6ab7d
22 changed files with 694 additions and 61 deletions

View File

@ -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<RegionResolutionError<'tcx>> {
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

View File

@ -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>,

View File

@ -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<Self::Item<'a>>;
/// }
/// ```
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<FxHashSet<ty::Predicate<'_>>> = 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 `<Self as LendingIterator>::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 &regions {
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 &regions {
for (region_b, region_b_idx) in &regions {
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<'tcx>>,
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,
&region_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<Ty<'tcx>>,
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
/// `<P0 as Trait<P1..Pn>>::GAT<Pn..Pm>` 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<T: TypeFoldable<'tcx>>(
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<Self::BreakTy> {
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<TyCtxt<'tcx>> {
Some(self.tcx)
}
}
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {

View File

@ -70,6 +70,7 @@
#![feature(never_type)]
#![feature(slice_partition_dedup)]
#![feature(control_flow_enum)]
#![feature(hash_drain_filter)]
#![recursion_limit = "256"]
#[macro_use]

View File

@ -8,7 +8,7 @@
// check that we don't normalize with trait defaults.
trait Collection<T> {
type Iter<'iter>: Iterator<Item=&'iter T> where T: 'iter;
type Iter<'iter>: Iterator<Item=&'iter T> where T: 'iter, Self: 'iter;
type Family: CollectionFamily;
// Test associated type defaults with parameters
type Sibling<U>: Collection<U> =

View File

@ -8,7 +8,7 @@
// run-pass
trait Collection<T> {
type Iter<'iter>: Iterator<Item=&'iter T> where T: 'iter;
type Iter<'iter>: Iterator<Item=&'iter T> where T: 'iter, Self: 'iter;
type Family: CollectionFamily;
// Test associated type defaults with parameters
type Sibling<U>: Collection<U> =

View File

@ -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<'_>;
}

View File

@ -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<'_>;
}

View File

@ -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>;
}

View File

@ -7,7 +7,7 @@ LL | let sub: Box<dyn SuperTrait<SubType = SubStruct>> = 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<Box<dyn SuperTrait<SubType = SubStruct<'_>>>>` for `Box<SuperStruct>`

View File

@ -17,12 +17,12 @@ fn t(&'a self) -> &'a T {
}
trait MapLike<K, V> {
type VRefCont<'a>: RefCont<'a, V>;
type VRefCont<'a>: RefCont<'a, V> where Self: 'a;
fn get<'a>(&'a self, key: &K) -> Option<Self::VRefCont<'a>>;
}
impl<K: Ord, V: 'static> MapLike<K, V> for std::collections::BTreeMap<K, V> {
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)
}

View File

@ -7,7 +7,7 @@ LL | as Box<dyn MapLike<u8, u8, VRefCont = dyn RefCont<'_, u8>>>;
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<K, V> {
| ------- 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<K, V> {
| ------- 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<Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>>` for `Box<BTreeMap<u8, u8>>`

View File

@ -9,6 +9,7 @@ enum Either<L, R> {
pub trait HasChildrenOf {
type T;
type TRef<'a>;
//~^ Missing required bounds
fn ref_children<'a>(&'a self) -> Vec<Self::TRef<'a>>;
fn take_children(self) -> Vec<Self::T>;
@ -20,9 +21,9 @@ impl<Left, Right> HasChildrenOf for Either<Left, Right>
Right: HasChildrenOf,
{
type T = Either<Left::T, Right::T>;
// 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
<Left as HasChildrenOf>::T: 'a,
<Right as HasChildrenOf>::T: 'a

View File

@ -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 | | <Left as HasChildrenOf>::T: 'a,
LL | | <Right as HasChildrenOf>::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 | | <Left as HasChildrenOf>::T: 'a,
LL | | <Right as HasChildrenOf>::T: 'a
LL | | = Either<&'a Left::T, &'a Right::T>;
| |________________________________________^ found
error: aborting due to 2 previous errors
error: aborting due to previous error

View File

@ -13,7 +13,8 @@ trait SearchableResource<Criteria> {
trait SearchableResourceExt<Criteria>: SearchableResource<Criteria> {
type Future<'f, A: 'f + ?Sized, B: 'f>: Future<Output = Result<Vec<A::SearchResult>, ()>> + 'f
where
A: SearchableResource<B>;
A: SearchableResource<B>,
Self: 'f;
fn search<'c>(&'c self, client: &'c ()) -> Self::Future<'c, Self, Criteria>;
}
@ -29,6 +30,7 @@ impl<T, Criteria> SearchableResourceExt<Criteria> for T
type Future<'f, A, B: 'f>
where
A: SearchableResource<B> + ?Sized + 'f,
Self: 'f,
= SearchFutureTy<'f, A, B>;
fn search<'c>(&'c self, _client: &'c ()) -> Self::Future<'c, Self, Criteria> {

View File

@ -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<T>
where
Self: 'static,
for<'a> Self: GatTrait<Gat<'a> = &'a T>,
{
fn copy(&self) -> Self::Gat<'_> where T: Copy {

View File

@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/issue-88360.rs:14:9
--> $DIR/issue-88360.rs:15:9
|
LL | trait SuperTrait<T>
| - this type parameter

View File

@ -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<'_>;
}

View File

@ -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<T> 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<T> {
type Out<'x>;
//~^ Missing required bounds
fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>;
}
/*
impl<T> Deserializer<T> 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<T> {
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<T, U> {
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>(T);
// We pass `Wrap<T>` and we see `&'z Wrap<T>`, so we require `D: 'x`
trait Des {
type Out<'x, D>;
//~^ Missing required bounds
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, Wrap<T>>;
}
/*
impl Des for () {
type Out<'x, D> = &'x D; // Not okay
fn des<'a, T>(&self, data: &'a Wrap<T>) -> Self::Out<'a, Wrap<T>> {
data
}
}
*/
// We have `T` and `'z` as GAT substs. Because of `&'z Wrap<T>`, 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<T>) -> Self::Out<'z, T>;
}
/*
impl Des2 for () {
type Out<'x, D> = &'x D;
fn des<'a, T>(&self, data: &'a Wrap<T>) -> 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<Item = Self::Item<'a>>;
//~^ 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() {}

View File

@ -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<Item = Self::Item<'a>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
| |
| 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

View File

@ -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<Self::Item<'a>>;
}
struct Foo<T: StreamingIterator> {
struct Foo<T: StreamingIterator + 'static> {
// Applying a concrete lifetime to the constructor outside the trait.
bar: <T as StreamingIterator>::Item<'static>,
}
@ -30,7 +30,7 @@ struct StreamEnumerate<I> {
}
impl<I: StreamingIterator> StreamingIterator for StreamEnumerate<I> {
type Item<'a> = (usize, I::Item<'a>);
type Item<'a> where Self: 'a = (usize, I::Item<'a>);
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> {
match self.iter.next() {
None => None,
@ -44,7 +44,7 @@ fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> {
}
impl<I: Iterator> StreamingIterator for I {
type Item<'a> = <I as Iterator>::Item;
type Item<'a> where Self: 'a = <I as Iterator>::Item;
fn next(&mut self) -> Option<<I as StreamingIterator>::Item<'_>> {
Iterator::next(self)
}

View File

@ -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>;
}