Skip to content

GATs outlives lint: Try to prove bounds #91849

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 13, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_infer/src/infer/free_regions.rs
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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>,
54 changes: 46 additions & 8 deletions compiler/rustc_typeck/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
@@ -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);
Comment on lines +581 to +582
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is separate from the above logic; bug fix for #91036


let errors = infcx.resolve_regions(
id.expect_owner().to_def_id(),
2 changes: 1 addition & 1 deletion src/test/ui/generic-associated-types/issue-86787.rs
Original file line number Diff line number Diff line change
@@ -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>;
7 changes: 5 additions & 2 deletions src/test/ui/generic-associated-types/issue-86787.stderr
Original file line number Diff line number Diff line change
@@ -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

45 changes: 32 additions & 13 deletions src/test/ui/generic-associated-types/self-outlives-lint.rs
Original file line number Diff line number Diff line change
@@ -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,22 +112,22 @@ 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>;
}

// 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
//~^ 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() {}
95 changes: 71 additions & 24 deletions src/test/ui/generic-associated-types/self-outlives-lint.stderr
Original file line number Diff line number Diff line change
@@ -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: 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 12 previous errors
error: aborting due to 13 previous errors