Skip to content

Commit

Permalink
Exit function (#487)
Browse files Browse the repository at this point in the history
* 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'
  • Loading branch information
kengorab authored Nov 6, 2024
1 parent 9bf8d03 commit bbb0c7a
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 32 deletions.
18 changes: 2 additions & 16 deletions projects/compiler/example.abra
Original file line number Diff line number Diff line change
@@ -1,16 +1,2 @@
func ok(): Result<Int, String> = Ok(123)
func err(): Result<Int, String> = Err("foo")

func f1(): Result<Int, Int> {
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())
val x = if true 123 else Process.exit()
println("x =", x)
34 changes: 22 additions & 12 deletions projects/compiler/src/compiler.abra
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,10 @@ export type Compiler {
}
}

if node.ty.kind == TypeKind.Never {
self._currentFn.block.buildHalt()
}

Ok(res)
}
TypedAstNodeKind.Array(items) => {
Expand Down Expand Up @@ -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)
}
}
}
}
Expand All @@ -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)
}
}
}
}
Expand Down Expand Up @@ -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)
}
}
}
}
Expand Down
18 changes: 14 additions & 4 deletions projects/compiler/src/typechecker.abra
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)))
}
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions projects/std/src/libc.abra
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,6 @@ export func uname(utsnameBuf: Pointer<Byte>): Int

@CBinding("rand")
export func rand(): Int

@CBinding("exit")
export func exit(status: Int): Unit
3 changes: 3 additions & 0 deletions projects/std/src/prelude.abra
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
Expand Down

0 comments on commit bbb0c7a

Please sign in to comment.