diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/AnyToTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/AnyToTest.java new file mode 100644 index 000000000000..15713bbe7215 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/AnyToTest.java @@ -0,0 +1,77 @@ +package org.enso.interpreter.test; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import org.enso.interpreter.runtime.data.EnsoMultiValue; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.data.text.Text; +import org.enso.test.utils.ContextUtils; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Source; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AnyToTest { + private static Context ctx; + + private static final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + @BeforeClass + public static void initCtx() { + ctx = ContextUtils.createDefaultContext(out); + } + + @AfterClass + public static void disposeCtx() { + ctx.close(); + ctx = null; + } + + @Before + public void resetOutput() { + out.reset(); + } + + private String getStdOut() { + return out.toString(StandardCharsets.UTF_8); + } + + @Test + public void multiValueToInteger() throws Exception { + var ensoCtx = ContextUtils.leakContext(ctx); + var types = + new Type[] {ensoCtx.getBuiltins().number().getInteger(), ensoCtx.getBuiltins().text()}; + var code = + """ + from Standard.Base import all + + private eq a b = a == b + + conv style v = case style of + 0 -> v.to Integer + 1 -> v:Integer + 2 -> v.to Text + 3 -> v:Text + 99 -> eq + + """; + var conv = + ContextUtils.evalModule(ctx, Source.newBuilder("enso", code, "conv.enso").build(), "conv"); + var both = EnsoMultiValue.create(types, types.length, new Object[] {2L, Text.create("Two")}); + var eq = + ContextUtils.executeInContext( + ctx, + () -> { + var bothValue = ctx.asValue(both); + var asIntegerTo = conv.execute(0, bothValue); + var asIntegerCast = conv.execute(1, bothValue); + var equals = conv.execute(99, null); + return equals.execute(asIntegerTo, asIntegerCast); + }); + assertTrue("Any.to and : give the same result", eq.asBoolean()); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java index 2d0e4777fbad..dea870c6fed6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java @@ -100,7 +100,10 @@ private Type extractType(Object self) { return extractType(this, self); } - static boolean hasType(TypeOfNode typeOfNode, Object value) { + static boolean hasTypeNoMulti(TypeOfNode typeOfNode, Object value) { + if (value instanceof EnsoMultiValue) { + return false; + } return typeOfNode.hasType(value); } @@ -109,7 +112,11 @@ static boolean isDataflowError(Object value) { } @Specialization( - guards = {"hasType(dispatch, that)", "!isDataflowError(self)", "!isDataflowError(that)"}) + guards = { + "hasTypeNoMulti(dispatch, that)", + "!isDataflowError(self)", + "!isDataflowError(that)" + }) Object doConvertFrom( VirtualFrame frame, State state, @@ -265,7 +272,7 @@ Object doConvertText( @Specialization( guards = { - "!hasType(typeOfNode, that)", + "!hasTypeNoMulti(typeOfNode, that)", "!interop.isTime(that)", "interop.isDate(that)", }) @@ -287,7 +294,7 @@ Object doConvertDate( @Specialization( guards = { - "!hasType(typeOfNode, that)", + "!hasTypeNoMulti(typeOfNode, that)", "interop.isTime(that)", "!interop.isDate(that)", }) @@ -309,7 +316,7 @@ Object doConvertTime( @Specialization( guards = { - "!hasType(typeOfNode, that)", + "!hasTypeNoMulti(typeOfNode, that)", "interop.isTime(that)", "interop.isDate(that)", }) @@ -331,7 +338,7 @@ Object doConvertDateTime( @Specialization( guards = { - "!hasType(typeOfNode, that)", + "!hasTypeNoMulti(typeOfNode, that)", "interop.isDuration(that)", }) Object doConvertDuration( @@ -352,7 +359,7 @@ Object doConvertDuration( @Specialization( guards = { - "!hasType(typeOfNode, thatMap)", + "!hasTypeNoMulti(typeOfNode, thatMap)", "interop.hasHashEntries(thatMap)", }) Object doConvertMap( @@ -374,7 +381,7 @@ Object doConvertMap( return invokeFunctionNode.execute(function, frame, state, arguments); } - @Specialization(guards = {"!hasType(methods, that)", "!interop.isString(that)"}) + @Specialization(guards = {"!hasTypeNoMulti(methods, that)", "!interop.isString(that)"}) Object doFallback( VirtualFrame frame, State state, diff --git a/test/Base_Tests/src/Semantic/Conversion_Spec.enso b/test/Base_Tests/src/Semantic/Conversion_Spec.enso index 3be205435fb6..70c5c0d1db0d 100644 --- a/test/Base_Tests/src/Semantic/Conversion_Spec.enso +++ b/test/Base_Tests/src/Semantic/Conversion_Spec.enso @@ -341,7 +341,7 @@ add_specs suite_builder = x==x . should_be_true (x:Float)==42.3 . should_be_true (x:Fool)==42.3 . should_be_false - x==42.3 . should_be_true + x==42.3 . should_be_false 42.3==(x.to Float) . should_be_true 42.3==(x.to Fool) . should_be_false 42.3==x . should_be_true