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:
Edward Kmett 2022-02-06 04:02:09 -05:00 committed by GitHub
parent 9c689dfe42
commit 8a70debb59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 2259 additions and 460 deletions

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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. */

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
/**

View File

@ -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.");
}
}

View File

@ -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.");
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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}.
*

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -49,4 +49,9 @@ public class PanicSentinel extends AbstractTruffleException {
boolean hasSpecialDispatch() {
return true;
}
@ExportMessage
boolean hasSpecialConversion() {
return true;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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<>();
}
}

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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 =>
}
}
}
}

View File

@ -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)

View File

@ -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
)
}

View File

@ -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]

View File

@ -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
|

View File

@ -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

View File

@ -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 {

View File

@ -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]

View File

@ -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 =

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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
}
}
}

View File

@ -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

View File

@ -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

View 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

View File

@ -0,0 +1,6 @@
from Standard.Base import all
type Foo a
type Bar a
Vector.from (that:Foo) = [that.a]

View 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