Skip to content
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

Merged
merged 7 commits into from Jul 13, 2016

Conversation

Projects
None yet
4 participants
@jseyfried
Copy link
Contributor

jseyfried commented Jul 5, 2016

Fixes #34543.

This is a [breaking-change].
The following would break be a warning (c.f. #34660 (comment)):

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

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Jul 5, 2016

Starting a crater run.

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 5, 2016

@eddyb Thanks!

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 5, 2016

cc @nrc

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Jul 5, 2016

Crater report claims 42 root regressions (87 total).
However, most of those seem to be caused by a single nom macro.

@jseyfried Apparently this change causes type-checking to find a mismatch between some type and ().
I wonder if it accidentally produces StmtSemi where it shouldn't.

@jseyfried jseyfried force-pushed the jseyfried:fix_parse_stmt branch from 9dbe21b to e94e014 Jul 5, 2016

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 5, 2016

@eddyb
That regression was caused by f639fc3.
The underlying issue is that trailing semicolons are ignored after expansions in expression positions:

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.

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Jul 5, 2016

@jseyfried Sheesh, that's awful. I'll start a second crater run.

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 5, 2016

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

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 5, 2016

@nrc

Would it be possible to turn this into a warning for a cycle?

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 let statements from lacking semicolons:

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 rustc it seems likely to cause significant breakage in practice).

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Jul 5, 2016

Crater report has 7 confirmed root regressions (13 total), most of which are:

expected expression, found ``

@jseyfried jseyfried force-pushed the jseyfried:fix_parse_stmt branch from 30ea84e to b2b4dac Jul 5, 2016

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 5, 2016

The above commit should fix the remaining breakage.

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 6, 2016

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.

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 6, 2016

@nrc
Ok, I can do a warning cycle for macro-expanded let statements that lack semicolons.
Should this warning apply to final let statements? E.x.

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.

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 6, 2016

@nrc Actually, not all remaining potential breakage is warnable.
For example, this compiles today but doesn't compile after this PR:

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.

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 6, 2016

@nrc @eddyb
After my most recent commit, this PR can only break macros that expand into multiple statements.

All the breakage so far has been due to macros that expand into one statement / expression.
Macros that expand into multiple statements appear to be far less common in practice, so I believe this PR is highly unlikely to cause breakage in the wild (consistent with the lack crater-detected breakage).

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 7, 2016

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 let without a semi-colon example should be an error.

@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

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 7, 2016

Nominating for discussion due to potential breakage.

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 7, 2016

un-nominating since this PR is turning into more of a bug fix and will avoid breakage after all.

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 7, 2016

@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, _> {

This comment has been minimized.

@nrc

nrc Jul 7, 2016

Member

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?

This comment has been minimized.

@jseyfried

jseyfried Jul 7, 2016

Author Contributor

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.

This comment has been minimized.

@jseyfried

jseyfried Jul 7, 2016

Author Contributor

Done.

span: span
});
}
/// Finish parsing expressions that start with macros and handle trailing semicolons

This comment has been minimized.

@nrc

nrc Jul 7, 2016

Member

Should "expressions" be "statements" here?

This comment has been minimized.

@jseyfried

jseyfried Jul 7, 2016

Author Contributor

Could be either, I'll change it to "expression statements" to be most precise.

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 7, 2016

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.

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 7, 2016

@nrc
The cargo-clippy breakage appears to be spurious. The breakage has nothing to do with semicolons or macros, I couldn't reproduce it locally, and it doesn't break on the second crater run, which should have caused strictly more breakage.

The clocked-dispatch breakage is caused by this (let statements lacking semicolons). This is easy to warn.

The Inflector breakage is caused by this (non-braced macro invocations lacking semicolons). With the exception of the corner case I described here, this is also feasible to warn.
We would warn when a non-braced macro invocation is followed by something that can start an expression but can't continue an expression. The corner case is a problem because - can start an expression or continue an expression.

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Jul 8, 2016

☔️ The latest upstream changes (presumably #34720) made this pull request unmergeable. Please resolve the merge conflicts.

@jseyfried jseyfried force-pushed the jseyfried:fix_parse_stmt branch from 1ed1085 to bf3cb4b Jul 12, 2016

@jseyfried

This comment has been minimized.

Copy link
Contributor Author

jseyfried commented Jul 12, 2016

@nrc I rebased and started a best-effort warning cycle.

@jseyfried jseyfried force-pushed the jseyfried:fix_parse_stmt branch from bf3cb4b to f789bc0 Jul 12, 2016

jseyfried added some commits Jul 5, 2016

Allow macro-expanded macros in trailing expression positions to expan…
…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.

@jseyfried jseyfried force-pushed the jseyfried:fix_parse_stmt branch from f789bc0 to 57fac56 Jul 13, 2016

@nrc

This comment has been minimized.

Copy link
Member

nrc commented Jul 13, 2016

@bors: r+

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Jul 13, 2016

📌 Commit 57fac56 has been approved by nrc

@bors

This comment has been minimized.

Copy link
Contributor

bors commented Jul 13, 2016

⌛️ Testing commit 57fac56 with merge 2ab18ce...

bors added a commit that referenced this pull request Jul 13, 2016

Auto merge of #34660 - jseyfried:fix_parse_stmt, r=nrc
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

@bors bors merged commit 57fac56 into rust-lang:master Jul 13, 2016

2 checks passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details

whatisinternet added a commit to whatisinternet/Inflector that referenced this pull request Jul 27, 2016

Fixed macro issue outlined here: rust-lang/rust#34660 (comment)
This is only a point release because of the minor change.

Also updated regex dependency which is also only a point change.

@whatisinternet whatisinternet referenced this pull request Jul 27, 2016

Merged

Chore/stable fixes #24

@jseyfried jseyfried deleted the jseyfried:fix_parse_stmt branch Sep 12, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.