Skip to content

use of proc_macro[2]::TokenStream in a vector in TLS causes rustc panic #63804

Open
@hawkinsw

Description

@hawkinsw

I am relatively new to rust, but I am a huge, huge fan. I hate to report an issue and hope that it's something worthwhile.

I am attempting to use proc_macro[2]::TokenStream in the implementation of a proc macro. I share a vector of TokenStreams across multiple invocations of those proc macros using storage allocated with thread_local!. That sounds complicated, but I promise it's not:

extern crate proc_macro;
extern crate proc_macro2;

use proc_macro::TokenStream;
use quote::quote;
use std::cell::RefCell;

thread_local!(static ARMS: RefCell<Vec<proc_macro2::TokenStream>> = RefCell::new(Vec::<proc_macro2::TokenStream>::new()));

#[proc_macro_attribute]
pub fn arm(
    _ : TokenStream,
    item: TokenStream,
) -> TokenStream {
    let ts = proc_macro2::TokenStream::new();
    ARMS.with(|arms| {
        arms.borrow_mut().push(ts);
    });
    item
}

#[proc_macro]
pub fn make_arms(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
  proc_macro::TokenStream::from(quote!{ pub fn arms(&self) { self.arm1(); } })
}

Here's how I could use these proc macros:

extern crate rust_macro_error;

use rust_macro_error::arm;
use rust_macro_error::make_arms;

struct T {
    _i: usize,
}

impl T {
    pub fn new() -> Self {
        T { _i: 2 }
    }
    #[arm]
    pub fn arm1(&self) {
    }
    make_arms!();
}

fn main() {
    T::new().arms();
}

Here is my Cargo.toml

[package]
name = "rust_macro_error"
version = "0.1.0"
authors = ["Will Hawkins <[email protected]>"]
edition = "2018"

[lib]
proc-macro = true

[[bin]]
name = "rust_macro_error"
path = "src/main.rs"

[dependencies]
quote="1.0.0"
proc-macro2="1.0.1"

[dependencies.syn]
version="1.0"
features = ["full"]

If it's easier, you can grab these files from this git repository: http://git.obs.cr/hawkinsw/rust_macro_error

When I run cargo build, I get a rustc failure. I have attempted to compile this with stable, nightly and "head" (stage2 compiler of HEAD). I get the same error no matter which compiler:

fatal runtime error: failed to initiate panic, error 5

I can run the compiler under gdb but see nothing worthwhile:

#0  0x00007ffff32999f3 in futex_wait_cancelable (private=<optimized out>, 
    expected=0, futex_word=0x7fffec4f54a8)
    at ../sysdeps/unix/sysv/linux/futex-internal.h:88
#1  __pthread_cond_wait_common (abstime=0x0, mutex=0x7fffec4f5450, 
    cond=0x7fffec4f5480) at pthread_cond_wait.c:502
#2  __pthread_cond_wait (cond=0x7fffec4f5480, mutex=0x7fffec4f5450)
    at pthread_cond_wait.c:655
#3  0x00007ffff3db4e62 in std::sys::unix::condvar::Condvar::wait (
    mutex=<optimized out>, self=<optimized out>)
    at src/libstd/sys/unix/condvar.rs:71
#4  std::sys_common::condvar::Condvar::wait (mutex=<optimized out>, 
    self=<optimized out>) at src/libstd/sys_common/condvar.rs:41
#5  std::sync::condvar::Condvar::wait (self=<optimized out>, guard=...)
    at src/libstd/sync/condvar.rs:204
#6  0x00007ffff3d917db in std::thread::park () at src/libstd/thread/mod.rs:911
#7  0x00007ffff3d746e2 in std::sync::mpsc::blocking::WaitToken::wait (self=...)
    at src/libstd/sync/mpsc/blocking.rs:71
#8  0x00007ffff702275d in std::sync::mpsc::stream::Packet<T>::recv (
    self=0x7fffd8001080, deadline=...)
    at /home/hawkinsw/code/rust/rust/src/libstd/sync/mpsc/stream.rs:194
#9  0x00007ffff70267ac in std::sync::mpsc::Receiver<T>::recv (
    self=0x7ffff04f88f0)
    at /home/hawkinsw/code/rust/rust/src/libstd/sync/mpsc/mod.rs:1197
#10 0x00007ffff7027055 in <std::sync::mpsc::IntoIter<T> as core::iter::traits::iterator::Iterator>::next (self=0x7ffff04f88f0)
    at /home/hawkinsw/code/rust/rust/src/libstd/sync/mpsc/mod.rs:1533
#11 jobserver::imp::spawn_helper::{{closure}} ()
    at /home/hawkinsw/.cargo/registry/src/github.com-1ecc6299db9ec823/jobserver-0.1.16/src/lib.rs:635
#12 std::sys_common::backtrace::__rust_begin_short_backtrace (f=...)
    at /home/hawkinsw/code/rust/rust/src/libstd/sys_common/backtrace.rs:77
#13 0x00007ffff702a983 in std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}} () at /home/hawkinsw/code/rust/rust/src/libstd/thread/mod.rs:470
#14 <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once (self=..., _args=<optimized out>)
    at /home/hawkinsw/code/rust/rust/src/libstd/panic.rs:315
#15 std::panicking::try::do_call (data=<optimized out>)
    at /home/hawkinsw/code/rust/rust/src/libstd/panicking.rs:296
#16 0x00007ffff3db6a14 in __rust_maybe_catch_panic (f=0x0, 
    data=0x7fffec4f54a8 "\002", data_ptr=0x7ffff04f8a00, 
    vtable_ptr=0x7ffff04f8a08) at src/libpanic_unwind/lib.rs:80
#17 0x00007ffff702ab84 in std::panicking::try (f=...)
    at /home/hawkinsw/code/rust/rust/src/libstd/panicking.rs:275
#18 std::panic::catch_unwind (f=...)
    at /home/hawkinsw/code/rust/rust/src/libstd/panic.rs:394
#19 std::thread::Builder::spawn_unchecked::{{closure}} ()
    at /home/hawkinsw/code/rust/rust/src/libstd/thread/mod.rs:469
#20 core::ops::function::FnOnce::call_once{{vtable-shim}} ()
    at /home/hawkinsw/code/rust/rust/src/libcore/ops/function.rs:235
#21 0x00007ffff3d75a6f in <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once (self=..., args=<optimized out>)
    at /home/hawkinsw/code/rust/rust/src/liballoc/boxed.rs:922
#22 0x00007ffff3d7e7d0 in <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once (self=0x7fffec4f55b0, args=<optimized out>)
    at /home/hawkinsw/code/rust/rust/src/liballoc/boxed.rs:922
#23 std::sys_common::thread::start_thread (main=<optimized out>)
    at src/libstd/sys_common/thread.rs:13
#24 std::sys::unix::thread::Thread::new::thread_start (main=0x7fffec4f55b0)
    at src/libstd/sys/unix/thread.rs:79
#25 0x00007ffff32936db in start_thread (arg=0x7ffff04fa700)
    at pthread_create.c:463
#26 0x00007ffff3a4b88f in clone ()
    at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

There are a few interesting things that I have noticed while attempting to debug this:

  1. If I use another type, say String, in the TLS vector, everything works fine.
  2. If I don't push any values to the vector, everything works a-ok.
  3. I can sometimes get the error message:
    use-after-free in 'proc_macro' handle
    and can narrow that down to line 315 in libproc_macro/bridge/client.rs. I understand that to mean that the "server" for the proc_macro implementation is not alive, for some reason. I have seen Panic inside panic when procedural macro is called with proc_macro::bridge::client #60593 and think that they are related but cannot seem to find the common thread.

I have debugged as much as I can and I am reaching out for your help! I would love to help solve the problem if you can point me in the right direction.

Again, thank you for all the work that you are doing for rust and the community around the language.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)A-proc-macrosArea: Procedural macrosT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions