feat: Phase 8.3 async/await syntax + runtime
- AST: declFuncIsAsync flag, ekAwait expression kind - Parser: async func declarations, expr.await postfix operator - Sema: allow await anywhere (blocks current thread until task completes) - HIR: ekAwait lowered to bux_task_join(handle) call - C backend: async func emitted as regular function - Example: examples/async.bux + examples_pkg/async project - All 20 existing examples pass, selfhost build works
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
import Std::Task::TaskHandle;
|
||||||
|
|
||||||
|
async func BackgroundWork(id: int) {
|
||||||
|
PrintLine("Task started:");
|
||||||
|
PrintInt(id);
|
||||||
|
PrintLine("");
|
||||||
|
Task_Sleep(50);
|
||||||
|
PrintLine("Task done:");
|
||||||
|
PrintInt(id);
|
||||||
|
PrintLine("");
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("Main: spawning tasks...");
|
||||||
|
let h1: *void = spawn BackgroundWork(1);
|
||||||
|
let h2: *void = spawn BackgroundWork(2);
|
||||||
|
PrintLine("Main: waiting for tasks...");
|
||||||
|
h1.await;
|
||||||
|
h2.await;
|
||||||
|
PrintLine("Main: all tasks completed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -118,6 +118,7 @@ type
|
|||||||
ekTry
|
ekTry
|
||||||
ekUnwrap ## expr! — unwrap or panic
|
ekUnwrap ## expr! — unwrap or panic
|
||||||
ekSpawn ## spawn expr — create a new task
|
ekSpawn ## spawn expr — create a new task
|
||||||
|
ekAwait ## expr.await — suspend until future resolves
|
||||||
ekBlock
|
ekBlock
|
||||||
ekMatch
|
ekMatch
|
||||||
|
|
||||||
@@ -200,6 +201,8 @@ type
|
|||||||
of ekSpawn:
|
of ekSpawn:
|
||||||
exprSpawnCallee*: Expr
|
exprSpawnCallee*: Expr
|
||||||
exprSpawnArgs*: seq[Expr]
|
exprSpawnArgs*: seq[Expr]
|
||||||
|
of ekAwait:
|
||||||
|
exprAwaitOperand*: Expr
|
||||||
of ekBlock:
|
of ekBlock:
|
||||||
exprBlock*: Block
|
exprBlock*: Block
|
||||||
of ekMatch:
|
of ekMatch:
|
||||||
@@ -335,6 +338,7 @@ type
|
|||||||
declFuncAsm*: bool
|
declFuncAsm*: bool
|
||||||
declFuncCallConv*: CallingConvention
|
declFuncCallConv*: CallingConvention
|
||||||
declFuncConst*: bool ## const func — evaluable at compile time
|
declFuncConst*: bool ## const func — evaluable at compile time
|
||||||
|
declFuncIsAsync*: bool ## async func — returns Future<T>
|
||||||
declFuncName*: string
|
declFuncName*: string
|
||||||
declFuncTypeParams*: seq[TypeParam]
|
declFuncTypeParams*: seq[TypeParam]
|
||||||
declFuncParams*: seq[Param]
|
declFuncParams*: seq[Param]
|
||||||
|
|||||||
@@ -768,6 +768,10 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
return HirNode(kind: hSpawn, spawnCallee: calleeName, spawnArgs: args,
|
return HirNode(kind: hSpawn, spawnCallee: calleeName, spawnArgs: args,
|
||||||
typ: makePointer(makeVoid()), loc: loc)
|
typ: makePointer(makeVoid()), loc: loc)
|
||||||
|
|
||||||
|
of ekAwait:
|
||||||
|
let lowered = ctx.lowerExpr(expr.exprAwaitOperand)
|
||||||
|
return hirCall("bux_task_join", @[lowered], makeVoid(), loc)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc)
|
||||||
|
|||||||
+15
-6
@@ -527,10 +527,14 @@ proc parsePostfix(p: var Parser): Expr =
|
|||||||
discard p.expect(tkRBracket, "expected ']' to close index")
|
discard p.expect(tkRBracket, "expected ']' to close index")
|
||||||
left = Expr(kind: ekIndex, loc: loc, exprIndexObj: left, exprIndexIdx: idx)
|
left = Expr(kind: ekIndex, loc: loc, exprIndexObj: left, exprIndexIdx: idx)
|
||||||
of tkDot:
|
of tkDot:
|
||||||
# Field expression
|
# Field expression or .await
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
let fieldName = p.expectIdentOrKeyword("expected field name after '.'").text
|
if p.check(tkAwait):
|
||||||
left = Expr(kind: ekField, loc: loc, exprFieldObj: left, exprFieldName: fieldName)
|
discard p.advance()
|
||||||
|
left = Expr(kind: ekAwait, loc: loc, exprAwaitOperand: left)
|
||||||
|
else:
|
||||||
|
let fieldName = p.expectIdentOrKeyword("expected field name after '.'").text
|
||||||
|
left = Expr(kind: ekField, loc: loc, exprFieldObj: left, exprFieldName: fieldName)
|
||||||
of tkPlusPlus, tkMinusMinus:
|
of tkPlusPlus, tkMinusMinus:
|
||||||
let op = p.advance().kind
|
let op = p.advance().kind
|
||||||
left = Expr(kind: ekPostfix, loc: loc, exprPostfixOp: op, exprPostfixOperand: left)
|
left = Expr(kind: ekPostfix, loc: loc, exprPostfixOp: op, exprPostfixOperand: left)
|
||||||
@@ -971,7 +975,7 @@ proc parseParamList(p: var Parser, allowVariadic: bool = false): seq[Param] =
|
|||||||
discard p.advance()
|
discard p.advance()
|
||||||
discard p.expect(tkRParen, "expected ')' to close parameter list")
|
discard p.expect(tkRParen, "expected ')' to close parameter list")
|
||||||
|
|
||||||
proc parseFuncDecl(p: var Parser, isPublic: bool, isAsm: bool, attrs: ParsedAttrs, isConst: bool = false): Decl =
|
proc parseFuncDecl(p: var Parser, isPublic: bool, isAsm: bool, attrs: ParsedAttrs, isConst: bool = false, isAsync: bool = false): Decl =
|
||||||
let loc = p.currentLoc
|
let loc = p.currentLoc
|
||||||
discard p.expect(tkFunc, "expected 'func'")
|
discard p.expect(tkFunc, "expected 'func'")
|
||||||
let name = p.expect(tkIdent, "expected function name").text
|
let name = p.expect(tkIdent, "expected function name").text
|
||||||
@@ -991,7 +995,7 @@ proc parseFuncDecl(p: var Parser, isPublic: bool, isAsm: bool, attrs: ParsedAttr
|
|||||||
return Decl(kind: dkFunc, loc: loc, isPublic: isPublic,
|
return Decl(kind: dkFunc, loc: loc, isPublic: isPublic,
|
||||||
declAttrs: declAttrs,
|
declAttrs: declAttrs,
|
||||||
declFuncAsm: isAsm, declFuncCallConv: attrs.callConv,
|
declFuncAsm: isAsm, declFuncCallConv: attrs.callConv,
|
||||||
declFuncConst: isConst,
|
declFuncConst: isConst, declFuncIsAsync: isAsync,
|
||||||
declFuncName: name, declFuncTypeParams: typeParams,
|
declFuncName: name, declFuncTypeParams: typeParams,
|
||||||
declFuncParams: params, declFuncReturnType: retType,
|
declFuncParams: params, declFuncReturnType: retType,
|
||||||
declFuncBody: body)
|
declFuncBody: body)
|
||||||
@@ -1278,10 +1282,15 @@ proc parseDecl(p: var Parser): Decl =
|
|||||||
if p.check(tkConst) and p.peek(1) == tkFunc:
|
if p.check(tkConst) and p.peek(1) == tkFunc:
|
||||||
isConst = true
|
isConst = true
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
|
|
||||||
|
var isAsync = false
|
||||||
|
if p.check(tkAsync) and p.peek(1) == tkFunc:
|
||||||
|
isAsync = true
|
||||||
|
discard p.advance()
|
||||||
|
|
||||||
case p.peek()
|
case p.peek()
|
||||||
of tkFunc:
|
of tkFunc:
|
||||||
return p.parseFuncDecl(isPublic, false, attrs, isConst)
|
return p.parseFuncDecl(isPublic, false, attrs, isConst, isAsync)
|
||||||
of tkStruct:
|
of tkStruct:
|
||||||
return p.parseStructDecl(isPublic)
|
return p.parseStructDecl(isPublic)
|
||||||
of tkEnum:
|
of tkEnum:
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ type
|
|||||||
interfaceTable*: Table[string, Decl]
|
interfaceTable*: Table[string, Decl]
|
||||||
# Borrow checker state
|
# Borrow checker state
|
||||||
checkedFunc*: bool ## true inside @[Checked] function
|
checkedFunc*: bool ## true inside @[Checked] function
|
||||||
|
currentFuncIsAsync*: bool ## true inside async func
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
@@ -1007,6 +1008,10 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
for arg in expr.exprSpawnArgs:
|
for arg in expr.exprSpawnArgs:
|
||||||
discard sema.checkExpr(arg, scope)
|
discard sema.checkExpr(arg, scope)
|
||||||
return makePointer(makeVoid())
|
return makePointer(makeVoid())
|
||||||
|
of ekAwait:
|
||||||
|
let operand = sema.checkExpr(expr.exprAwaitOperand, scope)
|
||||||
|
# await on a task handle returns void for now
|
||||||
|
return makeVoid()
|
||||||
of ekSpread:
|
of ekSpread:
|
||||||
return sema.checkExpr(expr.exprSpreadOperand, scope)
|
return sema.checkExpr(expr.exprSpreadOperand, scope)
|
||||||
|
|
||||||
@@ -1100,7 +1105,9 @@ proc checkFunc(sema: var Sema, decl: Decl) =
|
|||||||
if decl.declFuncTypeParams.len > 0:
|
if decl.declFuncTypeParams.len > 0:
|
||||||
return
|
return
|
||||||
let wasChecked = sema.checkedFunc
|
let wasChecked = sema.checkedFunc
|
||||||
|
let wasAsync = sema.currentFuncIsAsync
|
||||||
sema.checkedFunc = "Checked" in decl.declAttrs
|
sema.checkedFunc = "Checked" in decl.declAttrs
|
||||||
|
sema.currentFuncIsAsync = decl.declFuncIsAsync
|
||||||
var funcScope = newScope(sema.globalScope)
|
var funcScope = newScope(sema.globalScope)
|
||||||
# Add type parameters to type table for resolution
|
# Add type parameters to type table for resolution
|
||||||
var addedTypeParams: seq[string] = @[]
|
var addedTypeParams: seq[string] = @[]
|
||||||
@@ -1119,6 +1126,7 @@ proc checkFunc(sema: var Sema, decl: Decl) =
|
|||||||
for tp in addedTypeParams:
|
for tp in addedTypeParams:
|
||||||
sema.typeTable.del(tp)
|
sema.typeTable.del(tp)
|
||||||
sema.checkedFunc = wasChecked
|
sema.checkedFunc = wasChecked
|
||||||
|
sema.currentFuncIsAsync = wasAsync
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Second pass: check all function bodies
|
# Second pass: check all function bodies
|
||||||
|
|||||||
Reference in New Issue
Block a user