@@ -185,8 +185,7 @@ pub fn (flat &FlatAst) decode_fn_decl_signature(id FlatNodeId) FnDecl {
185185 recv_id := r.edge (n, 0 )
186186 typ_id := r.edge (n, 1 )
187187 attrs_id := r.edge (n, 2 )
188- typ_node := r.read_type (typ_id)
189- fn_typ := if typ_node is FnType { typ_node } else { FnType{} }
188+ fn_typ := r.read_fn_type (typ_id)
190189 return FnDecl{
191190 attributes: r.read_attr_list (attrs_id)
192191 is_public: (n.flags & flag_is_public) != 0
@@ -365,6 +364,77 @@ fn (r &FlatReader) read_parameter_list(id FlatNodeId) []Parameter {
365364 return out
366365}
367366
367+ // read_fn_type (s252) decodes a `.typ_fn` node straight into a FnType, without
368+ // the read_type→`Type(FnType{...})`→`is FnType` round-trip. Boxing a large
369+ // struct into the Type sum type and then unboxing it via smartcast corrupts the
370+ // FnType's slice headers (generic_params/params) on the arm64 self-host (the
371+ // documented chained-access/smartcast bug). This path is only ever exercised by
372+ // the flat decode, so the default self-host never hit it. Edge layout matches
373+ // the encoder's FnType arm and read_type's `.typ_fn`: 0=generics, 1=params,
374+ // 2=return_type.
375+ fn (r &FlatReader) read_fn_type (id FlatNodeId) FnType {
376+ if id < 0 {
377+ return FnType{}
378+ }
379+ n := r.node (id)
380+ if n.kind != .typ_fn {
381+ return FnType{}
382+ }
383+ return FnType{
384+ generic_params: r.read_expr_list (r.edge (n, 0 ))
385+ params: r.read_parameter_list (r.edge (n, 1 ))
386+ return_type: r.read_expr (r.edge (n, 2 ))
387+ }
388+ }
389+
390+ // read_ident (s254) decodes an expr_ident node straight into an Ident, avoiding
391+ // the read_expr→Expr→`is Ident` smartcast-unbox. Copying a struct out of the Expr
392+ // sum type via smartcast corrupts its fields (here the `name` string header) on
393+ // the arm64 self-host (same chained-access/smartcast bug as the FnType unbox in
394+ // s252). Returns an empty Ident for ids that don't point at an expr_ident.
395+ fn (r &FlatReader) read_ident (id FlatNodeId) Ident {
396+ if id < 0 || id > = r.flat.nodes.len {
397+ return Ident{}
398+ }
399+ n := r.node (id)
400+ if n.kind != .expr_ident {
401+ return Ident{}
402+ }
403+ return Ident{
404+ pos: n.pos
405+ name: r.get_str (n.name_id)
406+ }
407+ }
408+
409+ // read_assign_stmt (s254) decodes a stmt_assign node straight into an AssignStmt,
410+ // avoiding the read_stmt→Stmt→`is AssignStmt` unbox (which corrupts the lhs/rhs
411+ // slice headers on arm64). Mirrors the `.stmt_assign` arm of read_stmt. Returns
412+ // an empty AssignStmt for ids that don't point at a stmt_assign.
413+ fn (r &FlatReader) read_assign_stmt (id FlatNodeId) AssignStmt {
414+ if id < 0 || id > = r.flat.nodes.len {
415+ return AssignStmt{}
416+ }
417+ n := r.node (id)
418+ if n.kind != .stmt_assign {
419+ return AssignStmt{}
420+ }
421+ lhs_len := n.extra
422+ mut lhs := []Expr{cap: lhs_len}
423+ for i in 0 .. lhs_len {
424+ lhs << r.read_expr (r.edge (n, i))
425+ }
426+ mut rhs := []Expr{cap: n.edge_count - lhs_len}
427+ for i in lhs_len .. n.edge_count {
428+ rhs << r.read_expr (r.edge (n, i))
429+ }
430+ return AssignStmt{
431+ op: unsafe { token.Token (int (n.aux)) }
432+ lhs: lhs
433+ rhs: rhs
434+ pos: n.pos
435+ }
436+ }
437+
368438fn (r &FlatReader) read_string_list (id FlatNodeId) []string {
369439 if id < 0 {
370440 return []string {}
@@ -499,8 +569,7 @@ fn (r &FlatReader) read_stmt(id FlatNodeId) Stmt {
499569 typ_id := r.edge (n, 1 )
500570 attrs_id := r.edge (n, 2 )
501571 stmts_id := r.edge (n, 3 )
502- typ_node := r.read_type (typ_id)
503- fn_typ := if typ_node is FnType { typ_node } else { FnType{} }
572+ fn_typ := r.read_fn_type (typ_id)
504573 return Stmt (FnDecl{
505574 attributes: r.read_attr_list (attrs_id)
506575 is_public: (n.flags & flag_is_public) != 0
@@ -737,8 +806,7 @@ fn (r &FlatReader) read_expr(id FlatNodeId) Expr {
737806 }
738807 .expr_fn_literal {
739808 fn_typ_id := r.edge (n, 0 )
740- typ_node := r.read_type (fn_typ_id)
741- fn_typ := if typ_node is FnType { typ_node } else { FnType{} }
809+ fn_typ := r.read_fn_type (fn_typ_id)
742810 cap_len := n.extra
743811 mut captured := []Expr{cap: cap_len}
744812 for i in 0 .. cap_len {
@@ -795,10 +863,11 @@ fn (r &FlatReader) read_expr(id FlatNodeId) Expr {
795863 })
796864 }
797865 .expr_if_guard {
798- child := r.read_stmt (r.edge (n, 0 ))
799- assign := if child is AssignStmt { child } else { AssignStmt{} }
866+ // s254: read the assign straight into an AssignStmt; the old
867+ // `read_stmt → if child is AssignStmt { child }` unbox corrupted its
868+ // lhs/rhs slice headers on arm64.
800869 return Expr (IfGuardExpr{
801- stmt: assign
870+ stmt: r. read_assign_stmt (r. edge (n, 0 ))
802871 pos: n.pos
803872 })
804873 }
@@ -850,9 +919,12 @@ fn (r &FlatReader) read_expr(id FlatNodeId) Expr {
850919 expr := r.read_expr (r.edge (n, 0 ))
851920 mut args := []Ident{cap: n.edge_count - 1 }
852921 for i in 1 .. n.edge_count {
853- e := r.read_expr (r.edge (n, i))
854- if e is Ident {
855- args << e
922+ // s254: read each arg straight into an Ident (the old
923+ // `read_expr → if e is Ident { args << e }` unbox corrupted the
924+ // Ident's `name` on arm64).
925+ arg_id := r.edge (n, i)
926+ if arg_id > = 0 && arg_id < r.flat.nodes.len && r.node (arg_id).kind == .expr_ident {
927+ args << r.read_ident (arg_id)
856928 }
857929 }
858930 return Expr (LambdaExpr{
@@ -982,12 +1054,13 @@ fn (r &FlatReader) read_expr(id FlatNodeId) Expr {
9821054 })
9831055 }
9841056 .expr_selector {
985- lhs := r.read_expr (r.edge (n, 0 ))
986- rhs := r.read_expr (r.edge (n, 1 ))
987- ident := if rhs is Ident { rhs } else { Ident{} }
1057+ // s254: read the rhs straight into an Ident; the previous
1058+ // `read_expr → if rhs is Ident { rhs }` unbox corrupted the Ident's
1059+ // `name` on the arm64 self-host, yielding garbage `missing X.<name>`
1060+ // checker errors for selector types like `strings.Builder`.
9881061 return Expr (SelectorExpr{
989- lhs: lhs
990- rhs: ident
1062+ lhs: r. read_expr (r. edge (n, 0 ))
1063+ rhs: r. read_ident (r. edge (n, 1 ))
9911064 pos: n.pos
9921065 })
9931066 }
0 commit comments