I'm trying to get my head around function-like procedural macros and struggling with the basics.
For a starter, I tried to create a macro that just prints all the tokens and does nothing:
extern crate proc_macro;
extern crate syn;
use proc_macro::TokenStream;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
println!( "{:?}", input );
TokenStream::new()
}
I then tried to use it in another binary:
extern crate rust_procmacro_test;
fn main() {
rust_procmacro_test::my_macro!( aaa );
}
When I do cargo build I get this:
Compiling rust_procmacro_test v0.1.0
Compiling procmacro_user v0.1.0
error[E0658]: procedural macros cannot be expanded to statements (see issue #54727)
--> src\main.rs:5:5
|
5 | rust_procmacro_test::my_macro!( aaa );
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TokenStream [Ident { ident: "aaa", span: #0 bytes(114..117) }]
error: aborting due to previous error
It tells me that this cannot be compiled, but println inside the macro works. What is happening?
Issue #54727 is very broad and I'm not sure how it relates to this.
I'm using stable-i686-pc-windows-gnu, rustc 1.31.1 (b6c32da9b 2018-12-18).
What is happening?
While it's possible to define a function-like procedural macro in stable Rust, it's not yet possible to call one as an expression / statement. This is because there are concerns about how hygiene should be applied. There are other locations, such as in item position, where it is available:
extern crate rust_procmacro_test;
rust_procmacro_test::my_macro!( aaa );
fn main() {}
but println inside the macro works
This is probably just some laziness in the implementation — the macro is eagerly evaluated and then the feature gate is checked later. While this might be inefficient at the moment, once the feature is fully stabilized it won't matter.
Related
I would like to generate a variable type and initializer from a (procedural) macro. Since macros consume and generate streams (and not trees) of tokens, morally, it should be possible to do something like:
extern crate quote;
use quote::quote;
#[proc_macro]
pub fn foo(tokens: TokenStream) -> TokenStream {
TokenStream::from(quote! {
: u8 = 42
})
}
pub static FOO foo!();
But alas, this fails with
|
28 | pub static FOO foo!();
| ^^^ expected one of `:`, `;`, or `=`
Now, of course I could turn this inside out and require writing something like
foo!(FOO)
which would expand to
pub static FOO: u8 = 42;
but the reason I would like to do things piecewise is to be able to use the same macro to generate types-and-initializers in different contexts (e.g. for local let bindings, not just statics).
You can't do what you are trying to do, not because : u8 = 42 is not correctly converted to a TokenStream, but simply because macro invocations (independently of their definition) cannot occur anywhere. In particular, they can only occur where a handle of the TokenStream would be reduced to a node, which is not the case in your example. This is because Rust has to parse the file once, before it knows anything about what macros do, exactly to find out all macros. In this first step, it only knows about its own syntax. It is smart enough to say "ok, this whole node of my AST is just this macro invocation" without knowing what the macro invocation will actually do, but it can't say "this macro invocation will feed me these tokens, so the next token I should read is ;" (which would be the case in your example), because it doesn't know what the macro does yet!
To convince yourself, image that the macro foo!() produces the token mod A {, and bar!() the token }. Then, the following example auto-destroys itself
foo!()
#[proc_macro]
fn foo(_: TokenStream) -> TokenStream { ... }
#[proc_macro]
fn bar(_: TokenStream) -> TokenStream { ... }
bar!()
Because if you expand correctly the macros, then they hide themselves by putting themselves into a module, which prevents the macros from being expanded, which makes them visible again, ... Even worse examples (where the issue occurs before visibility checks, at the parsing step) are easy to provide as soon as you allow what you are trying to do.
I tried to add allow dead_code and unused_must_use:
#[allow(dead_code)]
#[allow(unused_must_use)]
#[implement(MyStruct)]
pub struct MyStructList(pub Rc<Vec<MyStruct>>);
But still got the warning, still new to rust, what does it mean to call drop ?
warning: unused return value of `Box::<T>::from_raw` that must be used
--> test.rs
|
| #[implement(MyStruct)]
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: call `drop(from_raw(ptr))` if you intend to drop the `Box`
= note: this warning originates in the attribute macro `implement` (in Nightly builds, run with -Z macro-backtrace for more info)
I had this issue once, with a macro of my own. At the end I fixed the macro, but while the bug was there I workarounded with this trick:
#[allow(unused_must_use)]
mod my_struct {
use super::*;
#[implement(MyStruct)]
pub struct MyStructList(pub Rc<Vec<MyStruct>>);
}
pub use my_struct::*;
This is similar to the solution by #Andrew, but the allow directive applies only to the inner private module, instead of to all your module.
You may need to add/fix the pub use below depending on the details of what your macro exactly does. You may prefer pub use my_struct::MyStructList;, for example, and have finer control of your exports.
I want to define an enum using a macro. The enum needs to implement the strum traits {Display, EnumIter, EnumString}. I also want to keep the warnings missing_copy_implementations, missing_debug_implementations on. I came up with the following snippet:
#![warn(
missing_copy_implementations,
missing_debug_implementations,
)]
macro_rules! define_fruits {
{$($fruit:ident -> $name:literal),* $(,)?} => {
#[derive(Display, EnumIter, EnumString, Clone, Copy, Debug)]
pub enum Fruits {
$(
#[strum(to_string = $name)]
$fruit,
)*
}
};
}
define_fruits! {
Apple -> "green",
Orange -> "orange",
}
The above works fine except I get the warnings:
|
4 | missing_copy_implementations,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this warning originates in the macro `define_fruits` (in Nightly builds, run with -Z macro-backtrace for more info)
and similar for missing_debug_implementations.
These warnings go away when I remove Display, EnumIter, EnumString from my enum. They also go away if I define the same enum outside of a macro.
I cannot seem to find a way to get rid of the warnings in the above scenario, can someone help?
TL/DR: the problem is with derive(EnumIter), which isn't compatible with these lints. I've opened the issue on strum repository asking for possible changes.
To see what really happens, let's try to cargo expand the current code. After some simplification and stubbing out the unnecessary parts, we can see the following structure:
///An iterator over the variants of [Self]
pub struct FruitsIter {
idx: usize,
back_idx: usize,
marker: PhantomData<()>,
}
This is a type you get when you call IntoEnumIterator::iter derived for your enum. There's no Copy or Debug implementation for this struct - they are neither derived nor explicitly added by strum, so the lint sees the violation and fires.
A hint on the problem source can be seen when looking at the exact error, as provided by cargo check (or at the eror highlighting from rust-analyzer):
warning: type could implement `Copy`; consider adding `impl Copy`
--> src/lib.rs:10:27
|
10 | #[derive(Display, EnumIter, EnumString, Clone, Copy, Debug)]
| ___________________________^
11 | | pub enum Fruits {
| |___________^
Note that the span of the "erroneous" code starts from the EnumIter - since that's the token which the FruitsIter's span is tied to, warning on the FruitsIter is shown as a warning on the derive(EnumIter). And indeed, if we drop this derive - warning disappears.
There's nothing you can do with this, I'm afraid, aside from explicitly allowing these lints for the whole module containing your enum. This is something that should probably be fixed by the strum maintainers.
I am writing an OS in Rust and need to directly call into a virtual address that I'm calculating (of type u32). I expected this to be relatively simple:
let code = virtual_address as (extern "C" fn ());
(code)();
However, this complains that the cast is non-primitive. It suggests I use the From trait, but I don't see how this could help (although I am relatively new to Rust and so could be missing something).
error[E0605]: non-primitive cast: `u32` as `extern "C" fn()`
--> src/main.rs:3:16
|
3 | let code = virtual_address as (extern "C" fn ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
I have everything in libcore at my disposal, but haven't ported std and so can't rely on anything that isn't no_std
Casts of the type _ as f-ptr are not allowed (see the Rustonomicon chapter on casts). So, as far as I can tell, the only way to cast to function pointer types is to use the all mighty weapon mem::transmute().
But before we can use transmute(), we have to bring our input into the right memory layout. We do this by casting to *const () (a void pointer). Afterwards we can use transmute() to get what we want:
let ptr = virtual_address as *const ();
let code: extern "C" fn() = unsafe { std::mem::transmute(ptr) };
(code)();
If you find yourself doing this frequently, various kinds of macros can remove the boilerplate. One possibility:
macro_rules! example {
($address:expr, $t:ty) => {
std::mem::transmute::<*const (), $t>($address as _)
};
}
let f = unsafe { example!(virtual_address, extern "C" fn()) };
f();
However, a few notes on this:
If you, future reader, want to use this to do simple FFI things: please take a moment to think about it again. Calculating function pointers yourself is rarely necessary.
Usually extern "C" functions have the type unsafe extern "C" fn(). This means that those functions are unsafe to call. You should probably add the unsafe to your function.
Using the below code in main.rs, when I run cargo test it returns error code 176, when I add a test or `any statement in main function. It starts returning error code 160.
#![no_std]
#![no_main]
#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(not(test))]
extern crate panic_halt;
#[no_mangle]
pub unsafe extern "C" fn main() {
}
From this link I found out, that
Exit code 176 means "Unable to install on a network drive. Select
another install location in your preferences and retry installing."
When I tried to find back trace through lldb it returned
error: invalid thread
I couldn't find any thread which mentions similar error, so asking it here.
Any help is appreciated.
Using nightly (nightly-x86_64-apple-darwin)
Thanks.
The problem is your main function, which has an invalid signature for unix platforms. It must return an i32 (or better c_int, but for simplicity we suppose they are idential).
The 176 is just some random value in the rax register when leaving the main function.
So change your main signature to:
pub unsafe extern "C" fn main() -> i32
and return a return code (e.g. 0 which means success)
or
pub unsafe extern "C" fn main() -> !
and use the exit syscall on linux (or an infinite loop on embedded systems).