rust-lang/rust

Tracking issue for `macro_reexport` feature

aturon opened this issue · 26 comments

Tracking for stabilization of #[macro_reexport].

durka commented

Any opinions on whether this should be stabilized?

I would love to see it stabilized. Currently, a crate A which exports a macro whose expansion references a macro from crate B cannot express that dependency: it is pushed onto the user of crate A, even though otherwise they wouldn't have to know about the implementation detail. If crate A could reexport the macro from crate B, it would be a better situation.

cc @rust-lang/lang, particularly @nrc.

In the long run this will be able to go through the normal pub use system (as eventually macros will work with the normal name resolution system). But given that that's a ways off, and we already have stabilized the basics of macro_rules, we could consider doing something here as well.

Nominating for discussion in next lang meeting.

Hear ye, hear ye. This issue is hereby promoted to final comment period. The discussion is whether to stabilize this feature beginning in the next release cycle.

nrc commented

I think we should stabilise. As @aturon says, eventually it should be replaced, but having it in the short/medium term is a big win.

Some serious bugs and downsides of macro_reexport that I know of are:

  • The source of macro is mangled during the reexportation process. This means that error messages of the macro when used from the reexported location get worse. For example compare std's try! to core's
  • The macro is just copy/pasted into the reexported crate. This means that references like $crate break because they don't refer to the original crate, but now the reexported crate. This means it's basically useless to reexport a macro unless the two crates have the same exact internal structure.

I personally see this as a half-baked feature that was just nice to reduce duplication in std/core, it doesn't seem like it fits the normal high standard we might have for language features. There's certainly an argument that macro_rules! is "bad enough" that this doesn't make it worse, but I would prefer to not continue to make it worse.

I'd be fine deprecating and removing this while just copying the macros between core/std to have the same source.

durka commented

The first bug seems possible to fix -- if the mangled source of a macro is stored in crate metadata, the unmangled source can be too. I agree the second one is a downside, but it can be mitigated by pub useing items that are referenced from a macro when you reexport it. And not all macros reference items in the crate anyway! With no stable way to reexport macros, macro crates cannot participate in the dependency system. So I think this makes macro_rules! better (though not a whole lot better), not worse.

nrc commented

We should fix both the bugs that @alexcrichton points out before stabilising (I was not aware of them). Are there issues filed for those?

Unfortunately not that I know of :(

Sorry I meant to write these thoughts down when the initial tracking issue was made, but I guess I forgot...

durka commented

The mangled-source issue is #20923.

Making $crate "work" when re-exporting a macro seems to have no easy
fix. Or perhaps it depends on what you mean by "fix" and "work". That
is, you could expand to a path that is relative to the extern crate in
the intermediate crate, but that would require the intermediate crate
to make that extern crate a public export, which may not be what you
want.

Also, if $crate DOES expand to the original source crate, that means
that you MUST pub use the crate link, versus reproducing the
members, right?

After discussion in the @rust-lang/lang meeting, we decided not to stabilize this feature just yet (and hopefully not ever).

The key points of discussion already appear in this thread:

  • The interaction with $crate seems thoroughly flawed. The only way to use this is if you are adopting a "facade" pattern like libstd, where each successive crate in the facade re-exports everything.
  • In practice, cargo features are probably a better way to achieve that pattern, and certainly more widely used.
durka commented

I agree that it's only practical for a facade pattern, but sometimes that's
fine or there are no non-macro items so it's not an issue.

I'm confused by your last bullet. Can you elaborate on how Cargo features
provide a facility for macro crates to participate in the dependency system?
On Apr 22, 2016 19:33, "Niko Matsakis" notifications@github.com wrote:

After discussion in the @rust-lang/lang
https://github.com/orgs/rust-lang/teams/lang meeting, we decided not
to stabilize this feature just yet (and hopefully not ever).

The key points of discussion already appear in this thread:

  • The interaction with $crate seems thoroughly flawed. The only way to
    use this is if you are adopting a "facade" pattern like libstd, where each
    successive crate in the facade re-exports everything.
  • In practice, cargo features are probably a better way to achieve
    that pattern, and certainly more widely used.


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
#29638 (comment)

On Sat, Apr 23, 2016 at 08:00:26AM -0700, Alex Burka wrote:

I'm confused by your last bullet. Can you elaborate on how Cargo features
provide a facility for macro crates to participate in the dependency system?

I meant that cargo features can be used in preference to the facade pattern.

durka commented

Sorry for being thick... I just don't see what the relation is between
Cargo features and re-exporting things (macros or otherwise).

On Wed, Apr 27, 2016 at 1:55 PM, Niko Matsakis notifications@github.com
wrote:

On Sat, Apr 23, 2016 at 08:00:26AM -0700, Alex Burka wrote:

I'm confused by your last bullet. Can you elaborate on how Cargo features
provide a facility for macro crates to participate in the dependency
system?

I meant that cargo features can be used in preference to the facade
pattern.


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
#29638 (comment)

It can also be useful to re-export things from crates maintained by other people / in other repositories.

Since 1.15.0, macro_rules! macro can be re-exported with:

extern crate somelib;
pub use somelib::*;

As far as I’m concerned this removes the need for this feature.

Test case:

set -e
cargo new aa
cargo new bb
cargo new cc
echo 'bb = {path = "../bb"}' >> aa/Cargo.toml
echo 'cc = {path = "../cc"}' >> bb/Cargo.toml

echo '#[macro_use] extern crate bb;' > aa/src/lib.rs
echo '#[test] fn a() { assert_eq!(c!(), 42) }' >> aa/src/lib.rs

echo '#[macro_use] extern crate cc;' > bb/src/lib.rs
echo 'pub use cc::*;' >> bb/src/lib.rs

echo '#[macro_export] macro_rules! c { () => { 42 } }' > cc/src/lib.rs

(cd aa && cargo +1.15.0 test)
rm -r aa bb cc
est31 commented

I'm not sure whether that is a full replacement of the feature, as you can refine by doing #[macro_use(macro_a, macro_b, macro_c)]. std itself uses it. When I tried your example when replacing with pub use cc::c, it failed.

when replacing with pub use cc::c, it failed.

Isn’t that a bug to fix?

est31 commented

Isn’t that a bug to fix?

lol apparently the reverse is true, the glob use feature is a bug, and should be feature gated.

Also cc #35896

Maybe this should have been feature-gated but it hasn’t been for three release cycles.

Is this the tracking issue for use_extern_macros or is there a separate tracking issue for that? How are these two features different / related?

est31 commented

@withoutboats the two features are different. I'd say they both implement the same thing but in different ways.

macro_reexport:

#![crate_type = "dylib"]
#![feature(macro_reexport)]

#[macro_reexport(reexported)]
#[macro_use] #[no_link]
extern crate macro_reexport_1;

use_extern_macros:

#![feature(use_extern_macros)]

extern crate two_macros;

::two_macros::macro_one!();
two_macros::macro_one!();

mod foo { pub use two_macros::macro_one as bar; }

The tracking issue for use_extern_macros is #35896 according to its feature gate, the official place where tracking issues are defined for features.

I would guess that the intention is that use_extern_macros deprecates and replaces the macro_reexport feature, but maybe both are intended to be stabilized, one only working for macros 1.0 and one only working for macros 2.0...

@jseyfried From the discussion here and in #40768 (comment) it sounds like you think the feature is ready and are in favor of stabilizing. Is that correct?

nrc commented

macro_rexport should never be stabilised and should be deprecated once macros 2.0 happens. I don't recall exactly what was intended for use_extern_macros. iirc it was meant to be temporary to ease the transition from macros 1.0 to macros 2.0, but I might be misremembering.

@cramertj
#![feature(macro_reexport)] gates #[macro_reexport(foo, bar)] extern crate macros;, which we want to deprecate.

What I am in favor of stabilizing is #![feature(use_extern_macros)]; this allows you to reexport macros with use:

extern crate macros;
pub use macros::{foo, bar};

#![feature(use_extern_macros)] is implied by #![feature(decl_macro)] and by #![feature(proc_macro)]. We made it a separate feature since we wanted to land it before #![feature(decl_macro)] and #![feature(proc_macro)] landed and since it can stand on its own.

@jseyfried As far as I can tell, there is not a separate tracking issue for use_extern_macros.