diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-07-05 22:02:23 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-07-05 22:02:23 +0100 |
| commit | 21b6a5c800dda09ba859beea02037718eda108e3 (patch) | |
| tree | 53d491a3c3af67d4794e68d4c11ca1481321a2a4 /src/test/java | |
| parent | fa240b137df27cf6a938eb0432ef62ad7ba718e4 (diff) | |
Type check returns
Diffstat (limited to 'src/test/java')
9 files changed, 118 insertions, 37 deletions
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 bc047b82..4143a91a 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java @@ -103,6 +103,15 @@ public class TypedNodeMatchers { ); } + public static Matcher<TypedFunctionStatementNode> isTypedReturnNode( + Matcher<TypedExpressionNode> expression + ) { + return instanceOf( + TypedReturnNode.class, + has("expression", x -> x.expression(), expression) + ); + } + public static Matcher<TypedExpressionNode> isTypedStringLiteralNode(String value) { return instanceOf( TypedStringLiteralNode.class, 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 4d61dfc2..ed78e1d4 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java @@ -65,6 +65,10 @@ public class Untyped { return new UntypedReferenceNode(name, Sources.NULL); } + public static UntypedReturnNode return_(UntypedExpressionNode expression) { + return new UntypedReturnNode(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/TypeCheckerCallTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java index 6f1c5c99..33cfda12 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java @@ -30,7 +30,7 @@ public class TypeCheckerCallTests { .withReturnType(Types.STRING) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var typed = TypeChecker.typeCheckExpression(untyped, context); @@ -51,7 +51,7 @@ public class TypeCheckerCallTests { List.of() ); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", Types.STRING))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", Types.STRING))); var error = assertThrows( UnexpectedTypeError.class, @@ -76,7 +76,7 @@ public class TypeCheckerCallTests { .addPositionalParam(Types.INT) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var typed = TypeChecker.typeCheckExpression(untyped, context); @@ -105,7 +105,7 @@ public class TypeCheckerCallTests { .addNamedParam("y", Types.INT) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var typed = TypeChecker.typeCheckExpression(untyped, context); @@ -131,7 +131,7 @@ public class TypeCheckerCallTests { .addPositionalParam(Types.INT) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var error = assertThrows( UnexpectedTypeError.class, @@ -155,7 +155,7 @@ public class TypeCheckerCallTests { .addNamedParam("x", Types.INT) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var error = assertThrows( UnexpectedTypeError.class, @@ -177,7 +177,7 @@ public class TypeCheckerCallTests { var functionType = FunctionType.builder() .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var error = assertThrows( WrongNumberOfPositionalArgsError.class, @@ -199,7 +199,7 @@ public class TypeCheckerCallTests { .addNamedParam("x", Types.STRING) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var error = assertThrows( MissingNamedArgError.class, @@ -221,7 +221,7 @@ public class TypeCheckerCallTests { var functionType = FunctionType.builder() .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var error = assertThrows( UnexpectedNamedArgError.class, @@ -245,7 +245,7 @@ public class TypeCheckerCallTests { .addNamedParam("x", Types.STRING) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var error = assertThrows( CannotPassNamedArgTwiceError.class, @@ -266,7 +266,7 @@ public class TypeCheckerCallTests { .withReturnType(Types.UNIT) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); var error = assertThrows( CallIsMissingEffectsError.class, @@ -287,7 +287,7 @@ public class TypeCheckerCallTests { .withReturnType(Types.UNIT) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("f", functionType))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("f", functionType))); assertThrows( FunctionHasNoEffectsError.class, @@ -308,7 +308,8 @@ public class TypeCheckerCallTests { var context = TypeCheckerContext.EMPTY .enterFunction( P.map(Map.entry("f", TypeCheckerContext.defined(functionType))), - Effects.IO + Effects.IO, + Types.UNIT ); var typed = TypeChecker.typeCheckExpression(untyped, context); @@ -334,7 +335,8 @@ public class TypeCheckerCallTests { var context = TypeCheckerContext.EMPTY .enterFunction( P.map(Map.entry("f", TypeCheckerContext.defined(functionType))), - Effects.NONE + Effects.NONE, + Types.UNIT ); var error = assertThrows( diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java index 6dea7d63..a03a1f44 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java @@ -25,7 +25,7 @@ public class TypeCheckerFunctionDefinitionTests { .withReturnType(Untyped.typeLevelReference("Unit")) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); var typed = TypeChecker.typeCheckNamespaceStatement(untyped, context); @@ -49,7 +49,7 @@ public class TypeCheckerFunctionDefinitionTests { .withReturnType(Untyped.typeLevelReference("Unit")) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Int", Types.metaType(Types.INT)), Map.entry("String", Types.metaType(Types.STRING)), Map.entry("Unit", Types.metaType(Types.UNIT)) @@ -82,7 +82,7 @@ public class TypeCheckerFunctionDefinitionTests { .withReturnType(Untyped.typeLevelReference("Unit")) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Io", Types.effectType(Effects.IO)), Map.entry("Unit", Types.metaType(Types.UNIT)) )); @@ -103,7 +103,7 @@ public class TypeCheckerFunctionDefinitionTests { .addStatement(Untyped.expressionStatement(Untyped.stringLiteral("hello"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); var typed = TypeChecker.typeCheckNamespaceStatement(untyped, context); @@ -124,7 +124,7 @@ public class TypeCheckerFunctionDefinitionTests { .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("String", Types.metaType(Types.STRING)), Map.entry("Unit", Types.metaType(Types.UNIT)) )); @@ -148,7 +148,7 @@ public class TypeCheckerFunctionDefinitionTests { .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("String", Types.metaType(Types.STRING)), Map.entry("Unit", Types.metaType(Types.UNIT)) )); @@ -181,7 +181,7 @@ public class TypeCheckerFunctionDefinitionTests { .withReturnType(Types.UNIT) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("f", functionType), Map.entry("Io", Types.effectType(Effects.IO)), Map.entry("Unit", Types.metaType(Types.UNIT)) @@ -205,7 +205,7 @@ public class TypeCheckerFunctionDefinitionTests { .addStatement(Untyped.expressionStatement(Untyped.reference("y"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Unit", Types.metaType(Types.UNIT)), Map.entry("x", Types.STRING), Map.entry("y", Types.STRING) @@ -230,7 +230,7 @@ public class TypeCheckerFunctionDefinitionTests { .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("String", Types.metaType(Types.STRING)), Map.entry("Unit", Types.metaType(Types.UNIT)) )); diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java index c4851b13..3ae36edb 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java @@ -20,7 +20,7 @@ public class TypeCheckerNamespaceTests { public void canTypeCheckEmptyNamespace() { var untyped = Untyped.namespace(List.of()); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); var result = TypeChecker.typeCheckNamespace(untyped, context); @@ -39,7 +39,7 @@ public class TypeCheckerNamespaceTests { .build() )); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); var result = TypeChecker.typeCheckNamespace(untyped, context); @@ -67,7 +67,7 @@ public class TypeCheckerNamespaceTests { ) ); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Unit", Types.metaType(Types.UNIT)))); var result = TypeChecker.typeCheckNamespace(untyped, context); diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReferenceTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReferenceTests.java index 14440354..0992648d 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReferenceTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReferenceTests.java @@ -18,7 +18,7 @@ public class TypeCheckerReferenceTests { public void whenNameIsBoundInEnvironmentThenTypeIsUsedFromEnvironment() { var untyped = Untyped.reference("x"); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("x", Types.STRING))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("x", Types.STRING))); var typed = TypeChecker.typeCheckExpression(untyped, context); @@ -29,7 +29,7 @@ public class TypeCheckerReferenceTests { public void whenNameIsNotBoundInEnvironmentThenErrorIsThrown() { var untyped = Untyped.reference("x"); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("y", Types.STRING))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("y", Types.STRING))); var error = assertThrows( UnknownVariableError.class, diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReturnTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReturnTests.java new file mode 100644 index 00000000..08fb61fd --- /dev/null +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerReturnTests.java @@ -0,0 +1,66 @@ +package org.shedlang.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.typechecker.errors.CannotReturnOutsideOfFunction; +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.Types; +import org.shedlang.compiler.util.P; + +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 TypeCheckerReturnTests { + @Test + public void expressionIsTypeChecked() { + var untyped = Untyped.return_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, Types.STRING); + + var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); + + assertThat(typed.value(), isTypedReturnNode(isTypedStringLiteralNode("hello"))); + } + + @Test + public void whenNotInFunctionThenReturnIsForbidden() { + var untyped = Untyped.return_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterNamespace(P.map()); + + assertThrows( + CannotReturnOutsideOfFunction.class, + () -> TypeChecker.typeCheckFunctionStatement(untyped, context) + ); + } + + @Test + public void canReturnSubtypeOfReturnType() { + var untyped = Untyped.return_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, Types.TOP); + + var typed = TypeChecker.typeCheckFunctionStatement(untyped, context); + + assertThat(typed.value(), isTypedReturnNode(isTypedStringLiteralNode("hello"))); + } + + @Test + public void cannotReturnNonSubtypeOfReturnType() { + var untyped = Untyped.return_(Untyped.stringLiteral("hello")); + var context = TypeCheckerContext.EMPTY + .enterFunction(P.map(), Effects.NONE, 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)); + } +} diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java index 589f9f1d..2c386dd3 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java @@ -21,7 +21,7 @@ public class TypeCheckerTypeLevelReferenceTests { public void whenNameIsBoundInEnvironmentThenTypeIsUsedFromEnvironment() { var untyped = Untyped.typeLevelReference("String"); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("String", Types.metaType(Types.STRING)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("String", Types.metaType(Types.STRING)))); var typed = TypeChecker.typeCheckTypeLevelExpression(TypeSets.META_TYPE, untyped, context); @@ -32,7 +32,7 @@ public class TypeCheckerTypeLevelReferenceTests { public void whenNameRefersToNonTypeLevelValueThenErrorIsThrown() { var untyped = Untyped.typeLevelReference("String"); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("String", Types.STRING))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("String", Types.STRING))); var error = assertThrows( UnexpectedTypeError.class, @@ -47,7 +47,7 @@ public class TypeCheckerTypeLevelReferenceTests { public void whenNameRefersToValueOfWrongTypeThenErrorIsThrown() { var untyped = Untyped.typeLevelReference("Io"); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("Io", Types.effectType(Effects.IO)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Io", Types.effectType(Effects.IO)))); var error = assertThrows( UnexpectedTypeError.class, @@ -62,7 +62,7 @@ public class TypeCheckerTypeLevelReferenceTests { public void whenNameIsNotBoundInEnvironmentThenErrorIsThrown() { var untyped = Untyped.typeLevelReference("String"); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map(Map.entry("Int", Types.metaType(Types.INT)))); + .enterNamespaceWithDefinedVariables(P.map(Map.entry("Int", Types.metaType(Types.INT)))); var error = assertThrows( UnknownVariableError.class, diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java index d80522dc..447ea672 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerValTests.java @@ -48,7 +48,7 @@ public class TypeCheckerValTests { .addStatement(Untyped.expressionStatement(Untyped.reference("x"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Unit", Types.metaType(Types.UNIT)) )); @@ -71,7 +71,7 @@ public class TypeCheckerValTests { .addStatement(Untyped.val(Untyped.targetVariable("y"), Untyped.reference("x"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Unit", Types.metaType(Types.UNIT)) )); @@ -94,7 +94,7 @@ public class TypeCheckerValTests { .addStatement(Untyped.val(Untyped.targetVariable("x"), Untyped.stringLiteral("hello"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Unit", Types.metaType(Types.UNIT)) )); @@ -113,7 +113,7 @@ public class TypeCheckerValTests { .addStatement(Untyped.val(Untyped.targetVariable("x"), Untyped.stringLiteral("hello"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Unit", Types.metaType(Types.UNIT)), Map.entry("x", Types.INT) )); @@ -133,7 +133,7 @@ public class TypeCheckerValTests { .addStatement(Untyped.val(Untyped.targetVariable("x"), Untyped.stringLiteral("hello"))) .build(); var context = TypeCheckerContext.EMPTY - .enterBlockWithDefinedVariables(P.map( + .enterNamespaceWithDefinedVariables(P.map( Map.entry("Unit", Types.metaType(Types.UNIT)) )); |
