feat: defer statement support in both compilers

- Add  statement for function-level deferred execution
- Deferred expressions run in LIFO order on function exit (return or implicit)
- Bootstrap: desugar defers in hir_lower.nim (inject before return + end of func)
- Selfhost: emit defers in c_backend.bux via CEmitter defer stack
- Both: add tkDefer token, skDefer AST node, hDefer HIR node
- Parser, lexer, sema, token files updated in both bootstrap/ and src/
This commit is contained in:
2026-06-08 20:59:27 +03:00
parent cf92b63ba6
commit a67271b08c
15 changed files with 385 additions and 4 deletions
+31 -2
View File
@@ -11,6 +11,7 @@ type
varCounter*: int
tryCounter*: int
pendingStmts*: seq[HirNode]
deferStmts*: seq[HirNode]
typeSubst*: Table[string, Type] # Type parameter substitution for generics
importTable*: Table[string, string] # Local name → fully qualified name for imports
genericStructs*: Table[string, Decl] # Generic struct declarations
@@ -1005,7 +1006,13 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
of skReturn:
let value = if stmt.stmtReturnValue != nil: ctx.lowerExpr(stmt.stmtReturnValue) else: nil
return ctx.flushPending(hirReturn(value, loc))
var stmts = ctx.pendingStmts
ctx.pendingStmts = @[]
# Add defers in reverse order (LIFO)
for i in countdown(ctx.deferStmts.len - 1, 0):
stmts.add(ctx.deferStmts[i])
stmts.add(hirReturn(value, loc))
return hirBlock(stmts, nil, makeVoid(), loc)
of skIf:
let cond = ctx.lowerExpr(stmt.stmtIfCond)
@@ -1120,6 +1127,11 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
return ctx.flushPending(HirNode(kind: hMatch, matchSubject: subject, matchArms: arms,
typ: makeVoid(), loc: loc))
of skDefer:
let body = ctx.lowerExpr(stmt.stmtDeferBody)
ctx.deferStmts.add(body)
return nil
of skDecl:
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
typ: makeVoid(), loc: loc)
@@ -1188,7 +1200,24 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
ctx.currentFuncRetType = retType
ctx.currentFuncDecl = decl
ctx.varTypeExprs = initTable[string, TypeExpr]() # Clear local vars for new function
let body = if funcBody != nil: ctx.lowerBlock(funcBody) else: nil
var body = if funcBody != nil: ctx.lowerBlock(funcBody) else: nil
# Inject remaining defers at end of function (for implicit return)
if ctx.deferStmts.len > 0 and body != nil and body.kind == hBlock:
# Only add if last statement is not already a return (defers already injected there)
var hasReturn = false
if body.blockStmts.len > 0 and body.blockStmts[^1].kind == hReturn:
hasReturn = true
elif body.blockStmts.len > 0 and body.blockStmts[^1].kind == hBlock:
# Check nested block's last statement
let last = body.blockStmts[^1]
if last.blockStmts.len > 0 and last.blockStmts[^1].kind == hReturn:
hasReturn = true
if not hasReturn:
for i in countdown(ctx.deferStmts.len - 1, 0):
body.blockStmts.add(ctx.deferStmts[i])
ctx.deferStmts = @[]
ctx.currentFuncDecl = oldFuncDecl
ctx.currentFuncRetType = oldFuncRetType
ctx.varTypeExprs = oldVarTypeExprs