Module derive_adhoc::doc_implementation
source · Expand description
§Implementation approach - how does this work?
You do not need to understand this in order to use derive-adhoc.
Also, you should not rely on the details here. They don’t form part of the public interface.
§Introduction
It is widely understood that proc macro invocations cannot communicate with each other. (Some people have tried sneaking round the back with disk files etc. but this can break due to incremental and concurrent compilation.)
But, a proc macro can define a macro_rules
macro.
Then, later proc macros can invoke that macro.
The expansion can even invoke further macros.
In this way, a proc macro invocation can communicate with
subsequent proc macro invocations.
(This trick is also used by
ambassador
.)
There is a further complication.
One macro X cannot peer into and dismantle
the expansion of another macro Y.
(This is necessary for soundness, when macros can generate unsafe
.)
So we must sometimes define macros that apply other macros
(whose name is supplied as an argument)
to what is conceptually the first macro’s output.
§Implementation approach - truly ad-hoc macros
§1. #[derive(Adhoc)]
proc macro for saving struct definitions
Implemented in capture.rs::derive_adhoc_derive_macro
.
When applied to (e.g.) pub struct StructName
, generates this
macro_rules! derive_adhoc_driver_StructName { {
{ $($template:tt)* }
{ ($orig_dollar:tt) $(future:tt)* }
$($tpassthrough:tt)*
} => {
derive_adhoc_expand!{
{ pub struct StructName { /* original struct definition */ } }
{ }
{ $($template)* }
{ $($tpassthrough)* }
}
} }
(The extra { }
parts after the driver and template
include space for future expansion.)
In the pub struct
part every $
is replaced with $orig_dollar
,
to use the $
passed in at the invocation site.
§2. derive_adhoc!
function-like proc macro for applying to a template
Implemented in invocation.rs::derive_adhoc_func_macro
.
When applied like this
derive_adhoc!{
StructName TOPTIONS...:
TEMPLATE...
}
Expands to
derive_adhoc_driver_StructName! {
{ TEMPLATE... }
{ ($) }
crate; [TOPTIONS...]
}
§3. Function-like proc macro to do the actual expansion
Implemented in expand.rs::derive_adhoc_expand_func_macro
.
The result of expanding the above is this:
derive_adhoc_expand!{
{ pub struct StructName { /* original struct definition */ } }
{ }
{ TEMPLATE... }
{ crate; [TOPTIONS...] /*no template name*/; }
}
derive_adhoc_expand
parses pub struct StructName
,
and implements a bespoke template expander,
whose template syntax resembles the expansion syntax from macro_rules
.
crate
is just used as the expansion for ${crate}
.
(For an ad-hoc template, the local crate is correct.)
§Implementation approach - reusable template macros
§1. define_derive_adhoc!
macro for defining a reuseable template
Implemented in definition.rs::define_derive_adhoc_func_macro
.
When used like this
define_derive_adhoc! {
MyMacro TOPTIONS... =
TEMPLATE...
}
Expands to
macro_rules! derive_adhoc_template_Template { {
{ $($driver:tt)* }
$( [ $($aoptions:tt)* ] )?
{ $($future:tt)* }
$($dpassthrough:tt)*
} => {
derive_adhoc_expand! {
{ $($driver)* }
$( [ $(aoptions)* ] )?
{ $($dpassthrough)* }
{ TEMPLATE... }
{ $crate; [TOPTIONS...] Template; }
}
} }
Except, every $
in the TEMPLATE is replaced with $orig_dollar
.
This is because a macro_rules!
template
is not capable of generating a
literal in the expansion $
.
(With the still-unstable
decl_macro
feature, $$
does this.)
The template expansion engine treats $orig_dollar
as just a single $
.
(Again, the extra { }
parts after the driver and template
include space for future expansion.)
§2. #[derive_adhoc(Template)]
, implemented in #[derive(Adhoc)]
Template reuse is also implemented in capture.rs::derive_adhoc_derive_macro
.
This
#[derive(Adhoc)]
#[derive_adhoc(Template[AOPTIONS,...])]
pub struct StructName { ... }
Generates (in addition to the derive_adhoc_driver_StructName
definition)
derive_adhoc_template_Template! {
{ #[derive_adhoc(Template)] struct StructName { ... } }
[1 0 AOPTIONS] // but only if AOPTIONS is nonempty
{ }
}
The literal $
is there to work around a limitation in macro_rules!
,
see above.
§3. Actual expansion
The call to derive_adhoc_template_Template!
is expanded according to the macro_rules!
definition,
resulting in a call to derive_adhoc_expand
:
derive_adhoc_expand!{
{ pub struct StructName { /* original struct definition */ } }
[1 0 AOPTIONS] // but only if AOPTIONS is nonempty
{ }
{ TEMPLATE... }
{ $crate; [TOPTIONS...] Template; }
}