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);
+ }
+}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java
index abe121ff456..690927386f9 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeMethodNode.java
@@ -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;
/**
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/displaytext/InvalidConversionTargetErrorToDisplayTextNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/displaytext/InvalidConversionTargetErrorToDisplayTextNode.java
new file mode 100644
index 00000000000..73acf6aa167
--- /dev/null
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/displaytext/InvalidConversionTargetErrorToDisplayTextNode.java
@@ -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.");
+ }
+}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/displaytext/NoSuchConversionErrorToDisplayTextNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/displaytext/NoSuchConversionErrorToDisplayTextNode.java
new file mode 100644
index 00000000000..e5627c3175d
--- /dev/null
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/displaytext/NoSuchConversionErrorToDisplayTextNode.java
@@ -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.");
+ }
+}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/CreateUnresolvedSymbolNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/CreateUnresolvedSymbolNode.java
index b7314af2758..c92e9ed325f 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/CreateUnresolvedSymbolNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/CreateUnresolvedSymbolNode.java
@@ -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);
+ }
}
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolNameNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolNameNode.java
index 3d04487d899..e967674a726 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolNameNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolNameNode.java
@@ -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);
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolScopeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolScopeNode.java
index cff3981543f..7f2b2a6ed3d 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolScopeNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetUnresolvedSymbolScopeNode.java
@@ -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);
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsUnresolvedSymbolNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsUnresolvedSymbolNode.java
index 68f01c55459..7d0f8426647 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsUnresolvedSymbolNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsUnresolvedSymbolNode.java
@@ -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);
}
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java
index 3355011ec8c..1c2b5f0698a 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/Error.java
@@ -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}.
*
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConversion.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConversion.java
new file mode 100644
index 00000000000..978e3f3f261
--- /dev/null
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/UnresolvedConversion.java
@@ -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.
+ *
+ * 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);
+ }
+ }
+}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/Atom.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/Atom.java
index f2a6ce0dda9..410155e3515 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/Atom.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/Atom.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java
index a20de00ba3f..0abc7f645b6 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/atom/AtomConstructor.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java
index 763b2baf9ca..899c7d9a20b 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/callable/function/Function.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java
index f4ef811e7bd..8c86a76a814 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Array.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java
index d9051b2bcd6..311093cd7a5 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/text/Text.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java
index cdcbd7d4041..229cbf96fd7 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/DataflowError.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicSentinel.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicSentinel.java
index da0ab563dbf..c00c14cd39a 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicSentinel.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/PanicSentinel.java
@@ -49,4 +49,9 @@ public class PanicSentinel extends AbstractTruffleException {
boolean hasSpecialDispatch() {
return true;
}
+
+ @ExportMessage
+ boolean hasSpecialConversion() {
+ return true;
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/RedefinedConversionException.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/RedefinedConversionException.java
new file mode 100644
index 00000000000..0c7b800efe2
--- /dev/null
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/error/RedefinedConversionException.java
@@ -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);
+ }
+}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultBooleanExports.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultBooleanExports.java
index 7be7ac46b57..9d41a1f39af 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultBooleanExports.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultBooleanExports.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultDoubleExports.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultDoubleExports.java
index f9e4b388006..1375685cb1c 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultDoubleExports.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultDoubleExports.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultLongExports.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultLongExports.java
index 391d3449d05..949a0869255 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultLongExports.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/DefaultLongExports.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/MethodDispatchLibrary.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/MethodDispatchLibrary.java
index e221530e3c8..27cafbd7563 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/MethodDispatchLibrary.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/MethodDispatchLibrary.java
@@ -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();
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java
index fe8aa13642a..edc0679427c 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/number/EnsoBigInteger.java
@@ -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;
+ }
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java
index baf3bcab847..965624314f3 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/scope/ModuleScope.java
@@ -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 polyglotSymbols = new HashMap<>();
private Map constructors = new HashMap<>();
private Map> methods = new HashMap<>();
+ private Map> conversions = new HashMap<>();
private Set imports = new HashSet<>();
private Set 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 ensureConversionsFor(AtomConstructor cons) {
+ //var methods = ensureMethodMapFor(cons);
+ //methods.
+ return conversions.computeIfAbsent(cons, k -> new HashMap<>());
+ }
+
+ private Map getConversionsFor(AtomConstructor cons) {
+ Map 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 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> getConversions() {
+ return conversions;
+ }
+
/** @return the polyglot symbols imported into this scope. */
public Map 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<>();
}
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java
index 8f2aa41a4dc..808b54772ef 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/Types.java
@@ -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;
diff --git a/engine/runtime/src/main/resources/Builtins.enso b/engine/runtime/src/main/resources/Builtins.enso
index 9249aaf301b..64619260432 100644
--- a/engine/runtime/src/main/resources/Builtins.enso
+++ b/engine/runtime/src/main/resources/Builtins.enso
@@ -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
diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala
index b476714835e..5a6c49bbaf0 100644
--- a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala
+++ b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala
@@ -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
diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala
index 2e2ec56ff63..153851b759d 100644
--- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala
+++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/AstToIr.scala
@@ -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)
diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala
index 4cfe79f2411..a6f5f757bf2 100644
--- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala
+++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala
@@ -10,91 +10,55 @@ import org.enso.compiler.data.{BindingsMap, CompilerConfig}
import org.enso.compiler.exception.{BadPatternMatch, CompilerError}
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Scope => AliasScope}
import org.enso.compiler.pass.analyse.AliasAnalysis.{Graph => AliasGraph}
-import org.enso.compiler.pass.analyse.{
- AliasAnalysis,
- BindingAnalysis,
- DataflowAnalysis,
- TailCall
-}
+import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis, DataflowAnalysis, TailCall}
import org.enso.compiler.pass.optimise.ApplicationSaturation
-import org.enso.compiler.pass.resolve.{
- MethodDefinitions,
- Patterns,
- UppercaseNames
-}
+import org.enso.compiler.pass.resolve.{MethodDefinitions, Patterns, UppercaseNames}
import org.enso.interpreter.epb.EpbParser
import org.enso.interpreter.node.callable.argument.ReadArgumentNode
-import org.enso.interpreter.node.callable.function.{
- BlockNode,
- CreateFunctionNode
-}
+import org.enso.interpreter.node.callable.function.{BlockNode, CreateFunctionNode}
import org.enso.interpreter.node.callable.thunk.{CreateThunkNode, ForceNode}
-import org.enso.interpreter.node.callable.{
- ApplicationNode,
- InvokeCallableNode,
- SequenceLiteralNode
-}
+import org.enso.interpreter.node.callable.{ApplicationNode, InvokeCallableNode, SequenceLiteralNode}
import org.enso.interpreter.node.controlflow.caseexpr._
import org.enso.interpreter.node.expression.atom.QualifiedAccessorNode
import org.enso.interpreter.node.expression.constant._
import org.enso.interpreter.node.expression.foreign.ForeignMethodCallNode
-import org.enso.interpreter.node.expression.literal.{
- BigIntegerLiteralNode,
- DecimalLiteralNode,
- IntegerLiteralNode,
- TextLiteralNode
-}
+import org.enso.interpreter.node.expression.literal.{BigIntegerLiteralNode, DecimalLiteralNode, IntegerLiteralNode, TextLiteralNode}
import org.enso.interpreter.node.scope.{AssignmentNode, ReadLocalVariableNode}
-import org.enso.interpreter.node.{
- BaseNode,
- ClosureRootNode,
- MethodRootNode,
- ExpressionNode => RuntimeExpression
-}
+import org.enso.interpreter.node.{BaseNode, ClosureRootNode, MethodRootNode, ExpressionNode => RuntimeExpression}
import org.enso.interpreter.runtime.Context
-import org.enso.interpreter.runtime.callable.UnresolvedSymbol
-import org.enso.interpreter.runtime.callable.argument.{
- ArgumentDefinition,
- CallArgument
-}
+import org.enso.interpreter.runtime.callable.{UnresolvedConversion, UnresolvedSymbol}
+import org.enso.interpreter.runtime.callable.argument.{ArgumentDefinition, CallArgument}
import org.enso.interpreter.runtime.callable.atom.AtomConstructor
-import org.enso.interpreter.runtime.callable.function.{
- FunctionSchema,
- Function => RuntimeFunction
-}
+import org.enso.interpreter.runtime.callable.function.{FunctionSchema, Function => RuntimeFunction}
import org.enso.interpreter.runtime.data.text.Text
-import org.enso.interpreter.runtime.scope.{
- FramePointer,
- LocalScope,
- ModuleScope
-}
+import org.enso.interpreter.runtime.scope.{FramePointer, LocalScope, ModuleScope}
import org.enso.interpreter.{Constants, Language}
-import java.math.BigInteger
+import java.math.BigInteger
import org.enso.compiler.core.IR.Name.Special
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
/** This is an implementation of a codegeneration pass that lowers the Enso
- * [[IR]] into the truffle [[org.enso.compiler.core.Core.Node]] structures that
- * are actually executed.
- *
- * It should be noted that, as is, there is no support for cross-module links,
- * with each lowering pass operating solely on a single module.
- *
- * @param context the language context instance for which this is executing
- * @param source the source code that corresponds to the text for which code
- * is being generated
- * @param moduleScope the scope of the module for which code is being generated
- * @param compilerConfig the configuration for the compiler
- */
+ * [[IR]] into the truffle [[org.enso.compiler.core.Core.Node]] structures that
+ * are actually executed.
+ *
+ * It should be noted that, as is, there is no support for cross-module links,
+ * with each lowering pass operating solely on a single module.
+ *
+ * @param context the language context instance for which this is executing
+ * @param source the source code that corresponds to the text for which code
+ * is being generated
+ * @param moduleScope the scope of the module for which code is being generated
+ * @param compilerConfig the configuration for the compiler
+ */
class IrToTruffle(
- val context: Context,
- val source: Source,
- val moduleScope: ModuleScope,
- val compilerConfig: CompilerConfig
-) {
+ val context: Context,
+ val source: Source,
+ val moduleScope: ModuleScope,
+ val compilerConfig: CompilerConfig
+ ) {
val language: Language = context.getLanguage
@@ -103,31 +67,31 @@ class IrToTruffle(
// ==========================================================================
/** Executes the codegen pass on the input [[IR]].
- *
- * Please note that the IR passed to this function should not contain _any_
- * errors (members of [[IR.Error]]). These must be dealt with and reported
- * before codegen runs, as they will cause a compiler error.
- *
- * In future, this restriction will be relaxed to admit errors that are
- * members of [[IR.Diagnostic.Kind.Interactive]], such that we can display
- * these to users during interactive execution.
- *
- * @param ir the IR to generate code for
- */
+ *
+ * Please note that the IR passed to this function should not contain _any_
+ * errors (members of [[IR.Error]]). These must be dealt with and reported
+ * before codegen runs, as they will cause a compiler error.
+ *
+ * In future, this restriction will be relaxed to admit errors that are
+ * members of [[IR.Diagnostic.Kind.Interactive]], such that we can display
+ * these to users during interactive execution.
+ *
+ * @param ir the IR to generate code for
+ */
def run(ir: IR.Module): Unit = processModule(ir)
/** Executes the codegen pass on an inline input.
- *
- * @param ir the IR to generate code for
- * @param localScope the scope in which the inline input exists
- * @param scopeName the name of `localScope`
- * @return an truffle expression representing `ir`
- */
+ *
+ * @param ir the IR to generate code for
+ * @param localScope the scope in which the inline input exists
+ * @param scopeName the name of `localScope`
+ * @return an truffle expression representing `ir`
+ */
def runInline(
- ir: IR.Expression,
- localScope: LocalScope,
- scopeName: String
- ): RuntimeExpression = {
+ ir: IR.Expression,
+ localScope: LocalScope,
+ scopeName: String
+ ): RuntimeExpression = {
new ExpressionProcessor(localScope, scopeName).runInline(ir)
}
@@ -136,13 +100,13 @@ class IrToTruffle(
// ==========================================================================
/** Generates truffle nodes from the top-level definitions of an Enso module
- * and registers these definitions in scope in the compiler.
- *
- * It does not directly return any constructs, but instead registers these
- * constructs for later access in the compiler and language context.
- *
- * @param module the module for which code should be generated
- */
+ * and registers these definitions in scope in the compiler.
+ *
+ * It does not directly return any constructs, but instead registers these
+ * constructs for later access in the compiler and language context.
+ *
+ * @param module the module for which code should be generated
+ */
private def processModule(module: IR.Module): Unit = {
generateMethods()
generateReExportBindings(module)
@@ -162,14 +126,10 @@ class IrToTruffle(
val methodDefs = module.bindings.collect {
case method: IR.Module.Scope.Definition.Method.Explicit => method
}
- val conversionDefs = module.bindings.collect {
- case conversion: IR.Module.Scope.Definition.Method.Conversion =>
- conversion
- }
// Register the imports in scope
imports.foreach {
- case poly @ Import.Polyglot(i: Import.Polyglot.Java, _, _, _, _) =>
+ case poly@Import.Polyglot(i: Import.Polyglot.Java, _, _, _, _) =>
this.moduleScope.registerPolyglotSymbol(
poly.getVisibleName,
context.getEnvironment.lookupHostSymbol(i.getJavaName)
@@ -225,7 +185,7 @@ class IrToTruffle(
.unsafeGetMetadata(
AliasAnalysis,
s"Missing scope information for method " +
- s"`${methodDef.typeName.name}.${methodDef.methodName.name}`."
+ s"`${methodDef.typeName.name}.${methodDef.methodName.name}`."
)
.unsafeAs[AliasAnalysis.Info.Scope.Root]
val dataflowInfo = methodDef.unsafeGetMetadata(
@@ -293,13 +253,18 @@ class IrToTruffle(
}
})
+ val conversionDefs = module.bindings.collect {
+ case conversion: IR.Module.Scope.Definition.Method.Conversion =>
+ conversion
+ }
+
// Register the conversion definitions in scope
conversionDefs.foreach(methodDef => {
val scopeInfo = methodDef
.unsafeGetMetadata(
AliasAnalysis,
s"Missing scope information for conversion " +
- s"`${methodDef.typeName.name}.${methodDef.methodName.name}`."
+ s"`${methodDef.typeName.name}.${methodDef.methodName.name}`."
)
.unsafeAs[AliasAnalysis.Info.Scope.Root]
val dataflowInfo = methodDef.unsafeGetMetadata(
@@ -307,33 +272,11 @@ class IrToTruffle(
"Method definition missing dataflow information."
)
- val consOpt =
- methodDef.methodReference.typePointer
- .getMetadata(MethodDefinitions)
- .map { res =>
- res.target match {
- case BindingsMap.ResolvedModule(module) =>
- module.unsafeAsModule().getScope.getAssociatedType
- case BindingsMap.ResolvedConstructor(definitionModule, cons) =>
- definitionModule
- .unsafeAsModule()
- .getScope
- .getConstructors
- .get(cons.name)
- case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
- throw new CompilerError(
- "Impossible polyglot symbol, should be caught by MethodDefinitions pass."
- )
- case _: BindingsMap.ResolvedMethod =>
- throw new CompilerError(
- "Impossible here, should be caught by MethodDefinitions pass."
- )
- }
- }
-
- consOpt.foreach { cons =>
+ val toOpt = getConstructorResolution(methodDef.methodReference.typePointer)
+ val fromOpt = getConstructorResolution(methodDef.sourceTypeName)
+ toOpt.zip(fromOpt).foreach { case (toType, fromType) =>
val expressionProcessor = new ExpressionProcessor(
- cons.getName ++ Constants.SCOPE_SEPARATOR ++ methodDef.methodName.name,
+ toType.getName ++ Constants.SCOPE_SEPARATOR ++ methodDef.methodName.name,
scopeInfo.graph,
scopeInfo.graph.rootScope,
dataflowInfo
@@ -349,7 +292,7 @@ class IrToTruffle(
moduleScope,
body,
makeSection(methodDef.location),
- cons,
+ toType,
methodDef.methodName.name
)
val callTarget = Truffle.getRuntime.createCallTarget(rootNode)
@@ -363,7 +306,7 @@ class IrToTruffle(
"Conversion bodies must be functions at the point of codegen."
)
}
- moduleScope.registerMethod(cons, methodDef.methodName.name, function)
+ moduleScope.registerConversionMethod(toType, fromType, function)
}
})
}
@@ -373,21 +316,43 @@ class IrToTruffle(
// ==========================================================================
/** Creates a source section from a given location in the code.
- *
- * @param location the location to turn into a section
- * @return the source section corresponding to `location`
- */
+ *
+ * @param location the location to turn into a section
+ * @return the source section corresponding to `location`
+ */
private def makeSection(
- location: Option[IdentifiedLocation]
- ): SourceSection = {
+ location: Option[IdentifiedLocation]
+ ): SourceSection = {
location
.map(loc => source.createSection(loc.start, loc.length))
.getOrElse(source.createUnavailableSection())
}
+ private def getConstructorResolution(expr: IR): Option[AtomConstructor] =
+ expr.getMetadata(MethodDefinitions).map { res =>
+ res.target match {
+ case BindingsMap.ResolvedModule(module) =>
+ module.unsafeAsModule().getScope.getAssociatedType
+ case BindingsMap.ResolvedConstructor(definitionModule, cons) =>
+ definitionModule
+ .unsafeAsModule()
+ .getScope
+ .getConstructors
+ .get(cons.name)
+ case BindingsMap.ResolvedPolyglotSymbol(_, _) =>
+ throw new CompilerError(
+ "Impossible polyglot symbol, should be caught by MethodDefinitions pass."
+ )
+ case _: BindingsMap.ResolvedMethod =>
+ throw new CompilerError(
+ "Impossible here, should be caught by MethodDefinitions pass."
+ )
+ }
+ }
+
private def getTailStatus(
- expression: IR.Expression
- ): BaseNode.TailStatus = {
+ expression: IR.Expression
+ ): BaseNode.TailStatus = {
val isTailPosition =
expression.getMetadata(TailCall).contains(TailCall.TailPosition.Tail)
val isTailAnnotated = TailCall.isTailAnnotated(expression)
@@ -403,17 +368,17 @@ class IrToTruffle(
}
/** Sets the source section for a given expression node to the provided
- * location.
- *
- * @param expr the expression to set the location for
- * @param location the location to assign to `expr`
- * @tparam T the type of `expr`
- * @return `expr` with its location set to `location`
- */
+ * location.
+ *
+ * @param expr the expression to set the location for
+ * @param location the location to assign to `expr`
+ * @tparam T the type of `expr`
+ * @return `expr` with its location set to `location`
+ */
private def setLocation[T <: RuntimeExpression](
- expr: T,
- location: Option[IdentifiedLocation]
- ): T = {
+ expr: T,
+ location: Option[IdentifiedLocation]
+ ): T = {
location.foreach { loc =>
expr.setSourceLocation(loc.start, loc.length)
loc.id.foreach { id => expr.setId(id) }
@@ -427,7 +392,7 @@ class IrToTruffle(
private def generateEnsoProjectMethod(): Unit = {
val name = BindingsMap.Generated.ensoProjectMethodName
- val pkg = context.getPackageOf(moduleScope.getModule.getSourceFile)
+ val pkg = context.getPackageOf(moduleScope.getModule.getSourceFile)
val body = Truffle.getRuntime.createCallTarget(
new EnsoProjectNode(language, context, pkg)
)
@@ -510,31 +475,31 @@ class IrToTruffle(
// ==========================================================================
/** This class is responsible for performing codegen of [[IR]] constructs that
- * are Enso program expressions.
- *
- * @param scope the scope in which the code generation is occurring
- * @param scopeName the name of `scope`
- */
+ * are Enso program expressions.
+ *
+ * @param scope the scope in which the code generation is occurring
+ * @param scopeName the name of `scope`
+ */
sealed private class ExpressionProcessor(
- val scope: LocalScope,
- val scopeName: String
- ) {
+ val scope: LocalScope,
+ val scopeName: String
+ ) {
private var currentVarName = ""
// === Construction =======================================================
/** Constructs an [[ExpressionProcessor]] instance with a defaulted local
- * scope.
- *
- * @param scopeName the name to attribute to the default local scope.
- */
+ * scope.
+ *
+ * @param scopeName the name to attribute to the default local scope.
+ */
def this(
- scopeName: String,
- graph: AliasGraph,
- scope: AliasScope,
- dataflowInfo: DataflowAnalysis.Metadata
- ) = {
+ scopeName: String,
+ graph: AliasGraph,
+ scope: AliasScope,
+ dataflowInfo: DataflowAnalysis.Metadata
+ ) = {
this(
new LocalScope(None, graph, scope, dataflowInfo),
scopeName
@@ -542,35 +507,35 @@ class IrToTruffle(
}
/** Creates an instance of [[ExpressionProcessor]] that operates in a child
- * scope of `this`.
- *
- * @param name the name of the child scope
- * @return an expression processor operating on a child scope
- */
+ * scope of `this`.
+ *
+ * @param name the name of the child scope
+ * @return an expression processor operating on a child scope
+ */
def createChild(
- name: String,
- scope: AliasScope
- ): ExpressionProcessor = {
+ name: String,
+ scope: AliasScope
+ ): ExpressionProcessor = {
new ExpressionProcessor(this.scope.createChild(scope), name)
}
// === Runner =============================================================
/** Runs the code generation process on the provided piece of [[IR]].
- *
- * @param ir the IR to generate code for
- * @return a truffle expression that represents the same program as `ir`
- */
+ *
+ * @param ir the IR to generate code for
+ * @return a truffle expression that represents the same program as `ir`
+ */
def run(ir: IR.Expression): RuntimeExpression = {
val runtimeExpression = ir match {
- case block: IR.Expression.Block => processBlock(block)
- case literal: IR.Literal => processLiteral(literal)
- case app: IR.Application => processApplication(app)
- case name: IR.Name => processName(name)
- case function: IR.Function => processFunction(function)
+ case block: IR.Expression.Block => processBlock(block)
+ case literal: IR.Literal => processLiteral(literal)
+ case app: IR.Application => processApplication(app)
+ case name: IR.Name => processName(name)
+ case function: IR.Function => processFunction(function)
case binding: IR.Expression.Binding => processBinding(binding)
- case caseExpr: IR.Case => processCase(caseExpr)
- case typ: IR.Type => processType(typ)
+ case caseExpr: IR.Case => processCase(caseExpr)
+ case typ: IR.Type => processType(typ)
case _: IR.Empty =>
throw new CompilerError(
"Empty IR nodes should not exist during code generation."
@@ -591,11 +556,11 @@ class IrToTruffle(
}
/** Executes the expression processor on a piece of code that has been
- * written inline.
- *
- * @param ir the IR to generate code for
- * @return a truffle expression that represents the same program as `ir`
- */
+ * written inline.
+ *
+ * @param ir the IR to generate code for
+ * @return a truffle expression that represents the same program as `ir`
+ */
def runInline(ir: IR.Expression): RuntimeExpression = {
val expression = run(ir)
expression
@@ -604,10 +569,10 @@ class IrToTruffle(
// === Processing =========================================================
/** Performs code generation for an Enso block expression.
- *
- * @param block the block to generate code for
- * @return the truffle nodes corresponding to `block`
- */
+ *
+ * @param block the block to generate code for
+ * @return the truffle nodes corresponding to `block`
+ */
def processBlock(block: IR.Expression.Block): RuntimeExpression = {
if (block.suspended) {
val scopeInfo = block
@@ -618,7 +583,7 @@ class IrToTruffle(
.unsafeAs[AliasAnalysis.Info.Scope.Child]
val childFactory = this.createChild("suspended-block", scopeInfo.scope)
- val childScope = childFactory.scope
+ val childScope = childFactory.scope
val blockNode = childFactory.processBlock(block.copy(suspended = false))
@@ -635,7 +600,7 @@ class IrToTruffle(
setLocation(CreateThunkNode.build(callTarget), block.location)
} else {
val statementExprs = block.expressions.map(this.run).toArray
- val retExpr = this.run(block.returnValue)
+ val retExpr = this.run(block.returnValue)
val blockNode = BlockNode.build(statementExprs, retExpr)
setLocation(blockNode, block.location)
@@ -643,10 +608,10 @@ class IrToTruffle(
}
/** Performs code generation for an Enso type operator.
- *
- * @param value the type operation to generate code for
- * @return the truffle nodes corresponding to `value`
- */
+ *
+ * @param value the type operation to generate code for
+ * @return the truffle nodes corresponding to `value`
+ */
def processType(value: IR.Type): RuntimeExpression = {
setLocation(
ErrorNode.build(
@@ -664,16 +629,16 @@ class IrToTruffle(
}
/** Performs code generation for an Enso case expression.
- *
- * @param caseExpr the case expression to generate code for
- * @return the truffle nodes corresponding to `caseExpr`
- */
+ *
+ * @param caseExpr the case expression to generate code for
+ * @return the truffle nodes corresponding to `caseExpr`
+ */
def processCase(caseExpr: IR.Case): RuntimeExpression =
caseExpr match {
case IR.Case.Expr(scrutinee, branches, location, _, _) =>
val scrutineeNode = this.run(scrutinee)
- val maybeCases = branches.map(processCaseBranch)
+ val maybeCases = branches.map(processCaseBranch)
val allCasesValid = maybeCases.forall(_.isRight)
if (allCasesValid) {
@@ -708,14 +673,14 @@ class IrToTruffle(
}
/** Performs code generation for an Enso case branch.
- *
- * @param branch the case branch to generate code for
- * @return the truffle nodes correspondingg to `caseBranch` or an error if
- * the match is invalid
- */
+ *
+ * @param branch the case branch to generate code for
+ * @return the truffle nodes correspondingg to `caseBranch` or an error if
+ * the match is invalid
+ */
def processCaseBranch(
- branch: IR.Case.Branch
- ): Either[BadPatternMatch, BranchNode] = {
+ branch: IR.Case.Branch
+ ): Either[BadPatternMatch, BranchNode] = {
val scopeInfo = branch
.unsafeGetMetadata(
AliasAnalysis,
@@ -726,7 +691,7 @@ class IrToTruffle(
val childProcessor = this.createChild("case_branch", scopeInfo.scope)
branch.pattern match {
- case named @ Pattern.Name(_, _, _, _) =>
+ case named@Pattern.Name(_, _, _, _) =>
val arg = List(genArgFromMatchField(named))
val branchCodeNode = childProcessor.processFunctionBody(
@@ -739,11 +704,11 @@ class IrToTruffle(
CatchAllBranchNode.build(branchCodeNode.getCallTarget)
Right(branchNode)
- case cons @ Pattern.Constructor(constructor, _, _, _, _) =>
+ case cons@Pattern.Constructor(constructor, _, _, _, _) =>
if (!cons.isDesugared) {
throw new CompilerError(
"Nested patterns desugaring must have taken place by the " +
- "point of code generation."
+ "point of code generation."
)
}
@@ -755,37 +720,37 @@ class IrToTruffle(
case None =>
Left(BadPatternMatch.NonVisibleConstructor(constructor.name))
case Some(
- BindingsMap.Resolution(BindingsMap.ResolvedModule(mod))
- ) =>
+ BindingsMap.Resolution(BindingsMap.ResolvedModule(mod))
+ ) =>
Right(mod.unsafeAsModule().getScope.getAssociatedType)
case Some(
- BindingsMap.Resolution(
- BindingsMap.ResolvedConstructor(mod, cons)
- )
- ) =>
+ BindingsMap.Resolution(
+ BindingsMap.ResolvedConstructor(mod, cons)
+ )
+ ) =>
Right(
mod.unsafeAsModule().getScope.getConstructors.get(cons.name)
)
case Some(
- BindingsMap.Resolution(
- BindingsMap.ResolvedPolyglotSymbol(_, _)
- )
- ) =>
+ BindingsMap.Resolution(
+ BindingsMap.ResolvedPolyglotSymbol(_, _)
+ )
+ ) =>
throw new CompilerError(
"Impossible polyglot symbol here, should be caught by Patterns resolution pass."
)
case Some(
- BindingsMap.Resolution(
- BindingsMap.ResolvedMethod(_, _)
- )
- ) =>
+ BindingsMap.Resolution(
+ BindingsMap.ResolvedMethod(_, _)
+ )
+ ) =>
throw new CompilerError(
"Impossible method here, should be caught by Patterns resolution pass."
)
}
}
- val fieldNames = cons.unsafeFieldsAsNamed
+ val fieldNames = cons.unsafeFieldsAsNamed
val fieldsAsArgs = fieldNames.map(genArgFromMatchField)
val branchCodeNode = childProcessor.processFunctionBody(
@@ -795,12 +760,12 @@ class IrToTruffle(
)
runtimeConsOpt.map { atomCons =>
- val any = context.getBuiltins.any
- val array = context.getBuiltins.mutable.array
- val bool = context.getBuiltins.bool
- val number = context.getBuiltins.number
+ val any = context.getBuiltins.any
+ val array = context.getBuiltins.mutable.array
+ val bool = context.getBuiltins.bool
+ val number = context.getBuiltins.number
val polyglot = context.getBuiltins.polyglot.getPolyglot
- val text = context.getBuiltins.text
+ val text = context.getBuiltins.text
val branchNode: BranchNode =
if (atomCons == bool.getTrue) {
BooleanBranchNode.build(true, branchCodeNode.getCallTarget)
@@ -842,11 +807,11 @@ class IrToTruffle(
"Branch documentation should be desugared at an earlier stage."
)
case IR.Error.Pattern(
- _,
- IR.Error.Pattern.WrongArity(name, expected, actual),
- _,
- _
- ) =>
+ _,
+ IR.Error.Pattern.WrongArity(name, expected, actual),
+ _,
+ _
+ ) =>
Left(BadPatternMatch.WrongArgCount(name, expected, actual))
}
@@ -862,10 +827,10 @@ class IrToTruffle(
*/
/** Generates an argument from a field of a pattern match.
- *
- * @param name the pattern field to generate from
- * @return `name` as a function definition argument.
- */
+ *
+ * @param name the pattern field to generate from
+ * @return `name` as a function definition argument.
+ */
def genArgFromMatchField(name: Pattern.Name): IR.DefinitionArgument = {
IR.DefinitionArgument.Specified(
name.name,
@@ -873,16 +838,16 @@ class IrToTruffle(
None,
suspended = false,
name.location,
- passData = name.name.passData,
+ passData = name.name.passData,
diagnostics = name.name.diagnostics
)
}
/** Generates code for an Enso binding expression.
- *
- * @param binding the binding to generate code for
- * @return the truffle nodes corresponding to `binding`
- */
+ *
+ * @param binding the binding to generate code for
+ * @return the truffle nodes corresponding to `binding`
+ */
def processBinding(binding: IR.Expression.Binding): RuntimeExpression = {
val occInfo = binding
.unsafeGetMetadata(
@@ -902,10 +867,10 @@ class IrToTruffle(
}
/** Generates code for an Enso function.
- *
- * @param function the function to generate code for
- * @return the truffle nodes corresponding to `function`
- */
+ *
+ * @param function the function to generate code for
+ * @return the truffle nodes corresponding to `function`
+ */
def processFunction(function: IR.Function): RuntimeExpression = {
val scopeInfo = function
.unsafeGetMetadata(AliasAnalysis, "No scope info on a function.")
@@ -914,7 +879,7 @@ class IrToTruffle(
if (function.body.isInstanceOf[IR.Function]) {
throw new CompilerError(
"Lambda found directly as function body. It looks like Lambda " +
- "Consolidation hasn't run."
+ "Consolidation hasn't run."
)
}
@@ -936,10 +901,10 @@ class IrToTruffle(
}
/** Generates code for an Enso name.
- *
- * @param name the name to generate code for
- * @return the truffle nodes corresponding to `name`
- */
+ *
+ * @param name the name to generate code for
+ * @return the truffle nodes corresponding to `name`
+ */
def processName(name: IR.Name): RuntimeExpression = {
val nameExpr = name match {
case IR.Name.Literal(nameStr, _, _, _, _, _) =>
@@ -950,7 +915,7 @@ class IrToTruffle(
)
.unsafeAs[AliasAnalysis.Info.Occurrence]
- val slot = scope.getFramePointer(useInfo.id)
+ val slot = scope.getFramePointer(useInfo.id)
val global = name.getMetadata(UppercaseNames)
if (slot.isDefined) {
ReadLocalVariableNode.build(slot.get)
@@ -982,6 +947,8 @@ class IrToTruffle(
"Impossible here, should be desugared by UppercaseNames resolver"
)
}
+ } else if (nameStr == "from") {
+ ConstantObjectNode.build(UnresolvedConversion.build(moduleScope))
} else {
DynamicSymbolNode.build(
UnresolvedSymbol.build(nameStr, moduleScope)
@@ -994,16 +961,16 @@ class IrToTruffle(
IR.Name.Literal(
Constants.Names.THIS_ARGUMENT,
isReferent = false,
- isMethod = false,
+ isMethod = false,
location,
passData
)
)
case IR.Name.Special(name, _, _, _) =>
val fun = name match {
- case Special.NewRef => context.getBuiltins.special().getNewRef
- case Special.ReadRef => context.getBuiltins.special().getReadRef
- case Special.WriteRef => context.getBuiltins.special().getWriteRef
+ case Special.NewRef => context.getBuiltins.special().getNewRef
+ case Special.ReadRef => context.getBuiltins.special().getReadRef
+ case Special.WriteRef => context.getBuiltins.special().getWriteRef
case Special.RunThread => context.getBuiltins.special().getRunThread
case Special.JoinThread =>
context.getBuiltins.special().getJoinThread
@@ -1033,18 +1000,20 @@ class IrToTruffle(
}
/** Generates code for an Enso literal.
- *
- * @param literal the literal to generate code for
- * @return the truffle nodes corresponding to `literal`
- */
+ *
+ * @param literal the literal to generate code for
+ * @return the truffle nodes corresponding to `literal`
+ */
def processLiteral(literal: IR.Literal): RuntimeExpression =
literal match {
- case lit @ IR.Literal.Number(base, value, location, _, _) =>
+ case lit@IR.Literal.Number(base, value, location, _, _) =>
val node = if (lit.isFractional) {
DecimalLiteralNode.build(value.toDouble)
} else if (base.isDefined) {
val baseNum =
- try { Integer.parseInt(base.get) }
+ try {
+ Integer.parseInt(base.get)
+ }
catch {
case _: NumberFormatException =>
throw new CompilerError(
@@ -1077,10 +1046,10 @@ class IrToTruffle(
}
/** Generates a runtime implementation for compile error nodes.
- *
- * @param error the IR representing a compile error.
- * @return a runtime node representing the error.
- */
+ *
+ * @param error the IR representing a compile error.
+ * @return a runtime node representing the error.
+ */
def processError(error: IR.Error): RuntimeExpression = {
val payload: AnyRef = error match {
case Error.InvalidIR(_, _, _) =>
@@ -1148,22 +1117,22 @@ class IrToTruffle(
}
/** Processes function arguments, generates arguments reads and creates
- * a node to represent the whole method body.
- *
- * @param arguments the argument definitions
- * @param body the body definition
- * @return a node for the final shape of function body and pre-processed
- * argument definitions.
- */
+ * a node to represent the whole method body.
+ *
+ * @param arguments the argument definitions
+ * @param body the body definition
+ * @return a node for the final shape of function body and pre-processed
+ * argument definitions.
+ */
def buildFunctionBody(
- arguments: List[IR.DefinitionArgument],
- body: IR.Expression
- ): (BlockNode, Array[ArgumentDefinition]) = {
+ arguments: List[IR.DefinitionArgument],
+ body: IR.Expression
+ ): (BlockNode, Array[ArgumentDefinition]) = {
val argFactory = new DefinitionArgumentProcessor(scopeName, scope)
val argDefinitions = new Array[ArgumentDefinition](arguments.size)
val argExpressions = new ArrayBuffer[RuntimeExpression]
- val seenArgNames = mutable.Set[String]()
+ val seenArgNames = mutable.Set[String]()
// Note [Rewriting Arguments]
val argSlots =
@@ -1211,11 +1180,11 @@ class IrToTruffle(
}
private def buildForeignBody(
- language: EpbParser.ForeignLanguage,
- code: String,
- argumentNames: List[String],
- argumentSlots: List[FrameSlot]
- ): RuntimeExpression = {
+ language: EpbParser.ForeignLanguage,
+ code: String,
+ argumentNames: List[String],
+ argumentSlots: List[FrameSlot]
+ ): RuntimeExpression = {
val src = EpbParser.buildSource(language, code, scopeName)
val foreignCt = context.getEnvironment
.parseInternal(src, argumentNames: _*)
@@ -1226,17 +1195,17 @@ class IrToTruffle(
}
/** Generates code for an Enso function body.
- *
- * @param arguments the arguments to the function
- * @param body the body of the function
- * @param location the location at which the function exists in the source
- * @return a truffle node representing the described function
- */
+ *
+ * @param arguments the arguments to the function
+ * @param body the body of the function
+ * @param location the location at which the function exists in the source
+ * @return a truffle node representing the described function
+ */
def processFunctionBody(
- arguments: List[IR.DefinitionArgument],
- body: IR.Expression,
- location: Option[IdentifiedLocation]
- ): CreateFunctionNode = {
+ arguments: List[IR.DefinitionArgument],
+ body: IR.Expression,
+ location: Option[IdentifiedLocation]
+ ): CreateFunctionNode = {
val (fnBodyNode, argDefinitions) = buildFunctionBody(arguments, body)
val fnRootNode = ClosureRootNode.build(
language,
@@ -1278,17 +1247,17 @@ class IrToTruffle(
*/
/** Generates code for an Enso function application.
- *
- * @param application the function application to generate code for
- * @return the truffle nodes corresponding to `application`
- */
+ *
+ * @param application the function application to generate code for
+ * @return the truffle nodes corresponding to `application`
+ */
def processApplication(application: IR.Application): RuntimeExpression =
application match {
case IR.Application.Prefix(fn, args, hasDefaultsSuspended, loc, _, _) =>
val callArgFactory = new CallArgumentProcessor(scope, scopeName)
val arguments = args
- val callArgs = new ArrayBuffer[CallArgument]()
+ val callArgs = new ArrayBuffer[CallArgument]()
for ((unprocessedArg, position) <- arguments.view.zipWithIndex) {
val arg = callArgFactory.run(unprocessedArg, position)
@@ -1303,8 +1272,8 @@ class IrToTruffle(
val appNode = application.getMetadata(ApplicationSaturation) match {
case Some(
- ApplicationSaturation.CallSaturation.Exact(createOptimised)
- ) =>
+ ApplicationSaturation.CallSaturation.Exact(createOptimised)
+ ) =>
createOptimised(moduleScope)(scope)(callArgs.toList)
case _ =>
ApplicationNode.build(
@@ -1341,7 +1310,7 @@ class IrToTruffle(
case sec: IR.Application.Operator.Section =>
throw new CompilerError(
s"Explicit operator sections not supported during codegen but " +
- s"$sec found"
+ s"$sec found"
)
}
}
@@ -1351,34 +1320,34 @@ class IrToTruffle(
// ==========================================================================
/** Performs codegen for call-site arguments in Enso.
- *
- * @param scope the scope in which the function call exists
- * @param scopeName the name of `scope`
- */
+ *
+ * @param scope the scope in which the function call exists
+ * @param scopeName the name of `scope`
+ */
sealed private class CallArgumentProcessor(
- val scope: LocalScope,
- val scopeName: String
- ) {
+ val scope: LocalScope,
+ val scopeName: String
+ ) {
// === Runner =============================================================
/** Executes codegen on the call-site argument.
- *
- * @param arg the argument definition
- * @param position the position of the argument at the call site
- * @return a truffle construct corresponding to the argument definition
- * `arg`
- */
+ *
+ * @param arg the argument definition
+ * @param position the position of the argument at the call site
+ * @return a truffle construct corresponding to the argument definition
+ * `arg`
+ */
def run(arg: IR.CallArgument, position: Int): CallArgument =
arg match {
case IR.CallArgument.Specified(
- name,
- value,
- _,
- shouldBeSuspended,
- _,
- _
- ) =>
+ name,
+ value,
+ _,
+ shouldBeSuspended,
+ _,
+ _
+ ) =>
val scopeInfo = arg
.unsafeGetMetadata(
AliasAnalysis,
@@ -1387,8 +1356,8 @@ class IrToTruffle(
.unsafeAs[AliasAnalysis.Info.Scope.Child]
val shouldSuspend = value match {
- case _: IR.Name => false
- case _: IR.Literal.Text => false
+ case _: IR.Name => false
+ case _: IR.Literal.Text => false
case _: IR.Literal.Number => false
case _ =>
shouldBeSuspended.getOrElse(
@@ -1456,14 +1425,14 @@ class IrToTruffle(
// ==========================================================================
/** Performs codegen for definition-site arguments in Enso.
- *
- * @param scope the scope in which the function is defined
- * @param scopeName the name of `scope`
- */
+ *
+ * @param scope the scope in which the function is defined
+ * @param scopeName the name of `scope`
+ */
sealed private class DefinitionArgumentProcessor(
- val scopeName: String = "",
- val scope: LocalScope
- ) {
+ val scopeName: String = "",
+ val scope: LocalScope
+ ) {
// === Runner =============================================================
@@ -1478,16 +1447,16 @@ class IrToTruffle(
*/
/** Executes the code generator on the provided definition-site argument.
- *
- * @param inputArg the argument to generate code for
- * @param position the position of `arg` at the function definition site
- * @return a truffle entity corresponding to the definition of `arg` for a
- * given function
- */
+ *
+ * @param inputArg the argument to generate code for
+ * @param position the position of `arg` at the function definition site
+ * @return a truffle entity corresponding to the definition of `arg` for a
+ * given function
+ */
def run(
- inputArg: IR.DefinitionArgument,
- position: Int
- ): ArgumentDefinition =
+ inputArg: IR.DefinitionArgument,
+ position: Int
+ ): ArgumentDefinition =
inputArg match {
case arg: IR.DefinitionArgument.Specified =>
val defaultExpression = arg.defaultValue
diff --git a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala
index c43396901d7..8f6f49d8c94 100644
--- a/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala
+++ b/engine/runtime/src/main/scala/org/enso/compiler/core/IR.scala
@@ -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.
diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala
index 4e0e543dfc9..d80b4f31fc4 100644
--- a/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala
+++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/analyse/AliasAnalysis.scala
@@ -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 =>
+ }
}
}
}
diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/FunctionBinding.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/FunctionBinding.scala
index cf9ec545dc1..7ce5636c24a 100644
--- a/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/FunctionBinding.scala
+++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/desugar/FunctionBinding.scala
@@ -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)
diff --git a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/OverloadsResolution.scala b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/OverloadsResolution.scala
index 0941f086851..20025dc11eb 100644
--- a/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/OverloadsResolution.scala
+++ b/engine/runtime/src/main/scala/org/enso/compiler/pass/resolve/OverloadsResolution.scala
@@ -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
)
}
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/AliasAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/AliasAnalysisTest.scala
index 1332d36a389..8ec03075736 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/AliasAnalysisTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/AliasAnalysisTest.scala
@@ -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]
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala
index 8af54261fc5..f8c3aede462 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/BindingAnalysisTest.scala
@@ -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
|
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala
index 6ea695a2258..4ef8507bd9a 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/DataflowAnalysisTest.scala
@@ -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
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala
index 137c15c5bcb..0f3a6d1bab2 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/analyse/TailCallTest.scala
@@ -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 {
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/FunctionBindingTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/FunctionBindingTest.scala
index e149f2b80e8..44577bd2c9a 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/FunctionBindingTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/FunctionBindingTest.scala
@@ -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]
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/GenerateMethodBodiesTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/GenerateMethodBodiesTest.scala
index 4e50cdb2dbc..74d8c5bb73b 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/GenerateMethodBodiesTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/desugar/GenerateMethodBodiesTest.scala
@@ -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 =
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala
index df362d5ca3e..d562017479f 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/MethodDefinitionsTest.scala
@@ -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 {
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/ModuleThisToHereTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/ModuleThisToHereTest.scala
index 77167609110..36aa4bd7ad1 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/ModuleThisToHereTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/ModuleThisToHereTest.scala
@@ -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
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/OverloadsResolutionTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/OverloadsResolutionTest.scala
index fb82262652b..efbc15405ac 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/OverloadsResolutionTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/OverloadsResolutionTest.scala
@@ -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
diff --git a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala
index b1669a74e7f..8b40880df20 100644
--- a/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala
+++ b/engine/runtime/src/test/scala/org/enso/compiler/test/pass/resolve/SuspendedArgumentsTest.scala
@@ -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]
diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ConversionMethodsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ConversionMethodsTest.scala
new file mode 100644
index 00000000000..8d0505fad0a
--- /dev/null
+++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ConversionMethodsTest.scala
@@ -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
+ }
+ }
+}
diff --git a/test/Tests/src/Data/Text/Default_Regex_Engine_Spec.enso b/test/Tests/src/Data/Text/Default_Regex_Engine_Spec.enso
index b5c5e4d8605..5dab66ee92d 100644
--- a/test/Tests/src/Data/Text/Default_Regex_Engine_Spec.enso
+++ b/test/Tests/src/Data/Text/Default_Regex_Engine_Spec.enso
@@ -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
diff --git a/test/Tests/src/Main.enso b/test/Tests/src/Main.enso
index 05d229676df..ac0009eed46 100644
--- a/test/Tests/src/Main.enso
+++ b/test/Tests/src/Main.enso
@@ -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
diff --git a/test/Tests/src/Semantic/Conversion/Methods.enso b/test/Tests/src/Semantic/Conversion/Methods.enso
new file mode 100644
index 00000000000..1d7ea4a0ee6
--- /dev/null
+++ b/test/Tests/src/Semantic/Conversion/Methods.enso
@@ -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
diff --git a/test/Tests/src/Semantic/Conversion/Types.enso b/test/Tests/src/Semantic/Conversion/Types.enso
new file mode 100644
index 00000000000..6fce83e0339
--- /dev/null
+++ b/test/Tests/src/Semantic/Conversion/Types.enso
@@ -0,0 +1,6 @@
+from Standard.Base import all
+
+type Foo a
+type Bar a
+
+Vector.from (that:Foo) = [that.a]
diff --git a/test/Tests/src/Semantic/Conversion_Spec.enso b/test/Tests/src/Semantic/Conversion_Spec.enso
new file mode 100644
index 00000000000..afa390b0fbf
--- /dev/null
+++ b/test/Tests/src/Semantic/Conversion_Spec.enso
@@ -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