Help stabilize a subset of Macros 2.0!


#61

Why macros was designed to be not namespaced? Because if they do, then there will be no difference between where the macro is located, regardless it’s in my crate or not.

Maybe everything in Rust should be namespaced so the language is more intuitive (Less things to learn, friendly to it’s user)? Please consider it when you guys start to working on Rust 2.0 :wink:


#62

That is exactly how macros 2.0 already works, which is what this thread is talking about stabilising.


#63

I think local macros still require #[macro_use] so that the compiler knows what order in which to expand them. After all, macros can expand to other macro definitions and even modules!

If you could use them then I think it would probably need weird restrictions like "use foo::my_macro must come after the declaration of mod foo;" (similar to the limitations of #[macro_use]).

(But I haven’t checked myself; maybe @Ixrec is right for macro macros?)

(Update: he is! :smiley: )


#64

macro_rules is vestigial; we reserved the macro keyword for a real macro design, which is what macros 2.0 is. We needed to ship Rust 1.0 and had to make some hard choices; this was one of them.


#65

Yeah, I believe the main purpose of https://github.com/rust-lang/rfcs/pull/1560 was to specify how this could possibly work in the face of properly namespaced/module-ized macros.


#66

Nah, I think RFC 1560 was the one that made this work:

mod parent {
    use self::child_1::x;
    mod child_1 {
        pub fn x() {}
    }

    mod child_2 {
        use parent::x; // In 1.0.0 this produces "unresolved import"
    }
}

There is an example in 1560 where a glob import appears to import a macro (after the quote “this example assumes modularised macros”), but it isn’t the main focus. I also don’t see any discussion of use on the declarative macros 2.0 tracking issue.


#67

Some more sleuthing:

  • RFC 1561 (macro naming and modularization) seems to suggest that use should work on declarative macros 2.0.
  • Talk in the tracking issue also seems to take this as a given. I.e. it is only macro_rules! macros that should get the short end of the stick.
  • The “roadmap” for that issue has every box ticked with no explicit mention of decl macros 2.0.
  • declarative macros 2.0 still has no documentation

Baaaahhhhhh, you know what? Documentation or no documentation, I’m just going to copy the first thing I see on the tracking issue, tweak it until it compiles, and see what happens:

#![feature(decl_macro)]

fn main() {
  println!("{}", m::m_helper!());
}

mod m {
  pub macro m_helper() {
    3
  }
}

Output:

3

Whew! So there you have it. Contrary to what I previously said, it appears that use-able, locally-defined macros are not only planned, but they are already implemented (at least partially). However, it is only for declarative macros 2.0 (macro, not macro_rules!)


#68

In fact pretty crazy things already seem to work:

#![feature(decl_macro)]

fn main() {
    foo::Foo.poke();
}

mod foo {
    super::def_struct!( Foo );
}

macro def_struct( $name:ident ) {
    struct Foo;
    impl Foo {  fn msg( &self ) -> &str { "yo!" }  }

    pub struct $name;
    add_poke!( $name, Foo.msg() );
}

use m::add_poke;

mod m {
    pub fn show( msg : &str ) { 
        println!( "{}", msg );
    }

    pub macro add_poke( $t:ty, $msg:expr ) {
        impl super::Pokeable for $t {
            fn poke( &self ) { 
                show( $msg );
            }
        }
    }
}

trait Pokeable {
    fn poke( &self );
}

Notice the struct $name in def_struct (where $name is Foo) injects a definition into the caller, while the struct Foo in the same macro is invisible and does not conflict with it, yet the latter can be used in an expr passed to another macro that implements a trait for the first struct. Holy cow, and this works right now.

The same hygienic magic (I presume) that’s keeping Foo from conflicting with Foo can also have some unfortunate results though, such as when I tried to access a field of a struct created by a macro:

5 |     foo::X.child.poke();
  |            ^^^^^ did you mean `child`?

I also noticed that m::show has to be public currently. I can imagine why, but I hope that requirement will go away eventually.