Open abonander opened 7 years ago
cc @jseyfried
@abonander What about this?
#![feature(proc_macro)]
extern crate foo_macros;
use foo_macros::foo_attr;
mod bar {
#![foo_attr]
}
If compiles, then the issue is that the attribute is at the crate root, not that it is an inner attribute.
@jseyfried Updated issue as this only affects the crate root
So @jseyfried and I figured out on IRC that this is due to the compiler trying to expand the attribute before the use_extern_macros
code gets to see the actual import. I'd really like to make this work as I have some cool use-cases for it. We had a couple ideas for how to get this working but we figured we should expand the discussion on it before taking any action.
My best idea is along the lines of this: if an attribute macro fails to resolve at crate root only (since inner attributes on modules should resolve in the parent's scope), import-process root, get the resolution for the attribute, reinitialize the resolver and copy the resolution for the attribute macro, then expand the root module.
Some concerns from @jseyfried:
I believe that would be doable implementation-wise, but it could lead to coherence issues For example, if the attribute macro expanded into nothing Or if it expanded into a different import of itself The resolution of a name isn't supposed to change as we expand things It can only go from "indeterminate" to "success" or "indeterminate" to "failed" If we preserve that with extra checks, what you're proposing could work though, I think
cc @nrc
@nrc Some input here when you get the chance, I'd like to tackle this soonish.
sorry it took a while to see this - I was away on parental leave and then had to nuke my notifications.
since inner attributes on modules should resolve in the parent's scope
I wonder if this axiom is correct? An alternative seems to be that we could resolve attributes at the scope in which they are written rather than the scope in which they apply. Would that have knock-on effects?
resolve attributes at the scope in which they are written rather than the scope in which they apply
Interesting, I like this idea. I'm not aware of any knock-on effects, it should be straightforward to implement, and it would address this use case.
I was under the impression that inner attributes were syntactic sugar for outer attributes, and it makes sense for outer attributes to resolve in the same scope as the item they're applied to. Crates are just special because there's no outer scope to resolve.
I was under the impression that inner attributes were syntactic sugar for outer attributes
Whether a feature is desugared or not is an implementation detail and we should do what makes the most sense from the user's POV, not the compilers. Desugaring can take place after name resolution if we like, so we don't need to change the implementation either.
So the plan of action is to just reorder attribute expansion and import processing for a given item? Or just at the crate root for now?
@abonander I wouldn't think of this as reordering attribute expansion or import processing, just changing the scope in which e.g. bar
resolves in #[foo] mod m { #![bar] #[baz] fn f() {} ... }
from that of foo
to that of baz
.
If we make this change, I think we should make the change everywhere (not just at the crate root) for consistency.
#![feature(extern_absolute_paths, proc_macro)]
#![::my_crate::my_attribute]
I believe the absoluteness of the path can be omitted in the future, once the RFC 2126 implementation proceeds further.
However #45458 still makes using the crate-level macros difficult. The issue concerns compiler plugins, but can also be reproduced through attribute macros.
Moving from #48066 as per @Manishearth wishes. Although I would prefer if the title of this issue was updated - the resolution itself is somewhat "solved" in nightly. Even if the current ::absolute
syntax ends up not being the final one, I'd expect the same mechanism to work with whichever syntax the module update ends up implementing.
While normal proc-macro attribute work, crate level proc-macro attributes seem to have several issues. The clearest of these is the order of the attributes and extern crate
statements: https://github.com/rust-lang/rust/issues/41430. #![feature(extern_absolute_paths)]
feature can work around this specific issue; However this alone doesn't make proc macros work.
Some of these issues are due to lack of library support (syn), but some are caused by rustc itself.
First of all: The following proc-macro works just fine with nightly 29c8276ce:
#[proc_macro_attribute]
fn attribute(_: TokenStream, input: TokenStream) -> TokenStream {
input
}
#![feature(extern_absolute_paths)]
#![::my_crate::attribute]
fn main() {}
As long as the macro doesn't touch the input, everything works just fine. However this isn't really that useful for a macro. Once we try doing anything with the input token stream (short of a .clone()
), things start breaking.
#[proc_macro_attribute]
fn attribute(_: TokenStream, input: TokenStream) -> TokenStream {
TokenStream::from_iter( input.into_iter() )
}
#![feature(extern_absolute_paths)]
#![::my_crate::attribute]
fn main() {}
This results in the following error message
error: expected identifier, found `{`
--> <macro expansion>:1:1
|
1 | pub mod {
| ^^^ expected identifier
The reason seems to be that the TokenStream
the attribute receives looks like:
pub mod {
...
}
This is most likely caused by the ast::Crate
modeling the items as a Mod
with no Ident
: https://github.com/rust-lang/rust/blob/master/src/libsyntax/ast.rs#L447-L451
pub mod
away :x:Implementing a Crate
type for Syn and having its to_tokens
skip the pub mod {
.. }
bits allows the parser to process the output correctly. I'm also hoping this would be the way to go, since the parse_crate_mod method just parses inner attributes and mod items without seemingly caring about the mod
keywords or braces.
However now we encounter the following ICE:
``` error: an inner attribute is not permitted in this context | = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. thread 'rustc' panicked at 'internal error: entered unreachable code', libsyntax/ext/expand.rs:258:18 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. stack backtrace: 0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49 1: std::sys_common::backtrace::_print at libstd/sys_common/backtrace.rs:71 2: std::panicking::default_hook::{{closure}} at libstd/sys_common/backtrace.rs:59 at libstd/panicking.rs:380 3: std::panicking::default_hook at libstd/panicking.rs:396 4: std::panicking::rust_panic_with_hook at libstd/panicking.rs:576 5: std::panicking::begin_panic 6: syntax::ext::expand::MacroExpander::expand_crate 7: rustc_driver::driver::phase_2_configure_and_expand_inner::{{closure}} 8: rustc_driver::driver::phase_2_configure_and_expand_inner 9: rustc_driver::driver::compile_input 10: rustc_driver::run_compiler error: internal compiler error: unexpected panic ```
```
Compiling test_crate v0.1.0 (file:///C:/Dev/Projects/crate_level_proc_macro/test_crate)
thread '
```
Compiling test_crate v0.1.0 (file:///C:/Dev/Projects/crate_level_proc_macro/test_crate)
thread '
A crate's token-stream should look exactly like the source; we shouldn't be emitting pub mod {}
around its contents.
I think the solution here would be to introduce Crate
variants for Expansion[Kind]
/Annotatable
/Nonterminal
and handle them accordingly. However, syn
would have to know to parse multiple items or else most users won't see the whole input.
cc @petrochenkov
Or would it be preferable to pass a syntatically correct representation of the crate as some pub mod krate {}
?
However, syn would have to know to parse multiple items or else most users won't see the whole input.
I believe syn already handles "crate" properly as https://docs.rs/syn/0.12/syn/struct.File.html
Or would it be preferable to pass a syntatically correct representation of the crate as some
pub mod krate {}
?
From a user perspective, I found the pub mod {}
a bit confusing at first, given those tokens do not exist in the parsed input. However if this issue didn't exist and I could have just used syn, I would have never even encountered the pub mod { .. }
syntax as a user.
So while a token stream without pub mod {}
would be more pure, I don't know if it would be worth a much more complex implementation at least from user perspective.
I think for sanity's sake we should pass the crate's tokens just as they appear in the source, which would mean handling a crate not like a module but like its own thing.
In #50101 @alexcrichton stated that he doesn't expect support for proc-macros on the crate root to be stabilized in the foreseeable future, that we should address the possible use-cases individually instead of allowing an attribute to completely rewrite the crate.
The use case that prompted me to examine this in the first place was prototyping parts of the test framework pre-RFC. That RFC suggested a whole-crate proc-macro as one option for custom test framework. It even goes as far as stating the following for one of the options:
This assumes that #![foo] ("inner attribute") macros work on modules and on crates.
Currently the general understanding among people seems to be that inner attribute proc macros not working on modules and crates is a bug instead of an unsupported feature. If I remember right they do work on modules after all; Allowing people to wrap the whole crate in a mod
statement to achieve the same result.
Though I do appreciate @alexcrichton's concerns over this issue. Just to keep the discussion here I'll copy his response from #50101 here:
Er sorry yeah, let me clarify. Right now for "Macros 1.2" were very unlikely to stabilize attribute invocations on modules. It's a weird question of what does #[foo] mod foo; receive? Does it receive mod foo ;? The contents of the module foo?
Something like entire crate expansion also has huge repercussions on hygiene as well as whether it's feasible/quick. Entire crate expansion runs a risk of being extremely slow (we have to serialize back and forth with the procedural macro) and highly non-incremental (it's a complete black box to the compiler). I'd imagine that we're very far away from stabilizing this functionality, if at all.
In that sense I'd personally be wary of landing this functionality in the compiler, even on nightly. I think it'd be best to take other avenues of attack for use cases that would otherwise require entire-crate expansion.
FWIW for the test frameworks thing we can make it work without explicitly having this feature, what test frameworks need is a way to apply a whole-crate proc macro as an argument to rustc. This is less tricky.
On Fri, Apr 27, 2018, 2:01 AM Mikko Rantanen notifications@github.com wrote:
The use case that prompted me to examine this in the first place was prototyping parts of the test framework pre-RFC. That RFC suggested a whole-crate proc-macro as one option for custom test framework. It even goes as far as stating the following for one of the options:
This assumes that #![foo] ("inner attribute") macros work on modules and on crates.
Currently the general understanding among people seems to be that inner attribute proc macros not working on modules and crates is a bug instead of an unsupported feature. If I remember right they do work on modules after all; Allowing people to wrap the whole crate in a mod statement to achieve the same result.
Though I do appreciate @alexcrichton https://github.com/alexcrichton's concerns over this issue. Just to keep the discussion here I'll copy his response from #50101 https://github.com/rust-lang/rust/pull/50101 here:
Er sorry yeah, let me clarify. Right now for "Macros 1.2" were very unlikely to stabilize attribute invocations on modules. It's a weird question of what does #[foo] mod foo; receive? Does it receive mod foo ;? The contents of the module foo?
Something like entire crate expansion also has huge repercussions on hygiene as well as whether it's feasible/quick. Entire crate expansion runs a risk of being extremely slow (we have to serialize back and forth with the procedural macro) and highly non-incremental (it's a complete black box to the compiler). I'd imagine that we're very far away from stabilizing this functionality, if at all.
In that sense I'd personally be wary of landing this functionality in the compiler, even on nightly. I think it'd be best to take other avenues of attack for use cases that would otherwise require entire-crate expansion.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/41430#issuecomment-384910595, or mute the thread https://github.com/notifications/unsubscribe-auth/ABivSJMruLX34G06FmxGus3Hgf78cU7oks5tst5WgaJpZM4NDpSo .
@Manishearth Doesn't that result in the same hygiene/incremental issues? Referring here to @alexcrichton's comment:
Something like entire crate expansion also has huge repercussions on hygiene as well as whether it's feasible/quick. Entire crate expansion runs a risk of being extremely slow (we have to serialize back and forth with the procedural macro) and highly non-incremental (it's a complete black box to the compiler).
I know one of the options for the test framework RFC is to just generate a main function. The "Only add stuff"-kind of proc macro does somewhat circumvent the hygiene/incrmental compilation issues, but that's just one option mentioned in the RFC - all the other options face the same issues I'd imagine.
Essentially what I'm after here is: If those issues are solved/resolved/accepeted in relation to the the test RFC, doesn't that apply here as well? Although what I suspect is that given the issue raised here, the test RFC will end up going with the "just generate main()" option.
Slow and non-incremental matters less for testing.
"just add a main function" is none of the options, all of the options already have to run some kind of whole crate proc macro to rewrite things to be pub
. This is what libtest already does, it folds the entire crate. The question debated in the RFC is whether or not this should be exposed to the user as a whole crate proc macro API.
The hygiene issue is still a thing, but if folks just use the "generate main" API we put on crates.io that's still not a problem. And most of the frameworks which wish to do something more complex than that probably won't need to worry as much about hygiene.
Well, that's not entirely true -- the "just generate main" API would have less of a serialization overhead. But that's about it.
Another reason that I don't think that this is a great idea is https://github.com/rust-lang/rust/issues/43081 right now. Any change to the AST would lose span information for the entire crate. I think that's basically a showstopper until we fix that.
This is what libtest already does, it folds the entire crate.
Could someone point me to the code that does this? (I could not find it myself.) We want to update the Prusti verifier to work with the latest version of the Rust compiler. However, one of the blocking issues we have is that I cannot find any more a way to obtain a mutable reference into an AST. We used to rewrite AST to type-check specifications. Treating each specification as a separate procedural macro invocation is very problematic because we need to maintain a global state.
Since https://github.com/rust-lang/rust/issues/43081#issuecomment-726878122 mentions that https://github.com/rust-lang/rust/issues/43081 is likely to be fixed soon for all stable Rust syntax we can reiterate on this issue. I'd love to hear what else is blocking this feature and if there are any other show stoppers, bugs or design considerations before we can get there.
When invoking some attribute procedural macro with inner form, i.e.
#![foo_attr]
at the crate root, a resolution error occurs:This should, ideally, resolve and execute properly.