mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 00:52:09 +03:00
Implement conversions (#180312665) (#3227)
* Implement conversions
start wip branch for conversion methods for collaborating with marcin
add conversions to MethodDispatchLibrary (wip)
start MethodDispatchLibrary implementations
conversions for atoms and functions
Implement a bunch of missing conversion lookups
final bug fixes for merged methoddispatchlibrary implementations
UnresolvedConversion.resolveFor
progress on invokeConversion
start extracting constructors (still not working)
fix a bug
add some initial conversion tests
fix a bug in qualified name resolution, test conversions accross modules
implement error reporting, discover a ton of ignored errors...
start fixing errors that we exposed in the standard library
fix remaining standard lib type errors not caused by the inability to parse type signatures for operators
TODO: fix type signatures for operators. all of them are broken
fix type signature parsing for operators
test cases for meta & polyglot
play nice with polyglot
start pretending unresolved conversions are unresolved symbols
treat UnresolvedConversons as UnresolvedSymbols in enso user land
* update RELEASES.md
* disable test error about from conversions being tail calls. (pivotal issue #181113110)
* add changelog entry
* fix OverloadsResolutionTest
* fix MethodDefinitionsTest
* fix DataflowAnalysisTest
* the field name for a from conversion must be 'that'. Fix remaining tests that aren't ExpressionUpdates vs. ExecutionUpdate behavioral changes
* fix ModuleThisToHereTest
* feat: suppress compilation errors from Builtins
* Revert "feat: suppress compilation errors from Builtins"
This reverts commit 63d069bd4f
.
* fix tests
* fix: formatting
Co-authored-by: Dmitry Bushev <bushevdv@gmail.com>
Co-authored-by: Marcin Kostrzewa <marckostrzewa@gmail.com>
This commit is contained in:
parent
9c689dfe42
commit
8a70debb59
@ -10,6 +10,12 @@
|
||||
- Updated the Scala compiler and dependencies
|
||||
([#3214](https://github.com/enso-org/enso/pull/3214)).
|
||||
|
||||
## Interpreter/Runtime
|
||||
|
||||
- Added support for overloaded conversions. This allows the `from` method to be
|
||||
implemented with several overloads.
|
||||
([#3227](https://github.com/enso-org/enso/pull/3227))
|
||||
|
||||
## Enso 0.2.31 (2021-10-01)
|
||||
|
||||
## Interpreter/Runtime
|
||||
|
@ -44,6 +44,12 @@
|
||||
[3230]: https://github.com/enso-org/enso/pull/3230
|
||||
[3240]: https://github.com/enso-org/enso/pull/3240
|
||||
|
||||
#### Enso Compiler
|
||||
|
||||
- [Added overloaded `from` conversions.][3227]
|
||||
|
||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||
|
||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||
|
||||
<br/>![New Features](/docs/assets/tags/new_features.svg)
|
||||
|
@ -48,6 +48,8 @@ Any.== that = if Meta.is_same_object this that then True else
|
||||
Cons (Meta.Polyglot o_1) (Meta.Polyglot o_2) ->
|
||||
langs_match = (this_meta.get_language == Meta.Java) && (that_meta.get_language == Meta.Java)
|
||||
if langs_match.not then False else o_1.equals o_2
|
||||
Cons (Meta.Unresolved_Symbol _) (Meta.Unresolved_Symbol _) ->
|
||||
(this_meta.name == that_meta.name) && (this_meta.scope == that_meta.scope)
|
||||
## Constructor comparison is covered by the identity equality.
|
||||
Primitive objects should define their own equality.
|
||||
Therefore, there are no more cases to handle in this method.
|
||||
|
@ -148,7 +148,7 @@ delta = 3
|
||||
|
||||
Arguments:
|
||||
- m: The map to get the size of.
|
||||
size: Map -> Integer
|
||||
size : Map -> Integer
|
||||
size m = case m of
|
||||
Bin s _ _ _ _ -> s
|
||||
_ -> 0
|
||||
|
@ -31,7 +31,7 @@ os = here.from_text System.os
|
||||
## PRIVATE
|
||||
|
||||
Create an Os object from text.
|
||||
from_text: Text -> Os
|
||||
from_text : Text -> Os
|
||||
from_text os =
|
||||
if os == "linux" then Linux else
|
||||
if os == "macos" then MacOS else
|
||||
|
@ -214,7 +214,7 @@ simple_table_json =
|
||||
|
||||
## The headers for the columns in the JSON table `here.simple_table_json`.
|
||||
simple_table_json_headers : Vector Text
|
||||
simple_Table_json_headers = ["foo", "bar", "baz"]
|
||||
simple_table_json_headers = ["foo", "bar", "baz"]
|
||||
|
||||
## Some simple GeoJSON.
|
||||
geo_json : Enso_Json.Json
|
||||
|
@ -1283,8 +1283,9 @@ pad txt len =
|
||||
Adds ANSI bold escape sequences to text if the feature is enabled.
|
||||
|
||||
Arguments:
|
||||
- enabled: will insert ANSI sequences only if this flag is true and we are not on Windows.
|
||||
- txt: The text to possibly bold.
|
||||
ansi_bold_enabled : Text -> Text
|
||||
ansi_bold : Boolean -> Text -> Text
|
||||
ansi_bold enabled txt =
|
||||
case Platform.os of
|
||||
## Output formatting for Windows is not currently supported.
|
||||
|
@ -122,7 +122,7 @@ specify label ~behavior pending=Nothing =
|
||||
Arguments:
|
||||
- verb: The property (see `Verbs`) being asserted
|
||||
- argument: The argument to the verb.
|
||||
Anu.should : (Verbs -> Any -> Any) -> Any -> Assertion
|
||||
Any.should : (Verbs -> Any -> Any) -> Any -> Assertion
|
||||
Any.should verb argument = verb Verbs this argument
|
||||
|
||||
## Fail a test with the given message.
|
||||
|
@ -7,7 +7,9 @@ public class Constants {
|
||||
/** Names for different language elements. */
|
||||
public static class Names {
|
||||
public static final String THIS_ARGUMENT = "this";
|
||||
public static final String THAT_ARGUMENT = "that";
|
||||
public static final String CURRENT_MODULE = "here";
|
||||
public static final String FROM_MEMBER = "from";
|
||||
}
|
||||
|
||||
/** Cache sizes for different AST nodes. */
|
||||
|
@ -20,7 +20,6 @@ import org.enso.interpreter.runtime.state.Stateful;
|
||||
*/
|
||||
@NodeInfo(shortName = "App", description = "Executes function")
|
||||
public class ApplicationNode extends ExpressionNode {
|
||||
|
||||
private @Children ExpressionNode[] argExpressions;
|
||||
|
||||
@Child private InvokeCallableNode invokeCallableNode;
|
||||
|
@ -0,0 +1,218 @@
|
||||
package org.enso.interpreter.node.callable;
|
||||
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.dsl.*;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.dispatch.IndirectInvokeFunctionNode;
|
||||
import org.enso.interpreter.node.callable.resolver.AnyResolverNode;
|
||||
import org.enso.interpreter.node.callable.resolver.DataflowErrorResolverNode;
|
||||
import org.enso.interpreter.node.callable.resolver.HostMethodCallNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
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.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.library.dispatch.MethodDispatchLibrary;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@GenerateUncached
|
||||
@ReportPolymorphism
|
||||
@ImportStatic({HostMethodCallNode.PolyglotCallType.class, HostMethodCallNode.class})
|
||||
public abstract class IndirectInvokeConversionNode extends Node {
|
||||
|
||||
/** @return a new indirect method invocation node */
|
||||
public static IndirectInvokeConversionNode build() {
|
||||
return IndirectInvokeConversionNodeGen.create();
|
||||
}
|
||||
|
||||
public abstract Stateful execute(
|
||||
MaterializedFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail);
|
||||
|
||||
@Specialization(guards = "dispatch.canConvertFrom(that)")
|
||||
Stateful doConvertFrom(
|
||||
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 dispatch,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
|
||||
@Cached ConditionProfile atomProfile,
|
||||
@Cached ConditionProfile atomConstructorProfile,
|
||||
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
|
||||
try {
|
||||
Function function =
|
||||
dispatch.getConversionFunction(
|
||||
that,
|
||||
InvokeConversionNode.extractConstructor(
|
||||
this, _this, ctx, atomConstructorProfile, atomProfile),
|
||||
conversion);
|
||||
return indirectInvokeFunctionNode.execute(
|
||||
function,
|
||||
frame,
|
||||
state,
|
||||
arguments,
|
||||
schema,
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode,
|
||||
isTail);
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doDataflowError(
|
||||
MaterializedFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
DataflowError that,
|
||||
Object[] arguments,
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx,
|
||||
@Cached BranchProfile profile,
|
||||
@Cached ConditionProfile atomProfile,
|
||||
@Cached ConditionProfile atomConstructorProfile,
|
||||
@Cached IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
|
||||
try {
|
||||
Function function =
|
||||
dispatch.getConversionFunction(
|
||||
that,
|
||||
InvokeConversionNode.extractConstructor(
|
||||
this, _this, ctx, atomConstructorProfile, atomProfile),
|
||||
conversion);
|
||||
return indirectInvokeFunctionNode.execute(
|
||||
function,
|
||||
frame,
|
||||
state,
|
||||
arguments,
|
||||
schema,
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode,
|
||||
isTail);
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
profile.enter();
|
||||
return new Stateful(state, that);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doPanicSentinel(
|
||||
MaterializedFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
PanicSentinel that,
|
||||
Object[] arguments,
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
|
||||
BaseNode.TailStatus isTail) {
|
||||
throw that;
|
||||
}
|
||||
|
||||
@Specialization(guards = "interop.isString(that)")
|
||||
Stateful doConvertText(
|
||||
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 = "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);
|
||||
Text txt = Text.create(str);
|
||||
Function function =
|
||||
textDispatch.getConversionFunction(
|
||||
txt,
|
||||
InvokeConversionNode.extractConstructor(
|
||||
this, _this, ctx, atomConstructorProfile, atomProfile),
|
||||
conversion);
|
||||
arguments[0] = txt;
|
||||
return indirectInvokeFunctionNode.execute(
|
||||
function,
|
||||
frame,
|
||||
state,
|
||||
arguments,
|
||||
schema,
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode,
|
||||
isTail);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
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) {
|
||||
throw new PanicException(
|
||||
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
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.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.GenerateUncached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.ArityException;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import org.enso.interpreter.Constants;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.BaseNode.TailStatus;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode.ArgumentsExecutionMode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode.DefaultsExecutionMode;
|
||||
import org.enso.interpreter.node.callable.InvokeConversionNode;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
|
||||
/** A helper node to handle conversion application for the interop library. */
|
||||
@GenerateUncached
|
||||
@NodeInfo(description = "Helper node to handle conversion application through the interop library.")
|
||||
public abstract class InteropConversionCallNode extends Node {
|
||||
|
||||
public static InteropConversionCallNode build() {
|
||||
return InteropConversionCallNodeGen.create();
|
||||
}
|
||||
|
||||
public abstract Object execute(UnresolvedConversion conversion, Object state, Object[] arguments)
|
||||
throws ArityException;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
CallArgumentInfo[] buildSchema(int length) {
|
||||
CallArgumentInfo[] args = new CallArgumentInfo[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
args[i] = new CallArgumentInfo();
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
InvokeConversionNode buildInvoker(int length) {
|
||||
CallArgumentInfo[] args = buildSchema(length);
|
||||
return InvokeConversionNode.build(
|
||||
args,
|
||||
DefaultsExecutionMode.EXECUTE,
|
||||
ArgumentsExecutionMode.PRE_EXECUTED);
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {"!context.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)
|
||||
throws ArityException {
|
||||
Object[] args = new Object[cachedArgsLength];
|
||||
for (int i = 0; i < cachedArgsLength; i++) {
|
||||
args[i] = hostValueToEnsoNode.execute(arguments[i]);
|
||||
}
|
||||
if (cachedArgsLength < 2) throw ArityException.create(2, cachedArgsLength);
|
||||
return invokerNode.execute(null, state, conversion, args[0], args[1], args).getValue();
|
||||
}
|
||||
|
||||
@Specialization(replaces = "callCached")
|
||||
Object callUncached(
|
||||
UnresolvedConversion conversion,
|
||||
Object state,
|
||||
Object[] arguments,
|
||||
@Cached IndirectInvokeConversionNode indirectInvokeConversionNode,
|
||||
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode)
|
||||
throws ArityException {
|
||||
Object[] args = new Object[arguments.length];
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
args[i] = hostValueToEnsoNode.execute(arguments[i]);
|
||||
}
|
||||
if (arguments.length < 2) throw ArityException.create(2, arguments.length);
|
||||
return indirectInvokeConversionNode
|
||||
.execute(
|
||||
null,
|
||||
state,
|
||||
conversion,
|
||||
args[0],
|
||||
args[1],
|
||||
args,
|
||||
buildSchema(arguments.length),
|
||||
DefaultsExecutionMode.EXECUTE,
|
||||
ArgumentsExecutionMode.PRE_EXECUTED,
|
||||
TailStatus.NOT_TAIL)
|
||||
.getValue();
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
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.argument.Thunk;
|
||||
@ -82,11 +83,15 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
|
||||
@Child private InvokeFunctionNode invokeFunctionNode;
|
||||
@Child private InvokeMethodNode invokeMethodNode;
|
||||
@Child private InvokeConversionNode invokeConversionNode;
|
||||
@Child private ThunkExecutorNode thisExecutor;
|
||||
@Child private ThunkExecutorNode thatExecutor;
|
||||
private final ConditionProfile functionErrorProfile = ConditionProfile.createCountingProfile();
|
||||
|
||||
private final boolean canApplyThis;
|
||||
private final boolean canApplyThat;
|
||||
private final int thisArgumentPosition;
|
||||
private final int thatArgumentPosition;
|
||||
|
||||
private final ArgumentsExecutionMode argumentsExecutionMode;
|
||||
|
||||
@ -95,9 +100,12 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
DefaultsExecutionMode defaultsExecutionMode,
|
||||
ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
Integer thisArg = thisArgumentPosition(schema);
|
||||
|
||||
this.canApplyThis = thisArg != null;
|
||||
this.thisArgumentPosition = thisArg == null ? 0 : thisArg;
|
||||
this.thisArgumentPosition = thisArg == null ? -1 : thisArg;
|
||||
|
||||
Integer thatArg = thatArgumentPosition(schema, thisArgumentPosition);
|
||||
this.canApplyThat = thatArg != null;
|
||||
this.thatArgumentPosition = thatArg == null ? -1 : thatArg;
|
||||
|
||||
this.argumentsExecutionMode = argumentsExecutionMode;
|
||||
|
||||
@ -105,6 +113,8 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
this.invokeMethodNode =
|
||||
InvokeMethodNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
this.invokeConversionNode =
|
||||
InvokeConversionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
}
|
||||
|
||||
public static Integer thisArgumentPosition(CallArgumentInfo[] schema) {
|
||||
@ -120,7 +130,22 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Integer thatArgumentPosition(CallArgumentInfo[] schema, int thisArgumentPosition) {
|
||||
int idx = 0;
|
||||
for (; idx < schema.length; idx++) {
|
||||
CallArgumentInfo arg = schema[idx];
|
||||
|
||||
boolean isNamedThat = arg.isNamed() && arg.getName().equals(Constants.Names.THAT_ARGUMENT);
|
||||
if ((arg.isPositional() && thisArgumentPosition != idx) || isNamedThat) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Creates a new instance of this node.
|
||||
*
|
||||
* @param schema a description of the arguments being applied to the callable
|
||||
@ -158,6 +183,52 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
throw sentinel;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
public Stateful invokeConversion(
|
||||
UnresolvedConversion conversion, VirtualFrame callerFrame, Object state, Object[] arguments) {
|
||||
if (canApplyThis && canApplyThat) {
|
||||
Object selfArgument = arguments[thisArgumentPosition];
|
||||
Object thatArgument = arguments[thatArgumentPosition];
|
||||
if (argumentsExecutionMode.shouldExecute()) {
|
||||
if (thisExecutor == null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
Lock lock = getLock();
|
||||
lock.lock();
|
||||
try {
|
||||
if (thisExecutor == null) {
|
||||
thisExecutor = insert(ThunkExecutorNode.build());
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
if (thatExecutor == null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
Lock lock = getLock();
|
||||
lock.lock();
|
||||
try {
|
||||
if (thatExecutor == null) {
|
||||
thatExecutor = insert(ThunkExecutorNode.build());
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
Stateful selfResult = thisExecutor.executeThunk(selfArgument, state, 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);
|
||||
} else {
|
||||
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) {
|
||||
@ -217,6 +288,7 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
super.setTailStatus(isTail);
|
||||
invokeFunctionNode.setTailStatus(isTail);
|
||||
invokeMethodNode.setTailStatus(isTail);
|
||||
invokeConversionNode.setTailStatus(isTail);
|
||||
}
|
||||
|
||||
/** @return the source section for this node. */
|
||||
@ -234,5 +306,6 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
public void setId(UUID id) {
|
||||
invokeFunctionNode.setId(id);
|
||||
invokeMethodNode.setId(id);
|
||||
invokeConversionNode.setId(id);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,207 @@
|
||||
package org.enso.interpreter.node.callable;
|
||||
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.dsl.*;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
|
||||
import org.enso.interpreter.node.callable.resolver.AnyResolverNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
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.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.library.dispatch.MethodDispatchLibrary;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class InvokeConversionNode extends BaseNode {
|
||||
private @Child InvokeFunctionNode invokeFunctionNode;
|
||||
private final ConditionProfile atomProfile = ConditionProfile.createCountingProfile();
|
||||
private final ConditionProfile atomConstructorProfile = ConditionProfile.createCountingProfile();
|
||||
|
||||
/**
|
||||
* Creates a new node for method invocation.
|
||||
*
|
||||
* @param schema a description of the arguments being applied to the callable
|
||||
* @param defaultsExecutionMode the defaulted arguments handling mode for this call
|
||||
* @param argumentsExecutionMode the arguments execution mode for this call
|
||||
* @return a new invoke method node
|
||||
*/
|
||||
public static InvokeConversionNode build(
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
return InvokeConversionNodeGen.create(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
}
|
||||
|
||||
InvokeConversionNode(
|
||||
CallArgumentInfo[] schema,
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
this.invokeFunctionNode =
|
||||
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTailStatus(TailStatus tailStatus) {
|
||||
super.setTailStatus(tailStatus);
|
||||
this.invokeFunctionNode.setTailStatus(tailStatus);
|
||||
}
|
||||
|
||||
public abstract Stateful execute(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments);
|
||||
|
||||
static AtomConstructor extractConstructor(
|
||||
Node thisNode,
|
||||
Object _this,
|
||||
TruffleLanguage.ContextReference<Context> ctx,
|
||||
ConditionProfile atomConstructorProfile,
|
||||
ConditionProfile atomProfile) {
|
||||
if (atomConstructorProfile.profile(_this instanceof AtomConstructor)) {
|
||||
return (AtomConstructor) _this;
|
||||
} else if (atomProfile.profile(_this instanceof Atom)) {
|
||||
return ((Atom) _this).getConstructor();
|
||||
} else {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeInvalidConversionTargetError(_this), thisNode);
|
||||
}
|
||||
}
|
||||
|
||||
AtomConstructor extractConstructor(Object _this, TruffleLanguage.ContextReference<Context> ctx) {
|
||||
return extractConstructor(this, _this, ctx, atomConstructorProfile, atomProfile);
|
||||
}
|
||||
|
||||
@Specialization(guards = "dispatch.canConvertFrom(that)")
|
||||
Stateful doConvertFrom(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
try {
|
||||
Function function =
|
||||
dispatch.getConversionFunction(that, extractConstructor(_this, ctx), conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
throw new PanicException(
|
||||
ctx.get().getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doDataflowError(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
DataflowError that,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary dispatch,
|
||||
@Cached BranchProfile profile,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
try {
|
||||
Function function =
|
||||
dispatch.getConversionFunction(that, extractConstructor(_this, ctx), conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchConversionException e) {
|
||||
profile.enter();
|
||||
return new Stateful(state, that);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Stateful doPanicSentinel(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
PanicSentinel that,
|
||||
Object[] arguments) {
|
||||
throw that;
|
||||
}
|
||||
|
||||
@Specialization(guards = "interop.isString(that)")
|
||||
Stateful doConvertText(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary textDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
try {
|
||||
String str = interop.asString(that);
|
||||
Text txt = Text.create(str);
|
||||
Function function =
|
||||
textDispatch.getConversionFunction(txt, extractConstructor(_this, ctx), 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.canConvertFrom(that)",
|
||||
"!interop.isString(that)",
|
||||
"!methods.hasSpecialConversion(that)"
|
||||
})
|
||||
Stateful doFallback(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedConversion conversion,
|
||||
Object _this,
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop,
|
||||
@CachedContext(Language.class) Context ctx) {
|
||||
throw new PanicException(
|
||||
ctx.getBuiltins().error().makeNoSuchConversionError(_this, that, conversion), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceSection getSourceSection() {
|
||||
Node parent = getParent();
|
||||
return parent == null ? null : parent.getSourceSection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the expression ID of this node.
|
||||
*
|
||||
* @param id the expression ID to assign this node.
|
||||
*/
|
||||
public void setId(UUID id) {
|
||||
invokeFunctionNode.setId(id);
|
||||
}
|
||||
}
|
@ -32,7 +32,6 @@ 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 final int argumentCount;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,42 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error.displaytext;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
|
||||
@BuiltinMethod(type = "Invalid_Conversion_Target_Error", name = "to_display_text")
|
||||
public abstract class InvalidConversionTargetErrorToDisplayTextNode extends Node {
|
||||
static InvalidConversionTargetErrorToDisplayTextNode build() {
|
||||
return InvalidConversionTargetErrorToDisplayTextNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Text execute(Object _this);
|
||||
|
||||
@Specialization
|
||||
Text doAtom(
|
||||
Atom _this,
|
||||
@CachedLibrary(limit="10") InteropLibrary interopLibrary,
|
||||
@Cached TypeToDisplayTextNode fallback) {
|
||||
String fieldRep;
|
||||
Object target = _this.getFields()[0];
|
||||
try {
|
||||
fieldRep = interopLibrary.asString(interopLibrary.toDisplayString(target));
|
||||
} catch (UnsupportedMessageException e) {
|
||||
fieldRep = fallback.execute(target);
|
||||
}
|
||||
return Text.create(fieldRep).add(" is not a valid conversion target. Expected a type.");
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Text doConstructor(AtomConstructor _this) {
|
||||
return Text.create("Invalid conversion target type.");
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.enso.interpreter.node.expression.builtin.error.displaytext;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
@BuiltinMethod(type = "No_Such_Method_Error", name = "to_display_text")
|
||||
public abstract class NoSuchConversionErrorToDisplayTextNode extends Node {
|
||||
static NoSuchConversionErrorToDisplayTextNode build() {
|
||||
return NoSuchConversionErrorToDisplayTextNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Text execute(Object _this);
|
||||
|
||||
@Specialization
|
||||
Text doAtom(Atom _this, @Cached TypeToDisplayTextNode displayTypeNode) {
|
||||
return Text.create("Could not find a conversion from `")
|
||||
.add(displayTypeNode.execute(_this.getFields()[1]))
|
||||
.add("` to `")
|
||||
.add(displayTypeNode.execute(_this.getFields()[0]))
|
||||
.add("`");
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Text doConstructor(AtomConstructor _this) {
|
||||
return Text.create("Conversion could not be found.");
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
package org.enso.interpreter.node.expression.builtin.meta;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.Constants;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.ExpectStringNode;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
@ -15,7 +17,12 @@ import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
public class CreateUnresolvedSymbolNode extends Node {
|
||||
private @Child ExpectStringNode expectStringNode = ExpectStringNode.build();
|
||||
|
||||
UnresolvedSymbol execute(Object _this, Object name, ModuleScope scope) {
|
||||
return UnresolvedSymbol.build(expectStringNode.execute(name), scope);
|
||||
Object execute(Object _this, Object name, ModuleScope scope) {
|
||||
String result = expectStringNode.execute(name);
|
||||
if (result.equals(Constants.Names.FROM_MEMBER)) {
|
||||
return UnresolvedConversion.build(scope);
|
||||
} else {
|
||||
return UnresolvedSymbol.build(result, scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,47 @@
|
||||
package org.enso.interpreter.node.expression.builtin.meta;
|
||||
|
||||
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.nodes.Node;
|
||||
import org.enso.interpreter.Constants;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Meta",
|
||||
name = "get_unresolved_symbol_name",
|
||||
description = "Gets the name of an unresolved symbol")
|
||||
public class GetUnresolvedSymbolNameNode extends Node {
|
||||
Text execute(Object _this, UnresolvedSymbol symbol) {
|
||||
public abstract class GetUnresolvedSymbolNameNode extends Node {
|
||||
static GetUnresolvedSymbolNameNode build() {
|
||||
return GetUnresolvedSymbolNameNodeGen.create();
|
||||
}
|
||||
|
||||
public static Text fromText = Text.create(Constants.Names.FROM_MEMBER);
|
||||
|
||||
abstract Text execute(Object _this, Object symbol);
|
||||
|
||||
@Specialization
|
||||
Text doSymbol(Object _this, UnresolvedSymbol symbol) {
|
||||
return Text.create(symbol.getName());
|
||||
}
|
||||
|
||||
@Specialization
|
||||
Text doConversion(Object _this, UnresolvedConversion symbol) {
|
||||
return fromText;
|
||||
}
|
||||
|
||||
@Fallback
|
||||
Text doFallback(Object _this, Object symbol) {
|
||||
Builtins builtins = lookupContextReference(Language.class).get().getBuiltins();
|
||||
throw new PanicException(
|
||||
builtins.error().makeTypeError("Unresolved_Symbol", symbol, "symbol"), this);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,42 @@
|
||||
package org.enso.interpreter.node.expression.builtin.meta;
|
||||
|
||||
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.Language;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Meta",
|
||||
name = "get_unresolved_symbol_scope",
|
||||
description = "Gets the scope of an unresolved symbol")
|
||||
public class GetUnresolvedSymbolScopeNode extends Node {
|
||||
ModuleScope execute(Object _this, UnresolvedSymbol symbol) {
|
||||
public abstract class GetUnresolvedSymbolScopeNode extends Node {
|
||||
static GetUnresolvedSymbolScopeNode build() {
|
||||
return GetUnresolvedSymbolScopeNodeGen.create();
|
||||
}
|
||||
|
||||
abstract ModuleScope execute(Object _this, Object symbol);
|
||||
|
||||
@Specialization
|
||||
ModuleScope doSymbol(Object _this, UnresolvedSymbol symbol) {
|
||||
return symbol.getScope();
|
||||
}
|
||||
|
||||
@Specialization
|
||||
ModuleScope doConversion(Object _this, UnresolvedConversion symbol) {
|
||||
return symbol.getScope();
|
||||
}
|
||||
|
||||
@Fallback
|
||||
ModuleScope doFallback(Object _this, Object symbol) {
|
||||
Builtins builtins = lookupContextReference(Language.class).get().getBuiltins();
|
||||
throw new PanicException(
|
||||
builtins.error().makeTypeError("Unresolved_Symbol", symbol, "symbol"), this);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,6 @@ import org.enso.interpreter.runtime.type.TypesGen;
|
||||
description = "Checks if the argument is an unresolved symbol.")
|
||||
public class IsUnresolvedSymbolNode extends Node {
|
||||
boolean execute(Object _this, @AcceptsError Object value) {
|
||||
return TypesGen.isUnresolvedSymbol(value);
|
||||
return TypesGen.isUnresolvedSymbol(value) || TypesGen.isUnresolvedConversion(value);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.enso.interpreter.runtime.builtin;
|
||||
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.error.displaytext.*;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
@ -18,6 +19,7 @@ public class Error {
|
||||
private final AtomConstructor inexhaustivePatternMatchError;
|
||||
private final AtomConstructor uninitializedState;
|
||||
private final AtomConstructor noSuchMethodError;
|
||||
private final AtomConstructor noSuchConversionError;
|
||||
private final AtomConstructor polyglotError;
|
||||
private final AtomConstructor moduleNotInPackageError;
|
||||
private final AtomConstructor arithmeticError;
|
||||
@ -26,6 +28,7 @@ public class Error {
|
||||
private final AtomConstructor unsupportedArgumentsError;
|
||||
private final AtomConstructor moduleDoesNotExistError;
|
||||
private final AtomConstructor notInvokableError;
|
||||
private final AtomConstructor invalidConversionTargetError;
|
||||
|
||||
private final Atom arithmeticErrorShiftTooBig;
|
||||
private final Atom arithmeticErrorDivideByZero;
|
||||
@ -67,6 +70,19 @@ public class Error {
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "target", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "symbol", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
|
||||
noSuchConversionError =
|
||||
new AtomConstructor("No_Such_Conversion_Error", scope)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "target", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "that", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(2, "conversion", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
|
||||
invalidConversionTargetError =
|
||||
new AtomConstructor("Invalid_Conversion_Target_Error", scope)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "target", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
|
||||
polyglotError =
|
||||
new AtomConstructor("Polyglot_Error", scope)
|
||||
.initializeFields(
|
||||
@ -130,6 +146,19 @@ public class Error {
|
||||
noSuchMethodError,
|
||||
"to_display_text",
|
||||
NoSuchMethodErrorToDisplayTextMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerConstructor(noSuchConversionError);
|
||||
scope.registerMethod(
|
||||
noSuchConversionError,
|
||||
"to_display_text",
|
||||
NoSuchConversionErrorToDisplayTextMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerConstructor(invalidConversionTargetError);
|
||||
scope.registerMethod(
|
||||
invalidConversionTargetError,
|
||||
"to_display_text",
|
||||
InvalidConversionTargetErrorToDisplayTextMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerConstructor(polyglotError);
|
||||
scope.registerMethod(
|
||||
polyglotError,
|
||||
@ -203,6 +232,15 @@ public class Error {
|
||||
return noSuchMethodError.newInstance(target, symbol);
|
||||
}
|
||||
|
||||
public Atom makeNoSuchConversionError(
|
||||
Object target, Object that, UnresolvedConversion conversion) {
|
||||
return noSuchConversionError.newInstance(target, that, conversion);
|
||||
}
|
||||
|
||||
public Atom makeInvalidConversionTargetError(Object target) {
|
||||
return invalidConversionTargetError.newInstance(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the runtime representation of a {@code Type_Error}.
|
||||
*
|
||||
|
@ -0,0 +1,98 @@
|
||||
package org.enso.interpreter.runtime.callable;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.interop.*;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.Constants;
|
||||
import org.enso.interpreter.node.callable.InteropConversionCallNode;
|
||||
import org.enso.interpreter.node.callable.InteropMethodCallNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
import org.enso.interpreter.runtime.state.data.EmptyMap;
|
||||
|
||||
/** Simple runtime value representing a yet-unresolved by-name symbol. */
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
public class UnresolvedConversion implements TruffleObject {
|
||||
private final ModuleScope scope;
|
||||
|
||||
/**
|
||||
* Creates a new unresolved conversion.
|
||||
*
|
||||
* @param scope the scope in which this conversion was created
|
||||
*/
|
||||
private UnresolvedConversion(ModuleScope scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
/** @return the scope this symbol was used in. */
|
||||
public ModuleScope getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the symbol for a given hierarchy of constructors.
|
||||
*
|
||||
* <p>The constructors are checked in the first to last order, and the first match for this symbol
|
||||
* is returned. This is useful for certain subtyping relations, such as "any constructor is a
|
||||
* subtype of Any" or "Nat is a subtype of Int, is a subtype of Number".
|
||||
*
|
||||
* @param constructors the constructors hierarchy for which this symbol should be resolved
|
||||
* @return the resolved function definition, or null if not found
|
||||
*/
|
||||
public Function resolveFor(AtomConstructor into, AtomConstructor... constructors) {
|
||||
for (AtomConstructor constructor : constructors) {
|
||||
Function candidate = scope.lookupConversionDefinition(constructor, into);
|
||||
if (candidate != null) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return "UnresolvedConversion"; }
|
||||
|
||||
@ExportMessage
|
||||
String toDisplayString(boolean allowSideEffects) {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of this node.
|
||||
*
|
||||
* @param name the name that is unresolved
|
||||
* @param scope the scope in which the lookup will occur
|
||||
* @return a node representing an unresolved symbol {@code name} in {@code scope}
|
||||
*/
|
||||
public static UnresolvedConversion build(ModuleScope scope) {
|
||||
return new UnresolvedConversion(scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this object as executable through the interop library.
|
||||
*
|
||||
* @return always true
|
||||
*/
|
||||
@ExportMessage
|
||||
public boolean isExecutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Implements the logic of executing {@link UnresolvedConversion} through the interop library. */
|
||||
@ExportMessage
|
||||
@ImportStatic(Constants.CacheSizes.class)
|
||||
public static class Execute {
|
||||
@Specialization
|
||||
static Object doDispatch(
|
||||
UnresolvedConversion conversion,
|
||||
Object[] arguments,
|
||||
@Cached InteropConversionCallNode interopConversionCallNode)
|
||||
throws ArityException, UnsupportedTypeException, UnsupportedMessageException {
|
||||
return interopConversionCallNode.execute(conversion, EmptyMap.create(), arguments);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
@ -242,4 +243,60 @@ public class Atom implements TruffleObject {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean canConvertFrom() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context,
|
||||
AtomConstructor cons,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion) {
|
||||
return conversion.resolveFor(target, cons, context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"_this.constructor == cachedConstructor",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
Atom _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("_this.constructor") AtomConstructor cachedConstructor,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("doResolve(context, cachedConstructor, cachedTarget, cachedConversion)")
|
||||
Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
Atom _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, _this.constructor, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import org.enso.interpreter.node.expression.atom.InstantiateNode;
|
||||
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.InstantiateAtomNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
@ -231,8 +232,7 @@ public final class AtomConstructor implements TruffleObject {
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context, AtomConstructor cons, UnresolvedSymbol symbol) {
|
||||
static Function doResolve(Context context, AtomConstructor cons, UnresolvedSymbol symbol) {
|
||||
return symbol.resolveFor(cons, context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@ -250,8 +250,7 @@ public final class AtomConstructor implements TruffleObject {
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("symbol") UnresolvedSymbol cachedSymbol,
|
||||
@Cached("_this") AtomConstructor cachedConstructor,
|
||||
@Cached("doResolve(context, cachedConstructor, cachedSymbol)")
|
||||
Function function) {
|
||||
@Cached("doResolve(context, cachedConstructor, cachedSymbol)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@ -268,4 +267,58 @@ public final class AtomConstructor implements TruffleObject {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean canConvertFrom() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context,
|
||||
AtomConstructor cons,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion) {
|
||||
return conversion.resolveFor(target, cons, context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"_this == cachedConstructor",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
AtomConstructor _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("_this") AtomConstructor cachedConstructor,
|
||||
@Cached("doResolve(context, cachedConstructor, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
AtomConstructor _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, _this, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,11 @@ import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.CallerInfo;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
import org.enso.interpreter.runtime.state.data.EmptyMap;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
@ -405,4 +407,53 @@ public final class Function implements TruffleObject {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean canConvertFrom() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
return conversion.resolveFor(target, context.getBuiltins().function(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedTarget == target",
|
||||
"cachedConversion == conversion",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
Function _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("doResolve(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
Function _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
@ -160,4 +162,54 @@ public class Array implements TruffleObject {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static boolean canConvertFrom(Array receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
return conversion.resolveFor(
|
||||
target, context.getBuiltins().mutable().array(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
Array _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("doResolve(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
Array _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Number;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
@ -220,4 +222,60 @@ public class Text implements TruffleObject {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean canConvertFrom(Text receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean hasSpecialConversion(Text receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
return conversion.resolveFor(
|
||||
target, context.getBuiltins().text().getText(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedTarget == target",
|
||||
"cachedConversion == conversion",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
Text _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("doResolve(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
Text _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,23 @@
|
||||
package org.enso.interpreter.runtime.error;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleStackTrace;
|
||||
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.exception.AbstractTruffleException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import com.oracle.truffle.api.library.GenerateLibrary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
/**
|
||||
@ -93,4 +103,53 @@ public class DataflowError extends AbstractTruffleException {
|
||||
boolean hasSpecialDispatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasSpecialConversion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
return conversion.resolveFor(target, context.getBuiltins().dataflowError().constructor());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedTarget == target",
|
||||
"cachedConversion == conversion",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
DataflowError _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("doResolve(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
DataflowError _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,4 +49,9 @@ public class PanicSentinel extends AbstractTruffleException {
|
||||
boolean hasSpecialDispatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean hasSpecialConversion() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
package org.enso.interpreter.runtime.error;
|
||||
|
||||
import com.oracle.truffle.api.exception.AbstractTruffleException;
|
||||
|
||||
/** An exception thrown when the program tries to redefine an already-defined method */
|
||||
public class RedefinedConversionException extends AbstractTruffleException {
|
||||
|
||||
/**
|
||||
* Creates a new error.
|
||||
*
|
||||
* @param atom the name of the atom you are converting to
|
||||
* @param source the name of the atom you are converting from
|
||||
*/
|
||||
public RedefinedConversionException(String atom, String source) {
|
||||
super("You have already overloaded conversion from " + source + " to " + atom);
|
||||
}
|
||||
}
|
@ -9,13 +9,21 @@ import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Bool;
|
||||
import org.enso.interpreter.runtime.builtin.Number;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
@ExportLibrary(value = MethodDispatchLibrary.class, receiverType = Boolean.class)
|
||||
public class DefaultBooleanExports {
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
static boolean unbox(Boolean b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@ExportMessage
|
||||
static boolean hasFunctionalDispatch(Boolean receiver) {
|
||||
return true;
|
||||
@ -42,11 +50,6 @@ public class DefaultBooleanExports {
|
||||
return symbol.resolveFor(cons, bool.getBool(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
static boolean unbox(Boolean b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
@ -111,4 +114,111 @@ public class DefaultBooleanExports {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean canConvertFrom(Boolean receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean hasSpecialConversion(Boolean receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function resolveMethodOnPrimBoolean(Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
Bool bool = context.getBuiltins().bool();
|
||||
if (conversion.resolveFor(target, bool.getFalse()) != null) {
|
||||
return null;
|
||||
}
|
||||
if (conversion.resolveFor(target, bool.getTrue()) != null) {
|
||||
return null;
|
||||
}
|
||||
return conversion.resolveFor(target, bool.getBool(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function resolveMethodOnBool(Context context, boolean self, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
Bool bool = context.getBuiltins().bool();
|
||||
AtomConstructor cons = self ? bool.getTrue() : bool.getFalse();
|
||||
return conversion.resolveFor(target, cons, bool.getBool(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
Boolean _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("resolveMethodOnPrimBoolean(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"unbox(_this)",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE",
|
||||
replaces = "resolveCached")
|
||||
static Function resolveTrueCached(
|
||||
Boolean _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("resolveMethodOnBool(context, _this, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"!unbox(_this)",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE",
|
||||
replaces = "resolveCached")
|
||||
static Function resolveFalseCached(
|
||||
Boolean _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("resolveMethodOnBool(context, _this, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = {"resolveTrueCached", "resolveFalseCached"})
|
||||
static Function resolve(
|
||||
Boolean _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion symbol,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = resolveMethodOnBool(context, _this, target, symbol);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,13 @@ import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.library.ExportLibrary;
|
||||
import com.oracle.truffle.api.library.ExportMessage;
|
||||
import com.oracle.truffle.api.library.GenerateLibrary;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Number;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
@ExportLibrary(value = MethodDispatchLibrary.class, receiverType = Double.class)
|
||||
@ -57,4 +60,60 @@ public class DefaultDoubleExports {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean canConvertFrom(Double receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean hasSpecialConversion(Double receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
Number number = context.getBuiltins().number();
|
||||
return conversion.resolveFor(
|
||||
target, number.getDecimal(), number.getNumber(), context.getBuiltins().any());
|
||||
}
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
Double _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("doResolve(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
Double _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Number;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
@ExportLibrary(value = MethodDispatchLibrary.class, receiverType = Long.class)
|
||||
@ -60,4 +62,63 @@ public class DefaultLongExports {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean canConvertFrom(Long receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean hasSpecialConversion(Long receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
Number number = context.getBuiltins().number();
|
||||
return conversion.resolveFor(target,
|
||||
number.getSmallInteger(),
|
||||
number.getInteger(),
|
||||
number.getNumber(),
|
||||
context.getBuiltins().any());
|
||||
}
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedConversion == conversion",
|
||||
"cachedTarget == target",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
Long _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("doResolve(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
Long _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package org.enso.interpreter.runtime.library.dispatch;
|
||||
import com.oracle.truffle.api.library.GenerateLibrary;
|
||||
import com.oracle.truffle.api.library.Library;
|
||||
import com.oracle.truffle.api.library.LibraryFactory;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
/**
|
||||
@ -75,4 +77,25 @@ public abstract class MethodDispatchLibrary extends Library {
|
||||
throws NoSuchMethodException {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
|
||||
/* * Conversions */
|
||||
|
||||
/** An exception thrown when the library cannot lookup the conversion definition. */
|
||||
public static class NoSuchConversionException extends Exception {}
|
||||
|
||||
//@GenerateLibrary.Abstract(ifExported = {"getConversionFunction"})
|
||||
public boolean canConvertFrom(Object receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasSpecialConversion(Object receiver) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@GenerateLibrary.Abstract(ifExported = {"canConvertFrom"})
|
||||
public Function getConversionFunction(
|
||||
Object receiver, AtomConstructor target, UnresolvedConversion symbol)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ import com.oracle.truffle.api.library.ExportMessage;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.builtin.Number;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.library.dispatch.MethodDispatchLibrary;
|
||||
|
||||
@ -98,4 +100,60 @@ public class EnsoBigInteger implements TruffleObject {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public static boolean canConvertFrom(EnsoBigInteger receiver) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetConversionFunction {
|
||||
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
static Function doResolve(
|
||||
Context context, AtomConstructor target, UnresolvedConversion conversion) {
|
||||
Number number = context.getBuiltins().number();
|
||||
return conversion.resolveFor(
|
||||
target,
|
||||
number.getBigInteger(),
|
||||
number.getInteger(),
|
||||
number.getNumber(),
|
||||
context.getBuiltins().any());
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!context.isInlineCachingDisabled()",
|
||||
"cachedTarget == target",
|
||||
"cachedConversion == conversion",
|
||||
"function != null"
|
||||
},
|
||||
limit = "CACHE_SIZE")
|
||||
static Function resolveCached(
|
||||
EnsoBigInteger _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("conversion") UnresolvedConversion cachedConversion,
|
||||
@Cached("target") AtomConstructor cachedTarget,
|
||||
@Cached("doResolve(context, cachedTarget, cachedConversion)") Function function) {
|
||||
return function;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "resolveCached")
|
||||
static Function resolve(
|
||||
EnsoBigInteger _this,
|
||||
AtomConstructor target,
|
||||
UnresolvedConversion conversion,
|
||||
@CachedContext(Language.class) Context context)
|
||||
throws MethodDispatchLibrary.NoSuchConversionException {
|
||||
Function function = doResolve(context, target, conversion);
|
||||
if (function == null) {
|
||||
throw new MethodDispatchLibrary.NoSuchConversionException();
|
||||
}
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
package org.enso.interpreter.runtime.scope;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.error.RedefinedMethodException;
|
||||
import org.enso.interpreter.runtime.error.RedefinedConversionException;
|
||||
|
||||
/** A representation of Enso's per-file top-level scope. */
|
||||
public class ModuleScope implements TruffleObject {
|
||||
@ -21,6 +19,7 @@ public class ModuleScope implements TruffleObject {
|
||||
private Map<String, Object> polyglotSymbols = new HashMap<>();
|
||||
private Map<String, AtomConstructor> constructors = new HashMap<>();
|
||||
private Map<AtomConstructor, Map<String, Function>> methods = new HashMap<>();
|
||||
private Map<AtomConstructor, Map<AtomConstructor, Function>> conversions = new HashMap<>();
|
||||
private Set<ModuleScope> imports = new HashSet<>();
|
||||
private Set<ModuleScope> exports = new HashSet<>();
|
||||
|
||||
@ -123,6 +122,43 @@ public class ModuleScope implements TruffleObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of the conversion methods defined in this module for a given constructor.
|
||||
*
|
||||
* @param cons the constructor for which method map is requested
|
||||
* @return a list containing all the defined conversions in definition order
|
||||
*/
|
||||
private Map<AtomConstructor,Function> ensureConversionsFor(AtomConstructor cons) {
|
||||
//var methods = ensureMethodMapFor(cons);
|
||||
//methods.
|
||||
return conversions.computeIfAbsent(cons, k -> new HashMap<>());
|
||||
}
|
||||
|
||||
private Map<AtomConstructor, Function> getConversionsFor(AtomConstructor cons) {
|
||||
Map<AtomConstructor, Function> result = conversions.get(cons);
|
||||
if (result == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a conversion method for a given type
|
||||
*
|
||||
* @param toType type the conversion was defined to
|
||||
* @param fromType type the conversion was defined from
|
||||
* @param function the {@link Function} associated with this definition
|
||||
*/
|
||||
public void registerConversionMethod(AtomConstructor toType, AtomConstructor fromType, Function function) {
|
||||
Map<AtomConstructor, Function> sourceMap = ensureConversionsFor(toType);
|
||||
if (sourceMap.containsKey(fromType)) {
|
||||
throw new RedefinedConversionException(toType.getName(), fromType.getName());
|
||||
} else {
|
||||
sourceMap.put(fromType, function);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new symbol in the polyglot namespace.
|
||||
*
|
||||
@ -172,6 +208,24 @@ public class ModuleScope implements TruffleObject {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Function lookupConversionDefinition(AtomConstructor atom, AtomConstructor target) {
|
||||
Function definedWithAtom = atom.getDefinitionScope().getConversionsFor(target).get(atom);
|
||||
if (definedWithAtom != null) {
|
||||
return definedWithAtom;
|
||||
}
|
||||
Function definedHere = getConversionsFor(target).get(atom);
|
||||
if (definedHere != null) {
|
||||
return definedHere;
|
||||
}
|
||||
return imports.stream()
|
||||
.map(scope -> scope.getExportedConversion(atom, target))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
}
|
||||
|
||||
private Function getExportedMethod(AtomConstructor atom, String name) {
|
||||
Function here = getMethodMapFor(atom).get(name);
|
||||
if (here != null) {
|
||||
@ -184,6 +238,18 @@ public class ModuleScope implements TruffleObject {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private Function getExportedConversion(AtomConstructor atom, AtomConstructor target) {
|
||||
Function here = getConversionsFor(target).get(atom);
|
||||
if (here != null) {
|
||||
return here;
|
||||
}
|
||||
return exports.stream()
|
||||
.map(scope -> scope.getConversionsFor(target).get(atom))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency for this module.
|
||||
*
|
||||
@ -211,6 +277,11 @@ public class ModuleScope implements TruffleObject {
|
||||
return methods;
|
||||
}
|
||||
|
||||
/** @return the raw conversions map held by this module */
|
||||
public Map<AtomConstructor, Map<AtomConstructor, Function>> getConversions() {
|
||||
return conversions;
|
||||
}
|
||||
|
||||
/** @return the polyglot symbols imported into this scope. */
|
||||
public Map<String, Object> getPolyglotSymbols() {
|
||||
return polyglotSymbols;
|
||||
@ -221,6 +292,7 @@ public class ModuleScope implements TruffleObject {
|
||||
exports = new HashSet<>();
|
||||
methods = new HashMap<>();
|
||||
constructors = new HashMap<>();
|
||||
conversions = new HashMap<>();
|
||||
polyglotSymbols = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import com.oracle.truffle.api.dsl.TypeSystem;
|
||||
import com.oracle.truffle.api.interop.ArityException;
|
||||
import com.oracle.truffle.api.interop.UnsupportedTypeException;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
|
||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
@ -41,6 +42,7 @@ import org.yaml.snakeyaml.scanner.Constant;
|
||||
AtomConstructor.class,
|
||||
Thunk.class,
|
||||
DataflowError.class,
|
||||
UnresolvedConversion.class,
|
||||
UnresolvedSymbol.class,
|
||||
Array.class,
|
||||
EnsoBigInteger.class,
|
||||
@ -125,7 +127,7 @@ public class Types {
|
||||
return Constants.THUNK;
|
||||
} else if (TypesGen.isDataflowError(value)) {
|
||||
return Constants.ERROR;
|
||||
} else if (TypesGen.isUnresolvedSymbol(value)) {
|
||||
} else if (TypesGen.isUnresolvedSymbol(value) || TypesGen.isUnresolvedConversion(value)) {
|
||||
return Constants.UNRESOLVED_SYMBOL;
|
||||
} else if (TypesGen.isManagedResource(value)) {
|
||||
return Constants.MANAGED_RESOURCE;
|
||||
|
@ -618,7 +618,7 @@ type Meta
|
||||
Arguments:
|
||||
- name: The name of the unresolved symbol.
|
||||
- scope: The scope in which the symbol name is unresolved.
|
||||
create_unresolved_symbol : Text -> Module_Sope -> Unresolved_Symbol
|
||||
create_unresolved_symbol : Text -> Module_Scope -> Unresolved_Symbol
|
||||
create_unresolved_symbol name scope =
|
||||
@Builtin_Method "Meta.create_unresolved_symbol"
|
||||
|
||||
@ -781,8 +781,8 @@ type Meta
|
||||
|
||||
Arguments:
|
||||
- value: the value to get the type of.
|
||||
get_qualified_type_name : Any -> Text
|
||||
get_qualified_type_name value = @Builtin_Method "Meta.get_qualified_type_name"
|
||||
get_qualified_type_name : Any -> Text
|
||||
get_qualified_type_name value = @Builtin_Method "Meta.get_qualified_type_name"
|
||||
|
||||
## Utilities for working with primitive arrays.
|
||||
type Array
|
||||
|
@ -611,12 +611,8 @@ class Compiler(
|
||||
): Unit = {
|
||||
if (context.isStrictErrors) {
|
||||
val diagnostics = modules.flatMap { module =>
|
||||
if (module == builtins.getModule) {
|
||||
List()
|
||||
} else {
|
||||
val errors = gatherDiagnostics(module)
|
||||
List((module, errors))
|
||||
}
|
||||
val errors = gatherDiagnostics(module)
|
||||
List((module, errors))
|
||||
}
|
||||
if (reportDiagnostics(diagnostics)) {
|
||||
throw new CompilationAbortedException
|
||||
|
@ -39,13 +39,12 @@ object AstToIr {
|
||||
* @param inputAST the [[AST]] representing the program to translate
|
||||
* @return the [[IR]] representation of `inputAST`
|
||||
*/
|
||||
def translate(inputAST: AST): Module = {
|
||||
def translate(inputAST: AST): Module =
|
||||
inputAST match {
|
||||
case AST.Module.any(inputAST) => translateModule(inputAST)
|
||||
case _ =>
|
||||
throw new UnhandledEntity(inputAST, "translate")
|
||||
}
|
||||
}
|
||||
|
||||
/** Translates an inline program expression represented in the parser [[AST]]
|
||||
* into the compiler's [[IR]] representation.
|
||||
@ -255,21 +254,24 @@ object AstToIr {
|
||||
)
|
||||
case AST.Comment.any(comment) => translateComment(comment)
|
||||
case AstView.TypeAscription(typed, sig) =>
|
||||
typed match {
|
||||
case AST.Ident.any(ident) =>
|
||||
val typeName = Name.Here(None)
|
||||
val methodName = buildName(ident)
|
||||
val methodReference = Name.MethodReference(
|
||||
typeName,
|
||||
methodName,
|
||||
methodName.location
|
||||
)
|
||||
def buildAscription(ident: AST.Ident): IR.Type.Ascription = {
|
||||
val typeName = Name.Here(None)
|
||||
val methodName = buildName(ident)
|
||||
val methodReference = Name.MethodReference(
|
||||
typeName,
|
||||
methodName,
|
||||
methodName.location
|
||||
)
|
||||
|
||||
IR.Type.Ascription(
|
||||
methodReference,
|
||||
translateExpression(sig, insideTypeSignature = true),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
IR.Type.Ascription(
|
||||
methodReference,
|
||||
translateExpression(sig, insideTypeSignature = true),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
}
|
||||
typed match {
|
||||
case AST.Ident.any(ident) => buildAscription(ident)
|
||||
case AST.App.Section.Sides(opr) => buildAscription(opr)
|
||||
case AstView.MethodReference(_, _) =>
|
||||
IR.Type.Ascription(
|
||||
translateMethodReference(typed),
|
||||
@ -347,8 +349,13 @@ object AstToIr {
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
case AstView.TypeAscription(typed, sig) =>
|
||||
val typedIdent = typed match {
|
||||
case AST.App.Section.Sides(opr) => buildName(opr)
|
||||
case AST.Ident.any(ident) => buildName(ident)
|
||||
case other => translateExpression(other)
|
||||
}
|
||||
IR.Type.Ascription(
|
||||
translateExpression(typed),
|
||||
typedIdent,
|
||||
translateExpression(sig, insideTypeSignature = true),
|
||||
getIdentifiedLocation(inputAst)
|
||||
)
|
||||
@ -721,6 +728,20 @@ object AstToIr {
|
||||
)
|
||||
}
|
||||
|
||||
/** Translates an arbitrary expression, making sure to properly recognize
|
||||
* qualified names. Qualified names should, probably, at some point be
|
||||
* handled deeper in the compiler pipeline.
|
||||
*/
|
||||
private def translateQualifiedNameOrExpression(arg: AST): IR.Expression =
|
||||
arg match {
|
||||
case AstView.QualifiedName(segments) =>
|
||||
IR.Name.Qualified(
|
||||
segments.map(buildName(_)),
|
||||
getIdentifiedLocation(arg)
|
||||
)
|
||||
case _ => translateExpression(arg)
|
||||
}
|
||||
|
||||
/** Translates an argument definition from [[AST]] into [[IR]].
|
||||
*
|
||||
* @param arg the argument to translate
|
||||
@ -738,7 +759,7 @@ object AstToIr {
|
||||
case name: IR.Name =>
|
||||
DefinitionArgument.Specified(
|
||||
name,
|
||||
Some(translateExpression(ascType)),
|
||||
Some(translateQualifiedNameOrExpression(ascType)),
|
||||
mValue.map(translateExpression(_)),
|
||||
isSuspended,
|
||||
getIdentifiedLocation(arg)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4244,6 +4244,8 @@ object IR {
|
||||
keepDiagnostics: Boolean = true,
|
||||
keepIdentifiers: Boolean = false
|
||||
): DefinitionArgument
|
||||
|
||||
def withName(ir: IR.Name): DefinitionArgument
|
||||
}
|
||||
object DefinitionArgument {
|
||||
|
||||
@ -4310,6 +4312,8 @@ object IR {
|
||||
res
|
||||
}
|
||||
|
||||
override def withName(ir: Name): DefinitionArgument = copy(name=ir)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def duplicate(
|
||||
keepLocations: Boolean = true,
|
||||
@ -6564,9 +6568,15 @@ object IR {
|
||||
|
||||
case class SuspendedSourceArgument(argName: String) extends Reason {
|
||||
override def explain: String =
|
||||
s"The source type argument in a conversion (here $argName) cannot " +
|
||||
s"The `that` type argument in a conversion (here $argName) cannot " +
|
||||
s"be suspended."
|
||||
}
|
||||
|
||||
case class InvalidSourceArgumentName(argName: String) extends Reason {
|
||||
override def explain: String =
|
||||
s"The source type argument must be ignored or named `that`, but" +
|
||||
s" ${argName} was found."
|
||||
}
|
||||
}
|
||||
|
||||
/** A representation of an error resulting from name resolution.
|
||||
|
@ -135,37 +135,39 @@ case object AliasAnalysis extends IRPass {
|
||||
val zippedBindings = sourceBindings.lazyZip(copyBindings)
|
||||
|
||||
zippedBindings.foreach { case (sourceBinding, copyBinding) =>
|
||||
val sourceRootScopeGraph = sourceBinding
|
||||
.unsafeGetMetadata(
|
||||
this,
|
||||
"Alias analysis must have run."
|
||||
)
|
||||
.asInstanceOf[Info.Scope.Root]
|
||||
.graph
|
||||
val scopeMapping = mutable.Map[Scope, Scope]()
|
||||
val copyRootScopeGraph = sourceRootScopeGraph.deepCopy(scopeMapping)
|
||||
val sourceRootScopeGraphOpt = sourceBinding
|
||||
.getMetadata(this)
|
||||
|
||||
val sourceNodes = sourceBinding.preorder
|
||||
val copyNodes = copyBinding.preorder
|
||||
sourceRootScopeGraphOpt.map { sourceRootScopeGraphScope =>
|
||||
val sourceRootScopeGraph =
|
||||
sourceRootScopeGraphScope.asInstanceOf[Info.Scope.Root].graph
|
||||
|
||||
val matchedNodes = sourceNodes.lazyZip(copyNodes)
|
||||
val scopeMapping = mutable.Map[Scope, Scope]()
|
||||
val copyRootScopeGraph =
|
||||
sourceRootScopeGraph.deepCopy(scopeMapping)
|
||||
|
||||
matchedNodes.foreach { case (sourceNode, copyNode) =>
|
||||
sourceNode.getMetadata(this) match {
|
||||
case Some(meta) =>
|
||||
val newMeta = meta match {
|
||||
case root: Info.Scope.Root =>
|
||||
root.copy(graph = copyRootScopeGraph)
|
||||
case child: Info.Scope.Child =>
|
||||
child.copy(
|
||||
graph = copyRootScopeGraph,
|
||||
scope = child.scope.deepCopy(scopeMapping)
|
||||
)
|
||||
case occ: Info.Occurrence =>
|
||||
occ.copy(graph = copyRootScopeGraph)
|
||||
}
|
||||
copyNode.updateMetadata(this -->> newMeta)
|
||||
case None =>
|
||||
val sourceNodes = sourceBinding.preorder
|
||||
val copyNodes = copyBinding.preorder
|
||||
|
||||
val matchedNodes = sourceNodes.lazyZip(copyNodes)
|
||||
|
||||
matchedNodes.foreach { case (sourceNode, copyNode) =>
|
||||
sourceNode.getMetadata(this) match {
|
||||
case Some(meta) =>
|
||||
val newMeta = meta match {
|
||||
case root: Info.Scope.Root =>
|
||||
root.copy(graph = copyRootScopeGraph)
|
||||
case child: Info.Scope.Child =>
|
||||
child.copy(
|
||||
graph = copyRootScopeGraph,
|
||||
scope = child.scope.deepCopy(scopeMapping)
|
||||
)
|
||||
case occ: Info.Occurrence =>
|
||||
occ.copy(graph = copyRootScopeGraph)
|
||||
}
|
||||
copyNode.updateMetadata(this -->> newMeta)
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import org.enso.compiler.context.{InlineContext, ModuleContext}
|
||||
import org.enso.compiler.core.IR
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition
|
||||
import org.enso.compiler.core.IR.Module.Scope.Definition.Method
|
||||
import org.enso.compiler.core.ir.MetadataStorage.ToPair
|
||||
import org.enso.compiler.exception.CompilerError
|
||||
import org.enso.compiler.pass.IRPass
|
||||
import org.enso.compiler.pass.analyse.{
|
||||
@ -14,6 +15,7 @@ import org.enso.compiler.pass.analyse.{
|
||||
}
|
||||
import org.enso.compiler.pass.optimise.LambdaConsolidate
|
||||
import org.enso.compiler.pass.resolve.IgnoredBindings
|
||||
import org.enso.interpreter.Constants
|
||||
|
||||
import scala.annotation.unused
|
||||
|
||||
@ -172,10 +174,35 @@ case object FunctionBinding extends IRPass {
|
||||
)
|
||||
} else {
|
||||
val firstArgumentType = args.head.ascribedType.get
|
||||
val nonDefaultedArg = args.drop(1).find(_.defaultValue.isEmpty)
|
||||
val firstArgumentName = args.head.name
|
||||
val newFirstArgument =
|
||||
if (firstArgumentName.isInstanceOf[IR.Name.Blank]) {
|
||||
val newName = IR.Name
|
||||
.Literal(
|
||||
Constants.Names.THAT_ARGUMENT,
|
||||
firstArgumentName.isReferent,
|
||||
firstArgumentName.isMethod,
|
||||
firstArgumentName.location
|
||||
)
|
||||
|
||||
if (nonDefaultedArg.isEmpty) {
|
||||
val newBody = args
|
||||
args.head
|
||||
.withName(newName)
|
||||
.updateMetadata(
|
||||
IgnoredBindings -->> IgnoredBindings.State.Ignored
|
||||
)
|
||||
} else {
|
||||
args.head
|
||||
}
|
||||
val nonDefaultedArg = args.drop(1).find(_.defaultValue.isEmpty)
|
||||
if (newFirstArgument.name.name != Constants.Names.THAT_ARGUMENT) {
|
||||
IR.Error.Conversion(
|
||||
newFirstArgument,
|
||||
IR.Error.Conversion.InvalidSourceArgumentName(
|
||||
newFirstArgument.name.name
|
||||
)
|
||||
)
|
||||
} else if (nonDefaultedArg.isEmpty) {
|
||||
val newBody = (newFirstArgument :: args.tail)
|
||||
.map(_.mapExpressions(desugarExpression))
|
||||
.foldRight(desugarExpression(body))((arg, body) =>
|
||||
IR.Function.Lambda(List(arg), body, None)
|
||||
|
@ -114,8 +114,12 @@ case object OverloadsResolution extends IRPass {
|
||||
}
|
||||
}
|
||||
|
||||
val diagnostics = ir.bindings.collect {
|
||||
case diag: IR.Diagnostic => diag
|
||||
}
|
||||
|
||||
ir.copy(
|
||||
bindings = newAtoms ::: newMethods ::: conversions
|
||||
bindings = newAtoms ::: newMethods ::: conversions ::: diagnostics
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -839,8 +839,8 @@ class AliasAnalysisTest extends CompilerTest {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val conversionMethod =
|
||||
"""Bar.from (value : Foo) =
|
||||
| Bar value.get_thing here
|
||||
"""Bar.from (that : Foo) =
|
||||
| Bar that.get_thing here
|
||||
|""".stripMargin.preprocessModule.analyse.bindings.head
|
||||
.asInstanceOf[Method.Conversion]
|
||||
|
||||
|
@ -60,7 +60,7 @@ class BindingAnalysisTest extends CompilerTest {
|
||||
|Bar.baz = Baz 1 2 . foo
|
||||
|
|
||||
|from (_ : Bar) = Foo 0 0 0
|
||||
|from (value : Baz) = Foo value.x value.x value.y
|
||||
|from (that : Baz) = Foo that.x that.x that.y
|
||||
|
|
||||
|Foo.from (_ : Bar) = undefined
|
||||
|
|
||||
|
@ -1594,8 +1594,8 @@ class DataflowAnalysisTest extends CompilerTest {
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|Foo.from (value : Bar) =
|
||||
| Foo value 1
|
||||
|Foo.from (that : Bar) =
|
||||
| Foo that 1
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
val depInfo = ir.getMetadata(DataflowAnalysis).get
|
||||
|
@ -96,7 +96,7 @@ class TailCallTest extends CompilerTest {
|
||||
|
|
||||
|type MyAtom a b c
|
||||
|
|
||||
|Foo.from (value : Bar) = undefined
|
||||
|Foo.from (that : Bar) = undefined
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"mark methods as tail" in {
|
||||
|
@ -147,7 +147,7 @@ class FunctionBindingTest extends CompilerTest {
|
||||
|
||||
"be turned into Method.Conversion IR entities" in {
|
||||
val ir =
|
||||
s"""My_Type.$from (value : Other) ~config=Nothing = My_Type value.a
|
||||
s"""My_Type.$from (that : Other) ~config=Nothing = My_Type value.a
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Module.Scope.Definition.Method.Conversion]
|
||||
@ -156,7 +156,7 @@ class FunctionBindingTest extends CompilerTest {
|
||||
conversion.sourceTypeName.asInstanceOf[IR.Name].name shouldEqual "Other"
|
||||
val arguments = conversion.body.asInstanceOf[IR.Function.Lambda].arguments
|
||||
arguments.length shouldEqual 1
|
||||
arguments.head.name.name shouldEqual "value"
|
||||
arguments.head.name.name shouldEqual "that"
|
||||
arguments.head.ascribedType shouldBe defined
|
||||
arguments.head.defaultValue should not be defined
|
||||
arguments.head.suspended shouldBe false
|
||||
@ -210,7 +210,7 @@ class FunctionBindingTest extends CompilerTest {
|
||||
|
||||
"return an error if the conversion does not have a source type" in {
|
||||
val ir =
|
||||
s"""My_Type.$from value = value + value
|
||||
s"""My_Type.$from that = that + that
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Error.Conversion]
|
||||
@ -220,7 +220,7 @@ class FunctionBindingTest extends CompilerTest {
|
||||
|
||||
"return an error if the additional arguments don't have defaults" in {
|
||||
val ir =
|
||||
s"""My_Type.$from (value : Other) config = value + value
|
||||
s"""My_Type.$from (that : Other) config = that + that
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
ir.bindings.head shouldBe an[IR.Error.Conversion]
|
||||
|
@ -128,7 +128,7 @@ class GenerateMethodBodiesTest extends CompilerTest {
|
||||
|
||||
"have the `this` argument added" in {
|
||||
val ir =
|
||||
s"""My_Type.$from (value : Other) = value.a
|
||||
s"""My_Type.$from (that : Other) = that.a
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
val conversion =
|
||||
@ -141,7 +141,7 @@ class GenerateMethodBodiesTest extends CompilerTest {
|
||||
|
||||
"have their bodies replaced by an error if they redefine `this`" in {
|
||||
val ir =
|
||||
s"""My_Type.$from (value : Other) this=1 = value.a
|
||||
s"""My_Type.$from (that : Other) this=1 = that.a
|
||||
|""".stripMargin.preprocessModule.desugar
|
||||
|
||||
val conversion =
|
||||
|
@ -62,11 +62,11 @@ class MethodDefinitionsTest extends CompilerTest {
|
||||
|
|
||||
|Does_Not_Exist.method = 32
|
||||
|
|
||||
|Foo.from (value : Bar) = undefined
|
||||
|Foo.from (that : Bar) = undefined
|
||||
|
|
||||
|Bar.from (value : Does_Not_Exist) = undefined
|
||||
|Bar.from (that : Does_Not_Exist) = undefined
|
||||
|
|
||||
|Does_Not_Exist.from (value : Foo) = undefined
|
||||
|Does_Not_Exist.from (that : Foo) = undefined
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
"attach resolved atoms to the method definitions" in {
|
||||
|
@ -69,13 +69,13 @@ class ModuleThisToHereTest extends CompilerTest {
|
||||
| A -> this * here
|
||||
| z = y -> this + y
|
||||
|
|
||||
|from (other : Foo) =
|
||||
|from (that : Foo) =
|
||||
| x = this * this + this
|
||||
| y = case this of
|
||||
| A -> this * here
|
||||
| z = y -> this + y
|
||||
|
|
||||
|Foo.from (other : Foo) =
|
||||
|Foo.from (that : Foo) =
|
||||
| x = this * this + this
|
||||
| y = case this of
|
||||
| A -> this * here
|
||||
|
@ -85,8 +85,8 @@ class OverloadsResolutionTest extends CompilerTest {
|
||||
|
||||
"allow overloads on the source type" in {
|
||||
val ir =
|
||||
"""Unit.from (value : Integer) = undefined
|
||||
|Unit.from (value : Boolean) = undefined
|
||||
"""Unit.from (that : Integer) = undefined
|
||||
|Unit.from (that : Boolean) = undefined
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 2
|
||||
@ -96,9 +96,9 @@ class OverloadsResolutionTest extends CompilerTest {
|
||||
|
||||
"raise an error if there are multiple definitions with the same source type" in {
|
||||
val ir =
|
||||
"""Unit.from (value : Integer) = undefined
|
||||
|Unit.from (value : Boolean) = undefined
|
||||
|Unit.from (value : Boolean) = undefined
|
||||
"""Unit.from (that : Integer) = undefined
|
||||
|Unit.from (that : Boolean) = undefined
|
||||
|Unit.from (that : Boolean) = undefined
|
||||
|""".stripMargin.preprocessModule.resolve
|
||||
|
||||
ir.bindings.length shouldEqual 3
|
||||
|
@ -156,7 +156,7 @@ class SuspendedArgumentsTest extends CompilerTest {
|
||||
|
||||
val ir =
|
||||
"""File.from : Text -> Suspended -> Any
|
||||
|File.from (value : Text) config=Nothing = undefined
|
||||
|File.from (that : Text) config=Nothing = undefined
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
.asInstanceOf[Method.Conversion]
|
||||
|
||||
@ -172,7 +172,7 @@ class SuspendedArgumentsTest extends CompilerTest {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""File.from (~value : Text) = undefined
|
||||
"""File.from (~that : Text) = undefined
|
||||
|""".stripMargin.preprocessModule.resolve.bindings.head
|
||||
|
||||
ir shouldBe an[IR.Error.Conversion]
|
||||
|
@ -0,0 +1,26 @@
|
||||
package org.enso.interpreter.test.semantic
|
||||
|
||||
import org.enso.interpreter.test.{InterpreterContext, InterpreterTest}
|
||||
|
||||
class ConversionMethodsTest extends InterpreterTest {
|
||||
override def subject: String = "Methods"
|
||||
|
||||
override def specify(implicit
|
||||
interpreterContext: InterpreterContext
|
||||
): Unit = {
|
||||
"be defined in the global scope and dispatched to" in {
|
||||
val code =
|
||||
"""
|
||||
|type Foo foo
|
||||
|type Bar bar
|
||||
|type Baz baz
|
||||
|
|
||||
|Foo.from (that:Bar) = Foo that.bar
|
||||
|Foo.from (that:Baz) = Foo that.baz
|
||||
|
|
||||
|main = (Foo.from (Baz 10)).foo + (Foo.from (Bar 20)).foo
|
||||
|""".stripMargin
|
||||
eval(code) shouldEqual 30
|
||||
}
|
||||
}
|
||||
}
|
@ -398,7 +398,8 @@ spec =
|
||||
|
||||
Test.specify "should return Nothing if the group did not match" <|
|
||||
match.group 3 . should_equal Nothing
|
||||
Test.specify "should fail with No_Such_Group_Error if the group did not exist" <|
|
||||
|
||||
Test.specify "should fail with No_Such_Group_Error if the group did not exist" <|
|
||||
match.group "fail" . should_fail_with Regex.No_Such_Group_Error
|
||||
match.group 5 . should_fail_with Regex.No_Such_Group_Error
|
||||
|
||||
|
@ -4,6 +4,7 @@ import Standard.Test
|
||||
|
||||
import project.Semantic.Any_Spec
|
||||
import project.Semantic.Case_Spec
|
||||
import project.Semantic.Conversion_Spec
|
||||
import project.Semantic.Deep_Export.Spec as Deep_Export_Spec
|
||||
import project.Semantic.Error_Spec
|
||||
import project.Semantic.Import_Loop.Spec as Import_Loop_Spec
|
||||
@ -49,6 +50,7 @@ main = Test.Suite.run_main <|
|
||||
Any_Spec.spec
|
||||
Array_Spec.spec
|
||||
Case_Spec.spec
|
||||
Conversion_Spec.spec
|
||||
Deep_Export_Spec.spec
|
||||
Error_Spec.spec
|
||||
Examples_Spec.spec
|
||||
|
7
test/Tests/src/Semantic/Conversion/Methods.enso
Normal file
7
test/Tests/src/Semantic/Conversion/Methods.enso
Normal file
@ -0,0 +1,7 @@
|
||||
from Standard.Base import all
|
||||
import project.Semantic.Conversion.Types
|
||||
|
||||
get_foo = Types.Foo "foo"
|
||||
get_bar = Types.Bar "bar"
|
||||
|
||||
Text.from (that:Types.Bar) = that.a.to_text
|
6
test/Tests/src/Semantic/Conversion/Types.enso
Normal file
6
test/Tests/src/Semantic/Conversion/Types.enso
Normal file
@ -0,0 +1,6 @@
|
||||
from Standard.Base import all
|
||||
|
||||
type Foo a
|
||||
type Bar a
|
||||
|
||||
Vector.from (that:Foo) = [that.a]
|
100
test/Tests/src/Semantic/Conversion_Spec.enso
Normal file
100
test/Tests/src/Semantic/Conversion_Spec.enso
Normal file
@ -0,0 +1,100 @@
|
||||
from Standard.Base import all
|
||||
import project.Semantic.Conversion.Methods
|
||||
|
||||
import Standard.Test
|
||||
|
||||
type Foo foo
|
||||
type Bar bar
|
||||
type Baz baz
|
||||
type Quux quux
|
||||
type Quaffle
|
||||
type MyError err
|
||||
|
||||
type NotFoo notfoo
|
||||
|
||||
Foo.from (that:Bar) = Foo that.bar
|
||||
Foo.from (that:Baz) = Foo that.baz
|
||||
Foo.from (that:Text) = Foo that.length
|
||||
Foo.from (that:Number) first_param=0 second_param=0 third_param=0 = Foo [that, first_param, second_param, third_param]
|
||||
Foo.from (that:Function) = Foo (that 5)
|
||||
Foo.from (that:Boolean) = Foo that
|
||||
Foo.from (that:Array) = Foo that.length
|
||||
|
||||
NotFoo.from (that:True) = NotFoo that
|
||||
NotFoo.from (_:False) = NotFoo True
|
||||
NotFoo.from (_:Any) = NotFoo "ANY!!!"
|
||||
|
||||
Foo.from (_:Quaffle) = Foo "quaffle"
|
||||
Foo.from (_:Error) = Foo "oops"
|
||||
|
||||
foreign js make_str x = """
|
||||
return "js string"
|
||||
|
||||
foreign js call_function fn arg_1 = """
|
||||
return fn(arg_1, "a string");
|
||||
|
||||
Number.foo = "foo called"
|
||||
|
||||
spec =
|
||||
Test.group "Conversion" <|
|
||||
Test.specify "should be able to convert atoms" <|
|
||||
((Foo.from (Baz 10)).foo + (Foo.from (Bar 20)).foo) . should_equal 30
|
||||
Foo.from Quaffle . foo . should_equal "quaffle"
|
||||
Test.specify "should be able to convert text" <|
|
||||
Foo.from "123" . foo . should_equal 3
|
||||
Test.specify "should be able to convert foreign text" <|
|
||||
Foo.from (here.make_str 4) . foo . should_equal 9
|
||||
Test.specify "should be able to convert numbers" <|
|
||||
Foo.from 4 . should_equal (Foo [4, 0, 0, 0])
|
||||
Foo.from (10^100) . should_equal (Foo [10^100, 0, 0, 0])
|
||||
Foo.from 4.5 . should_equal (Foo [4.5, 0, 0, 0])
|
||||
Test.specify "should be able to convert dataflow errors" <|
|
||||
Foo.from (Error.throw <| MyError "i was bad") . should_equal (Foo "oops")
|
||||
Test.specify "should be able to convert functions" <|
|
||||
Foo.from (e -> e) . foo . should_equal 5
|
||||
Test.specify "should be able to convert booleans" <|
|
||||
Foo.from True . foo . should_be_true
|
||||
Foo.from False . foo . should_be_false
|
||||
NotFoo.from True . notfoo . should_be_true
|
||||
NotFoo.from False . notfoo . should_be_true
|
||||
Test.specify "should be able to convert arrays" <|
|
||||
Foo.from [1,2,3].to_array . foo . should_equal 3
|
||||
Test.specify "should be able to convert Any" <|
|
||||
NotFoo.from that=Quaffle . notfoo . should_equal "ANY!!!"
|
||||
NotFoo.from 4 . notfoo . should_equal "ANY!!!"
|
||||
NotFoo.from (e -> e) . notfoo . should_equal "ANY!!!"
|
||||
NotFoo.from [1,2,3].to_array . notfoo . should_equal "ANY!!!"
|
||||
Test.specify "should call intrinsic object conversions for unimported constructors" <|
|
||||
Vector.from Methods.get_foo . should_equal ["foo"]
|
||||
Test.specify "should call extension conversions" <|
|
||||
Text.from Methods.get_bar . should_equal "'bar'"
|
||||
|
||||
Test.specify "should fail graciously when there is no conversion" <|
|
||||
Panic.recover (Foo.from (Quux 10)) . catch .to_display_text . should_equal "Could not find a conversion from `Quux` to `Foo`"
|
||||
Test.specify "should fail graciously when the conversion target is invalid" <|
|
||||
Panic.recover (123.from (Quux 10)) . catch .to_display_text . should_equal "123 is not a valid conversion target. Expected a type."
|
||||
|
||||
Test.specify "should be callable with by-name arguments" <|
|
||||
.from that=4 this=Foo . should_equal (Foo [4, 0, 0, 0])
|
||||
Test.specify "should support the use of multiple arguments" <|
|
||||
Foo.from that=4 second_param=1 2 . should_equal (Foo [4, 2, 1, 0])
|
||||
|
||||
Test.specify "should play nicely with polyglot" <|
|
||||
here.call_function .from Foo . should_equal (Foo 8)
|
||||
|
||||
Test.specify "should support the meta functions" <|
|
||||
meta_from = Meta.meta .from
|
||||
is_symbol = case meta_from of
|
||||
Meta.Unresolved_Symbol _ -> True
|
||||
_ -> False
|
||||
is_symbol.should_be_true
|
||||
|
||||
.from . is_a Meta.Unresolved_Symbol . should_be_true
|
||||
|
||||
meta_from.name.should_equal "from"
|
||||
|
||||
Meta.meta .foo . rename "from" . should_equal .from
|
||||
Meta.meta .foo . rename "from" Foo "hello" . should_equal (Foo 5)
|
||||
|
||||
meta_from.rename "foo" 123 . should_equal "foo called"
|
||||
meta_from.rename "foo" . should_equal .foo
|
Loading…
Reference in New Issue
Block a user