Skip to content

Commit 9425b65

Browse files
authored
Unrolled build for #153513
Rollup merge of #153513 - fmease:gate-eq-pred, r=jackh726 Syntactically reject equality predicates ### Background (History) Equality predicates have been *syntactically* valid since PR #39158 / nightly-2017-02-02 / 1.16 (released 2017-03-16, 9 years ago), both forms that is: `$ty = $ty` *and* `$ty == $ty`. They're not registered as an unstable feature despite having a tracking issue (#20041). Naturally, they don't have a feature gate. Of course, we reject them post-expansion, so they are still semantically invalid. Parser scaffolding for `$ident = $ty` was added in RUST-19391 (2014) which was then generalized to `$ty = $ty` in RUST-20002 (2014) and extended to additionally cover `$ty == $ty` in RUST-39158 (2017). As mentioned, the last PR also made them grammatical. RUST-87471 (2021) attempted to start impl'ing typeck'ing but it was closed due to concerns: #87471 (comment) (already back in 2017 there were concerns: https://github.com/rust-lang/rust/pull/39158/changes#r97811244). In 2022, T-lang discussed this feature during a meeting and raised concerns: #20041 (comment). However, they were inclined to accept a restricted form, namely `T::AssocTy = $ty` (`T` is a (self) ty param or a self ty alias) and `<$ty as Trait>::AssocTy = $ty` since that's trivial to support in HIR ty lowering (this is still accurate). ### Change This renders equality predicates `$ty = $ty` and `$ty == $ty` syntactically invalid again. On stable, they're merely semantically invalid. This is motivated by the fact that 1. their syntax isn't even decided upon 1. should it be `=` or `==`? 2. should the LHS be syntactically restricted to (possibly qualified) paths (i.e., *TypePath | QualifiedPathInType*), only semantically or not at all? * "syntactically" could indeed make sense since we might want to generalize types to so-called *terms* here (i.e., *Type | GenericArgsConst*) for [mGCA](#132980) but we can't generalize `$ty = $ty` to `$term = $term` due to ambiguities [^1] that would require backtracking whereas `$typepath = $term` wouldn't have that problem 2. we might (decide to) never impl this feature * after all, implementation concerns were raised as early as 9 years ago which are still relevant today even with the next-gen trait solver on the horizon 3. the compiler + tools contain a bunch of code for dealing with these predicates * like lowering, name resolution, rustfmt and Clippy * that's entirely useless since they get unconditionally rejected anyway * this code doesn't need to exist (for now) I've merely upgraded the semantic hard error to a syntactic hard error, I've intentionally not introduced an unstable feature under which equality predicates become legal. That's because a hard error is easier to push through procedurally, it's an important first step (we can always turn it into a feature gate error later on) and since this potential feature isn't backed by any official lang experiment. I don't consider T-lang's message #20041 (comment) from 2022 to be sufficient because it doesn't answer questions like (1.i), (1.ii) or whether code like `T: Trait<A>, <T as Trait<B>>::AssocTy = Ty` (`A` ≠ `B`) or `T: Trait<'r>, for<'a> <T as Trait<'a>>::AssocTy = Ty` should be legal (which would be more powerful / could lead to typechecking issues, idk) or not assuming we're going with the restricted form ofc (we would need input from T-types). ### Lang nomination #153513 (comment) [^1]: Code like `fn f() -> i32 where { 0 }` is intentionally legal today but if we had `$term = $term` the `{ 0 }` could then also be the start of an equality predicate like `{ 0 } = 0`.
2 parents f20a92e + ddbf3b9 commit 9425b65

38 files changed

Lines changed: 182 additions & 716 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,8 +509,6 @@ pub enum WherePredicateKind {
509509
BoundPredicate(WhereBoundPredicate),
510510
/// A lifetime predicate (e.g., `'a: 'b + 'c`).
511511
RegionPredicate(WhereRegionPredicate),
512-
/// An equality predicate (unsupported).
513-
EqPredicate(WhereEqPredicate),
514512
}
515513

516514
/// A type bound.

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,18 +2111,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
21112111
in_where_clause: true,
21122112
})
21132113
}
2114-
WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => {
2115-
hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate {
2116-
lhs_ty: self.lower_ty_alloc(
2117-
lhs_ty,
2118-
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
2119-
),
2120-
rhs_ty: self.lower_ty_alloc(
2121-
rhs_ty,
2122-
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
2123-
),
2124-
})
2125-
}
21262114
});
21272115
hir::WherePredicate { hir_id, span, kind }
21282116
}

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 2 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
//! require name resolution or type checking, or other kinds of complex analysis.
1818
1919
use std::mem;
20-
use std::ops::{Deref, DerefMut};
2120
use std::str::FromStr;
2221

2322
use itertools::{Either, Itertools};
@@ -37,7 +36,6 @@ use rustc_session::lint::builtin::{
3736
};
3837
use rustc_span::{Ident, Span, kw, sym};
3938
use rustc_target::spec::{AbiMap, AbiMapping};
40-
use thin_vec::thin_vec;
4139

4240
use crate::diagnostics::{self, TildeConstReason};
4341

@@ -1593,14 +1591,8 @@ impl Visitor<'_> for AstValidator<'_> {
15931591
}
15941592

15951593
validate_generic_param_order(self.dcx(), &generics.params, generics.span);
1596-
1597-
for predicate in &generics.where_clause.predicates {
1598-
let span = predicate.span;
1599-
if let WherePredicateKind::EqPredicate(predicate) = &predicate.kind {
1600-
deny_equality_constraints(self, predicate, span, generics);
1601-
}
1602-
}
16031594
walk_list!(self, visit_generic_param, &generics.params);
1595+
16041596
for predicate in &generics.where_clause.predicates {
16051597
match &predicate.kind {
16061598
WherePredicateKind::BoundPredicate(bound_pred) => {
@@ -1625,7 +1617,7 @@ impl Visitor<'_> for AstValidator<'_> {
16251617
}
16261618
}
16271619
}
1628-
_ => {}
1620+
WherePredicateKind::RegionPredicate(_) => {}
16291621
}
16301622
self.visit_where_predicate(predicate);
16311623
}
@@ -1962,170 +1954,6 @@ impl Visitor<'_> for AstValidator<'_> {
19621954
}
19631955
}
19641956

1965-
/// When encountering an equality constraint in a `where` clause, emit an error. If the code seems
1966-
/// like it's setting an associated type, provide an appropriate suggestion.
1967-
fn deny_equality_constraints(
1968-
this: &AstValidator<'_>,
1969-
predicate: &WhereEqPredicate,
1970-
predicate_span: Span,
1971-
generics: &Generics,
1972-
) {
1973-
let mut err = diagnostics::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None };
1974-
1975-
// Given `<A as Foo>::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
1976-
if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind
1977-
&& let TyKind::Path(None, path) = &qself.ty.kind
1978-
&& let [PathSegment { ident, args: None, .. }] = &path.segments[..]
1979-
{
1980-
for param in &generics.params {
1981-
if param.ident == *ident
1982-
&& let [PathSegment { ident, args, .. }] = &full_path.segments[qself.position..]
1983-
{
1984-
// Make a new `Path` from `foo::Bar` to `Foo<Bar = RhsTy>`.
1985-
let mut assoc_path = full_path.clone();
1986-
// Remove `Bar` from `Foo::Bar`.
1987-
assoc_path.segments.pop();
1988-
let len = assoc_path.segments.len() - 1;
1989-
let gen_args = args.as_deref().cloned();
1990-
// Build `<Bar = RhsTy>`.
1991-
let arg = AngleBracketedArg::Constraint(AssocItemConstraint {
1992-
id: rustc_ast::node_id::DUMMY_NODE_ID,
1993-
ident: *ident,
1994-
gen_args,
1995-
kind: AssocItemConstraintKind::Equality {
1996-
term: predicate.rhs_ty.clone().into(),
1997-
},
1998-
span: ident.span,
1999-
});
2000-
// Add `<Bar = RhsTy>` to `Foo`.
2001-
match &mut assoc_path.segments[len].args {
2002-
Some(args) => match args.deref_mut() {
2003-
GenericArgs::Parenthesized(_) | GenericArgs::ParenthesizedElided(..) => {
2004-
continue;
2005-
}
2006-
GenericArgs::AngleBracketed(args) => {
2007-
args.args.push(arg);
2008-
}
2009-
},
2010-
empty_args => {
2011-
*empty_args = Some(
2012-
AngleBracketedArgs { span: ident.span, args: thin_vec![arg] }.into(),
2013-
);
2014-
}
2015-
}
2016-
err.assoc = Some(diagnostics::AssociatedSuggestion {
2017-
span: predicate_span,
2018-
ident: *ident,
2019-
param: param.ident,
2020-
path: pprust::path_to_string(&assoc_path),
2021-
})
2022-
}
2023-
}
2024-
}
2025-
2026-
let mut suggest =
2027-
|poly: &PolyTraitRef, potential_assoc: &PathSegment, predicate: &WhereEqPredicate| {
2028-
if let [trait_segment] = &poly.trait_ref.path.segments[..] {
2029-
let assoc = pprust::path_to_string(&ast::Path::from_ident(potential_assoc.ident));
2030-
let ty = pprust::ty_to_string(&predicate.rhs_ty);
2031-
let (args, span) = match &trait_segment.args {
2032-
Some(args) => match args.deref() {
2033-
ast::GenericArgs::AngleBracketed(args) => {
2034-
let Some(arg) = args.args.last() else {
2035-
return;
2036-
};
2037-
(format!(", {assoc} = {ty}"), arg.span().shrink_to_hi())
2038-
}
2039-
_ => return,
2040-
},
2041-
None => (format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi()),
2042-
};
2043-
let removal_span = if generics.where_clause.predicates.len() == 1 {
2044-
// We're removing th eonly where bound left, remove the whole thing.
2045-
generics.where_clause.span
2046-
} else {
2047-
let mut span = predicate_span;
2048-
let mut prev_span: Option<Span> = None;
2049-
let mut preds = generics.where_clause.predicates.iter().peekable();
2050-
// Find the predicate that shouldn't have been in the where bound list.
2051-
while let Some(pred) = preds.next() {
2052-
if let WherePredicateKind::EqPredicate(_) = pred.kind
2053-
&& pred.span == predicate_span
2054-
{
2055-
if let Some(next) = preds.peek() {
2056-
// This is the first predicate, remove the trailing comma as well.
2057-
span = span.with_hi(next.span.lo());
2058-
} else if let Some(prev_span) = prev_span {
2059-
// Remove the previous comma as well.
2060-
span = span.with_lo(prev_span.hi());
2061-
}
2062-
}
2063-
prev_span = Some(pred.span);
2064-
}
2065-
span
2066-
};
2067-
err.assoc2 = Some(diagnostics::AssociatedSuggestion2 {
2068-
span,
2069-
args,
2070-
predicate: removal_span,
2071-
trait_segment: trait_segment.ident,
2072-
potential_assoc: potential_assoc.ident,
2073-
});
2074-
}
2075-
};
2076-
2077-
if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
2078-
// Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
2079-
for bounds in generics.params.iter().map(|p| &p.bounds).chain(
2080-
generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind {
2081-
WherePredicateKind::BoundPredicate(p) => Some(&p.bounds),
2082-
_ => None,
2083-
}),
2084-
) {
2085-
for bound in bounds {
2086-
if let GenericBound::Trait(poly) = bound
2087-
&& poly.modifiers == TraitBoundModifiers::NONE
2088-
{
2089-
if full_path.segments[..full_path.segments.len() - 1]
2090-
.iter()
2091-
.map(|segment| segment.ident.name)
2092-
.zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name))
2093-
.all(|(a, b)| a == b)
2094-
&& let Some(potential_assoc) = full_path.segments.last()
2095-
{
2096-
suggest(poly, potential_assoc, predicate);
2097-
}
2098-
}
2099-
}
2100-
}
2101-
// Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
2102-
if let [potential_param, potential_assoc] = &full_path.segments[..] {
2103-
for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain(
2104-
generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind {
2105-
WherePredicateKind::BoundPredicate(p)
2106-
if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind
2107-
&& let [segment] = &path.segments[..] =>
2108-
{
2109-
Some((segment.ident, &p.bounds))
2110-
}
2111-
_ => None,
2112-
}),
2113-
) {
2114-
if ident == potential_param.ident {
2115-
for bound in bounds {
2116-
if let ast::GenericBound::Trait(poly) = bound
2117-
&& poly.modifiers == TraitBoundModifiers::NONE
2118-
{
2119-
suggest(poly, potential_assoc, predicate);
2120-
}
2121-
}
2122-
}
2123-
}
2124-
}
2125-
}
2126-
this.dcx().emit_err(err);
2127-
}
2128-
21291957
pub fn check_crate(
21301958
sess: &Session,
21311959
features: &Features,

compiler/rustc_ast_passes/src/diagnostics.rs

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -878,49 +878,6 @@ pub(crate) struct PatternInBodiless {
878878
pub span: Span,
879879
}
880880

881-
#[derive(Diagnostic)]
882-
#[diag("equality constraints are not yet supported in `where` clauses")]
883-
#[note("see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information")]
884-
pub(crate) struct EqualityInWhere {
885-
#[primary_span]
886-
#[label("not supported")]
887-
pub span: Span,
888-
#[subdiagnostic]
889-
pub assoc: Option<AssociatedSuggestion>,
890-
#[subdiagnostic]
891-
pub assoc2: Option<AssociatedSuggestion2>,
892-
}
893-
894-
#[derive(Subdiagnostic)]
895-
#[suggestion(
896-
"if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax",
897-
code = "{param}: {path}",
898-
style = "verbose",
899-
applicability = "maybe-incorrect"
900-
)]
901-
pub(crate) struct AssociatedSuggestion {
902-
#[primary_span]
903-
pub span: Span,
904-
pub ident: Ident,
905-
pub param: Ident,
906-
pub path: String,
907-
}
908-
909-
#[derive(Subdiagnostic)]
910-
#[multipart_suggestion(
911-
"if `{$trait_segment}::{$potential_assoc}` is an associated type you're trying to set, use the associated type binding syntax",
912-
applicability = "maybe-incorrect"
913-
)]
914-
pub(crate) struct AssociatedSuggestion2 {
915-
#[suggestion_part(code = "{args}")]
916-
pub span: Span,
917-
pub args: String,
918-
#[suggestion_part(code = "")]
919-
pub predicate: Span,
920-
pub trait_segment: Ident,
921-
pub potential_assoc: Ident,
922-
}
923-
924881
#[derive(Diagnostic)]
925882
#[diag("`#![feature]` may not be used on the {$channel} release channel", code = E0554)]
926883
pub(crate) struct FeatureOnNonNightly {

compiler/rustc_ast_pretty/src/pprust/state/item.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -869,14 +869,6 @@ impl<'a> State<'a> {
869869
self.print_lifetime_bounds(bounds);
870870
}
871871
}
872-
ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate {
873-
lhs_ty, rhs_ty, ..
874-
}) => {
875-
self.print_type(lhs_ty);
876-
self.space();
877-
self.word_space("=");
878-
self.print_type(rhs_ty);
879-
}
880872
}
881873
}
882874

compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,7 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> {
396396
self.visit_param_bound(bound, BoundKind::Bound)
397397
}
398398
}
399-
rustc_ast::WherePredicateKind::RegionPredicate(_)
400-
| rustc_ast::WherePredicateKind::EqPredicate(_) => {}
399+
rustc_ast::WherePredicateKind::RegionPredicate(_) => {}
401400
}
402401
}
403402
}

compiler/rustc_hir/src/hir.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,24 +1193,20 @@ pub enum WherePredicateKind<'hir> {
11931193
BoundPredicate(WhereBoundPredicate<'hir>),
11941194
/// A lifetime predicate (e.g., `'a: 'b + 'c`).
11951195
RegionPredicate(WhereRegionPredicate<'hir>),
1196-
/// An equality predicate (unsupported).
1197-
EqPredicate(WhereEqPredicate<'hir>),
11981196
}
11991197

12001198
impl<'hir> WherePredicateKind<'hir> {
12011199
pub fn in_where_clause(&self) -> bool {
12021200
match self {
12031201
WherePredicateKind::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause,
12041202
WherePredicateKind::RegionPredicate(p) => p.in_where_clause,
1205-
WherePredicateKind::EqPredicate(_) => false,
12061203
}
12071204
}
12081205

12091206
pub fn bounds(&self) -> GenericBounds<'hir> {
12101207
match self {
12111208
WherePredicateKind::BoundPredicate(p) => p.bounds,
12121209
WherePredicateKind::RegionPredicate(p) => p.bounds,
1213-
WherePredicateKind::EqPredicate(_) => &[],
12141210
}
12151211
}
12161212
}

compiler/rustc_hir/src/intravisit.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,10 +1200,6 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>(
12001200
try_visit!(visitor.visit_lifetime(lifetime));
12011201
walk_list!(visitor, visit_param_bound, bounds);
12021202
}
1203-
WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => {
1204-
try_visit!(visitor.visit_ty_unambig(lhs_ty));
1205-
try_visit!(visitor.visit_ty_unambig(rhs_ty));
1206-
}
12071203
}
12081204
V::Result::output()
12091205
}

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,6 @@ pub(super) fn check_number_of_early_bound_regions<'tcx>(
12021202
}
12031203
}
12041204
}
1205-
_ => {}
12061205
}
12071206
}
12081207
if let Some(impl_node) = tcx.hir_get_if_local(impl_def_id.into())
@@ -1225,7 +1224,6 @@ pub(super) fn check_number_of_early_bound_regions<'tcx>(
12251224
}
12261225
}
12271226
}
1228-
_ => {}
12291227
}
12301228
}
12311229
if impl_bounds == bounds_span.len() {

compiler/rustc_hir_analysis/src/collect/predicates_of.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
322322
(pred, span)
323323
}))
324324
}
325-
326-
hir::WherePredicateKind::EqPredicate(..) => {
327-
// FIXME(#20041)
328-
}
329325
}
330326
}
331327

0 commit comments

Comments
 (0)