From bd1515f9b219cef789cc31b2f8efbb217bfc2c3c Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 7 Feb 2025 20:53:02 +0000 Subject: Add basic type checking for native type declarations --- .../TypedNativeTypeDeclarationNodeMatcher.java | 62 +++++++++++ .../compiler/ast/typed/TypedNodeMatchers.java | 4 + .../TypeCheckerNativeTypeDeclarationTests.java | 122 +++++++++++++++++++++ .../org/shedlang/compiler/types/TypeMatchers.java | 23 ++++ 4 files changed, 211 insertions(+) create mode 100644 src/test/java/org/shedlang/compiler/ast/typed/TypedNativeTypeDeclarationNodeMatcher.java create mode 100644 src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNativeTypeDeclarationTests.java (limited to 'src/test/java') 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> submatchers + ) { + super(submatchers); + } + + @Override + protected Class expectedType() { + return TypedNativeTypeDeclarationNode.class; + } + + @Override + protected TypedNativeTypeDeclarationNodeMatcher create( + PVector> submatchers + ) { + return new TypedNativeTypeDeclarationNodeMatcher(submatchers); + } + + public TypedNativeTypeDeclarationNodeMatcher withType(Matcher type) { + return with(has( + "typeOrConstructor", + x -> x.typeOrConstructor(), + isTypeNotConstructor(type) + )); + } + + public TypedNativeTypeDeclarationNodeMatcher withTypeConstructor(Matcher> type) { + return with(has( + "typeOrConstructor", + x -> x.typeOrConstructor(), + isConstructorNotType(type) + )); + } + + public TypedNativeTypeDeclarationNodeMatcher withMethods( + Matcher> 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 isTypedParamNode( String name, Matcher>> 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 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 isStructType(NamespaceName namespaceName, String name) { return instanceOf( SimpleStructType.class, @@ -55,6 +63,21 @@ public class TypeMatchers { ); } + public static Matcher 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 isStructTypeLevelParam( NamespaceName namespaceName, String structName, -- cgit v1.2.3