aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2023-09-28 11:57:36 +0100
committerMichael Williamson <mike@zwobble.org>2023-09-28 11:57:36 +0100
commitb1ca0653b7bd80302a287faef3a7c7647f92d23a (patch)
tree4c5d219f68d8b3d9f4e9e4d6a575d8e4fd1f121f /src
parenta409b4f6576980f876ec880ddf24dadc5d418c58 (diff)
Define struct type info earlier
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java6
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java22
-rw-r--r--src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java9
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java35
4 files changed, 66 insertions, 6 deletions
diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java b/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java
index be94fb24..db9789e3 100644
--- a/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java
+++ b/src/main/java/org/shedlang/compiler/typechecker/TypeCheckSteps.java
@@ -5,6 +5,10 @@ public interface TypeCheckSteps<T> {
return context;
}
+ default TypeCheckerContext defineTypes(TypeCheckerContext context) {
+ return context;
+ }
+
default TypeCheckerContext declareFunctions(TypeCheckerContext context) {
return context;
}
@@ -12,6 +16,6 @@ public interface TypeCheckSteps<T> {
TypeCheckerResult<T> finish(TypeCheckerContext context);
default TypeCheckerResult<T> typeCheckAllPhases(TypeCheckerContext context) {
- return finish(declareFunctions(declareTypes(context)));
+ return finish(declareFunctions(defineTypes(declareTypes(context))));
}
}
diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
index bd5b43fd..54e9d9b9 100644
--- a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
@@ -676,6 +676,10 @@ public class TypeChecker {
}
for (var typeCheckSteps : typeCheckStepSets) {
+ context = typeCheckSteps.defineTypes(context);
+ }
+
+ for (var typeCheckSteps : typeCheckStepSets) {
context = typeCheckSteps.declareFunctions(context);
}
@@ -836,7 +840,7 @@ public class TypeChecker {
}
@Override
- public TypeCheckerResult<TypedStructDefinitionNode> finish(TypeCheckerContext context) {
+ public TypeCheckerContext defineTypes(TypeCheckerContext context) {
var typedFieldNodes = new ArrayList<TypedStructFieldDefinitionNode>();
var fieldTypes = OrderedPMap.<String, Type>empty();
@@ -852,14 +856,24 @@ public class TypeChecker {
var type = context.getLexicalState(StructType.class, untyped.name());
- // TODO: this needs to happen before type-checking function bodies
context = context.addStructTypeInfo(type, new StructTypeInfo(fieldTypes));
- return context.toResult(new TypedStructDefinitionNode(
+ var typedNode = new TypedStructDefinitionNode(
untyped.name(),
typedFieldNodes,
untyped.source()
- ));
+ );
+
+ context = context.addLexicalState(untyped.name(), typedNode);
+
+ return context;
+ }
+
+ @Override
+ public TypeCheckerResult<TypedStructDefinitionNode> finish(TypeCheckerContext context) {
+ var typedNode = context.getLexicalState(TypedStructDefinitionNode.class, untyped.name());
+
+ return context.toResult(typedNode);
}
};
}
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 a21e3375..7bd1402d 100644
--- a/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java
+++ b/src/test/java/org/shedlang/compiler/ast/typed/TypedNodeMatchers.java
@@ -83,6 +83,15 @@ public class TypedNodeMatchers {
);
}
+ public static Matcher<TypedExpressionNode> isTypedExpressionNode(
+ Type type
+ ) {
+ return instanceOf(
+ TypedExpressionNode.class,
+ has("type", x -> x.type(), equalTo(type))
+ );
+ }
+
public static Matcher<TypedFunctionStatementNode> isTypedExpressionStatementNode(
Matcher<TypedExpressionNode> expression
) {
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java
index 5a081076..226769bf 100644
--- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java
@@ -324,7 +324,7 @@ public class TypeCheckerNamespaceTests {
}
@Test
- public void functionCanUseTypeDefinedLater() {
+ public void functionSignatureCanUseTypeDefinedLater() {
var untyped = Untyped.namespace(NamespaceName.of("Example", "Project"), List.of(
UntypedFunctionDefinitionNode.builder()
.withName("printUser")
@@ -362,6 +362,39 @@ public class TypeCheckerNamespaceTests {
}
@Test
+ public void functionBodyCanAccessFieldOnTypeDefinedLater() {
+ var untyped = Untyped.namespace(NamespaceName.of("Example", "Project"), List.of(
+ UntypedFunctionDefinitionNode.builder()
+ .withName("printUser")
+ .addPositionalParam("user", Untyped.typeLevelReference("User"))
+ .withReturnType(Untyped.typeLevelReference("Unit"))
+ .addStatement(Untyped.expressionStatement(Untyped.fieldAccess(Untyped.reference("user"), "name")))
+ .build(),
+ UntypedStructDefinitionNode.builder()
+ .withName("User")
+ .addField(Untyped.structFieldDefinition("name", Untyped.typeLevelReference("String")))
+ .build()
+ ));
+ var context = TypeCheckerContext.EMPTY
+ .enterGlobal(P.map(Map.entry("String", Types.metaType(Types.STRING))))
+ .enterGlobal(P.map(Map.entry("Unit", Types.metaType(Types.UNIT))));
+
+ var result = TypeChecker.typeCheckNamespace(untyped, context);
+
+ assertThat(
+ result.value(),
+ isTypedNamespaceNode()
+ .withBody(isSequence(
+ isTypedFunctionDefinitionNode()
+ .withBody(isTypedBlockNode(isSequence(
+ isTypedExpressionStatementNode(isTypedExpressionNode(Types.STRING))
+ ))),
+ isTypedStructDefinitionNode()
+ ))
+ );
+ }
+
+ @Test
public void structsCanHaveMutuallyRecursiveFields() {
var namespaceName = NamespaceName.of("Example", "Project");
var untyped = Untyped.namespace(namespaceName, List.of(