diff --git a/projects/compiler/example.abra b/projects/compiler/example.abra index d109fda3..0499b764 100644 --- a/projects/compiler/example.abra +++ b/projects/compiler/example.abra @@ -1,12 +1,15 @@ -val m = { - a: 1, - b: 2, - c: 3, - s: 4 -} -println(m.size) -println(m) +func some(): Int? = Some(123) +func none(): Int? = None + +func f12(): Result { + var acc = 0 + while acc < 10 { + val x = try none() else return Some(12) + acc += x + } -for (k, v) in m { - println(k, v) + Some(acc) } + +/// Expect: Option.Some(value: 12) +println(f12()) diff --git a/projects/compiler/src/compiler.abra b/projects/compiler/src/compiler.abra index 189bd01d..963e199b 100644 --- a/projects/compiler/src/compiler.abra +++ b/projects/compiler/src/compiler.abra @@ -1803,23 +1803,32 @@ export type Compiler { } TypedAstNodeKind.Try(expr, elseClause) => { val exprVal = try self._compileExpression(expr) - val isErr = try self._emitResultValueIsOkVariant(exprVal, negate: true) + val isOptionTry = !!self._typeIsOption(expr.ty) + val isUnhappyPath = if isOptionTry { + try self._emitOptValueIsSomeVariant(exprVal, negate: true) + } else { + try self._emitResultValueIsOkVariant(exprVal, negate: true) + } - val labelIsErr = self._currentFn.block.addLabel("isErr") + val labelIsUnhappyPath = self._currentFn.block.addLabel("isUnhappyPath") val labelCont = self._currentFn.block.addLabel("cont") if elseClause |clause| { val phiCases: (Label, Value)[] = [] - val labelIsOk = self._currentFn.block.addLabel("isOk") - self._currentFn.block.buildJnz(isErr, labelIsErr, labelIsOk) + val labelIsHappyPath = self._currentFn.block.addLabel("isOk") + self._currentFn.block.buildJnz(isUnhappyPath, labelIsUnhappyPath, labelIsHappyPath) - self._currentFn.block.registerLabel(labelIsErr) + self._currentFn.block.registerLabel(labelIsUnhappyPath) if clause.pattern |(bindingPattern, vars)| { val variables = vars.keyBy(v => v.label.name) val errorQbeType = try self._getQbeTypeForTypeExpect(clause.errorType, "unacceptable type", None) - val bindingVal = try self._emitOptValueGetValue(errorQbeType, exprVal) + val bindingVal = if isOptionTry { + exprVal + } else { + try self._emitResultValueGetValue(errorQbeType, exprVal) + } try self._compileBindingPattern(bindingPattern, variables, Some(bindingVal)) } for node, idx in clause.block { @@ -1837,27 +1846,35 @@ export type Compiler { self._currentFn.block.buildJmp(labelCont) } - self._currentFn.block.registerLabel(labelIsOk) - val okQbeType = try self._getQbeTypeForTypeExpect(node.ty, "unacceptable type", None) - val okVal = try self._emitOptValueGetValue(okQbeType, exprVal) + self._currentFn.block.registerLabel(labelIsHappyPath) + val happyPathQbeType = try self._getQbeTypeForTypeExpect(node.ty, "unacceptable type", None) + val happyPathVal = if isOptionTry { + try self._emitOptValueGetValue(happyPathQbeType, exprVal) + } else { + try self._emitResultValueGetValue(happyPathQbeType, exprVal) + } val label = self._currentFn.block.currentLabel - phiCases.push((label, okVal)) + phiCases.push((label, happyPathVal)) self._currentFn.block.buildJmp(labelCont) self._currentFn.block.registerLabel(labelCont) val res = try self._currentFn.block.buildPhi(phiCases) else |e| return qbeError(e) Ok(res) } else { - self._currentFn.block.buildJnz(isErr, labelIsErr, labelCont) + self._currentFn.block.buildJnz(isUnhappyPath, labelIsUnhappyPath, labelCont) - self._currentFn.block.registerLabel(labelIsErr) + self._currentFn.block.registerLabel(labelIsUnhappyPath) self._currentFn.block.buildReturn(Some(exprVal)) self._currentFn.block.registerLabel(labelCont) - val okValTy = try self._getQbeTypeForTypeExpect(node.ty, "unacceptable type", None) - val okValue = try self._emitResultValueGetValue(okValTy, exprVal) + val happyPathQbeType = try self._getQbeTypeForTypeExpect(node.ty, "unacceptable type", None) + val happyPathVal = if isOptionTry { + try self._emitOptValueGetValue(happyPathQbeType, exprVal) + } else { + try self._emitResultValueGetValue(happyPathQbeType, exprVal) + } - Ok(okValue) + Ok(happyPathVal) } } _ => unreachable("node must be a statement") diff --git a/projects/compiler/src/typechecker.abra b/projects/compiler/src/typechecker.abra index 467b932e..f6bda3f0 100644 --- a/projects/compiler/src/typechecker.abra +++ b/projects/compiler/src/typechecker.abra @@ -1370,10 +1370,14 @@ type TypeError { InvalidTryLocationReason.NotWithinFunction => { lines.push("Try expressions can only be used inside of function bodies") } - InvalidTryLocationReason.InvalidFunctionReturnType(fnLabel, tryType, returnType) => { + InvalidTryLocationReason.InvalidFunctionReturnType(fnLabel, tryType, returnType, isResult) => { lines.push("The containing function '${fnLabel.name}' has return type '${returnType.repr()}', which is incompatible with the try expression's type '${tryType.repr()}'.") lines.push(self._getCursorLine(fnLabel.position, contents)) - lines.push("To be compatible, the function must return a Result whose error type matches that of the try expression") + if isResult { + lines.push("To be compatible, '${fnLabel.name}' must return a Result whose error type matches that of the try expression") + } else { + lines.push("To be compatible, '${fnLabel.name}' must return an Option type") + } } } } @@ -1450,7 +1454,7 @@ enum InvalidDestructuringReason { enum InvalidTryLocationReason { NotWithinFunction - InvalidFunctionReturnType(fnLabel: Label, tryType: Type, returnType: Type) + InvalidFunctionReturnType(fnLabel: Label, tryType: Type, returnType: Type, isResult: Bool) } enum TypeErrorKind { @@ -5194,29 +5198,41 @@ export type Typechecker { // TODO: support top-level try (the error case would just exit the program) val currentFn = if self.currentFunction |f| f else return Err(TypeError(position: token.position, kind: TypeErrorKind.InvalidTryLocation(InvalidTryLocationReason.NotWithinFunction))) - val (_, retErrType) = if self._typeIsResult(currentFn.returnType) |t| t else { - // A bit wasteful perhaps, but attempt to get the type of the try expression's subject without the hint, for a nicer error message - val typedExpr = try self._typecheckExpression(expr, None) - return Err(TypeError(position: token.position, kind: TypeErrorKind.InvalidTryLocation(InvalidTryLocationReason.InvalidFunctionReturnType(fnLabel: currentFn.label, tryType: typedExpr.ty, returnType: currentFn.returnType)))) - } + val (typedExpr, returnTypeResultErr) = if self._typeIsResult(currentFn.returnType) |(_, retErrType)| { + val hint = if typeHint |typeHint| { Some(Type(kind: TypeKind.Instance(StructOrEnum.Enum(self.project.preludeResultEnum), [typeHint, retErrType]))) } else None + val typedExpr = try self._typecheckExpression(expr, hint) + + (typedExpr, Some(retErrType)) + } else if self._typeIsOption(currentFn.returnType) |inner| { + val hint = if typeHint |typeHint| { Some(Type(kind: TypeKind.Instance(StructOrEnum.Enum(self.project.preludeOptionEnum), [typeHint]))) } else None + val typedExpr = try self._typecheckExpression(expr, hint) - val hint = if typeHint |typeHint| { - Some(Type(kind: TypeKind.Instance(StructOrEnum.Enum(self.project.preludeResultEnum), [typeHint, retErrType]))) + (typedExpr, None) + } else if elseClause { + val typedExpr = try self._typecheckExpression(expr, None) + (typedExpr, None) } else { - None + val typedExpr = try self._typecheckExpression(expr, None) + val isResult = !!self._typeIsResult(typedExpr.ty) + return Err(TypeError(position: token.position, kind: TypeErrorKind.InvalidTryLocation(InvalidTryLocationReason.InvalidFunctionReturnType(fnLabel: currentFn.label, tryType: typedExpr.ty, returnType: currentFn.returnType, isResult: isResult)))) } - val typedExpr = try self._typecheckExpression(expr, hint) - - // TODO: other Try-able types - val (tryValType, tryErrType) = if self._typeIsResult(typedExpr.ty) |t| t else return Err(TypeError(position: typedExpr.token.position, kind: TypeErrorKind.InvalidTryTarget(typedExpr.ty))) - val typedElseClause = if elseClause |(elseToken, bindingPattern, elseBlock)| { + val (tryValType, typedElseClause) = if elseClause |(elseToken, bindingPattern, elseBlock)| { val isStatement = if typeHint |hint| hint.kind == TypeKind.PrimitiveUnit else false + val (tryValType, tryErrType) = if self._typeIsResult(typedExpr.ty) |t| { + t + } else if self._typeIsOption(typedExpr.ty) |inner| { + (inner, typedExpr.ty) + } else { + return Err(TypeError(position: typedExpr.token.position, kind: TypeErrorKind.InvalidTryTarget(typedExpr.ty))) + } + val prevScope = self.currentScope self.currentScope = self.currentScope.makeChild("try_else", ScopeKind.Try) val elseBindingPattern = if bindingPattern |pat| { + // TODO: Emit warning if expression is an Option type, since the binding would always equal `None` val variables = try self._typecheckBindingPattern(false, pat, tryErrType) Some((pat, variables)) } else { @@ -5255,11 +5271,30 @@ export type Typechecker { self.currentScope = prevScope - Some(TypedTryElseClause(pattern: elseBindingPattern, errorType: tryErrType, block: typedNodes, terminator: terminator)) - } else if !self._typeSatisfiesRequired(ty: retErrType, required: tryErrType) { - return Err(TypeError(position: token.position, kind: TypeErrorKind.TryReturnTypeMismatch(fnLabel: currentFn.label, tryType: typedExpr.ty, tryErrType: tryErrType, retErrType: retErrType))) + val elseClause = TypedTryElseClause(pattern: elseBindingPattern, errorType: tryErrType, block: typedNodes, terminator: terminator) + (tryValType, Some(elseClause)) } else { - None + val tryValType = if self._typeIsResult(typedExpr.ty) |(exprOkType, exprErrType)| { + if returnTypeResultErr |retErrType| { + if !self._typeSatisfiesRequired(ty: retErrType, required: exprErrType) { + return Err(TypeError(position: token.position, kind: TypeErrorKind.TryReturnTypeMismatch(fnLabel: currentFn.label, tryType: typedExpr.ty, tryErrType: exprErrType, retErrType: retErrType))) + } else { + exprOkType + } + } else { + return Err(TypeError(position: token.position, kind: TypeErrorKind.InvalidTryLocation(InvalidTryLocationReason.InvalidFunctionReturnType(fnLabel: currentFn.label, tryType: typedExpr.ty, returnType: currentFn.returnType, isResult: true)))) + } + } else if self._typeIsOption(typedExpr.ty) |inner| { + if returnTypeResultErr { + return Err(TypeError(position: token.position, kind: TypeErrorKind.InvalidTryLocation(InvalidTryLocationReason.InvalidFunctionReturnType(fnLabel: currentFn.label, tryType: typedExpr.ty, returnType: currentFn.returnType, isResult: false)))) + } else { + inner + } + } else { + return Err(TypeError(position: typedExpr.token.position, kind: TypeErrorKind.InvalidTryTarget(typedExpr.ty))) + } + + (tryValType, None) } Ok(TypedAstNode(token: token, ty: tryValType, kind: TypedAstNodeKind.Try(typedExpr, typedElseClause))) diff --git a/projects/compiler/test/compiler/try_option.abra b/projects/compiler/test/compiler/try_option.abra new file mode 100644 index 00000000..e99ed3e8 --- /dev/null +++ b/projects/compiler/test/compiler/try_option.abra @@ -0,0 +1,223 @@ +func some(): Int? = Some(123) +func none(): Int? = None + +func f1(): Int? { + val x = try some() + Some(x + 1) +} +/// Expect: Option.Some(value: 124) +println(f1()) + +func f2(): Int? { + val x = try none() + Some(x + 1) +} +/// Expect: Option.None +println(f2()) + +func f3(): Int[]? { + val arr = [try some(), (try some()) + 1, (try some()) + 2] + Some(arr) +} +/// Expect: Option.Some(value: [123, 124, 125]) +println(f3()) + +func f4(): Int[]? { + val arr = [try some(), (try none()) + 1, (try some()) + 2] + Some(arr) +} +/// Expect: Option.None +println(f4()) + +func f5(): Int? { + val x = (try f1()) + (try f2()) + (try f3()).length + (try f4()).length + Some(x) +} +/// Expect: Option.None +println(f5()) + +// Testing else-clause with terminators + +func f6(): Int? { + var acc = 0 + while acc < 10 { + val x = try some() else { + acc = 199 + continue + } + acc += x + } + + Some(acc) +} + +/// Expect: Option.Some(value: 123) +println(f6()) + +func f7(): Int? { + var acc = 0 + while acc < 10 { + val x = try some() else break + acc += x + } + + Some(acc) +} + +/// Expect: Option.Some(value: 123) +println(f7()) + +func f8(): Int? { + var acc = 0 + while acc < 10 { + val x = try none() else { + acc = 199 + continue + } + acc += x + } + + Some(acc) +} + +/// Expect: Option.Some(value: 199) +println(f8()) + +func f9(): Int? { + var acc = 0 + while acc < 10 { + val x = try none() else break + acc += x + } + + Some(acc) +} + +/// Expect: Option.Some(value: 0) +println(f9()) + +func f10(): Int? { + var acc = 0 + while acc < 10 { + val x = try some() else return Some(12) + acc += x + } + + Some(acc) +} + +/// Expect: Option.Some(value: 123) +println(f10()) + +func f11(): Int? { + var acc = 0 + while acc < 10 { + val x = try some() else return None + acc += x + } + + Some(acc) +} + +/// Expect: Option.Some(value: 123) +println(f11()) + +func f12(): Int? { + var acc = 0 + while acc < 10 { + val x = try none() else return Some(12) + acc += x + } + + Some(acc) +} + +/// Expect: Option.Some(value: 12) +println(f12()) + +func f13(): Int? { + var acc = 0 + while acc < 10 { + val x = try none() else return None + acc += x + } + + Some(acc) +} + +/// Expect: Option.None +println(f13()) + +func f14(): Int? { + val x = try some() else return Some(12) + + Some(x) +} + +/// Expect: Option.Some(value: 123) +println(f14()) + +func f15(): Int? { + val x = try some() else return None + + Some(x) +} + +/// Expect: Option.Some(value: 123) +println(f15()) + +func f16(): Int? { + val x = try none() else return Some(12) + + Some(x) +} + +/// Expect: Option.Some(value: 12) +println(f16()) + +func f17(): Int? { + val x = try none() else return None + + Some(x) +} + +/// Expect: Option.None +println(f17()) + +// Testing else-clause without terminators + +func f18(): Int? { + val x = try some() else 7 + + Some(x) +} + +/// Expect: Option.Some(value: 123) +println(f18()) + +func f19(): Int { + val x = try some() else 7 + + x + 1 +} + +/// Expect: 124 +println(f19()) + +func f20(): Int? { + val x = try none() else 7 + + Some(x) +} + +/// Expect: Option.Some(value: 7) +println(f20()) + +func f21(): Int { + val x = try none() else 7 + + x + 1 +} + +/// Expect: 8 +println(f21()) diff --git a/projects/compiler/test/compiler/try.abra b/projects/compiler/test/compiler/try_result.abra similarity index 93% rename from projects/compiler/test/compiler/try.abra rename to projects/compiler/test/compiler/try_result.abra index d1d471e7..165729a7 100644 --- a/projects/compiler/test/compiler/try.abra +++ b/projects/compiler/test/compiler/try_result.abra @@ -67,7 +67,6 @@ func f7(): Result { /// Expect: Result.Ok(value: 123) println(f7()) - func f8(): Result { var acc = 0 while acc < 10 { @@ -84,7 +83,6 @@ func f8(): Result { /// Expect: Result.Ok(value: 199) println(f8()) - func f9(): Result { var acc = 0 while acc < 10 { @@ -197,11 +195,29 @@ func f18(): Result { /// Expect: Result.Ok(value: 123) println(f18()) -func f19(): Result { +func f19(): Int { + val x = try ok() else 7 + + x + 1 +} + +/// Expect: 124 +println(f19()) + +func f20(): Result { val x = try err() else 7 Ok(x) } /// Expect: Result.Ok(value: 7) -println(f19()) +println(f20()) + +func f21(): Int { + val x = try err() else 7 + + x + 1 +} + +/// Expect: 8 +println(f21()) diff --git a/projects/compiler/test/run-tests.js b/projects/compiler/test/run-tests.js index 0a777574..dc044141 100644 --- a/projects/compiler/test/run-tests.js +++ b/projects/compiler/test/run-tests.js @@ -544,9 +544,12 @@ const TYPECHECKER_TESTS = [ { test: "typechecker/try/try.2.abra", assertions: "typechecker/try/try.2.out.json" }, { test: "typechecker/try/try.3.abra", assertions: "typechecker/try/try.3.out.json" }, { test: "typechecker/try/try.4.abra", assertions: "typechecker/try/try.4.out.json" }, + { test: "typechecker/try/try.5.abra", assertions: "typechecker/try/try.5.out.json" }, + { test: "typechecker/try/try.6.abra", assertions: "typechecker/try/try.6.out.json" }, { test: "typechecker/try/error_bad_location.1.abra", assertions: "typechecker/try/error_bad_location.1.out" }, { test: "typechecker/try/error_bad_location.2.abra", assertions: "typechecker/try/error_bad_location.2.out" }, - { test: "typechecker/try/error_bad_return_type.abra", assertions: "typechecker/try/error_bad_return_type.out" }, + { test: "typechecker/try/error_bad_return_type_result.abra", assertions: "typechecker/try/error_bad_return_type_result.out" }, + { test: "typechecker/try/error_bad_return_type_option.abra", assertions: "typechecker/try/error_bad_return_type_option.out" }, { test: "typechecker/try/error_return_type_err_mismatch.abra", assertions: "typechecker/try/error_return_type_err_mismatch.out" }, { test: "typechecker/try/error_return_type_err_mismatch_generic.abra", assertions: "typechecker/try/error_return_type_err_mismatch_generic.out" }, { test: "typechecker/try/error_untryable_type.abra", assertions: "typechecker/try/error_untryable_type.out" }, @@ -554,6 +557,8 @@ const TYPECHECKER_TESTS = [ { test: "typechecker/try/error_else_block_empty.abra", assertions: "typechecker/try/error_else_block_empty.out" }, { test: "typechecker/try/error_else_return_type_mismatch.1.abra", assertions: "typechecker/try/error_else_return_type_mismatch.1.out" }, { test: "typechecker/try/error_else_return_type_mismatch.2.abra", assertions: "typechecker/try/error_else_return_type_mismatch.2.out" }, + { test: "typechecker/try/error_else_return_type_mismatch.3.abra", assertions: "typechecker/try/error_else_return_type_mismatch.3.out" }, + { test: "typechecker/try/error_else_return_type_mismatch.4.abra", assertions: "typechecker/try/error_else_return_type_mismatch.4.out" }, { test: "typechecker/try/error_else_type_mismatch.abra", assertions: "typechecker/try/error_else_type_mismatch.out" }, // Type identifiers @@ -800,7 +805,8 @@ const COMPILER_TESTS = [ { test: "compiler/maps.abra" }, { test: "compiler/sets.abra" }, { test: "compiler/match.abra" }, - { test: "compiler/try.abra" }, + { test: "compiler/try_result.abra" }, + { test: "compiler/try_option.abra" }, { test: "compiler/process.abra", args: ['-f', 'bar', '--baz', 'qux'], env: { FOO: 'bar' } }, { test: "compiler/process_callstack.abra" }, { test: "compiler/json.abra" }, diff --git a/projects/compiler/test/typechecker/try/error_bad_return_type_option.abra b/projects/compiler/test/typechecker/try/error_bad_return_type_option.abra new file mode 100644 index 00000000..94a3c13b --- /dev/null +++ b/projects/compiler/test/typechecker/try/error_bad_return_type_option.abra @@ -0,0 +1,6 @@ +func foo(): Int? = Some(123) + +func bar(): Int { + val x = try foo() + x + 1 +} \ No newline at end of file diff --git a/projects/compiler/test/typechecker/try/error_bad_return_type_option.out b/projects/compiler/test/typechecker/try/error_bad_return_type_option.out new file mode 100644 index 00000000..68b0ca34 --- /dev/null +++ b/projects/compiler/test/typechecker/try/error_bad_return_type_option.out @@ -0,0 +1,8 @@ +Error at %FILE_NAME%:4:11 +Invalid location for try expression + | val x = try foo() + ^ +The containing function 'bar' has return type 'Int', which is incompatible with the try expression's type 'Int?'. + | func bar(): Int { + ^ +To be compatible, 'bar' must return an Option type diff --git a/projects/compiler/test/typechecker/try/error_bad_return_type.abra b/projects/compiler/test/typechecker/try/error_bad_return_type_result.abra similarity index 100% rename from projects/compiler/test/typechecker/try/error_bad_return_type.abra rename to projects/compiler/test/typechecker/try/error_bad_return_type_result.abra diff --git a/projects/compiler/test/typechecker/try/error_bad_return_type.out b/projects/compiler/test/typechecker/try/error_bad_return_type_result.out similarity index 72% rename from projects/compiler/test/typechecker/try/error_bad_return_type.out rename to projects/compiler/test/typechecker/try/error_bad_return_type_result.out index b66dca49..17d17cfb 100644 --- a/projects/compiler/test/typechecker/try/error_bad_return_type.out +++ b/projects/compiler/test/typechecker/try/error_bad_return_type_result.out @@ -5,4 +5,4 @@ Invalid location for try expression The containing function 'bar' has return type 'Int', which is incompatible with the try expression's type 'Result'. | func bar(): Int { ^ -To be compatible, the function must return a Result whose error type matches that of the try expression +To be compatible, 'bar' must return a Result whose error type matches that of the try expression diff --git a/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.3.abra b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.3.abra new file mode 100644 index 00000000..1595dbab --- /dev/null +++ b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.3.abra @@ -0,0 +1,7 @@ +func some(): Int? = Some(123) + +func f1(): Result { + val x = try some() else return some() + + Ok(x + 1) +} \ No newline at end of file diff --git a/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.3.out b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.3.out new file mode 100644 index 00000000..61d92ba0 --- /dev/null +++ b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.3.out @@ -0,0 +1,6 @@ +Error at %FILE_NAME%:4:38 +Return type mismatch for function 'f1' + | val x = try some() else return some() + ^ +Expected Result +but instead found Int? diff --git a/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.4.abra b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.4.abra new file mode 100644 index 00000000..e638d1f3 --- /dev/null +++ b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.4.abra @@ -0,0 +1,7 @@ +func some(): Result = Ok(123) + +func f1(): Int? { + val x = try some() else return some() + + Some(x + 1) +} \ No newline at end of file diff --git a/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.4.out b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.4.out new file mode 100644 index 00000000..db375aec --- /dev/null +++ b/projects/compiler/test/typechecker/try/error_else_return_type_mismatch.4.out @@ -0,0 +1,6 @@ +Error at %FILE_NAME%:4:38 +Return type mismatch for function 'f1' + | val x = try some() else return some() + ^ +Expected Int? +but instead found Result diff --git a/projects/compiler/test/typechecker/try/try.5.abra b/projects/compiler/test/typechecker/try/try.5.abra new file mode 100644 index 00000000..fe3f34c9 --- /dev/null +++ b/projects/compiler/test/typechecker/try/try.5.abra @@ -0,0 +1,7 @@ +func ok(): Result = Ok(123) + +func f1(): Int { + val x = try ok() else 123 + + x + 1 +} diff --git a/projects/compiler/test/typechecker/try/try.5.out.json b/projects/compiler/test/typechecker/try/try.5.out.json new file mode 100644 index 00000000..29a465c0 --- /dev/null +++ b/projects/compiler/test/typechecker/try/try.5.out.json @@ -0,0 +1,325 @@ +{ + "id": 3, + "name": "%FILE_NAME%", + "code": [ + { + "token": { + "position": [1, 1], + "kind": { + "name": "Func" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "functionDeclaration", + "function": { + "label": { "name": "ok", "position": [1, 6] }, + "scope": { + "name": "$root::module_3::ok", + "variables": [], + "functions": [], + "types": [] + }, + "kind": "FunctionKind.Standalone", + "typeParameters": [], + "parameters": [], + "returnType": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Result" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + }, + { + "kind": "primitive", + "primitive": "String" + } + ] + }, + "body": [ + { + "token": { + "position": [1, 36], + "kind": { + "name": "LParen" + } + }, + "type": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Result" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + }, + { + "kind": "primitive", + "primitive": "String" + } + ] + }, + "node": { + "kind": "invocation", + "invokee": { + "function": "Ok", + "type": { + "kind": "function", + "parameters": [ + { + "required": true, + "type": { + "kind": "generic", + "name": "V" + } + } + ], + "returnType": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Result" }, + "typeParams": [ + { + "kind": "generic", + "name": "V" + }, + { + "kind": "generic", + "name": "E" + } + ] + } + } + }, + "arguments": [ + { + "token": { + "position": [1, 37], + "kind": { + "name": "Int", + "value": 123 + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "literal", + "value": 123 + } + } + ] + } + } + ] + } + } + }, + { + "token": { + "position": [3, 1], + "kind": { + "name": "Func" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "functionDeclaration", + "function": { + "label": { "name": "f1", "position": [3, 6] }, + "scope": { + "name": "$root::module_3::f1", + "variables": [ + { + "label": { "name": "x", "position": [4, 7] }, + "mutable": false, + "type": { + "kind": "primitive", + "primitive": "Int" + } + } + ], + "functions": [], + "types": [] + }, + "kind": "FunctionKind.Standalone", + "typeParameters": [], + "parameters": [], + "returnType": { + "kind": "primitive", + "primitive": "Int" + }, + "body": [ + { + "token": { + "position": [4, 3], + "kind": { + "name": "Val" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "bindingDeclaration", + "pattern": { + "kind": "variable", + "label": { "name": "x", "position": [4, 7] } + }, + "variables": [ + { + "label": { "name": "x", "position": [4, 7] }, + "mutable": false, + "type": { + "kind": "primitive", + "primitive": "Int" + } + } + ], + "expr": { + "token": { + "position": [4, 11], + "kind": { + "name": "Try" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "try", + "expr": { + "token": { + "position": [4, 17], + "kind": { + "name": "LParen" + } + }, + "type": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Result" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + }, + { + "kind": "primitive", + "primitive": "String" + } + ] + }, + "node": { + "kind": "invocation", + "invokee": { + "function": "ok", + "type": { + "kind": "function", + "parameters": [], + "returnType": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Result" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + }, + { + "kind": "primitive", + "primitive": "String" + } + ] + } + } + }, + "arguments": [] + } + }, + "elseBindingPattern": null, + "elseBlock": [ + { + "token": { + "position": [4, 25], + "kind": { + "name": "Int", + "value": 123 + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "literal", + "value": 123 + } + } + ] + } + } + } + }, + { + "token": { + "position": [6, 5], + "kind": { + "name": "Plus" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "binary", + "op": "BinaryOp.Add", + "left": { + "token": { + "position": [6, 3], + "kind": { + "name": "Ident", + "value": "x" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "identifier", + "name": "x" + } + }, + "right": { + "token": { + "position": [6, 7], + "kind": { + "name": "Int", + "value": 1 + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "literal", + "value": 1 + } + } + } + } + ] + } + } + } + ] +} diff --git a/projects/compiler/test/typechecker/try/try.6.abra b/projects/compiler/test/typechecker/try/try.6.abra new file mode 100644 index 00000000..2c4a23f4 --- /dev/null +++ b/projects/compiler/test/typechecker/try/try.6.abra @@ -0,0 +1,13 @@ +func ok(): Int? = Some(123) + +func f1(): Int? { + val x = try ok() + + Some(x + 1) +} + +func f2(): Int { + val x = try ok() else 123 + + x + 1 +} diff --git a/projects/compiler/test/typechecker/try/try.6.out.json b/projects/compiler/test/typechecker/try/try.6.out.json new file mode 100644 index 00000000..7760bb58 --- /dev/null +++ b/projects/compiler/test/typechecker/try/try.6.out.json @@ -0,0 +1,486 @@ +{ + "id": 3, + "name": "%FILE_NAME%", + "code": [ + { + "token": { + "position": [1, 1], + "kind": { + "name": "Func" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "functionDeclaration", + "function": { + "label": { "name": "ok", "position": [1, 6] }, + "scope": { + "name": "$root::module_3::ok", + "variables": [], + "functions": [], + "types": [] + }, + "kind": "FunctionKind.Standalone", + "typeParameters": [], + "parameters": [], + "returnType": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + }, + "body": [ + { + "token": { + "position": [1, 23], + "kind": { + "name": "LParen" + } + }, + "type": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + }, + "node": { + "kind": "invocation", + "invokee": "Option.Some", + "arguments": [ + { + "token": { + "position": [1, 24], + "kind": { + "name": "Int", + "value": 123 + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "literal", + "value": 123 + } + } + ] + } + } + ] + } + } + }, + { + "token": { + "position": [3, 1], + "kind": { + "name": "Func" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "functionDeclaration", + "function": { + "label": { "name": "f1", "position": [3, 6] }, + "scope": { + "name": "$root::module_3::f1", + "variables": [ + { + "label": { "name": "x", "position": [4, 7] }, + "mutable": false, + "type": { + "kind": "primitive", + "primitive": "Int" + } + } + ], + "functions": [], + "types": [] + }, + "kind": "FunctionKind.Standalone", + "typeParameters": [], + "parameters": [], + "returnType": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + }, + "body": [ + { + "token": { + "position": [4, 3], + "kind": { + "name": "Val" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "bindingDeclaration", + "pattern": { + "kind": "variable", + "label": { "name": "x", "position": [4, 7] } + }, + "variables": [ + { + "label": { "name": "x", "position": [4, 7] }, + "mutable": false, + "type": { + "kind": "primitive", + "primitive": "Int" + } + } + ], + "expr": { + "token": { + "position": [4, 11], + "kind": { + "name": "Try" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "try", + "expr": { + "token": { + "position": [4, 17], + "kind": { + "name": "LParen" + } + }, + "type": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + }, + "node": { + "kind": "invocation", + "invokee": { + "function": "ok", + "type": { + "kind": "function", + "parameters": [], + "returnType": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + } + } + }, + "arguments": [] + } + } + } + } + } + }, + { + "token": { + "position": [6, 7], + "kind": { + "name": "LParen" + } + }, + "type": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + }, + "node": { + "kind": "invocation", + "invokee": "Option.Some", + "arguments": [ + { + "token": { + "position": [6, 10], + "kind": { + "name": "Plus" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "binary", + "op": "BinaryOp.Add", + "left": { + "token": { + "position": [6, 8], + "kind": { + "name": "Ident", + "value": "x" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "identifier", + "name": "x" + } + }, + "right": { + "token": { + "position": [6, 12], + "kind": { + "name": "Int", + "value": 1 + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "literal", + "value": 1 + } + } + } + } + ] + } + } + ] + } + } + }, + { + "token": { + "position": [9, 1], + "kind": { + "name": "Func" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "functionDeclaration", + "function": { + "label": { "name": "f2", "position": [9, 6] }, + "scope": { + "name": "$root::module_3::f2", + "variables": [ + { + "label": { "name": "x", "position": [10, 7] }, + "mutable": false, + "type": { + "kind": "primitive", + "primitive": "Int" + } + } + ], + "functions": [], + "types": [] + }, + "kind": "FunctionKind.Standalone", + "typeParameters": [], + "parameters": [], + "returnType": { + "kind": "primitive", + "primitive": "Int" + }, + "body": [ + { + "token": { + "position": [10, 3], + "kind": { + "name": "Val" + } + }, + "type": { + "kind": "primitive", + "primitive": "Unit" + }, + "node": { + "kind": "bindingDeclaration", + "pattern": { + "kind": "variable", + "label": { "name": "x", "position": [10, 7] } + }, + "variables": [ + { + "label": { "name": "x", "position": [10, 7] }, + "mutable": false, + "type": { + "kind": "primitive", + "primitive": "Int" + } + } + ], + "expr": { + "token": { + "position": [10, 11], + "kind": { + "name": "Try" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "try", + "expr": { + "token": { + "position": [10, 17], + "kind": { + "name": "LParen" + } + }, + "type": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + }, + "node": { + "kind": "invocation", + "invokee": { + "function": "ok", + "type": { + "kind": "function", + "parameters": [], + "returnType": { + "kind": "enumInstance", + "enum": { "moduleId": 2, "name": "Option" }, + "typeParams": [ + { + "kind": "primitive", + "primitive": "Int" + } + ] + } + } + }, + "arguments": [] + } + }, + "elseBindingPattern": null, + "elseBlock": [ + { + "token": { + "position": [10, 25], + "kind": { + "name": "Int", + "value": 123 + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "literal", + "value": 123 + } + } + ] + } + } + } + }, + { + "token": { + "position": [12, 5], + "kind": { + "name": "Plus" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "binary", + "op": "BinaryOp.Add", + "left": { + "token": { + "position": [12, 3], + "kind": { + "name": "Ident", + "value": "x" + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "identifier", + "name": "x" + } + }, + "right": { + "token": { + "position": [12, 7], + "kind": { + "name": "Int", + "value": 1 + } + }, + "type": { + "kind": "primitive", + "primitive": "Int" + }, + "node": { + "kind": "literal", + "value": 1 + } + } + } + } + ] + } + } + } + ] +}