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

RFC: Attributes in formal function parameter position #2565

Merged
merged 9 commits into from
Apr 30, 2019

Conversation

Robbepop
Copy link
Contributor

@Robbepop Robbepop commented Oct 15, 2018

This RFC proposes to add attributes in formal function parameter position.

Rendered
Tracking issue: rust-lang/rust#60406

@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Oct 15, 2018
@Robbepop Robbepop changed the title RFC: attributes in formal function parameter position RFC: Attributes in formal function parameter position Oct 15, 2018
@SimonSapin
Copy link
Contributor

I think that the underscore prefix convention is well established and that #[unused] is not needed. The rest of this proposal still applies to procedural macros, though I don’t have a strong opinion on that.

@petrochenkov
Copy link
Contributor

Some questions to answer:

  • Whether macro attributes are supported.
  • Whether cfg is supported.
  • Whether attributes in fn types are supported (type F = fn(#[attr] param: Type); and type F = fn(#[attr] Type);).
  • Future compatibility with attributes on patterns #[attr] Pat and types #[attr] Type.

text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
text/2540-formal-function-parameter-attributes.md Outdated Show resolved Hide resolved
@Centril
Copy link
Contributor

Centril commented Oct 15, 2018

Thank you for and congratulations on writing your first RFC!
I and others have left some thoughts for you to address :)

@Centril
Copy link
Contributor

Centril commented Oct 15, 2018

A use case for this with proptest could be to enable the following:

#[proptest]
fn prop_my_property(#[types(T = u8 | u16)] elem: Vec<T>, ...) {
    ...
)

This would replace T with u8 and u16...

Also:

#[proptest]
fn prop_my_property(#[strat = 0..100] elem: usize, ...) {
    ...
)

Tho I'd probably formulate this instead as:

#[proptest]
#[proptest::types(T = u8 | u16)] 
fn prop_my_property<T>(elem: Vec<T>, ...) {
    ...
)

#[proptest]
fn prop_my_property(elem in 0..100usize, ...) { // this unfortunately won't parse
    ...
)

@Robbepop
Copy link
Contributor Author

I think that the underscore prefix convention is well established and that #[unused] is not needed. The rest of this proposal still applies to procedural macros, though I don’t have a strong opinion on that.

You might be right about that. It was just an idea I had. We do not need to have language defined attributes for now.

@Robbepop
Copy link
Contributor Author

Some questions to answer:

* Whether macro attributes are supported.

* Whether `cfg` is supported.

* Whether attributes in fn types are supported (`type F = fn(#[attr] param: Type);` and `type F = fn(#[attr] Type);`).

* Future compatibility with attributes on patterns `#[attr] Pat` and types `#[attr] Type`.

Good points!

I haven't concretely thought about macro attributes but I think that they should be supported for convenience if that doesn't mean too much extra work. I cannot see the end of the scope for this to be honest.

I have no clear answer for the cfg support. What do you think?

I would postpone the attributes in fn types for another RFC since at least to my understanding this would force having attributes be part of the type system.

What do you mean by the future compatibility with attributes on patterns and types? So what shouldn't be compatible with them in the future? I am sorry that I do not understand.

@Robbepop
Copy link
Contributor Author

A use case for this with proptest could be to enable the following:

#[proptest]
fn prop_my_property(#[types(T = u8 | u16)] elem: Vec<T>, ...) {
    ...
)

This would replace T with u8 and u16...

Also:

#[proptest]
fn prop_my_property(#[strat = 0..100] elem: usize, ...) {
    ...
)

Tho I'd probably formulate this instead as:

#[proptest]
#[proptest::types(T = u8 | u16)] 
fn prop_my_property<T>(elem: Vec<T>, ...) {
    ...
)

#[proptest]
fn prop_my_property(elem in 0..100usize, ...) { // this unfortunately won't parse
    ...
)

Thank you for those interesting ideas around the use case.
I will implement them in the RFC in the motivation section.

@petrochenkov
Copy link
Contributor

@Robbepop

I haven't concretely thought about macro attributes but I think that they should be supported for convenience if that doesn't mean too much extra work.

Macro attributes are supported only in a relatively small subset of attribute positions, so it's better to start with not supporting them.

I have no clear answer for the cfg support. What do you think?

cfg is generally supported in all attribute positions (IIRC it's under-implemented for generic parameters though), so it's better to support it.

I would postpone the attributes in fn types for another RFC since at least to my understanding this would force having attributes be part of the type system.

It wouldn't have any effect on type system, no more than attributes on function definitions.
Also, function types basically share the grammar with trait methods (at least on 2015 edition), so it may be more convenient to support attributes in them, at least for named parameters fn(#[attr] param: Type) (see below).

@petrochenkov
Copy link
Contributor

What do you mean by the future compatibility with attributes on patterns and types?

Right now we support attributes on arbitrary expressions, so it's quite possible that we'll eventually support attributes on arbitrary types and patterns as well.

In this case what the attribute is attached to in

fn f(#[attr] PAT: TYPE) {}

, to the pattern, or to the whole parameter?

I think we'll be able to reattach the attributes from parameters to patterns if pattern attributes become available, but I'm not entirely sure (macros?).

In this sense fn f(#[attr] &self) looks pretty suspicious, since &self is not a pattern, but I guess we'll be able to treat this as a sugar and reattach the attribute in the desugared form #[attr] self: &Self.

That's why fn f(#[attr] TYPE) (attribute on anonymous parameter) looks suspicious as well, so I suggest to not support this, so we don't have to think about attributes on types in addition to all the other issues.

@nielsle
Copy link

nielsle commented Oct 16, 2018

I think that I would prefer the following

#[ignore_args]
fn foo( #[ignore] bar: bool);

over the following

fn foo( #[ignore] bar: bool);

The first version is longer, but the scope of the macro is more clear. But then again this distinction is probably out of scope for the RFC.

@kennytm
Copy link
Member

kennytm commented Oct 16, 2018

In this case what the attribute is attached to in

fn f(#[attr] PAT: TYPE) {}

, to the pattern, or to the whole parameter?

Just wanna note that this thing already exists:

fn main() {
    match 3 {
        #[cfg(windows)] 
        3 => println!("a"),
        #[cfg(unix)] 
        3 => println!("b"),
        _ => println!("c"),
    }
}

and the attribute is applied to the whole arm, not the pattern.

@joshtriplett
Copy link
Member

I'm in favor of allowing attributes on parameters.

Please do drop the bits about replacing the _ convention with #[unused], though.

@Centril
Copy link
Contributor

Centril commented Oct 17, 2018

Please do drop the bits about replacing the _ convention with #[unused], though.

I believe it's not part of the proposal; rather, it is a hypothetical use case that some person could do in a procedural macro.

@Robbepop
Copy link
Contributor Author

In this case what the attribute is attached to in

fn f(#[attr] PAT: TYPE) {}

, to the pattern, or to the whole parameter?

Just wanna note that this thing already exists:

fn main() {
    match 3 {
        #[cfg(windows)] 
        3 => println!("a"),
        #[cfg(unix)] 
        3 => println!("b"),
        _ => println!("c"),
    }
}

and the attribute is applied to the whole arm, not the pattern.

So should we do the same for the parameter attributes so that the attribute is for the PAT and the TYPE?

@Robbepop
Copy link
Contributor Author

Please do drop the bits about replacing the _ convention with #[unused], though.

I believe it's not part of the proposal; rather, it is a hypothetical use case that some person could do in a procedural macro.

I should reformulate this to cause no misunderstandings.

@kennytm
Copy link
Member

kennytm commented Oct 18, 2018

@Robbepop Attributes typically aren't transitive. The attribute is applied to the parameter itself, not the pattern nor the type.

@pnkfelix pnkfelix self-assigned this Oct 25, 2018
@Robbepop
Copy link
Contributor Author

Robbepop commented Mar 4, 2019

@Robbepop I've taken the liberty to polish the RFC a bit, provide clarifications, and so on. I hope that's OK. Could you give the changes a look? After that I think we should be in a position to move ahead with this proposal.

I am so sorry - saw this just now.
Thank you for taking this over!

Edit:
@Centril I have read through the entire updated RFC. This got huge over time and it looks really good to me.
Thanks so much for taking it over and pushing it!

@Centril
Copy link
Contributor

Centril commented Mar 5, 2019

This RFC represents an extension to where attributes are permitted in the language. The extension is small, consistently applied, and has notable use cases as per comments in this RFC. I believe that the extension of attributes to other places in the language affords users to encode the EDSLs that they like. As this RFC is an important step on that road, I think we should move ahead with it and so therefore, I propose that we:

@rfcbot merge

@rfcbot
Copy link
Collaborator

rfcbot commented Mar 5, 2019

Team member @Centril has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. labels Mar 5, 2019
@scottmcm
Copy link
Member

Sounds reasonable to me, though the implementation will need to be careful to avoid adding a bunch of new "we forgot to block that attribute in that particular position" mistakes.

@rfcbot reviewed

@rfcbot
Copy link
Collaborator

rfcbot commented Apr 18, 2019

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot added final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. and removed proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. labels Apr 18, 2019
@nikomatsakis
Copy link
Contributor

NB: I tagged for @aturon as they are temporarily inactive.

@rfcbot rfcbot added the finished-final-comment-period The final comment period is finished for this RFC. label Apr 28, 2019
@rfcbot
Copy link
Collaborator

rfcbot commented Apr 28, 2019

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

The RFC will be merged soon.

@rfcbot rfcbot removed the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Apr 28, 2019
@Centril Centril merged commit 26ed27b into rust-lang:master Apr 30, 2019
@Centril
Copy link
Contributor

Centril commented Apr 30, 2019

Huzzah! This RFC has been merged!

Tracking issue: rust-lang/rust#60406

bors added a commit to rust-lang/rust that referenced this pull request Sep 21, 2019
Stabilize `param_attrs` in Rust 1.39.0

# Stabilization proposal

I propose that we stabilize `#![feature(param_attrs)]`.

Tracking issue: #60406
Version: 1.39 (2019-09-26 => beta, 2019-11-07 => stable).

## What is stabilized

It is now possible to add outer attributes like `#[cfg(..)]` on formal parameters of functions, closures, and function pointer types. For example:

```rust
fn len(
    #[cfg(windows)] slice: &[u16],
    #[cfg(not(windows))] slice: &[u8],
) -> usize {
    slice.len()
}
```

## What isn't stabilized

* Documentation comments like `/// Doc` on parameters.

* Code expansion of a user-defined `#[proc_macro_attribute]` macro used on parameters.

* Built-in attributes other than `cfg`, `cfg_attr`, `allow`, `warn`, `deny`, and `forbid`. Currently, only the lints `unused_variables` and `unused_mut` have effect and may be controlled on parameters.

## Motivation

The chief motivations for stabilizing `param_attrs` include:

* Finer conditional compilation with `#[cfg(..)]` and linting control of variables.

* Richer macro DSLs created by users.

* External tools and compiler internals can take advantage of the additional information that the parameters provide.

For more examples, see the [RFC][rfc motivation].

## Reference guide

In the grammar of function and function pointer, the grammar of variadic tails (`...`) and parameters are changed respectively from:

```rust
FnParam = { pat:Pat ":" }? ty:Type;
VaradicTail = "...";
```

into:

```rust
FnParam = OuterAttr* { pat:Pat ":" }? ty:Type;
VaradicTail = OuterAttr* "...";
```

The grammar of a closure parameter is changed from:

```rust
ClosureParam = pat:Pat { ":" ty:Type }?;
```

into:

```rust
ClosureParam = OuterAttr* pat:Pat { ":" ty:Type }?;
```

More generally, where there's a list of formal (value) parameters separated or terminated by `,` and delimited by `(` and `)`. Each parameter in that list may optionally be prefixed by `OuterAttr+`.

Note that in all cases, `OuterAttr*` applies to the whole parameter and not just the pattern. This distinction matters in pretty printing and in turn for macros.

## History

* On 2018-10-15, @Robbepop proposes [RFC 2565][rfc], "Attributes in formal function parameter position".

* On 2019-04-30, [RFC 2565][rfc] is merged and the tracking issue is made.

* On 2019-06-12, a partial implementation was completed. The implementation was done in [#60669][60669] by @c410-f3r and the PR was reviewed by @petrochenkov and @Centril.

* On 2019-07-29, [#61238][61238] was fixed in [#61856][61856]. The issue fixed was that lint attributes on function args had no effect. The PR was written by @c410-f3r and reviewed by @matthewjasper, @petrochenkov, and @oli-obk.

* On 2019-08-02, a bug [#63210][63210] was filed wherein the attributes on formal parameters would not be passed to macros. The issue was about forgetting to call the relevant method in `fn print_arg` in the pretty printer. In [#63212][63212], written by @Centril on 2019-08-02 and reviewed by @davidtwco, the issue aforementioned was fixed.

* This PR stabilizes `param_attrs`.

## Tests

* [On Rust 2018, attributes aren't permitted on function parameters without a pattern in trait definitions.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.rs)

* [All attributes that should be allowed. This includes `cfg`, `cfg_attr`, and lints check attributes.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs)

* [Built-in attributes, which should be forbidden, e.g., `#[test]`, are.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs)

* [`cfg` and `cfg_attr` are properly evaluated.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs)

* [`unused_mut`](https://github.com/rust-lang/rust/blob/46f405ec4d7c6bf16fc2eaafe7541019f1da2996/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs) and [`unused_variables`](https://github.com/rust-lang/rust/blob/master/src/test/ui/lint/lint-unused-variables.rs) are correctly applied to parameter patterns.

* [Pretty printing takes formal parameter attributes into account.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-pretty.rs)

## Possible future work

* Custom attributes inside function parameters aren't currently supported but it is something being worked on internally.

* Since documentation comments are syntactic sugar for `#[doc(...)]`, it is possible to allow literal `/// Foo` comments on function parameters.

[rfc motivation]: https://github.com/rust-lang/rfcs/blob/master/text/2565-formal-function-parameter-attributes.md#motivation
[rfc]: rust-lang/rfcs#2565
[60669]: #60669
[61856]: #61856
[63210]: #63210
[61238]: #61238
[63212]: #63212

This report is a collaborative work with @Centril.
Centril added a commit to Centril/rust that referenced this pull request Sep 21, 2019
Stabilize `param_attrs` in Rust 1.39.0

# Stabilization proposal

I propose that we stabilize `#![feature(param_attrs)]`.

Tracking issue: rust-lang#60406
Version: 1.39 (2019-09-26 => beta, 2019-11-07 => stable).

## What is stabilized

It is now possible to add outer attributes like `#[cfg(..)]` on formal parameters of functions, closures, and function pointer types. For example:

```rust
fn len(
    #[cfg(windows)] slice: &[u16],
    #[cfg(not(windows))] slice: &[u8],
) -> usize {
    slice.len()
}
```

## What isn't stabilized

* Documentation comments like `/// Doc` on parameters.

* Code expansion of a user-defined `#[proc_macro_attribute]` macro used on parameters.

* Built-in attributes other than `cfg`, `cfg_attr`, `allow`, `warn`, `deny`, and `forbid`. Currently, only the lints `unused_variables` and `unused_mut` have effect and may be controlled on parameters.

## Motivation

The chief motivations for stabilizing `param_attrs` include:

* Finer conditional compilation with `#[cfg(..)]` and linting control of variables.

* Richer macro DSLs created by users.

* External tools and compiler internals can take advantage of the additional information that the parameters provide.

For more examples, see the [RFC][rfc motivation].

## Reference guide

In the grammar of function and function pointer, the grammar of variadic tails (`...`) and parameters are changed respectively from:

```rust
FnParam = { pat:Pat ":" }? ty:Type;
VaradicTail = "...";
```

into:

```rust
FnParam = OuterAttr* { pat:Pat ":" }? ty:Type;
VaradicTail = OuterAttr* "...";
```

The grammar of a closure parameter is changed from:

```rust
ClosureParam = pat:Pat { ":" ty:Type }?;
```

into:

```rust
ClosureParam = OuterAttr* pat:Pat { ":" ty:Type }?;
```

More generally, where there's a list of formal (value) parameters separated or terminated by `,` and delimited by `(` and `)`. Each parameter in that list may optionally be prefixed by `OuterAttr+`.

Note that in all cases, `OuterAttr*` applies to the whole parameter and not just the pattern. This distinction matters in pretty printing and in turn for macros.

## History

* On 2018-10-15, @Robbepop proposes [RFC 2565][rfc], "Attributes in formal function parameter position".

* On 2019-04-30, [RFC 2565][rfc] is merged and the tracking issue is made.

* On 2019-06-12, a partial implementation was completed. The implementation was done in [rust-lang#60669][60669] by @c410-f3r and the PR was reviewed by @petrochenkov and @Centril.

* On 2019-07-29, [rust-lang#61238][61238] was fixed in [rust-lang#61856][61856]. The issue fixed was that lint attributes on function args had no effect. The PR was written by @c410-f3r and reviewed by @matthewjasper, @petrochenkov, and @oli-obk.

* On 2019-08-02, a bug [rust-lang#63210][63210] was filed wherein the attributes on formal parameters would not be passed to macros. The issue was about forgetting to call the relevant method in `fn print_arg` in the pretty printer. In [rust-lang#63212][63212], written by @Centril on 2019-08-02 and reviewed by @davidtwco, the issue aforementioned was fixed.

* This PR stabilizes `param_attrs`.

## Tests

* [On Rust 2018, attributes aren't permitted on function parameters without a pattern in trait definitions.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-2018.rs)

* [All attributes that should be allowed. This includes `cfg`, `cfg_attr`, and lints check attributes.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-allowed.rs)

* [Built-in attributes, which should be forbidden, e.g., `#[test]`, are.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-builtin-attrs.rs)

* [`cfg` and `cfg_attr` are properly evaluated.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs)

* [`unused_mut`](https://github.com/rust-lang/rust/blob/46f405ec4d7c6bf16fc2eaafe7541019f1da2996/src/test/ui/rfc-2565-param-attrs/param-attrs-cfg.rs) and [`unused_variables`](https://github.com/rust-lang/rust/blob/master/src/test/ui/lint/lint-unused-variables.rs) are correctly applied to parameter patterns.

* [Pretty printing takes formal parameter attributes into account.](https://github.com/rust-lang/rust/blob/master/src/test/ui/rfc-2565-param-attrs/param-attrs-pretty.rs)

## Possible future work

* Custom attributes inside function parameters aren't currently supported but it is something being worked on internally.

* Since documentation comments are syntactic sugar for `#[doc(...)]`, it is possible to allow literal `/// Foo` comments on function parameters.

[rfc motivation]: https://github.com/rust-lang/rfcs/blob/master/text/2565-formal-function-parameter-attributes.md#motivation
[rfc]: rust-lang/rfcs#2565
[60669]: rust-lang#60669
[61856]: rust-lang#61856
[63210]: rust-lang#63210
[61238]: rust-lang#61238
[63212]: rust-lang#63212

This report is a collaborative work with @Centril.
@matklad
Copy link
Member

matklad commented Feb 3, 2021

One thing I've noticed which wasn't obvious to me during RFC discussion. The following is now on the path of the least resistance:

fn f(
    foo: Foo,
    #[cfg(feature = "bar")]
    bar: Bar,
)

However, it breaks additiveness of the bar feature and otherwise creates code with non-trivially feature-dependent APIs. This is harder to work with and understand for humans (as such cfg necessary infects callers). This is also hard for tools -- implementing "change signature" refactor for this seems challenging.

Less obvious, but more maintainable option is to do this:

fn f(
    foo: Foo,
    bar: Bar,
) {
..
}

struct Bar {
  #[cfg(feature = "bar")]
  imp: RealImpl,
  #[cfg(not(feature = "bar"))]
  imp: DummyZstImpl,
}

@TedDriggs
Copy link

@matklad would a clippy lint against using cfg on parameters be a sufficient deterrent?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Proposals relating to attributes A-syntax Syntax related proposals & ideas disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this RFC. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.