From 0f40994a60b060bddba5f08f5f43e4c49099c015 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 1 Oct 2023 19:37:52 +0100 Subject: Add basic type checking for when expressions --- .../compiler/ast/typed/TypedExpressionNode.java | 2 +- .../compiler/ast/typed/TypedWhenCaseNode.java | 11 +++++ .../shedlang/compiler/ast/typed/TypedWhenNode.java | 14 ++++++ .../shedlang/compiler/typechecker/TypeChecker.java | 57 ++++++++++++++++++++-- 4 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/shedlang/compiler/ast/typed/TypedWhenCaseNode.java create mode 100644 src/main/java/org/shedlang/compiler/ast/typed/TypedWhenNode.java (limited to 'src/main/java') diff --git a/src/main/java/org/shedlang/compiler/ast/typed/TypedExpressionNode.java b/src/main/java/org/shedlang/compiler/ast/typed/TypedExpressionNode.java index 93758ccf..2f479f25 100644 --- a/src/main/java/org/shedlang/compiler/ast/typed/TypedExpressionNode.java +++ b/src/main/java/org/shedlang/compiler/ast/typed/TypedExpressionNode.java @@ -4,6 +4,6 @@ import org.shedlang.compiler.types.Type; public sealed interface TypedExpressionNode extends TypedNode - permits TypedBinaryOperationNode, TypedBoolLiteralNode, TypedCallNode, TypedFieldAccessNode, TypedIfNode, TypedIntLiteralNode, TypedReferenceNode, TypedStringLiteralNode, TypedTupleNode { + permits TypedBinaryOperationNode, TypedBoolLiteralNode, TypedCallNode, TypedFieldAccessNode, TypedIfNode, TypedIntLiteralNode, TypedReferenceNode, TypedStringLiteralNode, TypedTupleNode, TypedWhenNode { Type type(); } diff --git a/src/main/java/org/shedlang/compiler/ast/typed/TypedWhenCaseNode.java b/src/main/java/org/shedlang/compiler/ast/typed/TypedWhenCaseNode.java new file mode 100644 index 00000000..b50003ba --- /dev/null +++ b/src/main/java/org/shedlang/compiler/ast/typed/TypedWhenCaseNode.java @@ -0,0 +1,11 @@ +package org.shedlang.compiler.ast.typed; + +import org.shedlang.compiler.sources.Source; +import org.shedlang.compiler.types.Type; + +public record TypedWhenCaseNode( + TypedTypeLevelExpressionNode caseType, + TypedBlockNode body, + Source source +) implements TypedNode { +} diff --git a/src/main/java/org/shedlang/compiler/ast/typed/TypedWhenNode.java b/src/main/java/org/shedlang/compiler/ast/typed/TypedWhenNode.java new file mode 100644 index 00000000..d9a4f7c9 --- /dev/null +++ b/src/main/java/org/shedlang/compiler/ast/typed/TypedWhenNode.java @@ -0,0 +1,14 @@ +package org.shedlang.compiler.ast.typed; + +import org.shedlang.compiler.sources.Source; +import org.shedlang.compiler.types.Type; + +import java.util.List; + +public record TypedWhenNode( + TypedExpressionNode expression, + List cases, + Type type, + Source source +) implements TypedExpressionNode { +} diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java index d10531ad..58396e80 100644 --- a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java @@ -406,7 +406,7 @@ public class TypeChecker { typeCheckTuple(untypedTuple, context); case UntypedWhenNode untypedWhen -> - throw new UnsupportedOperationException(); + typeCheckWhen(untypedWhen, context); }; } @@ -682,7 +682,7 @@ public class TypeChecker { var typedBody = new ArrayList(); var typeCheckStepSets = untyped.body().stream() - .map(untypedStatement -> typeCheckNamespaceStatement(untypedStatement)) + .map(untypedStatement -> typeCheckStepsNamespaceStatement(untypedStatement)) .toList(); for (var typeCheckSteps : typeCheckStepSets) { @@ -786,7 +786,7 @@ public class TypeChecker { .toResult(typed); } - static TypeCheckSteps typeCheckNamespaceStatement( + static TypeCheckSteps typeCheckStepsNamespaceStatement( UntypedNamespaceStatementNode untyped ) { return switch (untyped) { @@ -1132,6 +1132,57 @@ public class TypeChecker { }; } + private static TypeCheckerResult typeCheckWhen( + UntypedWhenNode untyped, + TypeCheckerContext context + ) { + var typedExpressionResult = typeCheckExpression(untyped.expression(), context); + var typedExpression = typedExpressionResult.value(); + context = typedExpressionResult.context(); + + var typedCasesResult = context.mapWithContext( + untyped.cases(), + TypeChecker::typeCheckWhenCase + ); + var typedCases = typedCasesResult.value(); + context = typedCasesResult.context(); + + var caseBodyTypes = typedCases.stream() + .map(typedCase -> typedCase.body().type()) + .toList(); + + var type = Types.commonSupertype(caseBodyTypes); + + return context.toResult(new TypedWhenNode( + typedExpression, + typedCases, + type, + untyped.source() + )); + } + + private static TypeCheckerResult typeCheckWhenCase( + UntypedWhenCaseNode untyped, + TypeCheckerContext context + ) { + // TODO: check is a valid case type + var typedCaseType = typeCheckTypeLevelExpression( + TypeSets.META_TYPE, + untyped.caseType(), + context + ); + + var typedBodyResult = typeCheckBlockNested(untyped.body(), context); + var typedBody = typedBodyResult.value(); + context = typedBodyResult.context(); + + return context.toResult(new TypedWhenCaseNode( + typedCaseType, + typedBody, + untyped.source() + )); + } + private static TypeCheckerResult typeCheckWhile( UntypedWhileNode untyped, TypeCheckerContext context -- cgit v1.2.3