summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java49
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/types/TypeOrConstructor.java11
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java8
-rw-r--r--src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java55
4 files changed, 99 insertions, 24 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 e61cb7a..97008b6 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -208,7 +208,17 @@ public class TypeChecker {
TypeCheckerNamespaceContext context
) {
var structType = new SimpleStructType(context.namespaceName(), untyped.name());
- var metaType = new TypeLevelValueType(structType);
+
+ var structTypeOrConstructor = untyped.typeParams().isEmpty()
+ ? new TypeOrConstructor.Type<>(structType)
+ : new TypeOrConstructor.Constructor<>(new StructTypeConstructor(
+ untyped.typeParams().get().stream()
+ .map(typeParam -> new TypeParam(structType, typeParam.name()))
+ .toList(),
+ structType
+ ));
+ var metaType = new TypeLevelValueType(structTypeOrConstructor.value());
+
context.declare(untyped.name(), metaType, untyped.source());
return metaType;
}
@@ -217,7 +227,17 @@ public class TypeChecker {
UntypedStructDefinitionNode untyped,
TypeCheckerNamespaceContext context
) {
- var structType = (SimpleStructType) lookupMetaType(untyped.name(), untyped.source(), context);
+ var metaType = lookupTypeLevelValue(untyped.name(), untyped.source(), context);
+ var typeOrConstructor = switch (metaType) {
+ case SimpleStructType type ->
+ new TypeOrConstructor.Type<>(type);
+
+ case StructTypeConstructor typeConstructor ->
+ new TypeOrConstructor.Constructor<>(typeConstructor);
+
+ default ->
+ throw new UnsupportedOperationException("TODO");
+ };
var typeCheckedFieldDefinitions = untyped.fields().isPresent()
? Optional.of(typeCheckFieldDefinitions(untyped.fields().get(), context))
@@ -225,21 +245,24 @@ public class TypeChecker {
var typedFields = typeCheckedFieldDefinitions.map(TypeCheckFieldDefinitionsResult::fields);
var typedFieldNodes = typeCheckedFieldDefinitions.map(TypeCheckFieldDefinitionsResult::typedFieldNodes);
- context.defineStructType(structType, typedFields);
+ context.defineStructType(typeOrConstructor.unboundType(), typedFields);
- for (var sumType : context.variantOf(structType)) {
- var sumTypeFields = context.fieldsOf(sumType);
- typeCheckVariantType(
- sumType,
- sumTypeFields,
- structType,
- typedFields,
- context.toTypesInfo()
- );
+ // TODO: handle sums with generic structs
+ if (typeOrConstructor instanceof TypeOrConstructor.Type<SimpleStructType> type) {
+ for (var sumType : context.variantOf(type.value())) {
+ var sumTypeFields = context.fieldsOf(sumType);
+ typeCheckVariantType(
+ sumType,
+ sumTypeFields,
+ type.value(),
+ typedFields,
+ context.toTypesInfo()
+ );
+ }
}
return new TypedStructDefinitionNode(
- TypeOrConstructor.type(structType),
+ typeOrConstructor,
typedFieldNodes,
untyped.docComment(),
untyped.source()
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeOrConstructor.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeOrConstructor.java
index 3cffbc6..7904d29 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeOrConstructor.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeOrConstructor.java
@@ -14,6 +14,11 @@ public sealed interface TypeOrConstructor<T extends ConstructibleType> {
public String name() {
return this.value.name();
}
+
+ @Override
+ public T unboundType() {
+ return value;
+ }
}
record Constructor<T extends ConstructibleType>(TypeConstructor<T> value) implements TypeOrConstructor<T> {
@@ -21,8 +26,14 @@ public sealed interface TypeOrConstructor<T extends ConstructibleType> {
public String name() {
return this.value.name();
}
+
+ @Override
+ public T unboundType() {
+ return this.value.genericType();
+ }
}
String name();
TypeLevelValue value();
+ T unboundType();
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java
index 094a419..cfbb5ba 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java
@@ -18,4 +18,12 @@ public class Types {
new StructTypeConstructor(params, simpleStructType);
};
}
+
+ public static Type metaType(Type type) {
+ return new TypeLevelValueType(type);
+ }
+
+ public static Type metaType(TypeConstructor<?> constructor) {
+ return new TypeLevelValueType(constructor);
+ }
}
diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
index 9bdf9f8..91839f6 100644
--- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
+++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerStructDefinitionTests.java
@@ -5,13 +5,8 @@ import org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedStructDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedStructFieldDefinitionNode;
import org.zwobble.hobgoblin.compiler.ast.typed.TypedTypeLevelExpressionNode;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedArb;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceNode;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode;
-import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode;
-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.ast.untyped.*;
+import org.zwobble.hobgoblin.compiler.types.*;
import java.util.List;
import java.util.Optional;
@@ -23,7 +18,7 @@ import static org.zwobble.precisely.Matchers.*;
public class TypeCheckerStructDefinitionTests {
@Test
- public void structHasNamespaceFromContext() {
+ public void whenStructDefinitionHasNoTypeParamsThenNameIsBoundToType() {
var untyped = UntypedStructDefinitionNode.arbitrary()
.withName("X")
.build();
@@ -31,14 +26,52 @@ public class TypeCheckerStructDefinitionTests {
var typed = typeCheckNamespaceStatement(untyped, context);
+ var expectedType = new SimpleStructType(NamespaceName.of("a", "b"), "X");
assertThat(typed, instanceOf(
TypedStructDefinitionNode.class,
has(
- "type",
- TypedStructDefinitionNode::typeOrThrow,
- equalTo(new SimpleStructType(NamespaceName.of("a", "b"), "X"))
+ "typeOrConstructor",
+ TypedStructDefinitionNode::typeOrConstructor,
+ equalTo(TypeOrConstructor.type(expectedType))
)
));
+ assertThat(context.lookupType("X"), isOptionalOf(
+ equalTo(Types.metaType(expectedType))
+ ));
+ }
+
+ @Test
+ public void whenStructDefinitionHasTypeParamsThenNameIsBoundToTypeConstructor() {
+ var untyped = UntypedStructDefinitionNode.arbitrary()
+ .withName("X")
+ .withTypeParams(Optional.of(List.of(
+ UntypedTypeParamNode.arbitrary().withName("A").build(),
+ UntypedTypeParamNode.arbitrary().withName("B").build()
+ )))
+ .build();
+ var context = TypeCheckerContextArb.namespaceContext(NamespaceName.of("a", "b"));
+
+ var typed = typeCheckNamespaceStatement(untyped, context);
+
+ var expectedInnerType = new SimpleStructType(NamespaceName.of("a", "b"), "X");
+ var expectedTypeConstructor = Types.constructor(
+ List.of(
+ new TypeParam(expectedInnerType, "A"),
+ new TypeParam(expectedInnerType, "B")
+ ),
+ expectedInnerType
+ );
+ assertThat(typed, instanceOf(
+ TypedStructDefinitionNode.class,
+ has(
+ "typeOrConstructor",
+ TypedStructDefinitionNode::typeOrConstructor,
+ equalTo(TypeOrConstructor.constructor(expectedTypeConstructor))
+ )
+ ));
+ assertThat(context.lookupType("X"), isOptionalOf(
+ equalTo(Types.metaType(expectedTypeConstructor))
+ ));
}
@Test