aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2025-02-07 20:53:02 +0000
committerMichael Williamson <mike@zwobble.org>2025-02-07 21:11:55 +0000
commitbd1515f9b219cef789cc31b2f8efbb217bfc2c3c (patch)
tree13f18d9336f4702dcab5587664c8e67f8289bd3b /src
parent9b56f880febd39cb705d658a50512ef3ae1a9162 (diff)
Add basic type checking for native type declarations
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/shedlang/compiler/ast/typed/TypedNamespaceStatementNode.java2
-rw-r--r--src/main/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNode.java50
-rw-r--r--src/main/java/org/shedlang/compiler/shid/ShidCodeGen.java6
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java44
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/errors/CannotDefineNativeTypeOutsideOfNamespaceError.java10
-rw-r--r--src/main/java/org/shedlang/compiler/types/Scope.java2
-rw-r--r--src/main/java/org/shedlang/compiler/types/TypeParam.java2
-rw-r--r--src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java62
-rw-r--r--src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java4
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java122
-rw-r--r--src/test/java/org/shedlang/compiler/types/TypeMatchers.java23
11 files changed, 323 insertions, 4 deletions
diff --git a/src/main/java/org/shedlang/compiler/ast/typed/TypedNamespaceStatementNode.java b/src/main/java/org/shedlang/compiler/ast/typed/TypedNamespaceStatementNode.java
index d38cff5e..c63b58d7 100644
--- a/src/main/java/org/shedlang/compiler/ast/typed/TypedNamespaceStatementNode.java
+++ b/src/main/java/org/shedlang/compiler/ast/typed/TypedNamespaceStatementNode.java
@@ -1,4 +1,4 @@
package org.shedlang.compiler.ast.typed;
-public sealed interface TypedNamespaceStatementNode extends TypedNode permits TypedAssignNode, TypedEffectDefinitionNode, TypedFunctionDefinitionNode, TypedNativeFunctionDeclarationNode, TypedStructDefinitionNode, TypedSumTypeDefinitionNode {
+public sealed interface TypedNamespaceStatementNode extends TypedNode permits TypedAssignNode, TypedEffectDefinitionNode, TypedFunctionDefinitionNode, TypedNativeFunctionDeclarationNode, TypedNativeTypeDeclarationNode, TypedStructDefinitionNode, TypedSumTypeDefinitionNode {
}
diff --git a/src/main/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNode.java b/src/main/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNode.java
new file mode 100644
index 00000000..2c438577
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNode.java
@@ -0,0 +1,50 @@
+package org.shedlang.compiler.ast.typed;
+
+import org.pcollections.PVector;
+import org.shedlang.compiler.sources.Source;
+import org.shedlang.compiler.sources.Sources;
+import org.shedlang.compiler.types.*;
+import org.shedlang.compiler.util.P;
+
+import java.util.List;
+
+public record TypedNativeTypeDeclarationNode(
+ TypeOrConstructor<SimpleNativeType> typeOrConstructor,
+ List<TypedNativeFunctionDeclarationNode> methods,
+ Source source
+) implements TypedNamespaceStatementNode {
+ public static Builder builder() {
+ var namespaceName = NamespaceName.of("Default", "Test", "Namespace");
+ return new Builder(
+ TypeOrConstructor.type(Types.nativeType(namespaceName, "X")),
+ P.vector()
+ );
+ }
+
+ public record Builder(
+ TypeOrConstructor<SimpleNativeType> type,
+ PVector<TypedNativeFunctionDeclarationNode> methods
+ ) {
+ public TypedNativeTypeDeclarationNode build() {
+ return new TypedNativeTypeDeclarationNode(
+ this.type,
+ this.methods,
+ Sources.NULL
+ );
+ }
+
+ public Builder withType(SimpleNativeType type) {
+ return new Builder(
+ TypeOrConstructor.type(type),
+ this.methods
+ );
+ }
+
+ public Builder addMethod(TypedNativeFunctionDeclarationNode method) {
+ return new Builder(
+ this.type,
+ this.methods.plus(method)
+ );
+ }
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/shid/ShidCodeGen.java b/src/main/java/org/shedlang/compiler/shid/ShidCodeGen.java
index bf6d2072..11827861 100644
--- a/src/main/java/org/shedlang/compiler/shid/ShidCodeGen.java
+++ b/src/main/java/org/shedlang/compiler/shid/ShidCodeGen.java
@@ -593,6 +593,9 @@ public class ShidCodeGen {
case TypedNativeFunctionDeclarationNode nativeFunctionDeclaration ->
1;
+ case TypedNativeTypeDeclarationNode nativeTypeDeclaration ->
+ 0;
+
case TypedStructDefinitionNode structDefinition ->
0;
@@ -642,6 +645,9 @@ public class ShidCodeGen {
case TypedNativeFunctionDeclarationNode nativeFunctionDeclaration ->
compileNativeFunctionDeclaration(nativeFunctionDeclaration, context);
+ case TypedNativeTypeDeclarationNode nativeTypeDeclarationNode ->
+ throw new UnsupportedOperationException("TODO");
+
case TypedStructDefinitionNode structDefinition ->
compileStructDefinition(structDefinition, context);
diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
index 98fe892e..a9d98ff2 100644
--- a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
@@ -1609,7 +1609,7 @@ public class TypeChecker {
typeCheckStepsNativeFunctionDeclaration(nativeFunctionDeclaration);
case UntypedNativeTypeDeclarationNode nativeTypeDeclaration ->
- throw new UnsupportedOperationException("TODO");
+ typeCheckStepsNativeTypeDeclaration(nativeTypeDeclaration);
case UntypedStructDefinitionNode structDefinition ->
typeCheckStepsStructDefinition(structDefinition);
@@ -1658,6 +1658,48 @@ public class TypeChecker {
};
}
+ private static TypeCheckSteps<TypedNativeTypeDeclarationNode> typeCheckStepsNativeTypeDeclaration(
+ UntypedNativeTypeDeclarationNode untyped
+ ) {
+ return new TypeCheckSteps<>() {
+ @Override
+ public TypeCheckerContext declareTypes(TypeCheckerContext context) {
+ var namespaceName = context.namespaceName()
+ .orElseThrow(() -> new CannotDefineNativeTypeOutsideOfNamespaceError(untyped.source()));
+
+ var type = new SimpleNativeType(namespaceName, untyped.name());
+
+ var typeOrConstructor = createTypeOrConstructor(
+ untyped.typeLevelParams(),
+ type,
+ new Scope.NativeType(type)
+ );
+
+ context = context.addLexicalState(untyped.name(), typeOrConstructor);
+
+ return context.declareVariableType(
+ untyped.name(),
+ typeOrConstructor.toMetaType(),
+ untyped.source()
+ );
+ }
+
+ @Override
+ public TypeCheckerResult<TypedNativeTypeDeclarationNode> finish(TypeCheckerContext context) {
+ var typeOrConstructor = (TypeOrConstructor<SimpleNativeType>) context.getLexicalState(
+ TypeOrConstructor.class,
+ untyped.name()
+ );
+
+ return context.toResult(new TypedNativeTypeDeclarationNode(
+ typeOrConstructor,
+ List.of(),
+ untyped.source()
+ ));
+ }
+ };
+ }
+
private static TypedParamNode typeCheckParam(
UntypedParamNode untyped,
TypeCheckerContext context
diff --git a/src/main/java/org/shedlang/compiler/typechecker/errors/CannotDefineNativeTypeOutsideOfNamespaceError.java b/src/main/java/org/shedlang/compiler/typechecker/errors/CannotDefineNativeTypeOutsideOfNamespaceError.java
new file mode 100644
index 00000000..01e9dead
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/typechecker/errors/CannotDefineNativeTypeOutsideOfNamespaceError.java
@@ -0,0 +1,10 @@
+package org.shedlang.compiler.typechecker.errors;
+
+import org.shedlang.compiler.errors.InternalCompilerError;
+import org.shedlang.compiler.sources.Source;
+
+public class CannotDefineNativeTypeOutsideOfNamespaceError extends InternalCompilerError {
+ public CannotDefineNativeTypeOutsideOfNamespaceError(Source source) {
+ super("Cannot define native type outside of namespace", source);
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/types/Scope.java b/src/main/java/org/shedlang/compiler/types/Scope.java
index b0141916..9a61e51e 100644
--- a/src/main/java/org/shedlang/compiler/types/Scope.java
+++ b/src/main/java/org/shedlang/compiler/types/Scope.java
@@ -2,7 +2,7 @@ package org.shedlang.compiler.types;
public sealed interface Scope {
record Function(NamespaceName namespaceName, String name) implements Scope {}
- record Native(NativeType type) implements Scope {}
+ record NativeType(org.shedlang.compiler.types.NativeType type) implements Scope {}
record Struct(SimpleStructType type) implements Scope {}
record Sum(SimpleSumType type) implements Scope {}
}
diff --git a/src/main/java/org/shedlang/compiler/types/TypeParam.java b/src/main/java/org/shedlang/compiler/types/TypeParam.java
index 6d311940..c996dd97 100644
--- a/src/main/java/org/shedlang/compiler/types/TypeParam.java
+++ b/src/main/java/org/shedlang/compiler/types/TypeParam.java
@@ -10,7 +10,7 @@ public record TypeParam(Scope scope, String name) implements Type, TypeLevelPara
}
public static TypeParam forNativeType(SimpleNativeType nativeType, String name) {
- return new TypeParam(new Scope.Native(nativeType), name);
+ return new TypeParam(new Scope.NativeType(nativeType), name);
}
public static TypeParam forStructType(SimpleStructType structType, String name) {
diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java
new file mode 100644
index 00000000..16d3f36c
--- /dev/null
+++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java
@@ -0,0 +1,62 @@
+package org.shedlang.compiler.ast.typed;
+
+import org.pcollections.PVector;
+import org.shedlang.compiler.testing.MatcherBuilder;
+import org.shedlang.compiler.types.SimpleNativeType;
+import org.shedlang.compiler.types.TypeConstructor;
+import org.shedlang.compiler.util.P;
+import org.zwobble.precisely.Matcher;
+
+import static org.shedlang.compiler.types.TypeMatchers.isConstructorNotType;
+import static org.shedlang.compiler.types.TypeMatchers.isTypeNotConstructor;
+import static org.zwobble.precisely.Matchers.has;
+
+public class TypedNativeTypeDeclarationNodeMatcher extends MatcherBuilder<
+ TypedNamespaceStatementNode,
+ TypedNativeTypeDeclarationNode,
+ TypedNativeTypeDeclarationNodeMatcher
+> {
+ public static TypedNativeTypeDeclarationNodeMatcher empty() {
+ return new TypedNativeTypeDeclarationNodeMatcher(P.vector());
+ }
+
+ private TypedNativeTypeDeclarationNodeMatcher(
+ PVector<Matcher<? super TypedNativeTypeDeclarationNode>> submatchers
+ ) {
+ super(submatchers);
+ }
+
+ @Override
+ protected Class<TypedNativeTypeDeclarationNode> expectedType() {
+ return TypedNativeTypeDeclarationNode.class;
+ }
+
+ @Override
+ protected TypedNativeTypeDeclarationNodeMatcher create(
+ PVector<Matcher<? super TypedNativeTypeDeclarationNode>> submatchers
+ ) {
+ return new TypedNativeTypeDeclarationNodeMatcher(submatchers);
+ }
+
+ public TypedNativeTypeDeclarationNodeMatcher withType(Matcher<? super SimpleNativeType> type) {
+ return with(has(
+ "typeOrConstructor",
+ x -> x.typeOrConstructor(),
+ isTypeNotConstructor(type)
+ ));
+ }
+
+ public TypedNativeTypeDeclarationNodeMatcher withTypeConstructor(Matcher<? super TypeConstructor<?>> type) {
+ return with(has(
+ "typeOrConstructor",
+ x -> x.typeOrConstructor(),
+ isConstructorNotType(type)
+ ));
+ }
+
+ public TypedNativeTypeDeclarationNodeMatcher withMethods(
+ Matcher<Iterable<? extends TypedNativeFunctionDeclarationNode>> methods
+ ) {
+ return with(has("methods", x -> x.methods(), methods));
+ }
+}
diff --git a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java
index 7c0c4396..5d81d0f3 100644
--- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java
+++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java
@@ -283,6 +283,10 @@ public class TypedNodeMatchers {
return TypedNativeFunctionDeclarationNodeMatcher.empty();
}
+ public static TypedNativeTypeDeclarationNodeMatcher isTypedNativeTypeDeclarationNode() {
+ return TypedNativeTypeDeclarationNodeMatcher.empty();
+ }
+
public static Matcher<TypedParamNode> isTypedParamNode(
String name,
Matcher<Optional<? extends TypedTypeLevelExpressionNode<?>>> type
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java
new file mode 100644
index 00000000..c9dca725
--- /dev/null
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java
@@ -0,0 +1,122 @@
+package org.shedlang.compiler.typechecker;
+
+import org.junit.jupiter.api.Test;
+import org.shedlang.compiler.ast.untyped.Untyped;
+import org.shedlang.compiler.ast.untyped.UntypedNativeTypeDeclarationNode;
+import org.shedlang.compiler.typechecker.errors.CannotDefineNativeTypeOutsideOfNamespaceError;
+import org.shedlang.compiler.types.NamespaceName;
+import org.shedlang.compiler.types.Types;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.isTypedNativeTypeDeclarationNode;
+import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement;
+import static org.shedlang.compiler.types.TypeMatchers.*;
+import static org.zwobble.precisely.AssertThat.assertThat;
+import static org.zwobble.precisely.Matchers.*;
+
+public class TypeCheckerNativeTypeDeclarationTests {
+ private static final NamespaceName NAMESPACE_NAME = NamespaceName.of("Example");
+
+ @Test
+ public void whenOutsideOfNamespaceThenErrorIsThrown() {
+ var untyped = UntypedNativeTypeDeclarationNode.builder()
+ .withName("ExampleType")
+ .build();
+ var context = TypeCheckerContext.EMPTY;
+
+ assertThrows(
+ CannotDefineNativeTypeOutsideOfNamespaceError.class,
+ () -> TypeChecker.typeCheckStepsNamespaceStatement(untyped).typeCheckAllPhases(context)
+ );
+ }
+
+ @Test
+ public void whenDeclarationHasNoTypeLevelParamsThenTypedNodeHasSimpleNativeType() {
+ var untyped = UntypedNativeTypeDeclarationNode.builder()
+ .withName("ExampleType")
+ .build();
+ var context = TypeCheckerContext.EMPTY;
+
+ var typed = typeCheckNamespaceStatement(untyped, context);
+
+ assertThat(
+ typed.value(),
+ isTypedNativeTypeDeclarationNode()
+ .withType(isNativeType(NAMESPACE_NAME, "ExampleType"))
+ );
+ }
+
+ @Test
+ public void whenDeclarationHasTypeLevelParamsThenTypedNodeHasTypeConstructor() {
+ var untyped = UntypedNativeTypeDeclarationNode.builder()
+ .withName("ExampleType")
+ .withTypeLevelParams(List.of(
+ Untyped.typeParam("T1"),
+ Untyped.typeParam("T2")
+ ))
+ .build();
+ var context = TypeCheckerContext.EMPTY;
+
+ var typed = typeCheckNamespaceStatement(untyped, context);
+
+ assertThat(
+ typed.value(),
+ isTypedNativeTypeDeclarationNode()
+ .withTypeConstructor(isTypeConstructor(
+ isSequence(
+ isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T1"),
+ isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T2")
+ ),
+ isNativeType(NAMESPACE_NAME, "ExampleType")
+ ))
+ );
+ }
+
+ @Test
+ public void whenDeclarationHasNoTypeParamsThenTypeIsAddedToEnvironment() {
+ var untyped = UntypedNativeTypeDeclarationNode.builder()
+ .withName("ExampleType")
+ .build();
+ var context = TypeCheckerContext.EMPTY;
+
+ var typed = typeCheckNamespaceStatement(untyped, context);
+
+ assertThat(
+ typed.context().compileTimeLookup("ExampleType"),
+ isOptionalOf(equalTo(Variable.defined(
+ Types.metaType(Types.nativeType(NAMESPACE_NAME, "ExampleType")),
+ VariableType.CONSTANT
+ )))
+ );
+ }
+
+ @Test
+ public void whenDeclarationHasTypeParamsThenTypeConstructorIsAddedToEnvironment() {
+ var untyped = UntypedNativeTypeDeclarationNode.builder()
+ .withName("ExampleType")
+ .withTypeLevelParams(List.of(
+ Untyped.typeParam("T1"),
+ Untyped.typeParam("T2")
+ ))
+ .build();
+ var context = TypeCheckerContext.EMPTY;
+
+ var typed = typeCheckNamespaceStatement(untyped, context);
+
+ assertThat(
+ typed.context().compileTimeLookup("ExampleType"),
+ isOptionalOf(allOf(
+ has("type", x -> x.type(), isOptionalOf(isTypeLevelValueType(isTypeConstructor(
+ isSequence(
+ isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T1"),
+ isNativeTypeTypeLevelParam(NAMESPACE_NAME, "ExampleType", "T2")
+ ),
+ isNativeType(NAMESPACE_NAME, "ExampleType")
+ )))),
+ has("variableType", x -> x.variableType(), equalTo(VariableType.CONSTANT))
+ ))
+ );
+ }
+}
diff --git a/src/test/java/org/shedlang/compiler/types/TypeMatchers.java b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java
index 2c777c76..c6480342 100644
--- a/src/test/java/org/shedlang/compiler/types/TypeMatchers.java
+++ b/src/test/java/org/shedlang/compiler/types/TypeMatchers.java
@@ -10,6 +10,14 @@ public class TypeMatchers {
private TypeMatchers() {
}
+ public static Matcher<TypeLevelValue> isNativeType(NamespaceName namespaceName, String name) {
+ return instanceOf(
+ SimpleNativeType.class,
+ has("namespaceName", x -> x.namespaceName(), equalTo(namespaceName)),
+ has("name", x -> x.name(), equalTo(name))
+ );
+ }
+
public static Matcher<TypeLevelValue> isStructType(NamespaceName namespaceName, String name) {
return instanceOf(
SimpleStructType.class,
@@ -55,6 +63,21 @@ public class TypeMatchers {
);
}
+ public static Matcher<Type> isNativeTypeTypeLevelParam(
+ NamespaceName namespaceName,
+ String typeName,
+ String paramName
+ ) {
+ return instanceOf(
+ TypeParam.class,
+ has("scope", x -> x.scope(), instanceOf(
+ Scope.NativeType.class,
+ has("type", x -> x.type(), isNativeType(namespaceName, typeName))
+ )),
+ has("name", x -> x.name(), equalTo(paramName))
+ );
+ }
+
public static Matcher<Type> isStructTypeLevelParam(
NamespaceName namespaceName,
String structName,