Skip to content

First pass at a guide to compiler plugins #16995

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

Merged
merged 8 commits into from
Oct 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,7 @@ do
make_dir $h/test/doc-guide-pointers
make_dir $h/test/doc-guide-container
make_dir $h/test/doc-guide-tasks
make_dir $h/test/doc-guide-plugin
make_dir $h/test/doc-rust
done

Expand Down
2 changes: 1 addition & 1 deletion mk/docs.mk
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
######################################################################
DOCS := index intro tutorial guide guide-ffi guide-macros guide-lifetimes \
guide-tasks guide-container guide-pointers guide-testing \
guide-runtime complement-bugreport \
guide-runtime guide-plugin complement-bugreport \
complement-lang-faq complement-design-faq complement-project-faq rust \
rustdoc guide-unsafe guide-strings reference

Expand Down
2 changes: 1 addition & 1 deletion src/doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ something like:
pandoc --from=markdown --to=html5 --number-sections -o reference.html reference.md
~~~~

(rust.md being the Rust Reference Manual.)
(reference.md being the Rust Reference Manual.)

The syntax for pandoc flavored markdown can be found at:
http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown
Expand Down
8 changes: 4 additions & 4 deletions src/doc/complement-design-faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ representation as a primitive. This allows using Rust `enum`s in FFI where C
`enum`s are also used, for most use cases. The attribute can also be applied
to `struct`s to get the same layout as a C struct would.

[repr]: http://doc.rust-lang.org/rust.html#miscellaneous-attributes
[repr]: reference.html#miscellaneous-attributes

## There is no GC

Expand All @@ -56,7 +56,7 @@ Types which are [`Sync`][sync] are thread-safe when multiple shared
references to them are used concurrently. Types which are not `Sync` are not
thread-safe, and thus when used in a global require unsafe code to use.

[sync]: http://doc.rust-lang.org/core/kinds/trait.Sync.html
[sync]: core/kinds/trait.Sync.html

### If mutable static items that implement `Sync` are safe, why is taking &mut SHARABLE unsafe?

Expand Down Expand Up @@ -139,8 +139,8 @@ and explicitly calling the `clone` method. Making user-defined copy operators
explicit surfaces the underlying complexity, forcing the developer to opt-in
to potentially expensive operations.

[copy]: http://doc.rust-lang.org/core/kinds/trait.Copy.html
[clone]: http://doc.rust-lang.org/core/clone/trait.Clone.html
[copy]: core/kinds/trait.Copy.html
[clone]: core/clone/trait.Clone.html

## No move constructors

Expand Down
76 changes: 76 additions & 0 deletions src/doc/guide-macros.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
% 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 @@ -448,6 +457,66 @@ fn main() {
The two `'x` names did not clash, which would have caused the loop
to print "I am never printed" and to run forever.

# Scoping and macro import/export

Macros occupy a single global namespace. The interaction with Rust's system of
modules and crates is somewhat complex.

Definition and expansion of macros both happen in a single depth-first,
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a code example here, the text is subtle:

// macro!() not visible to any items here

mod foo {
    #![macro_escape]

    // macro!() not visible

    macro_rules! macro { () => { println!("hello") } }

    // macro!() visible
}

// macro!() visible (including in any nested modules)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of the longer example in my reply to Gankro above?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good.

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.

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.

An example:

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

// visible here: m1

mod foo {
// visible here: m1

#[macro_export]
macro_rules! m2 (() => (()))

// visible here: m1, m2
}

// visible here: m1

macro_rules! m3 (() => (()))

// visible here: m1, m3

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

macro_rules! m4 (() => (()))

// visible here: m1, m3, m4
}

// visible here: m1, m3, m4
# fn main() { }
```

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

# A final note

Macros, as currently implemented, are not for the faint of heart. Even
Expand All @@ -457,3 +526,10 @@ tricky. Invoking the `log_syntax!` macro can help elucidate intermediate
states, invoking `trace_macros!(true)` will automatically print those
intermediate states out, and passing the flag `--pretty expanded` as a
command-line argument to the compiler will show the result of expansion.

If Rust's macro system can't do what you need, you may want to write a
[compiler plugin](guide-plugin.html) instead. Compared to `macro_rules!`
macros, this is significantly more work, the interfaces are much less stable,
and the warnings about debugging apply ten-fold. In exchange you get the
flexibility of running arbitrary Rust code within the compiler. Syntax
extension plugins are sometimes called "procedural macros" for this reason.
Loading