Skip to content

Commit cfc5f25

Browse files
authored
Rollup merge of #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

20 files changed

+201
-68
lines changed

compiler/rustc_ast_passes/messages.ftl

-2
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

-11
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

-9
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

+9
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

+30
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

+57-3
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

-2
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

-6
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

-2
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

+1-1
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

tests/ui/async-await/async-fn/higher-ranked-async-fn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async fn f(arg: &i32) {}
1515

1616
async fn func<F>(f: F)
1717
where
18-
F: async for<'a> Fn(&'a i32),
18+
F: for<'a> async Fn(&'a i32),
1919
{
2020
let x: i32 = 0;
2121
f(&x).await;

tests/ui/impl-trait/normalize-tait-in-const.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ mod foo {
2424
}
2525
use foo::*;
2626

27-
const fn with_positive<F: ~const for<'a> Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
27+
const fn with_positive<F: for<'a> ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
2828
fun(filter_positive());
2929
}
3030

tests/ui/impl-trait/normalize-tait-in-const.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
error: `~const` can only be applied to `#[const_trait]` traits
22
--> $DIR/normalize-tait-in-const.rs:27:42
33
|
4-
LL | const fn with_positive<F: ~const for<'a> Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
4+
LL | const fn with_positive<F: for<'a> ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
55
| ^^^^^^^^^^^^^^^^^
66

77
error: `~const` can only be applied to `#[const_trait]` traits
88
--> $DIR/normalize-tait-in-const.rs:27:69
99
|
10-
LL | const fn with_positive<F: ~const for<'a> Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
10+
LL | const fn with_positive<F: for<'a> ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
1111
| ^^^^^^^^
1212

1313
error[E0015]: cannot call non-const closure in constant functions
@@ -19,7 +19,7 @@ LL | fun(filter_positive());
1919
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
2020
help: consider further restricting this bound
2121
|
22-
LL | const fn with_positive<F: ~const for<'a> Fn(&'a Alias<'a>) + ~const Destruct + ~const Fn(&foo::Alias<'_>)>(fun: F) {
22+
LL | const fn with_positive<F: for<'a> ~const Fn(&'a Alias<'a>) + ~const Destruct + ~const Fn(&foo::Alias<'_>)>(fun: F) {
2323
| ++++++++++++++++++++++++++++
2424
help: add `#![feature(effects)]` to the crate attributes to enable
2525
|
@@ -29,7 +29,7 @@ LL + #![feature(effects)]
2929
error[E0493]: destructor of `F` cannot be evaluated at compile-time
3030
--> $DIR/normalize-tait-in-const.rs:27:79
3131
|
32-
LL | const fn with_positive<F: ~const for<'a> Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
32+
LL | const fn with_positive<F: for<'a> ~const Fn(&'a Alias<'a>) + ~const Destruct>(fun: F) {
3333
| ^^^ the destructor for this type cannot be evaluated in constant functions
3434
LL | fun(filter_positive());
3535
LL | }

tests/ui/issues/issue-39089.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//@ check-pass
2-
#![allow(dead_code)]
31
fn f<T: ?for<'a> Sized>() {}
2+
//~^ ERROR `for<...>` binder should be placed before trait bound modifiers
43

54
fn main() {}

tests/ui/issues/issue-39089.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: `for<...>` binder should be placed before trait bound modifiers
2+
--> $DIR/issue-39089.rs:1:13
3+
|
4+
LL | fn f<T: ?for<'a> Sized>() {}
5+
| - ^^^^
6+
| |
7+
| place the `for<...>` binder before any modifiers
8+
9+
error: aborting due to 1 previous error
10+

tests/ui/parser/bounds-type.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
//@ compile-flags: -Z parse-only
2+
//@ edition: 2021
23

34
struct S<
45
T: 'a + Tr, // OK
56
T: Tr + 'a, // OK
67
T: 'a, // OK
78
T:, // OK
8-
T: ?for<'a> Trait, // OK
9+
T: for<'a> ?Trait, //~ ERROR `for<...>` binder not allowed with `?` trait polarity modifier
910
T: Tr +, // OK
1011
T: ?'a, //~ ERROR `?` may only modify trait bounds, not lifetime bounds
1112

1213
T: ~const Tr, // OK
13-
T: ~const ?Tr, // OK
14+
T: ~const ?Tr, //~ ERROR `~const` trait not allowed with `?` trait polarity modifier
1415
T: ~const Tr + 'a, // OK
1516
T: ~const 'a, //~ ERROR `~const` may only modify trait bounds, not lifetime bounds
1617
T: const 'a, //~ ERROR `const` may only modify trait bounds, not lifetime bounds
18+
19+
T: async Tr, // OK
20+
T: async ?Tr, //~ ERROR `async` trait not allowed with `?` trait polarity modifier
21+
T: async Tr + 'a, // OK
22+
T: async 'a, //~ ERROR `async` may only modify trait bounds, not lifetime bounds
23+
24+
T: const async Tr, // OK
25+
T: const async ?Tr, //~ ERROR `const async` trait not allowed with `?` trait polarity modifier
26+
T: const async Tr + 'a, // OK
27+
T: const async 'a, //~ ERROR `const` may only modify trait bounds, not lifetime bounds
1728
>;
1829

1930
fn main() {}

0 commit comments

Comments
 (0)