From cf0b8ec06858e4638f254df4c3232f5d8b4a638f Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 15 Oct 2018 11:51:16 +0200 Subject: [PATCH 1/9] Create 2540-formal-function-parameter-attributes.md --- ...40-formal-function-parameter-attributes.md | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 text/2540-formal-function-parameter-attributes.md diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md new file mode 100644 index 00000000000..6c3c604dd85 --- /dev/null +++ b/text/2540-formal-function-parameter-attributes.md @@ -0,0 +1,242 @@ +- Feature Name: formal_function_param_attrs +- Start Date: 2018-10-14 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +This RFC proposes to allow attributes in formal function parameter position. + +# Motivation +[motivation]: #motivation + +Having attributes on formal function parameters allows for certain different use cases. + +## Example: Handling of unused parameter + +In today's Rust it is possible to prefix the name of an identifier to silence the compiler about it being unused. +With attributes in formal function parameter position we could have an attribute like `#[unused]` that explicitely states this for a given parameter. + +```rust +fn foo(#[unused] bar: u32) -> bool; +``` + +Instead of + +```rust +fn foo(_bar: u32) -> bool +``` + +This would better reflect the explicit nature of Rust compared to the underscore prefix as of today. + +## Example: Low-level code + +For raw pointers that are oftentimes used when operating with C code one could provide the compiler with additional information about the set of parameters. +You could for example mirror C's restrict keyword or even be more explicit by stating what pointer argument might overlap. + +```rust +fn foo(#[overlaps_with(in_b) in_a: *const u8, #[overlaps_with(in_a)] in_b: *const u8, #[restrict] out: *mut u8); +``` + +Which might state that the pointers `in_a` and `in_b` might overlap but `out` is non overlapping. +Please note that I am *not* proposing to actually add this to the language! + +## Example: Procedural Macros + +Also procedural macros could greatly benefit from having their own defined custom attributes on formal parameters. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Formal parameters of functions, methods, closures and functions in trait definitions may have attributes attached to them. +This allows to provide additional information to a given formal parameter. + +To reify this we introduce the `#[unused]` attribute that states that the attributed parameter is unused in the associated implementation. + +## Examples + +The syntax for this is demonstrated by the code below: + +```rust +// Function +fn foo(#[unused] bar: u32) { .. } + +// Methods & trait & definitions: +// - `self` can also be attributed +fn foo(#[unused] self, ..) { .. } +fn foo(#[unused] &self, ..) { .. } +fn foo(#[unused] &mut self, ..) { .. } + +// Closures & Lamdas +|#[unused] x| { .. } +``` + +### Trait declarations + +```rust +fn foo(#[unused] self); +``` + +Note that while the `#[unused]` attribute is syntactically +possible to put here it doesn't actually make sense semantically +since method declarations have no implementation. +Other attributes might be very useful as for formal parameters in a method declaration. + +## Errors & Warnings + +There are two errornous situations when it comes to + +### Warning: Unused attribute + +When using an non-defined attribute that is not used by either the language or a custom defined procedural macro. + +``` +warning: unused attribute + --> src/main.rs:2 + | +2 | #[foo] bar: u32 + | ^^^^^^^^^ + | + = note: #[warn(unused_attributes)] on by default +``` + +### Error: Malformed attribute + +When using a known attribute that is not defined for formal parameters such as when attributing `fn main` with `#[allow]`. + +Example shows the usage of the known attribute `#[inline]` used on a formal parameter without being defined for it. + +``` +error[E0452]: malformed lint attribute + --> src/main.rs:2 + | +2 | #[inline] bar: u32 + | ^^^^^^^^ +``` + +The same applies for attributes with an incorrect format such as `#[inline(key = value)]` that is handled as its done in other contexts. + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## Description + +In accordance to the RFC for [attributes for generic params](https://github.com/frol/rust-rfcs/blob/master/text/1327-dropck-param-eyepatch.md) +this feature is guarded by the `formal_function_param_attrs` feature guard. + +The grammar of the following language items has to be adjusted to allow that +constructions like the following will become legal. + +- Function definitions +- Method definitions +- Trait function declarations and defnitions (with or without default impl) +- Lambda & closure definitions + +### Example: A single attributed parameter for function decl or definition + +```rust +fn foo(#[bar] baz: bool); +fn bar(#[bar] qux: bool) { println!("hi"); } +``` + +### Example: For methods or trait function definitions + +```rust +fn into_foo(#[bar] self); +fn foo(#[bar] &self); +fn foo_mut(#[bar] &mut self); +``` + +### Example: Multiple attributed parameters + +```rust +// Twice the same attribute +fn fst_foo(#[bar] baz: bool, #[bar] qiz: u32); + +// Different attributes +fn snd_foo(#[bar] baz: bool, #[qux] qiz: u32); +``` + +### Example: Any structured attribute + +```rust +fn foo(#[bar(Hello)] baz: bool); +fn bar(#[qux(qiz = World)] baz: bool); +``` + +### Example: Lambdas & closures + +```rust +let mut v = [5, 4, 1, 3, 2]; +v.sort_by(|#[bar] a, #[baz] b| a.cmp(b)); +``` + +## Errors + +Users can encounter two different errorneous situations. + +### Unknown attribute used + +When a user is using an attribute that is not known at the point of its invokation +a warning is generated similar to other usages of unknown attributes in the language. + +This may for example happen in the context of a procedural macros. + +### Malformed attribute + +When a user is using a known or language defined attribute at a non supported location +an error is generated like in other usages of malformed attributes in the language. +An example can be seen in the previous section. + + +# Drawbacks +[drawbacks]: #drawbacks + +All drawbacks for attributes in any location also count for this proposal. + +Having attributes in many different places of the language complicates its grammar. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## Why is this proposal considered the best in the space of available ideas? + +This proposal clearly goes the path of having attributes in more places of the language. +It nicely plays together with the advance of procedural macros and macros 2.0 where users +can define their own attributes for their special purposes. + +## Alternatives + +An alternative to having attributes for formal parameters might be to just use the current +set of available attributable items to store meta information about formal parameters like +in the following example: + +```rust +#[ignore(param = bar)] +fn foo(bar: bool); +``` + +Note that this does not work in all situations (for example closures) and might invole even +more complexity in user's code than simply allowing formal function parameter attributes. + +## Impact + +The impact will most certainly be that users might create custom attributes when +designing procedural macros involving formal function parameters. + +There should be no breakage of existing code. + +# Prior art +[prior-art]: #prior-art + +Some example languages that allows for attributes in formal function parameter positions are C# and C++. + +Also note that attributes in other parts of the Rust language could be considered prior art to this proposal. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +We might want to introduce new attributes for the language like the mentioned `#[unused]` attribute. +However, this RFC proposes to decide upon this in another RFC. From 67f2e2e8d8a4d80af11b95195e71a2943a120f53 Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 15 Oct 2018 12:36:20 +0200 Subject: [PATCH 2/9] Update 2540-formal-function-parameter-attributes.md --- text/2540-formal-function-parameter-attributes.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md index 6c3c604dd85..b37202cb512 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2540-formal-function-parameter-attributes.md @@ -85,8 +85,6 @@ Other attributes might be very useful as for formal parameters in a method decla ## Errors & Warnings -There are two errornous situations when it comes to - ### Warning: Unused attribute When using an non-defined attribute that is not used by either the language or a custom defined procedural macro. From 4529f899f98372cd1ff87f32f2a560c99aaf4169 Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 15 Oct 2018 13:12:28 +0200 Subject: [PATCH 3/9] Update 2540-formal-function-parameter-attributes.md --- text/2540-formal-function-parameter-attributes.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md index b37202cb512..48cca4dc348 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2540-formal-function-parameter-attributes.md @@ -36,7 +36,11 @@ For raw pointers that are oftentimes used when operating with C code one could p You could for example mirror C's restrict keyword or even be more explicit by stating what pointer argument might overlap. ```rust -fn foo(#[overlaps_with(in_b) in_a: *const u8, #[overlaps_with(in_a)] in_b: *const u8, #[restrict] out: *mut u8); +fn foo( + #[overlaps_with(in_b)] in_a: *const u8, + #[overlaps_with(in_a)] in_b: *const u8, + #[restrict] out: *mut u8 +); ``` Which might state that the pointers `in_a` and `in_b` might overlap but `out` is non overlapping. @@ -52,7 +56,7 @@ Also procedural macros could greatly benefit from having their own defined custo Formal parameters of functions, methods, closures and functions in trait definitions may have attributes attached to them. This allows to provide additional information to a given formal parameter. -To reify this we introduce the `#[unused]` attribute that states that the attributed parameter is unused in the associated implementation. +For the next examples the hypothetical `#[unused]` attribute means that the attributed parameter is unused in the associated implementation. ## Examples From 76da521c03f766084df767358f7e6c0b9ec141ae Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 15 Oct 2018 13:14:29 +0200 Subject: [PATCH 4/9] Update 2540-formal-function-parameter-attributes.md --- text/2540-formal-function-parameter-attributes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md index 48cca4dc348..c4896b42b1a 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2540-formal-function-parameter-attributes.md @@ -220,7 +220,7 @@ in the following example: fn foo(bar: bool); ``` -Note that this does not work in all situations (for example closures) and might invole even +Note that this does not work in all situations (for example closures) and might involve even more complexity in user's code than simply allowing formal function parameter attributes. ## Impact From 051be99581b356020e2b7ff57077e47a15e2eaaa Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 15 Oct 2018 13:42:20 +0200 Subject: [PATCH 5/9] Update 2540-formal-function-parameter-attributes.md --- ...40-formal-function-parameter-attributes.md | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md index c4896b42b1a..bf37930e2ae 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2540-formal-function-parameter-attributes.md @@ -16,7 +16,8 @@ Having attributes on formal function parameters allows for certain different use ## Example: Handling of unused parameter In today's Rust it is possible to prefix the name of an identifier to silence the compiler about it being unused. -With attributes in formal function parameter position we could have an attribute like `#[unused]` that explicitely states this for a given parameter. +With attributes in formal function parameter position we could hypothetically have an attribute like `#[unused]` that explicitely states this for a given parameter. +Note that `#[unused]` is not part of this proposal but merely a simple usecase. ```rust fn foo(#[unused] bar: u32) -> bool; @@ -28,7 +29,7 @@ Instead of fn foo(_bar: u32) -> bool ``` -This would better reflect the explicit nature of Rust compared to the underscore prefix as of today. +Especially Rust beginners might find the meaning of the above code snippet to be clearer. ## Example: Low-level code @@ -46,9 +47,36 @@ fn foo( Which might state that the pointers `in_a` and `in_b` might overlap but `out` is non overlapping. Please note that I am *not* proposing to actually add this to the language! -## Example: Procedural Macros +## Example: Usable with cfg_attr -Also procedural macros could greatly benefit from having their own defined custom attributes on formal parameters. +```rust +fn foo(#[cfg_attr(foo, bar)] baz: u32); +``` + +Which states that `baz` is attributed with the attribute `bar` if `foo` evaluates to `true`. + +## Example: Documentation comments + +Since documentation comments are attributes in their underlying representation we could have doc comments +for single parameters like shown below. + +```rust +/// Description of foo. +fn foo( + /// Description of foo's first parameter bar + bar: u32 +); +``` + +Of course this raises other questions: +- How does rust-fmt handle these? +- Are they allowed or should we postpone their allowance to another RFC? + +## Example: Procedural macros and custom attributes + +Also procedural macros and custom attributes could greatly benefit from having their own defined custom attributes on formal parameters. + +Examples will follow. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 66d944be8dacca6c796d87e6ce548262f9d9c1fe Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Mon, 15 Oct 2018 13:50:28 +0200 Subject: [PATCH 6/9] Update 2540-formal-function-parameter-attributes.md --- text/2540-formal-function-parameter-attributes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md index bf37930e2ae..d8cebc40d3c 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2540-formal-function-parameter-attributes.md @@ -100,7 +100,7 @@ fn foo(#[unused] self, ..) { .. } fn foo(#[unused] &self, ..) { .. } fn foo(#[unused] &mut self, ..) { .. } -// Closures & Lamdas +// Closures |#[unused] x| { .. } ``` @@ -162,7 +162,7 @@ constructions like the following will become legal. - Function definitions - Method definitions - Trait function declarations and defnitions (with or without default impl) -- Lambda & closure definitions +- Closure definitions ### Example: A single attributed parameter for function decl or definition @@ -196,7 +196,7 @@ fn foo(#[bar(Hello)] baz: bool); fn bar(#[qux(qiz = World)] baz: bool); ``` -### Example: Lambdas & closures +### Example: Closures ```rust let mut v = [5, 4, 1, 3, 2]; From 009286d502b9b283451c783a4e9ecef8b5e501d6 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 19 Jan 2019 02:05:50 +0100 Subject: [PATCH 7/9] rfc/param-attrs: make assorted adjustments and polish. --- ...40-formal-function-parameter-attributes.md | 569 ++++++++++++++---- 1 file changed, 439 insertions(+), 130 deletions(-) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md index d8cebc40d3c..5f445f846aa 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2540-formal-function-parameter-attributes.md @@ -1,4 +1,4 @@ -- Feature Name: formal_function_param_attrs +- Feature Name: `param_attrs` - Start Date: 2018-10-14 - RFC PR: - Rust Issue: @@ -6,220 +6,472 @@ # Summary [summary]: #summary -This RFC proposes to allow attributes in formal function parameter position. +Allow attributes in formal function parameter position. +For example, consider a Jax-Rs-style HTTP API: + +```rust +#[resource(path = "/foo/bar")] +impl MyResource { + #[get("/person/:name")] + fn get_person( + &self, + #[path_param = "name"] name: String, // <-- formal function parameter. + #[query_param = "limit"] limit: Option, // <-- here too. + ) { + ... + } +} +``` # Motivation [motivation]: #motivation -Having attributes on formal function parameters allows for certain different use cases. +Allowing attributes on formal function parameters enables external tools and +compiler internals to take advantage of the additional information that the +attributes provide. + +Conditional compilation with `#[cfg(..)]` is also +facilitated by allowing more ergonomic addition and removal of parameters. -## Example: Handling of unused parameter +Moreover, procedural macro authors can use annotations on +these parameters and thereby richer DSLs may be encoded by users. +We already saw an example of such a DSL in the [summary]. +To further illustrate potential usages, let's go through a few examples. -In today's Rust it is possible to prefix the name of an identifier to silence the compiler about it being unused. -With attributes in formal function parameter position we could hypothetically have an attribute like `#[unused]` that explicitely states this for a given parameter. -Note that `#[unused]` is not part of this proposal but merely a simple usecase. +## Compiler internals: Improving `#[rustc_args_required_const]` + +[memory_grow]: https://doc.rust-lang.org/nightly/core/arch/wasm32/fn.memory_grow.html + +A number of platform intrinsics are currently provided by rust compilers. +For example, we have [`core::arch::wasm32::memory_grow`][memory_grow] which, +for soundness reasons, requires that when `memory_grow` is applied, +`mem` must provided a `const` expression: ```rust -fn foo(#[unused] bar: u32) -> bool; +#[rustc_args_required_const(0)] +pub fn memory_grow(mem: u32, delta: usize) -> usize { .. } ``` -Instead of +This is specified in a positional manner, referring to `mem` by `0`. +While this is serviceable, this RFC enables us encode the invariant more directly: ```rust -fn foo(_bar: u32) -> bool +pub fn memory_grow( + #[rustc_args_required_const] mem: u32, + delta: usize +) -> usize { + .. +} ``` -Especially Rust beginners might find the meaning of the above code snippet to be clearer. +## Property based testing of polymorphic functions -## Example: Low-level code +[QuickCheck]: https://www.cs.tufts.edu/~nr/cs257/archive/john-hughes/quick.pdf +[proptest]: https://github.com/altsysrq/proptest +[quickcheck]: https://github.com/BurntSushi/quickcheck -For raw pointers that are oftentimes used when operating with C code one could provide the compiler with additional information about the set of parameters. -You could for example mirror C's restrict keyword or even be more explicit by stating what pointer argument might overlap. +Property based testing a la [QuickCheck] allows users to state properties they +expect their programs to adhere to. These properties are then tested by +randomly generating input data and running the properties with those. +The properties are can then be falsified by finding counter-examples. +If no such example are found, the test passes and the property is "verified". +In the Rust ecosystem, property based testing is primarily provided by the +[proptest] and [quickcheck] crates where the former uses integrated shrinking +whereas the latter uses type based shrinking. + +Consider a case where we want to test a "polymorphic" function on a number +of concrete types. ```rust -fn foo( - #[overlaps_with(in_b)] in_a: *const u8, - #[overlaps_with(in_a)] in_b: *const u8, - #[restrict] out: *mut u8 -); +#[proptest] // N.B. Using proptest doesn't look like this today. +fn prop_my_property(#[types(T = u8, u16, u32)] elem: Vec, ..) { .. } ``` -Which might state that the pointers `in_a` and `in_b` might overlap but `out` is non overlapping. -Please note that I am *not* proposing to actually add this to the language! +Here, we've overloaded the test for the types `u8`, `u16`, and `u32`. +The test will then act as if you had written: -## Example: Usable with cfg_attr +```rust +#[proptest] +fn prop_my_property_u8(elem: Vec, ..) { .. } + +#[proptest] +fn prop_my_property_u16(elem: Vec, ..) { .. } + +#[proptest] +fn prop_my_property_u32(elem: Vec, ..) { .. } +``` + +By allowing attributes on function parameters, the test can be specified +more succinctly and without repetition as done in the first example. + +## FFI and interoperation with other languages + +[wasm_bindgen]: https://github.com/rustwasm/wasm-bindgen + +There's interest in using attributes on function parameters for +[`#[wasm_bindgen]`][wasm_bindgen]. For example, to interoperate well +with TypeScript's type system, you could write: ```rust -fn foo(#[cfg_attr(foo, bar)] baz: u32); +#[wasm_bindgen] +impl RustLayoutEngine { + #[wasm_bindgen(constructor)] + pub fn new() -> Self { Default::default() } + + #[wasm_bindgen(typescript(return_type = "MapNode[]"))] + pub fn layout( + &self, + #[wasm_bindgen(typescript(type = "MapNode[]"))] + nodes: Vec, + #[wasm_bindgen(typescript(type = "MapEdge[]"))] + edges: Vec + ) -> Vec { + .. + } +} ``` -Which states that `baz` is attributed with the attribute `bar` if `foo` evaluates to `true`. +Currently, in `#[wasm_bindgen]`, the arguments and return type of `layout` +are all `any[]`. By using allowing the annotations above, tighter types can +be used which can help in catching problems at compile time rather than +having UI bugs later. -## Example: Documentation comments +## Greater control over optimizations in low-level code -Since documentation comments are attributes in their underlying representation we could have doc comments -for single parameters like shown below. +For raw pointers that are oftentimes used when operating with C code, +additional information could be given to the compiler about the set of parameters. +You could for example mirror C's restrict keyword or even be more explicit by +stating which pointer arguments may overlap: ```rust -/// Description of foo. fn foo( - /// Description of foo's first parameter bar - bar: u32 + #[overlaps_with(in_b)] in_a: *const u8, + #[overlaps_with(in_a)] in_b: *const u8, + #[restrict] out: *mut u8 ); ``` -Of course this raises other questions: -- How does rust-fmt handle these? -- Are they allowed or should we postpone their allowance to another RFC? +This would tell the compiler or some static analysis tool that the pointers +`in_a` and `in_b` might overlap but `out` is non overlapping. Note that neither +`overlaps_with` and `restrict` are part of this proposal; rather, they are +examples of what this RFC facilities. + +## Handling of unused parameter -## Example: Procedural macros and custom attributes +In today's Rust it is possible to prefix the name of an identifier to silence +the compiler about it being unused. With attributes on formal parameters, +we could hypothetically have an attribute like `#[unused]` that explicitly +states this for a given parameter. Note that `#[unused]` is not part of this +proposal but merely a simple use-case. In other words, we could write (1): -Also procedural macros and custom attributes could greatly benefit from having their own defined custom attributes on formal parameters. +```rust +fn foo(#[unused] bar: u32) -> bool { .. } +``` -Examples will follow. +instead of (2): + +```rust +fn foo(_bar: u32) -> bool { .. } +``` + +Especially Rust beginners might find the meaning of (1) to be clearer than (2). # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -Formal parameters of functions, methods, closures and functions in trait definitions may have attributes attached to them. -This allows to provide additional information to a given formal parameter. +Formal parameters of `fn` definitions as well closures parameters may have +attributes attached to them. Thereby, additional information may be provided. -For the next examples the hypothetical `#[unused]` attribute means that the attributed parameter is unused in the associated implementation. +For the purposes of illustration, let's assume we have the attributes +`#[orange]` and `#[lemon]` available to us. -## Examples +## Basic examples -The syntax for this is demonstrated by the code below: +The syntax for attaching attributes to parameters is shown in the snippet below: ```rust -// Function -fn foo(#[unused] bar: u32) { .. } +// Free functions: +fn foo(#[orange] bar: u32) { .. } + +impl Alpha { // In inherent implementations. + // - `self` can also be attributed: + fn bar(#[lemon] self, #[orange] x: u8) { .. } + fn baz(#[lemon] &self, #[orange] x: u8) { .. } + fn quux(#[lemon] &mut self, #[orange] x: u8) { .. } + + .. +} + +impl Beta for Alpha { // Also works in trait implementations. + fn bar(#[lemon] self, #[orange] x: u8) { .. } + fn baz(#[lemon] &self, #[orange] x: u8) { .. } + fn quux(#[lemon] &mut self, #[orange] x: u8) { .. } + + .. +} + +fn foo() { + // Closures: + let bar = |#[orange] x| { .. }; + let baz = |#[lemon] x: u8, #[orange] y| { .. }; +} +``` -// Methods & trait & definitions: -// - `self` can also be attributed -fn foo(#[unused] self, ..) { .. } -fn foo(#[unused] &self, ..) { .. } -fn foo(#[unused] &mut self, ..) { .. } +## Trait definitions -// Closures -|#[unused] x| { .. } +An `fn` definition doesn't need to have a body to permit parameter attributes. +Thus, in `trait` definitions, we may write: + +```rust +trait Beta { + fn bar(#[lemon] self, #[orange] x: u8); + fn baz(#[lemon] &self, #[orange] x: u8); + fn quux(#[lemon] &mut self, #[orange] x: u8); +} ``` -### Trait declarations +In Rust 2015, since anonymous parameters are allowed, you may also write: ```rust -fn foo(#[unused] self); +trait Beta { + fn bar(#[lemon] self, #[orange] u8); // <-- Note the absence of `x`! +} ``` -Note that while the `#[unused]` attribute is syntactically -possible to put here it doesn't actually make sense semantically -since method declarations have no implementation. -Other attributes might be very useful as for formal parameters in a method declaration. +## `fn` types -## Errors & Warnings +You can also use attributes in function pointer types. +For example, you may write: -### Warning: Unused attribute +```rust +type Foo = fn(#[orange] x: u8); +type Bar = fn(#[orange] String, #[lemon] y: String); +``` -When using an non-defined attribute that is not used by either the language or a custom defined procedural macro. +## Built-in attributes -``` -warning: unused attribute - --> src/main.rs:2 - | -2 | #[foo] bar: u32 - | ^^^^^^^^^ - | - = note: #[warn(unused_attributes)] on by default -``` +Attributes attached to formal parameters do not have an inherent meaning in +the type system or in the language. Instead, the meaning is what your +procedural macros, the tools you use, or what the compiler interprets certain +specific attributes as. -### Error: Malformed attribute +As for the built-in attributes and their semantics, we will, for the time being, +only permit the following attributes on parameters: -When using a known attribute that is not defined for formal parameters such as when attributing `fn main` with `#[allow]`. +- Lint check attributes, that is: + `#[allow(C)]`, `#[warn(C)]`, `#[deny(C)]`, `#[forbid(C)]`, + and tool lint attributes such as `#[allow(clippy::foobar)]`. -Example shows the usage of the known attribute `#[inline]` used on a formal parameter without being defined for it. +- Conditional compilation attributes: -``` -error[E0452]: malformed lint attribute - --> src/main.rs:2 - | -2 | #[inline] bar: u32 - | ^^^^^^^^ -``` + - `#[cfg_attr(...)]`, e.g. + + ```rust + fn foo(#[cfg_attr(bar, orange)] x: u8) { .. } + ``` + + If `bar` is active, this is equivalent to: + + ```rust + fn foo(#[orange] x: u8) { .. } + ``` + + And otherwise equivalent to: -The same applies for attributes with an incorrect format such as `#[inline(key = value)]` that is handled as its done in other contexts. + ```rust + fn foo(x: u8) { .. } + ``` + - `#[cfg(...)]`, e.g. + + ```rust + fn foo(#[cfg(bar)] x: u8, y: u16) { .. } + ``` + + If `bar` is active, this is equivalent to: + + ```rust + fn foo(x: u8, y: u16) { .. } + ``` + + And otherwise equivalent to: + + ```rust + fn foo(y: u16) { .. } + ``` + +All other built-in attributes will be rejected with a semantic check. +For example, you may not write: + +```rust +fn foo(#[inline] bar: u32) { .. } +``` # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -## Description +## Grammar + +Let `OuterAttr` denote the production for an attribute `#[...]`. + +On the formal parameters of an `fn` item, including on method receivers, +and irrespective of whether the `fn` has a body or not, `OuterAttr+` is allowed. +For example, all the following are valid: + +```rust +fn g1(#[attr1] #[attr2] pat: Type) { .. } + +fn g2(#[attr1] x: u8) { .. } -In accordance to the RFC for [attributes for generic params](https://github.com/frol/rust-rfcs/blob/master/text/1327-dropck-param-eyepatch.md) -this feature is guarded by the `formal_function_param_attrs` feature guard. +fn g3(#[attr] self) { .. } -The grammar of the following language items has to be adjusted to allow that -constructions like the following will become legal. +fn g4(#[attr] &self) { .. } -- Function definitions -- Method definitions -- Trait function declarations and defnitions (with or without default impl) -- Closure definitions +fn g5<'a>(#[attr] &mut self) { .. } -### Example: A single attributed parameter for function decl or definition +fn g6<'a>(#[attr] &'a self) { .. } + +fn g7<'a>(#[attr] &'a mut self) { .. } + +fn g8(#[attr] self: Self) { .. } +``` + +The attributes here apply to the parameter *as a whole*, +e.g. in `g2`, `#[attr]` applies to `pat: Type` as opposed to `pat`. + +### Variadics + +Attributes may also be attached to `...` on variadic functions, e.g. + +```rust +extern "C" { + fn foo(x: u8, #[attr] ...); +} +``` + +### Anonymous parameters in Rust 2015 + +In Rust 2015 edition, as `fn`s may have anonymous parameters, e.g. + +```rust +trait Foo { fn bar(u8); } +``` + +attributes are allowed on those, e.g. ```rust -fn foo(#[bar] baz: bool); -fn bar(#[bar] qux: bool) { println!("hi"); } +trait Foo { fn bar(#[attr] u8); } ``` -### Example: For methods or trait function definitions +### `fn` pointers + +[lykenware/gll]: https://github.com/lykenware/gll/ + +Assuming roughly the following type grammar for function pointers +(in the [lykenware/gll] notation): ```rust -fn into_foo(#[bar] self); -fn foo(#[bar] &self); -fn foo_mut(#[bar] &mut self); +Type = + | .. + | FnPtr:{ + binder:ForAllBinder? unsafety:"unsafe"? { "extern" abi:Abi }? + "fn" "(" inputs:FnSigInputs? ","? ")" { "->" ret_ty:Type }? + } + ; + +FnSigInputs = + | Regular:FnSigInput+ % "," + | Variadic:VaradicTail + | RegularAndVariadic:{ inputs:FnSigInput+ % "," "," "..." } + ; + +VaradicTail = "..."; +FnSigInput = { pat:Pat ":" }? ty:Type; ``` -### Example: Multiple attributed parameters +we change `VaradicTail` to: ```rust -// Twice the same attribute -fn fst_foo(#[bar] baz: bool, #[bar] qiz: u32); +VaradicTail = OuterAttr* "..."; +``` -// Different attributes -fn snd_foo(#[bar] baz: bool, #[qux] qiz: u32); +and change `FnSigInput` to: + +```rust +FnSigInput = OuterAttr* { pat:Pat ":" }? ty:Type; ``` -### Example: Any structured attribute +Similar to parameters in `fn` items, the attributes here also apply to the +pattern and the type if both are present, i.e. `pat: ty` as opposed to `pat`. + +### Closures + +Given roughly the following expression grammar for closures: ```rust -fn foo(#[bar(Hello)] baz: bool); -fn bar(#[qux(qiz = World)] baz: bool); +Expr = attrs:OuterAttr* kind:ExprKind; +ExprKind = + | .. + | Closure:{ + by_val:"move"? + "|" args:ClosureArg* % "," ","? "|" { "->" ret_ty:Type }? body:Expr + } + ; + +ClosureArg = pat:Pat { ":" ty:Type }?; ``` -### Example: Closures +we change `ClosureArg` into: ```rust -let mut v = [5, 4, 1, 3, 2]; -v.sort_by(|#[bar] a, #[baz] b| a.cmp(b)); +ClosureArg = OuterAttr* pat:Pat { ":" ty:Type }?; ``` -## Errors +As before, when the type is specified, `OuterAttr*` applies to `pat: Type` +as opposed to just `pat`. + +## Static semantics + +Attributes on formal parameters of functions, closures and function pointers +have no inherent meaning in the type system or elsewhere. Semantics, if there +are any, are given by the attributes themselves on a case by case basis or by +tools external to a Rust compiler. + +### Built-in attributes + +The built-in attributes that are permitted on the parameters are: + +1. lint check attributes including tool lint attributes. + +2. `cfg_attr(..)` unconditionally. -Users can encounter two different errorneous situations. +3. `cfg(..)` unconditionally. -### Unknown attribute used + When a `cfg(..)` is active, the formal parameter will be included + whereas if it is inactive, the formal parameter will be excluded. -When a user is using an attribute that is not known at the point of its invokation -a warning is generated similar to other usages of unknown attributes in the language. +All other built-in attributes are for the time being rejected with a *semantic* +check resulting in a compilation error. -This may for example happen in the context of a procedural macros. +### Macro attributes -### Malformed attribute +Finally, a registered `#[proc_macro_attribute]` may not be attached directly +to a formal parameter. For example, if given: -When a user is using a known or language defined attribute at a non supported location -an error is generated like in other usages of malformed attributes in the language. -An example can be seen in the previous section. +```rust +#[proc_macro_attribute] +pub fn attr(args: TokenStream, input: TokenStream) -> TokenStream { .. } +``` + +then it is not legal to write: + +```rust +fn foo(#[attr] x: u8) { .. } +``` + +## Dynamic semantics +No changes. # Drawbacks [drawbacks]: #drawbacks @@ -233,40 +485,97 @@ Having attributes in many different places of the language complicates its gramm ## Why is this proposal considered the best in the space of available ideas? -This proposal clearly goes the path of having attributes in more places of the language. -It nicely plays together with the advance of procedural macros and macros 2.0 where users -can define their own attributes for their special purposes. +This proposal goes the path of having attributes in more places of the language. +It nicely plays together with the advance of procedural macros and macros 2.0 +where users can define their own attributes for their special purposes. ## Alternatives -An alternative to having attributes for formal parameters might be to just use the current -set of available attributable items to store meta information about formal parameters like -in the following example: +An alternative to having attributes for formal parameters might be to just use +the current set of available attributable items to store meta information about +formal parameters like in the following example: ```rust #[ignore(param = bar)] fn foo(bar: bool); ``` -Note that this does not work in all situations (for example closures) and might involve even -more complexity in user's code than simply allowing formal function parameter attributes. +An example of this is `#[rustc_args_required_const]` as discussed +in the [motivation]. + +Note that this does not work in all situations (for example closures) and might +involve even more complexity in user's code than simply allowing attributes on +formal function parameters. ## Impact -The impact will most certainly be that users might create custom attributes when -designing procedural macros involving formal function parameters. +The impact will be that users might create custom attributes when designing +procedural macros involving formal function parameters. There should be no breakage of existing code. +## Variadics and `fn` pointers + +In this proposal it is legal to write `#[attr] ...` as well as `fn(#[attr] u8)`. +The primary justification for doing so is that conditional compilation with +`#[cfg(..)]` is facilitated. Moreover, since the `fn` type grammar and +that of `fn` items is somewhat shared, and since `...` is the tail of a +list, allowing attributes there makes for a simpler grammar. + # Prior art [prior-art]: #prior-art -Some example languages that allows for attributes in formal function parameter positions are C# and C++. +Some example languages that allow for attributes on formal function parameter +positions are Java, C#, and C++. -Also note that attributes in other parts of the Rust language could be considered prior art to this proposal. +Also note that attributes in other parts of the Rust language could be +considered prior art to this proposal. # Unresolved questions [unresolved-questions]: #unresolved-questions -We might want to introduce new attributes for the language like the mentioned `#[unused]` attribute. -However, this RFC proposes to decide upon this in another RFC. +None as of yet. + +# Future possibilities + +## Attributes in more places + +[RFC 2602]: https://github.com/rust-lang/rfcs/pull/2602 + +In the pursuit of allowing more flexible DSLs and more ergonomic conditional +compilation, [RFC 2602] builds upon this RFC. + +## Documentation comments + +In this RFC, we have not allowed documentation comments on parameters. +For example, you may not write: + +```rust +fn foo( + /// Some description about `bar`. + bar: u32 +) { + .. +} +``` + +Neither may you write the desugared form: + +```rust +fn foo( + #[doc = "Some description about `bar`."] + bar: u32 +) { + .. +} +``` + +In the future, we may want to consider supporting this form of documentation. +This will require support in `rustdoc` to actually display the information. + +## `#[proc_macro_attribute]` + +In this RFC we stated that `fn foo(#[attr] x: u8) { .. }`, +where `#[attr]` is a `#[proc_macro_attribute]` is not allowed. +In the future, if use cases arise to justify a change, we could lift this +restriction such that transformations can be done directly on `x: u8`. From d4ff5445da83900e2cea8db3c071711985aaed55 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Mon, 18 Feb 2019 00:49:35 +0100 Subject: [PATCH 8/9] rfc/param-attrs: clarify grammar. --- text/2540-formal-function-parameter-attributes.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2540-formal-function-parameter-attributes.md index 5f445f846aa..269513cdb7a 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2540-formal-function-parameter-attributes.md @@ -315,8 +315,8 @@ fn foo(#[inline] bar: u32) { .. } Let `OuterAttr` denote the production for an attribute `#[...]`. On the formal parameters of an `fn` item, including on method receivers, -and irrespective of whether the `fn` has a body or not, `OuterAttr+` is allowed. -For example, all the following are valid: +and irrespective of whether the `fn` has a body or not, `OuterAttr+` is allowed +but not required. For example, all the following are valid: ```rust fn g1(#[attr1] #[attr2] pat: Type) { .. } @@ -334,11 +334,17 @@ fn g6<'a>(#[attr] &'a self) { .. } fn g7<'a>(#[attr] &'a mut self) { .. } fn g8(#[attr] self: Self) { .. } + +fn g9(#[attr] self: Rc) { .. } ``` The attributes here apply to the parameter *as a whole*, e.g. in `g2`, `#[attr]` applies to `pat: Type` as opposed to `pat`. +More generally, an `fn` item contains a list of formal parameters separated or +terminated by `,` and delimited by `(` and `)`. Each parameter in that list may +optionally be prefixed by `OuterAttr+`. + ### Variadics Attributes may also be attached to `...` on variadic functions, e.g. @@ -349,6 +355,8 @@ extern "C" { } ``` +That is, for the purposes of this RFC, `...` is considered as a parameter. + ### Anonymous parameters in Rust 2015 In Rust 2015 edition, as `fn`s may have anonymous parameters, e.g. From 59dc355606294962d7033f51c0c134ad165967ab Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 30 Apr 2019 12:09:36 +0200 Subject: [PATCH 9/9] RFC 2565 --- ...ibutes.md => 2565-formal-function-parameter-attributes.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{2540-formal-function-parameter-attributes.md => 2565-formal-function-parameter-attributes.md} (98%) diff --git a/text/2540-formal-function-parameter-attributes.md b/text/2565-formal-function-parameter-attributes.md similarity index 98% rename from text/2540-formal-function-parameter-attributes.md rename to text/2565-formal-function-parameter-attributes.md index 269513cdb7a..759d710e3f3 100644 --- a/text/2540-formal-function-parameter-attributes.md +++ b/text/2565-formal-function-parameter-attributes.md @@ -1,7 +1,7 @@ - Feature Name: `param_attrs` - Start Date: 2018-10-14 -- RFC PR: -- Rust Issue: +- RFC PR: [rust-lang/rfcs#2565](https://github.com/rust-lang/rfcs/pull/2565) +- Rust Issue: [rust-lang/rust#60406](https://github.com/rust-lang/rust/issues/60406) # Summary [summary]: #summary