summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-10-16 08:21:19 +0100
committerMichael Williamson <mike@zwobble.org>2026-10-16 08:21:19 +0100
commit8fe3ffe8c7aad4c3270b889bce0efaf4ab912157 (patch)
tree9f538e30de2edf5ee0ef4468f9f878f4726b1aac
parent698596aeb11ed334d1edd95458561300d86f4d15 (diff)
Check row type is struct
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java12
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/errors/TableRowTypeMustBeStructError.java18
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java27
3 files changed, 53 insertions, 4 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 eb3e09d..211726b 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -468,12 +468,18 @@ public class TypeChecker {
UntypedTableDefinitionNode untyped,
TypeCheckerNamespaceContext context
) {
- var rowType = typeCheckMetaType(untyped.rowType(), context);
+ var rowTypeTypedNode = typeCheckMetaType(untyped.rowType(), context);
+
+ if (!(rowTypeTypedNode.value() instanceof StructType rowType)) {
+ throw new TableRowTypeMustBeStructError(
+ rowTypeTypedNode.value(),
+ rowTypeTypedNode.source()
+ );
+ }
return new TypedTableDefinitionNode(
untyped.name(),
- // TODO: handle not struct type.
- (StructType) rowType.value(),
+ rowType,
// TODO: check primary key is valid.
untyped.primaryKey(),
untyped.docComment(),
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/errors/TableRowTypeMustBeStructError.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/errors/TableRowTypeMustBeStructError.java
new file mode 100644
index 0000000..17b93d4
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/errors/TableRowTypeMustBeStructError.java
@@ -0,0 +1,18 @@
+package org.zwobble.hobgoblin.compiler.typechecker.errors;
+
+import org.zwobble.hobgoblin.compiler.errors.SourceError;
+import org.zwobble.hobgoblin.compiler.sources.Source;
+import org.zwobble.hobgoblin.compiler.types.Type;
+
+public class TableRowTypeMustBeStructError extends SourceError {
+ private final Type rowType;
+
+ public TableRowTypeMustBeStructError(Type rowType, Source source) {
+ super("row types must be structs, but was " + rowType.describe(), source);
+ this.rowType = rowType;
+ }
+
+ public Type rowType() {
+ return rowType;
+ }
+}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java
index 7c08688..a4e1cf4 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java
@@ -4,10 +4,13 @@ import org.junit.jupiter.api.Test;
import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTableDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode;
import org.zwobble.hobgoblin.compiler.sources.NullSource;
+import org.zwobble.hobgoblin.compiler.typechecker.errors.TableRowTypeMustBeStructError;
import org.zwobble.hobgoblin.compiler.types.NamespaceName;
+import org.zwobble.hobgoblin.compiler.types.SimpleNativeType;
import org.zwobble.hobgoblin.compiler.types.SimpleStructType;
import org.zwobble.hobgoblin.compiler.types.Types;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.zwobble.hobgoblin.compiler.ast.typed.TypedTableDefinitionNodeMatcher.isTypedTableDefinitionNode;
import static org.zwobble.hobgoblin.compiler.typechecker.TypeChecker.typeCheckDatabaseSchemaStatement;
import static org.zwobble.precisely.AssertThat.assertThat;
@@ -15,7 +18,7 @@ import static org.zwobble.precisely.Matchers.equalTo;
public class TypeCheckerTableDefinitionTests {
@Test
- public void databaseSchemaStatementsAreTypeChecked() {
+ public void minimalTableDefinition() {
var untyped = UntypedTableDefinitionNode.arbitrary()
.withName("authors")
.withRowType(UntypedTypeLevelReferenceNode.arbitrary()
@@ -35,4 +38,26 @@ public class TypeCheckerTableDefinitionTests {
.withRowType(equalTo(authorType))
);
}
+
+ @Test
+ public void whenRowTypeIsNotStructTypeThenErrorIsThrown() {
+ var untyped = UntypedTableDefinitionNode.arbitrary()
+ .withName("authors")
+ .withRowType(UntypedTypeLevelReferenceNode.arbitrary()
+ .withName("Author")
+ .build()
+ )
+ .build();
+ var globalContext = TypeCheckerContextArb.globalContext();
+ var namespaceContext = globalContext.enterNamespace(NamespaceName.of("App"));
+ var authorType = new SimpleNativeType(NamespaceName.of("App"), "Author");
+ namespaceContext.declare("Author", Types.metaType(authorType), NullSource.INSTANCE);
+
+ var error = assertThrows(
+ TableRowTypeMustBeStructError.class,
+ () -> typeCheckDatabaseSchemaStatement(untyped, namespaceContext)
+ );
+
+ assertThat(error.rowType(), equalTo(authorType));
+ }
}