Skip to content

Fix grammar rules containing or pertaining to bounds#2257

Open
fmease wants to merge 4 commits into
rust-lang:masterfrom
fmease:fix-bounds-grammars
Open

Fix grammar rules containing or pertaining to bounds#2257
fmease wants to merge 4 commits into
rust-lang:masterfrom
fmease:fix-bounds-grammars

Conversation

@fmease
Copy link
Copy Markdown
Member

@fmease fmease commented May 5, 2026

At least since PR rust-lang/rust#39158 (2017) bounds are intentionally always optional after "bound heralds" (:, impl, dyn). However, the Reference didn't reflect this fact everywhere. This PR rectifies this.

For example reference@master considers the following snippets to be syntactically ill-formed which directly contradicts rustc:

  • items type T: ;, type T: where;, type T: = U;
  • type paths T<U: >, T<U<V>: > (as briefly mentioned in PR Fix the grammar of generic arguments #2247)
  • types impl, fn() -> impl
  • types dyn, fn() -> dyn
  • struct T<'a:> where 'a:;

Furthermore, the edition disclaimer for trait object types is incomplete / imprecise / inaccurate:

It states that token sequence dyn:: will be interpreted as the start of a path in Rust 2015 which is correct in isolation but far from complete in context. Per argumentum e contrario, it would wrongly imply that in Rust 2015 the following snippets all contain (bare) trait object types:

  • type T = dyn;, type T = (dyn);, type T = [dyn];
  • type T = dyn<>;, type T = dyn<()>;, type T = dyn<<T>::S>;

To address this, I've changed the note to use an exhaustive and positive listing of tokens. The follow set is {PathIdentSegment, LIFETIME_OR_LABEL, for, (, ?} as per rustc's can_begin_dyn_bound_in_edition_2015.

@rustbot rustbot added the S-waiting-on-review Status: The marked PR is awaiting review from a maintainer label May 5, 2026
Comment thread src/types/trait-object.md Outdated
r[type.trait-object.syntax]
```grammar,types
TraitObjectType -> `dyn`? TypeParamBounds
TraitObjectType -> TypeParamBounds | `dyn` TypeParamBounds?
Copy link
Copy Markdown
Member Author

@fmease fmease May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to this grammar rule, TypeParamBounds (now: Bounds) must remain non-empty.

View changes since the review

@fmease fmease force-pushed the fix-bounds-grammars branch from 4200b2e to 7b807ac Compare May 5, 2026 10:25
Comment thread src/types/trait-object.md Outdated
@rustbot

This comment has been minimized.

@fmease fmease force-pushed the fix-bounds-grammars branch from 7b807ac to e8c2309 Compare May 6, 2026 13:38
@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@fmease fmease force-pushed the fix-bounds-grammars branch from e8c2309 to 9f6b416 Compare May 11, 2026 21:21
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 11, 2026

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Copy link
Copy Markdown
Contributor

@ehuss ehuss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks!

View changes since this review

Comment thread src/types/impl-trait.md
r[type.impl-trait.syntax]
```grammar,types
ImplTraitType -> `impl` Bounds
ImplTraitType -> `impl` Bounds?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a rule somewhere in this chapter that specifies at least one trait must be listed in the bounds? I believe this corresponds to the AtLeastOneTrait error.

Comment thread src/types/trait-object.md
r[type.trait-object.syntax]
```grammar,types
TraitObjectType -> `dyn`? Bounds
TraitObjectType -> Bounds | `dyn` Bounds?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to my other comment, can we have a rule to say at least one trait is required?

I'm not sure if it makes sense to combine that into type.trait-object.constraint or to have a separate rule just below that.

Comment thread src/types/trait-object.md Outdated
r[type.trait-object.syntax-edition2018]
> [!EDITION-2018]
> In the 2015 edition, if the first bound of the trait object is a path that starts with `::`, then the `dyn` will be treated as a part of the path. The first path can be put in parenthesis to get around this. As such, if you want a trait object with the trait `::your_module::Trait`, you should write it as `dyn (::your_module::Trait)`.
> In the 2015 edition, `dyn` must be followed by [PathIdentSegment][grammar-PathIdentSegment], [LIFETIME_OR_LABEL][grammar-LIFETIME_OR_LABEL], `for`, `(` or `?` to be interpreted as the start of a trait object type. Otherwise, it will be interpreted as a regular identifier.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link destinations aren't required here, since definitions for the rule names are generated automatically.

Suggested change
> In the 2015 edition, `dyn` must be followed by [PathIdentSegment][grammar-PathIdentSegment], [LIFETIME_OR_LABEL][grammar-LIFETIME_OR_LABEL], `for`, `(` or `?` to be interpreted as the start of a trait object type. Otherwise, it will be interpreted as a regular identifier.
> In the 2015 edition, `dyn` must be followed by [PathIdentSegment], [LIFETIME_OR_LABEL], `for`, `(` or `?` to be interpreted as the start of a trait object type. Otherwise, it will be interpreted as a regular identifier.

@fmease fmease force-pushed the fix-bounds-grammars branch from d43e9b5 to 5fd453f Compare May 27, 2026 12:22
Comment thread src/types/trait-object.md

r[type.trait-object.name]
Trait objects are written as the keyword `dyn` followed by a set of trait bounds, but with the following restrictions on the trait bounds.
Trait object types are written as the keyword `dyn` followed by a set of bounds, but with the following restrictions on the bounds:
Copy link
Copy Markdown
Member Author

@fmease fmease May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

followed by a set of […] bounds

(preexisting) Is set generally to be understood as a mathematical set in the Reference? Is it defined anywhere? Right now, it's unclear to me if this sentence makes any statements about duplicate bounds. As you know, dyn Trait + 'a + 'a and dyn NonAutoTrait + NonAutoTrait are illegal and dyn Sync + Sync is legal...

View changes since the review

Comment thread src/types/impl-trait.md
fn bar() -> impl Trait {
}
```

Copy link
Copy Markdown
Member Author

@fmease fmease May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm mirroring the structure of the section about trait object types here with the name vs. constraint split. I'm definitely not married to it. I actually find the rule name name a bit confusing and I feel like the rule sort of mixes syntax and semantics (just like its preexisting sibling for trait object types).

View changes since the review

Comment thread src/types/impl-trait.md
`impl Trait` types are written as the keyword `impl` followed by a set of bounds, but with the following restrictions on the bounds:

r[type.impl-trait.constraint]
There must be at least one trait bound, no more than one `use<..>` bound, and no more than one opt-out bound (e.g. `?Sized`).
Copy link
Copy Markdown
Member Author

@fmease fmease May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no more than one use<...> bound

This is already mentioned in rule type.impl-trait.generic-capture.precise.constraint-single but it feels logical to mentioned it here to be exhaustive.

View changes since the review

Comment thread src/types/impl-trait.md
`impl Trait` types are written as the keyword `impl` followed by a set of bounds, but with the following restrictions on the bounds:

r[type.impl-trait.constraint]
There must be at least one trait bound, no more than one `use<..>` bound, and no more than one opt-out bound (e.g. `?Sized`).
Copy link
Copy Markdown
Member Author

@fmease fmease May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no more than one opt-out bound (e.g. ?Sized)

Copying the terminology from the trait object section. Personally speaking I call them relaxed bounds and that's what the compiler calls them now everywhere, too (it used to call them relaxed bounds, maybe bounds and unbounds).

Strictly speaking, duplicate relaxed bounds are forbidden but on stable that doesn't make a difference since there's only one trait that can be relaxed (Sized).

It felt natural to mention this here but of course, relaxed bounds (and use<..> bounds) come with a lot more semantic restrictions (specifically when it comes to legal locations) but the Reference doesn't have a central place to talk about them1.

View changes since the review

Footnotes

  1. E.g., for the longest time ?Trait was allowed even for Trait != Sized but two years ago or so that was finally made an error but the Reference was never updated.

@fmease fmease force-pushed the fix-bounds-grammars branch from 5fd453f to 1a54708 Compare May 27, 2026 13:17
Comment thread src/types/trait-object.md

r[type.trait-object.constraint]
There may not be more than one non-auto trait, no more than one lifetime, and opt-out bounds (e.g. `?Sized`) are not allowed. Furthermore, paths to traits may be parenthesized.
r[type.trait-object.bounds]
Copy link
Copy Markdown
Member Author

@fmease fmease May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it to bounds from constraint since the corresponding rule for impl-Trait had to be renamed to bounds not to conflict with the preexisting types.impl-trait.constraint.

View changes since the review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: The marked PR is awaiting review from a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants