Skip to content

Commit cfc5f25

Browse files
authored
Rollup merge of rust-lang#127054 - compiler-errors:bound-ordering, r=fmease
Reorder trait bound modifiers *after* `for<...>` binder in trait bounds This PR suggests changing the grammar of trait bounds from: ``` [CONSTNESS] [ASYNCNESS] [?] [BINDER] [TRAIT_PATH] const async ? for<'a> Sized ``` to ``` ([BINDER] [CONSTNESS] [ASYNCNESS] | [?]) [TRAIT_PATH] ``` i.e., either ``` ? Sized ``` or ``` for<'a> const async Sized ``` (but not both) ### Why? I think it's strange that the binder applies "more tightly" than the `?` trait polarity. This becomes even weirder when considering that we (or at least, I) want to have `async` trait bounds expressed like: ``` where T: for<'a> async Fn(&'a ()) -> i32, ``` and not: ``` where T: async for<'a> Fn(&'a ()) -> i32, ``` ### Fallout No crates on crater use this syntax, presumably because it's literally useless. This will require modifying the reference grammar, though. ### Alternatives If this is not desirable, then we can alternatively keep parsing `for<'a>` after the `?` but deprecate it with either an FCW (or an immediate hard error), and begin parsing `for<'a>` *before* the `?`.
2 parents e7d66ea + 7da751a commit cfc5f25

File tree

20 files changed

+201
-68
lines changed

20 files changed

+201
-68
lines changed

compiler/rustc_ast_passes/messages.ftl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,6 @@ ast_passes_impl_trait_path = `impl Trait` is not allowed in path parameters
155155
ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
156156
.help = remove one of these features
157157
158-
ast_passes_incompatible_trait_bound_modifiers = `{$left}` and `{$right}` are mutually exclusive
159-
160158
ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
161159
.because = {$annotation} because of this
162160
.type = inherent impl for this type

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,17 +1366,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13661366
{
13671367
self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
13681368
}
1369-
(
1370-
_,
1371-
BoundConstness::Always(_) | BoundConstness::Maybe(_),
1372-
BoundPolarity::Negative(_) | BoundPolarity::Maybe(_),
1373-
) => {
1374-
self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers {
1375-
span: bound.span(),
1376-
left: modifiers.constness.as_str(),
1377-
right: modifiers.polarity.as_str(),
1378-
});
1379-
}
13801369
_ => {}
13811370
}
13821371

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -656,15 +656,6 @@ pub enum TildeConstReason {
656656
Item,
657657
}
658658

659-
#[derive(Diagnostic)]
660-
#[diag(ast_passes_incompatible_trait_bound_modifiers)]
661-
pub struct IncompatibleTraitBoundModifiers {
662-
#[primary_span]
663-
pub span: Span,
664-
pub left: &'static str,
665-
pub right: &'static str,
666-
}
667-
668659
#[derive(Diagnostic)]
669660
#[diag(ast_passes_const_and_async)]
670661
pub struct ConstAndAsync {

compiler/rustc_parse/messages.ftl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ parse_bare_cr = {$double_quotes ->
5353
5454
parse_bare_cr_in_raw_string = bare CR not allowed in raw string
5555
56+
parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier
57+
.label = there is not a well-defined meaning for a higher-ranked `{$polarity}` trait
58+
59+
parse_binder_before_modifiers = `for<...>` binder should be placed before trait bound modifiers
60+
.label = place the `for<...>` binder before any modifiers
61+
5662
parse_bounds_not_allowed_on_trait_aliases = bounds are not allowed on trait aliases
5763
5864
parse_box_not_pat = expected pattern, found {$descr}
@@ -577,6 +583,9 @@ parse_missing_trait_in_trait_impl = missing trait in a trait impl
577583
parse_modifier_lifetime = `{$modifier}` may only modify trait bounds, not lifetime bounds
578584
.suggestion = remove the `{$modifier}`
579585
586+
parse_modifiers_and_polarity = `{$modifiers_concatenated}` trait not allowed with `{$polarity}` trait polarity modifier
587+
.label = there is not a well-defined meaning for a `{$modifiers_concatenated} {$polarity}` trait
588+
580589
parse_more_than_one_char = character literal may only contain one codepoint
581590
.followed_by = this `{$chr}` is followed by the combining {$len ->
582591
[one] mark

compiler/rustc_parse/src/errors.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3212,3 +3212,33 @@ pub struct UnsafeAttrOutsideUnsafeSuggestion {
32123212
#[suggestion_part(code = ")")]
32133213
pub right: Span,
32143214
}
3215+
3216+
#[derive(Diagnostic)]
3217+
#[diag(parse_binder_before_modifiers)]
3218+
pub struct BinderBeforeModifiers {
3219+
#[primary_span]
3220+
pub binder_span: Span,
3221+
#[label]
3222+
pub modifiers_span: Span,
3223+
}
3224+
3225+
#[derive(Diagnostic)]
3226+
#[diag(parse_binder_and_polarity)]
3227+
pub struct BinderAndPolarity {
3228+
#[primary_span]
3229+
pub polarity_span: Span,
3230+
#[label]
3231+
pub binder_span: Span,
3232+
pub polarity: &'static str,
3233+
}
3234+
3235+
#[derive(Diagnostic)]
3236+
#[diag(parse_modifiers_and_polarity)]
3237+
pub struct PolarityAndModifiers {
3238+
#[primary_span]
3239+
pub polarity_span: Span,
3240+
#[label]
3241+
pub modifiers_span: Span,
3242+
pub polarity: &'static str,
3243+
pub modifiers_concatenated: String,
3244+
}

compiler/rustc_parse/src/parser/ty.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -935,9 +935,14 @@ impl<'a> Parser<'a> {
935935
/// If no modifiers are present, this does not consume any tokens.
936936
///
937937
/// ```ebnf
938-
/// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["async"] ["?" | "!"]
938+
/// CONSTNESS = [["~"] "const"]
939+
/// ASYNCNESS = ["async"]
940+
/// POLARITY = ["?" | "!"]
939941
/// ```
942+
///
943+
/// See `parse_generic_ty_bound` for the complete grammar of trait bound modifiers.
940944
fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> {
945+
let modifier_lo = self.token.span;
941946
let constness = if self.eat(&token::Tilde) {
942947
let tilde = self.prev_token.span;
943948
self.expect_keyword(kw::Const)?;
@@ -970,6 +975,7 @@ impl<'a> Parser<'a> {
970975
} else {
971976
BoundAsyncness::Normal
972977
};
978+
let modifier_hi = self.prev_token.span;
973979

974980
let polarity = if self.eat(&token::Question) {
975981
BoundPolarity::Maybe(self.prev_token.span)
@@ -980,13 +986,40 @@ impl<'a> Parser<'a> {
980986
BoundPolarity::Positive
981987
};
982988

989+
// Enforce the mutual-exclusivity of `const`/`async` and `?`/`!`.
990+
match polarity {
991+
BoundPolarity::Positive => {
992+
// All trait bound modifiers allowed to combine with positive polarity
993+
}
994+
BoundPolarity::Maybe(polarity_span) | BoundPolarity::Negative(polarity_span) => {
995+
match (asyncness, constness) {
996+
(BoundAsyncness::Normal, BoundConstness::Never) => {
997+
// Ok, no modifiers.
998+
}
999+
(_, _) => {
1000+
let constness = constness.as_str();
1001+
let asyncness = asyncness.as_str();
1002+
let glue =
1003+
if !constness.is_empty() && !asyncness.is_empty() { " " } else { "" };
1004+
let modifiers_concatenated = format!("{constness}{glue}{asyncness}");
1005+
self.dcx().emit_err(errors::PolarityAndModifiers {
1006+
polarity_span,
1007+
polarity: polarity.as_str(),
1008+
modifiers_span: modifier_lo.to(modifier_hi),
1009+
modifiers_concatenated,
1010+
});
1011+
}
1012+
}
1013+
}
1014+
}
1015+
9831016
Ok(TraitBoundModifiers { constness, asyncness, polarity })
9841017
}
9851018

9861019
/// Parses a type bound according to:
9871020
/// ```ebnf
9881021
/// TY_BOUND = TY_BOUND_NOPAREN | (TY_BOUND_NOPAREN)
989-
/// TY_BOUND_NOPAREN = [TRAIT_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
1022+
/// TY_BOUND_NOPAREN = [for<GENERIC_PARAMS> CONSTNESS ASYNCNESS | POLARITY] SIMPLE_PATH
9901023
/// ```
9911024
///
9921025
/// For example, this grammar accepts `for<'a: 'b> ~const ?m::Trait<'a>`.
@@ -996,16 +1029,37 @@ impl<'a> Parser<'a> {
9961029
has_parens: bool,
9971030
leading_token: &Token,
9981031
) -> PResult<'a, GenericBound> {
999-
let modifiers = self.parse_trait_bound_modifiers()?;
10001032
let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?;
10011033

1034+
let modifiers_lo = self.token.span;
1035+
let modifiers = self.parse_trait_bound_modifiers()?;
1036+
let modifiers_span = modifiers_lo.to(self.prev_token.span);
1037+
1038+
if let Some(binder_span) = binder_span {
1039+
match modifiers.polarity {
1040+
BoundPolarity::Negative(polarity_span) | BoundPolarity::Maybe(polarity_span) => {
1041+
self.dcx().emit_err(errors::BinderAndPolarity {
1042+
binder_span,
1043+
polarity_span,
1044+
polarity: modifiers.polarity.as_str(),
1045+
});
1046+
}
1047+
BoundPolarity::Positive => {}
1048+
}
1049+
}
1050+
10021051
// Recover erroneous lifetime bound with modifiers or binder.
10031052
// e.g. `T: for<'a> 'a` or `T: ~const 'a`.
10041053
if self.token.is_lifetime() {
10051054
let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span);
10061055
return self.parse_generic_lt_bound(lo, has_parens);
10071056
}
10081057

1058+
if let (more_lifetime_defs, Some(binder_span)) = self.parse_late_bound_lifetime_defs()? {
1059+
lifetime_defs.extend(more_lifetime_defs);
1060+
self.dcx().emit_err(errors::BinderBeforeModifiers { binder_span, modifiers_span });
1061+
}
1062+
10091063
let mut path = if self.token.is_keyword(kw::Fn)
10101064
&& self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
10111065
&& let Some(path) = self.recover_path_from_fn()

src/tools/rustfmt/tests/source/type.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ trait T: ~ const Super {}
146146

147147
const fn not_quite_const<S: ~ const T>() -> i32 { <S as T>::CONST }
148148

149-
struct S<T:~ const ? Sized>(std::marker::PhantomData<T>);
150-
151149
impl ~ const T {}
152150

153151
fn apit(_: impl ~ const T) {}

src/tools/rustfmt/tests/target/negative-bounds.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,3 @@ where
33
i32: !Copy,
44
{
55
}
6-
7-
fn maybe_const_negative()
8-
where
9-
i32: ~const !Copy,
10-
{
11-
}

src/tools/rustfmt/tests/target/type.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,6 @@ const fn not_quite_const<S: ~const T>() -> i32 {
153153
<S as T>::CONST
154154
}
155155

156-
struct S<T: ~const ?Sized>(std::marker::PhantomData<T>);
157-
158156
impl ~const T {}
159157

160158
fn apit(_: impl ~const T) {}

src/tools/tidy/src/ui_tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::path::{Path, PathBuf};
1616
const ENTRY_LIMIT: u32 = 901;
1717
// FIXME: The following limits should be reduced eventually.
1818

19-
const ISSUES_ENTRY_LIMIT: u32 = 1672;
19+
const ISSUES_ENTRY_LIMIT: u32 = 1673;
2020

2121
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
2222
"rs", // test source files

0 commit comments

Comments
 (0)