Skip to content

Commit c2b5373

Browse files
committed
On incorrect equality constraint likely to be assoc type, suggest appropriate syntax
When encountering `where <A as Foo>::Bar = B`, it is possible that `Bar` is an associated type. If so, suggest `where A: Foo<Bar = B>`. CC #20041.
1 parent ddf322f commit c2b5373

File tree

4 files changed

+134
-12
lines changed

4 files changed

+134
-12
lines changed

src/librustc_ast_passes/ast_validation.rs

Lines changed: 83 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_session::Session;
2323
use rustc_span::symbol::{kw, sym};
2424
use rustc_span::Span;
2525
use std::mem;
26+
use std::ops::DerefMut;
2627

2728
const MORE_EXTERN: &str =
2829
"for more information, visit https://doc.rust-lang.org/std/keyword.extern.html";
@@ -1113,17 +1114,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11131114

11141115
for predicate in &generics.where_clause.predicates {
11151116
if let WherePredicate::EqPredicate(ref predicate) = *predicate {
1116-
self.err_handler()
1117-
.struct_span_err(
1118-
predicate.span,
1119-
"equality constraints are not yet supported in `where` clauses",
1120-
)
1121-
.span_label(predicate.span, "not supported")
1122-
.note(
1123-
"see issue #20041 <https://github.com/rust-lang/rust/issues/20041> \
1124-
for more information",
1125-
)
1126-
.emit();
1117+
deny_equality_constraints(self, predicate, generics);
11271118
}
11281119
}
11291120

@@ -1300,6 +1291,87 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13001291
}
13011292
}
13021293

1294+
fn deny_equality_constraints(
1295+
this: &mut AstValidator<'_>,
1296+
predicate: &WhereEqPredicate,
1297+
generics: &Generics,
1298+
) {
1299+
let mut err = this.err_handler().struct_span_err(
1300+
predicate.span,
1301+
"equality constraints are not yet supported in `where` clauses",
1302+
);
1303+
err.span_label(predicate.span, "not supported");
1304+
1305+
// Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
1306+
if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind {
1307+
if let TyKind::Path(None, path) = &qself.ty.kind {
1308+
match &path.segments[..] {
1309+
[PathSegment { ident, args: None, .. }] => {
1310+
for param in &generics.params {
1311+
if param.ident == *ident {
1312+
let param = ident;
1313+
match &full_path.segments[qself.position..] {
1314+
[PathSegment { ident, .. }] => {
1315+
// Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
1316+
let mut assoc_path = full_path.clone();
1317+
// Remove `Bar` from `Foo::Bar`.
1318+
assoc_path.segments.pop();
1319+
let len = assoc_path.segments.len() - 1;
1320+
// Build `<Bar = RhsTy>`.
1321+
let arg = AngleBracketedArg::Constraint(AssocTyConstraint {
1322+
id: rustc_ast::node_id::DUMMY_NODE_ID,
1323+
ident: *ident,
1324+
kind: AssocTyConstraintKind::Equality {
1325+
ty: predicate.rhs_ty.clone(),
1326+
},
1327+
span: ident.span,
1328+
});
1329+
// Add `<Bar = RhsTy>` to `Foo`.
1330+
match &mut assoc_path.segments[len].args {
1331+
Some(args) => match args.deref_mut() {
1332+
GenericArgs::Parenthesized(_) => continue,
1333+
GenericArgs::AngleBracketed(args) => {
1334+
args.args.push(arg);
1335+
}
1336+
},
1337+
empty_args => {
1338+
*empty_args = AngleBracketedArgs {
1339+
span: ident.span,
1340+
args: vec![arg],
1341+
}
1342+
.into();
1343+
}
1344+
}
1345+
err.span_suggestion_verbose(
1346+
predicate.span,
1347+
&format!(
1348+
"if `{}` is an associated type you're trying to set, \
1349+
use the associated type binding syntax",
1350+
ident
1351+
),
1352+
format!(
1353+
"{}: {}",
1354+
param,
1355+
pprust::path_to_string(&assoc_path)
1356+
),
1357+
Applicability::MaybeIncorrect,
1358+
);
1359+
}
1360+
_ => {}
1361+
};
1362+
}
1363+
}
1364+
}
1365+
_ => {}
1366+
}
1367+
}
1368+
}
1369+
err.note(
1370+
"see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information",
1371+
);
1372+
err.emit();
1373+
}
1374+
13031375
pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool {
13041376
let mut validator = AstValidator {
13051377
session,

src/test/ui/generic-associated-types/missing-bounds.fixed

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,15 @@ impl<B: std::ops::Add<Output = B>> Add for D<B> {
3232
}
3333
}
3434

35+
struct E<B>(B);
36+
37+
impl<B: Add> Add for E<B> where B: Add<Output = B>, B: std::ops::Add<Output = B> {
38+
//~^ ERROR equality constraints are not yet supported in `where` clauses
39+
type Output = Self;
40+
41+
fn add(self, rhs: Self) -> Self {
42+
Self(self.0 + rhs.0) //~ ERROR mismatched types
43+
}
44+
}
45+
3546
fn main() {}

src/test/ui/generic-associated-types/missing-bounds.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,15 @@ impl<B> Add for D<B> {
3232
}
3333
}
3434

35+
struct E<B>(B);
36+
37+
impl<B: Add> Add for E<B> where <B as Add>::Output = B {
38+
//~^ ERROR equality constraints are not yet supported in `where` clauses
39+
type Output = Self;
40+
41+
fn add(self, rhs: Self) -> Self {
42+
Self(self.0 + rhs.0) //~ ERROR mismatched types
43+
}
44+
}
45+
3546
fn main() {}

src/test/ui/generic-associated-types/missing-bounds.stderr

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
error: equality constraints are not yet supported in `where` clauses
2+
--> $DIR/missing-bounds.rs:37:33
3+
|
4+
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
5+
| ^^^^^^^^^^^^^^^^^^^^^^ not supported
6+
|
7+
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
8+
help: if `Output` is an associated type you're trying to set, use the associated type binding syntax
9+
|
10+
LL | impl<B: Add> Add for E<B> where B: Add<Output = B> {
11+
| ^^^^^^^^^^^^^^^^^^
12+
113
error[E0308]: mismatched types
214
--> $DIR/missing-bounds.rs:11:11
315
|
@@ -43,7 +55,23 @@ help: consider restricting type parameter `B`
4355
LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
4456
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
4557

46-
error: aborting due to 3 previous errors
58+
error[E0308]: mismatched types
59+
--> $DIR/missing-bounds.rs:42:14
60+
|
61+
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
62+
| - this type parameter
63+
...
64+
LL | Self(self.0 + rhs.0)
65+
| ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type
66+
|
67+
= note: expected type parameter `B`
68+
found associated type `<B as std::ops::Add>::Output`
69+
help: consider further restricting type parameter `B`
70+
|
71+
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B, B: std::ops::Add<Output = B> {
72+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
73+
74+
error: aborting due to 5 previous errors
4775

4876
Some errors have detailed explanations: E0308, E0369.
4977
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)