-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bugs in macro-expanded statement parsing #34660
Conversation
Starting a crater run. |
@eddyb Thanks! |
cc @nrc |
Crater report claims 42 root regressions (87 total). @jseyfried Apparently this change causes type-checking to find a mismatch between some type and |
@eddyb macro_rules! m { () => { 2; } }
fn f() -> i32 {
let _: i32 = m!(); // The semicolon after `2` is ignored, so this is typechecks.
m!() // Before f639fc3, this invocation is considered to be in an expression position,
// so the semicolon after the `2` is ignored and this typechecks.
//
// After f639fc3, the invocation is considered to be in a statement position, so
// the semicolon is not ignored and it expands into a `StmtKind::Semi` statement,
// which doesn't typecheck.
} I removed f639fc3, which fixed the breakage. However, removing f639fc3 also means that macro_rules! n { () => {
let _ = ();
m!() // this is no longer considered to be in a statement position like it was
// before this PR, which could potentially lead to more breakage.
}
fn main() { n!(); } If this causes breakage in practice, I could treat only macro-expanded macros in trailing expression positions as statements. |
@jseyfried Sheesh, that's awful. I'll start a second crater run. |
Hmm, this seems like a bug fix and therefore something we should land even if it does break code. Would it be possible to turn this into a warning for a cycle? (Doesn't seem easy to me). Otherwise we should submit a PR to nom and fix that bustage (if possible) before landing this. cc @rust-lang/compiler |
I think the only change we can't phase in with a warning cycle is making macros in trailing expression statement positions expand into statements, not expressions (i.e. f639fc3). While I prefer this change, it's not super important and I had only included it here to reduce breakage (which backfired). I'd like to land as much as we can in this PR without causing breakage in practice. After that, we can do a PR that adds a warning cycle for the rest. If @eddyb's crater run comes back clean, all that will remain is to forbid trailing macro-expanded macro_rules m! { () => {
let x = 0 //< This PR will not affect trailing let statements, i.e. this will still be allowed.
}}
fn main() { m!(); } If we want to, it would be easy to start a warning cycle for this (we haven't cratered it yet, but based on the fallout in |
Crater report has 7 confirmed root regressions (13 total), most of which are: expected expression, found `` |
The above commit should fix the remaining breakage. |
even if crater comes back clean, there might still be breakage out there in projects or platforms not covered by crater. It might be worth having a warning cycle for any errors one might reasonably expect to occur. |
@nrc macro_rules! m { () => { let x = 0 } }
fn main() {
m!(); // Is this OK? I think not, but it might be too common in the wild
// to include in the warning cycle -- we haven't cratered it yet.
} The rest of the potential breakage comes from non-braced macro invocation expression statements requiring trailing semicolons (except at the end of a block). Warning for this would be significantly more involved, but is doable. |
@nrc Actually, not all remaining potential breakage is warnable. macro_rules! m { () => { () } }
macro_rules! n {
() => { m!() - 1 } // This is treated like "m!(); -1" today
}
fn main() { n!(); } and this doesn't compile today but does compile after this PR: macro_rules! m { () => { 2 } } //< The only difference is that the `()` is changed to a `2`
macro_rules! n {
() => { m!() - 1 }
}
fn main() { n!(); } Since we can't distinguish these cases until type-checking, a complete warning cycle is infeasible. |
@nrc @eddyb All the breakage so far has been due to macros that expand into one statement / expression. |
So, regarding what to warn on, I think we should do the right thing and warn wherever possible. We shouldn't preserve incorrect behaviour for the sake of preventing breakage, unless there is an overwhelming number of uses. So, for example, the @jseyfried I'm not sure I understand the second more complex examples - could you explain the rules that the macro system will be using with this PR? cc @rust-lang/compiler @rust-lang/lang |
Nominating for discussion due to potential breakage. |
un-nominating since this PR is turning into more of a bug fix and will avoid breakage after all. |
@eddyb: could you do a Crater run with this version of the PR please? |
@@ -117,12 +117,18 @@ impl<'a> MacResult for ParserAnyMacro<'a> { | |||
|
|||
fn make_stmts(self: Box<ParserAnyMacro<'a>>) | |||
-> Option<SmallVector<ast::Stmt>> { | |||
let parse_stmt = |parser: &mut Parser<'a>| -> ::parse::PResult<'a, _> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you comment on what this closure does (i.e., why you don't just use parser.parse_stmt)?
Could it be a function rather than a closure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I'll replace the method finish_parsing_statement
with something like parse_full_stmt
and use that here instead of defining a closure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Discussed at the compiler meeting, if it is possible, we would like to do a warning cycle here. During which time, we could also submit PRs to the broken crates. @jseyfried how hard would it be to do a warning cycle for the current PR? cc @Manishearth - Clippy is one of the crates with breakage here, I expect it is an easy fix, it should just need adding a semicolon or two to a macro somewhere. |
@nrc The The |
☔ The latest upstream changes (presumably #34720) made this pull request unmergeable. Please resolve the merge conflicts. |
1ed1085
to
bf3cb4b
Compare
@nrc I rebased and started a best-effort warning cycle. |
bf3cb4b
to
f789bc0
Compare
…d into statements: ```rust macro_rules! m { () => { let x = 1; x } } macro_rules! n { () => { m!() //< This can now expand into statements }} fn main() { n!(); } ``` and revert needless fallout fixes.
f789bc0
to
57fac56
Compare
@bors: r+ |
📌 Commit 57fac56 has been approved by |
Fix bugs in macro-expanded statement parsing Fixes #34543. This is a [breaking-change]. For example, the following would break: ```rust macro_rules! m { () => { println!("") println!("") //^ Semicolons are now required on macro-expanded non-braced macro invocations //| in statement positions. let x = 0 //^ Semicolons are now required on macro-expanded `let` statements //| that are followed by more statements, so this would break. let y = 0 //< (this would still be allowed to reduce breakage in the wild) } fn main() { m!() } ``` r? @eddyb
This is only a point release because of the minor change. Also updated regex dependency which is also only a point change.
Fixes #34543.
This is a [breaking-change].
The following would
breakbe a warning (c.f. #34660 (comment)):r? @eddyb