Skip to content

Commit

Permalink
Question 9: metavariables are opaque
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Nov 28, 2018
1 parent 4f8bbe6 commit f0be82e
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
65 changes: 65 additions & 0 deletions questions/009-opaque-metavariable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
Answer: 21
Difficulty: 2

# Hint

Upon being matched as a `$:expr`, the matched expression becomes a single opaque
token tree.

# Explanation

This question involves the behavior of macro matchers as regards matching macro
metavariables.

Starting from the bottom of the quiz code, the invocation `t!(1)` matches the
first rule of `t!` and expands to `e!(1); m!(1);`.

The invocation `e!(1)` matches the first rule of `e!`. As part of this match,
the expression `1` is packaged into an opaque expression token called `$e`. At
no subsequent point will it be possible for any `macro_rules!` macro to look
inside of `$e`. All that can be known is that `$e` is *some* expression.

In any case, `e!(1)` expands to `m!($e)` where `$e` is an opaque expression
containing `1`. That `m!($e)` *does not* match the first rule of `m!` because
`$e` is opaque. Instead it matches the second rule of `m!` and prints `2`.

After `e!(1)` there is an invocation `m!(1)` coming from the expansion of `t!`.
That one *does* match the first rule of `m!` and prints `1`. The output of this
program is `21`.

Most fragment specifiers have this behavior of becoming opaque token boxes, but
some do not. Specifiers that are opaque once matched:

- `$:block`
- `$:expr`
- `$:item`
- `$:literal`
- `$:meta`
- `$:pat`
- `$:path`
- `$:stmt`
- `$:ty`

The rest of the specifiers do not become opaque and can be inspected by
subsequent rules:

- `$:ident`
- `$:lifetime`
- `$:tt`

For example:

```rust
macro_rules! m {
('a) => {};
}

macro_rules! l {
($l:lifetime) => {
// $l is not opaque.
m!($l);
}
}

l!('a);
```
16 changes: 16 additions & 0 deletions questions/009-opaque-metavariable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
macro_rules! m {
(1) => { print!("1") };
($tt:tt) => { print!("2") };
}

macro_rules! e {
($e:expr) => { m!($e) };
}

macro_rules! t {
($tt:tt) => { e!($tt); m!($tt); };
}

fn main() {
t!(1);
}

0 comments on commit f0be82e

Please sign in to comment.