Phase 8.3: Fix spawn to use bux_task_spawn for non-async functions

- Added exprSpawnAsync flag to AST ekSpawn
- Sema checks if callee is async and sets the flag
- HIR hSpawn carries spawnAsync flag
- C backend uses bux_task_spawn for non-async (OS threads) and bux_async_spawn for async (coroutines)
- Added concurrency example to test suite
This commit is contained in:
2026-06-01 11:58:17 +03:00
parent cefd2a8442
commit 8b2179ccf9
7 changed files with 25 additions and 9 deletions
+1
View File
@@ -207,6 +207,7 @@ type
of ekSpawn:
exprSpawnCallee*: Expr
exprSpawnArgs*: seq[Expr]
exprSpawnAsync*: bool
of ekAwait:
exprAwaitOperand*: Expr
of ekBlock:
+8 -7
View File
@@ -294,14 +294,15 @@ proc emitExpr(be: var CBackend, node: HirNode): string =
return &"sizeof({typ})"
of hSpawn:
if node.spawnArgs.len > 0:
# Fallback to OS thread spawn for functions with arguments
var argsStr = ""
let arg = be.emitExpr(node.spawnArgs[0])
argsStr = &"(void*){arg}"
return &"bux_task_spawn({node.spawnCallee}, {argsStr})"
else:
if node.spawnAsync:
# Async coroutine spawn
return &"bux_async_spawn({node.spawnCallee})"
else:
# OS thread spawn
var argsStr = "NULL"
if node.spawnArgs.len > 0:
argsStr = &"(void*){be.emitExpr(node.spawnArgs[0])}"
return &"bux_task_spawn((void* (*)(void*)){node.spawnCallee}, {argsStr})"
of hDynRef:
let data = be.emitExpr(node.dynRefData)
+1
View File
@@ -118,6 +118,7 @@ type
of hSpawn:
spawnCallee*: string
spawnArgs*: seq[HirNode]
spawnAsync*: bool
of hEmit:
emitCode*: string
of hDynRef:
+1
View File
@@ -832,6 +832,7 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
for arg in expr.exprSpawnArgs:
args.add(ctx.lowerExpr(arg))
return HirNode(kind: hSpawn, spawnCallee: calleeName, spawnArgs: args,
spawnAsync: expr.exprSpawnAsync,
typ: makePointer(makeVoid()), loc: loc)
of ekAwait:
+1 -1
View File
@@ -465,7 +465,7 @@ proc parsePrimary(p: var Parser): Expr =
if p.check(tkComma):
discard p.advance()
discard p.expect(tkRParen, "expected ')' after spawn arguments")
return Expr(kind: ekSpawn, loc: loc, exprSpawnCallee: callee, exprSpawnArgs: args)
return Expr(kind: ekSpawn, loc: loc, exprSpawnCallee: callee, exprSpawnArgs: args, exprSpawnAsync: false)
of tkHashLine:
discard p.advance()
return Expr(kind: ekIntrinsic, loc: loc, exprIntrinsic: ikLine)
+12
View File
@@ -1143,6 +1143,18 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
discard sema.checkExpr(expr.exprSpawnCallee, scope)
for arg in expr.exprSpawnArgs:
discard sema.checkExpr(arg, scope)
# Determine if callee is async
var calleeName = ""
case expr.exprSpawnCallee.kind
of ekIdent:
calleeName = expr.exprSpawnCallee.exprIdent
of ekPath:
calleeName = expr.exprSpawnCallee.exprPath.join("_")
else: discard
if calleeName != "":
let sym = sema.globalScope.lookup(calleeName)
if sym != nil and sym.decl != nil and sym.decl.kind == dkFunc and sym.decl.declFuncIsAsync:
expr.exprSpawnAsync = true
return makePointer(makeVoid())
of ekAwait:
let operand = sema.checkExpr(expr.exprAwaitOperand, scope)