Skip to content

Commit cb449d2

Browse files
authored
Rollup merge of #64676 - estebank:assoc-type-bound-in-generic, r=petrochenkov
Parse assoc type bounds in generic params and provide custom diagnostic Fix #26271.
2 parents b66e732 + 0f2f16d commit cb449d2

File tree

3 files changed

+80
-38
lines changed

3 files changed

+80
-38
lines changed

src/libsyntax/parse/parser/generics.rs

+61-38
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,31 @@ impl<'a> Parser<'a> {
100100
} else if self.check_ident() {
101101
// Parse type parameter.
102102
params.push(self.parse_ty_param(attrs)?);
103+
} else if self.token.can_begin_type() {
104+
// Trying to write an associated type bound? (#26271)
105+
let snapshot = self.clone();
106+
match self.parse_ty_where_predicate() {
107+
Ok(where_predicate) => {
108+
self.struct_span_err(
109+
where_predicate.span(),
110+
"bounds on associated types do not belong here",
111+
)
112+
.span_label(where_predicate.span(), "belongs in `where` clause")
113+
.emit();
114+
}
115+
Err(mut err) => {
116+
err.cancel();
117+
std::mem::replace(self, snapshot);
118+
break
119+
}
120+
}
103121
} else {
104122
// Check for trailing attributes and stop parsing.
105123
if !attrs.is_empty() {
106124
if !params.is_empty() {
107125
self.struct_span_err(
108126
attrs[0].span,
109-
&format!("trailing attribute after generic parameter"),
127+
"trailing attribute after generic parameter",
110128
)
111129
.span_label(attrs[0].span, "attributes must go before parameters")
112130
.emit();
@@ -202,43 +220,7 @@ impl<'a> Parser<'a> {
202220
}
203221
));
204222
} else if self.check_type() {
205-
// Parse optional `for<'a, 'b>`.
206-
// This `for` is parsed greedily and applies to the whole predicate,
207-
// the bounded type can have its own `for` applying only to it.
208-
// Examples:
209-
// * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
210-
// * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
211-
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
212-
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
213-
214-
// Parse type with mandatory colon and (possibly empty) bounds,
215-
// or with mandatory equality sign and the second type.
216-
let ty = self.parse_ty()?;
217-
if self.eat(&token::Colon) {
218-
let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
219-
where_clause.predicates.push(ast::WherePredicate::BoundPredicate(
220-
ast::WhereBoundPredicate {
221-
span: lo.to(self.prev_span),
222-
bound_generic_params: lifetime_defs,
223-
bounded_ty: ty,
224-
bounds,
225-
}
226-
));
227-
// FIXME: Decide what should be used here, `=` or `==`.
228-
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
229-
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
230-
let rhs_ty = self.parse_ty()?;
231-
where_clause.predicates.push(ast::WherePredicate::EqPredicate(
232-
ast::WhereEqPredicate {
233-
span: lo.to(self.prev_span),
234-
lhs_ty: ty,
235-
rhs_ty,
236-
id: ast::DUMMY_NODE_ID,
237-
}
238-
));
239-
} else {
240-
return self.unexpected();
241-
}
223+
where_clause.predicates.push(self.parse_ty_where_predicate()?);
242224
} else {
243225
break
244226
}
@@ -252,6 +234,47 @@ impl<'a> Parser<'a> {
252234
Ok(where_clause)
253235
}
254236

237+
fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
238+
let lo = self.token.span;
239+
// Parse optional `for<'a, 'b>`.
240+
// This `for` is parsed greedily and applies to the whole predicate,
241+
// the bounded type can have its own `for` applying only to it.
242+
// Examples:
243+
// * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
244+
// * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
245+
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
246+
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
247+
248+
// Parse type with mandatory colon and (possibly empty) bounds,
249+
// or with mandatory equality sign and the second type.
250+
let ty = self.parse_ty()?;
251+
if self.eat(&token::Colon) {
252+
let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
253+
Ok(ast::WherePredicate::BoundPredicate(
254+
ast::WhereBoundPredicate {
255+
span: lo.to(self.prev_span),
256+
bound_generic_params: lifetime_defs,
257+
bounded_ty: ty,
258+
bounds,
259+
}
260+
))
261+
// FIXME: Decide what should be used here, `=` or `==`.
262+
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
263+
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
264+
let rhs_ty = self.parse_ty()?;
265+
Ok(ast::WherePredicate::EqPredicate(
266+
ast::WhereEqPredicate {
267+
span: lo.to(self.prev_span),
268+
lhs_ty: ty,
269+
rhs_ty,
270+
id: ast::DUMMY_NODE_ID,
271+
}
272+
))
273+
} else {
274+
self.unexpected()
275+
}
276+
}
277+
255278
pub(super) fn choose_generics_over_qpath(&self) -> bool {
256279
// There's an ambiguity between generic parameters and qualified paths in impls.
257280
// If we see `<` it may start both, so we have to inspect some following tokens.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait Tr {
2+
type TrSubtype;
3+
}
4+
5+
struct Bar<'a, Item: Tr, <Item as Tr>::TrSubtype: 'a> {
6+
//~^ ERROR bounds on associated types do not belong here
7+
item: Item,
8+
item_sub: &'a <Item as Tr>::TrSubtype,
9+
}
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: bounds on associated types do not belong here
2+
--> $DIR/assoc-type-in-type-arg.rs:5:26
3+
|
4+
LL | struct Bar<'a, Item: Tr, <Item as Tr>::TrSubtype: 'a> {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ belongs in `where` clause
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)