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

rustc: Implement custom derive (macros 1.1) #35957

Merged
merged 1 commit into from
Sep 3, 2016

Conversation

alexcrichton
Copy link
Member

This commit is an implementation of RFC 1681 which adds support to the
compiler for first-class user-define custom #[derive] modes with a far more
stable API than plugins have today.

The main features added by this commit are:

  • A new rustc-macro crate-type. This crate type represents one which will
    provide custom derive implementations and perhaps eventually flower into the
    implementation of macros 2.0 as well.
  • A new rustc_macro crate in the standard distribution. This crate will
    provide the runtime interface between macro crates and the compiler. The API
    here is particularly conservative right now but has quite a bit of room to
    expand into any manner of APIs required by macro authors.
  • The ability to load new derive modes through the #[macro_use] annotations on
    other crates.

All support added here is gated behind the rustc_macro feature gate, both for
the library support (the rustc_macro crate) as well as the language features.

There are a few minor differences from the implementation outlined in the RFC,
such as the rustc_macro crate being available as a dylib and all symbols are
dlsym'd directly instead of having a shim compiled. These should only affect
the implementation, however, not the public interface.

This commit also ended up touching a lot of code related to #[derive], making
a few notable changes:

  • Recognized derive attributes are no longer desugared to derive_Foo. Wasn't
    sure how to keep this behavior and not expose it to custom derive.
  • Derive attributes no longer have access to unstable features by default, they
    have to opt in on a granular level.
  • The derive(Copy,Clone) optimization is now done through another "obscure
    attribute" which is just intended to ferry along in the compiler that such an
    optimization is possible. The derive(PartialEq,Eq) optimization was also
    updated to do something similar.

One part of this PR which needs to be improved before stabilizing are the errors
and exact interfaces here. The error messages are relatively poor quality and
there are surprising spects of this such as #[derive(PartialEq, Eq, MyTrait)]
not working by default. The custom attributes added by the compiler end up
becoming unstable again when going through a custom impl.

Hopefully though this is enough to start allowing experimentation on crates.io!

@rust-highfive
Copy link
Collaborator

r? @eddyb

(rust_highfive has picked a reviewer for you, use r? to override)

@alexcrichton
Copy link
Member Author

r? @nrc

cc @cgswords

@rust-highfive rust-highfive assigned nrc and unassigned eddyb Aug 24, 2016
@@ -947,6 +947,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
middle::dead::check_crate(tcx, &analysis.access_levels);
});

time(time_passes, "rustc-maro checking", || {
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo.

@bors
Copy link
Contributor

bors commented Aug 25, 2016

☔ The latest upstream changes (presumably #35764) made this pull request unmergeable. Please resolve the merge conflicts.

@nrc
Copy link
Member

nrc commented Aug 26, 2016

So, I'm not sure what to do with this PR - there is a lot I'd like to change, and I'm not sure if that will be easier to do before or after landing (it is already quite large, as you say). Ideally we'd fix stuff up before landing, but I expect a lot of things won't affect the API and it will allow users to start iterating if we land early. I also don't want to hold up landing too much since it is going to be a pain to rebase and is important to move quickly here.

So, issues:

  • duplication of libproc_macro/librustc_macro - I think that we are starting small with proc_macro and it is going in the direction we want for macros 2.0. We have put quite a lot of effort into the design there and it seems a shame to ignore that. If you think some parts of that are too much initially, we can hide some of it in various ways. I think there is nearly enough in proc_macro to handle the 1.1 plan (precisely, there is enough with a PR I have which adds to_string for TokenStreams).
  • Implementation of TokenStream - should really be tokens rather than AST, that seems quite a big thing to deal with, although it is purely internal. This point is pretty much subsumed by the previous one though.
  • The dlsym strategy - I think it is better to use the existing macro infrastructure here. In particular, because it means we don't need to any ad hoc type checking code - the signature of the macro function is checked against a trait. Long-term, we also want to use a trait so we can allow macro authors to supply extra code or context about their macro. I have a PR which adds a new kind of Macro based on TokenStreams - I'd hoped to post that today, but I have been having testing difficulties.

All of these are things we can fix later, but would be easier to fix on a branch. So, I see three options:

  • land this PR as is and fix things up later
  • land my PR, then rebase this one to use the new macro kind and the existing TokenStream stuff
  • split up this PR and land parts that standalone (e.g., the new crate type, the derive work) now and the other bits later.

All else being equal I'd prefer the last option. However, if clients are eager to get stuck into this asap and/or it will be really painful, then I don't mind too much going for the first option.

I've had a read through the code and other than the points above, I'm pretty happy with it. If we do go for option 1, then I'd like to do a detailed pass, but there didn't seem to be much point in doing that just yet if we go for another option.

@eddyb
Copy link
Member

eddyb commented Aug 26, 2016

I think it is better to use the existing macro infrastructure here.

I disagree, other than in the interest of saving time. The current plugin-oriented infrastructure makes a lot of assumptions about the simple nature of name resolution in macros 1.0, whereas using metadata is more akin to the behavior of existing item hierarchies.
Furthermore, I believe it's good to distance procedural macro crates from rustc plugins, and to prevent them from being loaded as plugins.

@aturon
Copy link
Member

aturon commented Aug 26, 2016

@nrc @alexcrichton

OK, it's seeming like there's a lack of communication and breakdown of process here.

In particular:

duplication of libproc_macro/librustc_macro - I think that we are starting small with proc_macro and it is going in the direction we want for macros 2.0. We have put quite a lot of effort into the design there and it seems a shame to ignore that.

At the moment, the only accepted RFC we have on this topic is Macros 1.1, which was very precise about the naming and APIs that would be included, and the stabilization path. In contrast, the macros 2.0 direction here is currently only in blog post form, and a PR establishing libproc_macro with no accepted RFC.

I agree with @nrc that these two things need to be unified. But I am at a loss as to why this was never brought up in the comment thread for the Macros 1.1 RFC. AFAICT, the implementation in this PR matches precisely what the RFC said, which was very carefully written to ensure everyone was on the same page about the API details, including the name of the crate. Why weren't these issues raised at that point? I'm also a little surprised that we have started to land pieces of the larger libproc_macro without an RFC accepted about experimentation there (or even a check-in with the relevant teams). That's not normally how we go about adding major new unstable API surfaces.

So, that's where we are. The question now is, what's the best way out of this quagmire?

Personally, I think we should:

  • Very quickly come to consensus on any changes that are needed on Macros 1.1 on this thread. It sounds like it may just be the name of the crate? (Which I thought was already bikeshedded quite a bit...)
  • Examine the plausibility of merging the two crates. It seems like @nrc and @alexcrichton (and possibly @cswords) should set up a call ASAP to try to sort this out.
  • Ideally, land this PR with that unification in place -- unless that requires a large amount of rework. (I don't know the details well enough to say).

@aturon
Copy link
Member

aturon commented Aug 26, 2016

(I should say that conversely, I'm was also surprised that this PR ignores the existence of libproc_macro; but OTOH it seems that very few people even realized that libproc_macro had landed, myself included. Like I said, there seems to be a process/communication breakdown prior to this PR.)

@cgswords
Copy link
Contributor

For what it's worth, there was a lot of dissent about the name of the initial macro crate [1] [2] [3]. @nrc and I setteld on libproc_macro as the least of all evils in a side discussion, but we definitely should have done more (eg talked with @alexcrichton / commented more on the PR).

As for the landing stuff, part of the 'lack-of-RFC' thing was that I spent a decent chunk of my internship on the project, and there was a push to get it landed the compiler before my internship was over (which happened just barely in time).

As for actual tokensterams, there has been a tokenstream implementation in libsyntax for a few months now, and regardless of the ultimate fate of libproc_macro/librustc_macro, Macros 1.1 should probably lean on that whenever possible.

@aturon
Copy link
Member

aturon commented Aug 26, 2016

As for the landing stuff, part of the 'lack-of-RFC' thing was that I spent a decent chunk of my internship on the project, and there was a push to get it landed the compiler before my internship was over (which happened just barely in time).

Yeah, I certainly understand that, and I don't think you are at fault :)

We do sometimes allow landing experimental things without an RFC, but we usually have some kind of heads-up to the relevant teams and consensus that the experiment is OK, as well as a broadcast to the larger community about what's happening. It's probably obvious why: once something lands, there's significant inertia around it, so if we really want to operate in a consensus-based way we need consensus even on experiments.

Anyway, the people overseeing/reviewing this work should have ensured that communication happened, and planned ahead for the inevitable last-minute rush.

It's water under the bridge at this point, regardless.

@alexcrichton
Copy link
Member Author

Yes as @aturon mentioned I was hoping to just follow the design in the RFC, especially the contentious points about the API. I'd personally prefer to not deviate that far from what was accepted.

I was also under the impression that we needed more plumbing to use something like libproc_macro for custom derive modes. To be clear, we're not talking about entirely new macro systems here. We're talking about one and only one feature, custom derive. The #[derive] attribute takes an AST today, and in lieu of entirely rewriting that as part of this PR, it was simply easier to stick with that representation for the internals.

Once we move everything to token streams then we can fuse the libraries and/or build one on the other, and once we figure out the stable naming I figure we can move everything to that. In the meantime, though, rustc_macro is the accepted name for this library, and I don't know off the top of my head how to use token streams internally (but would love to know how!).

@cgswords
Copy link
Contributor

cgswords commented Aug 27, 2016

To clarify my position (which, again, is opinion):

  1. I appreciate the RFC process, but Macros 1.1 seemed to move much faster than most RFCs by months. (That's hypothesis, but I would guess data mining confirms it by a good margin.) Moreover, it isn't exactly like the name concern wasn't raised (see above comment); it simply wasn't addressed in the RFC process. And the suggestion that we temporarily stick with the rustc_macro name seems to invalidate the whole drive for stabilization -- if the goal here is permanent stability, then the final name is certainly up for grabs (cc @ubsan @nikomatsakis). Moreover, since it wasn't seriously discussed as part of the RFC process, it must be discussed here. To summarize: based on the 'fast and forward' stabilization strategy, once we land 1.1, that name sticks, and that's the ball game. And since it wasn't dealt with before, it must be dealt with here.
  2. TokenStreams already exist in the compiler (at 2+ months old). Extending the original with impls here is the right solution; defining a second TokenStream in the compiler is confusing at best and catastrophic at worst for stable users. Publicly leaking an ast::item concretizes the internals more than any Macros 2.0 proposal has (or likely ever will) suggest.

@aturon
Copy link
Member

aturon commented Aug 27, 2016

@cgswords

First, I'm sorry if I came down hard before. I'm mostly just frustrated about the lack of coordination for this PR, which is leading to friction and wasted work.

I appreciate the RFC process, but Macros 1.1 seemed to move much faster than most RFCs by months.

RFCs merge at a wide range of rates, depending on scope and priority. Narrow, high priority RFCs often move quickly; libs RFCs often move relatively quickly. I don't understand the relevance of this point, in any case.

Moreover, it isn't exactly like the name concern wasn't raised (see above comment); it simply wasn't addressed in the RFC process.

Looking more closely at the history, you're correct: the name became an explicit "unresolved question", to be resolved prior to stabilization. However:

Moreover, since it wasn't seriously discussed as part of the RFC process, it must be discussed here.

So, the other part of the process for landing features in Rust is stabilization, where we generally have a tracking issue where people can discuss final details before stabilization (and we have a widely-communicated final comment period, to ensure that everyone's aware). I believe the intent was to sort out the final naming there.

Even so, we do need an initial name, and I agree that rustc_macro is not an appropriate choice for what will eventually become our general procedural macros story. I propose we go with proc_macro for now, but immediately start up more in-depth discussion on the tracking issue. How does that sound?

TokenStreams already exist in the compiler (at 2+ months old). Extending the original with impls here is the right solution; defining a second TokenStream in the compiler is confusing at best and catastrophic at worst for stable users. Publicly leaking an ast::item concretizes the internals more than any Macros 2.0 proposal has (or likely ever will) suggest.

I agree that the two should be sharing a definition in the long run. But nothing is leaking an ast::item here, AFAICT.

I think the question right now is an engineering choice: given that we'd like to land Macros 1.1 relatively quickly, what's the best option for internal representation for now? Here, I don't know enough of the details to really weigh in; if the existing TokenStream is close to having full support for Macros 1.1, then it does indeed seem correct to just finish that off.

@alexcrichton, can you please spell out in a bit more detail the rationale for:

  1. Not implementing directly in terms of the existing TokenStream type? (It sounds like @nrc is close to having the missing ingredients? I wasn't able to find the PR he mentioned.)
  2. Using a newtype wrapper in the case that this is just TokenStream from libsyntax? Can we just re-export?

The above touches on two of the three issues @nrc raised. The dlsym issue, I can't really speak to.

@eddyb
Copy link
Member

eddyb commented Aug 27, 2016

Can we just re-export?

One thing to remember is that impls of stable traits on stable types are always stable (AFAIK), so any trait impl on TokenStream would have to be audited and considered a guarantee once stabilized.

@aturon
Copy link
Member

aturon commented Aug 27, 2016

One thing to remember is that impls of stable traits on stable types are always stable (AFAIK), so any trait impl on TokenStream would have to be audited and considered a guarantee once stabilized.

That's a good point. I'm not sure how much of a risk that will be for this particular case, but it does seem prudent to newtype and otherwise be very careful about the promises we're making here.

I would note that there's impetus to move somewhat quickly here, just in the sense that at a minimum this feature is 12 weeks out from hitting stable, and the earlier we can land it in nightly, the earlier we can start getting feedback.

@aturon
Copy link
Member

aturon commented Aug 27, 2016

Gah, I somehow missed @alexcrichton's comment which already spells out some of the impetus for working with ASTs today. So cancel that question :-)

@alexcrichton
Copy link
Member Author

alexcrichton commented Aug 27, 2016

Whoa yes ok I think this may need to slow down a bit. I'd like to clarify some things first:

@cgswords

And the suggestion that we temporarily stick with the rustc_macro name seems to invalidate the whole drive for stabilization

I implement the name rustc_macro because it's what was accepted in the RFC. There is nothing new that is stable as part of this PR, and that is very intentionally so. As @aturon mentioned as this is an open question that will likely change when we actually stabilize.

The name has already been debated on the RFC. I was hoping to not reopen debate about the name on this PR (as it just blocks everyone using the feature). The library can be named whatever, but to be clear the name rustc_macro is not stable, nor is it intended to be stable. It's representing the current consensus on the RFC to ensure that this moves along quickly without getting held up over what's already been discussed.

To summarize: based on the 'fast and forward' stabilization strategy, once we land 1.1, that name sticks, and that's the ball game.

To reiterate from above again, nothing new is stable in this PR. The discussion on the RFC likely means the name will be changed, and it will be changed before stabilizing. We can do this in a manner that doesn't break any existing rustc_macro users and we'll deal with that down the road.

TokenStreams already exist in the compiler (at 2+ months old).

Again, to be clear, I have absolutely no intention of supplanting this. It is a technical fact that token streams are not how #[derive] works today, and as a result the more natural representation currently is just an AST item. As clarified by @eddyb this is not exposed in any way, shape, or form.

I would want to move over to the internal TokenStream type as quickly as possible once we actually can. Currently I'm under the impression that it requires more refactoring in the macro system, and there's no need to wait for any of that refactoring for this feature to land.

In other words, we could land this today and then change basically everything about how #[derive] is expanded internally without breaking everyone, as was exactly the intention.

Publicly leaking an ast::item concretizes the internals more than any Macros 2.0 proposal has (or likely ever will) suggest.

To reiterate again, this is not happening, nor is it ever planned to happen.


@aturon

Not implementing directly in terms of the existing TokenStream type?

Sure! Today #[derive] operates over ASTs, not token streams. I don't personally know of a way to convert an AST to a token stream. The second #[derive] works over token streams we can reexport libsyntax's TokenStream type if possible or just change the implementation of the stable API.

Using a newtype wrapper in the case that this is just TokenStream from libsyntax?

This is somewhat covered above, primarily by the lack of conversion from an AST to a TokenStream. If this exists that can be done, but it still won't change the fact that the underlying interface is all ASTs, not token streams.


@nrc

The dlsym strategy - I think it is better to use the existing macro infrastructure here. In particular, because it means we don't need to any ad hoc type checking code.

I'm not sure I understand this? The #[plugin_registrar] function today isn't typechecked at all AFAIK.

@cgswords
Copy link
Contributor

cgswords commented Aug 27, 2016

@aturon @alexcrichton Er, I really didn't mean that comment to seem as harsh as it apparently came across. Sorry about that. :/ And also, I clearly didn't read the code closely enough. Sorry about that, too. Thanks for explaining why I was wrong. I'm definitely onboard with this PR now, and excited to have it land.

@eddyb
Copy link
Member

eddyb commented Aug 27, 2016

@cgswords Do you think the quasi-quoting (& eventually other tools) can be in their own plugin crate, with everything that's not a syntax extension in the regular macro crate?
Eventually the macro crate would cause its dependencies which are syntax extensions to load and export them, I would think.
Which gives another reason to go the non-plugin_registrar route: it would make such transitive dependencies actually doable, with eventual full module system interaction, because rustc can reason about individual expander functions it knows about from metadata, and which crates they come from.

@nrc
Copy link
Member

nrc commented Aug 28, 2016

@eddyb

I think it is better to use the existing macro infrastructure here.

I disagree, other than in the interest of saving time. The current plugin-oriented infrastructure makes a lot of assumptions about the simple nature of name resolution in macros 1.0, whereas using metadata is more akin to the behavior of existing item hierarchies.
Furthermore, I believe it's good to distance procedural macro crates from rustc plugins, and to prevent them from being loaded as plugins.

Sorry, I was a bit vague in my statement and also misunderstood a bit what was going on here. I think where/how we store the compiled macro isn't too important and the approach here (dlsym/metadata) seems fine. What I don't like is checking the signature of the macro by adding custom code to type checking. We can do what we do for MultiModifier, etc. and use a trait implementation for a function and use regular type checking to enforce the signature. Having read the PR in a bit more detail, this bit seems more orthogonal to the storage of the macro and the implementation of derive than I first thought.

I think name resolution and the way we store the macros is mostly orthogonal, either way there seems to be some degree of work to do to store macros in either fashion for plugins 2.0.

@eddyb
Copy link
Member

eddyb commented Aug 28, 2016

@nrc You can't do much better other than by enforcing a trait obligation instead of checking the individual type parts of the signature, i.e. something like this.

@nrc
Copy link
Member

nrc commented Aug 28, 2016

@aturon

OK, it's seeming like there's a lack of communication and breakdown of process here.

Yes. Sorry for my part of this.

At the moment, the only accepted RFC we have on this topic is Macros 1.1, which was very precise about the naming and APIs that would be included, and the stabilization path.

I certainly misunderstood parts of the RFC, it seems. The spec for librustc_macro's TokenStreams doesn't have any implementation, and given the reference to the existing TokenStream, I assumed the implementation would be simply wrapping that.

More generally, my reading of the RFC was from the perspective of a maximally minimal slice of the proposed procedural macros functionality, and I assumed the implementation would follow that philosophy.

In contrast, the macros 2.0 direction here is currently only in blog post form, and a PR establishing libproc_macro with no accepted RFC.

We talked explicitly about this (when discussing my q2 goals, iirc) and you told me we should experiment first before writing a libmacro RFC and that the level of detail in the blog post was enough to start that experimentation. My suggestion was to write an RFC and you told me not to. Perhaps I misunderstood where this experimentation should take place or you've changed your mind since the macros 1.1 RFC, but I thought evolving a small libmacro in-tree is the only way to do this.

There is really only the beginning of the library there and I didn't think it was worth announcing until we had a TokenStream-based macro form to use it with. I missed an opportunity to announce at least to internals that something had landed, but I didn't know there was any other macro work going on, so didn't think it was urgent.

I agree with @nrc that these two things need to be unified. But I am at a loss as to why this was never brought up in the comment thread for the Macros 1.1 RFC. AFAICT, the implementation in this PR matches precisely what the RFC said, which was very carefully written to ensure everyone was on the same page about the API details, including the name of the crate. Why weren't these issues raised at that point?

I don't think there is much difference of opinion on the API here, only in the implementation, which is not really touched on in the RFC beyond some discussion of the crate types and how macros are stored. In particular the implementation for TokenStreams is not given and I assumed we'd discuss this after the RFC was accepted and before implementation (which is usually what happens with RFCs, I think it is very rare to bring up implmentation details in an RFC thread).

From the RFC:

The actual underlying representation of TokenStream will be basically the same
as it is in the compiler today. (the details on this are a little light
intentionally, shouldn't be much need to go into too much detail).

I'm also a little surprised that we have started to land pieces of the larger libproc_macro without an RFC accepted about experimentation there (or even a check-in with the relevant teams). That's not normally how we go about adding major new unstable API surfaces.

See above.

So, that's where we are. The question now is, what's the best way out of this quagmire?

  • Very quickly come to consensus on any changes that are needed on Macros 1.1 on this thread. It sounds like it may just be the name of the crate? (Which I thought was already bikeshedded quite a bit...)

I'm not actually too concerned about the name itself, more that we don't have two separate crates (my first proposal in this thread was to change the name of the existing libproc_macro to librustc_macro and merge in what is in this PR). We could also keep two separate crates, but have rustc_macro re-export a subset of proc_macro (since I think the two crates have different long term goals).

  • Examine the plausibility of merging the two crates. It seems like @nrc and @alexcrichton (and possibly @cswords) should set up a call ASAP to try to sort this out.

This is important, and we should.

  • Ideally, land this PR with that unification in place -- unless that requires a large amount of rework. (I don't know the details well enough to say).

Agreed. But I'm also happy to land as is, as long as there is momentum to address the issue quickly, rather than leave it open once we have something that clients like Serde can use.

@nrc
Copy link
Member

nrc commented Aug 28, 2016

@alexcrichton

I was also under the impression that we needed more plumbing to use something like libproc_macro for custom derive modes. To be clear, we're not talking about entirely new macro systems here. We're talking about one and only one feature, custom derive. The #[derive] attribute takes an AST today, and in lieu of entirely rewriting that as part of this PR, it was simply easier to stick with that representation for the internals.

Hmm, so this seems to be key to the choice of implementation of TokenStream. We certainly have the constraints that the clients must manipulate Strings and the deriving infrastructure wants to see AST. I agree we should avoid changing too much of that. However, it seems that this does not necessarily inform the implementation of TokenStream - we must go from AST to String to AST somewhere, but I don't think it is necessarily easier to do it one way vs another. In particular in this PR we go from String to AST in from_str, whereas I imagined that we would go from string to tokens in from_str and from tokens to AST in the custom derive glue (src/libsyntax_ext/deriving/custom.rs).

@nrc
Copy link
Member

nrc commented Aug 28, 2016

@alexcrichton

@nrc

The dlsym strategy - I think it is better to use the existing macro infrastructure here. In particular, because it means we don't need to any ad hoc type checking code.

I'm not sure I understand this? The #[plugin_registrar] function today isn't typechecked at all AFAIK.

When a macro is registered with the registrar, its implementation (usually a function) must satisfy a trait bound, that only happens if the function signature matches the function type for which there is an impl of the syntax extension trait. I imagined that we could do something similar here, except that the register call would not be needed, it would effectively be desugared by the compiler.

//! new custom derive modes through `#[rustc_macro_derive]`.
//!
//! Added recently as part of [RFC 1681] this crate is currently *unstable* and
//! requires the `#![feature(rustc_macro)]` directive to use. Eventually,
Copy link
Member

Choose a reason for hiding this comment

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

This should be rustc_macro_lib.

@alexcrichton
Copy link
Member Author

@bors: r=nrc

@bors
Copy link
Contributor

bors commented Sep 2, 2016

📌 Commit 66ec18f has been approved by nrc

@alexcrichton
Copy link
Member Author

@bors: r- oh right haven't fixed tests yet.

This commit is an implementation of [RFC 1681] which adds support to the
compiler for first-class user-define custom `#[derive]` modes with a far more
stable API than plugins have today.

[RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md

The main features added by this commit are:

* A new `rustc-macro` crate-type. This crate type represents one which will
  provide custom `derive` implementations and perhaps eventually flower into the
  implementation of macros 2.0 as well.

* A new `rustc_macro` crate in the standard distribution. This crate will
  provide the runtime interface between macro crates and the compiler. The API
  here is particularly conservative right now but has quite a bit of room to
  expand into any manner of APIs required by macro authors.

* The ability to load new derive modes through the `#[macro_use]` annotations on
  other crates.

All support added here is gated behind the `rustc_macro` feature gate, both for
the library support (the `rustc_macro` crate) as well as the language features.

There are a few minor differences from the implementation outlined in the RFC,
such as the `rustc_macro` crate being available as a dylib and all symbols are
`dlsym`'d directly instead of having a shim compiled. These should only affect
the implementation, however, not the public interface.

This commit also ended up touching a lot of code related to `#[derive]`, making
a few notable changes:

* Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't
  sure how to keep this behavior and *not* expose it to custom derive.

* Derive attributes no longer have access to unstable features by default, they
  have to opt in on a granular level.

* The `derive(Copy,Clone)` optimization is now done through another "obscure
  attribute" which is just intended to ferry along in the compiler that such an
  optimization is possible. The `derive(PartialEq,Eq)` optimization was also
  updated to do something similar.

---

One part of this PR which needs to be improved before stabilizing are the errors
and exact interfaces here. The error messages are relatively poor quality and
there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]`
not working by default. The custom attributes added by the compiler end up
becoming unstable again when going through a custom impl.

Hopefully though this is enough to start allowing experimentation on crates.io!

syntax-[breaking-change]
@alexcrichton
Copy link
Member Author

@bors: r=nrc

@bors
Copy link
Contributor

bors commented Sep 2, 2016

📌 Commit ecc6c39 has been approved by nrc

@bors
Copy link
Contributor

bors commented Sep 3, 2016

⌛ Testing commit ecc6c39 with merge a029ea3...

bors added a commit that referenced this pull request Sep 3, 2016
rustc: Implement custom derive (macros 1.1)

This commit is an implementation of [RFC 1681] which adds support to the
compiler for first-class user-define custom `#[derive]` modes with a far more
stable API than plugins have today.

[RFC 1681]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md

The main features added by this commit are:

* A new `rustc-macro` crate-type. This crate type represents one which will
  provide custom `derive` implementations and perhaps eventually flower into the
  implementation of macros 2.0 as well.

* A new `rustc_macro` crate in the standard distribution. This crate will
  provide the runtime interface between macro crates and the compiler. The API
  here is particularly conservative right now but has quite a bit of room to
  expand into any manner of APIs required by macro authors.

* The ability to load new derive modes through the `#[macro_use]` annotations on
  other crates.

All support added here is gated behind the `rustc_macro` feature gate, both for
the library support (the `rustc_macro` crate) as well as the language features.

There are a few minor differences from the implementation outlined in the RFC,
such as the `rustc_macro` crate being available as a dylib and all symbols are
`dlsym`'d directly instead of having a shim compiled. These should only affect
the implementation, however, not the public interface.

This commit also ended up touching a lot of code related to `#[derive]`, making
a few notable changes:

* Recognized derive attributes are no longer desugared to `derive_Foo`. Wasn't
  sure how to keep this behavior and *not* expose it to custom derive.

* Derive attributes no longer have access to unstable features by default, they
  have to opt in on a granular level.

* The `derive(Copy,Clone)` optimization is now done through another "obscure
  attribute" which is just intended to ferry along in the compiler that such an
  optimization is possible. The `derive(PartialEq,Eq)` optimization was also
  updated to do something similar.

---

One part of this PR which needs to be improved before stabilizing are the errors
and exact interfaces here. The error messages are relatively poor quality and
there are surprising spects of this such as `#[derive(PartialEq, Eq, MyTrait)]`
not working by default. The custom attributes added by the compiler end up
becoming unstable again when going through a custom impl.

Hopefully though this is enough to start allowing experimentation on crates.io!
@@ -302,7 +305,6 @@ declare_features! (
(removed, struct_inherit, "1.0.0", None),
(removed, test_removed_feature, "1.0.0", None),
(removed, visible_private_types, "1.0.0", None),
(removed, unsafe_no_drop_flag, "1.0.0", None)
Copy link

Choose a reason for hiding this comment

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

I am curious, was this line removed intentionally?

Copy link
Contributor

@petrochenkov petrochenkov Sep 3, 2016

Choose a reason for hiding this comment

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

Heh, couple of pages above (line 94) there's a comment // Don't ever remove anything from this list; set them to 'Removed'., but people do this anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops yes this was not intentional, just a botched merge.

Copy link
Member

Choose a reason for hiding this comment

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

FWIW library features do get removed all the time so I don't think we need to bother anymore with this list.

alexcrichton added a commit to alexcrichton/rust that referenced this pull request Sep 3, 2016
This feature was accidentally removed in
rust-lang#35957.
Manishearth added a commit to Manishearth/rust that referenced this pull request Sep 5, 2016
…rielb1

Add back feature accidentally removed

This feature was accidentally removed in
rust-lang#35957.
bors added a commit to rust-lang/cargo that referenced this pull request Sep 6, 2016
Macros 1.1

Tested with serde-rs/serde#530. This should be able to merge independently of rust-lang/rust#35957.

r? @alexcrichton
@alexcrichton alexcrichton deleted the macros-1.1 branch September 6, 2016 20:54
jakllsch pushed a commit to jakllsch/rust that referenced this pull request Sep 8, 2016
This feature was accidentally removed in
rust-lang#35957.
bors added a commit that referenced this pull request Sep 22, 2016
Adds a `ProcMacro` form of syntax extension

This commit adds syntax extension forms matching the types for procedural macros 2.0 (RFC #1566), these still require the usual syntax extension boiler plate, but this is a first step towards proper implementation and should be useful for macros 1.1 stuff too.

Supports both attribute-like and function-like macros.

Note that RFC #1566 has not been accepted yet, but I think there is consensus that we want to head in vaguely that direction and so this PR will be useful in any case. It is also fairly easy to undo and does not break any existing programs.

This is related to #35957 in that I hope it can be used in the implementation of macros 1.1, however, there is no direct overlap and is more of a complement than a competing proposal. There is still a fair bit of work to do before the two can be combined.

r? @jseyfried

cc @alexcrichton, @cgswords, @eddyb, @aturon
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.