Skip to content

Macros from the "log" crate conflict with #[wasm_bindgen] #158

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

Closed
jsheard opened this issue Apr 22, 2018 · 2 comments
Closed

Macros from the "log" crate conflict with #[wasm_bindgen] #158

jsheard opened this issue Apr 22, 2018 · 2 comments

Comments

@jsheard
Copy link
Contributor

jsheard commented Apr 22, 2018

This program:

#![feature(proc_macro, wasm_custom_section, wasm_import_module)]

extern crate wasm_bindgen;
#[macro_use] extern crate log;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct ArbitraryStruct {
    x: u32,
}

#[wasm_bindgen]
pub fn info_print(message: &str) -> ArbitraryStruct {
    info!("{}", message);

    ArbitraryStruct { x: 42 }
}

Fails to compile:

error[E0425]: cannot find value `message` in this scope
 --> <info macros>:3:60
  |
3 | $ ( $ arg : tt ) * ) => ( log ! ( $ crate :: Level :: Info , $ ( $ arg ) * ) ;
  |                                                            ^ not found in this scope

The log macros are unable to see any of the surrounding variables, but as far as I can tell this only happens inside a #[wasm_bindgen] annotated function that returns a #[wasm_bindgen] annotated struct. If the function returns nothing or u32 or even String then it compiles successfully.

@konstin
Copy link
Contributor

konstin commented Apr 22, 2018

This is actually a regression in nightly-2018-04-15 and not related to wasm_bindgen or log specifically. I had this exact error with a different proc macro emitting a println, though the setup was to complicated to make a proper bug report out of it.

Code to Reproduce

Trying to this narrow down in wasm_bindgen has eventually led to this repository with a complete minimized example without wasm_bindgen. The relevant code boils down to the following:

The proc macro:

#![feature(proc_macro)]

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn noop_derive(_attr: TokenStream, input: TokenStream) -> TokenStream {
    // It is required to generate a new token stream; Just returning the
    // input will pass
    let item: syn::Item = syn::parse(input).unwrap();

    quote!(#item).into()
}

The function using it:

#![feature(proc_macro)]

extern crate nightly_regression_derive;

use nightly_regression_derive::noop_derive;

pub struct Foo {
    x: u32,
}

#[noop_derive]
pub fn info_print(message: &str) {
    println!("{}", message);
    Foo { x: 42 };
}

Commands to reproduce

In the repository there's reproduce.sh executing those commands.

$ rustup default nightly-2018-04-12
info: using existing install for 'nightly-2018-04-12-x86_64-unknown-linux-gnu'
info: default toolchain set to 'nightly-2018-04-12-x86_64-unknown-linux-gnu'

  nightly-2018-04-12-x86_64-unknown-linux-gnu unchanged - rustc 1.27.0-nightly (ad610bed8 2018-04-11)
$ cargo build

    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
$ rustup default nightly-2018-04-15
info: using existing install for 'nightly-2018-04-15-x86_64-unknown-linux-gnu'
info: default toolchain set to 'nightly-2018-04-15-x86_64-unknown-linux-gnu'

  nightly-2018-04-15-x86_64-unknown-linux-gnu unchanged - rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)
$ cargo build
   Compiling nightly_regression v0.1.0 (file:///home/konsti/nightly_regression)
error[E0425]: cannot find value `message` in this scope
 --> <println macros>:3:44
  |
3 | ) => ( print ! ( concat ! ( $ fmt , "\n" ) , $ ( $ arg ) * ) ) ;
  |                                            ^ not found in this scope

error: aborting due to previous error

For more information about this error, try `rustc --explain E0425`.
error: Could not compile `nightly_regression`.

To learn more, run the command again with --verbose.

Expanded code

The exanded code shows that message is in scope despite the compiler saying otherwise. The generated code is identical for the two nightlies, despite rustfmt differing in the inserted whitespace. It is identical for nightly-2018-04-14 and nightly-2018-04-15 ignoring a newline after the struct.

cargo rustc -- -Z unstable-options --pretty=expanded -Z trace-macros > expanded.rs; rustfmt expanded.rs

#![feature(prelude_import)]
#![no_std]
#![feature(proc_macro)]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std;

extern crate nightly_regression_derive;

use nightly_regression_derive::noop_derive;

pub struct Foo {
    x: u32,
}
pub fn info_print(message: &str) {
    ::io::_print(::std::fmt::Arguments::new_v1_formatted(
        &["", "\n"],
        &match (&message,) {
            (__arg0,) => [::std::fmt::ArgumentV1::new(
                __arg0,
                ::std::fmt::Display::fmt,
            )],
        },
        &[::std::fmt::rt::v1::Argument {
            position: ::std::fmt::rt::v1::Position::At(0usize),
            format: ::std::fmt::rt::v1::FormatSpec {
                fill: ' ',
                align: ::std::fmt::rt::v1::Alignment::Unknown,
                flags: 0u32,
                precision: ::std::fmt::rt::v1::Count::Implied,
                width: ::std::fmt::rt::v1::Count::Implied,
            },
        }],
    ));
    Foo { x: 42 };
}

Further investigation

The function or method must contain the instantiation of a struct with at least one public field.

Example without a public field that passes #![feature(proc_macro)]

extern crate nightly_regression_derive;

use nightly_regression_derive::noop_derive;

pub struct Foo;

#[noop_derive]
pub fn info_print(message: &str) {
println!("{}", message);
Foo { };
}

The function or method must contain a println or log using a local variable.

Example with a const as parameter to println that passes #![feature(proc_macro)]

extern crate nightly_regression_derive;

use nightly_regression_derive::noop_derive;

const MESSAGE: &str = "Hello World";

pub struct Foo {
x: u32,
}

#[noop_derive]
pub fn info_print() {
println!("{}", MESSAGE);
Foo { x: 42 };
}

The order of the prinln and the instantiation and other in between statements make no differnce.

Example with reorder fields and other statements that fails #![feature(proc_macro)]

extern crate nightly_regression_derive;

use nightly_regression_derive::noop_derive;

pub struct Foo {
x: u32,
}

#[noop_derive]
pub fn info_print(message: &str) {
let _a = 1;
Foo { x: 42 };
let _b = 1;
println!("{}", message);
let _c = 1;
}

The crate type has no influence (I tested the implicit, cdylib and dylib)

It must be a function or method emitted by a proc macro. The proc macro must return a newly generated TokenStream and not the input. I.e.

Proc macro that makes the code pass. The generated code is identical ignoring some newlines.
#![feature(proc_macro)]

extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn noop_derive(_attr: TokenStream, input: TokenStream) -> TokenStream {
    input
}

@alexcrichton
Copy link
Contributor

Thanks for the report! This should be fixed by rust-lang/rust#50152 which will hopefully be in the next nightly!

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

No branches or pull requests

3 participants