From 813d4b15f404ace15684a474404ae88563d27579 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Wed, 19 Jul 2023 14:44:13 +0100 Subject: Bodge together proper checks for dependencies between statements --- .../typechecker/TypeCheckerNamespaceTests.java | 173 ++++++++++++++++++++- 1 file changed, 169 insertions(+), 4 deletions(-) (limited to 'src/test/java') diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java index 03db0b54..e9e5aba5 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java @@ -3,17 +3,17 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.untyped.Untyped; import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode; +import org.shedlang.compiler.typechecker.errors.ReferenceBeforeDefinitionError; 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.isTypedFunctionDefinitionNode; -import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.isTypedNamespaceNode; +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; -import static org.zwobble.precisely.Matchers.isSequence; +import static org.zwobble.precisely.Matchers.*; public class TypeCheckerNamespaceTests { @Test @@ -118,6 +118,171 @@ public class TypeCheckerNamespaceTests { ); } + @Test + public void functionCanReferenceValDefinedEarlier() { + var untyped = Untyped.namespace(List.of( + Untyped.val(Untyped.targetVariable("x"), Untyped.boolLiteral(true)), + UntypedFunctionDefinitionNode.builder() + .withName("f") + .withReturnType(Untyped.typeLevelReference("Unit")) + .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) + .build() + )); + var context = TypeCheckerContext.EMPTY + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var result = TypeChecker.typeCheckNamespace(untyped, context); + + assertThat( + result.value(), + isTypedNamespaceNode() + .withBody(isSequence( + isTypedValNode(isTypedTargetVariable("x"), isTypedBoolLiteralNode(true)), + isTypedFunctionDefinitionNode().withName("f") + ) + ) + ); + } + + @Test + public void functionCannotReferenceValDefinedLater() { + var untyped = Untyped.namespace(List.of( + UntypedFunctionDefinitionNode.builder() + .withName("f") + .withReturnType(Untyped.typeLevelReference("Unit")) + .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) + .build(), + Untyped.val(Untyped.targetVariable("x"), Untyped.boolLiteral(true)) + )); + var context = TypeCheckerContext.EMPTY + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var error = assertThrows( + ReferenceBeforeDefinitionError.class, + () -> TypeChecker.typeCheckNamespace(untyped, context) + ); + + assertThat(error.variableName(), equalTo("x")); + } + + @Test + public void valCanReferenceFunctionDefinedEarlier() { + var untyped = Untyped.namespace(List.of( + UntypedFunctionDefinitionNode.builder() + .withName("f") + .withReturnType(Untyped.typeLevelReference("Unit")) + .build(), + Untyped.val(Untyped.targetVariable("x"), Untyped.call(Untyped.reference("f"), List.of())) + )); + var context = TypeCheckerContext.EMPTY + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var result = TypeChecker.typeCheckNamespace(untyped, context); + + assertThat( + result.value(), + isTypedNamespaceNode() + .withBody(isSequence( + isTypedFunctionDefinitionNode().withName("f"), + isTypedValNode(isTypedTargetVariable("x"), anything()) + )) + ); + } + + @Test + public void valCanReferenceFunctionDefinedLater() { + var untyped = Untyped.namespace(List.of( + Untyped.val(Untyped.targetVariable("x"), Untyped.call(Untyped.reference("f"), List.of())), + UntypedFunctionDefinitionNode.builder() + .withName("f") + .withReturnType(Untyped.typeLevelReference("Unit")) + .build() + )); + var context = TypeCheckerContext.EMPTY + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var result = TypeChecker.typeCheckNamespace(untyped, context); + + assertThat( + result.value(), + isTypedNamespaceNode() + .withBody(isSequence( + isTypedValNode(isTypedTargetVariable("x"), anything()), + isTypedFunctionDefinitionNode().withName("f") + )) + ); + } + + @Test + public void valCanReferenceFunctionThatUsesValDefinedEarlier() { + var untyped = Untyped.namespace(List.of( + Untyped.val(Untyped.targetVariable("x"), Untyped.boolLiteral(true)), + Untyped.val(Untyped.targetVariable("y"), Untyped.call(Untyped.reference("f"), List.of())), + UntypedFunctionDefinitionNode.builder() + .withName("f") + .withReturnType(Untyped.typeLevelReference("Unit")) + .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) + .build() + )); + var context = TypeCheckerContext.EMPTY + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var result = TypeChecker.typeCheckNamespace(untyped, context); + + assertThat( + result.value(), + isTypedNamespaceNode() + .withBody(isSequence( + isTypedValNode(isTypedTargetVariable("x"), anything()), + isTypedValNode(isTypedTargetVariable("y"), anything()), + isTypedFunctionDefinitionNode().withName("f") + )) + ); + } + + @Test + public void valCannotReferenceFunctionThatUsesThatVal() { + var untyped = Untyped.namespace(List.of( + Untyped.val(Untyped.targetVariable("x"), Untyped.call(Untyped.reference("f"), List.of())), + UntypedFunctionDefinitionNode.builder() + .withName("f") + .withReturnType(Untyped.typeLevelReference("Unit")) + .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) + .build() + )); + var context = TypeCheckerContext.EMPTY + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var error = assertThrows( + ReferenceBeforeDefinitionError.class, + () -> TypeChecker.typeCheckNamespace(untyped, context) + ); + + assertThat(error.variableName(), equalTo("x")); + } + + @Test + public void valCannotReferenceFunctionThatUsesValDefinedLater() { + var untyped = Untyped.namespace(List.of( + Untyped.val(Untyped.targetVariable("x"), Untyped.call(Untyped.reference("f"), List.of())), + Untyped.val(Untyped.targetVariable("y"), Untyped.boolLiteral(true)), + UntypedFunctionDefinitionNode.builder() + .withName("f") + .withReturnType(Untyped.typeLevelReference("Unit")) + .addStatement(Untyped.expressionStatement(Untyped.reference("y"))) + .build() + )); + var context = TypeCheckerContext.EMPTY + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + + var error = assertThrows( + ReferenceBeforeDefinitionError.class, + () -> TypeChecker.typeCheckNamespace(untyped, context) + ); + + assertThat(error.variableName(), equalTo("y")); + } + @Test public void typesOfFieldsAreStored() { var namespaceName = NamespaceName.of("Example"); -- cgit v1.2.3