Phase 8.1: ! (unwrap/panic) operator — syntax, parsing, HIR lowering, runtime panic

This commit is contained in:
2026-05-31 13:45:55 +03:00
parent 3949a2f91e
commit 98d1354b7a
7 changed files with 59 additions and 0 deletions
+3
View File
@@ -116,6 +116,7 @@ type
ekCast ekCast
ekIs ekIs
ekTry ekTry
ekUnwrap ## expr! — unwrap or panic
ekBlock ekBlock
ekMatch ekMatch
@@ -193,6 +194,8 @@ type
of ekTry: of ekTry:
exprTryOperand*: Expr exprTryOperand*: Expr
exprTryType*: TypeExpr # nil for Result?, or explicit target type exprTryType*: TypeExpr # nil for Result?, or explicit target type
of ekUnwrap:
exprUnwrapOperand*: Expr
of ekBlock: of ekBlock:
exprBlock*: Block exprBlock*: Block
of ekMatch: of ekMatch:
+49
View File
@@ -373,6 +373,8 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
of ekTry: of ekTry:
# For now, assume Result<int, String> -> int or Option<int> -> int # For now, assume Result<int, String> -> int or Option<int> -> int
return makeInt() return makeInt()
of ekUnwrap:
return makeInt()
of ekBlock: of ekBlock:
if expr.exprBlock.stmts.len > 0: if expr.exprBlock.stmts.len > 0:
let last = expr.exprBlock.stmts[^1] let last = expr.exprBlock.stmts[^1]
@@ -698,6 +700,51 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
ctx.pendingStmts.add(ifNode) ctx.pendingStmts.add(ifNode)
return okLoad 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: of ekMatch:
let subject = ctx.lowerExpr(expr.exprMatchSubject) let subject = ctx.lowerExpr(expr.exprMatchSubject)
var arms: seq[HirMatchArm] = @[] var arms: seq[HirMatchArm] = @[]
@@ -1065,6 +1112,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
result.add(findGenericCalls(expr.exprUnaryOperand)) result.add(findGenericCalls(expr.exprUnaryOperand))
of ekTry: of ekTry:
result.add(findGenericCalls(expr.exprTryOperand)) result.add(findGenericCalls(expr.exprTryOperand))
of ekUnwrap:
result.add(findGenericCalls(expr.exprUnwrapOperand))
of ekAssign: of ekAssign:
result.add(findGenericCalls(expr.exprAssignTarget)) result.add(findGenericCalls(expr.exprAssignTarget))
result.add(findGenericCalls(expr.exprAssignValue)) result.add(findGenericCalls(expr.exprAssignValue))
+3
View File
@@ -508,6 +508,9 @@ proc parsePostfix(p: var Parser): Expr =
of tkQuestion: of tkQuestion:
discard p.advance() discard p.advance()
left = Expr(kind: ekTry, loc: loc, exprTryOperand: left, exprTryType: nil) 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: of tkLBrace:
if p.structInitAllowed and left.kind in {ekIdent, ekPath, ekGenericCall}: if p.structInitAllowed and left.kind in {ekIdent, ekPath, ekGenericCall}:
discard p.advance() discard p.advance()
+4
View File
@@ -734,6 +734,10 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
# For now, assume Result<int, String> -> int # For now, assume Result<int, String> -> int
# TODO: check operand is Result/Option and current function returns same type # TODO: check operand is Result/Option and current function returns same type
return makeInt() return makeInt()
of ekUnwrap:
let operandType = sema.checkExpr(expr.exprUnwrapOperand, scope)
# Unwrap: extract Ok value or panic on Err
return makeInt()
of ekBlock: of ekBlock:
var blockScope = newScope(scope) var blockScope = newScope(scope)
var lastType = makeVoid() var lastType = makeVoid()
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.