Skip to content
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

Forwarding cargo features into the wasm #35

Open
CAD97 opened this issue Mar 9, 2020 · 3 comments
Open

Forwarding cargo features into the wasm #35

CAD97 opened this issue Mar 9, 2020 · 3 comments

Comments

@CAD97
Copy link
Contributor

CAD97 commented Mar 9, 2020

I have a macro I want to run through watt (in fact, I need to in order to break a self-dependency bootstrap cycle). However, I additionally want to have the macro emit slightly more or less depending on enabled features of my facade crate.

(Specifically, because I'm writing a proc macro that targets working with syn, I want to support disabling the bits covered by syn features. In the future, I can maybe emit #[cfg(accessible(::my_runtime::syn::path)], but for now, this is covered by my runtime having equivalent feature flags.)

I have it... theoretically working, but the hack to call one of 24 different symbols is... not pretty at best.

image

image

It'd be really nice if watt would support passing through a second FFI-safe argument so we can inform the proc macro about some more state. The author would be responsible for making sure that cargo still understands all of the inputs to the proc macro for when it needs to be rerun.

@dtolnay
Copy link
Owner

dtolnay commented Mar 9, 2020

I would prefer not to support a second argument. But the shim crate has complete control of the TokenStream given to the wasm macro, so you could prepend any extra information onto there.

@CAD97
Copy link
Contributor Author

CAD97 commented Mar 10, 2020

Alright, yes, especially because it's just a set of flags that I need to pass through to the macro, I've successfully set up to pass some magic keywords at the beginning of the token stream. For anyone finding this later, here's what it looks like:

The proc-macro crate:

fn ident(s: &str) -> TokenTree {
    TokenTree::Ident(Ident::new(s, Span::call_site()))
}

#[proc_macro]
pub fn compile(input: TokenStream) -> TokenStream {
    let mut processed_input = TokenStream::new();
    if cfg!(feature = "syn.parsing") {
        processed_input.extend(Some(ident("syn_parsing")));
    }
    if cfg!(feature = "syn.printing") {
        processed_input.extend(Some(ident("syn_printing")));
    }
    if cfg!(feature = "syn.clone-impls") {
        processed_input.extend(Some(ident("syn_clone_impls")));
    }
    if cfg!(feature = "syn.extra-traits") {
        processed_input.extend(Some(ident("syn_extra_traits")));
    }
    processed_input.extend(input);

The wasm landing pad:

mod kw {
    syn::custom_keyword!(syn_parsing);
    syn::custom_keyword!(syn_printing);
    syn::custom_keyword!(syn_clone_impls);
    syn::custom_keyword!(syn_extra_traits);
}

struct MacroInput {
    opts: compile::Opts,
    input: TokenStream,
}

impl syn::parse::Parse for MacroInput {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        struct Flags {
            parsing: Option<kw::syn_parsing>,
            printing: Option<kw::syn_printing>,
            clone_impls: Option<kw::syn_clone_impls>,
            extra_traits: Option<kw::syn_extra_traits>,
        }
        let flags = Flags {
            parsing: input.parse()?,
            printing: input.parse()?,
            clone_impls: input.parse()?,
            extra_traits: input.parse()?,
        };
        let input = input.parse()?;
        Ok(MacroInput {
            opts: compile::Opts {
                syn: compile::syn::Opts {
                    parsing: flags.parsing.is_some(),
                    printing: flags.printing.is_some(),
                    clone_impls: flags.clone_impls.is_some(),
                    extra_traits: flags.extra_traits.is_some(),
                },
            },
            input,
        })
    }
}

#[no_mangle]
pub extern "C" fn compile(input: TokenStream) -> TokenStream {
    syn::parse2(input)
        .and_then(|MacroInput { opts, input }| pegcel_codegen::compile(input, opts))
        .unwrap_or_else(|err| err.to_compile_error())
}

I can understand the desire to keep the API of the watt machine to just that of proc_macros. However, this feels like jumping through hoops in the name of theoretical purity.

The actual thing I'm trying to accomplish here is pass on the cargo feature set being used for the actual proc_macro crate to the WASM-based proc_macro impl. Additionally, I (obviously) would like to use only a singular wasm blob.

This specifically (choosing behavior based on cargo features) is a behavior easily achievable in nonsandboxed proc macros, and doesn't mess with any of the desirable features of repeatable proc macros that don't touch the environment.

Would you be opposed to a solution that purely serves that use case (cargo features), rather than more generally being any ffi-safe auxiliary argument?

@dtolnay
Copy link
Owner

dtolnay commented Mar 10, 2020

I would consider a PR for a function that lets the wasm macro query for whether a particular feature is enabled in the shim crate. I don't have an implementation in mind for how that would work.

@CAD97 CAD97 changed the title Add some way to pass auxilary info Forwarding cargo features into the wasm Mar 12, 2020
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

Successfully merging a pull request may close this issue.

2 participants