Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

cannot find value / weird hygiene when using proc-macro-hack from macro_rules #46

Closed
japaric opened this issue Nov 17, 2019 · 2 comments

Comments

@japaric
Copy link

japaric commented Nov 17, 2019

STR

Implementation

// crate: bar
// proc-macro-hack = "=0.5.11"
// quote = "=1.0.2"
// syn = "=1.0.8"

extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro_hack::proc_macro_hack;
use quote::quote;
use syn::{
    parse::{self, Parse, ParseStream},
    parse_macro_input, Expr, Token,
};

#[proc_macro_hack]
pub fn sum(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as Input);
    let left = &input.left;
    let right = &input.right;
    quote!(#left + #right).into()
}

struct Input {
    left: Expr,
    _comma: Token![,],
    right: Expr,
}

impl Parse for Input {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        Ok(Input {
            left: input.parse()?,
            _comma: input.parse()?,
            right: input.parse()?,
        })
    }
}

Export

// crate: foo
// proc-macro-hack = "=0.5.11"

use proc_macro_hack::proc_macro_hack;

#[proc_macro_hack]
pub use bar::sum;

Use

macro_rules! good {
    () => {{
        let x = 0;
        let y = 1;
        foo::sum!(x, y)
    }}
}

macro_rules! weird {
    ($expr:expr) => {{
        let z = 3;
        foo::sum!(z, $expr)
    }}
}

fn main() {
    let a = good!();
    let b = weird!(2); //~ error: cannot find value `z` in this scope
    let z = 99;
    let c = weird!(1); // evaluates to 100
}

Here I expected b to compile and c to evaluate to 4.

Expansion

cargo expand-ion of let b:

    let b = {
        let z = 3;
        {
            enum ProcMacroHack {
                Value = ("z, 2", 0).1,
            }
            z + 2
        }
    };

Copy-pasting this into the original file produces the expected output (as in it compiles).

Metadata

$ rustc -V
rustc 1.41.0-nightly (82cf3a448 2019-11-14)

Is this a known limitation of embedding a proc-macro-hack in a macro_rules macro or is this a compiler bug? Adding support_nested and / or fake_call_site to the proc_macro_hack re-export didn't help.

@dtolnay
Copy link
Owner

dtolnay commented Nov 17, 2019

This is a compiler bug; rust-lang/rust#43081. The compiler currently has no ability to convert from its syntax tree of enum ProcMacroHack { Value = (stringify! { z , $expr }, 0).1, } into a TokenStream for passing into foo::sum. The syntax::ast::Expr inside $expr causes it to just lose all spans in the entire macro input when doing the conversion.

Depending on the use case you can usually work around this by doing less parsing in macro_rules and more parsing in your proc macro to avoid passing around syntax::ast::Expr tokens.

macro_rules! weird {
    ($($expr:tt)*) => {{
        let z = 3;
        repro::sum!(z, $($expr)*)
    }}
}

@japaric
Copy link
Author

japaric commented Nov 17, 2019

Thanks for the prompt answer! The work around works perfectly for my use case. I'll add a note to the documentation of my proc-macro-hack macro so others won't run into this rustc bug.

You probably don't want to keep a non-actionable, upstream bug so I'll close this. Thanks again.

@japaric japaric closed this as completed Nov 17, 2019
yvt added a commit to yvt/try_match-rs that referenced this issue May 30, 2020
Tested with:
- nightly-2020-05-30
- nightly-2020-04-30
- 1.40.0
- 1.37.0

This commit fixes non-trivial patterns causing an error saying
"unexpected token". This is done by parenthesizing `$p`. This might have
something to do with an input pattern being wrapped by a
`None`-delimited group and I suspect this might have been overlooked by
the crater run <rust-lang/rust#72622>, but I'm
not sure.

This commit also implements a work-around for the issue
<dtolnay/proc-macro-hack#46> by capturing
the input expression by `$($in:tt)*`. The first work-around might have
revealed this issue.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants