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

Allow #[attr] if to be passed to proc macros #68618

Closed
Aaron1011 opened this issue Jan 28, 2020 · 5 comments · Fixed by #69201
Closed

Allow #[attr] if to be passed to proc macros #68618

Aaron1011 opened this issue Jan 28, 2020 · 5 comments · Fixed by #69201
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-frontend Area: Compiler frontend (errors, parsing and HIR) A-parser Area: The parsing of Rust source code to an AST C-feature-request Category: A feature request, i.e: not implemented / a PR. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@Aaron1011
Copy link
Member

The following code:

fn main() {
    #[attr] if let Some(_) = Ok(true) {}
}

produces this error:

error: attributes are not yet allowed on `if` expressions
 --> src/main.rs:2:5
  |
2 |     #[attr] if let Some(_) = Ok(true) {}
  |     ^^^^^^^

error: aborting due to previous error

Proc macros like pin-project currently use a trick to work around attributes on expressions being unstable: The entire function is annotated with a 'wrapper' attribute, which goes through and manually parses expression attributes. For example:

#[my_attr]
fn foo() {
    #[my_attr] let a = 25;
}

In the example above, the #[my_attr] let a = 25; will be processed by the outer #[my_attr] fn foo() implementation, which will strip away the expression-based #[my_attr] before emitting the final TokenStream.

Unfortunately, the current 'attributes are not yet allowed on if expressions' check is done during parsing:

self.error_attr_on_if_expr(&expr);

While the proc-macro itself can parse the attribute on the if let without any errors, compilation will still fail due to the parsing error that was emitted before any proc-macros were run.

It would be extremely useful if this check were to be moved until after parsing, so that proc macros had a chance to remove attributes from if expressions.

Aaron1011 added a commit to Aaron1011/pin-project that referenced this issue Jan 28, 2020
This PR would work correctly, except for the fact that
rust-lang/rust#68618 causes a compilation error to be emitted before we
even have a chance to run. That issue is independent of the
implementation of this PR, so this PR should start working automatically
once the issue is resolved.
@Aaron1011 Aaron1011 changed the title Allow #[attr] if let to be passed to proc macros Allow #[attr] if to be passed to proc macros Jan 28, 2020
@Centril
Copy link
Contributor

Centril commented Jan 28, 2020

From a parsing perspective the current restriction is pretty ad-hoc and annoying, as it complicates things, so if there's no good reason for it to stay, I would also like to see it go, and would be happy to review such a PR (including with tests for the parsing, pretty printing, and ideally some local relevant refactoring).

However, to make sure there are no problems, I'd like for @petrochenkov to confirm.

@Centril Centril added A-attributes Area: Attributes (`#[…]`, `#![…]`) A-parser Area: The parsing of Rust source code to an AST C-feature-request Category: A feature request, i.e: not implemented / a PR. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. A-frontend Area: Compiler frontend (errors, parsing and HIR) labels Jan 28, 2020
@ExpHP
Copy link
Contributor

ExpHP commented Jan 29, 2020

Yeah, to me, it seems that allowing the attribute to be present during the proc_macro call is tantamount to declaring that the syntax is legal. So the sensible change would be to make it always legal, rather than only letting it exist prior to macro expansion.

@petrochenkov
Copy link
Contributor

@Centril
Copy link
Contributor

Centril commented Jan 29, 2020

Hmm; seems like the concern was primarily semantic (in terms of macros & lints) as opposed to syntactic?

@Aaron1011
Copy link
Member Author

Aaron1011 commented Jan 29, 2020

So the sensible change would be to make it always legal, rather than only letting it exist prior to macro expansion.

As @Centril and @petrochenkov mentioned, the semantic meaning of such an attribute is ambiguous. However, that's not a problem with accepting it syntactically, since the proc macro processing the function can interpret it however it wants.

I think it would be a good idea to allow 'normal' proc macro attrs on 'if' expressions as well (e.g. without an attribute on the entire function). The attribute invocation would be passed the entire 'if else' chain, giving the proc macro the maximum flexibility for interpreting it. The decision as to which part of the chain to actually modify would be decided by the macro implementation.

However, this would certainly require further discussion. I don't think it needs to block accepting these attributes syntactically - doing so doesn't commit us to accepting them semantically (e.g. we could still emit an error if the post-expansion code contains attributes on 'if' expressions.

Aaron1011 added a commit to Aaron1011/rust that referenced this issue Jan 29, 2020
Fixes rust-lang#68618

Previously, attributes on 'if' expressions (e.g. `#[attr] if true {}`)
were disallowed during parsing. This made it impossible for macros to
perform any custom handling of such attributes (e.g. stripping them
away), since a compilation error would be emitted before they ever had a
chance to run.

This PR permits attributes on 'if' expressions ('if-attrs' from here on)
syntactically, i.e. during parsing. We instead deny if-attrs
during AST validation, which occurs after all macro expansions have run.

This is a conservative change which allows more code to be processed by
macros. It does not commit us to *semantically* accepting if-attrs. For
example, the following code is not allowed even with this PR:

```rust
fn builtin_attr() {
    #[allow(warnings)] if true {}
}

fn custom_attr() {
    #[my_proc_macro_attr] if true {}
}
```

However, the following code *is* accepted

```rust
#[cfg(FALSE)]
fn foo() {
    #[allow(warnings)] if true {}
    #[my_custom_attr] if true {}
}

#[my_custom_attr]
fn use_within_body() {
    #[allow(warnings)] if true {}
    #[my_custom_attr] if true {}
}
```
Centril added a commit to Centril/rust that referenced this issue Mar 9, 2020
…=Centril

Permit attributes on 'if' expressions

Previously, attributes on 'if' expressions (e.g. `#[attr] if true {}`)
were disallowed during parsing. This made it impossible for macros to
perform any custom handling of such attributes (e.g. stripping them
away), since a compilation error would be emitted before they ever had a
chance to run.

This PR permits attributes on 'if' expressions ('if-attrs' from here on).
Both built-in attributes (e.g. `#[allow]`, `#[cfg]`) and proc-macro attributes are supported.

We still do *not* accept attributes on 'other parts' of an if-else
chain. That is, the following code snippet still fails to parse:

```rust
if true {} #[attr] else if false {} else #[attr] if false {} #[attr]
else {}
```

Closes rust-lang#68618
Centril added a commit to Centril/rust that referenced this issue Mar 9, 2020
…=Centril

Permit attributes on 'if' expressions

Previously, attributes on 'if' expressions (e.g. `#[attr] if true {}`)
were disallowed during parsing. This made it impossible for macros to
perform any custom handling of such attributes (e.g. stripping them
away), since a compilation error would be emitted before they ever had a
chance to run.

This PR permits attributes on 'if' expressions ('if-attrs' from here on).
Both built-in attributes (e.g. `#[allow]`, `#[cfg]`) and proc-macro attributes are supported.

We still do *not* accept attributes on 'other parts' of an if-else
chain. That is, the following code snippet still fails to parse:

```rust
if true {} #[attr] else if false {} else #[attr] if false {} #[attr]
else {}
```

Closes rust-lang#68618
Centril added a commit to Centril/rust that referenced this issue Mar 9, 2020
…=Centril

Permit attributes on 'if' expressions

Previously, attributes on 'if' expressions (e.g. `#[attr] if true {}`)
were disallowed during parsing. This made it impossible for macros to
perform any custom handling of such attributes (e.g. stripping them
away), since a compilation error would be emitted before they ever had a
chance to run.

This PR permits attributes on 'if' expressions ('if-attrs' from here on).
Both built-in attributes (e.g. `#[allow]`, `#[cfg]`) and proc-macro attributes are supported.

We still do *not* accept attributes on 'other parts' of an if-else
chain. That is, the following code snippet still fails to parse:

```rust
if true {} #[attr] else if false {} else #[attr] if false {} #[attr]
else {}
```

Closes rust-lang#68618
@bors bors closed this as completed in 4ec9975 Mar 9, 2020
Aaron1011 added a commit to Aaron1011/pin-project that referenced this issue Mar 9, 2020
This PR would work correctly, except for the fact that
rust-lang/rust#68618 causes a compilation error to be emitted before we
even have a chance to run. That issue is independent of the
implementation of this PR, so this PR should start working automatically
once the issue is resolved.
bors bot added a commit to taiki-e/pin-project that referenced this issue Mar 20, 2020
181: Allow `#[project]` to be used on `if let` expressions r=taiki-e a=Aaron1011

This PR would work correctly, except for the fact that
rust-lang/rust#68618 causes a compilation error to be emitted before we
even have a chance to run. That issue is independent of the
implementation of this PR, so this PR should start working automatically
once the issue is resolved.

Co-authored-by: Aaron Hill <aa1ronham@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-frontend Area: Compiler frontend (errors, parsing and HIR) A-parser Area: The parsing of Rust source code to an AST C-feature-request Category: A feature request, i.e: not implemented / a PR. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
4 participants