summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-06-25 18:17:19 +0100
committerMichael Williamson <mike@zwobble.org>2026-06-26 10:56:49 +0100
commitad3e527a337ba2c02eac1aafdc17124c76cc1b92 (patch)
treeda177b3bd140971fd0e9c70a55630ce0a512dde4
parentbe132b1abe3ef9d1663f15799b971d5cfee3f469 (diff)
Throw better error when value is not type
-rw-r--r--hobgoblin/src/ast/untyped.hob2
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java2
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java9
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java63
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java2
5 files changed, 74 insertions, 4 deletions
diff --git a/hobgoblin/src/ast/untyped.hob b/hobgoblin/src/ast/untyped.hob
index 6b74e3e..0fd4403 100644
--- a/hobgoblin/src/ast/untyped.hob
+++ b/hobgoblin/src/ast/untyped.hob
@@ -72,6 +72,8 @@ struct UntypedSumVariantDefinitionNode {
sum UntypedTypeLevelExpressionNode {
variant UntypedConstructedTypeNode;
variant UntypedTypeLevelReferenceNode;
+
+ field source: Source;
}
struct UntypedConstructedTypeNode {
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java
index b5dbd5d..532d8de 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedTypeLevelExpressionNode.java
@@ -10,6 +10,8 @@ public sealed interface UntypedTypeLevelExpressionNode permits org.zwobble.hobgo
// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode.Builder body
}
+ public org.zwobble.hobgoblin.compiler.sources.Source source();
+
// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode body
// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode body
}
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 5003e3a..c291aaf 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -258,7 +258,7 @@ public class TypeChecker {
);
}
- private static TypedTypeLevelExpressionNode<Type> typeCheckMetaType(
+ static TypedTypeLevelExpressionNode<Type> typeCheckMetaType(
UntypedTypeLevelExpressionNode untyped,
TypeCheckerNamespaceContext context
) {
@@ -266,8 +266,11 @@ public class TypeChecker {
if (typed.value() instanceof Type) {
return (TypedTypeLevelExpressionNode<Type>) typed;
} else {
- // TODO: better error
- throw new RuntimeException("not a metatype");
+ throw new UnexpectedTypeError(
+ new TypeSet.MetaType(),
+ new TypeLevelValueType(typed.value()),
+ untyped.source()
+ );
}
}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java
new file mode 100644
index 0000000..1f966b1
--- /dev/null
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java
@@ -0,0 +1,63 @@
+package org.zwobble.hobgoblin.compiler.typechecker;
+
+import org.junit.jupiter.api.Test;
+import org.zwobble.hobgoblin.compiler.ast.typed.TypedTypeLevelReferenceNode;
+import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedArb;
+import org.zwobble.hobgoblin.compiler.types.*;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.zwobble.precisely.AssertThat.assertThat;
+import static org.zwobble.precisely.Matchers.*;
+import static org.zwobble.precisely.Matchers.equalTo;
+
+public class TypeCheckerMetaTypeTests {
+ @Test
+ public void whenVariableIsNotTypeLevelValueThenErrorIsThrown() {
+ var untyped = UntypedArb.typeLevelReference("X");
+ var context = TypeCheckerContextArb.namespaceContext();
+ var nativeType = SimpleNativeType.builtin("Int");
+ context.declare("X", nativeType);
+
+ var error = assertThrows(
+ UnexpectedTypeError.class,
+ () -> TypeChecker.typeCheckMetaType(untyped, context)
+ );
+
+ assertThat(error.expected(), equalTo(new TypeSet.MetaType()));
+ assertThat(error.actual(), equalTo(nativeType));
+ }
+
+ @Test
+ public void whenVariableIsTypeLevelValueButNotMetaTypeThenErrorIsThrown() {
+ var untyped = UntypedArb.typeLevelReference("List");
+ var context = TypeCheckerContextArb.namespaceContext();
+ var typeConstructor = new TypeConstructor<>(List.of(new TypeParam("T")), SimpleNativeType.builtin("List"));
+ context.declare("List", new TypeLevelValueType(typeConstructor));
+
+ var error = assertThrows(
+ UnexpectedTypeError.class,
+ () -> TypeChecker.typeCheckMetaType(untyped, context)
+ );
+
+ assertThat(error.expected(), equalTo(new TypeSet.MetaType()));
+ assertThat(error.actual(), equalTo(new TypeLevelValueType(typeConstructor)));
+ }
+
+ @Test
+ public void whenNameIsTypeLevelValueThenValueIsType() {
+ var untyped = UntypedArb.typeLevelReference("X");
+ var context = TypeCheckerContextArb.namespaceContext();
+ var nativeType = SimpleNativeType.builtin("Int");
+ context.declare("X", new TypeLevelValueType(nativeType));
+
+ var typed = TypeChecker.typeCheckMetaType(untyped, context);
+
+ assertThat(typed, instanceOf(
+ TypedTypeLevelReferenceNode.class,
+ has("name", TypedTypeLevelReferenceNode::name, equalTo("X")),
+ has("value", TypedTypeLevelReferenceNode::value, equalTo(nativeType))
+ ));
+ }
+}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java
index e633c94..0924eee 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTypeLevelReferenceTests.java
@@ -42,7 +42,7 @@ public class TypeCheckerTypeLevelReferenceTests {
}
@Test
- public void whenNameIsMetaTypeThenValueIsType() {
+ public void whenNameIsTypeLevelValueThenValueIsType() {
var untyped = UntypedArb.typeLevelReference("X");
var context = TypeCheckerContextArb.namespaceContext();
var nativeType = SimpleNativeType.builtin("Int");