diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-12-23 16:49:29 +0000 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-12-23 16:49:29 +0000 |
| commit | 6ea1e107ed1358e809e24681bc22284d4aa7e630 (patch) | |
| tree | b19f448bf5fbdda2062b9a30a4a8cf29cb56ad72 /src/test/java | |
| parent | 1886fc2b214bb2c5bfc4cb1ca3d49975c76da0d6 (diff) | |
Create type constructor for structs with type params
Diffstat (limited to 'src/test/java')
5 files changed, 163 insertions, 3 deletions
diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedStructDefinitionNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedStructDefinitionNodeMatcher.java index 46f0438f..6dd41fe8 100644 --- a/src/test/java/org/shedlang/compiler/ast/typed/TypedStructDefinitionNodeMatcher.java +++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedStructDefinitionNodeMatcher.java @@ -3,11 +3,13 @@ package org.shedlang.compiler.ast.typed; import org.pcollections.PVector; import org.shedlang.compiler.testing.MatcherBuilder; import org.shedlang.compiler.types.StructType; -import org.shedlang.compiler.types.UserDefinedEffect; +import org.shedlang.compiler.types.TypeConstructor; +import org.shedlang.compiler.types.TypeOrConstructor; import org.shedlang.compiler.util.P; import org.zwobble.precisely.Matcher; import static org.zwobble.precisely.Matchers.has; +import static org.zwobble.precisely.Matchers.instanceOf; public class TypedStructDefinitionNodeMatcher extends MatcherBuilder< TypedNamespaceStatementNode, @@ -33,7 +35,17 @@ public class TypedStructDefinitionNodeMatcher extends MatcherBuilder< } public TypedStructDefinitionNodeMatcher withType(Matcher<? super StructType> type) { - return with(has("type", x -> x.type(), type)); + return with(has("typeOrConstructor", x -> x.typeOrConstructor(), instanceOf( + TypeOrConstructor.Type.class, + has("value", x -> x.value(), instanceOf(StructType.class, type)) + ))); + } + + public TypedStructDefinitionNodeMatcher withTypeConstructor(Matcher<TypeConstructor<?>> type) { + return with(has("typeOrConstructor", x -> x.typeOrConstructor(), instanceOf( + TypeOrConstructor.Constructor.class, + has("value", x -> (TypeConstructor<StructType>) x.value(), type) + ))); } public TypedStructDefinitionNodeMatcher withFields( 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 8d105dd9..559ca461 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/Untyped.java @@ -224,6 +224,10 @@ public class Untyped { return new UntypedTupleTypeNode(elements, Sources.NULL); } + public static UntypedTypeLevelParamNode typeLevelParam(String name) { + return new UntypedTypeLevelParamNode(name, Sources.NULL); + } + public static UntypedTypeLevelReferenceNode typeLevelReference(String name) { return new UntypedTypeLevelReferenceNode(name, Sources.NULL); } diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerConstructedTypeTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerConstructedTypeTests.java new file mode 100644 index 00000000..a3c78120 --- /dev/null +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerConstructedTypeTests.java @@ -0,0 +1,74 @@ +package org.shedlang.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.typechecker.errors.UnexpectedTypeError; +import org.shedlang.compiler.typechecker.errors.UnknownVariableError; +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 java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.isTypedTypeLevelReferenceNode; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; + +public class TypeCheckerConstructedTypeTests { + @Test + public void whenNameIsBoundInEnvironmentThenTypeIsUsedFromEnvironment() { + var untyped = Untyped.typeLevelReference("String"); + var context = TypeCheckerContext.EMPTY + .enterGlobal(P.map(Map.entry("String", Types.metaType(Types.STRING)))); + + var typed = TypeChecker.typeCheckTypeLevelExpression(TypeSets.META_TYPE, untyped, context); + + assertThat(typed, isTypedTypeLevelReferenceNode("String", Types.STRING)); + } + + @Test + public void whenNameRefersToNonTypeLevelValueThenErrorIsThrown() { + var untyped = Untyped.typeLevelReference("String"); + var context = TypeCheckerContext.EMPTY + .enterGlobal(P.map(Map.entry("String", Types.STRING))); + + var error = assertThrows( + UnexpectedTypeError.class, + () -> TypeChecker.typeCheckTypeLevelExpression(TypeSets.META_TYPE, untyped, context) + ); + + assertThat(error.expected(), equalTo(TypeSets.META_TYPE)); + assertThat(error.actual(), equalTo(Types.STRING)); + } + + @Test + public void whenNameRefersToValueOfWrongTypeThenErrorIsThrown() { + var untyped = Untyped.typeLevelReference("Io"); + var context = TypeCheckerContext.EMPTY + .enterGlobal(P.map(Map.entry("Io", Types.effectType(Effects.IO)))); + + var error = assertThrows( + UnexpectedTypeError.class, + () -> TypeChecker.typeCheckTypeLevelExpression(TypeSets.META_TYPE, untyped, context) + ); + + assertThat(error.expected(), equalTo(TypeSets.META_TYPE)); + assertThat(error.actual(), equalTo(Types.effectType(Effects.IO))); + } + + @Test + public void whenNameIsNotBoundInEnvironmentThenErrorIsThrown() { + var untyped = Untyped.typeLevelReference("String"); + var context = TypeCheckerContext.EMPTY + .enterGlobal(P.map(Map.entry("Int", Types.metaType(Types.INT)))); + + var error = assertThrows( + UnknownVariableError.class, + () -> TypeChecker.typeCheckTypeLevelExpression(TypeSets.META_TYPE, untyped, context) + ); + + assertThat(error.variableName(), equalTo("String")); + } +} diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java index 1f8af21d..230fd811 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerStructDefinitionTests.java @@ -9,12 +9,13 @@ import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; +import java.util.List; 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.shedlang.compiler.types.TypeMatchers.*; import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.*; @@ -35,6 +36,48 @@ public class TypeCheckerStructDefinitionTests { } @Test + public void whenDefinitionHasNoTypeLevelParamsThenTypedNodeHasType() { + var untyped = UntypedStructDefinitionNode.builder() + .withName("ExampleStruct") + .build(); + var context = TypeCheckerContext.EMPTY; + + var typed = typeCheckNamespaceStatement(untyped, context); + + assertThat( + typed.value(), + isTypedStructDefinitionNode() + .withType(isStructType(NAMESPACE_NAME, "ExampleStruct")) + ); + } + + @Test + public void whenDefinitionHasTypeLevelParamsThenTypedNodeHasTypeConstructor() { + var untyped = UntypedStructDefinitionNode.builder() + .withName("ExampleStruct") + .withTypeLevelParams(List.of( + Untyped.typeLevelParam("T1"), + Untyped.typeLevelParam("T2") + )) + .build(); + var context = TypeCheckerContext.EMPTY; + + var typed = typeCheckNamespaceStatement(untyped, context); + + assertThat( + typed.value(), + isTypedStructDefinitionNode() + .withTypeConstructor(isTypeConstructor( + isSequence( + isStructTypeLevelParam(NAMESPACE_NAME, "ExampleStruct", "T1"), + isStructTypeLevelParam(NAMESPACE_NAME, "ExampleStruct", "T2") + ), + isStructType(NAMESPACE_NAME, "ExampleStruct") + )) + ); + } + + @Test public void typeIsAddedToEnvironment() { var untyped = UntypedStructDefinitionNode.builder() .withName("ExampleStruct") diff --git a/src/test/java/org/shedlang/compiler/types/TypeMatchers.java b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java index 429caec7..cc47bad4 100644 --- a/src/test/java/org/shedlang/compiler/types/TypeMatchers.java +++ b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java @@ -2,6 +2,8 @@ package org.shedlang.compiler.types; import org.zwobble.precisely.Matcher; +import java.util.List; + import static org.zwobble.precisely.Matchers.*; public class TypeMatchers { @@ -15,4 +17,29 @@ public class TypeMatchers { has("name", x -> x.name(), equalTo(name)) ); } + + public static Matcher<TypeConstructor<?>> isTypeConstructor( + Matcher<? super Iterable<? extends TypeParam>> params, + Matcher<? super Type> genericType + ) { + return instanceOf( + TypeConstructor.class, + has("params", x -> (List<TypeParam>) x.params(), params), + has("genericType", x -> x.genericType(), genericType) + ); + } + + public static Matcher<TypeParam> isStructTypeLevelParam( + NamespaceName namespaceName, + String structName, + String paramName + ) { + return allOf( + has("scope", x -> x.scope(), instanceOf( + Scope.Struct.class, + has("type", x -> x.type(), isStructType(namespaceName, structName)) + )), + has("name", x -> x.name(), equalTo(paramName)) + ); + } } |
