Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macro reform #20482

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d1cf1b1
Don't test codegen-units errors on stage1 (c.f. #20184)
kmcallister Jan 1, 2015
5e5924b
Replace LetSyntaxTT with MacroRulesTT
kmcallister Sep 15, 2014
ad7c647
Add a special macro nonterminal $crate
kmcallister Sep 16, 2014
e2a9c04
Allow leading :: in use items
kmcallister Dec 10, 2014
5382881
Implement macro re-export
kmcallister Sep 16, 2014
1c2fddc
Remove unused if_ok! macro
kmcallister Dec 10, 2014
73806dd
Use $crate and macro reexport to reduce duplicated code
kmcallister Sep 16, 2014
fc58479
Stop using macro_escape as an inner attribute
kmcallister Dec 19, 2014
5bf385b
Rename macro_escape to macro_use
kmcallister Dec 19, 2014
5171b32
creader: Convert free functions to Env methods
kmcallister Dec 21, 2014
24aa7f0
creader: Use a single struct
kmcallister Dec 21, 2014
677b7ca
Reformat metadata for exported macros
kmcallister Dec 31, 2014
f314e2c
creader: Load parts of plugin metadata on demand
kmcallister Jan 1, 2015
60be2f5
Replace #[phase] with #[plugin] / #[macro_use] / #[no_link]
kmcallister Jan 1, 2015
0816255
Move #[macro_reexport] to extern crate
kmcallister Jan 2, 2015
aa69cbd
Allow selective macro import
kmcallister Jan 2, 2015
c9f0ff3
Reserve the keyword 'macro'
kmcallister Jan 2, 2015
416137e
Modernize macro_rules! invocations
kmcallister Jan 2, 2015
d0163d3
Pass the #[plugin(...)] meta item to the registrar
kmcallister Jan 3, 2015
c2e2697
Un-gate macro_rules
kmcallister Jan 3, 2015
bbbb85a
Forbid '#[macro_use] extern crate' outside the crate root
kmcallister Jan 3, 2015
34b995d
Add a test case for accidental macro re-export
kmcallister Jan 3, 2015
78e841d
Update docs
kmcallister Jan 3, 2015
2167e5b
Add missing #[no_link]
kmcallister Jan 6, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 8 additions & 1 deletion src/compiletest/compiletest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@

extern crate test;
extern crate getopts;
#[phase(plugin, link)] extern crate log;

#[cfg(stage0)]
#[phase(plugin, link)]
extern crate log;

#[cfg(not(stage0))]
#[macro_use]
extern crate log;

extern crate regex;

Expand Down
155 changes: 97 additions & 58 deletions src/doc/guide-macros.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
% The Rust Macros Guide

<div class="unstable-feature">
<b>Warning:</b> There are currently various problems with invoking macros, how
they interact with their environment, and how they are used outside of the
location in which they are defined. Macro definitions are likely to change
slightly in the future. For this reason, they are hidden behind the
<code>macro_rules</code> <a href="reference.html#compiler-features">feature
attribute</a>.
</div>

# Introduction

Functions are the primary tool that programmers can use to build abstractions.
Expand Down Expand Up @@ -46,19 +37,18 @@ lightweight custom syntax extensions, themselves defined using the
the pattern in the above code:

~~~~
# #![feature(macro_rules)]
# enum T { SpecialA(uint), SpecialB(uint) }
# fn f() -> uint {
# let input_1 = T::SpecialA(0);
# let input_2 = T::SpecialA(0);
macro_rules! early_return(
macro_rules! early_return {
($inp:expr $sp:path) => ( // invoke it like `(input_5 SpecialE)`
match $inp {
$sp(x) => { return x; }
_ => {}
}
);
);
}
// ...
early_return!(input_1 T::SpecialA);
// ...
Expand Down Expand Up @@ -109,10 +99,10 @@ that could be invoked like: `my_macro!(i->(( 2+2 )))`.

## Invocation location

A macro invocation may take the place of (and therefore expand to)
an expression, an item, or a statement.
The Rust parser will parse the macro invocation as a "placeholder"
for whichever of those three nonterminals is appropriate for the location.
A macro invocation may take the place of (and therefore expand to) an
expression, item, statement, or pattern. The Rust parser will parse the macro
invocation as a "placeholder" for whichever syntactic form is appropriate for
the location.

At expansion time, the output of the macro will be parsed as whichever of the
three nonterminals it stands in for. This means that a single macro might,
Expand Down Expand Up @@ -166,12 +156,11 @@ separator token (a comma-separated list could be written `$(...),*`), and `+`
instead of `*` to mean "at least one".

~~~~
# #![feature(macro_rules)]
# enum T { SpecialA(uint),SpecialB(uint),SpecialC(uint),SpecialD(uint)}
# fn f() -> uint {
# let input_1 = T::SpecialA(0);
# let input_2 = T::SpecialA(0);
macro_rules! early_return(
macro_rules! early_return {
($inp:expr, [ $($sp:path)|+ ]) => (
match $inp {
$(
Expand All @@ -180,7 +169,7 @@ macro_rules! early_return(
_ => {}
}
)
);
}
// ...
early_return!(input_1, [T::SpecialA|T::SpecialC|T::SpecialD]);
// ...
Expand Down Expand Up @@ -228,7 +217,6 @@ solves the problem.
Now consider code like the following:

~~~~
# #![feature(macro_rules)]
# enum T1 { Good1(T2, uint), Bad1}
# struct T2 { body: T3 }
# enum T3 { Good2(uint), Bad2}
Expand All @@ -255,8 +243,7 @@ a match, but with a syntax that suits the problem better. The following macro
can solve the problem:

~~~~
# #![feature(macro_rules)]
macro_rules! biased_match (
macro_rules! biased_match {
// special case: `let (x) = ...` is illegal, so use `let x = ...` instead
( ($e:expr) ~ ($p:pat) else $err:stmt ;
binds $bind_res:ident
Expand All @@ -275,7 +262,7 @@ macro_rules! biased_match (
_ => { $err }
};
)
);
}

# enum T1 { Good1(T2, uint), Bad1}
# struct T2 { body: T3 }
Expand All @@ -297,13 +284,12 @@ like this, we might prefer to write a single macro invocation. The input
pattern we want is clear:

~~~~
# #![feature(macro_rules)]
# fn main() {}
# macro_rules! b(
# macro_rules! b {
( $( ($e:expr) ~ ($p:pat) else $err:stmt ; )*
binds $( $bind_res:ident ),*
)
# => (0));
# => (0) }
~~~~

However, it's not possible to directly expand to nested match statements. But
Expand All @@ -320,35 +306,32 @@ process the semicolon-terminated lines, one-by-one. So, we want the following
input patterns:

~~~~
# #![feature(macro_rules)]
# macro_rules! b(
# macro_rules! b {
( binds $( $bind_res:ident ),* )
# => (0));
# => (0) }
# fn main() {}
~~~~

...and:

~~~~
# #![feature(macro_rules)]
# fn main() {}
# macro_rules! b(
# macro_rules! b {
( ($e :expr) ~ ($p :pat) else $err :stmt ;
$( ($e_rest:expr) ~ ($p_rest:pat) else $err_rest:stmt ; )*
binds $( $bind_res:ident ),*
)
# => (0));
# => (0) }
~~~~

The resulting macro looks like this. Note that the separation into
`biased_match!` and `biased_match_rec!` occurs only because we have an outer
piece of syntax (the `let`) which we only want to transcribe once.

~~~~
# #![feature(macro_rules)]
# fn main() {

macro_rules! biased_match_rec (
macro_rules! biased_match_rec {
// Handle the first layer
( ($e :expr) ~ ($p :pat) else $err :stmt ;
$( ($e_rest:expr) ~ ($p_rest:pat) else $err_rest:stmt ; )*
Expand All @@ -366,10 +349,10 @@ macro_rules! biased_match_rec (
);
// Produce the requested values
( binds $( $bind_res:ident ),* ) => ( ($( $bind_res ),*) )
);
}

// Wrap the whole thing in a `let`.
macro_rules! biased_match (
macro_rules! biased_match {
// special case: `let (x) = ...` is illegal, so use `let x = ...` instead
( $( ($e:expr) ~ ($p:pat) else $err:stmt ; )*
binds $bind_res:ident
Expand All @@ -388,7 +371,7 @@ macro_rules! biased_match (
binds $( $bind_res ),*
);
)
);
}


# enum T1 { Good1(T2, uint), Bad1}
Expand Down Expand Up @@ -434,17 +417,15 @@ As an example, `loop` and `for-loop` labels (discussed in the lifetimes guide)
will not clash. The following code will print "Hello!" only once:

~~~
#![feature(macro_rules)]

macro_rules! loop_x (
macro_rules! loop_x {
($e: expr) => (
// $e will not interact with this 'x
'x: loop {
println!("Hello!");
$e
}
);
);
}

fn main() {
'x: loop {
Expand All @@ -467,45 +448,53 @@ lexical-order traversal of a crate's source. So a macro defined at module scope
is visible to any subsequent code in the same module, which includes the body
of any subsequent child `mod` items.

If a module has the `macro_escape` attribute, its macros are also visible in
its parent module after the child's `mod` item. If the parent also has
`macro_escape` then the macros will be visible in the grandparent after the
parent's `mod` item, and so forth.
If a module has the `macro_use` attribute, its macros are also visible in its
parent module after the child's `mod` item. If the parent also has `macro_use`
then the macros will be visible in the grandparent after the parent's `mod`
item, and so forth.

Independent of `macro_escape`, the `macro_export` attribute controls visibility
between crates. Any `macro_rules!` definition with the `macro_export`
attribute will be visible to other crates that have loaded this crate with
`phase(plugin)`. There is currently no way for the importing crate to control
which macros are imported.
The `macro_use` attribute can also appear on `extern crate`. In this context
it controls which macros are loaded from the external crate, e.g.

```rust,ignore
#[macro_use(foo, bar)]
extern crate baz;
```

If the attribute is given simply as `#[macro_use]`, all macros are loaded. If
there is no `#[macro_use]` attribute then no macros are loaded. Only macros
defined with the `#[macro_export]` attribute may be loaded.

To load a crate's macros *without* linking it into the output, use `#[no_link]`
as well.

An example:

```rust
# #![feature(macro_rules)]
macro_rules! m1 (() => (()));
macro_rules! m1 { () => (()) }

// visible here: m1

mod foo {
// visible here: m1

#[macro_export]
macro_rules! m2 (() => (()));
macro_rules! m2 { () => (()) }

// visible here: m1, m2
}

// visible here: m1

macro_rules! m3 (() => (()));
macro_rules! m3 { () => (()) }

// visible here: m1, m3

#[macro_escape]
#[macro_use]
mod bar {
// visible here: m1, m3

macro_rules! m4 (() => (()));
macro_rules! m4 { () => (()) }

// visible here: m1, m3, m4
}
Expand All @@ -514,8 +503,58 @@ mod bar {
# fn main() { }
```

When this library is loaded with `#[phase(plugin)] extern crate`, only `m2`
will be imported.
When this library is loaded with `#[use_macros] extern crate`, only `m2` will
be imported.

The Rust Reference has a [listing of macro-related
attributes](reference.html#macro--and-plugin-related-attributes).

# The variable `$crate`

A further difficulty occurs when a macro is used in multiple crates. Say that
`mylib` defines

```rust
pub fn increment(x: uint) -> uint {
x + 1
}

#[macro_export]
macro_rules! inc_a {
($x:expr) => ( ::increment($x) )
}

#[macro_export]
macro_rules! inc_b {
($x:expr) => ( ::mylib::increment($x) )
}
# fn main() { }
```

`inc_a` only works within `mylib`, while `inc_b` only works outside the
library. Furthermore, `inc_b` will break if the user imports `mylib` under
another name.

Rust does not (yet) have a hygiene system for crate references, but it does
provide a simple workaround for this problem. Within a macro imported from a
crate named `foo`, the special macro variable `$crate` will expand to `::foo`.
By contrast, when a macro is defined and then used in the same crate, `$crate`
will expand to nothing. This means we can write

```rust
#[macro_export]
macro_rules! inc {
($x:expr) => ( $crate::increment($x) )
}
# fn main() { }
```

to define a single macro that works both inside and outside our library. The
function name will expand to either `::increment` or `::mylib::increment`.

To keep this system simple and correct, `#[macro_use] extern crate ...` may
only appear at the root of your crate, not inside `mod`. This ensures that
`$crate` is a single identifier.

# A final note

Expand Down
14 changes: 8 additions & 6 deletions src/doc/guide-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ extend the compiler's behavior with new syntax extensions, lint checks, etc.

A plugin is a dynamic library crate with a designated "registrar" function that
registers extensions with `rustc`. Other crates can use these extensions by
loading the plugin crate with `#[phase(plugin)] extern crate`. See the
loading the plugin crate with `#[plugin] extern crate`. See the
[`rustc::plugin`](rustc/plugin/index.html) documentation for more about the
mechanics of defining and loading a plugin.

Arguments passed as `#[plugin=...]` or `#[plugin(...)]` are not interpreted by
rustc itself. They are provided to the plugin through the `Registry`'s [`args`
method](rustc/plugin/registry/struct.Registry.html#method.args).

# Syntax extensions

Plugins can extend Rust's syntax in various ways. One kind of syntax extension
Expand Down Expand Up @@ -105,10 +109,9 @@ pub fn plugin_registrar(reg: &mut Registry) {
Then we can use `rn!()` like any other macro:

```ignore
#![feature(phase)]
#![feature(plugin)]

#[phase(plugin)]
extern crate roman_numerals;
#[plugin] extern crate roman_numerals;

fn main() {
assert_eq!(rn!(MMXV), 2015);
Expand Down Expand Up @@ -217,8 +220,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
Then code like

```ignore
#[phase(plugin)]
extern crate lint_plugin_test;
#[plugin] extern crate lint_plugin_test;

fn lintme() { }
```
Expand Down
Loading