From 9335b1d7d7dcc5fe1f5be8ebe8d8662ae86c7481 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 5 Nov 2023 19:17:38 +0000 Subject: Add tests for effects in try body --- .../compiler/typechecker/TypeCheckerTryTests.java | 66 ++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'src/test/java') diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java index 32808834..566a93ee 100644 --- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java +++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerTryTests.java @@ -2,17 +2,23 @@ package org.shedlang.compiler.typechecker; import org.junit.jupiter.api.Test; import org.shedlang.compiler.ast.untyped.Untyped; +import org.shedlang.compiler.ast.untyped.UntypedCallNode; import org.shedlang.compiler.ast.untyped.UntypedTryNode; +import org.shedlang.compiler.typechecker.errors.UnexpectedEffectError; import org.shedlang.compiler.types.Effects; +import org.shedlang.compiler.types.FunctionType; import org.shedlang.compiler.types.NamespaceName; import org.shedlang.compiler.types.Types; import org.shedlang.compiler.util.P; import java.util.Map; +import java.util.Optional; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.shedlang.compiler.ast.typed.TypedNodeMatchers.*; import static org.shedlang.compiler.typechecker.TypeCheckerTesting.typeCheckExpression; import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; import static org.zwobble.precisely.Matchers.isSequence; public class TypeCheckerTryTests { @@ -40,4 +46,64 @@ public class TypeCheckerTryTests { ))) ); } + + @Test + public void whenEffectIsNotAllowedInExpressionScopeAndIsNotHandledEffectThenEffectIsNotAllowedInBodyScope() { + var handledEffect = Effects.userDefined(NAMESPACE_NAME, "HandledEmpty"); + var unexpectedEffect = Effects.userDefined(NAMESPACE_NAME, "UnexpectedEffect"); + var untyped = UntypedTryNode.builder() + .addBodyStatement(Untyped.expressionStatement( + UntypedCallNode.builder() + .withReceiver(Untyped.reference("f")) + .withHasEffects(true) + .build() + )) + .withHandledEffect(Untyped.typeLevelReference(handledEffect.name())) + .build(); + var context = TypeCheckerContext.EMPTY + .enterFunction( + P.map( + Map.entry(handledEffect.name(), Variable.defined(Types.effectType(handledEffect), VariableType.CONSTANT)), + Map.entry("f", Variable.defined(FunctionType.builder().withEffect(unexpectedEffect).build(), VariableType.VAL)) + ), + Effects.NONE, + Optional.empty() + ); + + var error = assertThrows( + UnexpectedEffectError.class, + () -> typeCheckExpression(untyped, context) + ); + + assertThat(error.allowed(), equalTo(Effects.NONE)); + assertThat(error.actual(), equalTo(unexpectedEffect)); + } + + @Test + public void effectsAllowedInExpressionScopeAreAllowedInBodyScope() { + var handledEffect = Effects.userDefined(NAMESPACE_NAME, "HandledEmpty"); + var allowedEffect = Effects.userDefined(NAMESPACE_NAME, "AllowedEffect"); + var untyped = UntypedTryNode.builder() + .addBodyStatement(Untyped.expressionStatement( + UntypedCallNode.builder() + .withReceiver(Untyped.reference("f")) + .withHasEffects(true) + .build() + )) + .withHandledEffect(Untyped.typeLevelReference(handledEffect.name())) + .build(); + var context = TypeCheckerContext.EMPTY + .enterFunction( + P.map( + Map.entry(handledEffect.name(), Variable.defined(Types.effectType(handledEffect), VariableType.CONSTANT)), + Map.entry("f", Variable.defined(FunctionType.builder().withEffect(allowedEffect).build(), VariableType.VAL)) + ), + allowedEffect, + Optional.empty() + ); + + var result = typeCheckExpression(untyped, context); + + assertThat(result.value(), isTypedTryNode()); + } } -- cgit v1.2.3