summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-08-02 14:19:23 +0100
committerMichael Williamson <mike@zwobble.org>2026-08-02 14:19:23 +0100
commitff49a4cd2231787867b4aafc95bd5705057bfcc3 (patch)
tree13ca6588de1f5f5dae010e537c54af3ad8b1c06d
parentf8b54eca1cedff1fcccb099b3cd1dbea81a1410b (diff)
Introduce NativeTypeConstructor for greater safety
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/builtins/NativeTypes.java8
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java9
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/types/NativeTypeConstructor.java9
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/types/TypeConstructor.java13
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java18
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerConstructedTypeTests.java6
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java2
7 files changed, 44 insertions, 21 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/builtins/NativeTypes.java b/src/main/java/org/zwobble/hobgoblin/compiler/builtins/NativeTypes.java
index 485df8c..f4e499e 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/builtins/NativeTypes.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/builtins/NativeTypes.java
@@ -13,7 +13,7 @@ public class NativeTypes {
public static final SimpleNativeType BOX_INNER = SimpleNativeType.builtin("Box");
- public static TypeConstructor<SimpleNativeType> BOX = new TypeConstructor<>(
+ public static TypeConstructor<SimpleNativeType> BOX = Types.constructor(
List.of(new TypeParam("T")),
BOX_INNER
);
@@ -24,7 +24,7 @@ public class NativeTypes {
public static final SimpleNativeType LIST_INNER = SimpleNativeType.builtin("List");
- public static TypeConstructor<SimpleNativeType> LIST = new TypeConstructor<>(
+ public static TypeConstructor<SimpleNativeType> LIST = Types.constructor(
List.of(new TypeParam("T")),
LIST_INNER
);
@@ -35,7 +35,7 @@ public class NativeTypes {
public static final SimpleNativeType OPTION_INNER = SimpleNativeType.builtin("Option");
- public static TypeConstructor<SimpleNativeType> OPTION = new TypeConstructor<>(
+ public static TypeConstructor<SimpleNativeType> OPTION = Types.constructor(
List.of(new TypeParam("T")),
OPTION_INNER
);
@@ -46,7 +46,7 @@ public class NativeTypes {
public static final SimpleNativeType SHARED_INNER = SimpleNativeType.builtin("Shared");
- public static TypeConstructor<SimpleNativeType> SHARED = new TypeConstructor<>(
+ public static TypeConstructor<SimpleNativeType> SHARED = Types.constructor(
List.of(new TypeParam("T")),
SHARED_INNER
);
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 4b02f60..9429313 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -408,12 +408,9 @@ public class TypeChecker {
.map(untypedArg -> typeCheckMetaType(untypedArg, context).value())
.toList();
- var constructedType = switch (receiver.genericType()) {
- case SimpleNativeType _ -> {
- @SuppressWarnings("unchecked")
- var narrowReceiver = (TypeConstructor<SimpleNativeType>) receiver;
- yield new ConstructedNativeType(narrowReceiver, args);
- }
+ var constructedType = switch (receiver) {
+ case NativeTypeConstructor nativeTypeConstructor ->
+ new ConstructedNativeType(nativeTypeConstructor, args);
};
return new TypedConstructedTypeNode(
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/NativeTypeConstructor.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/NativeTypeConstructor.java
new file mode 100644
index 0000000..43e0cfc
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/NativeTypeConstructor.java
@@ -0,0 +1,9 @@
+package org.zwobble.hobgoblin.compiler.types;
+
+import java.util.List;
+
+public record NativeTypeConstructor(
+ List<TypeParam> params,
+ SimpleNativeType genericType
+) implements TypeConstructor<SimpleNativeType> {
+}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeConstructor.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeConstructor.java
index 1e412d7..e2726fb 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeConstructor.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeConstructor.java
@@ -2,12 +2,11 @@ package org.zwobble.hobgoblin.compiler.types;
import java.util.List;
-public record TypeConstructor<T extends ConstructibleType>(
- List<TypeParam> params,
- T genericType
-) implements TypeLevelValue {
- @Override
- public String describe() {
- return genericType.describe();
+public sealed interface TypeConstructor<T extends ConstructibleType> extends TypeLevelValue permits NativeTypeConstructor {
+ List<TypeParam> params();
+ T genericType();
+
+ default String describe() {
+ return genericType().describe();
}
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java
new file mode 100644
index 0000000..6d8fcbb
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java
@@ -0,0 +1,18 @@
+package org.zwobble.hobgoblin.compiler.types;
+
+import java.util.List;
+
+public class Types {
+ private Types() {}
+
+ @SuppressWarnings("unchecked")
+ public static <T extends ConstructibleType> TypeConstructor<T> constructor(
+ List<TypeParam> params,
+ T genericType
+ ) {
+ return (TypeConstructor<T>) switch (genericType) {
+ case SimpleNativeType simpleNativeType ->
+ new NativeTypeConstructor(params, simpleNativeType);
+ };
+ }
+}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerConstructedTypeTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerConstructedTypeTests.java
index 09c0f8c..c0a7f93 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerConstructedTypeTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerConstructedTypeTests.java
@@ -22,7 +22,7 @@ public class TypeCheckerConstructedTypeTests {
);
var context = TypeCheckerContextArb.namespaceContext();
var listType = SimpleNativeType.builtin("List");
- var listTypeConstructor = new TypeConstructor<>(List.of(new TypeParam("T")), listType);
+ var listTypeConstructor = Types.constructor(List.of(new TypeParam("T")), listType);
context.declare("List", new TypeLevelValueType(listTypeConstructor), NullSource.INSTANCE);
var int32Type = SimpleNativeType.builtin("Int32");
context.declare("Int32", new TypeLevelValueType(int32Type), NullSource.INSTANCE);
@@ -42,7 +42,7 @@ public class TypeCheckerConstructedTypeTests {
);
var context = TypeCheckerContextArb.namespaceContext();
var listType = SimpleNativeType.builtin("List");
- var listTypeConstructor = new TypeConstructor<>(List.of(new TypeParam("T")), listType);
+ var listTypeConstructor = Types.constructor(List.of(new TypeParam("T")), listType);
context.declare("List", new TypeLevelValueType(listTypeConstructor), NullSource.INSTANCE);
var error = assertThrows(
@@ -65,7 +65,7 @@ public class TypeCheckerConstructedTypeTests {
);
var context = TypeCheckerContextArb.namespaceContext();
var listType = SimpleNativeType.builtin("List");
- var listTypeConstructor = new TypeConstructor<>(List.of(new TypeParam("T")), listType);
+ var listTypeConstructor = Types.constructor(List.of(new TypeParam("T")), listType);
context.declare("List", new TypeLevelValueType(listTypeConstructor), NullSource.INSTANCE);
var error = assertThrows(
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java
index c1b308c..4566b9b 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerMetaTypeTests.java
@@ -35,7 +35,7 @@ public class TypeCheckerMetaTypeTests {
public void whenVariableIsTypeLevelValueButNotMetaTypeThenErrorIsThrown() {
var untyped = UntypedArb.typeLevelReference("List");
var context = TypeCheckerContextArb.namespaceContext();
- var typeConstructor = new TypeConstructor<>(List.of(new TypeParam("T")), SimpleNativeType.builtin("List"));
+ var typeConstructor = Types.constructor(List.of(new TypeParam("T")), SimpleNativeType.builtin("List"));
context.declare("List", new TypeLevelValueType(typeConstructor), NullSource.INSTANCE);
var error = assertThrows(