aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2023-07-02 12:19:46 +0100
committerMichael Williamson <mike@zwobble.org>2023-07-02 12:19:46 +0100
commit71a8ca11b03331a92bb271cbd1fbcb877e587300 (patch)
tree3a73642a3285bca6b0049656ba8d421f548c4bfb /src
parent1f86ef817b64e5e78ed92dd767fa38306c7662b5 (diff)
Check call to function with effect is marked with effects
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/shedlang/compiler/ShedCompiler.java2
-rw-r--r--src/main/java/org/shedlang/compiler/ast/typed/TypedFunctionDefinitionNode.java1
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java4
-rw-r--r--src/main/java/org/shedlang/compiler/typechecker/errors/CallIsMissingEffectsError.java18
-rw-r--r--src/main/java/org/shedlang/compiler/types/Effect.java2
-rw-r--r--src/main/java/org/shedlang/compiler/types/Effects.java2
-rw-r--r--src/main/java/org/shedlang/compiler/types/FunctionType.java16
-rw-r--r--src/main/java/org/shedlang/compiler/types/NoneEffect.java10
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java22
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java3
-rw-r--r--src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java7
11 files changed, 75 insertions, 12 deletions
diff --git a/src/main/java/org/shedlang/compiler/ShedCompiler.java b/src/main/java/org/shedlang/compiler/ShedCompiler.java
index bf26436e..d727aea5 100644
--- a/src/main/java/org/shedlang/compiler/ShedCompiler.java
+++ b/src/main/java/org/shedlang/compiler/ShedCompiler.java
@@ -42,6 +42,7 @@ public class ShedCompiler {
Map.entry("print", new FunctionType(
List.of(Types.STRING),
List.of(),
+ Effects.IO,
Types.UNIT
))
))
@@ -52,6 +53,7 @@ public class ShedCompiler {
Map.entry("print", new FunctionType(
List.of(Types.STRING),
List.of(),
+ Effects.IO,
Types.UNIT
))
));
diff --git a/src/main/java/org/shedlang/compiler/ast/typed/TypedFunctionDefinitionNode.java b/src/main/java/org/shedlang/compiler/ast/typed/TypedFunctionDefinitionNode.java
index 417b9ad6..42783224 100644
--- a/src/main/java/org/shedlang/compiler/ast/typed/TypedFunctionDefinitionNode.java
+++ b/src/main/java/org/shedlang/compiler/ast/typed/TypedFunctionDefinitionNode.java
@@ -42,6 +42,7 @@ public record TypedFunctionDefinitionNode(
params.named().stream()
.map(param -> new NamedParam(param.name(), param.type().value()))
.toList(),
+ effect.map(e -> e.value()).orElse(Effects.NONE),
returnType.value()
),
source
diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
index 770b7063..4d2b7326 100644
--- a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java
@@ -142,6 +142,10 @@ public class TypeChecker {
var typedArgs = argsResult.value();
context = argsResult.context();
+ if (!receiverType.effect().equals(Effects.NONE) && !untyped.hasEffects()) {
+ throw new CallIsMissingEffectsError(receiverType.effect(), untyped.source());
+ }
+
return context.toResult(new TypedCallNode(
typedReceiver,
typedArgs,
diff --git a/src/main/java/org/shedlang/compiler/typechecker/errors/CallIsMissingEffectsError.java b/src/main/java/org/shedlang/compiler/typechecker/errors/CallIsMissingEffectsError.java
new file mode 100644
index 00000000..083d9a03
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/typechecker/errors/CallIsMissingEffectsError.java
@@ -0,0 +1,18 @@
+package org.shedlang.compiler.typechecker.errors;
+
+import org.shedlang.compiler.errors.SourceError;
+import org.shedlang.compiler.sources.Source;
+import org.shedlang.compiler.types.Effect;
+
+public class CallIsMissingEffectsError extends SourceError {
+ private final Effect effect;
+
+ public CallIsMissingEffectsError(Effect effect, Source source) {
+ super("Function has effect, but call is missing effect: " + effect.describe(), source);
+ this.effect = effect;
+ }
+
+ public Effect effect() {
+ return effect;
+ }
+}
diff --git a/src/main/java/org/shedlang/compiler/types/Effect.java b/src/main/java/org/shedlang/compiler/types/Effect.java
index 257a3847..7e8bc203 100644
--- a/src/main/java/org/shedlang/compiler/types/Effect.java
+++ b/src/main/java/org/shedlang/compiler/types/Effect.java
@@ -1,4 +1,4 @@
package org.shedlang.compiler.types;
-public sealed interface Effect extends TypeLevelValue permits IoEffect {
+public sealed interface Effect extends TypeLevelValue permits IoEffect, NoneEffect {
}
diff --git a/src/main/java/org/shedlang/compiler/types/Effects.java b/src/main/java/org/shedlang/compiler/types/Effects.java
index 820d31c5..c894a58b 100644
--- a/src/main/java/org/shedlang/compiler/types/Effects.java
+++ b/src/main/java/org/shedlang/compiler/types/Effects.java
@@ -4,5 +4,7 @@ public class Effects {
private Effects() {
}
+ public static final Effect NONE = NoneEffect.INSTANCE;
+
public static final Effect IO = IoEffect.INSTANCE;
}
diff --git a/src/main/java/org/shedlang/compiler/types/FunctionType.java b/src/main/java/org/shedlang/compiler/types/FunctionType.java
index 22d3487b..e40439f7 100644
--- a/src/main/java/org/shedlang/compiler/types/FunctionType.java
+++ b/src/main/java/org/shedlang/compiler/types/FunctionType.java
@@ -8,10 +8,11 @@ import java.util.List;
public record FunctionType(
List<Type> positionalParams,
List<NamedParam> namedParams,
+ Effect effect,
Type returnType
) implements Type {
public static Builder builder() {
- return new Builder(Empty.vector(), Empty.vector(), Types.UNIT);
+ return new Builder(Empty.vector(), Empty.vector(), Effects.NONE, Types.UNIT);
}
@Override
@@ -23,23 +24,28 @@ public record FunctionType(
public record Builder(
PVector<Type> positionalParams,
PVector<NamedParam> namedParams,
+ Effect effect,
Type returnType
) {
public FunctionType build() {
- return new FunctionType(positionalParams, namedParams, returnType);
+ return new FunctionType(positionalParams, namedParams, effect, returnType);
}
public Builder addNamedParam(String name, Type type) {
var namedParam = new NamedParam(name, type);
- return new Builder(positionalParams, namedParams.plus(namedParam), returnType);
+ return new Builder(positionalParams, namedParams.plus(namedParam), effect, returnType);
}
public Builder addPositionalParam(Type type) {
- return new Builder(positionalParams.plus(type), namedParams, returnType);
+ return new Builder(positionalParams.plus(type), namedParams, effect, returnType);
+ }
+
+ public Builder withEffect(Effect effect) {
+ return new Builder(positionalParams, namedParams, effect, returnType);
}
public Builder withReturnType(Type returnType) {
- return new Builder(positionalParams, namedParams, returnType);
+ return new Builder(positionalParams, namedParams, effect, returnType);
}
}
}
diff --git a/src/main/java/org/shedlang/compiler/types/NoneEffect.java b/src/main/java/org/shedlang/compiler/types/NoneEffect.java
new file mode 100644
index 00000000..e6fe5f95
--- /dev/null
+++ b/src/main/java/org/shedlang/compiler/types/NoneEffect.java
@@ -0,0 +1,10 @@
+package org.shedlang.compiler.types;
+
+public record NoneEffect() implements Effect {
+ static final NoneEffect INSTANCE = new NoneEffect();
+
+ @Override
+ public String describe() {
+ return "None";
+ }
+}
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java
index 24ded26f..ad1ab07e 100644
--- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerCallTests.java
@@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test;
import org.shedlang.compiler.ast.untyped.Untyped;
import org.shedlang.compiler.typechecker.errors.*;
import org.shedlang.compiler.typechecker.typesets.TypeSets;
+import org.shedlang.compiler.types.Effects;
import org.shedlang.compiler.types.FunctionType;
import org.shedlang.compiler.types.Types;
import org.shedlang.compiler.util.P;
@@ -252,4 +253,25 @@ public class TypeCheckerCallTests {
assertThat(error.argName(), equalTo("x"));
}
+
+ @Test
+ public void givenFunctionHasEffectsWhenCallingFunctionWithoutExplicitEffectsThenErrorIsThrown() {
+ var untyped = Untyped.call(
+ Untyped.reference("f"),
+ List.of()
+ );
+ var functionType = FunctionType.builder()
+ .withEffect(Effects.IO)
+ .withReturnType(Types.UNIT)
+ .build();
+ var context = TypeCheckerContext.EMPTY
+ .enter(P.map(Map.entry("f", functionType)));
+
+ var error = assertThrows(
+ CallIsMissingEffectsError.class,
+ () -> TypeChecker.typeCheckExpression(untyped, context)
+ );
+
+ assertThat(error.effect(), equalTo(Effects.IO));
+ }
}
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java
index 26ea2693..b7b2d66e 100644
--- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerFunctionDefinitionTests.java
@@ -36,7 +36,7 @@ public class TypeCheckerFunctionDefinitionTests {
.withEffect(isOptionalEmpty())
.withReturnType(isTypedTypeLevelReferenceNode("Unit", Types.UNIT))
.withBody(isSequence())
- .withType(new FunctionType(List.of(), List.of(), Types.UNIT))
+ .withType(new FunctionType(List.of(), List.of(), Effects.NONE, Types.UNIT))
);
}
@@ -68,6 +68,7 @@ public class TypeCheckerFunctionDefinitionTests {
.withType(new FunctionType(
List.of(Types.STRING),
List.of(new NamedParam("y", Types.INT)),
+ Effects.NONE,
Types.UNIT
))
);
diff --git a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java
index b9b6679c..8d537515 100644
--- a/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java
+++ b/src/test/java/org/shedlang/compiler/typechecker/TypeCheckerNamespaceTests.java
@@ -3,10 +3,7 @@ package org.shedlang.compiler.typechecker;
import org.junit.jupiter.api.Test;
import org.shedlang.compiler.ast.untyped.Untyped;
import org.shedlang.compiler.ast.untyped.UntypedFunctionDefinitionNode;
-import org.shedlang.compiler.types.FunctionType;
-import org.shedlang.compiler.types.NamespaceName;
-import org.shedlang.compiler.types.NamespaceTypeInfo;
-import org.shedlang.compiler.types.Types;
+import org.shedlang.compiler.types.*;
import org.shedlang.compiler.util.P;
import java.util.List;
@@ -77,7 +74,7 @@ public class TypeCheckerNamespaceTests {
assertThat(
result.context().namespaceTypeInfo().get(namespaceName),
equalTo(new NamespaceTypeInfo(P.orderedMap(
- Map.entry("f", new FunctionType(List.of(), List.of(), Types.UNIT))
+ Map.entry("f", new FunctionType(List.of(), List.of(), Effects.NONE, Types.UNIT))
)))
);
}