From 684fae5bea295144d33eeb160509b9a0bb95d022 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 00:14:52 -0700 Subject: [PATCH 01/36] Simple postfix macros This RFC introduces simple postfix macros, of the form `expr.ident!()`, to make macro invocations more readable and maintainable in left-to-right method chains. In particular, this proposal will make it possible to write chains like `future().await!().further_computation().await!()`, potentially with `?` interspersed as well; these read conveniently from left to right rather than alternating between the right and left sides of the expression. I believe this proposal will allow more in-depth experimentation in the crate ecosystem with features that would otherwise require compiler changes, such as introducing new postfix control-flow mechanisms analogous to `?`. --- text/0000-simple-postfix-macros.md | 171 +++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 text/0000-simple-postfix-macros.md diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md new file mode 100644 index 00000000000..268225d3bc8 --- /dev/null +++ b/text/0000-simple-postfix-macros.md @@ -0,0 +1,171 @@ +- Feature Name: `simple_postfix_macros` +- Start Date: 2018-05-12 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Allow simple postfix macros, of the form `expr.ident!()`, to make macro +invocations more readable and maintainable in left-to-right method chains. + +# Motivation +[motivation]: #motivation + +The transition from `try!` to `?` doesn't just make error handling more +concise; it allows reading expressions from left to right. An expression like +`try!(try!(try!(foo()).bar()).baz())` required the reader to go back and forth +between the left and right sides of the expression, and carefully match +parentheses. The equivalent `foo()?.bar()?.baz()?` allows reading from left to +right. + +The introduction of `await!` in RFC 2394 brings back the same issue, in the +form of expressions like `await!(await!(await!(foo()).bar()).baz())`. This RFC +would allow creating a postfix form of any such macro, simplifying that +expression into a more readable `foo().await!().bar().await!().baz().await!()`. + +Previous discussions of method-like macros have stalled in the process of +attempting to combine properties of macros (such as unevaluated arguments) with +properties of methods (such as type-based or trait-based dispatch). This RFC +proposes a minimal change to the macro system that allows defining a simple +style of postfix macro, designed specifically for `await!` and for future cases +like `try!` and `await!`, without blocking potential future extensions. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +When defining a macro using `macro_rules!`, you can include a first argument +that uses a designator of `self` (typically `$self:self`). This must appear as +the first argument, and outside any repetition. If the macro includes such a +case, then Rust code may invoke that macro using the method-like syntax +`expr.macro!(args)`. The Rust compiler will expand the macro into code that +receives the evaluated `expr` as its first argument. + +```rust +macro_rules! log_value { + ($self:self, $msg:expr) => ({ + eprintln!("{}:{}: {}: {:?}", file!(), line!(), $msg, $self); + $self + }) +} + +fn value(x: T) -> T { + println!("evaluated {:?}", x); + x +} + +fn main() { + value("hello").log_value!("value").len().log_value!("len"); +} +``` + +This will print: + +``` +evaluated "hello" +src/main.rs:14: value: "hello" +src/main.rs:14: len: 5 +``` + +Notice that `"hello"` only gets evaluated once, rather than once per reference +to `$self`, and that the `file!` and `line!` macros refer to the locations of +the invocations of `log_value!`. + +A macro that accepts multiple combinations of arguments may accept `$self` in +some variations and not in others. For instance, `await!` could allow both of +the following: + +```rust +await!(some_future()); +some_other_future().await!().further_future_computation().await!(); +``` + +This method-like syntax allows macros to cleanly integrate in a left-to-right +method chain, while still making use of control flow and other features that +only a macro can provide. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +When expanding a postfix macro, the compiler will effectively create a +temporary binding for the value of `$self`, and substitute that binding +for each expansion of `$self`. This stands in contrast to other macro +arguments, which get expanded into the macro body without evaluation. This +change avoids any potential ambiguities regarding the scope of the `$self` +argument and how much it leaves unevaluated, by evaluating it fully. + +The `await!` macro, whether defined in Rust code or built into the compiler, +would effectively have the following two cases: + +```rust +macro_rules! await { + ($e:expr) => ({ + // ... Current body of await! ... + }) + ($self:$self) => ( + await!($self) + ) +} +``` + +Note that postfix macros cannot dispatch differently based on the type of the +expression they're invoked on. This includes whether the expression has type +`T`, `&T`, or `&mut T`. The internal binding the compiler creates for that +expression will have that same type. + +Calling `stringify!` on `$self` will return `"$self"`. + +Using the `self` designator on any macro argument other than the first will +produce a compile-time error. + +Wrapping any form of repetition around the `self` argument will produce a +compile-time error. + +# Drawbacks +[drawbacks]: #drawbacks + +Creating a new kind of macro, and a new argument designator (`self`) that gets +evaluated at a different time, adds complexity to the macro system. + +No equivalent means exists to define a postfix proc macro; this RFC +intentionally leaves specification of such means to future RFCs, for future +development and experimentation. + +# Rationale and alternatives +[alternatives]: #alternatives + +Rather than this minimal approach, we could define a full postfix macro system +that allows processing the preceding expression without evaluation. This would +require specifying how much of the preceding expression to process unevaluated, +including chains of such macros. The approach proposed in this RFC does not +preclude specifying a richer system in the future; such a future system could +use a designator other than `self`, or could easily extend this syntax to add +further qualifiers on `self` (for instance, `$self:self:another_designator` or +`$self:self(argument)`). + +We could define a built-in postfix macro version of `await!`, without providing +a means for developers to define their own postfix macros. This would +also prevent developers from. + +We could define a new postfix operator for `await!`, analogous to `?`. This +would require selecting and assigning an appropriate symbol. This RFC allows +fitting constructs that affect control flow into method chains without +elevating them to a terse symbolic operator. + +We could do nothing at all, and leave `await!` in its current macro form, or +potentially change it into a language keyword in the future. In this case, the +problem of integrating `await` with method chains will remain. + +# Prior art +[prior-art]: #prior-art + +The evolution of `try!` into `?` serves as prior art for moving an important +macro-style control-flow mechanism from prefix to postfix. `await!` has similar +properties, and has already prompted discussions both of how to move it to +postfix and how to integrate it with error handling using `?`. + +# Unresolved questions +[unresolved]: #unresolved-questions + +- Should we define a means of creating postfix proc macros, or can we defer that? +- Does evaluating `$self` create any other corner cases besides `stringify!`? From 86c29a337a1df3879610c1120eeea20b1fa9ed03 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 20:14:15 -0700 Subject: [PATCH 02/36] Fix typo: $self:$self -> $self:self --- text/0000-simple-postfix-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 268225d3bc8..e91cc6748b1 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -102,7 +102,7 @@ macro_rules! await { ($e:expr) => ({ // ... Current body of await! ... }) - ($self:$self) => ( + ($self:self) => ( await!($self) ) } From bdf9e4112b83b69ca2acf603b13e073748fb5d62 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 20:17:41 -0700 Subject: [PATCH 03/36] Finish incomplete sentence --- text/0000-simple-postfix-macros.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index e91cc6748b1..5677069b9ff 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -144,8 +144,12 @@ further qualifiers on `self` (for instance, `$self:self:another_designator` or `$self:self(argument)`). We could define a built-in postfix macro version of `await!`, without providing -a means for developers to define their own postfix macros. This would -also prevent developers from. +a means for developers to define their own postfix macros. This would address +the specific issue with `await!`, but would not help developers create +solutions for similar future issues. This would perpetuate the problem of +requiring changes to the language and compiler to solve such problems, rather +than allowing developers to experiment with solutions in the broader Rust +ecosystem. We could define a new postfix operator for `await!`, analogous to `?`. This would require selecting and assigning an appropriate symbol. This RFC allows From e35414c4367b56d3fcbc362df3dfb74c934f3da3 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 20:26:07 -0700 Subject: [PATCH 04/36] Explain the special-case for `stringify!` --- text/0000-simple-postfix-macros.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 5677069b9ff..0b5d384d0b7 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -113,7 +113,8 @@ expression they're invoked on. This includes whether the expression has type `T`, `&T`, or `&mut T`. The internal binding the compiler creates for that expression will have that same type. -Calling `stringify!` on `$self` will return `"$self"`. +Since `$self` represents an internal temporary location created by the +compiler, calling `stringify!` on `$self` will just return `"$self"`. Using the `self` designator on any macro argument other than the first will produce a compile-time error. From 423237ecfcf735e91f0a8f74394f17455d0adf02 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 20:34:14 -0700 Subject: [PATCH 05/36] Provide further rationale for forcing the evaluation of `$self` --- text/0000-simple-postfix-macros.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 0b5d384d0b7..3a37e0b5948 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -138,11 +138,17 @@ development and experimentation. Rather than this minimal approach, we could define a full postfix macro system that allows processing the preceding expression without evaluation. This would require specifying how much of the preceding expression to process unevaluated, -including chains of such macros. The approach proposed in this RFC does not -preclude specifying a richer system in the future; such a future system could -use a designator other than `self`, or could easily extend this syntax to add -further qualifiers on `self` (for instance, `$self:self:another_designator` or -`$self:self(argument)`). +including chains of such macros. Furthermore, unlike existing macros, which +wrap *around* the expression whose evaluation they modify, if a postfix macro +could arbitrarily control the evaluation of the method chain it postfixed, such +a macro could change the interpretation of an arbitrarily long expression that +it appears at the *end* of, which has the potential to create significantly +more confusion when reading the code. + +The approach proposed in this RFC does not preclude specifying a richer system +in the future; such a future system could use a designator other than `self`, +or could easily extend this syntax to add further qualifiers on `self` (for +instance, `$self:self:another_designator` or `$self:self(argument)`). We could define a built-in postfix macro version of `await!`, without providing a means for developers to define their own postfix macros. This would address From b4544df82654b53c43644037a1f9bf1b33735160 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 20:59:01 -0700 Subject: [PATCH 06/36] Explain use of `$self:self` rather than `$self` Add an alternative for `$self` and explain the upsides and downsides. --- text/0000-simple-postfix-macros.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 3a37e0b5948..7599d13214d 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -146,9 +146,9 @@ it appears at the *end* of, which has the potential to create significantly more confusion when reading the code. The approach proposed in this RFC does not preclude specifying a richer system -in the future; such a future system could use a designator other than `self`, -or could easily extend this syntax to add further qualifiers on `self` (for -instance, `$self:self:another_designator` or `$self:self(argument)`). +in the future; such a future system could use a new designator other than +`self`, or could easily extend this syntax to add further qualifiers on `self` +(for instance, `$self:self:another_designator` or `$self:self(argument)`). We could define a built-in postfix macro version of `await!`, without providing a means for developers to define their own postfix macros. This would address @@ -167,6 +167,14 @@ We could do nothing at all, and leave `await!` in its current macro form, or potentially change it into a language keyword in the future. In this case, the problem of integrating `await` with method chains will remain. +In the syntax to define a postfix macro, we could use just `$self` rather than +`$self:self`. `$self` is not currently valid syntax, so we could use it for +this purpose without affecting any existing valid macro. This would make such +declarations look closer to a method declaration, which uses `self` without a +type. However, macros do currently allow `self` as the name of a macro argument +when used with a designator, such as `$self:expr`; this could lead to potential +confusion, and would preclude some approaches for future extension. + # Prior art [prior-art]: #prior-art From b0605db5701086941a1e9c03a2155b93d20d3130 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 21:14:55 -0700 Subject: [PATCH 07/36] Document that `$self:self` must have either a comma or close paren after it --- text/0000-simple-postfix-macros.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 7599d13214d..b2f31fe638d 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -122,6 +122,12 @@ produce a compile-time error. Wrapping any form of repetition around the `self` argument will produce a compile-time error. +If the `$self:self` argument does not appear by itself in the macro argument +list (`($self:self)`, with the closing parenthesis as the next token after +`$self:self`), then it must have a `,` immediately following it, prior to any +other tokens. Any subsequent tokens after the `,` will match what appears +between the delimiters after the macro name in its invocation. + # Drawbacks [drawbacks]: #drawbacks From 221de8eb5cf6810d14d4b18813fea3ee0811d16f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 21:15:53 -0700 Subject: [PATCH 08/36] Explicitly allow `$othername:self` but suggest `$self:self` by convention --- text/0000-simple-postfix-macros.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index b2f31fe638d..b61fb2e1eb3 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -128,6 +128,9 @@ list (`($self:self)`, with the closing parenthesis as the next token after other tokens. Any subsequent tokens after the `,` will match what appears between the delimiters after the macro name in its invocation. +A macro may attach the designator `self` to a parameter not named `$self`, such +as `$x:self`. Using `$self:self` is a convention, not a requirement. + # Drawbacks [drawbacks]: #drawbacks From 0ebe83e3c6ff7b9403667aba36d48e47d72dd09f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 23:00:12 -0700 Subject: [PATCH 09/36] Specify what designators `$self` will match if passed to another macro --- text/0000-simple-postfix-macros.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index b61fb2e1eb3..7c61737a9f8 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -114,7 +114,9 @@ expression they're invoked on. This includes whether the expression has type expression will have that same type. Since `$self` represents an internal temporary location created by the -compiler, calling `stringify!` on `$self` will just return `"$self"`. +compiler, calling `stringify!` on `$self` will just return `"$self"`. If passed +to another macro, `$self` will only match a macro argument using a designator +of `:expr`, `:tt`, or `:self`. Using the `self` designator on any macro argument other than the first will produce a compile-time error. From 24999bbb46d21bc976926e34be6c5316b53fbe90 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 15 May 2018 23:41:37 -0700 Subject: [PATCH 10/36] Explicitly state that a postfix macro can use any delimiters --- text/0000-simple-postfix-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 7c61737a9f8..83bcee2da14 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -133,6 +133,10 @@ between the delimiters after the macro name in its invocation. A macro may attach the designator `self` to a parameter not named `$self`, such as `$x:self`. Using `$self:self` is a convention, not a requirement. +A postfix macro invocation, like any other macro invocation, may use any form +of delimiters around the subsequent arguments: parentheses (`expr.m!()`), +braces (`expr.m!{}`), or square brackets (`expr.m![]`). + # Drawbacks [drawbacks]: #drawbacks From dac094302b531c5d732fc1b02fe92173b0cd4ecf Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 16 May 2018 13:22:37 -0700 Subject: [PATCH 11/36] Clarify a reference to await --- text/0000-simple-postfix-macros.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 83bcee2da14..da694290fff 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -180,7 +180,8 @@ elevating them to a terse symbolic operator. We could do nothing at all, and leave `await!` in its current macro form, or potentially change it into a language keyword in the future. In this case, the -problem of integrating `await` with method chains will remain. +problem of integrating `await` and similar constructs with method chains will +remain. In the syntax to define a postfix macro, we could use just `$self` rather than `$self:self`. `$self` is not currently valid syntax, so we could use it for From cd18fc94d1f8e93f46f52383ede2914956658955 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 16 May 2018 13:22:55 -0700 Subject: [PATCH 12/36] Add a potential alternative syntax, diverging more from method call syntax --- text/0000-simple-postfix-macros.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index da694290fff..770e2e0ba7a 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -178,6 +178,13 @@ would require selecting and assigning an appropriate symbol. This RFC allows fitting constructs that affect control flow into method chains without elevating them to a terse symbolic operator. +Rather than writing `expr.macroname!()`, we could write `expr.!macroname()` or +similar, placing the `!` closer to the `.` of the method call. This would place +greater attention on the invocation, but would break the similarity with +existing macro naming that people have grown accustomed to spotting when +reading code. This also seems more likely to get confused with the prefix unary +`!` operator. + We could do nothing at all, and leave `await!` in its current macro form, or potentially change it into a language keyword in the future. In this case, the problem of integrating `await` and similar constructs with method chains will From 81e1e8df6198b1e76d574eb26d3aa1a1e21dc2fc Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 16 May 2018 13:24:16 -0700 Subject: [PATCH 13/36] Further discussion of postfix macro syntax and non-type-based dispatch --- text/0000-simple-postfix-macros.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 770e2e0ba7a..cf479c2663b 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -147,6 +147,20 @@ No equivalent means exists to define a postfix proc macro; this RFC intentionally leaves specification of such means to future RFCs, for future development and experimentation. +Macros have historically not interacted with the type system, while method +calls (`expr.method()`) do type-based dispatch based on the type or trait of +the expression. In introducing postfix macros that look similar to method +calls, this proposal does not attempt to introduce type-based dispatch of +macros at the same time; an invocation `expr.m!()` does not in any way depend +on the type of `expr` (though the expansion of the macro may have expectations +about that type that the compiler will still enforce). A future RFC may +introduce type-based dispatch for postfix macros; however, any such future RFC +seems likely to still provide a means of writing postfix macros that apply to +any type. This RFC provides a means to implement that subset of postfix macros +that apply to any type, enabling a wide range of language experimentation +(particularly in readability and usability) that has previously required +changes to the language itself. + # Rationale and alternatives [alternatives]: #alternatives From be68f24d8726a47d6dcd67597cf1b0e81871f041 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 21 May 2018 15:31:15 -0700 Subject: [PATCH 14/36] Clarify that postfix macros follow existing macro scope rules --- text/0000-simple-postfix-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index cf479c2663b..f97a989f024 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -113,6 +113,10 @@ expression they're invoked on. This includes whether the expression has type `T`, `&T`, or `&mut T`. The internal binding the compiler creates for that expression will have that same type. +Macros defined using this mechanism follow exactly the same namespace and +scoping rules as any other macro. If a macro accepting a `$self:self` argument +is in scope, Rust code may call it on any object. + Since `$self` represents an internal temporary location created by the compiler, calling `stringify!` on `$self` will just return `"$self"`. If passed to another macro, `$self` will only match a macro argument using a designator From f32688f3a1ded4344b34e24850d48d5b77c19d65 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 21 May 2018 15:34:33 -0700 Subject: [PATCH 15/36] Explicitly state that this doesn't block future type-based dispatch --- text/0000-simple-postfix-macros.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index f97a989f024..5ae45bb5f6e 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -29,7 +29,9 @@ attempting to combine properties of macros (such as unevaluated arguments) with properties of methods (such as type-based or trait-based dispatch). This RFC proposes a minimal change to the macro system that allows defining a simple style of postfix macro, designed specifically for `await!` and for future cases -like `try!` and `await!`, without blocking potential future extensions. +like `try!` and `await!`, without blocking potential future extensions. In +particular, this RFC does not in any way preclude a future implementation of +postfix macros with full type-based or trait-based dispatch. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From c9cb3be4f8321c659b088e1860c4833ffd49df31 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 7 Nov 2020 15:53:46 -0800 Subject: [PATCH 16/36] Clarify type inference --- text/0000-simple-postfix-macros.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 5ae45bb5f6e..32bdf840193 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -113,7 +113,9 @@ macro_rules! await { Note that postfix macros cannot dispatch differently based on the type of the expression they're invoked on. This includes whether the expression has type `T`, `&T`, or `&mut T`. The internal binding the compiler creates for that -expression will have that same type. +expression will participate in type inference as normal, including the expanded +body of the macro. If the compiler cannot unambiguously determine the type of +the internal binding, it will produce a compile-time error. Macros defined using this mechanism follow exactly the same namespace and scoping rules as any other macro. If a macro accepting a `$self:self` argument From fcf7a571317a25738d4efe659da04ad6180ca070 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 7 Nov 2020 15:57:30 -0800 Subject: [PATCH 17/36] Add further guidance about proc macros --- text/0000-simple-postfix-macros.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 32bdf840193..168da6d1b56 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -153,7 +153,8 @@ evaluated at a different time, adds complexity to the macro system. No equivalent means exists to define a postfix proc macro; this RFC intentionally leaves specification of such means to future RFCs, for future -development and experimentation. +development and experimentation. A postfix macro can trivially forward its +arguments to a proc macro. Macros have historically not interacted with the type system, while method calls (`expr.method()`) do type-based dispatch based on the type or trait of From 68d7673720144e84ab7d40da524dad3023ceb5cb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 7 Nov 2020 15:59:00 -0800 Subject: [PATCH 18/36] Rewrite to cover .await as precedent `await!(expr)` became `expr.await`; refer to that as precedent, and remove passages suggesting the use of postfix macros for await syntax. --- text/0000-simple-postfix-macros.md | 67 +++++++++--------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 168da6d1b56..5aae36b89bb 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -19,19 +19,23 @@ between the left and right sides of the expression, and carefully match parentheses. The equivalent `foo()?.bar()?.baz()?` allows reading from left to right. -The introduction of `await!` in RFC 2394 brings back the same issue, in the -form of expressions like `await!(await!(await!(foo()).bar()).baz())`. This RFC -would allow creating a postfix form of any such macro, simplifying that -expression into a more readable `foo().await!().bar().await!().baz().await!()`. +However, unlike macro syntax, postfix syntax can *only* be used by language +extensions; it's not possible to experiment with postfix syntax in macro-based +extensions to the language. Language changes often start through +experimentation, and such experimentation can also result in sufficiently good +alternatives to avoid requiring language extensions. Having a postfix macro +syntax would serve both goals: enabling the prototyping of new language +features, and providing compelling syntax without having to extend the +language. Previous discussions of method-like macros have stalled in the process of attempting to combine properties of macros (such as unevaluated arguments) with properties of methods (such as type-based or trait-based dispatch). This RFC proposes a minimal change to the macro system that allows defining a simple -style of postfix macro, designed specifically for `await!` and for future cases -like `try!` and `await!`, without blocking potential future extensions. In -particular, this RFC does not in any way preclude a future implementation of -postfix macros with full type-based or trait-based dispatch. +style of postfix macro, inspired specifically by `try!(expr)` becoming `expr?` +and `await!(expr)` becoming `expr.await`, without blocking potential future +extensions. In particular, this RFC does not in any way preclude a future +implementation of postfix macros with full type-based or trait-based dispatch. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -74,12 +78,12 @@ to `$self`, and that the `file!` and `line!` macros refer to the locations of the invocations of `log_value!`. A macro that accepts multiple combinations of arguments may accept `$self` in -some variations and not in others. For instance, `await!` could allow both of -the following: +some variations and not in others. For instance, a macro `do_thing` could allow +both of the following: ```rust -await!(some_future()); -some_other_future().await!().further_future_computation().await!(); +do_thing!(some_expression()); +some_other_expression().do_thing!().further_expression().do_thing!(); ``` This method-like syntax allows macros to cleanly integrate in a left-to-right @@ -96,20 +100,6 @@ arguments, which get expanded into the macro body without evaluation. This change avoids any potential ambiguities regarding the scope of the `$self` argument and how much it leaves unevaluated, by evaluating it fully. -The `await!` macro, whether defined in Rust code or built into the compiler, -would effectively have the following two cases: - -```rust -macro_rules! await { - ($e:expr) => ({ - // ... Current body of await! ... - }) - ($self:self) => ( - await!($self) - ) -} -``` - Note that postfix macros cannot dispatch differently based on the type of the expression they're invoked on. This includes whether the expression has type `T`, `&T`, or `&mut T`. The internal binding the compiler creates for that @@ -188,19 +178,6 @@ in the future; such a future system could use a new designator other than `self`, or could easily extend this syntax to add further qualifiers on `self` (for instance, `$self:self:another_designator` or `$self:self(argument)`). -We could define a built-in postfix macro version of `await!`, without providing -a means for developers to define their own postfix macros. This would address -the specific issue with `await!`, but would not help developers create -solutions for similar future issues. This would perpetuate the problem of -requiring changes to the language and compiler to solve such problems, rather -than allowing developers to experiment with solutions in the broader Rust -ecosystem. - -We could define a new postfix operator for `await!`, analogous to `?`. This -would require selecting and assigning an appropriate symbol. This RFC allows -fitting constructs that affect control flow into method chains without -elevating them to a terse symbolic operator. - Rather than writing `expr.macroname!()`, we could write `expr.!macroname()` or similar, placing the `!` closer to the `.` of the method call. This would place greater attention on the invocation, but would break the similarity with @@ -208,11 +185,6 @@ existing macro naming that people have grown accustomed to spotting when reading code. This also seems more likely to get confused with the prefix unary `!` operator. -We could do nothing at all, and leave `await!` in its current macro form, or -potentially change it into a language keyword in the future. In this case, the -problem of integrating `await` and similar constructs with method chains will -remain. - In the syntax to define a postfix macro, we could use just `$self` rather than `$self:self`. `$self` is not currently valid syntax, so we could use it for this purpose without affecting any existing valid macro. This would make such @@ -224,10 +196,9 @@ confusion, and would preclude some approaches for future extension. # Prior art [prior-art]: #prior-art -The evolution of `try!` into `?` serves as prior art for moving an important -macro-style control-flow mechanism from prefix to postfix. `await!` has similar -properties, and has already prompted discussions both of how to move it to -postfix and how to integrate it with error handling using `?`. +The evolution of `try!(expr)` into `expr?`, and the evolution of `await!(expr)` +into `expr.await`, both serve as prior art for moving an important macro-style +control-flow mechanism from prefix to postfix. # Unresolved questions [unresolved]: #unresolved-questions From 3e992873f4d6cd64ed9fcf97166db9b491e9071f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 7 Nov 2020 16:12:54 -0800 Subject: [PATCH 19/36] Observe that macros can use let bindings for type constraints --- text/0000-simple-postfix-macros.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 5aae36b89bb..e673ebd4f6b 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -105,7 +105,9 @@ expression they're invoked on. This includes whether the expression has type `T`, `&T`, or `&mut T`. The internal binding the compiler creates for that expression will participate in type inference as normal, including the expanded body of the macro. If the compiler cannot unambiguously determine the type of -the internal binding, it will produce a compile-time error. +the internal binding, it will produce a compile-time error. If the macro wishes +to constrain the type of `$self`, it can do so by writing a `let` binding for +`$self` with the desired type. Macros defined using this mechanism follow exactly the same namespace and scoping rules as any other macro. If a macro accepting a `$self:self` argument From f2d4bd0d1d3205ffb1a342fcbd295f396c834b5f Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 7 Nov 2020 16:13:06 -0800 Subject: [PATCH 20/36] Provide a full example of expansion --- text/0000-simple-postfix-macros.md | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index e673ebd4f6b..05965a7c701 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -100,6 +100,46 @@ arguments, which get expanded into the macro body without evaluation. This change avoids any potential ambiguities regarding the scope of the `$self` argument and how much it leaves unevaluated, by evaluating it fully. +For example, given the following macro definition: + +```rust +macro_rules! log_value { + ($self:self, $msg:expr) => ({ + eprintln!("{}:{}: {}: {:?}", file!(), line!(), $msg, $self); + $self + }) +} + +fn value(x: T) -> T { + println!("evaluated {:?}", x); + x +} + +fn main() { + value("hello").log_value!("value").len().log_value!("len"); +} +``` + +The invocation in `main` will expand to the following: + +```rust + { + let _internal2 = { + let _internal1 = value("hello"); + eprintln!("{}:{}: {}: {:?}", "src/main.rs", 14, "value", _internal1); + _internal1 + } + .len(); + eprintln!("{}:{}: {}: {:?}", "src/main.rs", 14, "len", _internal2); + _internal2 + }; +``` + +The code within the inner curly braces represents the expansion of the first +`log_value!`; the code within the outer curly braces represents the expansion +of the second `log_value!`. The compiler will generate unique symbols for each +internal variable. + Note that postfix macros cannot dispatch differently based on the type of the expression they're invoked on. This includes whether the expression has type `T`, `&T`, or `&mut T`. The internal binding the compiler creates for that From 07c26b6d28a0486cbf8c9ea5a6f8f3a641bdcab1 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 29 Nov 2021 19:13:42 -0800 Subject: [PATCH 21/36] Set RFC PR URL --- text/0000-simple-postfix-macros.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 05965a7c701..311d415a1ad 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -1,6 +1,6 @@ - Feature Name: `simple_postfix_macros` - Start Date: 2018-05-12 -- RFC PR: (leave this empty) +- RFC PR: [rust-lang/rfcs#2442](/~https://github.com/rust-lang/rfcs/pull/2442) - Rust Issue: (leave this empty) # Summary From 21cd66cd8afe1c3cdd40aabcd98ed39d1cd91ce7 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 29 Nov 2021 20:06:37 -0800 Subject: [PATCH 22/36] Document interaction with qualified paths --- text/0000-simple-postfix-macros.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/0000-simple-postfix-macros.md b/text/0000-simple-postfix-macros.md index 311d415a1ad..56ef566b01a 100644 --- a/text/0000-simple-postfix-macros.md +++ b/text/0000-simple-postfix-macros.md @@ -153,6 +153,10 @@ Macros defined using this mechanism follow exactly the same namespace and scoping rules as any other macro. If a macro accepting a `$self:self` argument is in scope, Rust code may call it on any object. +A macro may only be called postfix if it is directly in scope and can be called +unqualified. A macro available via a qualified path does not support postfix +calls. + Since `$self` represents an internal temporary location created by the compiler, calling `stringify!` on `$self` will just return `"$self"`. If passed to another macro, `$self` will only match a macro argument using a designator From 4467a5ead2931956315d7aecbce58a27de2c1987 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 30 Nov 2021 16:21:41 -0800 Subject: [PATCH 23/36] Rename file to match RFC number --- ...000-simple-postfix-macros.md => 2442-simple-postfix-macros.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-simple-postfix-macros.md => 2442-simple-postfix-macros.md} (100%) diff --git a/text/0000-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md similarity index 100% rename from text/0000-simple-postfix-macros.md rename to text/2442-simple-postfix-macros.md From e0cb6bc993e8600a5336697b80bacbbbdc46f789 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 30 Nov 2021 16:28:16 -0800 Subject: [PATCH 24/36] Change desugaring to use `match` to preserve temporary lifetimes --- text/2442-simple-postfix-macros.md | 37 ++++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 56ef566b01a..7797d1451f4 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -94,11 +94,12 @@ only a macro can provide. [reference-level-explanation]: #reference-level-explanation When expanding a postfix macro, the compiler will effectively create a -temporary binding for the value of `$self`, and substitute that binding -for each expansion of `$self`. This stands in contrast to other macro -arguments, which get expanded into the macro body without evaluation. This -change avoids any potential ambiguities regarding the scope of the `$self` -argument and how much it leaves unevaluated, by evaluating it fully. +temporary binding (as though with `match`) for the value of `$self`, and +substitute that binding for each expansion of `$self`. This stands in contrast +to other macro arguments, which get expanded into the macro body without +evaluation. This change avoids any potential ambiguities regarding the scope of +the `$self` argument and how much it leaves unevaluated, by evaluating it +fully. For example, given the following macro definition: @@ -123,22 +124,28 @@ fn main() { The invocation in `main` will expand to the following: ```rust - { - let _internal2 = { - let _internal1 = value("hello"); + match value("hello") { + _internal1 => match ({ eprintln!("{}:{}: {}: {:?}", "src/main.rs", 14, "value", _internal1); _internal1 } - .len(); - eprintln!("{}:{}: {}: {:?}", "src/main.rs", 14, "len", _internal2); - _internal2 + .len()) + { + _internal2 => { + eprintln!("{}:{}: {}: {:?}", "src/main.rs", 14, "len", _internal2); + _internal2 + } + }, }; ``` -The code within the inner curly braces represents the expansion of the first -`log_value!`; the code within the outer curly braces represents the expansion -of the second `log_value!`. The compiler will generate unique symbols for each -internal variable. +The first `match` represents the expansion of the first `log_value!`; the +second `match` represents the expansion of the second `log_value!`. The +compiler will generate unique symbols for each internal variable. + +The use of `match` in the desugaring ensures that temporary lifetimes last +until the end of the expression; a desugaring based on `let` would end +temporary lifetimes before calling the postfix macro. Note that postfix macros cannot dispatch differently based on the type of the expression they're invoked on. This includes whether the expression has type From d966d4195f318712dbf03658e1041d8c0b15d661 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 30 Nov 2021 16:42:42 -0800 Subject: [PATCH 25/36] Support `&self` and `&mut self`, to handle `some_struct.field.postfix!()` --- text/2442-simple-postfix-macros.md | 39 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 7797d1451f4..a199bcb08ee 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -90,6 +90,9 @@ This method-like syntax allows macros to cleanly integrate in a left-to-right method chain, while still making use of control flow and other features that only a macro can provide. +A postfix macro may accept `self` by reference or mutable reference, by using a +designator of `&self` or `&mut self` in place of `self`. + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -147,18 +150,20 @@ The use of `match` in the desugaring ensures that temporary lifetimes last until the end of the expression; a desugaring based on `let` would end temporary lifetimes before calling the postfix macro. +A designator of `&self` becomes a match binding of `ref _internal`; a +designator of `&mut self` becomes a match binding of `ref mut _internal`. + Note that postfix macros cannot dispatch differently based on the type of the -expression they're invoked on. This includes whether the expression has type -`T`, `&T`, or `&mut T`. The internal binding the compiler creates for that -expression will participate in type inference as normal, including the expanded -body of the macro. If the compiler cannot unambiguously determine the type of -the internal binding, it will produce a compile-time error. If the macro wishes -to constrain the type of `$self`, it can do so by writing a `let` binding for -`$self` with the desired type. +expression they're invoked on. The internal binding the compiler creates for +that expression will participate in type inference as normal, including the +expanded body of the macro. If the compiler cannot unambiguously determine the +type of the internal binding, it will produce a compile-time error. If the +macro wishes to constrain the type of `$self`, it can do so by writing a `let` +binding for `$self` with the desired type. Macros defined using this mechanism follow exactly the same namespace and -scoping rules as any other macro. If a macro accepting a `$self:self` argument -is in scope, Rust code may call it on any object. +scoping rules as any other macro. If a postfix macro is in scope, Rust code may +call it on any object. A macro may only be called postfix if it is directly in scope and can be called unqualified. A macro available via a qualified path does not support postfix @@ -169,8 +174,8 @@ compiler, calling `stringify!` on `$self` will just return `"$self"`. If passed to another macro, `$self` will only match a macro argument using a designator of `:expr`, `:tt`, or `:self`. -Using the `self` designator on any macro argument other than the first will -produce a compile-time error. +Using the `self` or `&self` or `&mut self` designator on any macro argument +other than the first will produce a compile-time error. Wrapping any form of repetition around the `self` argument will produce a compile-time error. @@ -181,8 +186,9 @@ list (`($self:self)`, with the closing parenthesis as the next token after other tokens. Any subsequent tokens after the `,` will match what appears between the delimiters after the macro name in its invocation. -A macro may attach the designator `self` to a parameter not named `$self`, such -as `$x:self`. Using `$self:self` is a convention, not a requirement. +A macro may attach the designator `self` (or `&self` or `&mut self`) to a +parameter not named `$self`, such as `$x:self`. Using `$self:self` is a +convention, not a requirement. A postfix macro invocation, like any other macro invocation, may use any form of delimiters around the subsequent arguments: parentheses (`expr.m!()`), @@ -246,6 +252,10 @@ type. However, macros do currently allow `self` as the name of a macro argument when used with a designator, such as `$self:expr`; this could lead to potential confusion, and would preclude some approaches for future extension. +We could omit support for `&self` and `&mut self` and only support `self`. +However, this would make `some_struct.field.postfix!()` move out of `field`, +which would make it much less usable. + # Prior art [prior-art]: #prior-art @@ -258,3 +268,6 @@ control-flow mechanism from prefix to postfix. - Should we define a means of creating postfix proc macros, or can we defer that? - Does evaluating `$self` create any other corner cases besides `stringify!`? +- Is the desugaring of `&self` and `&mut self` correct? Is there another + desugaring that would work better? What happens if the type is already a + reference? From 293e8d6c85d3ffb2f379b7794732991abfe6dfc7 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Apr 2022 20:10:10 -0700 Subject: [PATCH 26/36] Make stringify! return a string representation of the full receiver expression --- text/2442-simple-postfix-macros.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index a199bcb08ee..84010947ee5 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -169,10 +169,15 @@ A macro may only be called postfix if it is directly in scope and can be called unqualified. A macro available via a qualified path does not support postfix calls. -Since `$self` represents an internal temporary location created by the -compiler, calling `stringify!` on `$self` will just return `"$self"`. If passed -to another macro, `$self` will only match a macro argument using a designator -of `:expr`, `:tt`, or `:self`. +Even though `$self` represents an internal temporary location provided by the +compiler, calling `stringify!` on `$self` will return a stringified +representation of the full receiver expression. For instance, given +`a.b()?.c.m!()`, `stringify!($self)` will return `"a.b()?.c"`. This allows +postfix macros to provide functionality such as `dbg!` or `assert!` that wants +to show the receiver expression. + +If passed to another macro, `$self` will only match a macro argument using a +designator of `:expr`, `:tt`, or `:self`. Using the `self` or `&self` or `&mut self` designator on any macro argument other than the first will produce a compile-time error. From df0b8f7a5f9de2568b95c49cc2e37f534264734a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Apr 2022 20:11:58 -0700 Subject: [PATCH 27/36] Update "Unresolved questions" and add a "Future work" section --- text/2442-simple-postfix-macros.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 84010947ee5..8acc3dbdfe7 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -271,8 +271,10 @@ control-flow mechanism from prefix to postfix. # Unresolved questions [unresolved]: #unresolved-questions -- Should we define a means of creating postfix proc macros, or can we defer that? -- Does evaluating `$self` create any other corner cases besides `stringify!`? - Is the desugaring of `&self` and `&mut self` correct? Is there another desugaring that would work better? What happens if the type is already a reference? + +# Future work + +- We may also want a means of creating postfix proc macros. From 82da7f9098477e1268be7a50ea59da7f07ecda63 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Apr 2022 21:08:59 -0700 Subject: [PATCH 28/36] Postfix macros: Desugar to use the same autoref mechanism as closure capture --- text/2442-simple-postfix-macros.md | 52 +++++++++++++++++------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 8acc3dbdfe7..3702170cda4 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -90,8 +90,9 @@ This method-like syntax allows macros to cleanly integrate in a left-to-right method chain, while still making use of control flow and other features that only a macro can provide. -A postfix macro may accept `self` by reference or mutable reference, by using a -designator of `&self` or `&mut self` in place of `self`. +A postfix macro may accept `self` by value, by reference, or by mutable +reference; the compiler will automatically use the appropriate type of +reference, just as it does for closure captures. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -104,7 +105,12 @@ evaluation. This change avoids any potential ambiguities regarding the scope of the `$self` argument and how much it leaves unevaluated, by evaluating it fully. -For example, given the following macro definition: +In the following expansion, `k#autoref` represents an internal compiler feature +within pattern syntax (which this RFC does not propose exposing directly), to +invoke the same compiler machinery currently used by closure captures to +determine whether to use `ref`, `ref mut`, or a by-value binding. + +Given the following macro definition: ```rust macro_rules! log_value { @@ -128,13 +134,13 @@ The invocation in `main` will expand to the following: ```rust match value("hello") { - _internal1 => match ({ + k#autoref _internal1 => match ({ eprintln!("{}:{}: {}: {:?}", "src/main.rs", 14, "value", _internal1); _internal1 } .len()) { - _internal2 => { + k#autoref _internal2 => { eprintln!("{}:{}: {}: {:?}", "src/main.rs", 14, "len", _internal2); _internal2 } @@ -150,8 +156,10 @@ The use of `match` in the desugaring ensures that temporary lifetimes last until the end of the expression; a desugaring based on `let` would end temporary lifetimes before calling the postfix macro. -A designator of `&self` becomes a match binding of `ref _internal`; a -designator of `&mut self` becomes a match binding of `ref mut _internal`. +The use of `k#autoref` in the desugaring allows a postfix macro to work in +contexts such as `some_struct.field.mac!()` (in which the macro must accept +`&self` by reference to avoid moving out of the struct field), as well as in +contexts that must take ownership of the receiver in order to function. Note that postfix macros cannot dispatch differently based on the type of the expression they're invoked on. The internal binding the compiler creates for @@ -179,8 +187,8 @@ to show the receiver expression. If passed to another macro, `$self` will only match a macro argument using a designator of `:expr`, `:tt`, or `:self`. -Using the `self` or `&self` or `&mut self` designator on any macro argument -other than the first will produce a compile-time error. +Using the `self` designator on any macro argument other than the first will +produce a compile-time error. Wrapping any form of repetition around the `self` argument will produce a compile-time error. @@ -191,9 +199,8 @@ list (`($self:self)`, with the closing parenthesis as the next token after other tokens. Any subsequent tokens after the `,` will match what appears between the delimiters after the macro name in its invocation. -A macro may attach the designator `self` (or `&self` or `&mut self`) to a -parameter not named `$self`, such as `$x:self`. Using `$self:self` is a -convention, not a requirement. +A macro may attach the designator `self` to a parameter not named `$self`, such +as `$x:self`. Using `$self:self` is a convention, not a requirement. A postfix macro invocation, like any other macro invocation, may use any form of delimiters around the subsequent arguments: parentheses (`expr.m!()`), @@ -257,9 +264,15 @@ type. However, macros do currently allow `self` as the name of a macro argument when used with a designator, such as `$self:expr`; this could lead to potential confusion, and would preclude some approaches for future extension. -We could omit support for `&self` and `&mut self` and only support `self`. -However, this would make `some_struct.field.postfix!()` move out of `field`, -which would make it much less usable. +We could omit the `k#autoref` mechanism and only support `self`. However, this +would make `some_struct.field.postfix!()` move out of `field`, which would make +it much less usable. + +We could omit the `k#autoref` mechanism in favor of requiring the macro to +specify whether it accepts `self`, `&self`, or `&mut self`. However, this would +prevent writing macros that can accept either a reference or a value, violating +user expectations compared to method calls (which can accept `&self` but still +get called with a non-reference receiver). # Prior art [prior-art]: #prior-art @@ -268,13 +281,8 @@ The evolution of `try!(expr)` into `expr?`, and the evolution of `await!(expr)` into `expr.await`, both serve as prior art for moving an important macro-style control-flow mechanism from prefix to postfix. -# Unresolved questions -[unresolved]: #unresolved-questions - -- Is the desugaring of `&self` and `&mut self` correct? Is there another - desugaring that would work better? What happens if the type is already a - reference? - # Future work - We may also want a means of creating postfix proc macros. +- We *may* want to expose `k#autoref` for other purposes. We may also want to + use it in the definition of other syntax desugaring in the future. From 446b8021e13dd5ca4dba41eb79ec222060039ca5 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Apr 2022 21:37:10 -0700 Subject: [PATCH 29/36] Add alternate definition syntaxes to alternatives section --- text/2442-simple-postfix-macros.md | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 3702170cda4..83eef052bc4 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -264,6 +264,39 @@ type. However, macros do currently allow `self` as the name of a macro argument when used with a designator, such as `$self:expr`; this could lead to potential confusion, and would preclude some approaches for future extension. +In the syntax to define a postfix macro, rather than using +``` +($self:self, $arg:expr) => (...) +``` +we could use +``` +$self:self.($arg:expr) => (...) +``` +. This would have the advantage of looking more similar to the invocation, but +the disadvantage of looking much different than method definition syntax (in +which `self` appears within the function arguments). + +In the syntax to define a postfix macro, rather than using +``` +($self:self, $arg:expr) => (...) +``` +we could use +``` +($self:self. $arg:expr) => (...) +``` +or +``` +($self:self $arg:expr) => (...) +``` +. Using a `.` might be evocative of method syntax, but would be unusual and +harder to remember. Using no delimiter at all would reflect that the `,` does +not actually match a literal `,` in syntax, and might be clearer when using +macros whose arguments use unusual syntax substantially different than method +arguments (e.g. `expr.mac!(:thwack => boing | :poit <= narf)`) but would be +confusing and error-prone for macros intended to resemble method calls (e.g. +`expr.mac!(arg1, arg2, arg3)`). (Making the `,` optional seems even more +confusing and prone to ambiguity.) + We could omit the `k#autoref` mechanism and only support `self`. However, this would make `some_struct.field.postfix!()` move out of `field`, which would make it much less usable. From 32ba75fd9da1f5d563ef4ebeb302b20183adc6b7 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Apr 2022 21:59:43 -0700 Subject: [PATCH 30/36] Document renaming of postfix macros on import --- text/2442-simple-postfix-macros.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 83eef052bc4..08bbdf75ec3 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -173,9 +173,11 @@ Macros defined using this mechanism follow exactly the same namespace and scoping rules as any other macro. If a postfix macro is in scope, Rust code may call it on any object. -A macro may only be called postfix if it is directly in scope and can be called -unqualified. A macro available via a qualified path does not support postfix -calls. +A macro may only be called as a postfix macro if it is directly in scope as an +unqualified name. A macro available via a qualified path (for instance, +`path::to::macro`) does not support postfix calls. If an imported macro uses +`as` to rename it (for instance, `use path::to::macro_name as other_name`), it +supports postfix calls using the new name, not the original name. Even though `$self` represents an internal temporary location provided by the compiler, calling `stringify!` on `$self` will return a stringified From 5711eca9d0cfd5bdd3be5cffc2a739f71065abb9 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Apr 2022 22:28:34 -0700 Subject: [PATCH 31/36] Add alternative about UFCS-style use with any macro --- text/2442-simple-postfix-macros.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 08bbdf75ec3..c0280aa1b68 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -309,6 +309,21 @@ prevent writing macros that can accept either a reference or a value, violating user expectations compared to method calls (which can accept `&self` but still get called with a non-reference receiver). +Rather than requiring a macro to explicitly declare that it works with postfix +syntax using a `:self` specifier, we could allow calling existing macros using +postfix syntax, and automatically pass the receiver as the first argument. This +would work similarly to [Uniform Function Call +Syntax](https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax), as well as +similarly to the ability to call `Type::method(obj, arg)` rather than +`obj.method(arg)`. Working with all existing macros would be both an advantage +and a disadvantage. This RFC proposes to require macros to explicitly opt-in to +postfix syntax, so that the macro has control over whether it makes sense in +postfix position, and so that the macro can determine what syntax makes the +most sense in postfix position. An explicit opt-in also seems appropriate +given the pre-evaluation behavior of the `:self` specifier, which seems +sufficiently different to warrant a different specifier rather than existing +specifiers like `:expr`. + # Prior art [prior-art]: #prior-art From c13ba49055439e9d792830a3cb98e6e41b1bb5c6 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 3 Apr 2022 22:35:35 -0700 Subject: [PATCH 32/36] Discuss alternative of omitting the delimiters for zero-argument postfix macros --- text/2442-simple-postfix-macros.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index c0280aa1b68..7d2fb6c481f 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -324,6 +324,19 @@ given the pre-evaluation behavior of the `:self` specifier, which seems sufficiently different to warrant a different specifier rather than existing specifiers like `:expr`. +We could allow postfix macros to omit the delimiters entirely when they have no +arguments. For instance, instead of `a.b()?.c.mac!()`, we could allow writing +`a.b()?.c.mac!`. This would work well when using a macro as a substitute for a +postfix keyword (similar to `.await` or `?`). The `!` would remain, to indicate +the invocation of a macro. However, some macros may prefer to look like a +zero-argument method call (`.mac!()`). Allowing *both* `.mac!` and `.mac!()` +introduces potential ambiguities. If we want to allow `.mac!` in the future, we +could provide an opt-in syntax for the macro to use, which would allow omitting +the delimiter; if, in the future, we determine that we can unambiguously allow +a macro to support *optional* parentheses, we could allow opting into both. +This RFC proposes that we always require the delimiter for now, for simplicity +and to avoid ambiguity. + # Prior art [prior-art]: #prior-art From 731dbc6718d9e0997396ecbc0ea1da903a5ba278 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 16 Jan 2024 17:29:31 -0800 Subject: [PATCH 33/36] Explicitly document that a macro can support both postfix and non-postfix --- text/2442-simple-postfix-macros.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 7d2fb6c481f..cefa5eac92f 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -204,6 +204,12 @@ between the delimiters after the macro name in its invocation. A macro may attach the designator `self` to a parameter not named `$self`, such as `$x:self`. Using `$self:self` is a convention, not a requirement. +A single macro may define multiple rules, some that use `$self:self` and some +that do not, which allows it to be invoked either as a postfix macro or as a +non-postfix macro. Rules specifying `$self:self` will only match postfix +invocations, and rules not specifying `$self:self` will only match non-postfix +invocations. + A postfix macro invocation, like any other macro invocation, may use any form of delimiters around the subsequent arguments: parentheses (`expr.m!()`), braces (`expr.m!{}`), or square brackets (`expr.m![]`). From a409770a78ce1faf6f6f11c8ce6e86c2fd8dae56 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 16 Jan 2024 17:35:45 -0800 Subject: [PATCH 34/36] Note future possibility of use in std (not part of this RFC) --- text/2442-simple-postfix-macros.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index cefa5eac92f..5ff39495050 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -352,6 +352,9 @@ control-flow mechanism from prefix to postfix. # Future work +- We may want to add postfix support to some existing macros in the standard + library. This RFC does not make any specific proposals for doing so, but once + the capability exists, the standard library may wish to use it. - We may also want a means of creating postfix proc macros. - We *may* want to expose `k#autoref` for other purposes. We may also want to use it in the definition of other syntax desugaring in the future. From 37b333db3e34b646067b888b4d8c3b8d78d28b9e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 16 Jan 2024 18:53:39 -0800 Subject: [PATCH 35/36] Add info about `stringify!` to guide-level explanation --- text/2442-simple-postfix-macros.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 5ff39495050..7e38c535b63 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -90,6 +90,12 @@ This method-like syntax allows macros to cleanly integrate in a left-to-right method chain, while still making use of control flow and other features that only a macro can provide. +If a postfix macro calls `stringify!($self)`, it will get a stringified +representation of the full receiver expression. For instance, given +`a.b()?.c.m!()`, `stringify!($self)` will return `"a.b()?.c"`. This allows +postfix macros to provide debugging functionality such as `dbg!` or `assert!` +that wants to show the receiver expression. + A postfix macro may accept `self` by value, by reference, or by mutable reference; the compiler will automatically use the appropriate type of reference, just as it does for closure captures. @@ -183,8 +189,8 @@ Even though `$self` represents an internal temporary location provided by the compiler, calling `stringify!` on `$self` will return a stringified representation of the full receiver expression. For instance, given `a.b()?.c.m!()`, `stringify!($self)` will return `"a.b()?.c"`. This allows -postfix macros to provide functionality such as `dbg!` or `assert!` that wants -to show the receiver expression. +postfix macros to provide debugging functionality such as `dbg!` or `assert!` +that wants to show the receiver expression. If passed to another macro, `$self` will only match a macro argument using a designator of `:expr`, `:tt`, or `:self`. From 0194091fdc92f0440a23167c3621341239cf8d6d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 16 Jan 2024 18:54:28 -0800 Subject: [PATCH 36/36] Give examples of autoref behavior in guide-level explanation --- text/2442-simple-postfix-macros.md | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/text/2442-simple-postfix-macros.md b/text/2442-simple-postfix-macros.md index 7e38c535b63..fd082438ec6 100644 --- a/text/2442-simple-postfix-macros.md +++ b/text/2442-simple-postfix-macros.md @@ -98,7 +98,35 @@ that wants to show the receiver expression. A postfix macro may accept `self` by value, by reference, or by mutable reference; the compiler will automatically use the appropriate type of -reference, just as it does for closure captures. +reference, just as it does for closure captures. For instance, consider a +simple macro that shows an expression and its value: + +```rust +macro_rules! dbg { + ($self:self) => { eprintln!("{}: {}", stringify!($self), $self) } +} + +some_struct.field.dbg!(); // This does not consume or copy the field +some_struct_ref.field.dbg!(); // This works as well +``` + +Or, consider a simple postfix version of `writeln!`: + +```rust +macro_rules! writeln { + ($self:self, $args:tt) => { writeln!($self, $args) } + ... // remaining rules for the non-postfix version +} + +some_struct.field.writeln!("hello world")?; // This does not consume the field +some_struct.field.writeln!("hello {name}")?; // So it can be called repeatedly +some_struct.field.write_all(b"just like methods can")?; +``` + +This makes the `.writeln!(...)` macro work similarly to a method, which uses +`&mut self` but similarly can be called on an expression without explicitly +writing `&mut`. This allows postfix macros to call methods on the receiver, +whether those methods take `self`, `&self`, or `&mut self`. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation