Skip to content

Commit d4fc281

Browse files
committed
Syntactically reject equality predicates
1 parent 024757f commit d4fc281

16 files changed

Lines changed: 166 additions & 110 deletions

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rustc_ast::*;
2727
use rustc_ast_pretty::pprust::{self, State};
2828
use rustc_attr_parsing::validate_attr;
2929
use rustc_data_structures::fx::FxIndexMap;
30-
use rustc_errors::{DiagCtxtHandle, LintBuffer};
30+
use rustc_errors::{Diag, DiagCtxtHandle, LintBuffer, StashKey, Subdiagnostic as _};
3131
use rustc_feature::Features;
3232
use rustc_session::Session;
3333
use rustc_session::lint::BuiltinLintDiag;
@@ -1559,14 +1559,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
15591559
}
15601560

15611561
validate_generic_param_order(self.dcx(), &generics.params, generics.span);
1562-
1563-
for predicate in &generics.where_clause.predicates {
1564-
let span = predicate.span;
1565-
if let WherePredicateKind::EqPredicate(predicate) = &predicate.kind {
1566-
deny_equality_constraints(self, predicate, span, generics);
1567-
}
1568-
}
15691562
walk_list!(self, visit_generic_param, &generics.params);
1563+
15701564
for predicate in &generics.where_clause.predicates {
15711565
match &predicate.kind {
15721566
WherePredicateKind::BoundPredicate(bound_pred) => {
@@ -1590,7 +1584,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
15901584
}
15911585
}
15921586
}
1593-
_ => {}
1587+
WherePredicateKind::RegionPredicate(_) => {}
1588+
WherePredicateKind::EqPredicate(eq_predicate) => {
1589+
// NOTE: We compute and attach the suggestion here instead of in the parser as
1590+
// it needs access to `generics.params`. While those could be passed to
1591+
// the where-clause parser, it would worsen its many callsites!
1592+
self.dcx().try_steal_modify_and_emit_err(
1593+
eq_predicate.lhs_ty.span,
1594+
StashKey::EqualityPredicate,
1595+
|diag| {
1596+
suggest_replacing_eq_pred_with_assoc_ty_binding(
1597+
eq_predicate,
1598+
predicate.span,
1599+
generics,
1600+
diag,
1601+
)
1602+
},
1603+
);
1604+
}
15941605
}
15951606
self.visit_where_predicate(predicate);
15961607
}
@@ -1880,24 +1891,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
18801891
}
18811892
}
18821893

1883-
/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems
1884-
/// like it's setting an associated type, provide an appropriate suggestion.
1885-
fn deny_equality_constraints(
1886-
this: &AstValidator<'_>,
1894+
fn suggest_replacing_eq_pred_with_assoc_ty_binding(
18871895
predicate: &WhereEqPredicate,
18881896
predicate_span: Span,
18891897
generics: &Generics,
1898+
diag: &mut Diag<'_>,
18901899
) {
1891-
let mut err = errors::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None };
1892-
18931900
// Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
18941901
if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind
18951902
&& let TyKind::Path(None, path) = &qself.ty.kind
1896-
&& let [PathSegment { ident, args: None, .. }] = &path.segments[..]
1903+
&& let [PathSegment { ident, args: None, .. }] = path.segments[..]
18971904
{
18981905
for param in &generics.params {
1899-
if param.ident == *ident
1900-
&& let [PathSegment { ident, args, .. }] = &full_path.segments[qself.position..]
1906+
if param.ident == ident
1907+
&& let [PathSegment { ident, ref args, .. }] = full_path.segments[qself.position..]
19011908
{
19021909
// Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
19031910
let mut assoc_path = full_path.clone();
@@ -1908,7 +1915,7 @@ fn deny_equality_constraints(
19081915
// Build `<Bar = RhsTy>`.
19091916
let arg = AngleBracketedArg::Constraint(AssocItemConstraint {
19101917
id: rustc_ast::node_id::DUMMY_NODE_ID,
1911-
ident: *ident,
1918+
ident,
19121919
gen_args,
19131920
kind: AssocItemConstraintKind::Equality {
19141921
term: predicate.rhs_ty.clone().into(),
@@ -1931,12 +1938,13 @@ fn deny_equality_constraints(
19311938
);
19321939
}
19331940
}
1934-
err.assoc = Some(errors::AssociatedSuggestion {
1941+
errors::UseAssocTypeBindingSyntaxSugg {
19351942
span: predicate_span,
1936-
ident: *ident,
1943+
ident,
19371944
param: param.ident,
19381945
path: pprust::path_to_string(&assoc_path),
1939-
})
1946+
}
1947+
.add_to_diag(diag);
19401948
}
19411949
}
19421950
}
@@ -1959,7 +1967,7 @@ fn deny_equality_constraints(
19591967
None => (format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi()),
19601968
};
19611969
let removal_span = if generics.where_clause.predicates.len() == 1 {
1962-
// We're removing th eonly where bound left, remove the whole thing.
1970+
// We're removing the only where bound left, remove the whole thing.
19631971
generics.where_clause.span
19641972
} else {
19651973
let mut span = predicate_span;
@@ -1982,13 +1990,14 @@ fn deny_equality_constraints(
19821990
}
19831991
span
19841992
};
1985-
err.assoc2 = Some(errors::AssociatedSuggestion2 {
1993+
errors::UseAssocTypeBindingSyntaxMultipartSugg {
19861994
span,
19871995
args,
19881996
predicate: removal_span,
19891997
trait_segment: trait_segment.ident,
19901998
potential_assoc: potential_assoc.ident,
1991-
});
1999+
}
2000+
.add_to_diag(diag);
19922001
}
19932002
};
19942003

@@ -2041,7 +2050,6 @@ fn deny_equality_constraints(
20412050
}
20422051
}
20432052
}
2044-
this.dcx().emit_err(err);
20452053
}
20462054

20472055
pub fn check_crate(

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -879,27 +879,14 @@ pub(crate) struct PatternInBodiless {
879879
pub span: Span,
880880
}
881881

882-
#[derive(Diagnostic)]
883-
#[diag("equality constraints are not yet supported in `where` clauses")]
884-
#[note("see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information")]
885-
pub(crate) struct EqualityInWhere {
886-
#[primary_span]
887-
#[label("not supported")]
888-
pub span: Span,
889-
#[subdiagnostic]
890-
pub assoc: Option<AssociatedSuggestion>,
891-
#[subdiagnostic]
892-
pub assoc2: Option<AssociatedSuggestion2>,
893-
}
894-
895882
#[derive(Subdiagnostic)]
896883
#[suggestion(
897-
"if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax",
884+
"use the associated type binding syntax if `{$ident}` is an associated type you're trying to set",
898885
code = "{param}: {path}",
899886
style = "verbose",
900887
applicability = "maybe-incorrect"
901888
)]
902-
pub(crate) struct AssociatedSuggestion {
889+
pub(crate) struct UseAssocTypeBindingSyntaxSugg {
903890
#[primary_span]
904891
pub span: Span,
905892
pub ident: Ident,
@@ -909,10 +896,10 @@ pub(crate) struct AssociatedSuggestion {
909896

910897
#[derive(Subdiagnostic)]
911898
#[multipart_suggestion(
912-
"if `{$trait_segment}::{$potential_assoc}` is an associated type you're trying to set, use the associated type binding syntax",
899+
"use the associated type binding syntax if `{$trait_segment}::{$potential_assoc}` is an associated type you're trying to set",
913900
applicability = "maybe-incorrect"
914901
)]
915-
pub(crate) struct AssociatedSuggestion2 {
902+
pub(crate) struct UseAssocTypeBindingSyntaxMultipartSugg {
916903
#[suggestion_part(code = "{args}")]
917904
pub span: Span,
918905
pub args: String,

compiler/rustc_errors/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ pub enum StashKey {
382382
/// it's a method call without parens. If later on in `hir_typeck` we find out that this is
383383
/// the case we suppress this message and we give a better suggestion.
384384
GenericInFieldExpr,
385+
EqualityPredicate,
385386
}
386387

387388
fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R {

compiler/rustc_parse/src/errors.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4470,3 +4470,12 @@ impl TokenDescription {
44704470
}
44714471
}
44724472
}
4473+
4474+
#[derive(Diagnostic)]
4475+
#[diag("equality constraints are not supported in where-clauses")]
4476+
#[note("see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information")]
4477+
pub(crate) struct EqualityConstraintInWhereClause {
4478+
#[primary_span]
4479+
#[label("not supported")]
4480+
pub span: Span,
4481+
}

compiler/rustc_parse/src/parser/generics.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -581,25 +581,41 @@ impl<'a> Parser<'a> {
581581
// * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
582582
let (bound_vars, _) = self.parse_higher_ranked_binder()?;
583583

584-
// Parse type with mandatory colon and (possibly empty) bounds,
585-
// or with mandatory equality sign and the second type.
586584
let ty = self.parse_ty_for_where_clause()?;
585+
587586
if self.eat(exp!(Colon)) {
587+
// The bounds may be empty; we intentionally accept predicates like `Ty:`.
588588
let bounds = self.parse_generic_bounds()?;
589-
Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
589+
590+
return Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
590591
bound_generic_params: bound_vars,
591592
bounded_ty: ty,
592593
bounds,
593-
}))
594-
// FIXME: Decide what should be used here, `=` or `==`.
595-
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
596-
} else if self.eat(exp!(Eq)) || self.eat(exp!(EqEq)) {
594+
}));
595+
}
596+
597+
// NOTE: If we ever end up impl'ing and stabilizing equality predicates,
598+
// we need to pick between `=` and `==`, both is not an option!
599+
if self.eat(exp!(Eq)) || self.eat(exp!(EqEq)) {
597600
let rhs_ty = self.parse_ty()?;
598-
Ok(ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { lhs_ty: ty, rhs_ty }))
599-
} else {
600-
self.maybe_recover_bounds_doubled_colon(&ty)?;
601-
self.unexpected_any()
601+
602+
let diag = self.dcx().create_err(errors::EqualityConstraintInWhereClause {
603+
span: ty.span.to(rhs_ty.span),
604+
});
605+
diag.stash(ty.span, rustc_errors::StashKey::EqualityPredicate);
606+
607+
// NOTE: If we ever end up impl'ing equality predicates,
608+
// we ought to track the binder in the AST node!
609+
let _ = bound_vars;
610+
611+
return Ok(ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate {
612+
lhs_ty: ty,
613+
rhs_ty,
614+
}));
602615
}
616+
617+
self.maybe_recover_bounds_doubled_colon(&ty)?;
618+
self.unexpected_any()
603619
}
604620

605621
pub(super) fn choose_generics_over_qpath(&self, start: usize) -> bool {

tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn paint<C:BoxCar>(c: C, d: C::Color) {
3131

3232
fn dent_object_2<COLOR>(c: &dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR {
3333
//~^ ERROR the value of the associated types
34-
//~| ERROR equality constraints are not yet supported in `where` clauses
34+
//~| ERROR equality constraints are not supported in where-clauses
3535
}
3636

3737
fn dent_object_3<X, COLOR>(c: X)

tests/ui/associated-types/associated-type-projection-from-multiple-supertraits.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: equality constraints are not yet supported in `where` clauses
1+
error: equality constraints are not supported in where-clauses
22
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:47
33
|
44
LL | fn dent_object_2<COLOR>(c: &dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR {

tests/ui/generic-associated-types/equality-bound.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
fn sum<I: Iterator<Item = ()>>(i: I) -> i32 where I::Item = i32 {
2-
//~^ ERROR equality constraints are not yet supported in `where` clauses
2+
//~^ ERROR equality constraints are not supported in where-clauses
33
panic!()
44
}
55
fn sum2<I: Iterator>(i: I) -> i32 where I::Item = i32 {
6-
//~^ ERROR equality constraints are not yet supported in `where` clauses
6+
//~^ ERROR equality constraints are not supported in where-clauses
77
panic!()
88
}
99
fn sum3<J: Iterator>(i: J) -> i32 where I::Item = i32 {
10-
//~^ ERROR equality constraints are not yet supported in `where` clauses
10+
//~^ ERROR equality constraints are not supported in where-clauses
1111
//~| ERROR cannot find type `I`
1212
panic!()
1313
}
@@ -18,7 +18,7 @@ struct X {}
1818

1919
impl FromIterator<bool> for X {
2020
fn from_iter<T>(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A,
21-
//~^ ERROR equality constraints are not yet supported in `where` clauses
21+
//~^ ERROR equality constraints are not supported in where-clauses
2222
//~| ERROR cannot find type `A` in this scope
2323
{
2424
todo!()
@@ -29,7 +29,7 @@ struct Y {}
2929

3030
impl FromIterator<bool> for Y {
3131
fn from_iter<T>(_: T) -> Self where T: IntoIterator, T::Item = A,
32-
//~^ ERROR equality constraints are not yet supported in `where` clauses
32+
//~^ ERROR equality constraints are not supported in where-clauses
3333
//~| ERROR cannot find type `A` in this scope
3434
{
3535
todo!()
@@ -40,7 +40,7 @@ struct Z {}
4040

4141
impl FromIterator<bool> for Z {
4242
fn from_iter<T: IntoIterator>(_: T) -> Self where IntoIterator::Item = A,
43-
//~^ ERROR equality constraints are not yet supported in `where` clauses
43+
//~^ ERROR equality constraints are not supported in where-clauses
4444
//~| ERROR cannot find type `A` in this scope
4545
{
4646
todo!()
@@ -51,7 +51,7 @@ struct K {}
5151

5252
impl FromIterator<bool> for K {
5353
fn from_iter<T: IntoIterator>(_: T) -> Self where T::Item = A,
54-
//~^ ERROR equality constraints are not yet supported in `where` clauses
54+
//~^ ERROR equality constraints are not supported in where-clauses
5555
//~| ERROR cannot find type `A` in this scope
5656
{
5757
todo!()
@@ -62,7 +62,7 @@ struct L {}
6262

6363
impl FromIterator<bool> for L {
6464
fn from_iter<T>(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator,
65-
//~^ ERROR equality constraints are not yet supported in `where` clauses
65+
//~^ ERROR equality constraints are not supported in where-clauses
6666
//~| ERROR cannot find type `A` in this scope
6767
{
6868
todo!()
@@ -73,7 +73,7 @@ struct M {}
7373

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

0 commit comments

Comments
 (0)