@@ -1791,7 +1791,7 @@ pub const Serialized = extern struct {
17911791 }
17921792
17931793 /// Deserialize a ModuleEnv from the buffer, updating the ModuleEnv in place
1794- pub fn deserialize (
1794+ pub noinline fn deserialize (
17951795 self : * Serialized ,
17961796 offset : i64 ,
17971797 gpa : std.mem.Allocator ,
@@ -1803,47 +1803,66 @@ pub const Serialized = extern struct {
18031803 // On 32-bit platforms, Serialized may be larger due to using fixed-size types for platform-independent serialization.
18041804 comptime std .debug .assert (@sizeOf (@This ()) >= @sizeOf (Self ));
18051805
1806- // Overwrite ourself with the deserialized version, and return our pointer after casting it to Self.
1806+ // CRITICAL: We must deserialize ALL fields into local variables BEFORE writing to the
1807+ // output struct. This is because ModuleEnv is a regular struct (not extern), so Zig may
1808+ // reorder its fields differently than Serialized (which is extern). If we read from self
1809+ // while writing to env (which aliases self), we may read corrupted data in Release mode
1810+ // when field orderings differ.
1811+ //
1812+ // Following the same pattern as NodeStore.deserialize to avoid aliasing issues.
1813+
1814+ // Step 1: Deserialize all complex fields into local variables first
1815+ const deserialized_common = self .common .deserialize (offset , source ).* ;
1816+ const deserialized_types = self .types .deserialize (offset , gpa ).* ;
1817+ const deserialized_module_kind = self .module_kind .decode ();
1818+ const deserialized_all_defs = self .all_defs ;
1819+ const deserialized_all_statements = self .all_statements ;
1820+ const deserialized_exports = self .exports ;
1821+ const deserialized_builtin_statements = self .builtin_statements ;
1822+ const deserialized_external_decls = self .external_decls .deserialize (offset ).* ;
1823+ const deserialized_imports = (try self .imports .deserialize (offset , gpa )).* ;
1824+ const deserialized_diagnostics = self .diagnostics ;
1825+ const deserialized_store = self .store .deserialize (offset , gpa ).* ;
1826+ const deserialized_deferred_numeric_literals = self .deferred_numeric_literals .deserialize (offset ).* ;
1827+
1828+ // Step 2: Overwrite ourself with the deserialized version
18071829 const env = @as (* Self , @ptrFromInt (@intFromPtr (self )));
18081830
1809- // Deserialize common env first so we can look up identifiers
1810- const common = self .common .deserialize (offset , source ).* ;
1811-
18121831 env .* = Self {
18131832 .gpa = gpa ,
1814- .common = common ,
1815- .types = self . types . deserialize ( offset , gpa ) .* ,
1816- .module_kind = self . module_kind . decode () ,
1817- .all_defs = self . all_defs ,
1818- .all_statements = self . all_statements ,
1819- .exports = self . exports ,
1820- .builtin_statements = self . builtin_statements ,
1821- .external_decls = self . external_decls . deserialize ( offset ) .* ,
1822- .imports = ( try self . imports . deserialize ( offset , gpa )) .* ,
1833+ .common = deserialized_common ,
1834+ .types = deserialized_types ,
1835+ .module_kind = deserialized_module_kind ,
1836+ .all_defs = deserialized_all_defs ,
1837+ .all_statements = deserialized_all_statements ,
1838+ .exports = deserialized_exports ,
1839+ .builtin_statements = deserialized_builtin_statements ,
1840+ .external_decls = deserialized_external_decls ,
1841+ .imports = deserialized_imports ,
18231842 .module_name = module_name ,
18241843 .module_name_idx = undefined , // Not used for deserialized modules (only needed during fresh canonicalization)
1825- .diagnostics = self . diagnostics ,
1826- .store = self . store . deserialize ( offset , gpa ) .* ,
1844+ .diagnostics = deserialized_diagnostics ,
1845+ .store = deserialized_store ,
18271846 .evaluation_order = null , // Not serialized, will be recomputed if needed
18281847 // Well-known identifiers for type checking - look them up in the deserialized common env
1829- .try_ident = common .findIdent ("Try" ) orelse unreachable ,
1830- .out_of_range_ident = common .findIdent ("OutOfRange" ) orelse unreachable ,
1831- .builtin_module_ident = common .findIdent ("Builtin" ) orelse unreachable ,
1832- .plus_ident = common .findIdent (Ident .PLUS_METHOD_NAME ) orelse unreachable ,
1833- .minus_ident = common .findIdent ("minus" ) orelse unreachable ,
1834- .times_ident = common .findIdent ("times" ) orelse unreachable ,
1835- .div_by_ident = common .findIdent ("div_by" ) orelse unreachable ,
1836- .div_trunc_by_ident = common .findIdent ("div_trunc_by" ) orelse unreachable ,
1837- .rem_by_ident = common .findIdent ("rem_by" ) orelse unreachable ,
1838- .negate_ident = common .findIdent (Ident .NEGATE_METHOD_NAME ) orelse unreachable ,
1839- .not_ident = common .findIdent ("not" ) orelse unreachable ,
1840- .is_lt_ident = common .findIdent ("is_lt" ) orelse unreachable ,
1841- .is_lte_ident = common .findIdent ("is_lte" ) orelse unreachable ,
1842- .is_gt_ident = common .findIdent ("is_gt" ) orelse unreachable ,
1843- .is_gte_ident = common .findIdent ("is_gte" ) orelse unreachable ,
1844- .is_eq_ident = common .findIdent ("is_eq" ) orelse unreachable ,
1845- .is_ne_ident = common .findIdent ("is_ne" ) orelse unreachable ,
1846- .deferred_numeric_literals = self . deferred_numeric_literals . deserialize ( offset ) .* ,
1848+ .try_ident = deserialized_common .findIdent ("Try" ) orelse unreachable ,
1849+ .out_of_range_ident = deserialized_common .findIdent ("OutOfRange" ) orelse unreachable ,
1850+ .builtin_module_ident = deserialized_common .findIdent ("Builtin" ) orelse unreachable ,
1851+ .plus_ident = deserialized_common .findIdent (Ident .PLUS_METHOD_NAME ) orelse unreachable ,
1852+ .minus_ident = deserialized_common .findIdent ("minus" ) orelse unreachable ,
1853+ .times_ident = deserialized_common .findIdent ("times" ) orelse unreachable ,
1854+ .div_by_ident = deserialized_common .findIdent ("div_by" ) orelse unreachable ,
1855+ .div_trunc_by_ident = deserialized_common .findIdent ("div_trunc_by" ) orelse unreachable ,
1856+ .rem_by_ident = deserialized_common .findIdent ("rem_by" ) orelse unreachable ,
1857+ .negate_ident = deserialized_common .findIdent (Ident .NEGATE_METHOD_NAME ) orelse unreachable ,
1858+ .not_ident = deserialized_common .findIdent ("not" ) orelse unreachable ,
1859+ .is_lt_ident = deserialized_common .findIdent ("is_lt" ) orelse unreachable ,
1860+ .is_lte_ident = deserialized_common .findIdent ("is_lte" ) orelse unreachable ,
1861+ .is_gt_ident = deserialized_common .findIdent ("is_gt" ) orelse unreachable ,
1862+ .is_gte_ident = deserialized_common .findIdent ("is_gte" ) orelse unreachable ,
1863+ .is_eq_ident = deserialized_common .findIdent ("is_eq" ) orelse unreachable ,
1864+ .is_ne_ident = deserialized_common .findIdent ("is_ne" ) orelse unreachable ,
1865+ .deferred_numeric_literals = deserialized_deferred_numeric_literals ,
18471866 };
18481867
18491868 return env ;
0 commit comments