Decorate values with arbitrary warnings (#3248)

This commit is contained in:
Marcin Kostrzewa 2022-03-09 16:40:02 +01:00 committed by GitHub
parent 31be7c8b9a
commit 4653bfeeab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1256 additions and 166 deletions

View File

@ -105,8 +105,10 @@
- [Added overloaded `from` conversions.][3227]
- [Upgraded to Graal VM 21.3.0][3258]
- [Added the ability to decorate values with warnings.][3248]
[3227]: https://github.com/enso-org/enso/pull/3227
[3248]: https://github.com/enso-org/enso/pull/3248
[3258]: https://github.com/enso-org/enso/pull/3258
# Enso 2.0.0-alpha.18 (2021-10-12)

View File

@ -22,8 +22,10 @@ import project.Meta
import project.Meta.Enso_Project
import project.Polyglot.Java
import project.Runtime.Extensions
import project.System.Environment
import project.System.File
import project.Data.Text.Regex.Mode as Regex_Mode
import project.Warning
from Standard.Builtins import Nothing, Number, Integer, Any, True, False, Cons, Boolean, Arithmetic_Error
@ -37,8 +39,10 @@ export project.Data.Ordering.Sort_Order
export project.Data.Vector
export project.Math
export project.Meta
export project.System.Environment
export project.System.File
export project.Data.Text.Regex.Mode as Regex_Mode
export project.Warning
from project.Data.Any.Extensions export all
from project.Data.Array.Extensions export all

View File

@ -52,7 +52,7 @@ type Source_Location
end = this.end_column.to_text
row + ":" + start + "-" + end
False ->
start_line + '-' + end_line
start_line.to_text + '-' + end_line.to_text
cwd = File.current_directory
file = this.file.absolute
formatted_file = case file.is_child_of cwd of

View File

@ -0,0 +1,69 @@
from Standard.Base import all
## A representation of a dataflow warning attached to a value.
type Warning
## PRIVATE
The constructor to wrap primitive warnings.
type Warning prim_warning
## UNSTABLE
Returns the warning value usually its explanation or other contents.
value : Any
value = Prim_Warning.get_value this.prim_warning
## UNSTABLE
ADVANCED
A stack trace for the original warning creation.
origin : Vector.Vector Stack_Trace_Element
origin = Prim_Warning.get_origin this.prim_warning
## UNSTABLE
ADVANCED
A list of locations where the warning was reassigned in the order of
latest-first.
Warnings are reassigned whenever they interract with specific language
elements:
- When pattern matching, the warnings of the scrutinee will be reassigned
to the `case` expression result.
- When calling a method, warnings assigned to `this` will be reassigned to
the method return value.
- When calling a polyglot function or method, warnings assigned to any
arguments will be accumulated in the return value.
- The standard library methods reassign warnings such that their dataflow
nature is preserved.
reassignments : Vector.Vector Stack_Trace_Element
reassignments =
Vector.Vector (Prim_Warning.get_reassignments this.prim_warning) . map r->
loc = case Polyglot.has_source_location r of
False -> Nothing
True -> Source_Location (Polyglot.get_source_location r)
Stack_Trace_Element (Polyglot.get_executable_name r) loc
## UNSTABLE
Attaches a new warning to the value.
attach : Any -> Any -> Any
attach warning value =
origin = Runtime.get_stack_trace
Prim_Warning.attach value warning (origin.drop_start 1)
## UNSTABLE
Gets all the warnings attached to the given value. Warnings are returned in the
reverse-chronological order with respect to their attachment time.
get_all : Any -> Vector.Vector Warning
get_all value =
Vector.Vector (Prim_Warning.get_all value) . map Warning
## UNSTABLE
ADVANCED
Sets a new list of warnings for the given value. Any warnings already present
in `value` will be lost.
set warnings value =
Prim_Warning.set value (warnings.map .prim_warning).to_array

View File

@ -21,10 +21,14 @@ import Standard.Builtins
2*3 . should_equal 6
Suite.run_main : Any -> Nothing
Suite.run_main ~specs =
r = this.run specs
r = this.run specs here.config_from_env
code = if r.is_fail then 1 else 0
System.exit code
config_from_env =
only_group_regexp = Environment.get "TEST_ONLY_GROUP"
Suite_Config only_group_regexp
## Creates a new test group, desribing properties of the object
described by `this`.
@ -42,9 +46,9 @@ Suite.run_main ~specs =
2+3 . should_equal 5
Test.specify "should define multiplication" <|
2*3 . should_equal 6
Suite.run : Any -> Any
Suite.run ~specs =
r = State.run Suite (Suite Nil) <|
Suite.run : Any -> Suite_Config -> Any
Suite.run ~specs config =
r = State.run Suite (Suite config Nil) <|
specs
State.get Suite
r
@ -67,18 +71,20 @@ Suite.run ~specs =
Test.group "Number" <| Nothing
group : Text -> Any -> (Text | Nothing) -> Nothing
group name ~behaviors pending=Nothing =
case pending of
Nothing ->
r = State.run Spec (Spec name Nil) <|
behaviors
State.get Spec
r.print_report
suite = State.get Suite
new_suite = Suite (Cons r suite.specs)
State.put Suite new_suite
reason ->
IO.print_err ("[PENDING] " + name)
IO.print_err (" Reason: " + reason)
config = State.get Suite . config
if config.should_run_group name then
case pending of
Nothing ->
r = State.run Spec (Spec name Nil) <|
behaviors
State.get Spec
r.print_report
suite = State.get Suite
new_suite = Suite suite.config (Cons r suite.specs)
State.put Suite new_suite
reason ->
IO.print_err ("[PENDING] " + name)
IO.print_err (" Reason: " + reason)
## Specifies a single behavior, described by `this`.
@ -435,13 +441,24 @@ Spec.print_report =
IO.print_err (" - [PENDING] " + behavior.name)
IO.print_err (" Reason: " + reason)
## PRVATE
type Suite_Config
type Suite_Config only_group_regexp
should_run_group name =
regexp = this.only_group_regexp
case regexp of
Text -> name.matches regexp . catch (_->True)
_ -> True
## PRIVATE
The top-level entry point for a test suite.
Arguments:
- specs: The specs contained within the test suite.
type Suite specs
type Suite config specs
## PRIVATE

View File

@ -8,6 +8,7 @@ import com.oracle.truffle.api.TruffleLogger;
import com.oracle.truffle.api.debug.DebuggerTags;
import com.oracle.truffle.api.instrumentation.ProvidedTags;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import org.enso.distribution.DistributionManager;
import org.enso.distribution.Environment;
@ -58,6 +59,12 @@ import org.graalvm.options.OptionDescriptors;
})
public final class Language extends TruffleLanguage<Context> {
private IdExecutionInstrument idExecutionInstrument;
private static final LanguageReference<Language> REFERENCE =
LanguageReference.create(Language.class);
public static Language get(Node node) {
return REFERENCE.get(node);
}
/**
* Creates a new Enso context.

View File

@ -156,7 +156,8 @@ public abstract class IndirectInvokeCallableNode extends Node {
schema,
defaultsExecutionMode,
argumentsExecutionMode,
isTail);
isTail,
thisArgumentPosition);
} else {
throw new RuntimeException("Currying without `this` argument is not yet supported.");
}

View File

@ -23,10 +23,9 @@ import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.text.Text;
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.error.*;
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
import org.enso.interpreter.runtime.state.Stateful;
@ -50,7 +49,8 @@ public abstract class IndirectInvokeConversionNode extends Node {
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail);
BaseNode.TailStatus isTail,
int thatArgumentPosition);
@Specialization(guards = "dispatch.canConvertFrom(that)")
Stateful doConvertFrom(
@ -64,8 +64,8 @@ public abstract class IndirectInvokeConversionNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thatArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
@Cached ConditionProfile atomProfile,
@Cached ConditionProfile atomConstructorProfile,
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
@ -74,7 +74,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
dispatch.getConversionFunction(
that,
InvokeConversionNode.extractConstructor(
this, _this, ctx, atomConstructorProfile, atomProfile),
this, _this, atomConstructorProfile, atomProfile),
conversion);
return indirectInvokeFunctionNode.execute(
function,
@ -87,7 +87,11 @@ public abstract class IndirectInvokeConversionNode extends Node {
isTail);
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
Context.get(this)
.getBuiltins()
.error()
.makeNoSuchConversionError(_this, that, conversion),
this);
}
}
@ -103,8 +107,8 @@ public abstract class IndirectInvokeConversionNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thatArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
@Cached BranchProfile profile,
@Cached ConditionProfile atomProfile,
@Cached ConditionProfile atomConstructorProfile,
@ -114,7 +118,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
dispatch.getConversionFunction(
that,
InvokeConversionNode.extractConstructor(
this, _this, ctx, atomConstructorProfile, atomProfile),
this, _this, atomConstructorProfile, atomProfile),
conversion);
return indirectInvokeFunctionNode.execute(
function,
@ -142,10 +146,43 @@ public abstract class IndirectInvokeConversionNode extends Node {
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail) {
BaseNode.TailStatus isTail,
int thatArgumentPosition) {
throw that;
}
@Specialization
Stateful doWarning(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
WithWarnings that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thatArgumentPosition,
@Cached IndirectInvokeConversionNode childDispatch) {
arguments[thatArgumentPosition] = that.getValue();
ArrayRope<Warning> warnings = that.getReassignedWarnings(this);
Stateful result =
childDispatch.execute(
frame,
state,
conversion,
_this,
that.getValue(),
arguments,
schema,
defaultsExecutionMode,
argumentsExecutionMode,
isTail,
thatArgumentPosition);
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
}
@Specialization(guards = "interop.isString(that)")
Stateful doConvertText(
MaterializedFrame frame,
@ -158,12 +195,12 @@ public abstract class IndirectInvokeConversionNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thatArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
@CachedLibrary(limit = "10") InteropLibrary interop,
@Cached ConditionProfile atomProfile,
@Cached ConditionProfile atomConstructorProfile,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
try {
String str = interop.asString(that);
@ -172,7 +209,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
textDispatch.getConversionFunction(
txt,
InvokeConversionNode.extractConstructor(
this, _this, ctx, atomConstructorProfile, atomProfile),
this, _this, atomConstructorProfile, atomProfile),
conversion);
arguments[0] = txt;
return indirectInvokeFunctionNode.execute(
@ -188,31 +225,36 @@ public abstract class IndirectInvokeConversionNode extends Node {
throw new IllegalStateException("Impossible, that is guaranteed to be a string.");
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
Context.get(this)
.getBuiltins()
.error()
.makeNoSuchConversionError(_this, that, conversion),
this);
}
}
@Specialization(
guards = {
"!methods.canConvertFrom(that)",
"!interop.isString(that)",
"!methods.hasSpecialConversion(that)"
})
guards = {
"!methods.canConvertFrom(that)",
"!interop.isString(that)",
"!methods.hasSpecialConversion(that)"
})
Stateful doFallback(
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
Object that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedContext(Language.class) Context ctx) {
MaterializedFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
Object that,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thatArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop) {
throw new PanicException(
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
Context.get(this).getBuiltins().error().makeNoSuchConversionError(_this, that, conversion),
this);
}
}

View File

@ -27,10 +27,9 @@ import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.ArrayRope;
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.error.PanicException;
import org.enso.interpreter.runtime.error.*;
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.state.Stateful;
@ -54,7 +53,8 @@ public abstract class IndirectInvokeMethodNode extends Node {
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail);
BaseNode.TailStatus isTail,
int thisArgumentPosition);
@Specialization(guards = "dispatch.hasFunctionalDispatch(_this)")
Stateful doFunctionalDispatch(
@ -67,9 +67,9 @@ public abstract class IndirectInvokeMethodNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thisArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@Cached IndirectInvokeFunctionNode invokeFunctionNode,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
@Cached IndirectInvokeFunctionNode invokeFunctionNode) {
try {
Function function = dispatch.getFunctionalDispatch(_this, symbol);
return invokeFunctionNode.execute(
@ -83,7 +83,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
isTail);
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
}
}
@ -98,6 +98,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thisArgumentPosition,
@Cached DataflowErrorResolverNode dataflowErrorResolverNode,
@Cached IndirectInvokeFunctionNode invokeFunctionNode,
@Cached ConditionProfile profile) {
@ -117,6 +118,36 @@ public abstract class IndirectInvokeMethodNode extends Node {
}
}
@Specialization
Stateful doWarning(
MaterializedFrame frame,
Object state,
UnresolvedSymbol symbol,
WithWarnings _this,
Object[] arguments,
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thisArgumentPosition,
@Cached IndirectInvokeMethodNode childDispatch) {
arguments[thisArgumentPosition] = _this.getValue();
ArrayRope<Warning> warnings = _this.getReassignedWarnings(this);
Stateful result =
childDispatch.execute(
frame,
state,
symbol,
_this.getValue(),
arguments,
schema,
defaultsExecutionMode,
argumentsExecutionMode,
isTail,
thisArgumentPosition);
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
}
@Specialization
Stateful doPanicSentinel(
MaterializedFrame frame,
@ -127,7 +158,8 @@ public abstract class IndirectInvokeMethodNode extends Node {
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail) {
BaseNode.TailStatus isTail,
int thisArgumentPosition) {
throw _this;
}
@ -148,6 +180,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thisArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
@Bind("getPolyglotCallType(_this, symbol.getName(), interop)")
@ -184,10 +217,10 @@ public abstract class IndirectInvokeMethodNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thisArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
@Cached IndirectInvokeFunctionNode invokeFunctionNode) {
try {
String str = interop.asString(_this);
@ -207,7 +240,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
throw new IllegalStateException("Impossible, _this is guaranteed to be a string.");
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
}
}
@ -228,6 +261,7 @@ public abstract class IndirectInvokeMethodNode extends Node {
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
BaseNode.TailStatus isTail,
int thisArgumentPosition,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
@Bind("getPolyglotCallType(_this, symbol.getName(), interop)")

View File

@ -46,20 +46,21 @@ public abstract class InteropConversionCallNode extends Node {
InvokeConversionNode buildInvoker(int length) {
CallArgumentInfo[] args = buildSchema(length);
return InvokeConversionNode.build(
args,
DefaultsExecutionMode.EXECUTE,
ArgumentsExecutionMode.PRE_EXECUTED);
args, DefaultsExecutionMode.EXECUTE, ArgumentsExecutionMode.PRE_EXECUTED, 1);
}
Context getContext() {
return Context.get(this);
}
@Specialization(
guards = {"!context.isInlineCachingDisabled()", "arguments.length == cachedArgsLength"},
guards = {"!getContext().isInlineCachingDisabled()", "arguments.length == cachedArgsLength"},
limit = Constants.CacheSizes.FUNCTION_INTEROP_LIBRARY)
@ExplodeLoop
Object callCached(
UnresolvedConversion conversion,
Object state,
Object[] arguments,
@CachedContext(Language.class) Context context,
@Cached("arguments.length") int cachedArgsLength,
@Cached("buildInvoker(cachedArgsLength)") InvokeConversionNode invokerNode,
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode)
@ -68,7 +69,7 @@ public abstract class InteropConversionCallNode extends Node {
for (int i = 0; i < cachedArgsLength; i++) {
args[i] = hostValueToEnsoNode.execute(arguments[i]);
}
if (cachedArgsLength < 2) throw ArityException.create(2, cachedArgsLength);
if (cachedArgsLength < 2) throw ArityException.create(2, -1, cachedArgsLength);
return invokerNode.execute(null, state, conversion, args[0], args[1], args).getValue();
}
@ -84,7 +85,7 @@ public abstract class InteropConversionCallNode extends Node {
for (int i = 0; i < arguments.length; i++) {
args[i] = hostValueToEnsoNode.execute(arguments[i]);
}
if (arguments.length < 2) throw ArityException.create(2, arguments.length);
if (arguments.length < 2) throw ArityException.create(2, -1, arguments.length);
return indirectInvokeConversionNode
.execute(
null,
@ -96,7 +97,8 @@ public abstract class InteropConversionCallNode extends Node {
buildSchema(arguments.length),
DefaultsExecutionMode.EXECUTE,
ArgumentsExecutionMode.PRE_EXECUTED,
TailStatus.NOT_TAIL)
TailStatus.NOT_TAIL,
1)
.getValue();
}
}

View File

@ -61,7 +61,8 @@ public abstract class InteropMethodCallNode extends Node {
return InvokeMethodNode.build(
args,
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED,
0);
}
@Specialization(
@ -107,7 +108,8 @@ public abstract class InteropMethodCallNode extends Node {
buildSchema(arguments.length),
DefaultsExecutionMode.EXECUTE,
ArgumentsExecutionMode.PRE_EXECUTED,
TailStatus.NOT_TAIL)
TailStatus.NOT_TAIL,
0)
.getValue();
}
}

View File

@ -112,9 +112,11 @@ public abstract class InvokeCallableNode extends BaseNode {
this.invokeFunctionNode =
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
this.invokeMethodNode =
InvokeMethodNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
InvokeMethodNode.build(
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition);
this.invokeConversionNode =
InvokeConversionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
InvokeConversionNode.build(
schema, defaultsExecutionMode, argumentsExecutionMode, thatArgumentPosition);
}
public static Integer thisArgumentPosition(CallArgumentInfo[] schema) {
@ -143,9 +145,7 @@ public abstract class InvokeCallableNode extends BaseNode {
return null;
}
/**
*
* Creates a new instance of this node.
*
* @param schema a description of the arguments being applied to the callable
@ -215,20 +215,22 @@ public abstract class InvokeCallableNode extends BaseNode {
}
}
Stateful selfResult = thisExecutor.executeThunk(selfArgument, state, TailStatus.NOT_TAIL);
Stateful thatResult = thatExecutor.executeThunk(thatArgument, selfResult.getState(), TailStatus.NOT_TAIL);
Stateful thatResult =
thatExecutor.executeThunk(thatArgument, selfResult.getState(), TailStatus.NOT_TAIL);
selfArgument = selfResult.getValue();
thatArgument = thatResult.getValue();
state = thatResult.getState();
arguments[thisArgumentPosition] = selfArgument;
arguments[thatArgumentPosition] = thatArgument;
}
return invokeConversionNode.execute(callerFrame, state, conversion, selfArgument, thatArgument, arguments);
return invokeConversionNode.execute(
callerFrame, state, conversion, selfArgument, thatArgument, arguments);
} else {
throw new RuntimeException("Conversion currying without `this` or `that` argument is not supported.");
throw new RuntimeException(
"Conversion currying without `this` or `that` argument is not supported.");
}
}
@Specialization
public Stateful invokeDynamicSymbol(
UnresolvedSymbol symbol, VirtualFrame callerFrame, Object state, Object[] arguments) {
@ -261,8 +263,7 @@ public abstract class InvokeCallableNode extends BaseNode {
@Fallback
public Stateful invokeGeneric(
Object callable, VirtualFrame callerFrame, Object state, Object[] arguments) {
Context ctx = lookupContextReference(Language.class).get();
Atom error = ctx.getBuiltins().error().makeNotInvokableError(callable);
Atom error = Context.get(this).getBuiltins().error().makeNotInvokableError(callable);
throw new PanicException(error, this);
}

View File

@ -1,5 +1,6 @@
package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.VirtualFrame;
@ -20,19 +21,21 @@ import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.text.Text;
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.error.*;
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
import org.enso.interpreter.runtime.state.Stateful;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
public abstract class InvokeConversionNode extends BaseNode {
private @Child InvokeFunctionNode invokeFunctionNode;
private @Child InvokeConversionNode childDispatch;
private final ConditionProfile atomProfile = ConditionProfile.createCountingProfile();
private final ConditionProfile atomConstructorProfile = ConditionProfile.createCountingProfile();
private final int thatArgumentPosition;
/**
* Creates a new node for method invocation.
@ -45,16 +48,20 @@ public abstract class InvokeConversionNode extends BaseNode {
public static InvokeConversionNode build(
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
return InvokeConversionNodeGen.create(schema, defaultsExecutionMode, argumentsExecutionMode);
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
int thatArgumentPosition) {
return InvokeConversionNodeGen.create(
schema, defaultsExecutionMode, argumentsExecutionMode, thatArgumentPosition);
}
InvokeConversionNode(
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
int thatArgumentPosition) {
this.invokeFunctionNode =
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
this.thatArgumentPosition = thatArgumentPosition;
}
@Override
@ -74,7 +81,6 @@ public abstract class InvokeConversionNode extends BaseNode {
static AtomConstructor extractConstructor(
Node thisNode,
Object _this,
TruffleLanguage.ContextReference<Context> ctx,
ConditionProfile atomConstructorProfile,
ConditionProfile atomProfile) {
if (atomConstructorProfile.profile(_this instanceof AtomConstructor)) {
@ -83,12 +89,13 @@ public abstract class InvokeConversionNode extends BaseNode {
return ((Atom) _this).getConstructor();
} else {
throw new PanicException(
ctx.get().getBuiltins().error().makeInvalidConversionTargetError(_this), thisNode);
Context.get(thisNode).getBuiltins().error().makeInvalidConversionTargetError(_this),
thisNode);
}
}
AtomConstructor extractConstructor(Object _this, TruffleLanguage.ContextReference<Context> ctx) {
return extractConstructor(this, _this, ctx, atomConstructorProfile, atomProfile);
AtomConstructor extractConstructor(Object _this) {
return extractConstructor(this, _this, atomConstructorProfile, atomProfile);
}
@Specialization(guards = "dispatch.canConvertFrom(that)")
@ -99,15 +106,18 @@ public abstract class InvokeConversionNode extends BaseNode {
Object _this,
Object that,
Object[] arguments,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch) {
try {
Function function =
dispatch.getConversionFunction(that, extractConstructor(_this, ctx), conversion);
dispatch.getConversionFunction(that, extractConstructor(_this), conversion);
return invokeFunctionNode.execute(function, frame, state, arguments);
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
Context.get(this)
.getBuiltins()
.error()
.makeNoSuchConversionError(_this, that, conversion),
this);
}
}
@ -120,11 +130,10 @@ public abstract class InvokeConversionNode extends BaseNode {
DataflowError that,
Object[] arguments,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@Cached BranchProfile profile,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
@Cached BranchProfile profile) {
try {
Function function =
dispatch.getConversionFunction(that, extractConstructor(_this, ctx), conversion);
dispatch.getConversionFunction(that, extractConstructor(_this), conversion);
return invokeFunctionNode.execute(function, frame, state, arguments);
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
profile.enter();
@ -134,15 +143,51 @@ public abstract class InvokeConversionNode extends BaseNode {
@Specialization
Stateful doPanicSentinel(
VirtualFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
PanicSentinel that,
Object[] arguments) {
VirtualFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
PanicSentinel that,
Object[] arguments) {
throw that;
}
@Specialization
Stateful doWarning(
VirtualFrame frame,
Object state,
UnresolvedConversion conversion,
Object _this,
WithWarnings that,
Object[] arguments) {
// Cannot use @Cached for childDispatch, because we need to call notifyInserted.
if (childDispatch == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Lock lock = getLock();
lock.lock();
try {
if (childDispatch == null) {
childDispatch =
insert(
build(
invokeFunctionNode.getSchema(),
invokeFunctionNode.getDefaultsExecutionMode(),
invokeFunctionNode.getArgumentsExecutionMode(),
thatArgumentPosition));
childDispatch.setTailStatus(getTailStatus());
notifyInserted(childDispatch);
}
} finally {
lock.unlock();
}
}
arguments[thatArgumentPosition] = that.getValue();
ArrayRope<Warning> warnings = that.getReassignedWarnings(this);
Stateful result =
childDispatch.execute(frame, state, conversion, _this, that.getValue(), arguments);
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
}
@Specialization(guards = "interop.isString(that)")
Stateful doConvertText(
VirtualFrame frame,
@ -153,20 +198,23 @@ public abstract class InvokeConversionNode extends BaseNode {
Object[] arguments,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
@CachedLibrary(limit = "10") InteropLibrary interop) {
try {
String str = interop.asString(that);
Text txt = Text.create(str);
Function function =
textDispatch.getConversionFunction(txt, extractConstructor(_this, ctx), conversion);
textDispatch.getConversionFunction(txt, extractConstructor(_this), conversion);
arguments[0] = txt;
return invokeFunctionNode.execute(function, frame, state, arguments);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException("Impossible, that is guaranteed to be a string.");
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
Context.get(this)
.getBuiltins()
.error()
.makeNoSuchConversionError(_this, that, conversion),
this);
}
}
@ -184,10 +232,10 @@ public abstract class InvokeConversionNode extends BaseNode {
Object that,
Object[] arguments,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedContext(Language.class) Context ctx) {
@CachedLibrary(limit = "10") InteropLibrary interop) {
throw new PanicException(
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
Context.get(this).getBuiltins().error().makeNoSuchConversionError(_this, that, conversion),
this);
}
@Override

View File

@ -1,5 +1,6 @@
package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.VirtualFrame;
@ -12,6 +13,8 @@ import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
@ -21,10 +24,9 @@ import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.ArrayRope;
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.error.PanicException;
import org.enso.interpreter.runtime.error.*;
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
import org.enso.interpreter.runtime.state.Stateful;
@ -32,7 +34,10 @@ import org.enso.interpreter.runtime.state.Stateful;
public abstract class InvokeMethodNode extends BaseNode {
private @Child InvokeFunctionNode invokeFunctionNode;
private final ConditionProfile errorReceiverProfile = ConditionProfile.createCountingProfile();
private final BranchProfile polyglotArgumentErrorProfile = BranchProfile.create();
private @Child InvokeMethodNode childDispatch;
private final int argumentCount;
private final int thisArgumentPosition;
/**
* Creates a new node for method invocation.
@ -45,23 +50,30 @@ public abstract class InvokeMethodNode extends BaseNode {
public static InvokeMethodNode build(
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
return InvokeMethodNodeGen.create(schema, defaultsExecutionMode, argumentsExecutionMode);
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
int thisArgumentPosition) {
return InvokeMethodNodeGen.create(
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition);
}
InvokeMethodNode(
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
int thisArgumentPosition) {
this.invokeFunctionNode =
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
this.argumentCount = schema.length;
this.thisArgumentPosition = thisArgumentPosition;
}
@Override
public void setTailStatus(TailStatus tailStatus) {
super.setTailStatus(tailStatus);
this.invokeFunctionNode.setTailStatus(tailStatus);
if (childDispatch != null) {
childDispatch.setTailStatus(tailStatus);
}
}
public abstract Stateful execute(
@ -74,14 +86,13 @@ public abstract class InvokeMethodNode extends BaseNode {
UnresolvedSymbol symbol,
Object _this,
Object[] arguments,
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch) {
try {
Function function = dispatch.getFunctionalDispatch(_this, symbol);
return invokeFunctionNode.execute(function, frame, state, arguments);
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
}
}
@ -111,6 +122,41 @@ public abstract class InvokeMethodNode extends BaseNode {
throw _this;
}
@Specialization
Stateful doWarning(
VirtualFrame frame,
Object state,
UnresolvedSymbol symbol,
WithWarnings _this,
Object[] arguments) {
// Cannot use @Cached for childDispatch, because we need to call notifyInserted.
if (childDispatch == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
Lock lock = getLock();
lock.lock();
try {
if (childDispatch == null) {
childDispatch =
insert(
build(
invokeFunctionNode.getSchema(),
invokeFunctionNode.getDefaultsExecutionMode(),
invokeFunctionNode.getArgumentsExecutionMode(),
thisArgumentPosition));
childDispatch.setTailStatus(getTailStatus());
notifyInserted(childDispatch);
}
} finally {
lock.unlock();
}
}
arguments[thisArgumentPosition] = _this.getValue();
ArrayRope<Warning> warnings = _this.getReassignedWarnings(this);
Stateful result = childDispatch.execute(frame, state, symbol, _this.getValue(), arguments);
return new Stateful(result.getState(), WithWarnings.prependTo(result.getValue(), warnings));
}
@ExplodeLoop
@Specialization(
guards = {
@ -131,19 +177,33 @@ public abstract class InvokeMethodNode extends BaseNode {
HostMethodCallNode.PolyglotCallType polyglotCallType,
@Cached(value = "buildExecutors()") ThunkExecutorNode[] argExecutors,
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] profiles,
@Cached(value = "buildProfiles()", dimensions = 1) BranchProfile[] warningProfiles,
@Cached BranchProfile anyWarningsProfile,
@Cached HostMethodCallNode hostMethodCallNode) {
Object[] args = new Object[argExecutors.length];
boolean anyWarnings = false;
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>();
for (int i = 0; i < argExecutors.length; i++) {
Stateful r = argExecutors[i].executeThunk(arguments[i + 1], state, TailStatus.NOT_TAIL);
state = r.getState();
args[i] = r.getValue();
if (r.getValue() instanceof DataflowError) {
profiles[i].enter();
return r;
} else if (r.getValue() instanceof WithWarnings) {
warningProfiles[i].enter();
anyWarnings = true;
accumulatedWarnings =
accumulatedWarnings.append(((WithWarnings) r.getValue()).getReassignedWarnings(this));
args[i] = ((WithWarnings) r.getValue()).getValue();
}
state = r.getState();
args[i] = r.getValue();
}
return new Stateful(
state, hostMethodCallNode.execute(polyglotCallType, symbol.getName(), _this, args));
Object res = hostMethodCallNode.execute(polyglotCallType, symbol.getName(), _this, args);
if (anyWarnings) {
anyWarningsProfile.enter();
res = WithWarnings.prependTo(res, accumulatedWarnings);
}
return new Stateful(state, res);
}
@Specialization(
@ -160,8 +220,7 @@ public abstract class InvokeMethodNode extends BaseNode {
Object[] arguments,
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
@CachedLibrary(limit = "10") InteropLibrary interop) {
try {
String str = interop.asString(_this);
Text txt = Text.create(str);
@ -172,7 +231,7 @@ public abstract class InvokeMethodNode extends BaseNode {
throw new IllegalStateException("Impossible, _this is guaranteed to be a string.");
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
throw new PanicException(
ctx.get().getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
}
}

View File

@ -0,0 +1,45 @@
package org.enso.interpreter.node.callable.argument;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import java.util.stream.Stream;
public class GatherWarningsNode extends BaseNode {
private final int argsCount;
private final @CompilerDirectives.CompilationFinal(dimensions = 1) ConditionProfile[] profiles;
private GatherWarningsNode(int argsCount) {
this.argsCount = argsCount;
this.profiles =
Stream.generate(ConditionProfile::createCountingProfile)
.limit(argsCount)
.toArray(ConditionProfile[]::new);
}
public static GatherWarningsNode create(int argsCount) {
return new GatherWarningsNode(argsCount);
}
@ExplodeLoop
public ArrayRope<Warning> execute(Object... arguments) {
ArrayRope<Warning> result = new ArrayRope<>();
boolean anyFound = false;
for (int i = 0; i < argsCount; i++) {
if (profiles[i].profile(arguments[i] instanceof WithWarnings)) {
anyFound = true;
result = result.append(((WithWarnings) arguments[i]).getWarnings());
}
}
if (anyFound) {
return result;
} else {
return null;
}
}
}

View File

@ -160,15 +160,15 @@ public abstract class InvokeFunctionNode extends BaseNode {
public abstract Stateful execute(
Function callable, VirtualFrame callerFrame, Object state, Object[] arguments);
CallArgumentInfo[] getSchema() {
public CallArgumentInfo[] getSchema() {
return schema;
}
InvokeCallableNode.DefaultsExecutionMode getDefaultsExecutionMode() {
public InvokeCallableNode.DefaultsExecutionMode getDefaultsExecutionMode() {
return this.defaultsExecutionMode;
}
InvokeCallableNode.ArgumentsExecutionMode getArgumentsExecutionMode() {
public InvokeCallableNode.ArgumentsExecutionMode getArgumentsExecutionMode() {
return argumentsExecutionMode;
}
@ -187,4 +187,5 @@ public abstract class InvokeFunctionNode extends BaseNode {
public void setId(UUID id) {
functionCallInstrumentationNode.setId(id);
}
}

View File

@ -12,9 +12,8 @@ import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.ExpressionNode;
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.data.ArrayRope;
import org.enso.interpreter.runtime.error.*;
import org.enso.interpreter.runtime.type.TypesGen;
/**
@ -72,6 +71,13 @@ public abstract class CaseNode extends ExpressionNode {
throw sentinel;
}
@Specialization
Object doWarning(VirtualFrame frame, WithWarnings object) {
ArrayRope<Warning> warnings = object.getReassignedWarnings(this);
Object result = doMatch(frame, object.getValue());
return WithWarnings.appendTo(result, warnings);
}
/**
* Executes the case expression.
*
@ -80,12 +86,10 @@ public abstract class CaseNode extends ExpressionNode {
* @param ctx the language context reference
* @return the result of executing the case expression on {@code object}
*/
@Specialization(guards = {"!isDataflowError(object)", "!isPanicSentinel(object)"})
@Specialization(
guards = {"!isDataflowError(object)", "!isPanicSentinel(object)", "!isWarning(object)"})
@ExplodeLoop
public Object doMatch(
VirtualFrame frame,
Object object,
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
public Object doMatch(VirtualFrame frame, Object object) {
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
try {
for (BranchNode branchNode : cases) {
@ -93,7 +97,11 @@ public abstract class CaseNode extends ExpressionNode {
}
CompilerDirectives.transferToInterpreter();
throw new PanicException(
ctx.get().getBuiltins().error().inexhaustivePatternMatchError().newInstance(object),
Context.get(this)
.getBuiltins()
.error()
.inexhaustivePatternMatchError()
.newInstance(object),
this);
} catch (BranchSelectedException e) {
// Note [Branch Selection Control Flow]
@ -110,6 +118,10 @@ public abstract class CaseNode extends ExpressionNode {
return TypesGen.isPanicSentinel(sentinel);
}
boolean isWarning(Object warning) {
return warning instanceof WithWarnings;
}
/* Note [Branch Selection Control Flow]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Truffle provides no easy way to return control flow from multiple paths. This is entirely due

View File

@ -7,7 +7,11 @@ 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.Context;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.type.TypesGen;
/**
@ -19,16 +23,20 @@ public class InstantiateNode extends ExpressionNode {
private final AtomConstructor constructor;
private @Children ExpressionNode[] arguments;
private @CompilationFinal(dimensions = 1) ConditionProfile[] profiles;
private @CompilationFinal(dimensions = 1) ConditionProfile[] warningProfiles;
private @CompilationFinal(dimensions = 1) BranchProfile[] sentinelProfiles;
private final ConditionProfile anyWarningsProfile = ConditionProfile.createCountingProfile();
InstantiateNode(AtomConstructor constructor, ExpressionNode[] arguments) {
this.constructor = constructor;
this.arguments = arguments;
this.profiles = new ConditionProfile[arguments.length];
this.sentinelProfiles = new BranchProfile[arguments.length];
this.warningProfiles = new ConditionProfile[arguments.length];
for (int i = 0; i < arguments.length; ++i) {
this.profiles[i] = ConditionProfile.createCountingProfile();
this.sentinelProfiles[i] = BranchProfile.create();
this.warningProfiles[i] = ConditionProfile.createCountingProfile();
}
}
@ -54,12 +62,21 @@ public class InstantiateNode extends ExpressionNode {
@ExplodeLoop
public Object executeGeneric(VirtualFrame frame) {
Object[] argumentValues = new Object[arguments.length];
boolean anyWarnings = false;
ArrayRope<Warning> accumulatedWarnings = new ArrayRope<>();
for (int i = 0; i < arguments.length; i++) {
ConditionProfile profile = profiles[i];
ConditionProfile warningProfile = warningProfiles[i];
BranchProfile sentinelProfile = sentinelProfiles[i];
Object argument = arguments[i].executeGeneric(frame);
if (profile.profile(TypesGen.isDataflowError(argument))) {
return argument;
} else if (warningProfile.profile(argument instanceof WithWarnings)) {
anyWarnings = true;
WithWarnings originalArg = (WithWarnings) argument;
accumulatedWarnings =
accumulatedWarnings.append(originalArg.getReassignedWarnings(this));
argumentValues[i] = originalArg.getValue();
} else if (TypesGen.isPanicSentinel(argument)) {
sentinelProfile.enter();
throw TypesGen.asPanicSentinel(argument);
@ -67,6 +84,10 @@ public class InstantiateNode extends ExpressionNode {
argumentValues[i] = argument;
}
}
return constructor.newInstance(argumentValues);
if (anyWarningsProfile.profile(anyWarnings)) {
return WithWarnings.appendTo(constructor.newInstance(argumentValues), accumulatedWarnings);
} else {
return constructor.newInstance(argumentValues);
}
}
}

View File

@ -40,7 +40,7 @@ public class InstantiateAtomNode extends RootNode {
*/
@Override
public String getName() {
return "constructor::" + name;
return name;
}
/**

View File

@ -8,6 +8,7 @@ import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.Language;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.runtime.Context;
@ -24,7 +25,8 @@ public abstract class PutStateNode extends Node {
return PutStateNodeGen.create();
}
abstract Stateful execute(@MonadicState Object state, Object _this, Object key, Object new_state);
abstract Stateful execute(
@MonadicState Object state, Object _this, Object key, Object new_state);
@Specialization(guards = "state.getKey() == key")
Stateful doExistingSingleton(SingletonMap state, Object _this, Object key, Object new_state) {

View File

@ -0,0 +1,34 @@
package org.enso.interpreter.node.expression.builtin.warning;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
@BuiltinMethod(
type = "Prim_Warning",
name = "attach",
description = "Attaches the given warning to the value.")
public abstract class AttachWarningNode extends Node {
abstract WithWarnings execute(
Object _this, @AcceptsWarning Object value, Object warning, Object origin);
static AttachWarningNode build() {
return AttachWarningNodeGen.create();
}
@Specialization
WithWarnings doWarning(Object _this, WithWarnings value, Object warning, Object origin) {
return value.prepend(new Warning(warning, origin, Context.get(this).clockTick()));
}
@Fallback
WithWarnings doOther(Object _this, Object value, Object warning, Object origin) {
return new WithWarnings(value, new Warning(warning, origin, Context.get(this).clockTick()));
}
}

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.node.expression.builtin.warning;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.error.Warning;
@BuiltinMethod(
type = "Prim_Warning",
name = "get_origin",
description = "Gets the payload of the warning.")
public class GetOriginNode extends Node {
Object execute(Object _this, Warning warning) {
return warning.getOrigin();
}
}

View File

@ -0,0 +1,23 @@
package org.enso.interpreter.node.expression.builtin.warning;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.Warning;
import java.util.Arrays;
import java.util.Comparator;
@BuiltinMethod(
type = "Prim_Warning",
name = "get_reassignments",
description = "Gets the list of locations where the warnings was reassigned.")
public class GetReassignmentsNode extends Node {
Array execute(Object _this, Warning warning) {
Warning.Reassignment[] reassignments =
warning.getReassignments().toArray(Warning.Reassignment[]::new);
return new Array(Arrays.copyOf(reassignments, reassignments.length, Object[].class));
}
}

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.node.expression.builtin.warning;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.error.Warning;
@BuiltinMethod(
type = "Prim_Warning",
name = "get_value",
description = "Gets the payload of the warning.")
public class GetValueNode extends Node {
Object execute(Object _this, Warning warning) {
return warning.getValue();
}
}

View File

@ -0,0 +1,39 @@
package org.enso.interpreter.node.expression.builtin.warning;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import java.util.Arrays;
import java.util.Comparator;
@BuiltinMethod(
type = "Prim_Warning",
name = "get_all",
description = "Gets all the warnings associated with the value.")
public abstract class GetWarningsNode extends Node {
abstract Array execute(Object _this, @AcceptsWarning Object value);
static GetWarningsNode build() {
return GetWarningsNodeGen.create();
}
@Specialization
Array doWarning(Object _this, WithWarnings value) {
Warning[] warnings = value.getWarningsArray();
Arrays.sort(warnings, Comparator.comparing(Warning::getCreationTime).reversed());
Object[] result = new Object[warnings.length];
System.arraycopy(warnings, 0, result, 0, warnings.length);
return new Array(result);
}
@Fallback
Array doOther(Object _this, Object value) {
return new Array();
}
}

View File

@ -0,0 +1,43 @@
package org.enso.interpreter.node.expression.builtin.warning;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsWarning;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
@BuiltinMethod(
type = "Prim_Warning",
name = "set",
description = "Attaches the given warning to the value.")
public abstract class SetNode extends Node {
abstract Object execute(Object _this, @AcceptsWarning Object value, Array warnings);
static SetNode build() {
return SetNodeGen.create();
}
@Specialization
Object doWarning(Object _this, WithWarnings value, Array warnings) {
if (warnings.length() == 0) {
return value.getValue();
}
Warning[] warningsCast = new Warning[warnings.length()];
System.arraycopy(warnings.getItems(), 0, warningsCast, 0, warningsCast.length);
return new WithWarnings(value.getValue(), warningsCast);
}
@Fallback
Object doOther(Object _this, Object value, Array warnings) {
if (warnings.length() == 0) {
return value;
}
Warning[] warningsCast = new Warning[warnings.length()];
System.arraycopy(warnings.getItems(), 0, warningsCast, 0, warningsCast.length);
return new WithWarnings(value, warningsCast);
}
}

View File

@ -31,6 +31,7 @@ import scala.jdk.javaapi.OptionConverters;
import java.io.*;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
* The language context is the internal state of the language that is associated with each thread in
@ -62,6 +63,7 @@ public class Context {
private final TruffleLogger logger = TruffleLogger.getLogger(LanguageInfo.ID, Context.class);
private final DistributionManager distributionManager;
private final LockManager lockManager;
private final AtomicLong clock = new AtomicLong();
/**
* Creates a new Enso context.
@ -151,6 +153,10 @@ public class Context {
return REFERENCE.get(node);
}
public static TruffleLanguage.ContextReference<Context> getReference() {
return REFERENCE;
}
/** Performs eventual cleanup before the context is disposed of. */
public void shutdown() {
threadManager.shutdown();
@ -449,4 +455,8 @@ public class Context {
public TruffleLogger getLogger(Class<?> klass) {
return TruffleLogger.getLogger(LanguageInfo.ID, klass);
}
public long clockTick() {
return clock.getAndIncrement();
}
}

View File

@ -93,6 +93,7 @@ public class Builtins {
bool = new Bool(language, scope);
debug = new AtomConstructor("Debug", scope).initializeFields();
dataflowError = new DataflowError(language, scope);
Warning.initWarningMethods(language, scope);
projectDescription =
new AtomConstructor("Project_Description", scope)
.initializeFields(

View File

@ -0,0 +1,21 @@
package org.enso.interpreter.runtime.builtin;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.warning.*;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.scope.ModuleScope;
public class Warning {
public static void initWarningMethods(Language language, ModuleScope scope) {
var warning = new AtomConstructor("Prim_Warning", scope).initializeFields();
scope.registerConstructor(warning);
scope.registerMethod(warning, "get_all", GetWarningsMethodGen.makeFunction(language));
scope.registerMethod(warning, "attach", AttachWarningMethodGen.makeFunction(language));
scope.registerMethod(warning, "get_value", GetValueMethodGen.makeFunction(language));
scope.registerMethod(warning, "get_origin", GetOriginMethodGen.makeFunction(language));
scope.registerMethod(
warning, "get_reassignments", GetReassignmentsMethodGen.makeFunction(language));
scope.registerMethod(warning, "set", SetMethodGen.makeFunction(language));
}
}

View File

@ -82,7 +82,9 @@ public final class AtomConstructor implements TruffleObject {
argumentReaders[i] = ReadArgumentNode.build(i, args[i].getDefaultValue().orElse(null));
}
ExpressionNode instantiateNode = InstantiateNode.build(this, argumentReaders);
RootNode rootNode = InstantiateAtomNode.build(null, name, instantiateNode);
RootNode rootNode =
InstantiateAtomNode.build(
null, definitionScope.getModule().getName().item() + "." + name, instantiateNode);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
return new Function(callTarget, null, new FunctionSchema(args));
}
@ -303,7 +305,8 @@ public final class AtomConstructor implements TruffleObject {
@Cached("conversion") UnresolvedConversion cachedConversion,
@Cached("target") AtomConstructor cachedTarget,
@Cached("_this") AtomConstructor cachedConstructor,
@Cached("doResolve(context, cachedConstructor, cachedTarget, cachedConversion)") Function function) {
@Cached("doResolve(context, cachedConstructor, cachedTarget, cachedConversion)")
Function function) {
return function;
}

View File

@ -0,0 +1,121 @@
package org.enso.interpreter.runtime.data;
import java.util.Arrays;
import java.util.function.Function;
public final class ArrayRope<T> {
private final ArrayRopeSegment<T> segment;
private ArrayRope(ArrayRopeSegment<T> segment) {
this.segment = segment;
}
@SafeVarargs
public ArrayRope(T... elements) {
segment = new ArraySegment<>(elements);
}
public ArrayRope<T> append(ArrayRope<T> that) {
return new ArrayRope<>(new ConcatSegment<>(this.segment, that.segment));
}
@SafeVarargs
public final ArrayRope<T> append(T... items) {
return new ArrayRope<>(new ConcatSegment<>(this.segment, new ArraySegment<>(items)));
}
public ArrayRope<T> prepend(ArrayRope<T> that) {
return new ArrayRope<>(new ConcatSegment<>(that.segment, this.segment));
}
@SafeVarargs
public final ArrayRope<T> prepend(T... items) {
return new ArrayRope<>(new ConcatSegment<>(new ArraySegment<>(items), this.segment));
}
public T[] toArray(Function<Integer, T[]> genArray) {
T[] res = genArray.apply(size());
writeArray(res);
return res;
}
public void writeArray(T[] arr) {
segment.appendTo(arr, 0);
}
public int size() {
return segment.size();
}
@Override
public String toString() {
return "ArrayRope{" + "segment=" + segment + '}';
}
private interface ArrayRopeSegment<T> {
void appendTo(T[] builder, int start);
int size();
}
private static final class ArraySegment<T> implements ArrayRopeSegment<T> {
private final T[] elements;
public ArraySegment(T[] elements) {
this.elements = elements;
}
@Override
public void appendTo(T[] builder, int start) {
System.arraycopy(elements, 0, builder, start, elements.length);
}
@Override
public int size() {
return elements.length;
}
@Override
public String toString() {
return "ArraySegment{" + "elements=" + Arrays.toString(elements) + '}';
}
}
private static final class ConcatSegment<T> implements ArrayRopeSegment<T> {
private final ArrayRopeSegment<T> left;
private final ArrayRopeSegment<T> right;
private int cachedSize = UNKNOWN;
private static final int UNKNOWN = -1;
public ConcatSegment(ArrayRopeSegment<T> left, ArrayRopeSegment<T> right) {
this.left = left;
this.right = right;
}
@Override
public void appendTo(T[] builder, int start) {
left.appendTo(builder, start);
right.appendTo(builder, start + left.size());
}
@Override
public int size() {
if (cachedSize == UNKNOWN) {
cachedSize = left.size() + right.size();
}
return cachedSize;
}
@Override
public String toString() {
return "ConcatSegment{"
+ "left="
+ left
+ ", right="
+ right
+ ", cachedSize="
+ cachedSize
+ '}';
}
}
}

View File

@ -0,0 +1,84 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.data.EnsoSourceSection;
public class Warning implements TruffleObject {
private final Object value;
private final Object origin;
private final ArrayRope<Reassignment> reassignments;
private final long creationTime;
public Warning(Object value, Object origin, long creationTime) {
this(value, origin, creationTime, new ArrayRope<>());
}
public Warning(
Object value, Object origin, long creationTime, ArrayRope<Reassignment> reassignments) {
this.value = value;
this.origin = origin;
this.reassignments = reassignments;
this.creationTime = creationTime;
}
@ExportLibrary(InteropLibrary.class)
public static class Reassignment implements TruffleObject {
private final String methodName;
private final SourceSection location;
public Reassignment(String methodName, SourceSection location) {
this.methodName = methodName;
this.location = location;
}
@ExportMessage
boolean hasExecutableName() {
return true;
}
@ExportMessage
String getExecutableName() {
return methodName;
}
@ExportMessage
boolean hasSourceLocation() {
return location != null;
}
@ExportMessage
SourceSection getSourceLocation() {
return location;
}
}
public Object getValue() {
return value;
}
public Object getOrigin() {
return origin;
}
public long getCreationTime() {
return creationTime;
}
public ArrayRope<Reassignment> getReassignments() {
return reassignments;
}
public Warning reassign(Node location) {
RootNode root = location.getRootNode();
SourceSection section = location.getEncapsulatingSourceSection();
Reassignment reassignment = new Reassignment(root.getName(), section);
return new Warning(value, origin, creationTime, reassignments.prepend(reassignment));
}
}

View File

@ -0,0 +1,83 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.ArrayRope;
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
@ExportLibrary(MethodDispatchLibrary.class)
public class WithWarnings implements TruffleObject {
private final ArrayRope<Warning> warnings;
private final Object value;
public WithWarnings(Object value, Warning... warnings) {
this.warnings = new ArrayRope<>(warnings);
this.value = value;
}
private WithWarnings(Object value, ArrayRope<Warning> warnings) {
this.warnings = warnings;
this.value = value;
}
public Object getValue() {
return value;
}
public WithWarnings append(Warning... newWarnings) {
return new WithWarnings(value, warnings.append(newWarnings));
}
public WithWarnings append(ArrayRope<Warning> newWarnings) {
return new WithWarnings(value, warnings.append(newWarnings));
}
public WithWarnings prepend(Warning... newWarnings) {
return new WithWarnings(value, warnings.prepend(newWarnings));
}
public WithWarnings prepend(ArrayRope<Warning> newWarnings) {
return new WithWarnings(value, warnings.prepend(newWarnings));
}
public Warning[] getWarningsArray() {
return warnings.toArray(Warning[]::new);
}
public ArrayRope<Warning> getWarnings() {
return warnings;
}
public ArrayRope<Warning> getReassignedWarnings(Node location) {
Warning[] warnings = getWarningsArray();
for (int i = 0; i < warnings.length; i++) {
warnings[i] = warnings[i].reassign(location);
}
return new ArrayRope<>(warnings);
}
public static WithWarnings appendTo(Object target, ArrayRope<Warning> warnings) {
if (target instanceof WithWarnings) {
return ((WithWarnings) target).append(warnings);
} else {
return new WithWarnings(target, warnings);
}
}
public static WithWarnings prependTo(Object target, ArrayRope<Warning> warnings) {
if (target instanceof WithWarnings) {
return ((WithWarnings) target).prepend(warnings);
} else {
return new WithWarnings(target, warnings);
}
}
@ExportMessage
boolean hasSpecialDispatch() {
return true;
}
}

View File

@ -17,6 +17,7 @@ import org.enso.interpreter.runtime.data.text.Text;
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.error.Warning;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.polyglot.data.TypeGraph;
@ -49,7 +50,8 @@ import org.enso.polyglot.data.TypeGraph;
ModuleScope.class,
Ref.class,
PanicException.class,
PanicSentinel.class
PanicSentinel.class,
Warning.class
})
public class Types {

View File

@ -243,6 +243,30 @@ type Error
to_text : Text
to_text = @Builtin_Method "Error.to_text"
@Builtin_Type
type Prim_Warning
type Prim_Warning
## PRIVATE
attach : Any -> Any -> Any
attach value warning = @Builtin_Method "Prim_Warning.attach"
## PRIVATE
get_all : Any -> Array Prim_Warning
get_all value = @Builtin_Method "Prim_Warning.get_all"
## PRIVATE
get_origin : Prim_Warning -> Any
get_origin warn = @Builtin_Method "Prim_Warning.get_origin"
## PRIVATE
get_value : Prim_Warning -> Any
get_value warn = @Builtin_Method "Prim_Warning.get_value"
## PRIVATE
get_reassignments : Prim_Warning -> Any
get_reassignments warn = @Builtin_Method "Prim_Warning.get_reassignments"
## The runtime representation of a syntax error.
Arguments:

View File

@ -0,0 +1,11 @@
package org.enso.interpreter.dsl;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** An interface marking an argument as allowing it to accept a WithWarnings. */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.SOURCE)
public @interface AcceptsWarning {}

View File

@ -14,6 +14,7 @@ import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;
/** The processor used to generate code from the {@link BuiltinMethod} annotation. */
@SupportedAnnotationTypes("org.enso.interpreter.dsl.BuiltinMethod")
@ -82,7 +83,11 @@ public class MethodProcessor extends AbstractProcessor {
"org.enso.interpreter.runtime.callable.argument.ArgumentDefinition",
"org.enso.interpreter.runtime.callable.function.Function",
"org.enso.interpreter.runtime.callable.function.FunctionSchema",
"org.enso.interpreter.runtime.Context",
"org.enso.interpreter.runtime.data.ArrayRope",
"org.enso.interpreter.runtime.error.PanicException",
"org.enso.interpreter.runtime.error.Warning",
"org.enso.interpreter.runtime.error.WithWarnings",
"org.enso.interpreter.runtime.state.Stateful",
"org.enso.interpreter.runtime.type.TypesGen");
@ -111,15 +116,21 @@ public class MethodProcessor extends AbstractProcessor {
for (MethodDefinition.ArgumentDefinition arg : methodDefinition.getArguments()) {
if (!arg.isState() && !arg.isFrame() && !arg.isCallerInfo()) {
String condName = "arg" + arg.getPosition() + "ConditionProfile";
String branchName = "arg" + arg.getPosition() + "BranchProfile";
String condName = mkArgumentInternalVarName(arg) + "ConditionProfile";
String branchName = mkArgumentInternalVarName(arg) + "BranchProfile";
out.println(
" private final ConditionProfile "
+ condName
+ " = ConditionProfile.createCountingProfile();");
out.println(" private final BranchProfile " + branchName + " = BranchProfile.create();");
if (!arg.isThis() && !arg.acceptsWarning()) {
String warningName = mkArgumentInternalVarName(arg) + "WarningProfile";
out.println(
" private final BranchProfile " + warningName + " = BranchProfile.create();");
}
}
}
out.println(" private final BranchProfile anyWarningsProfile = BranchProfile.create();");
out.println(" private " + methodDefinition.getClassName() + "(Language language) {");
out.println(" super(language);");
@ -169,6 +180,8 @@ public class MethodProcessor extends AbstractProcessor {
out.println(
" Object[] arguments = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());");
List<String> callArgNames = new ArrayList<>();
boolean warningsPossible =
generateWarningsCheck(out, methodDefinition.getArguments(), "arguments");
for (MethodDefinition.ArgumentDefinition argumentDefinition :
methodDefinition.getArguments()) {
if (argumentDefinition.isState()) {
@ -178,15 +191,37 @@ public class MethodProcessor extends AbstractProcessor {
} else if (argumentDefinition.isCallerInfo()) {
callArgNames.add("callerInfo");
} else {
callArgNames.add("arg" + argumentDefinition.getPosition());
callArgNames.add(mkArgumentInternalVarName(argumentDefinition));
generateArgumentRead(out, argumentDefinition, "arguments");
}
}
String executeCall = "bodyNode.execute(" + String.join(", ", callArgNames) + ")";
if (methodDefinition.modifiesState()) {
out.println(" return " + executeCall + ";");
if (warningsPossible) {
out.println(" if (anyWarnings) {");
out.println(" anyWarningsProfile.enter();");
if (methodDefinition.modifiesState()) {
out.println(" Stateful result = " + executeCall + ";");
out.println(
" Object newValue = WithWarnings.appendTo(result.getValue(), gatheredWarnings);");
out.println(" return new Stateful(result.getState(), newValue);");
} else {
out.println(" Object result = " + executeCall + ";");
out.println(
" return new Stateful(state, WithWarnings.appendTo(result, gatheredWarnings));");
}
out.println(" } else {");
if (methodDefinition.modifiesState()) {
out.println(" return " + executeCall + ";");
} else {
out.println(" return new Stateful(state, " + executeCall + ");");
}
out.println(" }");
} else {
out.println(" return new Stateful(state, " + executeCall + ");");
if (methodDefinition.modifiesState()) {
out.println(" return " + executeCall + ";");
} else {
out.println(" return new Stateful(state, " + executeCall + ");");
}
}
out.println(" }");
@ -215,10 +250,7 @@ public class MethodProcessor extends AbstractProcessor {
out.println(" @Override");
out.println(" protected RootNode cloneUninitialized() {");
out.println(
" return new "
+ methodDefinition.getClassName()
+ "(lookupLanguageReference(Language.class).get());");
out.println(" return new " + methodDefinition.getClassName() + "(Language.get(this));");
out.println(" }");
out.println();
@ -239,8 +271,8 @@ public class MethodProcessor extends AbstractProcessor {
if (!arg.acceptsError()) {
String varName = "arg" + arg.getPosition();
String condProfile = "arg" + arg.getPosition() + "ConditionProfile";
String varName = mkArgumentInternalVarName(arg);
String condProfile = mkArgumentInternalVarName(arg) + "ConditionProfile";
out.println(
" if ("
+ condProfile
@ -252,7 +284,7 @@ public class MethodProcessor extends AbstractProcessor {
+ ");\n"
+ " }");
if (!(arg.getName().equals("this") && arg.getPosition() == 0)) {
String branchProfile = "arg" + arg.getPosition() + "BranchProfile";
String branchProfile = mkArgumentInternalVarName(arg) + "BranchProfile";
out.println(
" else if (TypesGen.isPanicSentinel("
+ varName
@ -270,7 +302,7 @@ public class MethodProcessor extends AbstractProcessor {
private void generateUncastedArgumentRead(
PrintWriter out, MethodDefinition.ArgumentDefinition arg, String argsArray) {
String varName = "arg" + arg.getPosition();
String varName = mkArgumentInternalVarName(arg);
out.println(
" "
+ arg.getTypeName()
@ -286,7 +318,7 @@ public class MethodProcessor extends AbstractProcessor {
private void generateUncheckedArgumentRead(
PrintWriter out, MethodDefinition.ArgumentDefinition arg, String argsArray) {
String castName = "TypesGen.as" + capitalize(arg.getTypeName());
String varName = "arg" + arg.getPosition();
String varName = mkArgumentInternalVarName(arg);
out.println(
" "
+ arg.getTypeName()
@ -304,13 +336,13 @@ public class MethodProcessor extends AbstractProcessor {
private void generateCheckedArgumentRead(
PrintWriter out, MethodDefinition.ArgumentDefinition arg, String argsArray) {
String castName = "TypesGen.expect" + capitalize(arg.getTypeName());
String varName = "arg" + arg.getPosition();
String varName = mkArgumentInternalVarName(arg);
out.println(" " + arg.getTypeName() + " " + varName + ";");
out.println(" try {");
out.println(
" " + varName + " = " + castName + "(" + argsArray + "[" + arg.getPosition() + "]);");
out.println(" } catch (UnexpectedResultException e) {");
out.println(" var builtins = lookupContextReference(Language.class).get().getBuiltins();");
out.println(" var builtins = Context.get(this).getBuiltins();");
out.println(
" var expected = builtins.fromTypeSystem(TypesGen.getName(arguments["
+ arg.getPosition()
@ -325,6 +357,52 @@ public class MethodProcessor extends AbstractProcessor {
out.println(" }");
}
private boolean generateWarningsCheck(
PrintWriter out, List<MethodDefinition.ArgumentDefinition> arguments, String argumentsArray) {
List<MethodDefinition.ArgumentDefinition> argsToCheck =
arguments.stream()
.filter(arg -> !arg.acceptsWarning() && !arg.isThis())
.collect(Collectors.toList());
if (argsToCheck.isEmpty()) {
return false;
} else {
out.println(" boolean anyWarnings = false;");
out.println(" ArrayRope<Warning> gatheredWarnings = new ArrayRope<>();");
for (var arg : argsToCheck) {
out.println(
" if ("
+ arrayRead(argumentsArray, arg.getPosition())
+ " instanceof WithWarnings) {");
out.println(" " + mkArgumentInternalVarName(arg) + "WarningProfile.enter();");
out.println(" anyWarnings = true;");
out.println(
" WithWarnings withWarnings = (WithWarnings) "
+ arrayRead(argumentsArray, arg.getPosition())
+ ";");
out.println(
" "
+ arrayRead(argumentsArray, arg.getPosition())
+ " = withWarnings.getValue();");
out.println(
" gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarnings(this));");
out.println(" }");
}
return true;
}
}
private String warningCheck(MethodDefinition.ArgumentDefinition arg) {
return "(" + mkArgumentInternalVarName(arg) + " instanceof WithWarnings)";
}
private String mkArgumentInternalVarName(MethodDefinition.ArgumentDefinition arg) {
return "arg" + arg.getPosition();
}
private String arrayRead(String array, int index) {
return array + "[" + index + "]";
}
private String capitalize(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}

View File

@ -1,9 +1,6 @@
package org.enso.interpreter.dsl.model;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.dsl.Suspend;
import org.enso.interpreter.dsl.*;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
@ -185,6 +182,7 @@ public class MethodDefinition {
private final boolean isCallerInfo;
private final boolean isSuspended;
private final boolean acceptsError;
private final boolean acceptsWarning;
private final int position;
private final VariableElement element;
@ -206,6 +204,7 @@ public class MethodDefinition {
acceptsError =
(element.getAnnotation(AcceptsError.class) != null)
|| type.toString().equals(DATAFLOW_ERROR);
acceptsWarning = element.getAnnotation(AcceptsWarning.class) != null;
isFrame = type.toString().equals(VIRTUAL_FRAME);
isCallerInfo = type.toString().equals(CALLER_INFO);
this.position = position;
@ -288,5 +287,13 @@ public class MethodDefinition {
public boolean acceptsError() {
return acceptsError;
}
public boolean acceptsWarning() {
return acceptsWarning;
}
public boolean isThis() {
return name.equals("this") || position == 0;
}
}
}

View File

@ -11,6 +11,7 @@ import project.Semantic.Import_Loop.Spec as Import_Loop_Spec
import project.Semantic.Meta_Spec
import project.Semantic.Names_Spec
import project.Semantic.Runtime_Spec
import project.Semantic.Warnings_Spec
import project.Semantic.Java_Interop_Spec
import project.Semantic.Js_Interop_Spec
@ -90,3 +91,4 @@ main = Test.Suite.run_main <|
Time_Spec.spec
Uri_Spec.spec
Vector_Spec.spec
Warnings_Spec.spec

View File

@ -0,0 +1,105 @@
from Standard.Base import all
polyglot java import java.lang.Long
import Standard.Test
type My_Warning reason
type My_Type a b c
My_Type.my_method = this.a + this.b + this.c
type Wrap foo
rewrap w = case w of
Wrap a -> Wrap a+1
poly_sum x y =
Long.sum x y
get_foo x = x.foo
unwrap x = Integer.from x
reassign_test x =
consed = Wrap x
reconsed = here.rewrap consed
i = here.unwrap reconsed
rereconsed = Wrap i
x1 = here.get_foo rereconsed
prim_sum = 1 + x1
r = here.poly_sum prim_sum 1
r
baz value = Warning.attach value "I have warned you"
bar value = here.baz value
foo value = here.bar value
Integer.from (that:Wrap) = that.foo
spec = Test.group "Dataflow Warnings" <|
Test.specify "should allow to attach multiple warnings and read them back" <|
x = 1233
y = Warning.attach "don't do this" x
z = Warning.attach "I'm serious" y
Warning.get_all z . map .value . should_equal ["I'm serious", "don't do this"]
Test.specify "should thread warnings through constructor calls" <|
z = Warning.attach (My_Warning "warn!!!") 3
y = Warning.attach (My_Warning "warn!!") 2
x = Warning.attach (My_Warning "warn!") 1
mtp = My_Type x y z
mtp.should_equal (My_Type 1 2 3)
Warning.get_all mtp . map .value . should_equal [My_Warning "warn!", My_Warning "warn!!", My_Warning "warn!!!"]
Test.specify "should thread warnings through method calls"
mtp = My_Type 1 2 3
warned = Warning.attach "omgggg" mtp
r = warned.my_method
r.should_equal 6
Warning.get_all r . map .value . should_equal ["omgggg"]
Test.specify "should thread warnings through polyglot calls" <|
y = Warning.attach "warn!!" 2
x = Warning.attach "warn!" 1
r = Long.sum x y
r.should_equal 3
Warning.get_all r . map .value . should_equal ['warn!', 'warn!!']
Test.specify "should thread warnings through case expressions" <|
z = Warning.attach (My_Warning "warn!!!") 3
y = Warning.attach (My_Warning "warn!!") 2
x = Warning.attach (My_Warning "warn!") 1
mtp = My_Type x y z
r = case mtp of
My_Type a b c -> a + b + c
r.should_equal 6
Warning.get_all r . map .value . should_equal [My_Warning "warn!", My_Warning "warn!!", My_Warning "warn!!!"]
Test.specify "should thread warnings through conversions" <|
z = Wrap (Warning.attach 'warn!' 1)
i = Integer.from z
Warning.get_all i . map .value . should_equal ['warn!']
Test.specify "should attach correct stacktraces" <|
current = Runtime.get_stack_trace
warned = here.foo "value"
warning_stack = Warning.get_all warned . head . origin
relevant = warning_stack . drop_end current.length
relevant.map .name . should_equal (['baz', 'bar', 'foo'].map ('Warnings_Spec.'+))
Test.specify "should attach reassignment info in the last-reassigned-first order" <|
x = Warning.attach "warn!" 1
r = here.reassign_test x
warn = Warning.get_all r . head
reassignments = warn.reassignments.map .name
reassignments.should_equal ['Warnings_Spec.poly_sum', 'Small_Integer.+', 'Warnings_Spec.get_foo', 'Warnings_Spec.Wrap', 'Warnings_Spec.unwrap', 'Warnings_Spec.rewrap', 'Warnings_Spec.Wrap']
Test.specify "should allow to set all warnings" <|
warned = Warning.attach 1 <| Warning.attach 2 <| Warning.attach 3 <| Warning.attach 4 "foo"
warnings = Warning.get_all warned
filtered = warnings.filter x-> x.value % 2 == 0
rewarned = Warning.set filtered warned
rewarned.should_equal 'foo'
Warning.get_all rewarned . map .value . should_equal [2,4]