Change:
See the minimal repro in a comment below. The original was a repository that wasn't meant to be published (yet). <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --: Needs as a non-rust dependency. See Easiest way should be to download the and build it with CMake, and point the above environment variables to the appropriate locations in your download folder.
Assigning myself to minimize, hopefully writing some cargo-minimize docs for incremental on the way.
I have an MCVE (less minimal than I had hoped sadly) Where is a proc macro crate containing I cannot manually expand the macro. I have already tried which didn't help. I will continue investigating this. To reproduce, first compile it without any cfgs and then enable .
Bisection results using cc the bisection points at searched nightlies: from nightly-2021-01-01 to nightly-2023-04-19 regressed nightly: nightly-2023-03-17 searched commit range: regressed commit: <details<summarybisected with <a href=''cargo-bisect-rustc</av0.6.6</summaryHost triple: aarch64-unknown-linux-gnu Reproduce with: </details
I've found some potentially interesting bits: I've printed the result from both sessions and got the following diff: Some DefIds are different (makes sense, a field was ) and two spans changed their SyntaxContext drastically. These are spans that are expanded from the proc macro using . <details<summarybefore</summary</details<details<summaryafter</summary</details
The fact that DefIds are different is not an issue, those are just a shorthand, what's relevant is the DefPathHash. I'm a bit more worried about the change in SyntaxContext. This may indicate a hash collision in expansions. Thanks a lot for the investigation.
Writing my progress here before forgetting about it. First execution: MIR for ; requires marking some spans with Replace, so creates expansion expn5, and syntax context 40; MIR for ; requires marking some other spans with Replace, so creates expansion expn6, and syntax context 41. Subtlety: expn5 and expn6 are created from the same span information, because eguiinspectderive does not give the two closures different spans. So these expansions' hashes collide. Because hash collisions are bad, we increment the disambiguator for expn6. expn5 gets hash expn6 gets hash Second execution: for is green, so is not re-computed; for is green too; MIR for depends on the layout of TileDef; for is re-built; requires marking the span with Replace, so to create an expansion and a syntax context; closure has not been built, there is no hash collision, so the expansion for closure has hash , which is not the expected hash ; .
The ICE itself should have been removed by , in which MIR building does not create expansions any more.
did fix the underlying problem, or only the way that it's reached here? (i.e. would it be possible to reach a similar ICE with non expansions)
It just removed the symptom. The underlying bug is still there, and should be fixed by I don't think it currently possible to trigger the ICE, not until someone adds post-lowering expansions back.

Discussion:
<ins> // force-host // no-prefer-dynamic #![crate_type = "proc-macro"] extern crate proc_macro; use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; #[proc_macro] pub fn expand(_: TokenStream) -> TokenStream { // Hand expansion/rewriting of // ``` // quote! { //     output_mut(|o| o.copied_text = "".into()); //     output_mut(|o| o.copied_text = format!("{:?}", self.tile_db)); // }.into() // ``` stream([ ident("output_mut"), group( Delimiter::Parenthesis, [ or(), ident("o"), or(), ident("o"), dot(), ident("copied_text"), eq(), string(""), dot(), ident("into"), group(Delimiter::Parenthesis, []), ], ), semi(), ident("output_mut"), group( Delimiter::Parenthesis, [ or(), ident("o"), or(), ident("o"), dot(), ident("copied_text"), eq(), ident("format"), bang(), group( Delimiter::Parenthesis, [string("{:?}"), comma(), ident("self"), dot(), ident("tile_db")], ), ], ), semi(), ]) } fn stream(s: impl IntoIterator<Item = TokenTree>) -> TokenStream { s.into_iter().collect() } fn ident(i: &str) -> TokenTree { TokenTree::Ident(Ident::new(i, Span::call_site())) } fn group(d: Delimiter, s: impl IntoIterator<Item = TokenTree>) -> TokenTree { TokenTree::Group(Group::new(d, s.into_iter().collect())) } fn semi() -> TokenTree { TokenTree::Punct(Punct::new(';', Spacing::Alone)) } fn or() -> TokenTree { TokenTree::Punct(Punct::new('|', Spacing::Alone)) } fn dot() -> TokenTree { TokenTree::Punct(Punct::new('.', Spacing::Alone)) } fn eq() -> TokenTree { TokenTree::Punct(Punct::new('=', Spacing::Alone)) } fn bang() -> TokenTree { TokenTree::Punct(Punct::new('!', Spacing::Alone)) } fn comma() -> TokenTree { TokenTree::Punct(Punct::new(',', Spacing::Alone)) } fn string(s: &str) -> TokenTree { TokenTree::Literal(Literal::string(s)) } </ins>

<ins> // aux-build: egui_inspect_derive.rs // revisions: cpass1 cpass2 extern crate egui_inspect_derive; pub struct TileDef { pub layer: (), #[cfg(cpass2)] pub blend_graphic: String, } pub(crate) struct GameState { pub(crate) tile_db: TileDb, } impl GameState { fn inspect_mut(&mut self) { egui_inspect_derive::expand! {} } } fn new() -> GameState { loop {} } fn main() { let mut app = new(); app.inspect_mut(); } // this is actually used pub struct TileDb { unknown_bg: TileDef, } impl std::fmt::Debug for TileDb { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { loop {} } } pub struct PlatformOutput { pub copied_text: String, } pub fn output_mut<R>(writer: impl FnOnce(&mut PlatformOutput) -> R) -> R { loop {} } </ins>