aboutsummaryrefslogtreecommitdiff
path: root/src/test/java
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2023-11-07 17:17:57 +0000
committerMichael Williamson <mike@zwobble.org>2023-11-07 17:17:57 +0000
commitabf50f46fc99affe0f3b97e3bb2016648362ebc4 (patch)
tree2248c5d1a3f09c55947afb58fc65090e311f354f /src/test/java
parent1e6ea9f8b84625c46a09b16fc7f68d04205465bd (diff)
Type check resume statements properly
Diffstat (limited to 'src/test/java')
-rw-r--r--src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java4
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionResumeTests.java116
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java2
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java2
4 files changed, 122 insertions, 2 deletions
diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java
index adae966a..51b6c2b6 100644
--- a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java
+++ b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java
@@ -157,6 +157,10 @@ public class Untyped {
return new UntypedResumeNode(Optional.empty(), Sources.NULL);
}
+ public static UntypedResumeNode resume(UntypedExpressionNode expression) {
+ return new UntypedResumeNode(Optional.of(expression), Sources.NULL);
+ }
+
public static UntypedStringLiteralNode stringLiteral(String value) {
return new UntypedStringLiteralNode(value, Sources.NULL);
}
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionResumeTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionResumeTests.java
new file mode 100644
index 00000000..c3326020
--- /dev/null
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionResumeTests.java
@@ -0,0 +1,116 @@
+package org.shedlang.compiler.typechecker;
+
+import org.junit.jupiter.api.Test;
+import org.shedlang.compiler.ast.untyped.Untyped;
+import org.shedlang.compiler.ast.untyped.UntypedFunctionExpressionNode;
+import org.shedlang.compiler.typechecker.errors.CannotResumeOutsideOfEffectHandlerFieldError;
+import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError;
+import org.shedlang.compiler.typechecker.typesets.TypeSets;
+import org.shedlang.compiler.types.Effects;
+import org.shedlang.compiler.types.FunctionType;
+import org.shedlang.compiler.types.NamespaceName;
+import org.shedlang.compiler.types.Types;
+import org.shedlang.compiler.util.P;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*;
+import static org.zwobble.precisely.AssertThat.assertThat;
+import static org.zwobble.precisely.Matchers.equalTo;
+
+public class TypeCheckerFunctionResumeTests {
+ @Test
+ public void expressionIsTypeChecked() {
+ var untyped = Untyped.resume(Untyped.stringLiteral("hello"));
+ var context = TypeCheckerContext.EMPTY
+ .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.STRING));
+
+ var typed = TypeChecker.typeCheckFunctionStatement(untyped, context);
+
+ assertThat(typed.value(), isTypedResumeNode(isTypedStringLiteralNode("hello")));
+ }
+
+ @Test
+ public void whenExpressionIsAbsentThenUnitIsReturned() {
+ var untyped = Untyped.resume();
+ var context = TypeCheckerContext.EMPTY
+ .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.UNIT));
+
+ var typed = TypeChecker.typeCheckFunctionStatement(untyped, context);
+
+ assertThat(typed.value(), isTypedResumeNode(isTypedUnitLiteralNode()));
+ }
+
+ @Test
+ public void whenNotInFunctionThenResumeIsForbidden() {
+ var untyped = Untyped.resume(Untyped.stringLiteral("hello"));
+ var context = TypeCheckerContext.EMPTY
+ .enterNamespace(NamespaceName.of("Example"), P.map());
+
+ assertThrows(
+ CannotResumeOutsideOfEffectHandlerFieldError.class,
+ () -> TypeChecker.typeCheckFunctionStatement(untyped, context)
+ );
+ }
+
+ @Test
+ public void whenInFunctionThenResumeIsForbidden() {
+ var untyped = Untyped.resume(Untyped.stringLiteral("hello"));
+ var context = TypeCheckerContext.EMPTY
+ .enterFunction(P.map(), Effects.NONE, ReturnTypeState.explicit(Types.STRING));
+
+ assertThrows(
+ CannotResumeOutsideOfEffectHandlerFieldError.class,
+ () -> TypeChecker.typeCheckFunctionStatement(untyped, context)
+ );
+ }
+
+ @Test
+ public void canResumeWIthSubtypeOfResumeType() {
+ var untyped = Untyped.resume(Untyped.stringLiteral("hello"));
+ var context = TypeCheckerContext.EMPTY
+ .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.TOP));
+
+ var typed = TypeChecker.typeCheckFunctionStatement(untyped, context);
+
+ assertThat(typed.value(), isTypedResumeNode(isTypedStringLiteralNode("hello")));
+ }
+
+ @Test
+ public void cannotResumeWithNonSubtypeOfResumeType() {
+ var untyped = Untyped.resume(Untyped.stringLiteral("hello"));
+ var context = TypeCheckerContext.EMPTY
+ .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.INT));
+
+ var error = assertThrows(
+ UnexpectedTypeError.class,
+ () -> TypeChecker.typeCheckFunctionStatement(untyped, context)
+ );
+
+ assertThat(error.expected(), equalTo(TypeSets.type(Types.INT)));
+ assertThat(error.actual(), equalTo(Types.STRING));
+ }
+
+ @Test
+ public void resumeTypeIsUsedAsTypeHintForExpression() {
+ var untyped = Untyped.resume(
+ UntypedFunctionExpressionNode.builder()
+ .addPositionalParam("x", Optional.empty())
+ .withoutReturnType()
+ .build()
+ );
+ var resumeType = FunctionType.builder()
+ .addPositionalParam(Types.STRING)
+ .withReturnType(Types.UNIT)
+ .build();
+ var context = TypeCheckerContext.EMPTY
+ .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(resumeType));
+
+ var typed = TypeChecker.typeCheckFunctionStatement(untyped, context);
+
+ assertThat(typed.value(), isTypedResumeNode(
+ isTypedFunctionExpressionNode().withType(resumeType)
+ ));
+ }
+}
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java
index f0c2daa7..8fe03d32 100644
--- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionReturnTests.java
@@ -59,7 +59,7 @@ public class TypeCheckerFunctionReturnTests {
public void whenInEffectHandlerThenReturnIsForbidden() {
var untyped = Untyped.functionReturn(Untyped.stringLiteral("hello"));
var context = TypeCheckerContext.EMPTY
- .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.UNIT));
+ .enterFunction(P.map(), Effects.NONE, ReturnTypeState.effectHandler(Types.STRING));
assertThrows(
CannotReturnFromEffectHandlerFieldError.class,
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java
index cffae60a..169816dc 100644
--- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java
@@ -366,7 +366,7 @@ public class TypeCheckerTryTests {
.addHandlerField(
UntypedFunctionDefinitionNode.builder()
.withName("effectField")
- .addStatement(Untyped.expressionStatement(
+ .addStatement(Untyped.resume(
UntypedCallNode.builder()
.withReceiver(Untyped.reference("f"))
.withHasEffects(true)