diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso
index bafe3431a303..46741a56a5c0 100644
--- a/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso
+++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Extensions.enso
@@ -295,7 +295,7 @@ Error.should_equal self that frames_to_skip=0 =
Number.should_equal : Float -> Float -> Integer -> Spec_Result
Number.should_equal self that epsilon=0 frames_to_skip=0 =
matches = case that of
- _ : Number -> self.equals that epsilon
+ n : Number -> self.equals n epsilon
_ -> self==that
case matches of
True -> Spec_Result.Success
diff --git a/docs/types/intersection-types.md b/docs/types/intersection-types.md
index 5d75b6e0873a..919e138a0cc1 100644
--- a/docs/types/intersection-types.md
+++ b/docs/types/intersection-types.md
@@ -80,18 +80,14 @@ method calls will also only accept the value if it satisfies the type it
_has been cast to_. Any additional remaining _hidden_ types
can only be brought back through an _explicit_ cast.
To perform an explicit cast that can uncover the 'hidden' part of a type write
-`f = c:Float`.
-
-
+`f = c:Float` or inspect the types in a `case` expression, e.g.
+```ruby
+case c of
+ f : Float -> f.sqrt
+ _ -> "Not a float"
+```
+Remember to use `f.sqrt` and not `c.sqrt`. `f` in the case branch _has been cast to_ `Float` while
+`c` in the case branch only _can be cast to_.
> [!WARNING]
> Keep in mind that while both argument type check in method definitions and a
diff --git a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java
index 2900d878fe48..43adb215bd69 100644
--- a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java
+++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java
@@ -220,7 +220,7 @@ private String[] typeOf(Object value) {
}
var typeOfNode = TypeOfNode.getUncached();
- Type[] allTypes = value == null ? null : typeOfNode.findAllTypesOrNull(value);
+ Type[] allTypes = value == null ? null : typeOfNode.findAllTypesOrNull(value, true);
if (allTypes != null) {
String[] result = new String[allTypes.length];
for (var i = 0; i < allTypes.length; i++) {
diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeMultiValueTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeMultiValueTest.java
index 51c9eb632d25..835c24f62f24 100644
--- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeMultiValueTest.java
+++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeMultiValueTest.java
@@ -49,8 +49,9 @@ private static Context ctx() {
new TestRootNode(
(frame) -> {
var arg = frame.getArguments()[0];
+ var allTypes = (boolean) frame.getArguments()[1];
var t = node.findTypeOrError(arg);
- var all = node.findAllTypesOrNull(arg);
+ var all = node.findAllTypesOrNull(arg, allTypes);
return new Object[] {t, all};
});
root.insertChildren(node);
@@ -116,16 +117,22 @@ public static void disposeCtx() throws Exception {
}
@Test
- public void typeOfCheck() {
- assertType(value, type, typeIndex);
+ public void typeOfCheckAllTypes() {
+ assertType(value, type, typeIndex, true);
}
- private static void assertType(Object value, String expectedTypeName, int typeIndex) {
+ @Test
+ public void typeOfCheckHasBeenCastToTypes() {
+ assertType(value, type, typeIndex, false);
+ }
+
+ private static void assertType(
+ Object value, String expectedTypeName, int typeIndex, boolean allTypes) {
assertNotNull("Value " + value + " should have a type", expectedTypeName);
ContextUtils.executeInContext(
ctx(),
() -> {
- var pairResult = (Object[]) testTypesCall.call(value);
+ var pairResult = (Object[]) testTypesCall.call(value, allTypes);
var t = pairResult[0];
var all = (Object[]) pairResult[1];
diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeValueTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeValueTest.java
index 2633448001c0..3c22ef041617 100644
--- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeValueTest.java
+++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeValueTest.java
@@ -3,8 +3,10 @@
import static org.junit.Assert.assertEquals;
import com.oracle.truffle.api.RootCallTarget;
+import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedConstructor;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
+import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
import org.enso.test.utils.ContextUtils;
@@ -28,8 +30,9 @@ private static Context ctx() {
new TestRootNode(
(frame) -> {
var arg = frame.getArguments()[0];
+ var allTypes = (boolean) frame.getArguments()[1];
var t = node.findTypeOrError(arg);
- var all = node.findAllTypesOrNull(arg);
+ var all = node.findAllTypesOrNull(arg, allTypes);
return new Object[] {t, all};
});
root.insertChildren(node);
@@ -54,7 +57,7 @@ public void typeOfUnresolvedConstructor() {
ctx(),
() -> {
var cnstr = UnresolvedConstructor.build(null, "Unknown_Name");
- var arr = (Object[]) testTypesCall.call(cnstr);
+ var arr = (Object[]) testTypesCall.call(cnstr, true);
var type = (Type) arr[0];
var allTypes = (Type[]) arr[1];
assertEquals("Function", type.getName());
@@ -70,7 +73,7 @@ public void typeOfUnresolvedSymbol() {
ctx(),
() -> {
var cnstr = UnresolvedSymbol.build("Unknown_Name", null);
- var arr = (Object[]) testTypesCall.call(cnstr);
+ var arr = (Object[]) testTypesCall.call(cnstr, true);
var type = (Type) arr[0];
var allTypes = (Type[]) arr[1];
assertEquals("Function", type.getName());
@@ -79,4 +82,29 @@ public void typeOfUnresolvedSymbol() {
return null;
});
}
+
+ @Test
+ public void multiValueWithHiddenType() {
+ ContextUtils.executeInContext(
+ ctx(),
+ () -> {
+ var ensoCtx = EnsoContext.get(testTypesCall.getRootNode());
+ var types =
+ new Type[] {
+ ensoCtx.getBuiltins().number().getInteger(), ensoCtx.getBuiltins().text()
+ };
+ var multi = EnsoMultiValue.create(types, 1, new Object[] {42L, "Meaning"});
+ var arr = (Object[]) testTypesCall.call(multi, true);
+ var allTypes = (Type[]) arr[1];
+ assertEquals("Two types", 2, allTypes.length);
+ assertEquals("Integer", types[0], allTypes[0]);
+ assertEquals("Text", types[1], allTypes[1]);
+
+ var arr1 = (Object[]) testTypesCall.call(multi, false);
+ var allTypes1 = (Type[]) arr1[1];
+ assertEquals("Just one type", 1, allTypes1.length);
+ assertEquals("Integer", types[0], allTypes1[0]);
+ return null;
+ });
+ }
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java
index e7106442abee..cd076538fca9 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java
@@ -152,20 +152,13 @@ final Object doThatConversionUncached(
InvokeFunctionNode invokeNode) {
var selfType = findType(typeOfNode, self);
if (that instanceof EnsoMultiValue multi) {
- var all = typeOfNode.findAllTypesOrNull(multi);
+ var all = typeOfNode.findAllTypesOrNull(multi, false);
assert all != null;
for (var thatType : all) {
var fn = findSymbol(symbol, thatType);
if (fn != null) {
- var thatCasted =
- EnsoMultiValue.CastToNode.getUncached()
- .findTypeOrNull(thatType, multi, false, false);
- if (thatCasted == null) {
- continue;
- }
var result =
- doDispatch(
- frame, self, thatCasted, selfType, thatType, fn, convertNode, invokeNode);
+ doDispatch(frame, self, multi, selfType, thatType, fn, convertNode, invokeNode);
if (result != null) {
return result;
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/CatchTypeBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/CatchTypeBranchNode.java
index f29b5876fe00..d74721bf7486 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/CatchTypeBranchNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/CatchTypeBranchNode.java
@@ -5,6 +5,7 @@
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.CountingConditionProfile;
import org.enso.interpreter.node.expression.builtin.meta.IsValueOfTypeNode;
+import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.Type;
/** An implementation of the case expression specialised to working on types. */
@@ -33,7 +34,13 @@ public static CatchTypeBranchNode build(
}
public void execute(VirtualFrame frame, Object state, Object value) {
- if (profile.profile(isValueOfTypeNode.execute(expectedType, value))) {
+ if (profile.profile(isValueOfTypeNode.execute(expectedType, value, true))) {
+ if (value instanceof EnsoMultiValue multi) {
+ var replacement =
+ EnsoMultiValue.CastToNode.getUncached().findTypeOrNull(expectedType, multi, true, true);
+ assert replacement != null : "Must find the type, when isValueOfTypeNode is true";
+ value = replacement;
+ }
accept(frame, state, new Object[] {value});
}
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java
index 6d29469fad89..e3ea9d1ef3f6 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNode.java
@@ -86,7 +86,7 @@ private Object executeCallbackOrRethrow(
AbstractTruffleException originalException,
InteropLibrary interopLibrary) {
- if (profile.profile(isValueOfTypeNode.execute(panicType, payload))) {
+ if (profile.profile(isValueOfTypeNode.execute(panicType, payload, true))) {
var builtins = EnsoContext.get(this).getBuiltins();
var cons = builtins.caughtPanic().getUniqueConstructor();
var caughtPanic =
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/GetStackTraceTextNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/GetStackTraceTextNode.java
index a4b0c9027792..8dce7b82ed1e 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/GetStackTraceTextNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/GetStackTraceTextNode.java
@@ -4,6 +4,7 @@
import com.oracle.truffle.api.TruffleStackTrace;
import com.oracle.truffle.api.TruffleStackTraceElement;
import com.oracle.truffle.api.nodes.Node;
+import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import org.enso.interpreter.dsl.BuiltinMethod;
@@ -76,6 +77,11 @@ String printStackTrace(Throwable throwable) {
var sourceLoc = errorFrame.getLocation().getEncapsulatingSourceSection();
if (sourceLoc != null) {
var path = sourceLoc.getSource().getPath();
+ try {
+ path = new File(sourceLoc.getSource().getURI()).getPath();
+ } catch (IllegalArgumentException | NullPointerException ignore) {
+ // keep original value of path
+ }
var ident = (path != null) ? path : sourceLoc.getSource().getName();
var loc =
(sourceLoc.getStartLine() == sourceLoc.getEndLine())
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsSimpleNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsSimpleNode.java
index f7aedb3f7464..aaf800c5d961 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsSimpleNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsSimpleNode.java
@@ -331,7 +331,7 @@ EqualsAndInfo equalsMultiValue(
@Shared("multiCast") @Cached EnsoMultiValue.CastToNode castNode,
@Shared("multiType") @Cached TypeOfNode typesNode,
@Shared("multiEquals") @Cached EqualsSimpleNode delegate) {
- var types = typesNode.findAllTypesOrNull(self);
+ var types = typesNode.findAllTypesOrNull(self, false);
assert types != null;
for (var t : types) {
var value = castNode.findTypeOrNull(t, self, false, false);
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsANode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsANode.java
index 753b369d3bed..51a5dbe2c858 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsANode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsANode.java
@@ -13,6 +13,6 @@ public class IsANode extends Node {
private @Child IsValueOfTypeNode check = IsValueOfTypeNode.build();
public boolean execute(@AcceptsError Object value, Object type) {
- return check.execute(type, value);
+ return check.execute(type, value, true);
}
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java
index 46431e3d1679..da3ab5653e46 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java
@@ -17,53 +17,72 @@
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
-import org.enso.interpreter.runtime.type.TypesGen;
/** An implementation of the payload check against the expected panic type. */
@NodeInfo(shortName = "IsValueOfTypeNode")
public abstract class IsValueOfTypeNode extends Node {
+ IsValueOfTypeNode() {}
+
public static IsValueOfTypeNode build() {
return IsValueOfTypeNodeGen.create();
}
- public abstract boolean execute(Object expectedType, Object payload);
+ /**
+ * @param expectedType the type to check
+ * @param obj the object to check
+ * @param includeExtraTypes specify {@code false} to return only types value has already been
+ * cast to, specify {@code true} to return all types value can be cast to
+ */
+ public abstract boolean execute(Object expectedType, Object obj, boolean includeExtraTypes);
@Specialization(guards = {"types.hasType(payload)"})
boolean doTyped(
Object expectedType,
Object payload,
+ boolean allTypes,
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
@Cached Typed typed) {
- return typed.execute(expectedType, payload);
+ return typed.execute(expectedType, payload, allTypes);
}
@Specialization(guards = {"!types.hasType(payload)"})
boolean doPolyglot(
Object expectedType,
Object payload,
+ boolean allTypes,
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
@Cached Untyped untyped) {
- return untyped.execute(expectedType, payload);
+ return untyped.execute(expectedType, payload, allTypes);
}
private static boolean typeAndCheck(
Object payload,
- Object expectedType,
+ Object expectedMeta,
+ boolean allTypes,
TypeOfNode typeOfNode,
IsSameObjectNode isSameObject,
CountingConditionProfile isSameObjectProfile) {
- Object tpeOfPayload = typeOfNode.findTypeOrError(payload);
- if (isSameObjectProfile.profile(isSameObject.execute(expectedType, tpeOfPayload))) {
- return true;
- } else if (TypesGen.isType(tpeOfPayload)) {
- Type tpe = TypesGen.asType(tpeOfPayload);
- var ctx = EnsoContext.get(typeOfNode);
- for (var superTpe : tpe.allTypes(ctx)) {
- boolean testSuperTpe = isSameObject.execute(expectedType, superTpe);
- if (testSuperTpe) {
+ if (expectedMeta instanceof Type expectedType) {
+ var arr = typeOfNode.findAllTypesOrNull(payload, allTypes);
+ if (arr == null) {
+ return false;
+ }
+ for (var tpeOfPayload : arr) {
+ if (isSameObjectProfile.profile(isSameObject.execute(expectedType, tpeOfPayload))) {
return true;
+ } else {
+ var ctx = EnsoContext.get(typeOfNode);
+ for (var superTpe : tpeOfPayload.allTypes(ctx)) {
+ boolean testSuperTpe = isSameObject.execute(expectedType, superTpe);
+ if (testSuperTpe) {
+ return true;
+ }
+ }
}
}
+ } else {
+ var tpe = typeOfNode.findTypeOrError(payload);
+ return isSameObject.execute(expectedMeta, tpe);
}
return false;
}
@@ -73,33 +92,33 @@ abstract static class Typed extends Node {
private @Child TypeOfNode typeOfNode = TypeOfNode.create();
private final CountingConditionProfile profile = CountingConditionProfile.create();
- abstract boolean execute(Object expectedType, Object payload);
+ abstract boolean execute(Object expectedType, Object payload, boolean allTypes);
@Specialization(guards = "isAnyType(expectedType)")
- boolean doAnyType(Object expectedType, Object payload) {
+ boolean doAnyType(Object expectedType, Object payload, boolean allTypes) {
return true;
}
@Specialization
- boolean doLongCheck(Type expectedType, long payload) {
+ boolean doLongCheck(Type expectedType, long payload, boolean allTypes) {
var numbers = EnsoContext.get(this).getBuiltins().number();
return checkParentTypes(numbers.getInteger(), expectedType);
}
@Specialization
- boolean doDoubleCheck(Type expectedType, double payload) {
+ boolean doDoubleCheck(Type expectedType, double payload, boolean allTypes) {
var numbers = EnsoContext.get(this).getBuiltins().number();
return checkParentTypes(numbers.getFloat(), expectedType);
}
@Specialization
- boolean doBigIntegerCheck(Type expectedType, EnsoBigInteger value) {
+ boolean doBigIntegerCheck(Type expectedType, EnsoBigInteger value, boolean allTypes) {
var numbers = EnsoContext.get(this).getBuiltins().number();
return checkParentTypes(numbers.getInteger(), expectedType);
}
@Specialization
- boolean doUnresolvedSymbol(Type expectedType, UnresolvedSymbol value) {
+ boolean doUnresolvedSymbol(Type expectedType, UnresolvedSymbol value, boolean allTypes) {
var funTpe = EnsoContext.get(this).getBuiltins().function();
return expectedType == funTpe;
}
@@ -119,8 +138,9 @@ private boolean checkParentTypes(Type actual, Type expected) {
boolean doType(
Type expectedType,
Object payload,
+ boolean allTypes,
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
- return typeAndCheck(payload, expectedType, typeOfNode, isSameObject, profile);
+ return typeAndCheck(payload, expectedType, allTypes, typeOfNode, isSameObject, profile);
}
@Specialization(
@@ -131,13 +151,14 @@ boolean doType(
public boolean doArrayViaType(
Object expectedType,
Object payload,
+ boolean allTypes,
@CachedLibrary(limit = "3") InteropLibrary interop,
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
return EnsoContext.get(this).getBuiltins().array() == types.getType(payload);
}
@Fallback
- boolean doOther(Object expectedType, Object payload) {
+ boolean doOther(Object expectedType, Object payload, boolean allTypes) {
return false;
}
@@ -155,7 +176,7 @@ abstract static class Untyped extends Node {
private @Child TypeOfNode typeOfNode = TypeOfNode.create();
private final CountingConditionProfile profile = CountingConditionProfile.create();
- abstract boolean execute(Object expectedType, Object payload);
+ abstract boolean execute(Object expectedType, Object payload, boolean allTypes);
@Specialization(
guards = {
@@ -163,7 +184,10 @@ abstract static class Untyped extends Node {
"isMetaInstance(interop, expectedType, payload)"
})
boolean doPolyglotType(
- Object expectedType, Object payload, @CachedLibrary(limit = "3") InteropLibrary interop) {
+ Object expectedType,
+ Object payload,
+ boolean allTypes,
+ @CachedLibrary(limit = "3") InteropLibrary interop) {
return true;
}
@@ -176,8 +200,8 @@ static boolean isMetaInstance(InteropLibrary interop, Object expectedType, Objec
}
@Fallback
- public boolean doOther(Object expectedType, Object payload) {
- return typeAndCheck(payload, expectedType, typeOfNode, isSameObject, profile);
+ public boolean doOther(Object expectedType, Object payload, boolean allTypes) {
+ return typeAndCheck(payload, expectedType, allTypes, typeOfNode, isSameObject, profile);
}
}
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/MetaTypeCheckNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/MetaTypeCheckNode.java
index 2e59365ddd50..fcd2ac1ab4ce 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/MetaTypeCheckNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/MetaTypeCheckNode.java
@@ -34,7 +34,7 @@ Object verifyMetaObject(VirtualFrame frame, Object v, @Cached IsValueOfTypeNode
if (isAllFitValue(v)) {
return v;
}
- if (isA.execute(expectedSupplier.get(), v)) {
+ if (isA.execute(expectedSupplier.get(), v, true)) {
return v;
} else {
return null;
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java
index e868bdbc327d..cb5f26b10249 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java
@@ -123,7 +123,7 @@ final Object findDirectMatch(VirtualFrame frame, Object v) {
return result;
}
}
- if (checkType.execute(expectedType, v)) {
+ if (checkType.execute(expectedType, v, isAllTypes())) {
return v;
}
return null;
@@ -176,7 +176,7 @@ Type[] findType(TypeOfNode typeOfNode, Object v) {
Type[] findType(TypeOfNode typeOfNode, Object v, Type[] previous) {
if (v instanceof EnsoMultiValue multi) {
- var all = typeOfNode.findAllTypesOrNull(multi);
+ var all = typeOfNode.findAllTypesOrNull(multi, isAllTypes());
assert all != null;
var to = 0;
// only consider methodDispatchTypes and not "all types" of the multi value
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java
index 4ed82b616e6d..22f31f090bd1 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java
@@ -88,8 +88,12 @@ final Type getType() {
}
@ExportMessage
- final Type[] allTypes() {
- return types.clone();
+ final Type[] allTypes(boolean includeExtraTypes) {
+ if (includeExtraTypes || methodDispatchTypes == types.length) {
+ return types.clone();
+ } else {
+ return Arrays.copyOf(types, methodDispatchTypes);
+ }
}
@ExportMessage
@@ -452,19 +456,22 @@ public static CastToNode getUncached() {
@Specialization
Object castsToAType(Type type, EnsoMultiValue mv, boolean reorderOnly, boolean allTypes) {
+ var ctx = EnsoContext.get(this);
var max = allTypes ? mv.types.length : mv.methodDispatchTypes;
for (var i = 0; i < max; i++) {
- if (mv.types[i] == type) {
- if (reorderOnly) {
- var copyTypes = mv.types.clone();
- var copyValues = mv.values.clone();
- copyTypes[i] = mv.types[0];
- copyValues[i] = mv.values[0];
- copyTypes[0] = mv.types[i];
- copyValues[0] = mv.values[i];
- return EnsoMultiValue.create(copyTypes, 1, copyValues);
- } else {
- return mv.values[i];
+ for (var t : mv.types[i].allTypes(ctx)) {
+ if (t == type) {
+ if (reorderOnly) {
+ var copyTypes = mv.types.clone();
+ var copyValues = mv.values.clone();
+ copyTypes[i] = mv.types[0];
+ copyValues[i] = mv.values[0];
+ copyTypes[0] = mv.types[i];
+ copyValues[0] = mv.values[i];
+ return EnsoMultiValue.create(copyTypes, 1, copyValues);
+ } else {
+ return mv.values[i];
+ }
}
}
}
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java
index 0649a0de805c..88360c843e6c 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java
@@ -53,7 +53,7 @@ public final boolean hasType(Object value) {
*/
public final TruffleObject findTypeOrError(Object value) {
try {
- var types = executeTypes(value);
+ var types = executeTypes(value, false);
return types[0];
} catch (NonTypeResult plain) {
return plain.result;
@@ -76,11 +76,13 @@ public final Type findTypeOrNull(Object value) {
* that the returned array is going to have at least one element, if it is non-{@code null}.
*
* @param value the value to check
+ * @param includeExtraTypes specify {@code false} to return only types value has already been
+ * case to, specify {@code true} to return all types value can be cast to
* @return either types associated with the value or {@code null} if there is no such type
*/
- public final Type[] findAllTypesOrNull(Object value) {
+ public final Type[] findAllTypesOrNull(Object value, boolean includeExtraTypes) {
try {
- var types = executeTypes(value);
+ var types = executeTypes(value, includeExtraTypes);
assert types != null && types.length > 0;
return types;
} catch (NonTypeResult ex) {
@@ -92,11 +94,13 @@ public final Type[] findAllTypesOrNull(Object value) {
* Internal implementation call to delegate to various {@link Specialization} methods.
*
* @param value the value to find type for
+ * @param includeExtraTypes {@code false} to return only types value has already been case
+ * to, {@code true} to return all types value can be cast to
* @return array of types with at least one element, but possibly more
* @throws NonTypeResult when there is a interop value result, but it is not a {@link
* Type}
*/
- abstract Type[] executeTypes(Object value) throws NonTypeResult;
+ abstract Type[] executeTypes(Object value, boolean includeExtraTypes) throws NonTypeResult;
/**
* Creates new optimizing instance of this node.
@@ -123,38 +127,39 @@ private static Type[] fromType(Type single) {
}
@Specialization
- Type[] doUnresolvedSymbol(UnresolvedSymbol value) {
+ Type[] doUnresolvedSymbol(UnresolvedSymbol value, boolean allTypes) {
return fromType(EnsoContext.get(this).getBuiltins().function());
}
@Specialization
- Type[] doDouble(double value) {
+ Type[] doDouble(double value, boolean allTypes) {
return fromType(EnsoContext.get(this).getBuiltins().number().getFloat());
}
@Specialization
- Type[] doLong(long value) {
+ Type[] doLong(long value, boolean allTypes) {
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
}
@Specialization
- Type[] doBigInteger(EnsoBigInteger value) {
+ Type[] doBigInteger(EnsoBigInteger value, boolean allTypes) {
return fromType(EnsoContext.get(this).getBuiltins().number().getInteger());
}
@Specialization
- Type[] doPanicException(PanicException value) {
+ Type[] doPanicException(PanicException value, boolean allTypes) {
return fromType(EnsoContext.get(this).getBuiltins().panic());
}
@Specialization
- Type[] doPanicSentinel(PanicSentinel value) {
+ Type[] doPanicSentinel(PanicSentinel value, boolean allTypes) {
return fromType(EnsoContext.get(this).getBuiltins().panic());
}
@Specialization
- Type[] doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) throws NonTypeResult {
- return withoutWarning.executeTypes(value.getValue());
+ Type[] doWarning(WithWarnings value, boolean includeExtraTypes, @Cached TypeOfNode withoutWarning)
+ throws NonTypeResult {
+ return withoutWarning.executeTypes(value.getValue(), includeExtraTypes);
}
static boolean isWithType(Object value, TypesLibrary types, InteropLibrary iop) {
@@ -180,6 +185,7 @@ static boolean isWithoutType(Object value, TypesLibrary types) {
@Specialization(guards = {"isWithoutType(value, types)"})
Type[] withoutType(
Object value,
+ boolean allTypes,
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types,
@Cached WithoutType delegate)
@@ -201,14 +207,15 @@ Type[] withoutType(
@Specialization(guards = {"isWithType(value, types, interop)"})
Type[] doType(
Object value,
+ boolean includeExtraTypes,
@Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop,
@Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) {
- return types.allTypes(value);
+ return types.allTypes(value, includeExtraTypes);
}
@Fallback
@CompilerDirectives.TruffleBoundary
- Type[] doAny(Object value) throws NonTypeResult {
+ Type[] doAny(Object value, boolean allTypes) throws NonTypeResult {
var err =
DataflowError.withDefaultTrace(
EnsoContext.get(this)
diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java
index eea5cd8a5231..f87c02aab94e 100644
--- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java
+++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java
@@ -74,9 +74,11 @@ public Type getType(Object receiver) {
* #getType(java.lang.Object)} and returns array with one element of that type
*
* @param receiver the typed object
+ * @param includeExtraTypes specify {@code false} to return only types value has already been
+ * case to, specify {@code true} to return all types value can be cast to
* @return the corresponding types for the {@code receiver}
*/
- public Type[] allTypes(Object receiver) {
+ public Type[] allTypes(Object receiver, boolean includeExtraTypes) {
var t = getType(receiver);
assert t != null : "null type for " + receiver;
return new Type[] {t};
diff --git a/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso b/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso
index 5c4774d693bd..4cee0a32e6b2 100644
--- a/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso
+++ b/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso
@@ -87,9 +87,13 @@ add_specs suite_builder =
(x:A).a . should_equal "a"
call_a obj:A = obj.a
+ call_b obj:B = obj.b
# A&X type has attribute a
call_a ax . should_equal "a"
+ # A&X can be converted to B
+ call_b ax . should_equal "b"
+ call_b (ax:X&A) . should_equal "b"
# according to "static typing" discussion at
# https://github.com/enso-org/enso/pull/11600#discussion_r1867584107
@@ -97,10 +101,35 @@ add_specs suite_builder =
Test.expect_panic Type_Error <|
call_a x . should_equal "a"
+ Test.expect_panic Type_Error <|
+ call_b x . should_equal "b"
+
+ call_b (x:X&A) . should_equal "b"
+
# multivalue ax restricted to X cannot be converted to B in to_b_to_c
Test.expect_panic Type_Error <|
to_b_to_c x
+ msg1 = case ax of
+ b:B -> "Not a "+b.to_text
+ a:A -> "==="+a.a
+ msg1 . should_equal "===a"
+
+ msg2 = case (ax:X&A) of
+ b:B -> "Not a "+b.to_text
+ a:A -> "==="+a.a
+ msg2 . should_equal "===a"
+
+ msg3 = case x of
+ b:B -> "Not a "+b.to_text
+ a:A -> "==="+a.a
+ msg3 . should_equal "===a"
+
+ msg4 = case x of
+ b:B -> "Not a "+b.to_text
+ a:A -> "A but also "+(a:X).to_text
+ msg4 . should_equal "A but also X"
+
group_builder.specify "Intersection type of unrelated types is not possible" <|
Test.expect_panic Type_Error <|
_ = X:X&B