summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java17
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java79
2 files changed, 89 insertions, 7 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
index c6f8721..408a0b2 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -313,6 +313,9 @@ public class TypeChecker {
// TODO: set source appropriately.
throw new SubtypeIsMissingFieldError(sumType, variantType, sumTypeField.name(), NullSource.INSTANCE);
}
+
+ // TODO: set source appropriately.
+ checkIsSubtype(variantTypeField.get().type(), sumTypeField.type(), NullSource.INSTANCE);
}
}
@@ -385,4 +388,18 @@ public class TypeChecker {
return value;
}
+
+ private static void checkIsSubtype(Type subtype, Type supertype, Source source) {
+ if (!isSubtype(subtype, supertype)) {
+ throw new UnexpectedTypeError(
+ new TypeSet.SingleType(supertype),
+ subtype,
+ source
+ );
+ }
+ }
+
+ private static boolean isSubtype(Type subtype, Type supertype) {
+ return subtype.equals(supertype);
+ }
}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java
index 36a4647..c19ee7f 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java
@@ -133,7 +133,7 @@ public class TypeCheckerSumDefinitionTests {
}
@Test
- public void givenVariantTypeIsDefinedFirstWhenVariantTypeIsMissingFieldThenErrorIsThrown() {
+ public void whenVariantTypeIsMissingFieldThenErrorIsThrown() {
var int32Type = SimpleNativeType.builtin("Int32");
var untyped = UntypedSumDefinitionNode.arbitrary()
.withName("Shape")
@@ -169,7 +169,76 @@ public class TypeCheckerSumDefinitionTests {
}
@Test
- public void givenSumTypeIsDefinedFirstWhenVariantTypeIsMissingFieldThenErrorIsThrown() {
+ public void whenVariantTypeHasFieldOfWrongTypeThenErrorIsThrown() {
+ var int32Type = SimpleNativeType.builtin("Int32");
+ var int64Type = SimpleNativeType.builtin("Int64");
+ var untyped = UntypedSumDefinitionNode.arbitrary()
+ .withName("Shape")
+ .withVariants(List.of(
+ UntypedSumVariantDefinitionNode.arbitrary()
+ .withType(UntypedTypeLevelReferenceNode.arbitrary().withName("Square"))
+ .build()
+ ))
+ .withFields(List.of(
+ UntypedStructFieldDefinitionNode.arbitrary()
+ .withName("area")
+ .withType(UntypedTypeLevelReferenceNode.arbitrary().withName("Int32"))
+ .build()
+ ))
+ .build();
+ var globalContext = TypeCheckerGlobalContext.initial();
+ globalContext.addNativeType(int32Type);
+ var namespaceName = NamespaceName.of("a", "b");
+ var namespaceContext = globalContext
+ .enterNamespace(namespaceName);
+ var variantType = new StructType(namespaceName, "Square");
+ namespaceContext.declare("Square", new TypeLevelValueType(variantType));
+ namespaceContext.defineStructType(variantType, List.of(new Field("area", int64Type)));
+
+ // TODO: more specific error
+ var error = assertThrows(
+ UnexpectedTypeError.class,
+ () -> typeCheckNamespaceStatement(untyped, namespaceContext)
+ );
+
+ assertThat(error.actual(), equalTo(int64Type));
+ assertThat(error.expected(), equalTo(new TypeSet.SingleType(int32Type)));
+ }
+
+ @Test
+ public void givenVariantTypeIsDefinedFirstWhenVariantTypeIsInvalidThenErrorIsThrown() {
+ var int32Type = SimpleNativeType.builtin("Int32");
+ var untyped = UntypedSumDefinitionNode.arbitrary()
+ .withName("Shape")
+ .withVariants(List.of(
+ UntypedSumVariantDefinitionNode.arbitrary()
+ .withType(UntypedTypeLevelReferenceNode.arbitrary().withName("Square"))
+ .build()
+ ))
+ .withFields(List.of(
+ UntypedStructFieldDefinitionNode.arbitrary()
+ .withName("area")
+ .withType(UntypedTypeLevelReferenceNode.arbitrary().withName("Int32"))
+ .build()
+ ))
+ .build();
+ var globalContext = TypeCheckerGlobalContext.initial();
+ globalContext.addNativeType(int32Type);
+ var namespaceName = NamespaceName.of("a", "b");
+ var namespaceContext = globalContext
+ .enterNamespace(namespaceName);
+ var variantType = new StructType(namespaceName, "Square");
+ namespaceContext.declare("Square", new TypeLevelValueType(variantType));
+ namespaceContext.defineStructType(variantType, List.of());
+
+ assertThrows(
+ SubtypeIsMissingFieldError.class,
+ () -> typeCheckNamespaceStatement(untyped, namespaceContext)
+ );
+ }
+
+ @Test
+ public void givenSumTypeIsDefinedFirstWhenVariantTypeIsInvalidThenErrorIsThrown() {
var int32Type = SimpleNativeType.builtin("Int32");
var untyped = UntypedStructDefinitionNode.arbitrary()
.withName("Square")
@@ -188,13 +257,9 @@ public class TypeCheckerSumDefinitionTests {
List.of(new Field("area", int32Type))
);
- var error = assertThrows(
+ assertThrows(
SubtypeIsMissingFieldError.class,
() -> typeCheckNamespaceStatement(untyped, namespaceContext)
);
-
- assertThat(error.supertype(), equalTo(sumType));
- assertThat(error.subtype(), equalTo(variantType));
- assertThat(error.fieldName(), equalTo("area"));
}
}