/// 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<Self::Item<'a>>;
+/// }
+/// ```
fn check_gat_where_clauses(
tcx: TyCtxt<'_>,
trait_item: &hir::TraitItem<'_>,
return;
}
let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id);
- let mut clauses = FxHashSet::default();
+ 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 mut visitor = GATSubstCollector {
tcx,
gat: trait_item.def_id.to_def_id(),
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);
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(
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 {
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()
}
}
+/// 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,
// check-fail
+use std::fmt::Debug;
+
// We have a `&'a self`, so we need a `Self: 'a`
trait Iterable {
type Item<'x>;
}
*/
+// 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<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() {}
error: Missing required bounds on Item
- --> $DIR/self-outlives-lint.rs:7:5
+ --> $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:23:5
+ --> $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:37:5
+ --> $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:44:5
+ --> $DIR/self-outlives-lint.rs:46:5
|
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>;
| ^^^^^^^^^^^^^^^-
| 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>;
| ^^^^^^^^^^^^^^^-
| 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<Item = Self::Item<'a>>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+ | |
+ | help: add the required where clauses: `where Self: 'a`
+
+error: aborting due to 11 previous errors