From 8b017e62f769a839ec6cf89e9f8906aa9f1fe6c0 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Wed, 23 Jan 2019 12:36:23 +0100 Subject: [PATCH] New RFC: proc-macro-attribute-recursion --- text/0000-proc-macro-attribute-recursion.md | 75 +++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 text/0000-proc-macro-attribute-recursion.md diff --git a/text/0000-proc-macro-attribute-recursion.md b/text/0000-proc-macro-attribute-recursion.md new file mode 100644 index 00000000000..f3a00183d6d --- /dev/null +++ b/text/0000-proc-macro-attribute-recursion.md @@ -0,0 +1,75 @@ +- Feature Name: proc_macro_attribute_recursion +- Start Date: 24.01.2019 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Expand `proc_macro_attributes` recursively. + +# Motivation +[motivation]: #motivation + +Currently, procedural macros have no way to expand macros at all. [RFC #2320](https://github.com/rust-lang/rfcs/pull/2320) aims to rectify this, but despite being reworked a lot still suffers from some complexity. + +This proc_macro author wants something workable now instead of waiting for that RFC while leaving the doors open for an eventual implementation. Also making this small part available allows us to collect experience with macro expansion in proc_macros at very modest cost. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +`proc_macro_attributes` can add other attributes in their output. The expander then expands all macros in the proc_macro output, and then checks again for any attributes, expanding them, too (possibly recursively): + +Here's an example from [flamer](https://crates.io/crates/flamer): + +```rust +use flamer::flame; + +macro_rules! macro_fun { + () => { + fn this_is_fun(x: u64) -> u64 { + x + 1 + } + } +} + +#[flame] macro_fun!(); +``` + +Flamer expands the `#[flame] macro_fun!();` to itself (including the `#[flame]` attribute). The expander then expands `macro_fun!()` yielding + +```rust +#[flame] +fn this_is_fun(x: u64) -> u64 { + x + 1 +} +``` + +Because of the `#[flame]` attribtue, this is fed back to flamer, which modifies the function resulting from the macro. Note that the attribute needs not be placed at the same AST node (and in fact, flamer would place it only on macro invocation nodes). + +`proc_macro` writers can implement their macros in terms of `proc_macro_attributes` (which is a very roundabout way to deal with macros, but at least it would work at all) to gain the same benefits. + +This way, `proc_macro_attribute`s can be deemed *recursive* like macros-by-example. Note that the macro recursion limit must also be observed by the `proc_macro_attribute` implementations. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The expander is extended to search the expansion of `proc_macro` and `proc_macro_attributes` for other macro invocations. Those are then expanded until there are no more attributes or macro invocations left or the macro expansion limit is reached, whichever comes first. + +Implementors will have to make sure to order the expansions within expanded output by their origin: macros which are in the `proc_macro_attribute`s' input need to be expanded before expanding macros that have been added by the `proc_macro_attribute`s themselves. This can easily be done by examining the `Span`s of the expansion and ordering them by `SyntaxContext`. + +# Drawbacks +[drawbacks]: #drawbacks + +No drawbacks have been found so far. + +# Rationale and alternatives +[alternatives]: #alternatives + +– leave things as they are, but this leaves proc_macro authors in the cold if they want to deal with macros in invocations +– [RFC #2320](https://github.com/rust-lang/rfcs/pull/2320) has a more general solution but tackles more complexity. Note that this RFC is a part of #2320 broken out, so we can still implement the rest of it afterwards + +# Unresolved questions +[unresolved]: #unresolved-questions + +None