From 0f40994a60b060bddba5f08f5f43e4c49099c015 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 1 Oct 2023 19:37:52 +0100 Subject: Add basic type checking for when expressions --- .../compiler/ast/typed/TypedNodeMatchers.java | 21 +++++++ .../org/shedlang/compiler/ast/untyped/Untyped.java | 14 +++++ .../typechecker/TypeCheckerAssignTests.java | 14 ----- .../TypeCheckerFunctionDefinitionTests.java | 19 ++---- .../TypeCheckerStructDefinitionTests.java | 18 +----- .../TypeCheckerSumTypeDefinitionTests.java | 18 +----- .../compiler/typechecker/TypeCheckerValTests.java | 19 ++---- .../compiler/typechecker/TypeCheckerVarTests.java | 15 +---- .../compiler/typechecker/TypeCheckerWhenTests.java | 68 ++++++++++++++++++++++ 9 files changed, 118 insertions(+), 88 deletions(-) create mode 100644 src/test/java/org/shedlang/compiler/typechecker/TypeCheckerWhenTests.java (limited to 'src/test/java') diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java index 1f64f631..c5281090 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java @@ -348,6 +348,27 @@ public class TypedNodeMatchers { ); } + public static Matcher isTypedWhenNode( + Matcher expression, + Matcher> cases + ) { + return instanceOf( + TypedWhenNode.class, + has("expression", x -> x.expression(), expression), + has("cases", x -> x.cases(), cases) + ); + } + + public static Matcher isTypedWhenCaseNode( + Matcher> caseType, + Matcher body + ) { + return allOf( + has("caseType", x -> x.caseType(), caseType), + has("body", x -> x.body(), body) + ); + } + public static Matcher isTypedWhileNode( Matcher condition, Matcher body 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 21335011..09067087 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java @@ -207,6 +207,20 @@ public class Untyped { return new UntypedAssignNode(targetVal(targetName), expression, Sources.NULL); } + public static UntypedWhenNode when( + UntypedExpressionNode expression, + List cases + ) { + return new UntypedWhenNode(expression, cases, Sources.NULL); + } + + public static UntypedWhenCaseNode whenCase( + UntypedTypeLevelExpressionNode caseType, + UntypedBlockNode body + ) { + return new UntypedWhenCaseNode(caseType, body, Sources.NULL); + } + public static UntypedWhileNode while_( UntypedExpressionNode condition, UntypedBlockNode body diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java index cc161b56..ff2e3f1d 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerAssignTests.java @@ -2,10 +2,8 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.typed.TypedFunctionStatementNode; -import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.ast.untyped.UntypedFunctionStatementNode; -import org.shedlang.compiler.ast.untyped.UntypedNamespaceStatementNode; import org.shedlang.compiler.typechecker.errors.CanOnlyAssignToVarError; import org.shedlang.compiler.typechecker.errors.TargetTupleHasWrongElementCount; import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError; @@ -177,16 +175,4 @@ public class TypeCheckerAssignTests { return TypeChecker.typeCheckFunctionStatement(untyped, context); } - - private static TypeCheckerResult typeCheckNamespaceStatement( - UntypedNamespaceStatementNode untyped, - TypeCheckerContext context - ) { - var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); - var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - - context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); - - return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); - } } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java index d6a600bc..78b89e90 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java @@ -1,13 +1,15 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; -import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.ast.untyped.UntypedCallNode; import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode; import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError; import org.shedlang.compiler.typechecker.typesets.TypeSets; -import org.shedlang.compiler.types.*; +import org.shedlang.compiler.types.Effects; +import org.shedlang.compiler.types.FunctionType; +import org.shedlang.compiler.types.NamedParam; +import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; import java.util.List; @@ -15,6 +17,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; @@ -383,16 +386,4 @@ public class TypeCheckerFunctionDefinitionTests { ))) ); } - - private static TypeCheckerResult typeCheckNamespaceStatement( - UntypedFunctionDefinitionNode untyped, - TypeCheckerContext context - ) { - var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); - var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - - context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); - - return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); - } } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java index 53c5d5e8..344cfa66 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java @@ -1,9 +1,7 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; -import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.Untyped; -import org.shedlang.compiler.ast.untyped.UntypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.UntypedStructDefinitionNode; import org.shedlang.compiler.typechecker.errors.CannotDefineStructOutsideOfNamespace; import org.shedlang.compiler.types.NamespaceName; @@ -14,6 +12,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; import static org.shedlang.compiler.types.TypeMatchers.isStructType; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; @@ -30,7 +29,7 @@ public class TypeCheckerStructDefinitionTests { assertThrows( CannotDefineStructOutsideOfNamespace.class, - () -> TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context) + () -> TypeChecker.typeCheckStepsNamespaceStatement(untyped).typeCheckAllPhases(context) ); } @@ -126,17 +125,4 @@ public class TypeCheckerStructDefinitionTests { )) ); } - - private static TypeCheckerResult typeCheckNamespaceStatement( - UntypedNamespaceStatementNode untyped, - TypeCheckerContext context - ) { - // TODO: remove duplication of this method - var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); - var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - - context = context.enterNamespace(NAMESPACE_NAME, environment.build()); - - return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); - } } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerSumTypeDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerSumTypeDefinitionTests.java index d70d3660..f93cea14 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerSumTypeDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerSumTypeDefinitionTests.java @@ -1,9 +1,7 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; -import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.Untyped; -import org.shedlang.compiler.ast.untyped.UntypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.UntypedSumTypeDefinitionNode; import org.shedlang.compiler.typechecker.errors.CannotDefineSumTypeOutsideOfNamespace; import org.shedlang.compiler.types.NamespaceName; @@ -14,6 +12,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.isTypedSumTypeDefinitionNode; +import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; @@ -29,7 +28,7 @@ public class TypeCheckerSumTypeDefinitionTests { assertThrows( CannotDefineSumTypeOutsideOfNamespace.class, - () -> TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context) + () -> TypeChecker.typeCheckStepsNamespaceStatement(untyped).typeCheckAllPhases(context) ); } @@ -82,17 +81,4 @@ public class TypeCheckerSumTypeDefinitionTests { )) ); } - - private static TypeCheckerResult typeCheckNamespaceStatement( - UntypedNamespaceStatementNode untyped, - TypeCheckerContext context - ) { - // TODO: remove duplication of this method - var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); - var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - - context = context.enterNamespace(NAMESPACE_NAME, environment.build()); - - return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); - } } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java index 9fd948ba..2688a4c1 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java @@ -2,8 +2,10 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.typed.TypedFunctionStatementNode; -import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; -import org.shedlang.compiler.ast.untyped.*; +import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode; +import org.shedlang.compiler.ast.untyped.UntypedFunctionStatementNode; +import org.shedlang.compiler.ast.untyped.UntypedNamespaceNode; import org.shedlang.compiler.typechecker.errors.DuplicateVariableName; import org.shedlang.compiler.typechecker.errors.ReferenceBeforeDefinitionError; import org.shedlang.compiler.types.NamespaceName; @@ -14,6 +16,7 @@ import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; @@ -157,16 +160,4 @@ public class TypeCheckerValTests { return TypeChecker.typeCheckFunctionStatement(untyped, context); } - - private static TypeCheckerResult typeCheckNamespaceStatement( - UntypedNamespaceStatementNode untyped, - TypeCheckerContext context - ) { - var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); - var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - - context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); - - return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); - } } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java index 466ab16c..53e2e65f 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerVarTests.java @@ -2,11 +2,9 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.typed.TypedFunctionStatementNode; -import org.shedlang.compiler.ast.typed.TypedNamespaceStatementNode; import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode; import org.shedlang.compiler.ast.untyped.UntypedFunctionStatementNode; -import org.shedlang.compiler.ast.untyped.UntypedNamespaceStatementNode; import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; @@ -14,6 +12,7 @@ import org.shedlang.compiler.util.P; import java.util.Map; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.anything; import static org.zwobble.precisely.Matchers.isSequence; @@ -65,16 +64,4 @@ public class TypeCheckerVarTests { return TypeChecker.typeCheckFunctionStatement(untyped, context); } - - private static TypeCheckerResult typeCheckNamespaceStatement( - UntypedNamespaceStatementNode untyped, - TypeCheckerContext context - ) { - var declaredVariables = DeclaredVariables.forNamespaceStatement(untyped, P.vector()); - var environment = EnvironmentBuilder.EMPTY.addUndefined(declaredVariables); - - context = context.enterNamespace(NamespaceName.of("Example"), environment.build()); - - return TypeChecker.typeCheckNamespaceStatement(untyped).typeCheckAllPhases(context); - } } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerWhenTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerWhenTests.java new file mode 100644 index 00000000..4fdaf950 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerWhenTests.java @@ -0,0 +1,68 @@ +package org.shedlang.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.types.*; +import org.shedlang.compiler.util.P; + +import java.util.List; +import java.util.Map; + +import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.isSequence; + +public class TypeCheckerWhenTests { + private static final NamespaceName NAMESPACE_NAME = NamespaceName.of("Example", "Project"); + private static final SumType SUM_TYPE = Types.sum(NAMESPACE_NAME, "SumType"); + private static final StructType VARIANT_TYPE_1 = Types.struct(NAMESPACE_NAME, "Variant1"); + private static final StructType VARIANT_TYPE_2 = Types.struct(NAMESPACE_NAME, "Variant2"); + + @Test + public void whenAllCasesAreHandledThenExpressionTypeChecks() { + var untyped = Untyped.when( + Untyped.reference("x"), + List.of( + Untyped.whenCase( + Untyped.typeLevelReference(VARIANT_TYPE_1.name()), + Untyped.block(List.of( + Untyped.blockReturn(Untyped.stringLiteral("a")) + )) + ), + Untyped.whenCase( + Untyped.typeLevelReference(VARIANT_TYPE_2.name()), + Untyped.block(List.of( + Untyped.blockReturn(Untyped.stringLiteral("b")) + )) + ) + ) + ); + var context = TypeCheckerContext.EMPTY + .addSumTypeInfo(SUM_TYPE, new SumTypeInfo(P.orderedSet(VARIANT_TYPE_1, VARIANT_TYPE_2))) + .enterGlobal(P.map( + Map.entry("x", SUM_TYPE), + Map.entry(VARIANT_TYPE_1.name(), Types.metaType(VARIANT_TYPE_1)), + Map.entry(VARIANT_TYPE_2.name(), Types.metaType(VARIANT_TYPE_2)) + )); + + var result = TypeChecker.typeCheckExpression(untyped, context); + + assertThat(result.value(), isTypedWhenNode( + isTypedReferenceNode("x", SUM_TYPE), + isSequence( + isTypedWhenCaseNode( + isTypedTypeLevelReferenceNode("Variant1", VARIANT_TYPE_1), + isTypedBlockNode(isSequence( + isTypedBlockReturnNode(isTypedStringLiteralNode("a")) + )) + ), + isTypedWhenCaseNode( + isTypedTypeLevelReferenceNode("Variant2", VARIANT_TYPE_2), + isTypedBlockNode(isSequence( + isTypedBlockReturnNode(isTypedStringLiteralNode("b")) + )) + ) + ) + )); + } +} -- cgit v1.2.3