From bbb0c7acd8175ee3100785e5ca72e927afe982bf Mon Sep 17 00:00:00 2001 From: Ken Gorab Date: Tue, 5 Nov 2024 20:42:30 -0500 Subject: [PATCH] Exit function (#487) * Exit function Introduce the `exit` function as a means of terminating the program early. This also requires the introduction of the `@noreturn` decorator to indicate that code following the invocation of such a function is not reachable. * Emit 'hlt' instruction when invoking a Never function * Ensure match expression blocks that contain noreturn function calls don't do a 'store' --- projects/compiler/example.abra | 18 ++------------ projects/compiler/src/compiler.abra | 34 +++++++++++++++++--------- projects/compiler/src/typechecker.abra | 18 +++++++++++--- projects/std/src/libc.abra | 3 +++ projects/std/src/prelude.abra | 3 +++ 5 files changed, 44 insertions(+), 32 deletions(-) diff --git a/projects/compiler/example.abra b/projects/compiler/example.abra index 75d0886d..136c0de9 100644 --- a/projects/compiler/example.abra +++ b/projects/compiler/example.abra @@ -1,16 +1,2 @@ -func ok(): Result = Ok(123) -func err(): Result = Err("foo") - -func f1(): Result { - var acc = 0 - while acc < 10 { - val x = try err() else break - println(x) - acc += x - } - - Ok(acc) -} - -/// Expect: Result.Ok(value: 124) -println(f1()) \ No newline at end of file +val x = if true 123 else Process.exit() +println("x =", x) diff --git a/projects/compiler/src/compiler.abra b/projects/compiler/src/compiler.abra index f276c0b9..da125bba 100644 --- a/projects/compiler/src/compiler.abra +++ b/projects/compiler/src/compiler.abra @@ -1429,6 +1429,10 @@ export type Compiler { } } + if node.ty.kind == TypeKind.Never { + self._currentFn.block.buildHalt() + } + Ok(res) } TypedAstNodeKind.Array(items) => { @@ -1698,11 +1702,13 @@ export type Compiler { for node, idx in ifBlock { val res = try self._compileStatement(node) if idx == ifBlock.length - 1 { - if res |res| { - val label = self._currentFn.block.currentLabel - phiCases.push((label, res)) - } else if !ifBlockTerminator { - return unreachable("last statement in if-expr block has no value and is not a terminator", node.token.position) + if !ifBlockTerminator { + if res |res| { + val label = self._currentFn.block.currentLabel + phiCases.push((label, res)) + } else { + return unreachable("last statement in if-expr block has no value and is not a terminator", node.token.position) + } } } } @@ -1715,11 +1721,13 @@ export type Compiler { for node, idx in elseBlock { val res = try self._compileStatement(node) if idx == elseBlock.length - 1 { - if res |res| { - val label = self._currentFn.block.currentLabel - phiCases.push((label, res)) - } else if !elseBlockTerminator { - return unreachable("last statement in if-expr else block has no value and is not a terminator", node.token.position) + if !elseBlockTerminator { + if res |res| { + val label = self._currentFn.block.currentLabel + phiCases.push((label, res)) + } else { + return unreachable("last statement in if-expr else block has no value and is not a terminator", node.token.position) + } } } } @@ -1966,8 +1974,10 @@ export type Compiler { val res = try self._compileStatement(node) if idx == case.body.length - 1 { if resValSlotCtx |(slot, slotTy)| { - if res |res| { - self._currentFn.block.buildStore(slotTy, res, slot) + if !case.terminator { + if res |res| { + self._currentFn.block.buildStore(slotTy, res, slot) + } } } } diff --git a/projects/compiler/src/typechecker.abra b/projects/compiler/src/typechecker.abra index 342f5383..5586a0a3 100644 --- a/projects/compiler/src/typechecker.abra +++ b/projects/compiler/src/typechecker.abra @@ -2997,7 +2997,10 @@ export type Typechecker { typedNodes.push(typedNode) continue } - _ => try self._typecheckStatement(node: node, typeHint: None) + _ => { + if self.currentScope.terminator return Err(TypeError(position: node.token.position, kind: TypeErrorKind.UnreachableCode)) + try self._typecheckStatement(node: node, typeHint: None) + } } typedNodes.push(typedNode) @@ -3231,7 +3234,9 @@ export type Typechecker { val terminator = self.currentScope.terminator self.currentScope = prevScope - self.currentScope.terminator = terminator + if terminator == Some(Terminator.Returning) { + self.currentScope.terminator = terminator + } Ok(TypedAstNode(token: token, ty: Type(kind: TypeKind.PrimitiveUnit), kind: TypedAstNodeKind.While(typedCondition, conditionBindingPattern, typedNodes, terminator))) } @@ -3342,7 +3347,9 @@ export type Typechecker { val terminator = self.currentScope.terminator self.currentScope = prevScope - self.currentScope.terminator = terminator + if terminator == Some(Terminator.Returning) { + self.currentScope.terminator = terminator + } val kind = TypedAstNodeKind.For(typedIterator, (itemPattern, variables), indexBinding, typedNodes) Ok(TypedAstNode(token: token, ty: Type(kind: TypeKind.PrimitiveUnit), kind: kind)) @@ -4535,7 +4542,10 @@ export type Typechecker { } } - val returnType = if fn.returnType.containsGenerics() { + val returnType = if fn.decorators.find(d => d.label.name == "noreturn") { + self.currentScope.terminator = Some(Terminator.Returning) + Type(kind: TypeKind.Never) + } else if fn.returnType.containsGenerics() { fn.returnType.withSubstitutedGenerics(resolvedGenerics: resolvedGenerics, retainUnknown: false, genericsInScope: genericsInScope) } else { fn.returnType diff --git a/projects/std/src/libc.abra b/projects/std/src/libc.abra index 9bdf31f1..1424ade4 100644 --- a/projects/std/src/libc.abra +++ b/projects/std/src/libc.abra @@ -49,3 +49,6 @@ export func uname(utsnameBuf: Pointer): Int @CBinding("rand") export func rand(): Int + +@CBinding("exit") +export func exit(status: Int): Unit diff --git a/projects/std/src/prelude.abra b/projects/std/src/prelude.abra index 294bf6a3..82b4821c 100644 --- a/projects/std/src/prelude.abra +++ b/projects/std/src/prelude.abra @@ -933,6 +933,9 @@ type Process { Uname(sysname: sysname, nodename: nodename, release: release, version: version, machine: machine) } + + @noreturn + func exit(status = 1) = libc.exit(status) } type SetIterator {