Skip to content

Commit

Permalink
Improve the handling of dataflow errors (#1433)
Browse files Browse the repository at this point in the history
  • Loading branch information
iamrecursion committed Feb 10, 2021
1 parent 52bf592 commit 9ee2873
Show file tree
Hide file tree
Showing 34 changed files with 481 additions and 75 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<br/>
</p>

### Get insights you can act on, fast.
### Get insights you can act on, fast

<p>
<a href="http://chat.enso.org">
Expand Down
13 changes: 11 additions & 2 deletions distribution/std-lib/Test/src/Test.enso
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,17 @@ Any.should verb argument = verb Verbs this argument
## Fail a test with the given message.
fail message = Panic.throw (Failure message)

## Expect a function to fail with the provided error.
expect_fail_with ~action matcher =
## Expect a function to fail with the provided dataflow error.
expect_error_with ~action matcher =
result = action
fail_msg = "Expected an error " + matcher.to_text + "but none occurred."
if result.is_error.not then here.fail fail_msg else
caught = result.catch x->x
if caught.is_a matcher then Nothing else
here.fail ("Unexpected error " + caught.to_text + " thrown.")

## Expect a function to fail with the provided panic.
expect_panic_with ~action matcher =
res = Panic.recover action
case res of
_ -> here.fail ("Expected a " + matcher.to_text + " to be thrown, but the action succeeded.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.NotInvokableException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.state.Stateful;

/**
Expand Down Expand Up @@ -93,6 +94,32 @@ Stateful invokeConstructor(
invokeFunctionNode);
}

@Specialization
Stateful invokeDataflowError(
DataflowError error,
MaterializedFrame callerFrame,
Object state,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail) {
return new Stateful(state, error);
}

@Specialization
Stateful invokePanicSentinel(
PanicSentinel sentinel,
MaterializedFrame callerFrame,
Object state,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail) {
throw sentinel;
}

@Specialization
public Stateful invokeDynamicSymbol(
UnresolvedSymbol symbol,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.enso.interpreter.node.callable;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
Expand Down Expand Up @@ -28,6 +29,7 @@
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.state.Stateful;

Expand Down Expand Up @@ -281,6 +283,20 @@ Stateful doDataflowError(
}
}

@Specialization
Stateful doPanicSentinel(
MaterializedFrame frame,
Object state,
UnresolvedSymbol symbol,
PanicSentinel _this,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail) {
throw _this;
}

@Specialization
Stateful doArray(
MaterializedFrame frame,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.NotInvokableException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.state.Stateful;

/**
Expand Down Expand Up @@ -142,6 +143,18 @@ Stateful invokeConstructor(
return invokeFunction(constructor.getConstructorFunction(), callerFrame, state, arguments);
}

@Specialization
Stateful invokeDataflowError(
DataflowError error, VirtualFrame callerFrame, Object state, Object[] arguments) {
return new Stateful(state, error);
}

@Specialization
Stateful invokePanicSentinel(
PanicSentinel sentinel, VirtualFrame callerFrame, Object state, Object[] arguments) {
throw sentinel;
}

@Specialization
public Stateful invokeDynamicSymbol(
UnresolvedSymbol symbol, VirtualFrame callerFrame, Object state, Object[] arguments) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.enso.interpreter.node.callable;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import java.util.UUID;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
import org.enso.interpreter.node.callable.resolver.ArrayResolverNode;
Expand All @@ -28,11 +28,10 @@
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.state.Stateful;

import java.util.UUID;

public abstract class InvokeMethodNode extends BaseNode {
private @Child InvokeFunctionNode invokeFunctionNode;
private final ConditionProfile errorProfile = ConditionProfile.createCountingProfile();
Expand Down Expand Up @@ -181,6 +180,16 @@ Stateful doDataflowError(
}
}

@Specialization
Stateful doPanicSentinel(
VirtualFrame frame,
Object state,
UnresolvedSymbol symbol,
PanicSentinel _this,
Object[] arguments) {
throw _this;
}

@Specialization
Stateful doArray(
VirtualFrame frame,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.type.TypesGen;

/**
Expand Down Expand Up @@ -58,6 +59,19 @@ public Object doError(VirtualFrame frame, DataflowError error) {
return error;
}

/**
* Rethrows a panic sentinel if it encounters one.
*
* @param frame the stack frame in which to execute
* @param sentinel the sentinel being matched against
* @return nothing
*/
@Specialization
public Object doPanicSentinel(VirtualFrame frame, PanicSentinel sentinel) {
CompilerDirectives.transferToInterpreter();
throw sentinel;
}

/**
* Executes the case expression.
*
Expand All @@ -66,7 +80,7 @@ public Object doError(VirtualFrame frame, DataflowError error) {
* @param ctx the language context reference
* @return the result of executing the case expression on {@code object}
*/
@Specialization(guards = "!isDataflowError(object)")
@Specialization(guards = {"!isDataflowError(object)", "!isPanicSentinel(object)"})
@ExplodeLoop
public Object doMatch(
VirtualFrame frame,
Expand All @@ -92,6 +106,10 @@ boolean isDataflowError(Object error) {
return TypesGen.isDataflowError(error);
}

boolean isPanicSentinel(Object sentinel) {
return TypesGen.isPanicSentinel(sentinel);
}

/* Note [Branch Selection Control Flow]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Truffle provides no easy way to return control flow from multiple paths. This is entirely due
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
Expand All @@ -18,13 +19,16 @@ public class InstantiateNode extends ExpressionNode {
private final AtomConstructor constructor;
private @Children ExpressionNode[] arguments;
private @CompilationFinal(dimensions = 1) ConditionProfile[] profiles;
private @CompilationFinal(dimensions = 1) BranchProfile[] sentinelProfiles;

InstantiateNode(AtomConstructor constructor, ExpressionNode[] arguments) {
this.constructor = constructor;
this.arguments = arguments;
this.profiles = new ConditionProfile[arguments.length];
this.sentinelProfiles = new BranchProfile[arguments.length];
for (int i = 0; i < arguments.length; ++i) {
this.profiles[i] = ConditionProfile.createCountingProfile();
this.sentinelProfiles[i] = BranchProfile.create();
}
}

Expand All @@ -51,8 +55,17 @@ public static InstantiateNode build(AtomConstructor constructor, ExpressionNode[
public Object executeGeneric(VirtualFrame frame) {
Object[] argumentValues = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
ConditionProfile profile = profiles[i];
BranchProfile sentinelProfile = sentinelProfiles[i];
Object argument = arguments[i].executeGeneric(frame);
argumentValues[i] = argument;
if (profile.profile(TypesGen.isDataflowError(argument))) {
return argument;
} else if (TypesGen.isPanicSentinel(argument)) {
sentinelProfile.enter();
throw TypesGen.asPanicSentinel(argument);
} else {
argumentValues[i] = argument;
}
}
return constructor.newInstance(argumentValues);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.enso.interpreter.node.expression.builtin.io;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import java.io.PrintStream;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.node.callable.InvokeCallableNode;
Expand All @@ -19,8 +20,6 @@
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.type.TypesGen;

import java.io.PrintStream;

@BuiltinMethod(
type = "IO",
name = "print_err",
Expand All @@ -31,7 +30,7 @@ static PrintErrNode build() {
}

abstract Stateful execute(
VirtualFrame frame, @MonadicState Object state, Object _this, Object message);
VirtualFrame frame, @MonadicState Object state, Object _this, @AcceptsError Object message);

@Specialization
Stateful doPrintText(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.CachedContext;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import java.io.PrintStream;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.node.callable.InvokeCallableNode;
Expand All @@ -29,13 +29,13 @@ public abstract class PrintlnNode extends Node {
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);

abstract Stateful execute(
VirtualFrame frame, @MonadicState Object state, Object _this, Object message);
VirtualFrame frame, @MonadicState Object state, Object _this, @AcceptsError Object message);

@Specialization
Stateful doPrintText(
VirtualFrame frame,
Object state,
Object self,
Object _this,
Text message,
@CachedContext(Language.class) Context ctx,
@Cached("build()") ToJavaStringNode toJavaStringNode) {
Expand All @@ -47,7 +47,7 @@ Stateful doPrintText(
Stateful doPrint(
VirtualFrame frame,
Object state,
Object self,
Object _this,
Object message,
@CachedContext(Language.class) Context ctx,
@Cached("buildSymbol(ctx)") UnresolvedSymbol symbol,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;

Expand All @@ -9,7 +10,7 @@
name = "is_atom_constructor",
description = "Checks if the argument is a constructor.")
public class IsAtomConstructorNode extends Node {
boolean execute(Object _this, Object value) {
boolean execute(Object _this, @AcceptsError Object value) {
return TypesGen.isAtomConstructor(value);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;

@BuiltinMethod(type = "Meta", name = "is_atom", description = "Checks if the argument is an atom")
public class IsAtomNode extends Node {
boolean execute(Object _this, Object value) {
boolean execute(Object _this, @AcceptsError Object value) {
return TypesGen.isAtom(value);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.type.TypesGen;

Expand All @@ -9,7 +10,7 @@
name = "is_error",
description = "Checks if the argument is an error.")
public class IsErrorNode extends Node {
boolean execute(Object _this, Object value) {
boolean execute(Object _this, @AcceptsError Object value) {
return TypesGen.isDataflowError(value);
}
}
Loading

0 comments on commit 9ee2873

Please sign in to comment.