Register length-bearing names before any type is mapped
The previous commit moved associated consts into `collect_impl`, which was still too late: a struct field's type can be `[u8; T::MAX_SIZE]`, and field types are mapped in the same pass that collects the `impl T` carrying the const. Whether the name resolved came down to which appeared first in the file -- the same order-dependence as the enum discriminants, one pass earlier. A pass before all the others now registers just the names an array length can refer to: associated consts, and the variants of C-like enums. Failures there are ignored on purpose, since it runs before the tables a type needs are filled; anything it cannot map yet is left to the pass that can. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e534fb9 parent: 2687049 modified
src/lower.rs +72 -0 | @@ -524,6 +524,20 @@ impl Lowerer { | ||
| 524 | 524 | } |
| 525 | 525 | } |
| 526 | 526 | |
| 527 | + // Pass 0.5: associated consts and enum variant names. | |
| 528 | + // | |
| 529 | + // A struct field's type can be `[u8; T::MAX_SIZE]`, and field types | |
| 530 | + // are mapped in pass 1 -- which is also when the `impl T` carrying | |
| 531 | + // that const is collected. Whether the name resolved therefore | |
| 532 | + // depended on which came first in the file. Registering the names on | |
| 533 | + // their own, before anything is mapped, removes the ordering. | |
| 534 | + for (m, f) in files { | |
| 535 | + self.cur_mod = m.clone(); | |
| 536 | + for item in &f.items { | |
| 537 | + self.precollect(item); | |
| 538 | + } | |
| 539 | + } | |
| 540 | + | |
| 527 | 541 | // Pass 1: signatures and struct shapes, so that a call can be typed |
| 528 | 542 | // regardless of declaration order (Rust has no forward declarations). |
| 529 | 543 | for (m, f) in files { |
| @@ -711,6 +725,64 @@ impl Lowerer { | ||
| 711 | 725 | } |
| 712 | 726 | } |
| 713 | 727 | |
| 728 | + /// Register the names an array length may refer to, before any type is | |
| 729 | + /// mapped. Names only -- everything else waits for the ordinary passes. | |
| 730 | + /// | |
| 731 | + /// Failures are ignored rather than reported: this runs before the tables | |
| 732 | + /// a type needs are filled, so a const whose own type is not yet mappable | |
| 733 | + /// is simply left to the pass that can map it. | |
| 734 | + fn precollect(&mut self, item: &Item) { | |
| 735 | + match item { | |
| 736 | + Item::Mod(m) => { | |
| 737 | + if let Some((_, items)) = &m.content { | |
| 738 | + for i in items { | |
| 739 | + self.precollect(i); | |
| 740 | + } | |
| 741 | + } | |
| 742 | + } | |
| 743 | + Item::Const(c) => { | |
| 744 | + if let Some(items) = derive_item_block(c) { | |
| 745 | + for i in &items { | |
| 746 | + self.precollect(i); | |
| 747 | + } | |
| 748 | + } | |
| 749 | + } | |
| 750 | + Item::Enum(e) => { | |
| 751 | + // Only the variant names, and only for a C-like enum: those | |
| 752 | + // are the ones that can appear in a length. | |
| 753 | + if e.variants.iter().all(|v| v.fields.is_empty()) { | |
| 754 | + let name = e.ident.to_string(); | |
| 755 | + self.enums.entry(name.clone()).or_insert_with(|| EnumDef { | |
| 756 | + name: name.clone(), | |
| 757 | + simple: true, | |
| 758 | + variants: e | |
| 759 | + .variants | |
| 760 | + .iter() | |
| 761 | + .map(|v| Variant { | |
| 762 | + name: v.ident.to_string(), | |
| 763 | + discriminant: None, | |
| 764 | + disc_expr: None, | |
| 765 | + fields: Vec::new(), | |
| 766 | + }) | |
| 767 | + .collect(), | |
| 768 | + }); | |
| 769 | + } | |
| 770 | + } | |
| 771 | + Item::Impl(im) => { | |
| 772 | + let Ok(self_ty) = self.map_ty(&im.self_ty) else { return }; | |
| 773 | + let tyn = type_name(&self_ty); | |
| 774 | + for it in &im.items { | |
| 775 | + let syn::ImplItem::Const(c) = it else { continue }; | |
| 776 | + let Ok(t) = self.map_ty(&c.ty) else { continue }; | |
| 777 | + let n = ident(&format!("{}_{}", tyn, c.ident)); | |
| 778 | + self.assoc_consts | |
| 779 | + .insert((tyn.clone(), c.ident.to_string()), (n, t.owned())); | |
| 780 | + } | |
| 781 | + } | |
| 782 | + _ => {} | |
| 783 | + } | |
| 784 | + } | |
| 785 | + | |
| 714 | 786 | fn collect(&mut self, item: &Item) -> Result<(), String> { |
| 715 | 787 | // A `#[cfg(..)]` item exists only under some feature set. Dropping it |
| 716 | 788 | // silently would change what the program does; picking a feature set |
| @@ -524,6 +524,20 @@ impl Lowerer { | |||
| 524 | } | 524 | } |
| 525 | } | 525 | } |
| 526 | 526 | ||
| 527 | + // Pass 0.5: associated consts and enum variant names. | ||
| 528 | + // | ||
| 529 | + // A struct field's type can be `[u8; T::MAX_SIZE]`, and field types | ||
| 530 | + // are mapped in pass 1 -- which is also when the `impl T` carrying | ||
| 531 | + // that const is collected. Whether the name resolved therefore | ||
| 532 | + // depended on which came first in the file. Registering the names on | ||
| 533 | + // their own, before anything is mapped, removes the ordering. | ||
| 534 | + for (m, f) in files { | ||
| 535 | + self.cur_mod = m.clone(); | ||
| 536 | + for item in &f.items { | ||
| 537 | + self.precollect(item); | ||
| 538 | + } | ||
| 539 | + } | ||
| 540 | + | ||
| 527 | // Pass 1: signatures and struct shapes, so that a call can be typed | 541 | // Pass 1: signatures and struct shapes, so that a call can be typed |
| 528 | // regardless of declaration order (Rust has no forward declarations). | 542 | // regardless of declaration order (Rust has no forward declarations). |
| 529 | for (m, f) in files { | 543 | for (m, f) in files { |
| @@ -711,6 +725,64 @@ impl Lowerer { | |||
| 711 | } | 725 | } |
| 712 | } | 726 | } |
| 713 | 727 | ||
| 728 | + /// Register the names an array length may refer to, before any type is | ||
| 729 | + /// mapped. Names only -- everything else waits for the ordinary passes. | ||
| 730 | + /// | ||
| 731 | + /// Failures are ignored rather than reported: this runs before the tables | ||
| 732 | + /// a type needs are filled, so a const whose own type is not yet mappable | ||
| 733 | + /// is simply left to the pass that can map it. | ||
| 734 | + fn precollect(&mut self, item: &Item) { | ||
| 735 | + match item { | ||
| 736 | + Item::Mod(m) => { | ||
| 737 | + if let Some((_, items)) = &m.content { | ||
| 738 | + for i in items { | ||
| 739 | + self.precollect(i); | ||
| 740 | + } | ||
| 741 | + } | ||
| 742 | + } | ||
| 743 | + Item::Const(c) => { | ||
| 744 | + if let Some(items) = derive_item_block(c) { | ||
| 745 | + for i in &items { | ||
| 746 | + self.precollect(i); | ||
| 747 | + } | ||
| 748 | + } | ||
| 749 | + } | ||
| 750 | + Item::Enum(e) => { | ||
| 751 | + // Only the variant names, and only for a C-like enum: those | ||
| 752 | + // are the ones that can appear in a length. | ||
| 753 | + if e.variants.iter().all(|v| v.fields.is_empty()) { | ||
| 754 | + let name = e.ident.to_string(); | ||
| 755 | + self.enums.entry(name.clone()).or_insert_with(|| EnumDef { | ||
| 756 | + name: name.clone(), | ||
| 757 | + simple: true, | ||
| 758 | + variants: e | ||
| 759 | + .variants | ||
| 760 | + .iter() | ||
| 761 | + .map(|v| Variant { | ||
| 762 | + name: v.ident.to_string(), | ||
| 763 | + discriminant: None, | ||
| 764 | + disc_expr: None, | ||
| 765 | + fields: Vec::new(), | ||
| 766 | + }) | ||
| 767 | + .collect(), | ||
| 768 | + }); | ||
| 769 | + } | ||
| 770 | + } | ||
| 771 | + Item::Impl(im) => { | ||
| 772 | + let Ok(self_ty) = self.map_ty(&im.self_ty) else { return }; | ||
| 773 | + let tyn = type_name(&self_ty); | ||
| 774 | + for it in &im.items { | ||
| 775 | + let syn::ImplItem::Const(c) = it else { continue }; | ||
| 776 | + let Ok(t) = self.map_ty(&c.ty) else { continue }; | ||
| 777 | + let n = ident(&format!("{}_{}", tyn, c.ident)); | ||
| 778 | + self.assoc_consts | ||
| 779 | + .insert((tyn.clone(), c.ident.to_string()), (n, t.owned())); | ||
| 780 | + } | ||
| 781 | + } | ||
| 782 | + _ => {} | ||
| 783 | + } | ||
| 784 | + } | ||
| 785 | + | ||
| 714 | fn collect(&mut self, item: &Item) -> Result<(), String> { | 786 | fn collect(&mut self, item: &Item) -> Result<(), String> { |
| 715 | // A `#[cfg(..)]` item exists only under some feature set. Dropping it | 787 | // A `#[cfg(..)]` item exists only under some feature set. Dropping it |
| 716 | // silently would change what the program does; picking a feature set | 788 | // silently would change what the program does; picking a feature set |