Resolve `Self` inside a struct's own declaration
`struct T { data: [u64; if Self::COMPRESS { 0 } else { Self::N * 2 }] }` --
`Self` there names the struct being declared, but `self_ty` was only ever set
inside an `impl`, so the field type could not be mapped.
Which in turn needed the constants declared before the object that uses them,
and `impl T` is written after `struct T`. They get their own pass ahead of the
type definitions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>8867e55 parent: ab0405b modified
src/lower.rs +81 -19 | @@ -568,6 +568,17 @@ impl Lowerer { | ||
| 568 | 568 | self.resolve_discriminants()?; |
| 569 | 569 | self.resolve_opaque()?; |
| 570 | 570 | |
| 571 | + // Pass 1.9: associated constants, which a type definition may mention. | |
| 572 | + // `struct T { data: [u64; Self::N] }` is the case -- the constant has | |
| 573 | + // to be declared before the object that uses it, and `impl T` is | |
| 574 | + // written after `struct T`. | |
| 575 | + for (m, f) in files { | |
| 576 | + self.cur_mod = m.clone(); | |
| 577 | + for item in &f.items { | |
| 578 | + self.emit_assoc_consts(item)?; | |
| 579 | + } | |
| 580 | + } | |
| 581 | + | |
| 571 | 582 | // Pass 2: type definitions, which every signature may mention. |
| 572 | 583 | for (m, f) in files { |
| 573 | 584 | self.cur_mod = m.clone(); |
| @@ -790,6 +801,28 @@ impl Lowerer { | ||
| 790 | 801 | } |
| 791 | 802 | } |
| 792 | 803 | |
| 804 | + fn collect_struct(&mut self, s: &syn::ItemStruct) -> Result<(), String> { | |
| 805 | + let g = Self::generics_of(&s.generics); | |
| 806 | + // The parameters must be in scope while the field types are mapped, | |
| 807 | + // so that `T` resolves to itself rather than to an unknown named type. | |
| 808 | + self.type_generics.insert(s.ident.to_string(), g); | |
| 809 | + let mut fields = Vec::new(); | |
| 810 | + for (i, f) in s.fields.iter().enumerate() { | |
| 811 | + let name = match &f.ident { | |
| 812 | + Some(id) => id.to_string(), | |
| 813 | + None => format!("f{i}"), // tuple struct | |
| 814 | + }; | |
| 815 | + // A field of `&[T]` / `&str` type is a borrow, and Nim's view | |
| 816 | + // types allow it as an object field, so it stays a view rather | |
| 817 | + // than being copied into a `seq`. | |
| 818 | + let t = self.map_ty(&f.ty)?; | |
| 819 | + let t = if returns_borrow(&f.ty) { t.unvar() } else { t.owned() }; | |
| 820 | + fields.push((name, t)); | |
| 821 | + } | |
| 822 | + self.structs.insert(s.ident.to_string(), fields); | |
| 823 | + Ok(()) | |
| 824 | + } | |
| 825 | + | |
| 793 | 826 | fn collect(&mut self, item: &Item) -> Result<(), String> { |
| 794 | 827 | // A `#[cfg(..)]` item exists only under some feature set. Dropping it |
| 795 | 828 | // silently would change what the program does; picking a feature set |
| @@ -830,25 +863,15 @@ impl Lowerer { | ||
| 830 | 863 | } |
| 831 | 864 | Item::Struct(s) => { |
| 832 | 865 | self.note_const_generics(&s.generics)?; |
| 833 | - let g = Self::generics_of(&s.generics); | |
| 834 | - // The parameters must be in scope while the field types are | |
| 835 | - // mapped, so that `T` resolves to itself rather than to an | |
| 836 | - // unknown named type. | |
| 837 | - self.type_generics.insert(s.ident.to_string(), g); | |
| 838 | - let mut fields = Vec::new(); | |
| 839 | - for (i, f) in s.fields.iter().enumerate() { | |
| 840 | - let name = match &f.ident { | |
| 841 | - Some(id) => id.to_string(), | |
| 842 | - None => format!("f{i}"), // tuple struct | |
| 843 | - }; | |
| 844 | - // A field of `&[T]` / `&str` type is a borrow, and Nim's | |
| 845 | - // view types allow it as an object field, so it stays a | |
| 846 | - // view rather than being copied into a `seq`. | |
| 847 | - let t = self.map_ty(&f.ty)?; | |
| 848 | - let t = if returns_borrow(&f.ty) { t.unvar() } else { t.owned() }; | |
| 849 | - fields.push((name, t)); | |
| 850 | - } | |
| 851 | - self.structs.insert(s.ident.to_string(), fields); | |
| 866 | + // `Self` inside a struct's own declaration names that struct, | |
| 867 | + // which is how `[u64; if Self::COMPRESS { .. }]` reads as a | |
| 868 | + // field type. | |
| 869 | + let outer_self = self | |
| 870 | + .self_ty | |
| 871 | + .replace(Nim::Named(s.ident.to_string(), Vec::new())); | |
| 872 | + let r = self.collect_struct(s); | |
| 873 | + self.self_ty = outer_self; | |
| 874 | + return r; | |
| 852 | 875 | } |
| 853 | 876 | Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => { |
| 854 | 877 | self.collect_bitflags(&m.mac)?; |
| @@ -2074,6 +2097,45 @@ impl Lowerer { | ||
| 2074 | 2097 | // --------------------------------------------------------------- items |
| 2075 | 2098 | |
| 2076 | 2099 | /// Emit the type definitions only: they must precede every signature. |
| 2100 | + /// Write out every associated constant, ahead of the types. | |
| 2101 | + fn emit_assoc_consts(&mut self, item: &Item) -> Result<(), String> { | |
| 2102 | + if !self.cfg_keeps(item_attrs(item))? { | |
| 2103 | + return Ok(()); | |
| 2104 | + } | |
| 2105 | + match item { | |
| 2106 | + Item::Impl(im) => { | |
| 2107 | + let self_ty = self.map_ty(&im.self_ty)?; | |
| 2108 | + let outer = self.self_ty.replace(self_ty.clone()); | |
| 2109 | + let tyname = type_name(&self_ty); | |
| 2110 | + let mut r = Ok(()); | |
| 2111 | + for it in &im.items { | |
| 2112 | + if let syn::ImplItem::Const(c) = it { | |
| 2113 | + r = r.and(self.assoc_const(&tyname, c)); | |
| 2114 | + } | |
| 2115 | + } | |
| 2116 | + self.self_ty = outer; | |
| 2117 | + r | |
| 2118 | + } | |
| 2119 | + Item::Mod(m) => { | |
| 2120 | + if let Some((_, items)) = &m.content { | |
| 2121 | + for i in items { | |
| 2122 | + self.emit_assoc_consts(i)?; | |
| 2123 | + } | |
| 2124 | + } | |
| 2125 | + Ok(()) | |
| 2126 | + } | |
| 2127 | + Item::Const(c) => { | |
| 2128 | + if let Some(items) = derive_item_block(c) { | |
| 2129 | + for i in &items { | |
| 2130 | + self.emit_assoc_consts(i)?; | |
| 2131 | + } | |
| 2132 | + } | |
| 2133 | + Ok(()) | |
| 2134 | + } | |
| 2135 | + _ => Ok(()), | |
| 2136 | + } | |
| 2137 | + } | |
| 2138 | + | |
| 2077 | 2139 | fn item_types(&mut self, item: &Item) -> Result<(), String> { |
| 2078 | 2140 | if !self.cfg_keeps(item_attrs(item))? { |
| 2079 | 2141 | return Ok(()); |
| @@ -568,6 +568,17 @@ impl Lowerer { | |||
| 568 | self.resolve_discriminants()?; | 568 | self.resolve_discriminants()?; |
| 569 | self.resolve_opaque()?; | 569 | self.resolve_opaque()?; |
| 570 | 570 | ||
| 571 | + // Pass 1.9: associated constants, which a type definition may mention. | ||
| 572 | + // `struct T { data: [u64; Self::N] }` is the case -- the constant has | ||
| 573 | + // to be declared before the object that uses it, and `impl T` is | ||
| 574 | + // written after `struct T`. | ||
| 575 | + for (m, f) in files { | ||
| 576 | + self.cur_mod = m.clone(); | ||
| 577 | + for item in &f.items { | ||
| 578 | + self.emit_assoc_consts(item)?; | ||
| 579 | + } | ||
| 580 | + } | ||
| 581 | + | ||
| 571 | // Pass 2: type definitions, which every signature may mention. | 582 | // Pass 2: type definitions, which every signature may mention. |
| 572 | for (m, f) in files { | 583 | for (m, f) in files { |
| 573 | self.cur_mod = m.clone(); | 584 | self.cur_mod = m.clone(); |
| @@ -790,6 +801,28 @@ impl Lowerer { | |||
| 790 | } | 801 | } |
| 791 | } | 802 | } |
| 792 | 803 | ||
| 804 | + fn collect_struct(&mut self, s: &syn::ItemStruct) -> Result<(), String> { | ||
| 805 | + let g = Self::generics_of(&s.generics); | ||
| 806 | + // The parameters must be in scope while the field types are mapped, | ||
| 807 | + // so that `T` resolves to itself rather than to an unknown named type. | ||
| 808 | + self.type_generics.insert(s.ident.to_string(), g); | ||
| 809 | + let mut fields = Vec::new(); | ||
| 810 | + for (i, f) in s.fields.iter().enumerate() { | ||
| 811 | + let name = match &f.ident { | ||
| 812 | + Some(id) => id.to_string(), | ||
| 813 | + None => format!("f{i}"), // tuple struct | ||
| 814 | + }; | ||
| 815 | + // A field of `&[T]` / `&str` type is a borrow, and Nim's view | ||
| 816 | + // types allow it as an object field, so it stays a view rather | ||
| 817 | + // than being copied into a `seq`. | ||
| 818 | + let t = self.map_ty(&f.ty)?; | ||
| 819 | + let t = if returns_borrow(&f.ty) { t.unvar() } else { t.owned() }; | ||
| 820 | + fields.push((name, t)); | ||
| 821 | + } | ||
| 822 | + self.structs.insert(s.ident.to_string(), fields); | ||
| 823 | + Ok(()) | ||
| 824 | + } | ||
| 825 | + | ||
| 793 | fn collect(&mut self, item: &Item) -> Result<(), String> { | 826 | fn collect(&mut self, item: &Item) -> Result<(), String> { |
| 794 | // A `#[cfg(..)]` item exists only under some feature set. Dropping it | 827 | // A `#[cfg(..)]` item exists only under some feature set. Dropping it |
| 795 | // silently would change what the program does; picking a feature set | 828 | // silently would change what the program does; picking a feature set |
| @@ -830,25 +863,15 @@ impl Lowerer { | |||
| 830 | } | 863 | } |
| 831 | Item::Struct(s) => { | 864 | Item::Struct(s) => { |
| 832 | self.note_const_generics(&s.generics)?; | 865 | self.note_const_generics(&s.generics)?; |
| 833 | - let g = Self::generics_of(&s.generics); | 866 | + // `Self` inside a struct's own declaration names that struct, |
| 834 | - // The parameters must be in scope while the field types are | 867 | + // which is how `[u64; if Self::COMPRESS { .. }]` reads as a |
| 835 | - // mapped, so that `T` resolves to itself rather than to an | 868 | + // field type. |
| 836 | - // unknown named type. | 869 | + let outer_self = self |
| 837 | - self.type_generics.insert(s.ident.to_string(), g); | 870 | + .self_ty |
| 838 | - let mut fields = Vec::new(); | 871 | + .replace(Nim::Named(s.ident.to_string(), Vec::new())); |
| 839 | - for (i, f) in s.fields.iter().enumerate() { | 872 | + let r = self.collect_struct(s); |
| 840 | - let name = match &f.ident { | 873 | + self.self_ty = outer_self; |
| 841 | - Some(id) => id.to_string(), | 874 | + return r; |
| 842 | - None => format!("f{i}"), // tuple struct | ||
| 843 | - }; | ||
| 844 | - // A field of `&[T]` / `&str` type is a borrow, and Nim's | ||
| 845 | - // view types allow it as an object field, so it stays a | ||
| 846 | - // view rather than being copied into a `seq`. | ||
| 847 | - let t = self.map_ty(&f.ty)?; | ||
| 848 | - let t = if returns_borrow(&f.ty) { t.unvar() } else { t.owned() }; | ||
| 849 | - fields.push((name, t)); | ||
| 850 | - } | ||
| 851 | - self.structs.insert(s.ident.to_string(), fields); | ||
| 852 | } | 875 | } |
| 853 | Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => { | 876 | Item::Macro(m) if path_name(&m.mac.path) == "bitflags" => { |
| 854 | self.collect_bitflags(&m.mac)?; | 877 | self.collect_bitflags(&m.mac)?; |
| @@ -2074,6 +2097,45 @@ impl Lowerer { | |||
| 2074 | // --------------------------------------------------------------- items | 2097 | // --------------------------------------------------------------- items |
| 2075 | 2098 | ||
| 2076 | /// Emit the type definitions only: they must precede every signature. | 2099 | /// Emit the type definitions only: they must precede every signature. |
| 2100 | + /// Write out every associated constant, ahead of the types. | ||
| 2101 | + fn emit_assoc_consts(&mut self, item: &Item) -> Result<(), String> { | ||
| 2102 | + if !self.cfg_keeps(item_attrs(item))? { | ||
| 2103 | + return Ok(()); | ||
| 2104 | + } | ||
| 2105 | + match item { | ||
| 2106 | + Item::Impl(im) => { | ||
| 2107 | + let self_ty = self.map_ty(&im.self_ty)?; | ||
| 2108 | + let outer = self.self_ty.replace(self_ty.clone()); | ||
| 2109 | + let tyname = type_name(&self_ty); | ||
| 2110 | + let mut r = Ok(()); | ||
| 2111 | + for it in &im.items { | ||
| 2112 | + if let syn::ImplItem::Const(c) = it { | ||
| 2113 | + r = r.and(self.assoc_const(&tyname, c)); | ||
| 2114 | + } | ||
| 2115 | + } | ||
| 2116 | + self.self_ty = outer; | ||
| 2117 | + r | ||
| 2118 | + } | ||
| 2119 | + Item::Mod(m) => { | ||
| 2120 | + if let Some((_, items)) = &m.content { | ||
| 2121 | + for i in items { | ||
| 2122 | + self.emit_assoc_consts(i)?; | ||
| 2123 | + } | ||
| 2124 | + } | ||
| 2125 | + Ok(()) | ||
| 2126 | + } | ||
| 2127 | + Item::Const(c) => { | ||
| 2128 | + if let Some(items) = derive_item_block(c) { | ||
| 2129 | + for i in &items { | ||
| 2130 | + self.emit_assoc_consts(i)?; | ||
| 2131 | + } | ||
| 2132 | + } | ||
| 2133 | + Ok(()) | ||
| 2134 | + } | ||
| 2135 | + _ => Ok(()), | ||
| 2136 | + } | ||
| 2137 | + } | ||
| 2138 | + | ||
| 2077 | fn item_types(&mut self, item: &Item) -> Result<(), String> { | 2139 | fn item_types(&mut self, item: &Item) -> Result<(), String> { |
| 2078 | if !self.cfg_keeps(item_attrs(item))? { | 2140 | if !self.cfg_keeps(item_attrs(item))? { |
| 2079 | return Ok(()); | 2141 | return Ok(()); |
modified
tests/cases/052-const-array-len.rs +13 -0 | @@ -29,6 +29,16 @@ impl Oid { | ||
| 29 | 29 | } |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | +// `Self` inside a struct's own declaration names that struct. | |
| 33 | +struct Table { | |
| 34 | + data: [u8; if Table::WIDE { 8 } else { Table::N * 2 }], | |
| 35 | +} | |
| 36 | + | |
| 37 | +impl Table { | |
| 38 | + const WIDE: bool = false; | |
| 39 | + const N: usize = 3; | |
| 40 | +} | |
| 41 | + | |
| 32 | 42 | struct Buf { |
| 33 | 43 | data: [u8; BLOCK], |
| 34 | 44 | wide: [u8; DOUBLE], |
| @@ -62,6 +72,9 @@ fn main() { | ||
| 62 | 72 | |
| 63 | 73 | // An associated const as a length, and in the braces rustc's expanded |
| 64 | 74 | // output writes it with. |
| 75 | + let tb = Table { data: [1u8; if Table::WIDE { 8 } else { Table::N * 2 }] }; | |
| 76 | + println!("{} {}", tb.data.len(), sum(&tb.data)); | |
| 77 | + | |
| 65 | 78 | let t2 = Oid::table(); |
| 66 | 79 | println!("{} {}", t2.len(), sum(&t2)); |
| 67 | 80 | |
| @@ -29,6 +29,16 @@ impl Oid { | |||
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | +// `Self` inside a struct's own declaration names that struct. | ||
| 33 | +struct Table { | ||
| 34 | + data: [u8; if Table::WIDE { 8 } else { Table::N * 2 }], | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +impl Table { | ||
| 38 | + const WIDE: bool = false; | ||
| 39 | + const N: usize = 3; | ||
| 40 | +} | ||
| 41 | + | ||
| 32 | struct Buf { | 42 | struct Buf { |
| 33 | data: [u8; BLOCK], | 43 | data: [u8; BLOCK], |
| 34 | wide: [u8; DOUBLE], | 44 | wide: [u8; DOUBLE], |
| @@ -62,6 +72,9 @@ fn main() { | |||
| 62 | 72 | ||
| 63 | // An associated const as a length, and in the braces rustc's expanded | 73 | // An associated const as a length, and in the braces rustc's expanded |
| 64 | // output writes it with. | 74 | // output writes it with. |
| 75 | + let tb = Table { data: [1u8; if Table::WIDE { 8 } else { Table::N * 2 }] }; | ||
| 76 | + println!("{} {}", tb.data.len(), sum(&tb.data)); | ||
| 77 | + | ||
| 65 | let t2 = Oid::table(); | 78 | let t2 = Oid::table(); |
| 66 | println!("{} {}", t2.len(), sum(&t2)); | 79 | println!("{} {}", t2.len(), sum(&t2)); |
| 67 | 80 | ||