Skip to content

Change keyword order for impl restrictions#155442

Open
CoCo-Japan-pan wants to merge 5 commits intorust-lang:mainfrom
CoCo-Japan-pan:impl-restriction-reorder
Open

Change keyword order for impl restrictions#155442
CoCo-Japan-pan wants to merge 5 commits intorust-lang:mainfrom
CoCo-Japan-pan:impl-restriction-reorder

Conversation

@CoCo-Japan-pan
Copy link
Copy Markdown
Contributor

@CoCo-Japan-pan CoCo-Japan-pan commented Apr 17, 2026

Based on #155222, this PR reorders keywords in trait definitions to group restrictions with visibility. It changes the order from pub(...) const unsafe auto impl(...) trait Foo {...} to pub(...) impl(...) const unsafe auto trait Foo {...}.

Tracking issue for restrictions: #105077

r? @Urgau
cc @jhpratt

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 17, 2026

The parser was modified, potentially altering the grammar of (stable) Rust
which would be a breaking change.

cc @fmease

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 17, 2026
@CoCo-Japan-pan CoCo-Japan-pan force-pushed the impl-restriction-reorder branch from 8e2d081 to 5d955ca Compare April 17, 2026 14:32
@rust-log-analyzer

This comment has been minimized.

@CoCo-Japan-pan CoCo-Japan-pan force-pushed the impl-restriction-reorder branch from 5d955ca to 4178059 Compare April 17, 2026 16:21
@CoCo-Japan-pan
Copy link
Copy Markdown
Contributor Author

The auto keyword was accidentally removed from the set of possible tokens, which caused UI tests to fail.
Added check_keyword(exp!(Auto)) in check_trait_front_matter to fix this.
Also removed 2b39bea.

@rust-log-analyzer

This comment has been minimized.

Comment thread tests/ui/macros/stringify.rs
Comment on lines 9 to +13
pub impl(crate::foo) trait Baz {} //~ ERROR incorrect `impl` restriction
//[without_gate]~^ ERROR `impl` restrictions are experimental
pub unsafe impl(crate::foo) trait BazUnsafe {} //~ ERROR incorrect `impl` restriction
pub impl(crate::foo) unsafe trait BazUnsafe {} //~ ERROR incorrect `impl` restriction
//[without_gate]~^ ERROR `impl` restrictions are experimental
pub auto impl(crate::foo) trait BazAuto {} //~ ERROR incorrect `impl` restriction
pub impl(crate::foo) auto trait BazAuto {} //~ ERROR incorrect `impl` restriction
Copy link
Copy Markdown
Member

@Urgau Urgau Apr 17, 2026

Choose a reason for hiding this comment

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

I suspect users will be confused about the positioning of the impl(...).

Could add a test with some invalid impl(...) positions and add a fixme in it to improve the recovery and diagnostics.

View changes since the review

Comment thread compiler/rustc_parse/src/parser/item.rs
Comment on lines +1203 to +1204
/// Returns whether any of the given keywords are `dist` tokens ahead over the current token tree.
pub(crate) fn is_keyword_tree_ahead(&self, dist: usize, kws: &[Symbol]) -> bool {
Copy link
Copy Markdown
Member

@Urgau Urgau Apr 17, 2026

Choose a reason for hiding this comment

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

I'm not sure I understand why we need this function. Can't we keep using is_keyword_ahead? or look_ahead?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is because the path in impl(in? path) can be arbitrarily long, and we need to parse it to determine how many tokens to look ahead. To handle this, we treat the (in? path) portion as a TokenTree::Delimited, so we look ahead over token trees rather than tokens.
For this reason, I have not added an explicit check for CloseParen at the moment. #155442 (comment) However, it might make sense to instead check that the Delimiter is Parenthesis.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see thanks, that makes sense.

Would be good if you could add a comment explaining that in code, other people might wonder the same thing as me.

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 18, 2026

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

Some changes occurred in src/tools/rustfmt

cc @rust-lang/rustfmt

@rustbot rustbot added T-clippy Relevant to the Clippy team. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue. labels Apr 18, 2026
@CoCo-Japan-pan CoCo-Japan-pan force-pushed the impl-restriction-reorder branch from b7bec21 to 646fa9a Compare April 18, 2026 16:06
@CoCo-Japan-pan CoCo-Japan-pan force-pushed the impl-restriction-reorder branch from 646fa9a to 78cb850 Compare April 18, 2026 16:22
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 18, 2026

This PR was rebased onto a different main 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.

Comment on lines +1057 to 1078
// `impl(`
if self.check_keyword(exp!(Impl)) && self.look_ahead(1, |t| t == &token::OpenParen) {
// `impl(..) trait`
self.is_keyword_tree_ahead(2, &[kw::Trait]) ||
// `impl(..) [const | unsafe | auto] trait`
(self.is_keyword_tree_ahead(2, &[kw::Const, kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(3, &[kw::Trait])) ||
// `impl(..) const [unsafe | auto] trait`
(self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) ||
// `impl(..) unsafe auto trait`
(self.is_keyword_tree_ahead(2, &[kw::Unsafe]) && self.is_keyword_tree_ahead(3, &[kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) ||
// `impl(..) const unsafe auto trait`
(self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe]) && self.is_keyword_tree_ahead(4, &[kw::Auto]) && self.is_keyword_tree_ahead(5, &[kw::Trait]))
} else {
// `trait`
self.check_keyword(exp!(Trait)) ||
// `auto trait`
self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait]) ||
// `unsafe auto? trait`
self.check_keyword(exp!(Unsafe)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait])) ||
// `const unsafe? auto? trait`
self.check_keyword(exp!(Const)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe, kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Auto]) && self.is_keyword_ahead(3, &[kw::Trait]))
}
Copy link
Copy Markdown
Member

@Urgau Urgau Apr 18, 2026

Choose a reason for hiding this comment

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

I don't this blob of code, it seems brittle and very easy to miss a case. (like impl(..) auto trait?)

I wonder if we could be cleaver enumerate all the combinations ahead and exhaustively check them.

I'm thinking of something like that. (I have not tested it, it's
Rust-ish code)

Suggested change
// `impl(`
if self.check_keyword(exp!(Impl)) && self.look_ahead(1, |t| t == &token::OpenParen) {
// `impl(..) trait`
self.is_keyword_tree_ahead(2, &[kw::Trait]) ||
// `impl(..) [const | unsafe | auto] trait`
(self.is_keyword_tree_ahead(2, &[kw::Const, kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(3, &[kw::Trait])) ||
// `impl(..) const [unsafe | auto] trait`
(self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe, kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) ||
// `impl(..) unsafe auto trait`
(self.is_keyword_tree_ahead(2, &[kw::Unsafe]) && self.is_keyword_tree_ahead(3, &[kw::Auto]) && self.is_keyword_tree_ahead(4, &[kw::Trait])) ||
// `impl(..) const unsafe auto trait`
(self.is_keyword_tree_ahead(2, &[kw::Const]) && self.is_keyword_tree_ahead(3, &[kw::Unsafe]) && self.is_keyword_tree_ahead(4, &[kw::Auto]) && self.is_keyword_tree_ahead(5, &[kw::Trait]))
} else {
// `trait`
self.check_keyword(exp!(Trait)) ||
// `auto trait`
self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait]) ||
// `unsafe auto? trait`
self.check_keyword(exp!(Unsafe)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait])) ||
// `const unsafe? auto? trait`
self.check_keyword(exp!(Const)) && (self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe, kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Auto]) && self.is_keyword_ahead(3, &[kw::Trait]))
}
const SUFFIXES: &[&[kw::Keyword]] = &[
&[kw::Trait],
&[kw::Auto, kw::Trait],
&[kw::Unsafe, kw::Trait],
&[kw::Unsafe, kw::Auto, kw::Trait],
&[kw::Const, kw::Trait],
&[kw::Const, kw::Auto, kw::Trait],
&[kw::Const, kw::Unsafe, kw::Trait],
&[kw::Const, kw::Unsafe, kw::Auto, kw::Trait],
];
// `impl(`
if self.check_keyword(exp!(Impl)) && self.look_ahead(1, |t| t == &token::OpenParen) {
SUFFIXES.iter().enumerate().any(|(idx, prefix)| self.is_keyword_tree_ahead(idx + 2, prefix));
} else {
SUFFIXES.iter().enumerate().any(|(idx, prefix)| self.is_keyword_ahead(idx, prefix));
}
}

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: Awaiting review from the assignee but also interested parties. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants