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

Explain editions when trying to use async block in 2015 edition #67204

Closed
estebank opened this issue Dec 10, 2019 · 17 comments · Fixed by #71783
Closed

Explain editions when trying to use async block in 2015 edition #67204

estebank opened this issue Dec 10, 2019 · 17 comments · Fixed by #71783
Assignees
Labels
A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints A-parser Area: The parsing of Rust source code to an AST A-resolve Area: Name/path resolution done by `rustc_resolve` specifically AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. D-edition Diagnostics: An error or lint that should account for edition differences. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@estebank
Copy link
Contributor

estebank commented Dec 10, 2019

Trying to use an async block in 2015 edition doesn't mention the concept of editions nor pushes the user in the right direction:

error: expected one of `,` or `}`, found `(`
  --> file12.rs:11:18
   |
10 |     let fut = async {
   |               ----- while parsing this struct
11 |         make_unit()?;
   |                  ^ expected one of `,` or `}`

error[E0422]: cannot find struct, variant or union type `async` in this scope
  --> file12.rs:10:15
   |
10 |     let fut = async {
   |               ^^^^^ not found in this scope
@estebank estebank added A-diagnostics Area: Messages for errors, warnings, and lints A-parser Area: The parsing of Rust source code to an AST T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. D-papercut Diagnostics: An error or lint that needs small tweaks. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-edition Diagnostics: An error or lint that should account for edition differences. A-async-await Area: Async & Await labels Dec 10, 2019
@Centril
Copy link
Contributor

Centril commented Dec 11, 2019

Can you provide the full input used to produce ^--?

@csmoe
Copy link
Member

csmoe commented Dec 11, 2019

fn foo() {
    let x = async {};
}
error[E0422]: cannot find struct, variant or union type `async` in this scope
 --> src/main.rs:3:12
  |
3 |    let x = async {}; 
  |            ^^^^^ not found in this scope

error: aborting due to previous error

For more information about this error, try `rustc --explain E0422`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

playground

@Centril
Copy link
Contributor

Centril commented Dec 11, 2019

That doesn't include:

error: expected one of , or }, found (

What does the parser error come from?

@Centril Centril added the A-resolve Area: Name/path resolution done by `rustc_resolve` specifically label Dec 11, 2019
@estebank
Copy link
Contributor Author

fn main() {}
fn bar() -> Result<(), ()> {
    panic!()
}

fn foo() -> Result<(), ()> {
   let x = async {
       bar()?;
   }; 
   Ok(())
}

@estebank
Copy link
Contributor Author

For what is worth, I think the parser should use the snapshot approach here when in 2015 mode and try both parses, if one succeeds and the other one fails, give a targeted error.

@Centril
Copy link
Contributor

Centril commented Dec 11, 2019

I think we could just do this in resolve instead of the parser as it seems like the parser is able to recover and we reach resolve.

@estebank
Copy link
Contributor Author

estebank commented Dec 11, 2019

We can, but if we also handle it in the parser we can make the output be

error: `async` blocks are only available in 2018 edition
 --> src/main.rs:7:11
  |
7 |     let x = async {
  | ____________^
8 ||        bar()?;
9 ||    }; 
  ||____^
  |
  = note: edit your `Cargo.toml` file and add `edition = 2018` to the `[package]` section if you wish to use async/await in your crate
  = note: for more information, visit <APPROPRIATE BOOK SECTION>

error: aborting due the previous errors

The reason the parser recovers is because when it finds a parse error it consumes the rest of the block.

@Centril
Copy link
Contributor

Centril commented Dec 12, 2019

I'm skeptical of the additional complexity here in doing it in the parser (which I suspect would be non-trivial, in an already complicated part of the parser). This would also have a perf cost in the happy path due to let snapshot = self.clone(); but in resolve we already know there's an error so no additional cost is paid in the happy path. I would suggest doing the initial work in resolve and then consider whether additional logic in the parser is necessary. At any rate, I would appreciate not landing any such PR while my parser refactoring PRs are unmerged.

@estebank
Copy link
Contributor Author

I would appreciate not landing any such PR while my parser refactoring PRs are unmerged.

Fair.

but in resolve we already know there's an error so no additional cost is paid in the happy path

True, the cost there is verbosity/vertical space, and that is fine, just not ideal.

I think you're overestimating the added complexity on isolation (or you're extrapolating to the addition of all these checks throughout the parser, in which case, fair) and of the size and prevalence of the performance hit. We can check whether the struct ident is async and do our recovery and branching parsing only in that case. I feel the edition difference, although brilliant from a backwards compatibility point of view requires us to do this kind of bending backwards to provide a good experience.

With all of that, this is not necessarily something we need to rush into because

  • This was more critical during early 2019 when 2015 code was more prevalent
  • The case has low likelihood to be hit by a newcomer today (but can still happen)
  • The resolve fix would be enough to guide a user, then this is just a verbosity problem

The first point is something we've gotten better at, mainly extrapolating what are some common errors that will be caused by a new feature and preemptively handling them. Async await for example had some great checks even before hitting beta. We should continue to strive for that level of quality in our output for all new features.

@tmandry tmandry added AsyncAwait-OnDeck AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. labels Dec 17, 2019
@tmandry
Copy link
Member

tmandry commented Dec 17, 2019

Visiting from triage. @estebank would you be willing to mentor this?

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Dec 17, 2019

Can we do something really simple like, in 2015 mode, if we see an error at the point where there is an async keyword, not try to parse ahead "properly" but just report a message about async not being part of Rust 2015 right there? I guess one tricky part might be recovery -- ideally we'd skip the body of the function. But I worry that parsing "for real" might cause us to fail recovery anyway, if the user has more than one error in the code.

I see I was a bit confused

@nikomatsakis
Copy link
Contributor

nikomatsakis commented Dec 17, 2019

If we do this in resolve, does this mean that the output would include both the parse error and then a "nice" resolve error? If so, I think that's not great. Ideally, we'd produce just a nice async error -- but at minimum we should give a nice async error first.

That said, I think we could just look for parse errors that occur when parsing some "struct named async" and trigger the nice error in that case, I'm not quite sure why we have to do anything too smart to read ahead and see if it "really is" an async block. I guess I think that the probability of 2015 code that uses a struct named async is low.

We might be able to thread the needle with some kind of "note" or something to also give the user better info about the parse error.

(However, I also think that parsing it as an async-block would be "ok", presuming that's something we do fairly often -- and maybe my proposal is sort of violating the "Hippocratic oath" of nice errors to "first do no harm". i.e., it could be pretty confusing to a user who does have a struct named async =)

@estebank estebank added the E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. label Dec 17, 2019
@estebank
Copy link
Contributor Author

@nikomatsakis I do think that long term there would be value in consider removing this discrepancy between 2015 and 2019 and make async usable in 2015, which would require deprecate async as a non-keyword in 2015. That being said, I think that the added parser complexity to handle this as you mention should be small (but every little bit adds up).

@tmandry I'm down for it.

@Centril Centril self-assigned this Dec 18, 2019
@Centril Centril removed the E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. label Dec 18, 2019
@JohnTitor JohnTitor added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jan 12, 2020
@tmandry
Copy link
Member

tmandry commented Mar 3, 2020

Ping from triage. @Centril have you made progress on this? If not, do you intend to work on it or should I make it available for someone else to take on?

@Centril
Copy link
Contributor

Centril commented Mar 6, 2020

If e.g., @estebank or anyone else wants to take over implementing this that would be fine. In that case I'll most likely review the PR. I'll probably not get to this in a while.

@estebank
Copy link
Contributor Author

estebank commented Mar 6, 2020

I won't be able to tackle this anytime soon but can help any interested party.

@tmandry tmandry added the E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. label Mar 6, 2020
@tmandry tmandry added the P-medium Medium priority label Mar 6, 2020
@nikomatsakis
Copy link
Contributor

@rustbot assign @estebank

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue May 7, 2020
Detect errors caused by `async` block in 2015 edition

Fix rust-lang#67204.
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue May 7, 2020
Detect errors caused by `async` block in 2015 edition

Fix rust-lang#67204.
@bors bors closed this as completed in f3691ac May 8, 2020
@tmandry tmandry moved this to Done in wg-async work Dec 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await A-diagnostics Area: Messages for errors, warnings, and lints A-parser Area: The parsing of Rust source code to an AST A-resolve Area: Name/path resolution done by `rustc_resolve` specifically AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-enhancement Category: An issue proposing an enhancement or a PR with one. D-edition Diagnostics: An error or lint that should account for edition differences. D-newcomer-roadblock Diagnostics: Confusing error or lint; hard to understand for new users. D-papercut Diagnostics: An error or lint that needs small tweaks. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

6 participants