Phase 8.1: ! (unwrap/panic) operator — syntax, parsing, HIR lowering, runtime panic
This commit is contained in:
@@ -116,6 +116,7 @@ type
|
||||
ekCast
|
||||
ekIs
|
||||
ekTry
|
||||
ekUnwrap ## expr! — unwrap or panic
|
||||
ekBlock
|
||||
ekMatch
|
||||
|
||||
@@ -193,6 +194,8 @@ type
|
||||
of ekTry:
|
||||
exprTryOperand*: Expr
|
||||
exprTryType*: TypeExpr # nil for Result?, or explicit target type
|
||||
of ekUnwrap:
|
||||
exprUnwrapOperand*: Expr
|
||||
of ekBlock:
|
||||
exprBlock*: Block
|
||||
of ekMatch:
|
||||
|
||||
@@ -373,6 +373,8 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||
of ekTry:
|
||||
# For now, assume Result<int, String> -> int or Option<int> -> int
|
||||
return makeInt()
|
||||
of ekUnwrap:
|
||||
return makeInt()
|
||||
of ekBlock:
|
||||
if expr.exprBlock.stmts.len > 0:
|
||||
let last = expr.exprBlock.stmts[^1]
|
||||
@@ -698,6 +700,51 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
||||
ctx.pendingStmts.add(ifNode)
|
||||
return okLoad
|
||||
|
||||
of ekUnwrap:
|
||||
let operand = ctx.lowerExpr(expr.exprUnwrapOperand)
|
||||
let operandType = ctx.resolveExprType(expr.exprUnwrapOperand)
|
||||
|
||||
var errTag = "Result_Err"
|
||||
var typeName = "Result"
|
||||
if operandType.kind == tkNamed:
|
||||
typeName = operandType.name
|
||||
if typeName == "Option":
|
||||
errTag = "Option_None"
|
||||
|
||||
let tmpName = ctx.freshTryVar()
|
||||
let tmpAlloca = hirAlloca(tmpName, operandType, loc)
|
||||
let tmpVar = hirVar(tmpName, makePointer(operandType), loc)
|
||||
let tmpStore = hirStore(tmpVar, operand, loc)
|
||||
|
||||
let tagPtr = HirNode(kind: hFieldPtr, fieldPtrBase: tmpVar, fieldName: "tag",
|
||||
typ: makePointer(makeNamed(typeName & "_Tag")), loc: loc)
|
||||
let tagLoad = HirNode(kind: hLoad, loadPtr: tagPtr,
|
||||
typ: makeNamed(typeName & "_Tag"), loc: loc)
|
||||
let errConst = hirVar(errTag, makeNamed(typeName & "_Tag"), loc)
|
||||
let cond = hirBinary(tkEq, tagLoad, errConst, makeBool(), loc)
|
||||
|
||||
# On error: call bux_panic("unwrap failed")
|
||||
let panicTok = Token(kind: tkStringLiteral, text: "\"unwrap failed\"", loc: loc)
|
||||
let panicMsg = HirNode(kind: hLit, litToken: panicTok, typ: makeStr(), loc: loc)
|
||||
let panicCall = hirCall("bux_panic", @[panicMsg], makeVoid(), loc)
|
||||
let thenBlock = hirBlock(@[panicCall], nil, makeVoid(), loc)
|
||||
let ifNode = HirNode(kind: hIf, ifCond: cond, ifThen: thenBlock,
|
||||
ifElse: nil, typ: makeVoid(), loc: loc)
|
||||
|
||||
# Extract the Ok/Some value
|
||||
let dataPtr = HirNode(kind: hFieldPtr, fieldPtrBase: tmpVar, fieldName: "data",
|
||||
typ: makePointer(makeNamed(typeName & "_Data")), loc: loc)
|
||||
let dataLoad = HirNode(kind: hLoad, loadPtr: dataPtr,
|
||||
typ: makeNamed(typeName & "_Data"), loc: loc)
|
||||
let okPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: "Ok_0",
|
||||
typ: makePointer(makeInt()), loc: loc)
|
||||
let okLoad = HirNode(kind: hLoad, loadPtr: okPtr, typ: makeInt(), loc: loc)
|
||||
|
||||
ctx.pendingStmts.add(tmpAlloca)
|
||||
ctx.pendingStmts.add(tmpStore)
|
||||
ctx.pendingStmts.add(ifNode)
|
||||
return okLoad
|
||||
|
||||
of ekMatch:
|
||||
let subject = ctx.lowerExpr(expr.exprMatchSubject)
|
||||
var arms: seq[HirMatchArm] = @[]
|
||||
@@ -1065,6 +1112,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
result.add(findGenericCalls(expr.exprUnaryOperand))
|
||||
of ekTry:
|
||||
result.add(findGenericCalls(expr.exprTryOperand))
|
||||
of ekUnwrap:
|
||||
result.add(findGenericCalls(expr.exprUnwrapOperand))
|
||||
of ekAssign:
|
||||
result.add(findGenericCalls(expr.exprAssignTarget))
|
||||
result.add(findGenericCalls(expr.exprAssignValue))
|
||||
|
||||
@@ -508,6 +508,9 @@ proc parsePostfix(p: var Parser): Expr =
|
||||
of tkQuestion:
|
||||
discard p.advance()
|
||||
left = Expr(kind: ekTry, loc: loc, exprTryOperand: left, exprTryType: nil)
|
||||
of tkBang:
|
||||
discard p.advance()
|
||||
left = Expr(kind: ekUnwrap, loc: loc, exprUnwrapOperand: left)
|
||||
of tkLBrace:
|
||||
if p.structInitAllowed and left.kind in {ekIdent, ekPath, ekGenericCall}:
|
||||
discard p.advance()
|
||||
|
||||
@@ -734,6 +734,10 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
||||
# For now, assume Result<int, String> -> int
|
||||
# TODO: check operand is Result/Option and current function returns same type
|
||||
return makeInt()
|
||||
of ekUnwrap:
|
||||
let operandType = sema.checkExpr(expr.exprUnwrapOperand, scope)
|
||||
# Unwrap: extract Ok value or panic on Err
|
||||
return makeInt()
|
||||
of ekBlock:
|
||||
var blockScope = newScope(scope)
|
||||
var lastType = makeVoid()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user