Skip to content

Commit

Permalink
Rollup merge of rust-lang#120751 - estebank:issue-68982, r=nnethercote
Browse files Browse the repository at this point in the history
Provide more suggestions on invalid equality where bounds

```
error: equality constraints are not yet supported in `where` clauses
  --> $DIR/equality-bound.rs:50:9
   |
LL |         IntoIterator::Item = A
   |         ^^^^^^^^^^^^^^^^^^^^^^ not supported
   |
   = note: see issue rust-lang#20041 <rust-lang#20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
   |
LL ~     fn from_iter<T: IntoIterator<Item = A>>(_: T) -> Self
LL ~
   |

error: equality constraints are not yet supported in `where` clauses
  --> $DIR/equality-bound.rs:63:9
   |
LL |         T::Item = A
   |         ^^^^^^^^^^^ not supported
   |
   = note: see issue rust-lang#20041 <rust-lang#20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
   |
LL ~     fn from_iter<T: IntoIterator<Item = A>>(_: T) -> Self
LL ~
   |
```

Fix rust-lang#68982.
  • Loading branch information
matthiaskrgr authored Feb 13, 2024
2 parents 020e846 + 535c643 commit 1b39691
Show file tree
Hide file tree
Showing 3 changed files with 293 additions and 39 deletions.
124 changes: 89 additions & 35 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1593,44 +1593,98 @@ fn deny_equality_constraints(
}
}
}
// Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
if let [potential_param, potential_assoc] = &full_path.segments[..] {
for param in &generics.params {
if param.ident == potential_param.ident {
for bound in &param.bounds {
if let ast::GenericBound::Trait(trait_ref, TraitBoundModifiers::NONE) =
bound

let mut suggest =
|poly: &PolyTraitRef, potential_assoc: &PathSegment, predicate: &WhereEqPredicate| {
if let [trait_segment] = &poly.trait_ref.path.segments[..] {
let assoc = pprust::path_to_string(&ast::Path::from_ident(potential_assoc.ident));
let ty = pprust::ty_to_string(&predicate.rhs_ty);
let (args, span) = match &trait_segment.args {
Some(args) => match args.deref() {
ast::GenericArgs::AngleBracketed(args) => {
let Some(arg) = args.args.last() else {
return;
};
(format!(", {assoc} = {ty}"), arg.span().shrink_to_hi())
}
_ => return,
},
None => (format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi()),
};
let removal_span = if generics.where_clause.predicates.len() == 1 {
// We're removing th eonly where bound left, remove the whole thing.
generics.where_clause.span
} else {
let mut span = predicate.span;
let mut prev: Option<Span> = None;
let mut preds = generics.where_clause.predicates.iter().peekable();
// Find the predicate that shouldn't have been in the where bound list.
while let Some(pred) = preds.next() {
if let WherePredicate::EqPredicate(pred) = pred
&& pred.span == predicate.span
{
if let [trait_segment] = &trait_ref.trait_ref.path.segments[..] {
let assoc = pprust::path_to_string(&ast::Path::from_ident(
potential_assoc.ident,
));
let ty = pprust::ty_to_string(&predicate.rhs_ty);
let (args, span) = match &trait_segment.args {
Some(args) => match args.deref() {
ast::GenericArgs::AngleBracketed(args) => {
let Some(arg) = args.args.last() else {
continue;
};
(format!(", {assoc} = {ty}"), arg.span().shrink_to_hi())
}
_ => continue,
},
None => (
format!("<{assoc} = {ty}>"),
trait_segment.span().shrink_to_hi(),
),
};
err.assoc2 = Some(errors::AssociatedSuggestion2 {
span,
args,
predicate: predicate.span,
trait_segment: trait_segment.ident,
potential_assoc: potential_assoc.ident,
});
if let Some(next) = preds.peek() {
// This is the first predicate, remove the trailing comma as well.
span = span.with_hi(next.span().lo());
} else if let Some(prev) = prev {
// Remove the previous comma as well.
span = span.with_lo(prev.hi());
}
}
prev = Some(pred.span());
}
span
};
err.assoc2 = Some(errors::AssociatedSuggestion2 {
span,
args,
predicate: removal_span,
trait_segment: trait_segment.ident,
potential_assoc: potential_assoc.ident,
});
}
};

if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
// Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
for bounds in generics.params.iter().map(|p| &p.bounds).chain(
generics.where_clause.predicates.iter().filter_map(|pred| match pred {
WherePredicate::BoundPredicate(p) => Some(&p.bounds),
_ => None,
}),
) {
for bound in bounds {
if let GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
if full_path.segments[..full_path.segments.len() - 1]
.iter()
.map(|segment| segment.ident.name)
.zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name))
.all(|(a, b)| a == b)
&& let Some(potential_assoc) = full_path.segments.iter().last()
{
suggest(poly, potential_assoc, predicate);
}
}
}
}
// Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
if let [potential_param, potential_assoc] = &full_path.segments[..] {
for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain(
generics.where_clause.predicates.iter().filter_map(|pred| match pred {
WherePredicate::BoundPredicate(p)
if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind
&& let [segment] = &path.segments[..] =>
{
Some((segment.ident, &p.bounds))
}
_ => None,
}),
) {
if ident == potential_param.ident {
for bound in bounds {
if let ast::GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
suggest(poly, potential_assoc, predicate);
}
}
}
}
Expand Down
67 changes: 67 additions & 0 deletions tests/ui/generic-associated-types/equality-bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,71 @@ fn sum3<J: Iterator>(i: J) -> i32 where I::Item = i32 {
panic!()
}

use std::iter::FromIterator;

struct X {}

impl FromIterator<bool> for X {
fn from_iter<T>(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A,
//~^ ERROR equality constraints are not yet supported in `where` clauses
//~| ERROR cannot find type `A` in this scope
{
todo!()
}
}

struct Y {}

impl FromIterator<bool> for Y {
fn from_iter<T>(_: T) -> Self where T: IntoIterator, T::Item = A,
//~^ ERROR equality constraints are not yet supported in `where` clauses
//~| ERROR cannot find type `A` in this scope
{
todo!()
}
}

struct Z {}

impl FromIterator<bool> for Z {
fn from_iter<T: IntoIterator>(_: T) -> Self where IntoIterator::Item = A,
//~^ ERROR equality constraints are not yet supported in `where` clauses
//~| ERROR cannot find type `A` in this scope
{
todo!()
}
}

struct K {}

impl FromIterator<bool> for K {
fn from_iter<T: IntoIterator>(_: T) -> Self where T::Item = A,
//~^ ERROR equality constraints are not yet supported in `where` clauses
//~| ERROR cannot find type `A` in this scope
{
todo!()
}
}

struct L {}

impl FromIterator<bool> for L {
fn from_iter<T>(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator,
//~^ ERROR equality constraints are not yet supported in `where` clauses
//~| ERROR cannot find type `A` in this scope
{
todo!()
}
}

struct M {}

impl FromIterator<bool> for M {
fn from_iter<T>(_: T) -> Self where T::Item = A, T: IntoIterator,
//~^ ERROR equality constraints are not yet supported in `where` clauses
//~| ERROR cannot find type `A` in this scope
{
todo!()
}
}
fn main() {}
141 changes: 137 additions & 4 deletions tests/ui/generic-associated-types/equality-bound.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ LL | fn sum<I: Iterator<Item = ()>>(i: I) -> i32 where I::Item = i32 {
help: if `Iterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn sum<I: Iterator<Item = ()>>(i: I) -> i32 where I::Item = i32 {
LL + fn sum<I: Iterator<Item = (), Item = i32>>(i: I) -> i32 where {
LL + fn sum<I: Iterator<Item = (), Item = i32>>(i: I) -> i32 {
|

error: equality constraints are not yet supported in `where` clauses
Expand All @@ -21,7 +21,7 @@ LL | fn sum2<I: Iterator>(i: I) -> i32 where I::Item = i32 {
help: if `Iterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn sum2<I: Iterator>(i: I) -> i32 where I::Item = i32 {
LL + fn sum2<I: Iterator<Item = i32>>(i: I) -> i32 where {
LL + fn sum2<I: Iterator<Item = i32>>(i: I) -> i32 {
|

error: equality constraints are not yet supported in `where` clauses
Expand All @@ -32,6 +32,138 @@ LL | fn sum3<J: Iterator>(i: J) -> i32 where I::Item = i32 {
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information

error: equality constraints are not yet supported in `where` clauses
--> $DIR/equality-bound.rs:20:58
|
LL | fn from_iter<T>(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A,
| ^^^^^^^^^^^^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn from_iter<T>(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A,
LL + fn from_iter<T>(_: T) -> Self where T: IntoIterator<Item = A>,
|

error: equality constraints are not yet supported in `where` clauses
--> $DIR/equality-bound.rs:31:58
|
LL | fn from_iter<T>(_: T) -> Self where T: IntoIterator, T::Item = A,
| ^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn from_iter<T>(_: T) -> Self where T: IntoIterator, T::Item = A,
LL + fn from_iter<T>(_: T) -> Self where T: IntoIterator<Item = A>,
|

error: equality constraints are not yet supported in `where` clauses
--> $DIR/equality-bound.rs:42:55
|
LL | fn from_iter<T: IntoIterator>(_: T) -> Self where IntoIterator::Item = A,
| ^^^^^^^^^^^^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn from_iter<T: IntoIterator>(_: T) -> Self where IntoIterator::Item = A,
LL + fn from_iter<T: IntoIterator<Item = A>>(_: T) -> Self
|

error: equality constraints are not yet supported in `where` clauses
--> $DIR/equality-bound.rs:53:55
|
LL | fn from_iter<T: IntoIterator>(_: T) -> Self where T::Item = A,
| ^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn from_iter<T: IntoIterator>(_: T) -> Self where T::Item = A,
LL + fn from_iter<T: IntoIterator<Item = A>>(_: T) -> Self
|

error: equality constraints are not yet supported in `where` clauses
--> $DIR/equality-bound.rs:64:41
|
LL | fn from_iter<T>(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator,
| ^^^^^^^^^^^^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn from_iter<T>(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator,
LL + fn from_iter<T>(_: T) -> Self where T: IntoIterator<Item = A>,
|

error: equality constraints are not yet supported in `where` clauses
--> $DIR/equality-bound.rs:75:41
|
LL | fn from_iter<T>(_: T) -> Self where T::Item = A, T: IntoIterator,
| ^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax
|
LL - fn from_iter<T>(_: T) -> Self where T::Item = A, T: IntoIterator,
LL + fn from_iter<T>(_: T) -> Self where T: IntoIterator<Item = A>,
|

error[E0412]: cannot find type `A` in this scope
--> $DIR/equality-bound.rs:20:79
|
LL | fn from_iter<T>(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A,
| ^ help: a struct with a similar name exists: `K`
...
LL | struct K {}
| -------- similarly named struct `K` defined here

error[E0412]: cannot find type `A` in this scope
--> $DIR/equality-bound.rs:31:68
|
LL | fn from_iter<T>(_: T) -> Self where T: IntoIterator, T::Item = A,
| ^ help: a struct with a similar name exists: `K`
...
LL | struct K {}
| -------- similarly named struct `K` defined here

error[E0412]: cannot find type `A` in this scope
--> $DIR/equality-bound.rs:42:76
|
LL | fn from_iter<T: IntoIterator>(_: T) -> Self where IntoIterator::Item = A,
| ^ help: a struct with a similar name exists: `K`
...
LL | struct K {}
| -------- similarly named struct `K` defined here

error[E0412]: cannot find type `A` in this scope
--> $DIR/equality-bound.rs:53:65
|
LL | struct K {}
| -------- similarly named struct `K` defined here
...
LL | fn from_iter<T: IntoIterator>(_: T) -> Self where T::Item = A,
| ^ help: a struct with a similar name exists: `K`

error[E0412]: cannot find type `A` in this scope
--> $DIR/equality-bound.rs:64:62
|
LL | struct K {}
| -------- similarly named struct `K` defined here
...
LL | fn from_iter<T>(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator,
| ^ help: a struct with a similar name exists: `K`

error[E0412]: cannot find type `A` in this scope
--> $DIR/equality-bound.rs:75:51
|
LL | struct K {}
| -------- similarly named struct `K` defined here
...
LL | fn from_iter<T>(_: T) -> Self where T::Item = A, T: IntoIterator,
| ^ help: a struct with a similar name exists: `K`

error[E0433]: failed to resolve: use of undeclared type `I`
--> $DIR/equality-bound.rs:9:41
|
Expand All @@ -41,6 +173,7 @@ LL | fn sum3<J: Iterator>(i: J) -> i32 where I::Item = i32 {
| use of undeclared type `I`
| help: a type parameter with a similar name exists: `J`

error: aborting due to 4 previous errors
error: aborting due to 16 previous errors

For more information about this error, try `rustc --explain E0433`.
Some errors have detailed explanations: E0412, E0433.
For more information about an error, try `rustc --explain E0412`.

0 comments on commit 1b39691

Please sign in to comment.