Rollup merge of #91849 - jackh726:gats-outlives-lint-part2, r=nikomatsakis

GATs outlives lint: Try to prove bounds

Fixes #91036
Fixes #90888
Fixes #91348 (better error + documentation to be added to linked issue)

Instead of checking for bounds directly, try to prove them in the associated type environment.

Also, add a bit of extra information to the error, including a link to the relevant discussion issue (#87479). That should be edited to include a brief summary of the current state of the outlives lint, including a brief background. It also might or might not be worth it to bump this to a full error code at some point.

r? ``@nikomatsakis``
This commit is contained in:
Matthias Krüger 2021-12-13 18:15:16 +01:00 committed by GitHub
commit 84878336b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 157 additions and 50 deletions

View File

@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Lift, Region, TyCtxt};
///
/// This stuff is a bit convoluted and should be refactored, but as we
/// transition to NLL, it'll all go away anyhow.
pub struct RegionRelations<'a, 'tcx> {
pub(crate) struct RegionRelations<'a, 'tcx> {
pub tcx: TyCtxt<'tcx>,
/// The context used for debug messages

View File

@ -28,7 +28,7 @@ use std::fmt;
/// assuming such values can be found. It returns the final values of
/// all the variables as well as a set of errors that must be reported.
#[instrument(level = "debug", skip(region_rels, var_infos, data))]
pub fn resolve<'tcx>(
pub(crate) fn resolve<'tcx>(
region_rels: &RegionRelations<'_, 'tcx>,
var_infos: VarInfos,
data: RegionConstraintData<'tcx>,

View File

@ -426,22 +426,48 @@ fn check_gat_where_clauses(
}
}
// If there are any missing clauses, emit an error
let mut clauses = clauses.unwrap_or_default();
// If there are any clauses that aren't provable, emit an error
let 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 param_env = tcx.param_env(trait_item.def_id);
let mut clauses: Vec<_> = clauses
.drain_filter(|clause| !written_predicates.predicates.iter().any(|p| &p.0 == clause))
.into_iter()
.filter(|clause| match clause.kind().skip_binder() {
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => {
!region_known_to_outlive(
tcx,
trait_item.hir_id(),
param_env,
&FxHashSet::default(),
a,
b,
)
}
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
!ty_known_to_outlive(
tcx,
trait_item.hir_id(),
param_env,
&FxHashSet::default(),
a,
b,
)
}
_ => bug!("Unexpected PredicateKind"),
})
.map(|clause| format!("{}", clause))
.collect();
// We sort so that order is predictable
clauses.sort();
if !clauses.is_empty() {
let plural = if clauses.len() > 1 { "s" } else { "" };
let mut err = tcx.sess.struct_span_err(
trait_item.span,
&format!("Missing required bounds on {}", trait_item.ident),
&format!("missing required bound{} on `{}`", plural, trait_item.ident),
);
let suggestion = format!(
@ -455,11 +481,22 @@ fn check_gat_where_clauses(
);
err.span_suggestion(
trait_item.generics.where_clause.tail_span_for_suggestion(),
"add the required where clauses",
&format!("add the required where clause{}", plural),
suggestion,
Applicability::MachineApplicable,
);
let bound = if clauses.len() > 1 { "these bounds are" } else { "this bound is" };
err.note(&format!(
"{} currently required to ensure that impls have maximum flexibility",
bound
));
err.note(
"we are soliciting feedback, see issue #87479 \
<https://github.com/rust-lang/rust/issues/87479> \
for more information",
);
err.emit()
}
}
@ -541,7 +578,8 @@ fn region_known_to_outlive<'tcx>(
});
use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
(&infcx).push_sub_region_constraint(origin, region_a, region_b);
// `region_a: region_b` -> `region_b <= region_a`
(&infcx).push_sub_region_constraint(origin, region_b, region_a);
let errors = infcx.resolve_regions(
id.expect_owner().to_def_id(),

View File

@ -9,7 +9,7 @@ enum Either<L, R> {
pub trait HasChildrenOf {
type T;
type TRef<'a>;
//~^ Missing required bounds
//~^ missing required
fn ref_children<'a>(&'a self) -> Vec<Self::TRef<'a>>;
fn take_children(self) -> Vec<Self::T>;

View File

@ -1,10 +1,13 @@
error: Missing required bounds on TRef
error: missing required bound on `TRef`
--> $DIR/issue-86787.rs:11:5
|
LL | type TRef<'a>;
| ^^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where Self: 'a`
| help: add the required where clause: `where Self: 'a`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: aborting due to previous error

View File

@ -7,7 +7,7 @@ use std::fmt::Debug;
// We have a `&'a self`, so we need a `Self: 'a`
trait Iterable {
type Item<'x>;
//~^ Missing required bounds
//~^ missing required
fn iter<'a>(&'a self) -> Self::Item<'a>;
}
@ -23,7 +23,7 @@ impl<T> Iterable for T {
// We have a `&'a T`, so we need a `T: 'x`
trait Deserializer<T> {
type Out<'x>;
//~^ Missing required bounds
//~^ missing required
fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>;
}
@ -37,14 +37,14 @@ impl<T> Deserializer<T> 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<T> {
type Out<'x>;
//~^ Missing required bounds
//~^ missing required
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
//~^ missing required
fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>;
}
@ -59,7 +59,7 @@ 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
//~^ missing required
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, Wrap<T>>;
}
/*
@ -75,7 +75,7 @@ impl Des for () {
// implied bound that `T: 'z`, so we require `D: 'x`
trait Des2 {
type Out<'x, D>;
//~^ Missing required bounds
//~^ missing required
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, T>;
}
/*
@ -90,7 +90,7 @@ impl Des2 for () {
// We see `&'z T`, so we require `D: 'x`
trait Des3 {
type Out<'x, D>;
//~^ Missing required bounds
//~^ missing required
fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>;
}
/*
@ -112,7 +112,7 @@ trait NoGat<'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
//~^ missing required
fn method(&'a self) -> Self::Bar<'a>;
}
@ -120,14 +120,14 @@ trait TraitLifetime<'a> {
// 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
//~^ missing required
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
//~^ missing required
fn method<'b>(&self, token: &'b ()) -> Self::Bar<'b> where Self: 'b;
}
@ -141,14 +141,15 @@ trait NotInReturn {
trait IterableTwo {
type Item<'a>;
type Iterator<'a>: Iterator<Item = Self::Item<'a>>;
//~^ Missing required bounds
//~^ missing required
fn iter<'a>(&'a self) -> Self::Iterator<'a>;
}
// We also should report region outlives clauses
// We also should report region outlives clauses. Here, we know that `'y: 'x`,
// because of `&'x &'y`, so we require that `'b: 'a`.
trait RegionOutlives {
type Bar<'a, 'b>;
//~^ Missing required bounds
//~^ missing required
fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y>;
}
@ -161,6 +162,17 @@ impl Foo for () {
}
*/
// Similar to the above, except with explicit bounds
trait ExplicitRegionOutlives<'ctx> {
type Fut<'out>;
//~^ missing required
fn test<'out>(ctx: &'ctx i32) -> Self::Fut<'out>
where
'ctx: 'out;
}
// If there are multiple methods that return the GAT, require a set of clauses
// that can be satisfied by *all* methods
trait MultipleMethods {
@ -170,4 +182,11 @@ trait MultipleMethods {
fn gimme_default(&self) -> Self::Bar<'static>;
}
// We would normally require `Self: 'a`, but we can prove that `Self: 'static`
// because of the the bounds on the trait, so the bound is proven
trait Trait: 'static {
type Assoc<'a>;
fn make_assoc(_: &u32) -> Self::Assoc<'_>;
}
fn main() {}

View File

@ -1,98 +1,145 @@
error: Missing required bounds on Item
error: missing required bound on `Item`
--> $DIR/self-outlives-lint.rs:9:5
|
LL | type Item<'x>;
| ^^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where Self: 'x`
| help: add the required where clause: `where Self: 'x`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Out
error: missing required bound on `Out`
--> $DIR/self-outlives-lint.rs:25:5
|
LL | type Out<'x>;
| ^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where T: 'x`
| help: add the required where clause: `where T: 'x`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Out
error: missing required bound on `Out`
--> $DIR/self-outlives-lint.rs:39:5
|
LL | type Out<'x>;
| ^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where T: 'x`
| help: add the required where clause: `where T: 'x`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Out
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`
|
= note: these bounds are currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Out
error: missing required bound on `Out`
--> $DIR/self-outlives-lint.rs:61:5
|
LL | type Out<'x, D>;
| ^^^^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where D: 'x`
| help: add the required where clause: `where D: 'x`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Out
error: missing required bound on `Out`
--> $DIR/self-outlives-lint.rs:77:5
|
LL | type Out<'x, D>;
| ^^^^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where D: 'x`
| help: add the required where clause: `where D: 'x`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Out
error: missing required bound on `Out`
--> $DIR/self-outlives-lint.rs:92:5
|
LL | type Out<'x, D>;
| ^^^^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where D: 'x`
| help: add the required where clause: `where D: 'x`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Bar
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`
|
= note: these bounds are currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Bar
error: missing required bound on `Bar`
--> $DIR/self-outlives-lint.rs:122:5
|
LL | type Bar<'b>;
| ^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where Self: 'a, Self: 'b`
| help: add the required where clause: `where Self: 'b`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Bar
error: missing required bound on `Bar`
--> $DIR/self-outlives-lint.rs:129:5
|
LL | type Bar<'b>;
| ^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where Self: 'b`
| help: add the required where clause: `where Self: 'b`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Iterator
error: missing required bound 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`
| help: add the required where clause: `where Self: 'a`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: Missing required bounds on Bar
--> $DIR/self-outlives-lint.rs:150:5
error: missing required bound on `Bar`
--> $DIR/self-outlives-lint.rs:151:5
|
LL | type Bar<'a, 'b>;
| ^^^^^^^^^^^^^^^^-
| |
| help: add the required where clauses: `where 'a: 'b`
| help: add the required where clause: `where 'b: 'a`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: aborting due to 12 previous errors
error: missing required bound on `Fut`
--> $DIR/self-outlives-lint.rs:167:5
|
LL | type Fut<'out>;
| ^^^^^^^^^^^^^^-
| |
| help: add the required where clause: `where 'ctx: 'out`
|
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: aborting due to 13 previous errors