Description
Hello,
I encountered an ICE while writing a proc-macro. The idea is to wrap the content of a function with the attribute on it in a closure and then call it. (The real code does some extra steps but those are not important here).
This looks like the following in quote!
:
let closure = move || {
#block
};
let mut result = closure();
result
The problem is the missing mut
when the original function mutates any of its arguments.
It works without problems for functions that do not mutate their arguments.
When the code produced by the proc-macro is inlined by hand the compiler correctly identifies the missing mut
and works as expected.
Code
I uploaded my reproduction on https://github.com/FrTerstappen/rust-ice-20250530
Cargo.toml
[workspace]
resolver = "3"
exclude = []
members = ["crates/ice_proc_macro", "crates/ice_test"]
default-members = ["crates/ice_test"]
[workspace.package]
version = "0.0.0"
edition = "2024"
license = "UNLICENSED"
publish = false
readme = "README.md"
authors = ["Dummy"]
rust-version = "1.87.0"
repository = "Dummy"
[workspace.dependencies]
proc-macro2 = { version = "1.0.95", default-features = false }
quote = { version = "1.0.40", default-features = false }
syn = { version = "2.0.101", default-features = true, features = ["full"] }
ice_proc_macro = { version = "0.0.0", path = "crates/ice_proc_macro" }
ice_test/Cargo.toml
[package]
name = "ice_test"
description = "TODO"
version.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
readme.workspace = true
authors.workspace = true
rust-version.workspace = true
repository.workspace = true
[dependencies]
ice_proc_macro.workspace = true
ice_test/src/main.rs
fn main() {
let mut dummy = 0;
test_ice(&mut dummy);
}
#[ice_proc_macro::slow_warning()]
pub fn test_ice(dummy: &mut i32) {
*dummy += 1;
}
ice_proc_macro/Cargo.toml
[package]
name = "ice_proc_macro"
version.workspace = true
edition.workspace = true
license.workspace = true
publish.workspace = true
readme.workspace = true
authors.workspace = true
rust-version.workspace = true
repository.workspace = true
[lib]
proc-macro = true
[dependencies]
proc-macro2.workspace = true
quote.workspace = true
syn = { workspace = true, features = ["full"] }
ice_proc_macro/src/lib.rs
use proc_macro::TokenStream;
use quote::{ToTokens as _, quote};
use syn::{parse, *};
#[proc_macro_attribute]
pub fn slow_warning(_attr: TokenStream, item: TokenStream) -> TokenStream {
let parsed = parse(item).unwrap();
let Item::Fn(function) = parsed else {
panic!("This can only be used on functions");
};
let block = Block {
brace_token: function.block.brace_token,
stmts: vec![],
};
let mut wrapped_function = ItemFn {
attrs: function.attrs.clone(),
vis: function.vis.clone(),
sig: function.sig.clone(),
block: Box::new(block),
};
let block = function.block;
let quoted = quote! {{
let closure = move || {
#block
};
let mut result = closure();
result
}};
let token_stream = quoted.into();
let parsed = parse(token_stream).unwrap();
wrapped_function.block = parsed;
let token_stream = wrapped_function.into_token_stream();
token_stream.into()
}
Meta
rustc --version --verbose
:
rustc 1.89.0-nightly (1bbd62e54 2025-05-29)
binary: rustc
commit-hash: 1bbd62e547ba5cc08ccb44c27def3d33195d2dd5
commit-date: 2025-05-29
host: x86_64-unknown-linux-gnu
release: 1.89.0-nightly
LLVM version: 20.1.5
Error output
Compiling ice_test v0.0.0 (/home/username/Documents/IceTest/crates/ice_test)
thread 'rustc' panicked at compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:847:56:
called `Option::unwrap()` on a `None` value
stack backtrace:
0: 0x76e930aa5273 - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h05e9f29e57dbb2e9
1: 0x76e931202af7 - core::fmt::write::hd2dec42d1c7eed2b
2: 0x76e930a9b0f3 - std::io::Write::write_fmt::he35f7ccd30502a06
3: 0x76e930aa50d2 - std::sys::backtrace::BacktraceLock::print::h0f7594efff5cfc74
4: 0x76e930aa8cca - std::panicking::default_hook::{{closure}}::h8c1a12024f8172d5
5: 0x76e930aa884f - std::panicking::default_hook::h9b1e5be56a49e97f
6: 0x76e92fb0d693 - std[aebe2beb07d4602e]::panicking::update_hook::<alloc[afc79f7023c726bc]::boxed::Box<rustc_driver_impl[fce054646584467]::install_ice_hook::{closure#1}>>::{closure#0}
7: 0x76e930aa9543 - std::panicking::rust_panic_with_hook::h466604244439fd1e
8: 0x76e930aa9206 - std::panicking::begin_panic_handler::{{closure}}::h6ba0bdba6613eee2
9: 0x76e930aa5739 - std::sys::backtrace::__rust_end_short_backtrace::h1c6dac257377e0cf
10: 0x76e930aa8efd - __rustc[9ea1f2a0e5b4e008]::rust_begin_unwind
11: 0x76e92d2a7820 - core::panicking::panic_fmt::h5eca1388f6f924aa
12: 0x76e92d2ad8cc - core::panicking::panic::h8b23f86cb3cf8301
13: 0x76e92d2a9379 - core::option::unwrap_failed::h0567c92acfdde0c7
14: 0x76e92f8ac898 - <rustc_borrowck[16831c75c4923d8d]::MirBorrowckCtxt>::show_mutating_upvar
15: 0x76e92f13c6c8 - <rustc_borrowck[16831c75c4923d8d]::MirBorrowckCtxt>::report_mutability_error
16: 0x76e9324bb7f7 - rustc_borrowck[16831c75c4923d8d]::do_mir_borrowck
17: 0x76e9314db80a - rustc_borrowck[16831c75c4923d8d]::mir_borrowck
18: 0x76e9314db519 - rustc_query_impl[b8ddf5711aeae68a]::plumbing::__rust_begin_short_backtrace::<rustc_query_impl[b8ddf5711aeae68a]::query_impl::mir_borrowck::dynamic_query::{closure#2}::{closure#0}, rustc_middle[f62d8e2871d7ed5a]::query::erase::Erased<[u8; 8usize]>>
19: 0x76e9314e291f - rustc_query_system[e527f3fe7b1ba41c]::query::plumbing::try_execute_query::<rustc_query_impl[b8ddf5711aeae68a]::DynamicConfig<rustc_data_structures[63644864ec52dfbb]::vec_cache::VecCache<rustc_span[674833ee7b2aca12]::def_id::LocalDefId, rustc_middle[f62d8e2871d7ed5a]::query::erase::Erased<[u8; 8usize]>, rustc_query_system[e527f3fe7b1ba41c]::dep_graph::graph::DepNodeIndex>, false, false, false>, rustc_query_impl[b8ddf5711aeae68a]::plumbing::QueryCtxt, true>
20: 0x76e9313a2d97 - rustc_query_impl[b8ddf5711aeae68a]::query_impl::mir_borrowck::get_query_incr::__rust_end_short_backtrace
21: 0x76e931d3095b - rustc_mir_transform[8802a7a466bf19d]::mir_drops_elaborated_and_const_checked
22: 0x76e931d2ffef - rustc_query_impl[b8ddf5711aeae68a]::plumbing::__rust_begin_short_backtrace::<rustc_query_impl[b8ddf5711aeae68a]::query_impl::mir_drops_elaborated_and_const_checked::dynamic_query::{closure#2}::{closure#0}, rustc_middle[f62d8e2871d7ed5a]::query::erase::Erased<[u8; 8usize]>>
23: 0x76e9314e291f - rustc_query_system[e527f3fe7b1ba41c]::query::plumbing::try_execute_query::<rustc_query_impl[b8ddf5711aeae68a]::DynamicConfig<rustc_data_structures[63644864ec52dfbb]::vec_cache::VecCache<rustc_span[674833ee7b2aca12]::def_id::LocalDefId, rustc_middle[f62d8e2871d7ed5a]::query::erase::Erased<[u8; 8usize]>, rustc_query_system[e527f3fe7b1ba41c]::dep_graph::graph::DepNodeIndex>, false, false, false>, rustc_query_impl[b8ddf5711aeae68a]::plumbing::QueryCtxt, true>
24: 0x76e9313a25e5 - rustc_query_impl[b8ddf5711aeae68a]::query_impl::mir_drops_elaborated_and_const_checked::get_query_incr::__rust_end_short_backtrace
25: 0x76e92ff709a5 - <rustc_middle[f62d8e2871d7ed5a]::ty::context::TyCtxt>::par_hir_body_owners::<rustc_interface[82c1fd2543dc0997]::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}
26: 0x76e92ff4dd6c - rustc_data_structures[63644864ec52dfbb]::sync::parallel::par_slice::par_rec::<&rustc_span[674833ee7b2aca12]::def_id::LocalDefId, rustc_data_structures[63644864ec52dfbb]::sync::parallel::par_for_each_in<&rustc_span[674833ee7b2aca12]::def_id::LocalDefId, &[rustc_span[674833ee7b2aca12]::def_id::LocalDefId], <rustc_middle[f62d8e2871d7ed5a]::ty::context::TyCtxt>::par_hir_body_owners<rustc_interface[82c1fd2543dc0997]::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}>::{closure#0}::{closure#0}>
27: 0x76e92ffaa6e0 - <rayon_core[6e5c1cc81dd3488b]::job::StackJob<rayon_core[6e5c1cc81dd3488b]::latch::SpinLatch, rayon_core[6e5c1cc81dd3488b]::join::join_context::call_b<(), rayon_core[6e5c1cc81dd3488b]::join::join::call<(), rustc_data_structures[63644864ec52dfbb]::sync::parallel::par_slice::par_rec<&rustc_span[674833ee7b2aca12]::def_id::LocalDefId, rustc_data_structures[63644864ec52dfbb]::sync::parallel::par_for_each_in<&rustc_span[674833ee7b2aca12]::def_id::LocalDefId, &[rustc_span[674833ee7b2aca12]::def_id::LocalDefId], <rustc_middle[f62d8e2871d7ed5a]::ty::context::TyCtxt>::par_hir_body_owners<rustc_interface[82c1fd2543dc0997]::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}>::{closure#0}::{closure#0}>::{closure#2}>::{closure#0}>::{closure#0}, ()> as rayon_core[6e5c1cc81dd3488b]::job::Job>::execute
28: 0x76e92f5f833e - <rayon_core[6e5c1cc81dd3488b]::registry::WorkerThread>::wait_until_cold
29: 0x76e92f5f5cc1 - <rayon_core[6e5c1cc81dd3488b]::registry::ThreadBuilder>::run
30: 0x76e92fb035bd - std[aebe2beb07d4602e]::sys::backtrace::__rust_begin_short_backtrace::<<rayon_core[6e5c1cc81dd3488b]::ThreadPoolBuilder>::build_scoped<rustc_interface[82c1fd2543dc0997]::util::run_in_thread_pool_with_globals<rustc_interface[82c1fd2543dc0997]::interface::run_compiler<(), rustc_driver_impl[fce054646584467]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#5}::{closure#0}::{closure#0}, rustc_interface[82c1fd2543dc0997]::util::run_in_thread_pool_with_globals<rustc_interface[82c1fd2543dc0997]::interface::run_compiler<(), rustc_driver_impl[fce054646584467]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#5}::{closure#0}::{closure#1}, ()>::{closure#0}::{closure#0}::{closure#0}, ()>
31: 0x76e92fb12592 - <<std[aebe2beb07d4602e]::thread::Builder>::spawn_unchecked_<<rayon_core[6e5c1cc81dd3488b]::ThreadPoolBuilder>::build_scoped<rustc_interface[82c1fd2543dc0997]::util::run_in_thread_pool_with_globals<rustc_interface[82c1fd2543dc0997]::interface::run_compiler<(), rustc_driver_impl[fce054646584467]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#5}::{closure#0}::{closure#0}, rustc_interface[82c1fd2543dc0997]::util::run_in_thread_pool_with_globals<rustc_interface[82c1fd2543dc0997]::interface::run_compiler<(), rustc_driver_impl[fce054646584467]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#5}::{closure#0}::{closure#1}, ()>::{closure#0}::{closure#0}::{closure#0}, ()>::{closure#1} as core[50b5a8f5e07183b8]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
32: 0x76e9323baebd - std::sys::pal::unix::thread::Thread::new::thread_start::h45be790786894548
33: 0x76e92bea57eb - <unknown>
34: 0x76e92bf2918c - <unknown>
35: 0x0 - <unknown>
error: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md
note: please make sure that you have updated to the latest nightly
note: please attach the file at `/home/username/Documents/IceTest/rustc-ice-2025-05-30T11_08_17-190002.txt` to your bug report
note: compiler flags: --crate-type bin -C embed-bitcode=no -C debuginfo=2 -C linker=clang -C incremental=[REDACTED] -C link-arg=-fuse-ld=/usr/bin/mold -C link-arg=-Wl,--no-rosegment -Z share-generics=y -Z threads=0 -C target-feature=+x87
note: some of the compiler flags provided by cargo are hidden
query stack during panic:
#0 [mir_borrowck] borrow-checking `test_ice`
#1 [mir_drops_elaborated_and_const_checked] elaborating drops for `test_ice::{closure#0}`
#2 [analysis] running analysis passes on this crate
end of query stack
error: could not compile `ice_test` (bin "ice_test")
Caused by:
process didn't exit successfully: `/home/username/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name ice_test --edition=2024 crates/ice_test/src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=259 --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=a97ebdd71087afaa -C extra-filename=-4e58fb3e312afec5 --out-dir /home/username/Documents/IceTest/target/x86_64-unknown-linux-gnu/debug/deps --target x86_64-unknown-linux-gnu -C linker=clang -C incremental=/home/username/Documents/IceTest/target/x86_64-unknown-linux-gnu/debug/incremental -L dependency=/home/username/Documents/IceTest/target/x86_64-unknown-linux-gnu/debug/deps -L dependency=/home/username/Documents/IceTest/target/debug/deps --extern ice_proc_macro=/home/username/Documents/IceTest/target/debug/deps/libice_proc_macro-30ef8dc7428832ac.so -Clink-arg=-fuse-ld=/usr/bin/mold -Clink-arg=-Wl,--no-rosegment -Zshare-generics=y -Zthreads=0 -Ctarget-feature=+x87` (exit status: 101)
Backtrace
RUST_BACKTRACE=1 cargo run
Compiling ice_test v0.0.0 (/home/username/Documents/IceTest/crates/ice_test)
thread 'rustc' panicked at compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs:847:56:
called `Option::unwrap()` on a `None` value
stack backtrace:
0: __rustc::rust_begin_unwind
1: core::panicking::panic_fmt
2: core::panicking::panic
3: core::option::unwrap_failed
4: <rustc_borrowck::MirBorrowckCtxt>::show_mutating_upvar
5: <rustc_borrowck::MirBorrowckCtxt>::report_mutability_error
6: rustc_borrowck::do_mir_borrowck
7: rustc_borrowck::mir_borrowck
[... omitted 1 frame ...]
8: rustc_mir_transform::mir_drops_elaborated_and_const_checked
[... omitted 1 frame ...]
9: <rustc_middle::ty::context::TyCtxt>::par_hir_body_owners::<rustc_interface::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}
10: rustc_data_structures::sync::parallel::par_slice::par_rec::<&rustc_span::def_id::LocalDefId, rustc_data_structures::sync::parallel::par_for_each_in<&rustc_span::def_id::LocalDefId, &[rustc_span::def_id::LocalDefId], <rustc_middle::ty::context::TyCtxt>::par_hir_body_owners<rustc_interface::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}>::{closure#0}::{closure#0}>
11: <rayon_core::job::StackJob<rayon_core::latch::SpinLatch, rayon_core::join::join_context::call_b<(), rayon_core::join::join::call<(), rustc_data_structures::sync::parallel::par_slice::par_rec<&rustc_span::def_id::LocalDefId, rustc_data_structures::sync::parallel::par_for_each_in<&rustc_span::def_id::LocalDefId, &[rustc_span::def_id::LocalDefId], <rustc_middle::ty::context::TyCtxt>::par_hir_body_owners<rustc_interface::passes::run_required_analyses::{closure#2}::{closure#0}>::{closure#0}>::{closure#0}::{closure#0}>::{closure#2}>::{closure#0}>::{closure#0}, ()> as rayon_core::job::Job>::execute
12: <rayon_core::registry::WorkerThread>::wait_until_cold
13: <rayon_core::registry::ThreadBuilder>::run
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md
note: please make sure that you have updated to the latest nightly
note: please attach the file at `/home/username/Documents/IceTest/rustc-ice-2025-05-30T11_13_14-190503.txt` to your bug report
note: compiler flags: --crate-type bin -C embed-bitcode=no -C debuginfo=2 -C linker=clang -C incremental=[REDACTED] -C link-arg=-fuse-ld=/usr/bin/mold -C link-arg=-Wl,--no-rosegment -Z share-generics=y -Z threads=0 -C target-feature=+x87
note: some of the compiler flags provided by cargo are hidden
query stack during panic:
#0 [mir_borrowck] borrow-checking `test_ice`
#1 [mir_drops_elaborated_and_const_checked] elaborating drops for `test_ice::{closure#0}`
#2 [analysis] running analysis passes on this crate
end of query stack
error: could not compile `ice_test` (bin "ice_test")
Caused by:
process didn't exit successfully: `/home/username/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name ice_test --edition=2024 crates/ice_test/src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=242 --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=a97ebdd71087afaa -C extra-filename=-4e58fb3e312afec5 --out-dir /home/username/Documents/IceTest/target/x86_64-unknown-linux-gnu/debug/deps --target x86_64-unknown-linux-gnu -C linker=clang -C incremental=/home/username/Documents/IceTest/target/x86_64-unknown-linux-gnu/debug/incremental -L dependency=/home/username/Documents/IceTest/target/x86_64-unknown-linux-gnu/debug/deps -L dependency=/home/username/Documents/IceTest/target/debug/deps --extern ice_proc_macro=/home/username/Documents/IceTest/target/debug/deps/libice_proc_macro-30ef8dc7428832ac.so -Clink-arg=-fuse-ld=/usr/bin/mold -Clink-arg=-Wl,--no-rosegment -Zshare-generics=y -Zthreads=0 -Ctarget-feature=+x87` (exit status: 101)