diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/number/decimal/FloatTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/number/decimal/FloatTest.java new file mode 100644 index 000000000000..487ae5a451ea --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/number/decimal/FloatTest.java @@ -0,0 +1,120 @@ +package org.enso.interpreter.node.expression.builtin.number.decimal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.math.BigInteger; +import org.enso.interpreter.runtime.data.EnsoMultiValue; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.number.EnsoBigInteger; +import org.enso.interpreter.test.WrappedPrimitive; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.TestRootNode; +import org.graalvm.polyglot.Context; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** Tests Truffle nodes for integer operations. */ +public class FloatTest { + + private static AbsNode absNode; + private static AddNode addNode; + private static TestRootNode root; + private static Context ctx; + + @BeforeClass + public static void setup() { + ctx = ContextUtils.createDefaultContext(); + ContextUtils.executeInContext( + ctx, + () -> { + absNode = AbsNode.build(); + addNode = AddNode.build(); + + root = new TestRootNode(); + root.insertChildren(absNode, addNode); + return null; + }); + } + + @AfterClass + public static void teardown() { + ctx.close(); + ctx = null; + } + + private static final EnsoBigInteger bigInt = + new EnsoBigInteger(new BigInteger("1000000000000000000000000000000000000")); + private static final EnsoBigInteger bigIntNegative = + new EnsoBigInteger(new BigInteger("-1000000000000000000000000000000000000")); + + @Test + public void testAbs23() { + ContextUtils.executeInContext( + ctx, + () -> { + assertEquals(23.1, absNode.execute(23.1), 0.01); + assertEquals(23.1, absNode.execute(-23.1), 0.01); + return null; + }); + } + + @Test + public void testAdd21And1Point0() { + ContextUtils.executeInContext( + ctx, + () -> { + assertEquals(23.1, addNode.execute(22.0, 1.1), 0.01); + return null; + }); + } + + @Test + public void testAdd21And1() { + ContextUtils.executeInContext( + ctx, + () -> { + assertEquals(23.1, addNode.execute(22.1, 1L), 0.01); + return null; + }); + } + + @Test + public void testAddMulti21And1() { + ContextUtils.executeInContext( + ctx, + () -> { + var nn = EnsoMultiValue.NewNode.getUncached(); + var leak = ContextUtils.leakContext(ctx); + var floatType = leak.getBuiltins().number().getFloat(); + var textType = leak.getBuiltins().text(); + var both = new Type[] {floatType, textType}; + var twentyTwoHello = nn.newValue(both, 2, 0, new Object[] {22.1, "Hello"}); + assertEquals(23.2, addNode.execute(1.1, twentyTwoHello), 0.01); + return null; + }); + } + + @Test + public void testAddInterop21And1() { + ContextUtils.executeInContext( + ctx, + () -> { + var twentyOne = new WrappedPrimitive(21.1); + assertEquals(23.1, addNode.execute(2.0, twentyOne), 0.01); + return null; + }); + } + + @Test + public void testAddDoubleAndText() { + ContextUtils.executeInContext( + ctx, + () -> { + assertThrows(PanicException.class, () -> addNode.execute(23.1, "Hello")); + return null; + }); + } +} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/IntegerTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/number/integer/IntegerTest.java similarity index 52% rename from engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/IntegerTest.java rename to engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/number/integer/IntegerTest.java index e4e2d48c8c4f..47085e798d64 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/IntegerTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/number/integer/IntegerTest.java @@ -1,28 +1,28 @@ -package org.enso.interpreter.test; +package org.enso.interpreter.node.expression.builtin.number.integer; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import java.math.BigInteger; -import org.enso.interpreter.node.expression.builtin.number.integer.AbsNode; -import org.enso.interpreter.node.expression.builtin.number.integer.AddNode; +import org.enso.interpreter.runtime.data.EnsoMultiValue; +import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.number.EnsoBigInteger; +import org.enso.interpreter.test.WrappedPrimitive; import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.TestRootNode; import org.graalvm.polyglot.Context; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import org.junit.experimental.theories.Theories; -import org.junit.runner.RunWith; /** Tests Truffle nodes for integer operations. */ -@RunWith(Theories.class) public class IntegerTest { private static AbsNode absNode; private static AddNode addNode; + private static TestRootNode root; private static Context ctx; @BeforeClass @@ -33,6 +33,9 @@ public static void setup() { () -> { absNode = AbsNode.build(); addNode = AddNode.build(); + + root = new TestRootNode(); + root.insertChildren(absNode, addNode); return null; }); } @@ -49,15 +52,33 @@ public static void teardown() { new EnsoBigInteger(new BigInteger("-1000000000000000000000000000000000000")); @Test - public void testAbs() { + public void testAbs23() { ContextUtils.executeInContext( ctx, () -> { assertEquals(23L, absNode.execute(23L)); assertEquals(23L, absNode.execute(-23L)); + return null; + }); + } + + @Test + public void testAbsBig() { + ContextUtils.executeInContext( + ctx, + () -> { assertTrue(absNode.execute(Long.MIN_VALUE) instanceof EnsoBigInteger); assertEquals(bigInt, absNode.execute(bigInt)); assertEquals(bigInt, absNode.execute(bigIntNegative)); + return null; + }); + } + + @Test + public void testAbsPanic() { + ContextUtils.executeInContext( + ctx, + () -> { assertThrows( "Decimals are not supported", PanicException.class, () -> absNode.execute(23.0)); assertThrows( @@ -67,11 +88,57 @@ public void testAbs() { } @Test - public void testAdd() { + public void testAdd21And1() { ContextUtils.executeInContext( ctx, () -> { assertEquals(23L, addNode.execute(22L, 1L)); + return null; + }); + } + + @Test + public void testAdd21And1Point0() { + ContextUtils.executeInContext( + ctx, + () -> { + assertEquals(23.1, ((Number) addNode.execute(22L, 1.1)).doubleValue(), 0.01); + return null; + }); + } + + @Test + public void testAddMulti21And1() { + ContextUtils.executeInContext( + ctx, + () -> { + var nn = EnsoMultiValue.NewNode.getUncached(); + var leak = ContextUtils.leakContext(ctx); + var intType = leak.getBuiltins().number().getInteger(); + var textType = leak.getBuiltins().text(); + var both = new Type[] {intType, textType}; + var twentyTwoHello = nn.newValue(both, 2, 0, new Object[] {22L, "Hello"}); + assertEquals(23L, addNode.execute(twentyTwoHello, 1L)); + return null; + }); + } + + @Test + public void testAddInterop21And1() { + ContextUtils.executeInContext( + ctx, + () -> { + var twentyOne = new WrappedPrimitive(21L); + assertEquals(23L, addNode.execute(twentyOne, 2L)); + return null; + }); + } + + @Test + public void testAddLongAndText() { + ContextUtils.executeInContext( + ctx, + () -> { assertThrows(PanicException.class, () -> addNode.execute(23L, "Hello")); return null; }); diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/CaseOfTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/CaseOfTest.java new file mode 100644 index 000000000000..5ee7e9f610fe --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/CaseOfTest.java @@ -0,0 +1,72 @@ +package org.enso.interpreter.test; + +import static org.junit.Assert.assertEquals; + +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.data.EnsoMultiValue; +import org.enso.interpreter.runtime.data.Type; +import org.enso.test.utils.ContextUtils; +import org.graalvm.polyglot.Context; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class CaseOfTest { + private static Context ctx; + private static EnsoContext leak; + + @BeforeClass + public static void initCtx() throws Exception { + ctx = ContextUtils.createDefaultContext(); + leak = ContextUtils.leakContext(ctx); + } + + @AfterClass + public static void closeCtx() { + ctx.close(); + ctx = null; + leak = null; + } + + @Test + public void caseOfBoolean() { + doCaseOfBoolean(true, false); + } + + @Test + public void caseOfInteropBoolean() { + var t = new WrappedPrimitive(true); + var f = new WrappedPrimitive(false); + doCaseOfBoolean(t, f); + } + + @Test + public void caseOfMultiValueBoolean() { + var n = EnsoMultiValue.NewNode.getUncached(); + + var bAndT = + new Type[] {leak.getBuiltins().bool().getType(), leak.getBuiltins().number().getInteger()}; + var t = n.newValue(bAndT, 2, 0, new Object[] {true, 300}); + var f = n.newValue(bAndT, 2, 0, new Object[] {false, 200}); + doCaseOfBoolean(t, f); + } + + private void doCaseOfBoolean(Object t, Object f) { + var code = + """ + from Standard.Base import True, False + + choose v = case v of + True -> 1 + False -> 2 + _ -> 3 + """; + + var choose = ContextUtils.evalModule(ctx, code, "choose.enso", "choose"); + + var one = choose.execute(t); + assertEquals("With " + t + " we should get 1", 1, one.asInt()); + var two = choose.execute(f); + assertEquals("With " + f + " we should get 2", 2, two.asInt()); + } +} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/EnsoMultiValueTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/EnsoMultiValueTest.java new file mode 100644 index 000000000000..c921a55428af --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/EnsoMultiValueTest.java @@ -0,0 +1,170 @@ +package org.enso.interpreter.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.HashSet; +import java.util.List; +import org.enso.pkg.QualifiedName; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.ProjectUtils; +import org.enso.test.utils.SourceModule; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.TypeLiteral; +import org.junit.AfterClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class EnsoMultiValueTest { + private static Context ctx; + @Rule public final TemporaryFolder dir = new TemporaryFolder(); + + private static Context ctx() { + if (ctx == null) { + ctx = ContextUtils.defaultContextBuilder().build(); + } + return ctx; + } + + @AfterClass + public static void disposeCtx() throws Exception { + if (ctx != null) { + ctx.close(); + ctx = null; + } + } + + @Test + public void keepIdentityOfAandB() throws Exception { + var types = + """ + from project.PrivateConversion import all + type A + A_Ctor x + + id_a self -> A = self + type B + B_Ctor x + + ab = + a = A.A_Ctor 1 + (a : A & B) + """; + + var privateConversion = + """ + from project.Types import all + + B.from (that : A) = B.B_Ctor that + """; + + var main = + """ + from project.Types import all + + main = + v = ab.id_a + [v.to_text, (v:A).to_text, (v:B).to_text] + """; + + var prjDir = dir.newFolder(); + var sources = new HashSet(); + sources.add(new SourceModule(QualifiedName.fromString("Types"), types)); + sources.add(new SourceModule(QualifiedName.fromString("PrivateConversion"), privateConversion)); + sources.add(new SourceModule(QualifiedName.fromString("Main"), main)); + ProjectUtils.createProject("Keep_Id", sources, prjDir.toPath()); + + ProjectUtils.testProjectRun( + prjDir.toPath(), + (tripple) -> { + var texts = tripple.as(new TypeLiteral>() {}); + assertEquals(3, texts.size()); + assertStartsWith("(A_Ctor", texts.get(0)); + assertStartsWith("(A_Ctor", texts.get(1)); + assertStartsWith("(B_Ctor", texts.get(2)); + }); + } + + @Test + public void sameFieldAccessAandB() { + sameFieldAccess("A & B"); + } + + @Test + public void sameFieldAccessBandA() { + sameFieldAccess("B & A"); + } + + private void sameFieldAccess(String cast) { + var code = + """ + type A + A_Ctor x y + + x_from_a self = self.x + + type B + B_Ctor x y + + x_from_b self = self.x + + B.from (that : A) = B.B_Ctor "B" that.y + + pair = + a = A.A_Ctor "A" 1 + both = (a : $cast) + + v_a = both.x_from_a + v_b = both.x_from_b + [v_a, v_b] + """ + .replace("$cast", cast); + + var pair = ContextUtils.evalModule(ctx(), code, "fields.enso", "pair"); + var texts = pair.as(new TypeLiteral>() {}); + assertEquals(2, texts.size()); + assertEquals("A", texts.get(0)); + assertEquals("B", texts.get(1)); + } + + @Test + public void trippleCastConfusion() { + var code = + """ + type A + A_Ctor x + type B + B_Ctor x + type C + C_Ctor x + + B.from (that : A) = B.B_Ctor that + C.from (that:B) = C.C_Ctor that + + texts = + a = A.A_Ctor 1 + ab = (a : A & B) + abc = ab:(A & B & C) + c = abc:C + + text_a = (c:A).to_text + text_b = (c:B).to_text + [text_a, text_b, c.to_text] + """; + + var tripple = ContextUtils.evalModule(ctx(), code, "tripple.enso", "texts"); + var texts = tripple.as(new TypeLiteral>() {}); + assertEquals(3, texts.size()); + assertStartsWith("(A_Ctor", texts.get(0)); + assertStartsWith("(B_Ctor", texts.get(1)); + assertStartsWith("(C_Ctor", texts.get(2)); + } + + private static void assertStartsWith(String exp, String actual) { + if (actual.startsWith(exp)) { + return; + } + fail("Expecting " + exp + " in " + actual); + } +} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WrappedPrimitive.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WrappedPrimitive.java index b141b7db2d63..b6f6b8ddeec6 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WrappedPrimitive.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/WrappedPrimitive.java @@ -8,28 +8,32 @@ import com.oracle.truffle.api.library.ExportMessage; import java.math.BigInteger; +/** + * A {@link TruffleObject} that wraps a primitive value. Useful for testing behavior of various node + * specializations when they received a foreign object. + */ @ExportLibrary(InteropLibrary.class) -final class WrappedPrimitive implements TruffleObject { +public final class WrappedPrimitive implements TruffleObject { private final Object value; - WrappedPrimitive(long value) { + public WrappedPrimitive(long value) { this.value = value; } - WrappedPrimitive(boolean value) { + public WrappedPrimitive(boolean value) { this.value = value; } - WrappedPrimitive(double value) { + public WrappedPrimitive(double value) { this.value = value; } - WrappedPrimitive(BigInteger value) { + public WrappedPrimitive(BigInteger value) { this.value = value; } - WrappedPrimitive(String value) { + public WrappedPrimitive(String value) { this.value = value; } @@ -133,7 +137,7 @@ String toDisplayString(boolean ignore) { return toString(); } - Object asDirect() { + public final Object asDirect() { return value; } 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 718b3fce27af..8f80726e7e80 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 @@ -302,7 +302,7 @@ Object doMultiValue( if (fnAndType != null) { var ctx = EnsoContext.get(this); if (ctx.getBuiltins().any() != fnAndType.getRight()) { - var unwrapSelf = castTo.findTypeOrNull(fnAndType.getRight(), self, false, false); + var unwrapSelf = castTo.findTypeOrNull(fnAndType.getRight(), self, true, false); if (unwrapSelf != null) { assert arguments[0] == self; arguments[0] = unwrapSelf; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/BooleanBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/BooleanBranchNode.java index ad82c644d3d3..2cb67c34476e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/BooleanBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/BooleanBranchNode.java @@ -4,8 +4,12 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.profiles.CountingConditionProfile; +import org.enso.interpreter.runtime.EnsoContext; /** An implementation of the case expression specialised to working on booleans. */ @NodeInfo(shortName = "BooleanMatch") @@ -31,12 +35,31 @@ public static BooleanBranchNode build( } @Specialization - void doAtom(VirtualFrame frame, Object state, boolean target) { + void doBoolean(VirtualFrame frame, Object state, boolean target) { if (profile.profile(matched == target)) { accept(frame, state, new Object[0]); } } + @Specialization( + guards = {"iop.isBoolean(target)"}, + limit = "3") + void doInterop( + VirtualFrame frame, + Object state, + Object target, + @CachedLibrary("target") InteropLibrary iop) { + try { + var value = iop.asBoolean(target); + if (profile.profile(matched == value)) { + accept(frame, state, new Object[0]); + } + } catch (UnsupportedMessageException ex) { + var ctx = EnsoContext.get(this); + throw ctx.raiseAssertionPanic(this, null, ex); + } + } + @Fallback void doFallback(VirtualFrame frame, Object state, Object target) {} } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/AbsNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/AbsNode.java index 16e3e4330051..9f35aa72ab97 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/AbsNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/AbsNode.java @@ -3,7 +3,13 @@ import org.enso.interpreter.dsl.BuiltinMethod; @BuiltinMethod(type = "Float", name = "abs", description = "Absolute value of a number.") -public class AbsNode extends FloatNode { +public final class AbsNode extends FloatNode { + private AbsNode() {} + + static AbsNode build() { + return new AbsNode(); + } + double execute(double own) { return Math.abs(own); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AbsNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AbsNode.java index 63a2fe0fdf88..814dc0f37bf4 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AbsNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AbsNode.java @@ -7,13 +7,14 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "abs", description = "Absolute value of a number") -public abstract class AbsNode extends IntegerNode { +public abstract class AbsNode extends IntegerNode.Unary { public static AbsNode build() { return AbsNodeGen.create(); } - public abstract Object execute(Object own); + @Override + abstract Object executeUnary(Object own); @Specialization(rewriteOn = ArithmeticException.class) long doLong(long self) { @@ -26,12 +27,12 @@ long doLong(long self) { @Specialization(replaces = "doLong") Object doLongOverflow(long self) { - return toEnsoNumberNode.execute(BigIntegerOps.abs(self)); + return toEnsoNumberNode().execute(BigIntegerOps.abs(self)); } @Specialization Object doBigInt(EnsoBigInteger self) { - return toEnsoNumberNode.execute(BigIntegerOps.abs(self.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.abs(self.getValue())); } @Fallback diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AddNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AddNode.java index 48e33b55ed4a..7ca2b3b0dd84 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AddNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/AddNode.java @@ -1,20 +1,17 @@ package org.enso.interpreter.node.expression.builtin.number.integer; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "+", description = "Addition of numbers.") -public abstract class AddNode extends IntegerNode { +public abstract class AddNode extends IntegerNode.Binary { - public abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); public static AddNode build() { return AddNodeGen.create(); @@ -27,7 +24,7 @@ long doLong(long self, long that) { @Specialization(replaces = "doLong") Object doOverflow(long self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.add(self, that)); + return toEnsoNumberNode().execute(BigIntegerOps.add(self, that)); } @Specialization @@ -38,17 +35,17 @@ Object doDouble(long self, double that) { @TruffleBoundary @Specialization Object doBigIntegers(EnsoBigInteger self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(self.asBigInteger().add(that.asBigInteger())); + return toEnsoNumberNode().execute(self.asBigInteger().add(that.asBigInteger())); } @Specialization Object doLongBigInteger(long self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.add(that.getValue(), self)); + return toEnsoNumberNode().execute(BigIntegerOps.add(that.getValue(), self)); } @Specialization Object doBigIntegerLong(EnsoBigInteger self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.add(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.add(self.getValue(), that)); } @Specialization @@ -57,15 +54,6 @@ Object doBigIntegerLong(EnsoBigInteger self, long that) { return self.getValue().doubleValue() + that; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached AddNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitAndNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitAndNode.java index 1e0fd34e28fe..f0b4cc4f8ffa 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitAndNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitAndNode.java @@ -1,19 +1,16 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "bit_and", description = "Bitwise and.") -public abstract class BitAndNode extends IntegerNode { +public abstract class BitAndNode extends IntegerNode.Binary { - public abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); public static BitAndNode build() { return BitAndNodeGen.create(); @@ -26,26 +23,17 @@ long doLongLong(long self, long that) { @Specialization Object doLongBigInt(long self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitAnd(self, that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.bitAnd(self, that.getValue())); } @Specialization Object doBigIntLong(EnsoBigInteger self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitAnd(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.bitAnd(self.getValue(), that)); } @Specialization Object doBigIntBigInt(EnsoBigInteger self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitAnd(self.getValue(), that.getValue())); - } - - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached BitAndNode delegate) { - return super.doInterop(self, that, iop, delegate); + return toEnsoNumberNode().execute(BigIntegerOps.bitAnd(self.getValue(), that.getValue())); } @Fallback diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitNotNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitNotNode.java index c87c6696d022..3b58c8b4206d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitNotNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitNotNode.java @@ -7,8 +7,9 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "bit_not", description = "Bitwise negation.") -public abstract class BitNotNode extends IntegerNode { - abstract Object execute(Object own); +public abstract class BitNotNode extends IntegerNode.Unary { + @Override + abstract Object executeUnary(Object own); static BitNotNode build() { return BitNotNodeGen.create(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitOrNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitOrNode.java index 466246cb0e45..9f12f1573640 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitOrNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitOrNode.java @@ -1,19 +1,16 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "bit_or", description = "Bitwise or.") -public abstract class BitOrNode extends IntegerNode { +public abstract class BitOrNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static BitOrNode build() { return BitOrNodeGen.create(); @@ -26,26 +23,17 @@ long doLong(long self, long that) { @Specialization Object doBigInteger(long self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitOr(self, that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.bitOr(self, that.getValue())); } @Specialization Object doLong(EnsoBigInteger self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitOr(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.bitOr(self.getValue(), that)); } @Specialization Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitOr(self.getValue(), that.getValue())); - } - - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached BitOrNode delegate) { - return super.doInterop(self, that, iop, delegate); + return toEnsoNumberNode().execute(BigIntegerOps.bitOr(self.getValue(), that.getValue())); } @Fallback diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftNode.java index cb954b73455e..66bb21009c0f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftNode.java @@ -6,9 +6,6 @@ import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.profiles.CountingConditionProfile; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; @@ -18,7 +15,7 @@ @ImportStatic(BigIntegerOps.class) @BuiltinMethod(type = "Integer", name = "bit_shift", description = "Bitwise shift.") -public abstract class BitShiftNode extends IntegerNode { +public abstract class BitShiftNode extends IntegerNode.Binary { private final CountingConditionProfile canShiftLeftInLongProfile = CountingConditionProfile.create(); @@ -27,7 +24,8 @@ public abstract class BitShiftNode extends IntegerNode { private final CountingConditionProfile rightShiftExceedsLongWidth = CountingConditionProfile.create(); - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); @NeverDefault static BitShiftNode build() { @@ -44,7 +42,7 @@ Object doLongShiftLeftExplicit(long self, long that) { if (canShiftLeftInLongProfile.profile(canShiftLeftInLong(self, that))) { return doLongShiftLeft(self, that); } else if (positiveFitsInInt.profile(BigIntegerOps.fitsInInt(that))) { - return toEnsoNumberNode.execute(BigIntegerOps.bitShiftLeft(self, (int) that)); + return toEnsoNumberNode().execute(BigIntegerOps.bitShiftLeft(self, (int) that)); } else { return DataflowError.withDefaultTrace( EnsoContext.get(this).getBuiltins().error().getShiftAmountTooLargeError(), this); @@ -100,7 +98,7 @@ Object doBigIntShiftLeftExplicit( @Specialization(guards = {"that < 0", "fitsInInt(that)"}) Object doBigIntShiftRight(EnsoBigInteger self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitShiftRight(self.getValue(), (int) -that)); + return toEnsoNumberNode().execute(BigIntegerOps.bitShiftRight(self.getValue(), (int) -that)); } @Specialization(guards = "that < 0", replaces = "doBigIntShiftRight") @@ -125,15 +123,6 @@ Object doBigIntThat(EnsoBigInteger self, EnsoBigInteger that) { } } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached BitShiftNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftRightNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftRightNode.java index 5fadab01e0d1..a0ac9aa8ec29 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftRightNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitShiftRightNode.java @@ -4,16 +4,15 @@ import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "bit_shift_r", description = "Bitwise right-shift.") -public abstract class BitShiftRightNode extends IntegerNode { - abstract Object execute(Object own, Object that); +public abstract class BitShiftRightNode extends IntegerNode.Binary { + + @Override + abstract Object executeBinary(Object own, Object that); static BitShiftRightNode build() { return BitShiftRightNodeGen.create(); @@ -49,15 +48,6 @@ Object doBigInteger( return bitShiftNode.execute(self, new EnsoBigInteger(BigIntegerOps.negate(that.getValue()))); } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached BitShiftRightNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitXorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitXorNode.java index c5be99950c11..919b22305188 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitXorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/BitXorNode.java @@ -1,19 +1,16 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "bit_xor", description = "Bitwise exclusive or.") -public abstract class BitXorNode extends IntegerNode { +public abstract class BitXorNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static BitXorNode build() { return BitXorNodeGen.create(); @@ -26,26 +23,17 @@ long doLong(long self, long that) { @Specialization Object doBigInteger(long self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitXor(self, that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.bitXor(self, that.getValue())); } @Specialization Object doLong(EnsoBigInteger self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitXor(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.bitXor(self.getValue(), that)); } @Specialization Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.bitXor(self.getValue(), that.getValue())); - } - - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached BitXorNode delegate) { - return super.doInterop(self, that, iop, delegate); + return toEnsoNumberNode().execute(BigIntegerOps.bitXor(self.getValue(), that.getValue())); } @Fallback diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/CeilNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/CeilNode.java index 982d9e28d429..e034a3cf6cde 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/CeilNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/CeilNode.java @@ -6,8 +6,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "ceil", description = "Small integer ceiling.") -public abstract class CeilNode extends IntegerNode { - abstract Object execute(Object own); +public abstract class CeilNode extends IntegerNode.Unary { + + @Override + abstract Object executeUnary(Object own); public static CeilNode build() { return CeilNodeGen.create(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivNode.java index a67a26ab03bb..b3f57dd9bf4a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivNode.java @@ -1,11 +1,7 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; @@ -13,9 +9,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "div", description = "Division of numbers.") -public abstract class DivNode extends IntegerNode { +public abstract class DivNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static DivNode build() { return DivNodeGen.create(); @@ -40,7 +37,7 @@ Object doBigInteger(long self, EnsoBigInteger that) { @Specialization Object doLong(EnsoBigInteger self, long that) { try { - return toEnsoNumberNode.execute(BigIntegerOps.divide(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.divide(self.getValue(), that)); } catch (ArithmeticException e) { return DataflowError.withDefaultTrace( EnsoContext.get(this).getBuiltins().error().getDivideByZeroError(), this); @@ -50,16 +47,7 @@ Object doLong(EnsoBigInteger self, long that) { @Specialization Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { // No need to trap, as 0 is never represented as an EnsoBigInteger. - return toEnsoNumberNode.execute(BigIntegerOps.divide(self.getValue(), that.getValue())); - } - - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached DivNode delegate) { - return super.doInterop(self, that, iop, delegate); + return toEnsoNumberNode().execute(BigIntegerOps.divide(self.getValue(), that.getValue())); } @Fallback diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivideNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivideNode.java index a1ba4eaf3ad3..cf158d2439bd 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivideNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/DivideNode.java @@ -1,19 +1,15 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "/", description = "Division of numbers.") -public abstract class DivideNode extends IntegerNode { +public abstract class DivideNode extends IntegerNode.Binary { @Override - abstract Object execute(Object own, Object that); + abstract Object executeBinary(Object own, Object that); static DivideNode build() { return DivideNodeGen.create(); @@ -49,15 +45,6 @@ static DivideNode build() { return BigIntegerOps.toDouble(self.getValue()) / that; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached DivideNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/FloorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/FloorNode.java index a68d874b4c5d..2cf819a7180c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/FloorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/FloorNode.java @@ -6,9 +6,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "floor", description = "Small integer floor.") -public abstract class FloorNode extends IntegerNode { +public abstract class FloorNode extends IntegerNode.Unary { - public abstract Object execute(Object own); + @Override + abstract Object executeUnary(Object own); public static FloorNode build() { return FloorNodeGen.create(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterNode.java index 1fbc3bfc5336..af14719ed1bc 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterNode.java @@ -1,11 +1,7 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; @@ -13,9 +9,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = ">", description = "Comparison of numbers.") -public abstract class GreaterNode extends IntegerNode { +public abstract class GreaterNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static GreaterNode build() { return GreaterNodeGen.create(); @@ -51,15 +48,6 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { return BigIntegerOps.compare(self.getValue(), that.getValue()) > 0; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached GreaterNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterOrEqualNode.java index e4afeb78c0f4..1d494ab29be6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/GreaterOrEqualNode.java @@ -1,11 +1,7 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; @@ -13,9 +9,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = ">=", description = "Comparison of numbers.") -public abstract class GreaterOrEqualNode extends IntegerNode { +public abstract class GreaterOrEqualNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static GreaterOrEqualNode build() { return GreaterOrEqualNodeGen.create(); @@ -51,15 +48,6 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { return BigIntegerOps.compare(self.getValue(), that.getValue()) >= 0; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached GreaterOrEqualNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/IntegerNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/IntegerNode.java index a5d257ca5faf..af4fb17cfac0 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/IntegerNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/IntegerNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.number.integer; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; @@ -11,10 +12,15 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.number.EnsoBigInteger; -abstract class IntegerNode extends Node { - @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.create(); +/** A base class for nodes that operate on Enso Integers (e.g. either {@code long} or {@link EnsoBigInteger}). Contains helper + * methods that can be used from subclasses of either {@link Unary} or {@link Binary} + * variant of this class. + */ +public sealed abstract class IntegerNode extends Node permits IntegerNode.Unary, IntegerNode.Binary { + @Child private ToEnsoNumberNode toEnsoNumberNode; + @Child private InteropLibrary iop; - IntegerNode() {} + private IntegerNode() {} @TruffleBoundary final PanicException throwTypeErrorIfNotInt(Object self, Object that) { @@ -35,33 +41,78 @@ final PanicException throwTypeErrorIfNotInt(Object self) { return new PanicException(builtins.error().makeTypeError(intType, self, "self"), this); } - final boolean isForeignNumber(InteropLibrary iop, TruffleObject obj) { + final Object toEnsoNumberOrNull(Object obj) { + return toEnsoNumberOrNull(obj, true); + } + + final Object toEnsoNumberOrNull(Object obj, boolean acceptDouble) { + if (obj instanceof Long) { + return obj; + } if (obj instanceof EnsoBigInteger) { - return false; + return obj; + } + if (acceptDouble && obj instanceof Double) { + return obj; } - return iop.isNumber(obj); + if (obj instanceof TruffleObject) { + try { + if (iop == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + iop = insert(InteropLibrary.getFactory().createDispatched(3)); + } + if (iop.isNumber(obj)) { + if (iop.fitsInLong(obj)) { + return iop.asLong(obj); + } else if (acceptDouble && iop.fitsInDouble(obj)) { + return iop.asDouble(obj); + } else if (iop.fitsInBigInteger(obj)) { + return toEnsoNumberNode().execute(iop.asBigInteger(obj)); + } + } + } catch (UnsupportedMessageException ex) { + // no conversion + } + } + return null; } - final Object doInterop( - Object self, TruffleObject that, InteropLibrary iop, IntegerNode delegate) { - try { - if (iop.fitsInLong(that)) { - return delegate.execute(self, iop.asLong(that)); - } else if (iop.fitsInDouble(that)) { - return delegate.execute(self, iop.asDouble(that)); - } else if (iop.fitsInBigInteger(that)) { - return delegate.execute(self, toEnsoNumberNode.execute(iop.asBigInteger(that))); - } - } catch (UnsupportedMessageException ex) { + final ToEnsoNumberNode toEnsoNumberNode() { + if (toEnsoNumberNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + toEnsoNumberNode = insert(ToEnsoNumberNode.create()); } - return doOther(self, that); + return toEnsoNumberNode; } - Object execute(Object own, Object that) { - throw new AbstractMethodError(); + /** + * Node operating on a single Enso Integer (e.g. either {@code long} or {@link EnsoBigInteger}) argument. + */ + public non-sealed abstract static class Unary extends IntegerNode { + public final Object execute(Object own) { + var ensoSelf = toEnsoNumberOrNull(own, false); + if (ensoSelf == null) { + throw throwTypeErrorIfNotInt(own); + } + return executeUnary(ensoSelf); + } + + abstract Object executeUnary(Object self); } - Object doOther(Object self, Object that) { - throw new AbstractMethodError(); + /** + * Node operating on a tow Enso Integers (e.g. either {@code long} or {@link EnsoBigInteger}) arguments. + */ + public non-sealed abstract static class Binary extends IntegerNode { + public final Object execute(Object own, Object that) { + var ensoSelf = toEnsoNumberOrNull(own, false); + var ensoThat = toEnsoNumberOrNull(that); + if (ensoSelf == null || ensoThat == null) { + throw throwTypeErrorIfNotInt(own, that); + } + return executeBinary(ensoSelf, ensoThat); + } + + abstract Object executeBinary(Object self, Object that); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessNode.java index e4698d6645e4..3c1390ac2cda 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessNode.java @@ -1,11 +1,7 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; @@ -13,9 +9,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "<", description = "Comparison of numbers.") -public abstract class LessNode extends IntegerNode { +public abstract class LessNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static LessNode build() { return LessNodeGen.create(); @@ -51,15 +48,6 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { return BigIntegerOps.compare(self.getValue(), that.getValue()) < 0; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached LessNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessOrEqualNode.java index 0c25add201ea..1dc599f24e2e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/LessOrEqualNode.java @@ -1,11 +1,7 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; @@ -13,10 +9,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "<=", description = "Comparison of numbers.") -public abstract class LessOrEqualNode extends IntegerNode { +public abstract class LessOrEqualNode extends IntegerNode.Binary { @Override - abstract Object execute(Object own, Object that); + abstract Object executeBinary(Object own, Object that); static LessOrEqualNode build() { return LessOrEqualNodeGen.create(); @@ -52,15 +48,6 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { return BigIntegerOps.compare(self.getValue(), that.getValue()) <= 0; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached LessOrEqualNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ModNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ModNode.java index 0439dee64b69..b14d1dca12e3 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ModNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ModNode.java @@ -1,12 +1,8 @@ package org.enso.interpreter.node.expression.builtin.number.integer; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import java.math.BigInteger; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; @@ -15,9 +11,10 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "%", description = "Modulo division of numbers.") -public abstract class ModNode extends IntegerNode { +public abstract class ModNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static ModNode build() { return ModNodeGen.create(); @@ -45,7 +42,7 @@ Object doLong(long self, long that) { Object doBigInteger(long self, EnsoBigInteger that) { var selfBigInt = BigInteger.valueOf(self); try { - return toEnsoNumberNode.execute(BigIntegerOps.modulo(selfBigInt, that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.modulo(selfBigInt, that.getValue())); } catch (ArithmeticException e) { return DataflowError.withDefaultTrace( EnsoContext.get(this).getBuiltins().error().getDivideByZeroError(), this); @@ -55,7 +52,7 @@ Object doBigInteger(long self, EnsoBigInteger that) { @Specialization Object doLong(EnsoBigInteger self, long that) { try { - return toEnsoNumberNode.execute(BigIntegerOps.modulo(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.modulo(self.getValue(), that)); } catch (ArithmeticException e) { return DataflowError.withDefaultTrace( EnsoContext.get(this).getBuiltins().error().getDivideByZeroError(), this); @@ -72,22 +69,13 @@ Object doLong(EnsoBigInteger self, long that) { @Specialization Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { try { - return toEnsoNumberNode.execute(BigIntegerOps.modulo(self.getValue(), that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.modulo(self.getValue(), that.getValue())); } catch (ArithmeticException e) { return DataflowError.withDefaultTrace( EnsoContext.get(this).getBuiltins().error().getDivideByZeroError(), this); } } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached ModNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/MultiplyNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/MultiplyNode.java index ec1c28f0c99c..6babf6a80efa 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/MultiplyNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/MultiplyNode.java @@ -1,19 +1,16 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "*", description = "Multiplication of numbers.") -public abstract class MultiplyNode extends IntegerNode { +public abstract class MultiplyNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static MultiplyNode build() { return MultiplyNodeGen.create(); @@ -26,7 +23,7 @@ long doLong(long self, long that) { @Specialization Object doOverflow(long self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.multiply(self, that)); + return toEnsoNumberNode().execute(BigIntegerOps.multiply(self, that)); } @Specialization @@ -36,17 +33,17 @@ Object doOverflow(long self, long that) { @Specialization Object doBigInteger(long self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.multiply(that.getValue(), self)); + return toEnsoNumberNode().execute(BigIntegerOps.multiply(that.getValue(), self)); } @Specialization Object doLong(EnsoBigInteger self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.multiply(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.multiply(self.getValue(), that)); } @Specialization Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.multiply(self.getValue(), that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.multiply(self.getValue(), that.getValue())); } @Specialization @@ -54,15 +51,6 @@ Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { return BigIntegerOps.toDouble(self.getValue()) * that; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached MultiplyNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/NegateNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/NegateNode.java index 7efaa3165184..64e5f52ed3e8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/NegateNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/NegateNode.java @@ -7,14 +7,15 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "negate", description = "Negation for numbers.") -public abstract class NegateNode extends IntegerNode { +public abstract class NegateNode extends IntegerNode.Unary { + + @Override + abstract Object executeUnary(Object own); static NegateNode build() { return NegateNodeGen.create(); } - abstract Object execute(Object own); - @Specialization(rewriteOn = ArithmeticException.class) long doNormal(long self) { return Math.negateExact(self); @@ -22,12 +23,12 @@ long doNormal(long self) { @Specialization Object doBigInt(EnsoBigInteger self) { - return toEnsoNumberNode.execute(BigIntegerOps.negate(self.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.negate(self.getValue())); } @Specialization Object doOverflow(long self) { - return toEnsoNumberNode.execute(BigIntegerOps.negate(self)); + return toEnsoNumberNode().execute(BigIntegerOps.negate(self)); } @Fallback diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ParseIntegerNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ParseIntegerNode.java index 8e7d70c63fd5..ff0c5ccca852 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ParseIntegerNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ParseIntegerNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.number.integer; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.profiles.BranchProfile; import java.math.BigInteger; @@ -16,7 +17,7 @@ description = """ Parse integer number""", autoRegister = false) -public final class ParseIntegerNode extends IntegerNode { +public final class ParseIntegerNode extends Node { @Child ToJavaStringNode toJavaString = ToJavaStringNode.build(); private final BranchProfile noEx1 = BranchProfile.create(); private final BranchProfile noEx2 = BranchProfile.create(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java index 22c8e52e5e27..5e98a2db7766 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/PowNode.java @@ -1,12 +1,8 @@ package org.enso.interpreter.node.expression.builtin.number.integer; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node.Child; import java.math.BigInteger; import org.enso.interpreter.dsl.BuiltinMethod; @@ -14,11 +10,12 @@ import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "^", description = "Exponentiation of numbers.") -public abstract class PowNode extends IntegerNode { +public abstract class PowNode extends IntegerNode.Binary { private @Child MultiplyNode multiplyNode = MultiplyNode.build(); - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static PowNode build() { return PowNodeGen.create(); @@ -68,7 +65,7 @@ Object doLong(EnsoBigInteger self, long that) { if (that == 0) { return 1L; } else if (that > 0) { - return toEnsoNumberNode.execute(BigIntegerOps.pow(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.pow(self.getValue(), that)); } else { return Math.pow(BigIntegerOps.toDouble(self.getValue()), that); } @@ -97,15 +94,6 @@ private static EnsoBigInteger toBigInteger(long self) { return new EnsoBigInteger(BigInteger.valueOf(self)); } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached PowNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/RoundNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/RoundNode.java index 0b0fa0d5fd89..4fe45a02bbf8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/RoundNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/RoundNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.number.integer; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.CountingConditionProfile; import com.oracle.truffle.api.profiles.PrimitiveValueProfile; @@ -10,7 +11,7 @@ type = "Integer", name = "round", description = "Decimal ceiling, converting to a small or big integer depending on size.") -public class RoundNode extends IntegerNode { +public final class RoundNode extends Node { private final CountingConditionProfile fitsProfile = CountingConditionProfile.create(); private final PrimitiveValueProfile constantPlacesDecimalPlaces = PrimitiveValueProfile.create(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/SubtractNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/SubtractNode.java index 883273fca567..e631e537d4b8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/SubtractNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/SubtractNode.java @@ -1,19 +1,16 @@ package org.enso.interpreter.node.expression.builtin.number.integer; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.library.CachedLibrary; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.number.EnsoBigInteger; @BuiltinMethod(type = "Integer", name = "-", description = "Subtraction of numbers.") -public abstract class SubtractNode extends IntegerNode { +public abstract class SubtractNode extends IntegerNode.Binary { - abstract Object execute(Object own, Object that); + @Override + abstract Object executeBinary(Object own, Object that); static SubtractNode build() { return SubtractNodeGen.create(); @@ -26,7 +23,7 @@ long doLong(long self, long that) { @Specialization Object doOverflow(long self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.subtract(self, that)); + return toEnsoNumberNode().execute(BigIntegerOps.subtract(self, that)); } @Specialization @@ -36,17 +33,17 @@ Object doOverflow(long self, long that) { @Specialization Object doBigInteger(long self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.subtract(self, that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.subtract(self, that.getValue())); } @Specialization Object doLong(EnsoBigInteger self, long that) { - return toEnsoNumberNode.execute(BigIntegerOps.subtract(self.getValue(), that)); + return toEnsoNumberNode().execute(BigIntegerOps.subtract(self.getValue(), that)); } @Specialization Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return toEnsoNumberNode.execute(BigIntegerOps.subtract(self.getValue(), that.getValue())); + return toEnsoNumberNode().execute(BigIntegerOps.subtract(self.getValue(), that.getValue())); } @Specialization @@ -54,15 +51,6 @@ Object doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { return BigIntegerOps.toDouble(self.getValue()) - that; } - @Specialization(guards = "isForeignNumber(iop, that)") - Object doInterop( - Object self, - TruffleObject that, - @CachedLibrary(limit = "3") InteropLibrary iop, - @Cached SubtractNode delegate) { - return super.doInterop(self, that, iop, delegate); - } - @Fallback Object doOther(Object self, Object that) { throw throwTypeErrorIfNotInt(self, that); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ToFloatNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ToFloatNode.java index 913fd264d63d..b545cd86a5a0 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ToFloatNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/integer/ToFloatNode.java @@ -10,8 +10,10 @@ type = "Integer", name = "to_float", description = "Conversion of integers to floats.") -public abstract class ToFloatNode extends IntegerNode { - public abstract Object execute(Object own); +public abstract class ToFloatNode extends IntegerNode.Unary { + + @Override + abstract Object executeUnary(Object own); public static ToFloatNode build() { return ToFloatNodeGen.create(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiType.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiType.java index 84a82b3ba794..fea007911f15 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiType.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiType.java @@ -192,9 +192,9 @@ Type[] slowlyComputeTypes(EnsoMultiType self, EnsoMultiType nextOrNull, int move System.arraycopy(next.types, 0, concat, self.types.length, next.types.length); } if (moveToFirst != 0) { - var first = concat[0]; - concat[0] = concat[moveToFirst]; - concat[moveToFirst] = first; + var toFirst = concat[moveToFirst]; + System.arraycopy(concat, 0, concat, 1, moveToFirst); + concat[0] = toFirst; } return concat; } 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 861e32d26a6c..fea5d49d9fbf 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 @@ -3,6 +3,7 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateUncached; @@ -33,12 +34,14 @@ import org.enso.interpreter.runtime.callable.UnresolvedSymbol; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.EnsoMultiType.AllTypesWith; +import org.enso.interpreter.runtime.data.atom.StructsLibrary; import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.graalvm.collections.Pair; @ExportLibrary(TypesLibrary.class) @ExportLibrary(InteropLibrary.class) +@ExportLibrary(value = StructsLibrary.class) public final class EnsoMultiValue extends EnsoObject { private final EnsoMultiType dispatch; private final EnsoMultiType extra; @@ -55,6 +58,10 @@ private EnsoMultiValue( this.values = values; } + final Object firstDispatchValue() { + return values[firstDispatch]; + } + /** Creates new instance of EnsoMultiValue from provided information. */ @GenerateUncached public abstract static class NewNode extends Node { @@ -502,6 +509,27 @@ Object invokeMember( throw UnknownIdentifierException.create(name); } + @ExportMessage + final boolean isStruct(@Shared("structs") @CachedLibrary(limit = "3") StructsLibrary delegate) { + // assumes the structure has been castTo with reorderOnly + // before method dispatch in InvokeMethodNode + return delegate.isStruct(values[firstDispatch]); + } + + @ExportMessage + final Object getField( + int index, @Shared("structs") @CachedLibrary(limit = "3") StructsLibrary delegate) { + // assumes the structure has been castTo with reorderOnly + // before method dispatch in InvokeMethodNode + return delegate.getField(values[firstDispatch], index); + } + + @ExportMessage + final void setField(int index, Object value, @Bind("$node") Node here) { + var ctx = EnsoContext.get(here); + throw ctx.raiseAssertionPanic(here, "Field assignment isn't supported", null); + } + @TruffleBoundary @Override public String toString() { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java index e80ba6a86dad..c2f66e776116 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldNode.java @@ -33,10 +33,10 @@ final class GetFieldNode extends GetFieldBaseNode { * @param frame current execution frame * @return the field value at predefined index */ + @Override public Object execute(VirtualFrame frame) { - // this is safe, as only Atoms will ever get here through method dispatch. - Atom atom = (Atom) Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[0]; - return structs.getField(atom, index); + var obj = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[0]; + return structs.getField(obj, index); } @Override diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldWithMatchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldWithMatchNode.java index 1fad5bd73d18..5972573b7df7 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldWithMatchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/atom/GetFieldWithMatchNode.java @@ -5,8 +5,11 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.NodeInfo; import org.enso.interpreter.EnsoLanguage; +import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.callable.function.Function; +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.interpreter.runtime.scope.ModuleScope; @NodeInfo( @@ -33,10 +36,45 @@ public GetFieldWithMatchNode( } } - @ExplodeLoop + @Override public Object execute(VirtualFrame frame) { // this is safe, as only Atoms will ever get here through method dispatch. - Atom atom = (Atom) Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[0]; + var self = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[0]; + if (self instanceof Atom atom) { + var value = searchAtom(atom); + if (value != null) { + return value; + } else { + throw noSuchFieldPanic(atom); + } + } else if (self instanceof EnsoMultiValue emv) { + CompilerDirectives.transferToInterpreter(); + var types = TypeOfNode.getUncached().findAllTypesOrNull(self, false); + if (types != null) { + Atom firstAtom = null; + for (var t : types) { + var v = EnsoMultiValue.CastToNode.getUncached().findTypeOrNull(t, emv, false, false); + if (v instanceof Atom atom) { + if (firstAtom == null) { + firstAtom = atom; + } + var value = searchAtom(atom); + if (value != null) { + return value; + } + } + } + if (firstAtom != null) { + throw noSuchFieldPanic(firstAtom); + } + } + } + var ctx = EnsoContext.get(this); + throw ctx.raiseAssertionPanic(this, fieldName, null); + } + + @ExplodeLoop + private Object searchAtom(Atom atom) { var constructor = atom.getConstructor(); for (int i = 0; i < getterPairs.length; i++) { var getter = getterPairs[i]; @@ -44,6 +82,6 @@ public Object execute(VirtualFrame frame) { return structsLibraries[i].getField(atom, getter.index); } } - throw noSuchFieldPanic(atom); + return null; } } diff --git a/lib/scala/interpreter-dsl/src/main/java/module-info.java b/lib/scala/interpreter-dsl/src/main/java/module-info.java index 30cfe105a677..949b98573f19 100644 --- a/lib/scala/interpreter-dsl/src/main/java/module-info.java +++ b/lib/scala/interpreter-dsl/src/main/java/module-info.java @@ -1,5 +1,4 @@ module org.enso.interpreter.dsl { - requires com.google.common; requires java.compiler; requires org.apache.commons.lang3; requires org.openide.util.lookup.RELEASE180; diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsMetadataProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/BuiltinsMetadataProcessor.java similarity index 99% rename from lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsMetadataProcessor.java rename to lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/BuiltinsMetadataProcessor.java index fb8c2f360127..2bd30ddbc4d3 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsMetadataProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/BuiltinsMetadataProcessor.java @@ -1,4 +1,4 @@ -package org.enso.interpreter.dsl; +package org.enso.interpreter.dsl.impl; import java.io.*; import java.nio.charset.StandardCharsets; diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/BuiltinsProcessor.java similarity index 99% rename from lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsProcessor.java rename to lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/BuiltinsProcessor.java index 2d0a9f8dc529..f2796bf971aa 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/BuiltinsProcessor.java @@ -1,4 +1,4 @@ -package org.enso.interpreter.dsl; +package org.enso.interpreter.dsl.impl; import com.google.common.base.CaseFormat; import java.io.IOException; @@ -24,6 +24,7 @@ import javax.tools.Diagnostic; import javax.tools.Diagnostic.Kind; import javax.tools.JavaFileObject; +import org.enso.interpreter.dsl.Builtin; import org.enso.interpreter.dsl.builtins.ClassName; import org.enso.interpreter.dsl.builtins.MethodNodeClassGenerator; import org.enso.interpreter.dsl.builtins.NoSpecializationClassGenerator; diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/atom/LayoutSpecProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/LayoutSpecProcessor.java similarity index 99% rename from lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/atom/LayoutSpecProcessor.java rename to lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/LayoutSpecProcessor.java index fa2e5971f236..1864940640c4 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/atom/LayoutSpecProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/LayoutSpecProcessor.java @@ -1,4 +1,4 @@ -package org.enso.interpreter.dsl.atom; +package org.enso.interpreter.dsl.impl; import java.io.IOException; import java.io.PrintWriter; @@ -12,6 +12,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; +import org.enso.interpreter.dsl.atom.LayoutSpec; import org.openide.util.lookup.ServiceProvider; @SupportedAnnotationTypes({"org.enso.interpreter.dsl.atom.LayoutSpec"}) diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/MethodProcessor.java similarity index 91% rename from lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java rename to lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/MethodProcessor.java index 46e528d51654..5c0b3bc6d273 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/MethodProcessor.java @@ -1,4 +1,4 @@ -package org.enso.interpreter.dsl; +package org.enso.interpreter.dsl.impl; import java.io.IOException; import java.io.PrintWriter; @@ -18,8 +18,8 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import org.enso.interpreter.dsl.model.MethodDefinition; @@ -71,54 +71,64 @@ public boolean handleProcess(Set annotations, RoundEnviro return true; } - private void handleTypeElement(TypeElement element, Boolean needsFrame) throws IOException { - ExecutableElement executeMethod = - element.getEnclosedElements().stream() - .filter( - x -> { - if (!(x instanceof ExecutableElement)) return false; - Name name = x.getSimpleName(); - return name.contentEquals("execute"); - }) - .map(x -> (ExecutableElement) x) - .findFirst() - .orElseGet( - () -> { - processingEnv - .getMessager() - .printMessage(Diagnostic.Kind.ERROR, "No execute method found.", element); - return null; - }); - if (executeMethod == null) return; - String pkgName = - processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString(); - - MethodDefinition def = new MethodDefinition(pkgName, element, executeMethod, needsFrame); - if (!def.validate(processingEnv)) { - return; + private Element findExecuteMethod(TypeElement e) { + for (var ee : e.getEnclosedElements()) { + if (ee instanceof ExecutableElement) { + if (ee.getSimpleName().contentEquals("execute")) { + return ee; + } + } } - generateCode(def); - String tpe = def.getType().toLowerCase(); - if (tpe.isEmpty()) { - processingEnv - .getMessager() - .printMessage( - Diagnostic.Kind.ERROR, - "Type of the BuiltinMethod cannot be empty in: " + def.getClassName()); - return; + if (findExecuteMethod(e.getSuperclass()) instanceof ExecutableElement ee) { + return ee; } - String fullClassName = def.getPackageName() + "." + def.getClassName(); - registerBuiltinMethod( - processingEnv.getFiler(), - def.getDeclaredName(), - fullClassName, - def.isStatic(), - def.isAutoRegister()); - if (def.hasAliases()) { - for (String alias : def.aliases()) { - registerBuiltinMethod( - processingEnv.getFiler(), alias, fullClassName, def.isStatic(), def.isAutoRegister()); + return null; + } + + private Element findExecuteMethod(TypeMirror t) { + if (t != null && processingEnv.getTypeUtils().asElement(t) instanceof TypeElement e) { + return findExecuteMethod(e); + } else { + return null; + } + } + + private void handleTypeElement(TypeElement element, Boolean needsFrame) throws IOException { + if (findExecuteMethod(element) instanceof ExecutableElement executeMethod) { + String pkgName = + processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString(); + + MethodDefinition def = new MethodDefinition(pkgName, element, executeMethod, needsFrame); + if (!def.validate(processingEnv)) { + return; } + generateCode(def); + String tpe = def.getType().toLowerCase(); + if (tpe.isEmpty()) { + processingEnv + .getMessager() + .printMessage( + Diagnostic.Kind.ERROR, + "Type of the BuiltinMethod cannot be empty in: " + def.getClassName()); + return; + } + String fullClassName = def.getPackageName() + "." + def.getClassName(); + registerBuiltinMethod( + processingEnv.getFiler(), + def.getDeclaredName(), + fullClassName, + def.isStatic(), + def.isAutoRegister()); + if (def.hasAliases()) { + for (String alias : def.aliases()) { + registerBuiltinMethod( + processingEnv.getFiler(), alias, fullClassName, def.isStatic(), def.isAutoRegister()); + } + } + } else { + processingEnv + .getMessager() + .printMessage(Diagnostic.Kind.ERROR, "No execute method found.", element); } } diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/TypeProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/TypeProcessor.java similarity index 99% rename from lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/TypeProcessor.java rename to lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/TypeProcessor.java index 7cc5ab8fc49b..b1cb9e3bcfc7 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/TypeProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/impl/TypeProcessor.java @@ -1,4 +1,4 @@ -package org.enso.interpreter.dsl; +package org.enso.interpreter.dsl.impl; import java.io.IOException; import java.io.PrintWriter; @@ -10,6 +10,7 @@ import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; +import org.enso.interpreter.dsl.BuiltinType; import org.openide.util.lookup.ServiceProvider; @SupportedAnnotationTypes("org.enso.interpreter.dsl.BuiltinType") diff --git a/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso b/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso index 33923d61bc6f..0669c82c95ec 100644 --- a/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso +++ b/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso @@ -165,7 +165,7 @@ add_specs suite_builder = (c:A&B&C).b_method . should_equal "B method" (c:A&B&C).c_method . should_equal "C method" - group_builder.specify "default to_text should delegate to one of values" pending="TODO: https://github.com/enso-org/enso/issues/11827" <| + group_builder.specify "default to_text should delegate to one of values" pending="To text doesn't dispatch properly #11827" <| ab = make_a_and_b ab.to_text . should_equal "(A_Ctor 1)" @@ -178,30 +178,33 @@ add_specs suite_builder = (c:A).to_text . should_equal "(A_Ctor 1)" (c:B).to_text . should_equal "(B_Ctor (A_Ctor 1))" - group_builder.specify "structural pattern matching should be able to match the primary type" pending="TODO: https://github.com/enso-org/enso/issues/12142" <| + group_builder.specify "structural pattern matching should be able to match the primary type" pending="Pattern matching: #12142" <| ab = make_a_and_b r = case ab of A.A_Ctor x -> "matched: "+x.to_text _ -> "structural matching of A.A_Ctor failed" r.should_equal "matched: 1" - group_builder.specify "should structural matching match other types?" pending="TODO: decide if we keep this test inhttps://github.com/enso-org/enso/issues/12142" <| + group_builder.specify "should structural matching match other types?" pending="Pattern matching: decide if we keep this test in #12142" <| ab = make_a_and_b r = case ab of B.B_Ctor x -> "matched: "+x.to_text _ -> "structural matching of B.B_Ctor failed" r.should_equal "matched: (A_Ctor 1)" - dispatch_pending="TODO: https://github.com/enso-org/enso/issues/12143" - group_builder.specify "calling a method on one of the types should not lose the intersection type" pending=dispatch_pending <| + group_builder.specify "calling a method on one of the types should not lose the intersection type" <| ab = make_a_and_b # Checked variant can hide the B part ab.a_id . is_a A . should_be_true - ab.a_id . is_a B . should_be_false + ab.a_id . is_a B . should_be_true # B is a hidden type + Test.expect_panic No_Such_Method (ab.a_id.b_method) + # But it can be uncovered via explicit cast - b = (ab.a_id):B - b.is_a A . should_be_false + a_via_id = ab.a_id + b = a_via_id:B + b.is_a A . should_be_true # A is a hidden type + Test.expect_panic No_Such_Method (b.a_method) b.is_a B . should_be_true new_ab = (ab.a_id):A&B @@ -210,21 +213,22 @@ add_specs suite_builder = new_ab.a_method . should_equal "A method" new_ab.b_method . should_equal "B method" - # But unchecked variant should keep both types and not hide anything + # dispatching method on self casts to Self type and always hides other types ab.a_id_unchecked . is_a A . should_be_true ab.a_id_unchecked . is_a B . should_be_true ab.a_id_unchecked.a_method . should_equal "A method" - ab.a_id_unchecked.b_method . should_equal "B method" + Test.expect_panic No_Such_Method (ab.a_id_unchecked.b_method) # The same should apply to the B part - ab.b_id . is_a A . should_be_false + ab.b_id . is_a A . should_be_true # A is a hidden type + Test.expect_panic No_Such_Method (ab.b_id.a_method) ab.b_id . is_a B . should_be_true new_ab_2 = (ab.b_id):A&B new_ab_2.is_a A . should_be_true new_ab_2.is_a B . should_be_true ab.b_id_unchecked . is_a A . should_be_true ab.b_id_unchecked . is_a B . should_be_true - ab.b_id_unchecked.a_method . should_equal "A method" + Test.expect_panic No_Such_Method (ab.b_id_unchecked.a_method) ab.b_id_unchecked.b_method . should_equal "B method" group_builder.specify "calling `.catch` on an intersection type should not lose the refinements" <| @@ -302,7 +306,7 @@ add_specs suite_builder = y.a_method . should_equal "A method" y.b_method . should_equal "B method" - group_builder.specify "attaching warnings to an intersection type should not lose even the hidden refinements" pending=dispatch_pending <| + group_builder.specify "attaching warnings to an intersection type should not lose even the hidden refinements" pending="Warnings not yet supported #12180" <| ab = make_a_and_b x = ab:A x_with_warning = Warning.attach (Illegal_State.Error "my warning") x