Register an enum before lowering its discriminants
Rust lets a discriminant name an earlier variant of the enum being declared. `enumflags2` expands to exactly that -- `Popup = next_bit(Auto::Window as u128)` -- and rustnim had no type for the sibling reference, because the enum was not inserted until after every discriminant had been lowered. Registered in shape first, with the real entry replacing it. Found by re-running expanded libcosmic once `u128` stopped being a blocker. The next halt after this one is `enumflags2::_internal::next_bit`, which is a dependency's function rather than a language feature -- noted in DESIGN.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
abb295c parent: f9ca982 modified
DESIGN.md +20 -0 | @@ -1190,3 +1190,23 @@ writing once per byte into the formatter. | ||
| 1190 | 1190 | |
| 1191 | 1191 | This is the crate whose six files the transpiler in `findings/` emitted empty |
| 1192 | 1192 | output for, while exiting 0. |
| 1193 | + | |
| 1194 | +### After 128-bit integers: where expanded libcosmic stops now | |
| 1195 | + | |
| 1196 | +Re-running the expanded crate (44,649 lines) with `u128` implemented moved the | |
| 1197 | +halt twice: | |
| 1198 | + | |
| 1199 | +1. `Auto::Window as u128` inside the *declaration* of `enum Auto` — a | |
| 1200 | + discriminant naming an earlier variant of the enum being declared, which is | |
| 1201 | + how `enumflags2` expands. The enum is now registered in shape before its | |
| 1202 | + discriminants are lowered, so the sibling reference has a type. Fixed. | |
| 1203 | +2. `::enumflags2::_internal::next_bit` — and this one is not a language | |
| 1204 | + feature at all. It is a plain function in a dependency crate, and | |
| 1205 | + `-Zunpretty=expanded` expands only the crate you point it at, so the | |
| 1206 | + definition is simply absent from the input. It is the dependency question | |
| 1207 | + again, with the same answer as before: other people's crates are just Rust, | |
| 1208 | + and the way through is to feed rustnim the dependency's source too, not to | |
| 1209 | + special-case the name. | |
| 1210 | + | |
| 1211 | +So the language-feature list did bottom out here, exactly as predicted; what | |
| 1212 | +is left in front of libcosmic is source availability, not semantics. | |
| @@ -1190,3 +1190,23 @@ writing once per byte into the formatter. | |||
| 1190 | 1190 | ||
| 1191 | This is the crate whose six files the transpiler in `findings/` emitted empty | 1191 | This is the crate whose six files the transpiler in `findings/` emitted empty |
| 1192 | output for, while exiting 0. | 1192 | output for, while exiting 0. |
| 1193 | + | ||
| 1194 | +### After 128-bit integers: where expanded libcosmic stops now | ||
| 1195 | + | ||
| 1196 | +Re-running the expanded crate (44,649 lines) with `u128` implemented moved the | ||
| 1197 | +halt twice: | ||
| 1198 | + | ||
| 1199 | +1. `Auto::Window as u128` inside the *declaration* of `enum Auto` — a | ||
| 1200 | + discriminant naming an earlier variant of the enum being declared, which is | ||
| 1201 | + how `enumflags2` expands. The enum is now registered in shape before its | ||
| 1202 | + discriminants are lowered, so the sibling reference has a type. Fixed. | ||
| 1203 | +2. `::enumflags2::_internal::next_bit` — and this one is not a language | ||
| 1204 | + feature at all. It is a plain function in a dependency crate, and | ||
| 1205 | + `-Zunpretty=expanded` expands only the crate you point it at, so the | ||
| 1206 | + definition is simply absent from the input. It is the dependency question | ||
| 1207 | + again, with the same answer as before: other people's crates are just Rust, | ||
| 1208 | + and the way through is to feed rustnim the dependency's source too, not to | ||
| 1209 | + special-case the name. | ||
| 1210 | + | ||
| 1211 | +So the language-feature list did bottom out here, exactly as predicted; what | ||
| 1212 | +is left in front of libcosmic is source availability, not semantics. | ||
modified
src/lower.rs +31 -7 | @@ -718,6 +718,37 @@ impl Lowerer { | ||
| 718 | 718 | } |
| 719 | 719 | self.type_generics |
| 720 | 720 | .insert(name.clone(), Self::generics_of(&e.generics)); |
| 721 | + | |
| 722 | + // Register the enum before lowering its discriminants. Rust | |
| 723 | + // lets a discriminant name an earlier variant of the enum | |
| 724 | + // being declared -- `Popup = next_bit(Auto::Window as u128)` | |
| 725 | + // is how `enumflags2` expands -- and without this the sibling | |
| 726 | + // reference has no known type. Shape only; the real entry | |
| 727 | + // replaces it below. | |
| 728 | + let simple = e.variants.iter().all(|v| v.fields.is_empty()); | |
| 729 | + for v in &e.variants { | |
| 730 | + self.variant_owner | |
| 731 | + .entry(v.ident.to_string()) | |
| 732 | + .or_default() | |
| 733 | + .push(name.clone()); | |
| 734 | + } | |
| 735 | + self.enums.insert( | |
| 736 | + name.clone(), | |
| 737 | + EnumDef { | |
| 738 | + name: name.clone(), | |
| 739 | + simple, | |
| 740 | + variants: e | |
| 741 | + .variants | |
| 742 | + .iter() | |
| 743 | + .map(|v| Variant { | |
| 744 | + name: v.ident.to_string(), | |
| 745 | + discriminant: None, | |
| 746 | + fields: Vec::new(), | |
| 747 | + }) | |
| 748 | + .collect(), | |
| 749 | + }, | |
| 750 | + ); | |
| 751 | + | |
| 721 | 752 | let mut variants = Vec::new(); |
| 722 | 753 | for v in &e.variants { |
| 723 | 754 | let vname = v.ident.to_string(); |
| @@ -739,13 +770,6 @@ impl Lowerer { | ||
| 739 | 770 | } |
| 740 | 771 | variants.push(Variant { name: vname, discriminant, fields }); |
| 741 | 772 | } |
| 742 | - let simple = variants.iter().all(|v| v.fields.is_empty()); | |
| 743 | - for v in &variants { | |
| 744 | - self.variant_owner | |
| 745 | - .entry(v.name.clone()) | |
| 746 | - .or_default() | |
| 747 | - .push(name.clone()); | |
| 748 | - } | |
| 749 | 773 | self.enums.insert( |
| 750 | 774 | name.clone(), |
| 751 | 775 | EnumDef { name, simple, variants }, |
| @@ -718,6 +718,37 @@ impl Lowerer { | |||
| 718 | } | 718 | } |
| 719 | self.type_generics | 719 | self.type_generics |
| 720 | .insert(name.clone(), Self::generics_of(&e.generics)); | 720 | .insert(name.clone(), Self::generics_of(&e.generics)); |
| 721 | + | ||
| 722 | + // Register the enum before lowering its discriminants. Rust | ||
| 723 | + // lets a discriminant name an earlier variant of the enum | ||
| 724 | + // being declared -- `Popup = next_bit(Auto::Window as u128)` | ||
| 725 | + // is how `enumflags2` expands -- and without this the sibling | ||
| 726 | + // reference has no known type. Shape only; the real entry | ||
| 727 | + // replaces it below. | ||
| 728 | + let simple = e.variants.iter().all(|v| v.fields.is_empty()); | ||
| 729 | + for v in &e.variants { | ||
| 730 | + self.variant_owner | ||
| 731 | + .entry(v.ident.to_string()) | ||
| 732 | + .or_default() | ||
| 733 | + .push(name.clone()); | ||
| 734 | + } | ||
| 735 | + self.enums.insert( | ||
| 736 | + name.clone(), | ||
| 737 | + EnumDef { | ||
| 738 | + name: name.clone(), | ||
| 739 | + simple, | ||
| 740 | + variants: e | ||
| 741 | + .variants | ||
| 742 | + .iter() | ||
| 743 | + .map(|v| Variant { | ||
| 744 | + name: v.ident.to_string(), | ||
| 745 | + discriminant: None, | ||
| 746 | + fields: Vec::new(), | ||
| 747 | + }) | ||
| 748 | + .collect(), | ||
| 749 | + }, | ||
| 750 | + ); | ||
| 751 | + | ||
| 721 | let mut variants = Vec::new(); | 752 | let mut variants = Vec::new(); |
| 722 | for v in &e.variants { | 753 | for v in &e.variants { |
| 723 | let vname = v.ident.to_string(); | 754 | let vname = v.ident.to_string(); |
| @@ -739,13 +770,6 @@ impl Lowerer { | |||
| 739 | } | 770 | } |
| 740 | variants.push(Variant { name: vname, discriminant, fields }); | 771 | variants.push(Variant { name: vname, discriminant, fields }); |
| 741 | } | 772 | } |
| 742 | - let simple = variants.iter().all(|v| v.fields.is_empty()); | ||
| 743 | - for v in &variants { | ||
| 744 | - self.variant_owner | ||
| 745 | - .entry(v.name.clone()) | ||
| 746 | - .or_default() | ||
| 747 | - .push(name.clone()); | ||
| 748 | - } | ||
| 749 | self.enums.insert( | 773 | self.enums.insert( |
| 750 | name.clone(), | 774 | name.clone(), |
| 751 | EnumDef { name, simple, variants }, | 775 | EnumDef { name, simple, variants }, |