// ast.bux — AST node types (Expr, Stmt, Decl, Pattern, TypeExpr) module Ast { // --------------------------------------------------------------------------- // SourceLocation (inline for convenience) // --------------------------------------------------------------------------- struct SourceLoc { line: uint32, column: uint32, } // --------------------------------------------------------------------------- // Token (lightweight inline) // --------------------------------------------------------------------------- struct AstToken { kind: int, text: String, line: uint32, column: uint32, } // --------------------------------------------------------------------------- // TypeExpr — type expressions // --------------------------------------------------------------------------- const tekNamed: int = 0; const tekPath: int = 1; const tekSlice: int = 2; const tekPointer: int = 3; const tekRef: int = 7; // &T — shared reference const tekMutRef: int = 8; // &mut T — mutable reference const tekTuple: int = 4; const tekSelf: int = 5; const tekFunc: int = 6; struct TypeExprList { te: *TypeExpr, next: *TypeExprList, } struct TypeExpr { kind: int, line: uint32, column: uint32, typeName: String, // for tekNamed / diagnostic name for tekFunc pathStr: String, // for tekPath (segments joined with ::) pathCount: int, // number of path segments typeArgName0: String, // up to 2 type args typeArgName1: String, typeArgCount: int, sliceElement: *TypeExpr, // for tekSlice pointerPointee: *TypeExpr, // for tekPointer / tekRef / tekMutRef refLifetime: String, // for tekRef / tekMutRef: "'a" or "" (elided) funcParams: *TypeExprList, // for tekFunc funcRet: *TypeExpr, // for tekFunc funcParamCount: int, // for tekFunc tupleElems: *TypeExprList, // for tekTuple tupleCount: int, // for tekTuple } // --------------------------------------------------------------------------- // Pattern — match patterns // --------------------------------------------------------------------------- const pkWildcard: int = 0; const pkLiteral: int = 1; const pkIdent: int = 2; const pkRange: int = 3; const pkEnum: int = 4; const pkStruct: int = 5; const pkTuple: int = 6; const pkGuarded: int = 7; // `p if cond` — patChild1 = inner, patGuardExpr = condition struct Pattern { kind: int, line: uint32, column: uint32, patIdent: String, // for pkIdent patLitKind: int, // for pkLiteral (token kind) patLitText: String, // for pkLiteral (token text) patRangeInclusive: bool, // for pkRange patEnumPath: String, // for pkEnum: "Enum::Variant" patStructName: String, // for pkStruct (type name) patFieldName: String, // for struct field entry: field name in Point { x: a } patChild1: *Pattern, // range lo / nested / guarded inner patChild2: *Pattern, // range hi / nested patArgs: *Pattern, // pkEnum/pkTuple/pkStruct field list (head) patNext: *Pattern, // next sibling in patArgs list patGuardExpr: *Expr, // for pkGuarded: the `if` condition } // Match arm: pattern => body struct MatchArm { line: uint32, column: uint32, pattern: *Pattern, body: *Expr, next: *MatchArm, } // --------------------------------------------------------------------------- // Expr — expressions (tagged union) // --------------------------------------------------------------------------- const ekLiteral: int = 0; const ekIdent: int = 1; const ekSelf: int = 2; const ekPath: int = 3; const ekSizeOf: int = 4; const ekUnary: int = 5; const ekPostfix: int = 6; const ekBinary: int = 7; const ekAssign: int = 8; const ekTernary: int = 9; const ekRange: int = 10; const ekCall: int = 11; const ekGenericCall: int = 12; const ekIndex: int = 13; const ekField: int = 14; const ekStructInit: int = 15; const ekSlice: int = 16; const ekTuple: int = 17; const ekCast: int = 18; const ekIs: int = 19; const ekTry: int = 20; const ekUnwrap: int = 23; const ekBlock: int = 21; const ekMatch: int = 22; const ekSpawn: int = 24; const ekAwait: int = 25; const ekStringInterp: int = 26; const ekClosure: int = 27; struct ExprList { expr: *Expr, next: *ExprList, argName: String, } struct Expr { kind: int, line: uint32, column: uint32, // Source file for multi-file #line / future macros (empty = inherit Decl) sourceFile: String, // Common fields strValue: String, // ident name, path segments, field name, callee intValue: int, // operator kind, intrinsic kind boolValue: bool, // range inclusive tokKind: int, // literal token kind tokText: String, // literal token text // Children (up to 3 sub-expressions) child1: *Expr, // left, operand, callee, cond, subject child2: *Expr, // right, index, then, value child3: *Expr, // else, third // Extra references refType: *TypeExpr, // cast type, is type, sizeof type refBlock: *Block, // for ekBlock // Generic call genericCallee: String, genericTypeArg0: String, genericTypeArg1: String, genericTypeArgCount: int, // Struct init fields structName: String, structFieldCount: int, // Closure params (for ekClosure) closureParams: *Decl, // Captures (for ekClosure) captureCount: int, captureName0: String, captureName1: String, captureName2: String, captureName3: String, captureName4: String, captureName5: String, captureName6: String, captureName7: String, captureType0: int, captureType1: int, captureType2: int, captureType3: int, captureType4: int, captureType5: int, captureType6: int, captureType7: int, // Call arguments (linked list for multi-arg support) callArgs: *ExprList, callArgCount: int, // Match arms (for ekMatch) matchArms: *MatchArm, matchArmCount: int, } // --------------------------------------------------------------------------- // Block — sequence of statements // --------------------------------------------------------------------------- struct Block { line: uint32, column: uint32, sourceFile: String, // multi-file #line (empty = inherit Decl) stmtCount: int, firstStmt: *Stmt, lastStmt: *Stmt, } // --------------------------------------------------------------------------- // Stmt — statements // --------------------------------------------------------------------------- const skExpr: int = 0; const skLet: int = 1; const skIf: int = 2; const skWhile: int = 3; const skDoWhile: int = 4; const skLoop: int = 5; const skFor: int = 6; const skMatch: int = 7; const skReturn: int = 8; const skBreak: int = 9; const skContinue: int = 10; const skDecl: int = 11; const skDefer: int = 12; const skSwitch: int = 13; struct ElseIf { line: uint32; column: uint32; cond: *Expr; block: *Block; } struct Stmt { kind: int, line: uint32, column: uint32, // Source file for multi-file #line / future macros (empty = inherit Decl) sourceFile: String, // Common fields strValue: String, // let name, pattern ident, label, for var boolValue: bool, // let mutable // Children child1: *Expr, // init expr, condition, iter expr, return value child2: *Expr, // match subject child3: *Expr, // extra refStmtType: *TypeExpr, // let type annotation refStmtPattern: *Pattern,// let pattern refStmtDecl: *Decl, // for skDecl refStmtBlock: *Block, // then/body block refStmtElse: *Block, // else block // Else-if chain elseIfCount: int, // Linked list nextStmt: *Stmt, } // --------------------------------------------------------------------------- // Decl — declarations // --------------------------------------------------------------------------- const dkFunc: int = 0; const dkStruct: int = 1; const dkEnum: int = 2; const dkUnion: int = 3; const dkInterface: int = 4; const dkImpl: int = 5; const dkModule: int = 6; const dkUse: int = 7; const dkConst: int = 8; const dkTypeAlias: int = 9; const dkExternFunc: int = 10; const dkExternVar: int = 11; struct Param { line: uint32; column: uint32; name: String; refParamType: *TypeExpr; isVariadic: bool; defaultExpr: *Expr; } struct StructField { line: uint32; column: uint32; isPublic: bool; name: String; refFieldType: *TypeExpr; } struct EnumVariant { line: uint32; column: uint32; name: String; fieldCount: int; fieldTypeName0: String; fieldTypeName1: String; } struct Decl { fieldCount: int, fields: *StructField, kind: int, line: uint32, column: uint32, isPublic: bool, isAsync: bool, isChecked: int, // @[Checked] attribute (0/1) isDrop: int, // @[Drop] attribute (0/1) isRelease: int, // @[Release] attribute (0/1) isConst: int, // const func (0/1) // Source file path for #line / diagnostics (multi-file projects) sourceFile: String, // Names strValue: String, // decl name strValue2: String, // interface name, dll name, module path // Type params (up to 2) typeParam0: String, typeParam1: String, typeParamCount: int, // Trait bounds for type params (e.g. ) typeParam0Bound: String, typeParam1Bound: String, // Params (for functions) paramCount: int, param0: Param, param1: Param, param2: Param, param3: Param, param4: Param, param5: Param, param6: Param, param7: Param, param8: Param, retType: *TypeExpr, // Body refBody: *Block, // Enum variants (up to 8) variantCount: int, variant0: EnumVariant, variant1: EnumVariant, variant2: EnumVariant, variant3: EnumVariant, variant4: EnumVariant, variant5: EnumVariant, variant6: EnumVariant, variant7: EnumVariant, variant8: EnumVariant, // Impl methods (up to 4) methodCount: int, // Use/import useKind: int, usePath: String, useNames: String, // joined names for multi-import // Const constType: *TypeExpr, constValue: *Expr, // Type alias aliasType: *TypeExpr, // Extern func extFuncDll: String, extFuncVariadic: bool, extFuncRetType: *TypeExpr, // Children childDecl1: *Decl, // linked list of decls (for module items, impl methods) childDecl2: *Decl, // Struct fields (up to 256) } // --------------------------------------------------------------------------- // Module — AST root // --------------------------------------------------------------------------- struct Module { name: String, path: String, // path segments joined itemCount: int, firstItem: *Decl, } // --------------------------------------------------------------------------- // Constructor helpers // --------------------------------------------------------------------------- func Ast_MakeExpr(kind: int, line: uint32, col: uint32) -> Expr { return Expr { kind: kind, line: line, column: col, sourceFile: "", strValue: "", intValue: 0, boolValue: false, tokKind: 0, tokText: "", child1: null as *Expr, child2: null as *Expr, child3: null as *Expr, refType: null as *TypeExpr, refBlock: null as *Block, genericCallee: "", genericTypeArg0: "", genericTypeArg1: "", genericTypeArgCount: 0, structName: "", structFieldCount: 0, callArgs: null as *ExprList, callArgCount: 0 }; } func Ast_MakeIdent(name: String, line: uint32, col: uint32) -> Expr { var e: Expr = Ast_MakeExpr(ekIdent, line, col); e.strValue = name; return e; } func Ast_MakeLiteral(tokKind: int, text: String, line: uint32, col: uint32) -> Expr { var e: Expr = Ast_MakeExpr(ekLiteral, line, col); e.tokKind = tokKind; e.tokText = text; return e; } func Ast_MakeBinary(op: int, left: *Expr, right: *Expr, line: uint32, col: uint32) -> Expr { var e: Expr = Ast_MakeExpr(ekBinary, line, col); e.intValue = op; e.child1 = left; e.child2 = right; return e; } func Ast_MakeCall(callee: *Expr, line: uint32, col: uint32) -> Expr { var e: Expr = Ast_MakeExpr(ekCall, line, col); e.child1 = callee; return e; } func Ast_MakeStmt(kind: int, line: uint32, col: uint32) -> Stmt { return Stmt { kind: kind, line: line, column: col, sourceFile: "", strValue: "", boolValue: false, child1: null as *Expr, child2: null as *Expr, child3: null as *Expr, refStmtType: null as *TypeExpr, refStmtPattern: null as *Pattern, refStmtDecl: null as *Decl, refStmtBlock: null as *Block, refStmtElse: null as *Block, elseIfCount: 0 }; } /// Stamp sourceFile on an expression tree (empty slots only — keeps grafted macro nodes). func Ast_StampExprFile(e: *Expr, path: String) { if e == null as *Expr { return; } if path == null as String || String_Eq(path, "") { return; } if e.sourceFile == null as String || String_Eq(e.sourceFile, "") { e.sourceFile = path; } Ast_StampExprFile(e.child1, path); Ast_StampExprFile(e.child2, path); Ast_StampExprFile(e.child3, path); if e.refBlock != null as *Block { Ast_StampBlockFile(e.refBlock, path); } var args: *ExprList = e.callArgs; while args != null as *ExprList { Ast_StampExprFile(args.expr, path); args = args.next; } // Match arms (guard lives on Pattern as patGuardExpr when pkGuarded) var arm: *MatchArm = e.matchArms; while arm != null as *MatchArm { Ast_StampExprFile(arm.body, path); if arm.pattern != null as *Pattern { Ast_StampPatternFile(arm.pattern, path); } arm = arm.next; } // Closure default param exprs if e.closureParams != null as *Decl { var pi: int = 0; while pi < e.closureParams.paramCount { var p: *Param = null as *Param; if pi == 0 { p = &e.closureParams.param0; } else if pi == 1 { p = &e.closureParams.param1; } else if pi == 2 { p = &e.closureParams.param2; } else if pi == 3 { p = &e.closureParams.param3; } else if pi == 4 { p = &e.closureParams.param4; } else if pi == 5 { p = &e.closureParams.param5; } else if pi == 6 { p = &e.closureParams.param6; } else if pi == 7 { p = &e.closureParams.param7; } else if pi == 8 { p = &e.closureParams.param8; } if p != null as *Param && p.defaultExpr != null as *Expr { Ast_StampExprFile(p.defaultExpr, path); } pi = pi + 1; } } } func Ast_StampPatternFile(pat: *Pattern, path: String) { if pat == null as *Pattern { return; } if path == null as String || String_Eq(path, "") { return; } if pat.patGuardExpr != null as *Expr { Ast_StampExprFile(pat.patGuardExpr, path); } Ast_StampPatternFile(pat.patChild1, path); Ast_StampPatternFile(pat.patChild2, path); Ast_StampPatternFile(pat.patArgs, path); Ast_StampPatternFile(pat.patNext, path); } func Ast_StampBlockFile(b: *Block, path: String) { if b == null as *Block { return; } if path == null as String || String_Eq(path, "") { return; } if b.sourceFile == null as String || String_Eq(b.sourceFile, "") { b.sourceFile = path; } var s: *Stmt = b.firstStmt; while s != null as *Stmt { Ast_StampStmtFile(s, path); s = s.nextStmt; } } func Ast_StampStmtFile(s: *Stmt, path: String) { if s == null as *Stmt { return; } if path == null as String || String_Eq(path, "") { return; } if s.sourceFile == null as String || String_Eq(s.sourceFile, "") { s.sourceFile = path; } Ast_StampExprFile(s.child1, path); Ast_StampExprFile(s.child2, path); Ast_StampExprFile(s.child3, path); if s.refStmtBlock != null as *Block { Ast_StampBlockFile(s.refStmtBlock, path); } if s.refStmtElse != null as *Block { Ast_StampBlockFile(s.refStmtElse, path); } if s.refStmtDecl != null as *Decl { // nested decl inside stmt — body stamp via Decl stamp discard; } } // --------------------------------------------------------------------------- // Macro / quote hygiene — graft sourceFile onto AST trees // --------------------------------------------------------------------------- // Stamp* = fill empty only (merge file paths; keep pre-set macro grafts) // Graft* = force overwrite (call-site attribution after expansion) // Clone* = deep copy for templates // Quote* = clone + hygiene policy (def-site keep file | call-site graft) /// Force-set sourceFile on an expression tree (overwrites existing). func Ast_GraftExprFile(e: *Expr, path: String) { if e == null as *Expr { return; } if path == null as String || String_Eq(path, "") { return; } e.sourceFile = path; Ast_GraftExprFile(e.child1, path); Ast_GraftExprFile(e.child2, path); Ast_GraftExprFile(e.child3, path); if e.refBlock != null as *Block { Ast_GraftBlockFile(e.refBlock, path); } var args: *ExprList = e.callArgs; while args != null as *ExprList { Ast_GraftExprFile(args.expr, path); args = args.next; } var arm: *MatchArm = e.matchArms; while arm != null as *MatchArm { Ast_GraftExprFile(arm.body, path); if arm.pattern != null as *Pattern { Ast_GraftPatternFile(arm.pattern, path); } arm = arm.next; } } func Ast_GraftPatternFile(pat: *Pattern, path: String) { if pat == null as *Pattern { return; } if path == null as String || String_Eq(path, "") { return; } if pat.patGuardExpr != null as *Expr { Ast_GraftExprFile(pat.patGuardExpr, path); } Ast_GraftPatternFile(pat.patChild1, path); Ast_GraftPatternFile(pat.patChild2, path); Ast_GraftPatternFile(pat.patArgs, path); Ast_GraftPatternFile(pat.patNext, path); } func Ast_GraftBlockFile(b: *Block, path: String) { if b == null as *Block { return; } if path == null as String || String_Eq(path, "") { return; } b.sourceFile = path; var s: *Stmt = b.firstStmt; while s != null as *Stmt { Ast_GraftStmtFile(s, path); s = s.nextStmt; } } func Ast_GraftStmtFile(s: *Stmt, path: String) { if s == null as *Stmt { return; } if path == null as String || String_Eq(path, "") { return; } s.sourceFile = path; Ast_GraftExprFile(s.child1, path); Ast_GraftExprFile(s.child2, path); Ast_GraftExprFile(s.child3, path); if s.refStmtBlock != null as *Block { Ast_GraftBlockFile(s.refStmtBlock, path); } if s.refStmtElse != null as *Block { Ast_GraftBlockFile(s.refStmtElse, path); } } /// Set line/column/sourceFile on a single node (no recurse). func Ast_SetExprLoc(e: *Expr, line: uint32, col: uint32, path: String) { if e == null as *Expr { return; } e.line = line; e.column = col; if path != null as String && !String_Eq(path, "") { e.sourceFile = path; } } func Ast_SetStmtLoc(s: *Stmt, line: uint32, col: uint32, path: String) { if s == null as *Stmt { return; } s.line = line; s.column = col; if path != null as String && !String_Eq(path, "") { s.sourceFile = path; } } /// Effective source path for #line / diagnostics (empty if unknown). func Ast_ExprSourceFile(e: *Expr) -> String { if e == null as *Expr { return ""; } if e.sourceFile == null as String { return ""; } return e.sourceFile; } // Deep clone for quote / macro expansion templates (shares TypeExpr/Decl pointers). func Ast_ClonePattern(pat: *Pattern) -> *Pattern { if pat == null as *Pattern { return null as *Pattern; } let n: *Pattern = bux_alloc(sizeof(Pattern)) as *Pattern; n.kind = pat.kind; n.line = pat.line; n.column = pat.column; n.patIdent = pat.patIdent; n.patLitKind = pat.patLitKind; n.patLitText = pat.patLitText; n.patRangeInclusive = pat.patRangeInclusive; n.patEnumPath = pat.patEnumPath; n.patStructName = pat.patStructName; n.patFieldName = pat.patFieldName; n.patChild1 = Ast_ClonePattern(pat.patChild1); n.patChild2 = Ast_ClonePattern(pat.patChild2); n.patArgs = Ast_ClonePattern(pat.patArgs); n.patNext = Ast_ClonePattern(pat.patNext); n.patGuardExpr = Ast_CloneExpr(pat.patGuardExpr); return n; } func Ast_CloneExprList(list: *ExprList) -> *ExprList { if list == null as *ExprList { return null as *ExprList; } let n: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; n.expr = Ast_CloneExpr(list.expr); n.next = Ast_CloneExprList(list.next); n.argName = list.argName; return n; } func Ast_CloneMatchArm(arm: *MatchArm) -> *MatchArm { if arm == null as *MatchArm { return null as *MatchArm; } let n: *MatchArm = bux_alloc(sizeof(MatchArm)) as *MatchArm; n.line = arm.line; n.column = arm.column; n.pattern = Ast_ClonePattern(arm.pattern); n.body = Ast_CloneExpr(arm.body); n.next = Ast_CloneMatchArm(arm.next); return n; } func Ast_CloneExpr(e: *Expr) -> *Expr { if e == null as *Expr { return null as *Expr; } let n: *Expr = bux_alloc(sizeof(Expr)) as *Expr; n.kind = e.kind; n.line = e.line; n.column = e.column; n.sourceFile = e.sourceFile; n.strValue = e.strValue; n.intValue = e.intValue; n.boolValue = e.boolValue; n.tokKind = e.tokKind; n.tokText = e.tokText; n.child1 = Ast_CloneExpr(e.child1); n.child2 = Ast_CloneExpr(e.child2); n.child3 = Ast_CloneExpr(e.child3); n.refType = e.refType; // share TypeExpr n.refBlock = Ast_CloneBlock(e.refBlock); n.genericCallee = e.genericCallee; n.genericTypeArg0 = e.genericTypeArg0; n.genericTypeArg1 = e.genericTypeArg1; n.genericTypeArgCount = e.genericTypeArgCount; n.structName = e.structName; n.structFieldCount = e.structFieldCount; n.closureParams = e.closureParams; // share param decl n.captureCount = e.captureCount; n.captureName0 = e.captureName0; n.captureName1 = e.captureName1; n.captureName2 = e.captureName2; n.captureName3 = e.captureName3; n.captureName4 = e.captureName4; n.captureName5 = e.captureName5; n.captureName6 = e.captureName6; n.captureName7 = e.captureName7; n.captureType0 = e.captureType0; n.captureType1 = e.captureType1; n.captureType2 = e.captureType2; n.captureType3 = e.captureType3; n.captureType4 = e.captureType4; n.captureType5 = e.captureType5; n.captureType6 = e.captureType6; n.captureType7 = e.captureType7; n.callArgs = Ast_CloneExprList(e.callArgs); n.callArgCount = e.callArgCount; n.matchArms = Ast_CloneMatchArm(e.matchArms); n.matchArmCount = e.matchArmCount; return n; } func Ast_CloneStmt(s: *Stmt) -> *Stmt { if s == null as *Stmt { return null as *Stmt; } let n: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; n.kind = s.kind; n.line = s.line; n.column = s.column; n.sourceFile = s.sourceFile; n.strValue = s.strValue; n.boolValue = s.boolValue; n.child1 = Ast_CloneExpr(s.child1); n.child2 = Ast_CloneExpr(s.child2); n.child3 = Ast_CloneExpr(s.child3); n.refStmtType = s.refStmtType; n.refStmtPattern = Ast_ClonePattern(s.refStmtPattern); n.refStmtDecl = s.refStmtDecl; n.refStmtBlock = Ast_CloneBlock(s.refStmtBlock); n.refStmtElse = Ast_CloneBlock(s.refStmtElse); n.elseIfCount = s.elseIfCount; n.nextStmt = Ast_CloneStmt(s.nextStmt); return n; } func Ast_CloneBlock(b: *Block) -> *Block { if b == null as *Block { return null as *Block; } let n: *Block = bux_alloc(sizeof(Block)) as *Block; n.line = b.line; n.column = b.column; n.sourceFile = b.sourceFile; n.stmtCount = b.stmtCount; n.firstStmt = Ast_CloneStmt(b.firstStmt); // Rebuild lastStmt var cur: *Stmt = n.firstStmt; var last: *Stmt = null as *Stmt; while cur != null as *Stmt { last = cur; cur = cur.nextStmt; } n.lastStmt = last; return n; } /// Definition-site quote: clone template, keep sourceFile (macro body locations). func Ast_QuoteDefSite(e: *Expr) -> *Expr { return Ast_CloneExpr(e); } /// Call-site quote: clone template, graft call-site file and span for #line / diags. func Ast_QuoteCallSite(e: *Expr, siteFile: String, siteLine: uint32, siteCol: uint32) -> *Expr { let n: *Expr = Ast_CloneExpr(e); if n == null as *Expr { return null as *Expr; } Ast_GraftExprFile(n, siteFile); // Root span is the invocation; children keep structure but share site file n.line = siteLine; n.column = siteCol; return n; } /// Apply call-site graft to a whole statement tree (for stmt-producing macros). func Ast_QuoteStmtCallSite(s: *Stmt, siteFile: String, siteLine: uint32, siteCol: uint32) -> *Stmt { let n: *Stmt = Ast_CloneStmt(s); if n == null as *Stmt { return null as *Stmt; } Ast_GraftStmtFile(n, siteFile); n.line = siteLine; n.column = siteCol; return n; } func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl { return Decl { kind: kind, line: line, column: col, isPublic: false, sourceFile: "", isAsync: false, isChecked: 0, isDrop: 0, isRelease: 0, isConst: 0, strValue: "", strValue2: "", typeParam0: "", typeParam1: "", typeParamCount: 0, typeParam0Bound: "", typeParam1Bound: "" , paramCount: 0, retType: null as *TypeExpr, refBody: null as *Block, fieldCount: 0, variantCount: 0, methodCount: 0, useKind: 0, usePath: "", useNames: "", constType: null as *TypeExpr, constValue: null as *Expr, aliasType: null as *TypeExpr, extFuncDll: "", extFuncVariadic: false, extFuncRetType: null as *TypeExpr, childDecl1: null as *Decl, childDecl2: null as *Decl }; } }