diff options
| -rw-r--r-- | src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java | 17 | ||||
| -rw-r--r-- | src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerSumDefinitionTests.java | 79 |
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")); } } |
