diff --git a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java index 83cda99cbb..ce38237cdc 100644 --- a/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java +++ b/engine/runtime-instrument-common/src/main/java/org/enso/interpreter/service/ExecutionCallbacks.java @@ -220,7 +220,7 @@ final class ExecutionCallbacks implements IdExecutionService.Callbacks { resultType = Constants.UNRESOLVED_SYMBOL; } else { var typeOfNode = TypeOfNode.getUncached(); - Object typeResult = value == null ? null : typeOfNode.execute(value); + Object typeResult = value == null ? null : typeOfNode.findTypeOrError(value); if (typeResult instanceof Type t) { resultType = getTypeQualifiedName(t); } else { diff --git a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala index c522022d49..a3b061514f 100644 --- a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala +++ b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala @@ -580,7 +580,7 @@ object ProgramExecutionSupport { Array[Object]( visualization.id, expressionId, - Try(TypeOfNode.getUncached.execute(expressionValue)) + Try(TypeOfNode.getUncached.findTypeOrError(expressionValue)) .getOrElse(expressionValue.getClass) ) ) @@ -640,8 +640,7 @@ object ProgramExecutionSupport { Option(error.getMessage).getOrElse(error.getClass.getSimpleName) if (!TypesGen.isPanicSentinel(expressionValue)) { val typeOfNode = - Option(TypeOfNode.getUncached.execute(expressionValue)) - .getOrElse(expressionValue.getClass) + TypeOfNode.getUncached.findTypeOrError(expressionValue) ctx.executionService.getLogger.log( Level.WARNING, "Execution of visualization [{0}] on value [{1}] of [{2}] failed. {3} | {4} | {5}", diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNodeTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNodeTest.java index 6545142ad1..6a4611bf0b 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNodeTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/error/CatchPanicNodeTest.java @@ -4,7 +4,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import com.oracle.truffle.api.interop.InteropLibrary; import org.enso.interpreter.node.expression.foreign.HostValueToEnsoNode; import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; @@ -23,9 +22,6 @@ import org.junit.BeforeClass; import org.junit.Test; public class CatchPanicNodeTest { - - private static final InteropLibrary interop = InteropLibrary.getUncached(); - private static Context context; private static CatchPanicNode catchPanicNode; private static HostValueToEnsoNode hostValueToEnsoNode; @@ -110,7 +106,7 @@ public class CatchPanicNodeTest { var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); assertEquals("One argument expected", 1, args.length); - var argType = TypeOfNode.getUncached().execute(args[0]); + var argType = TypeOfNode.getUncached().findTypeOrError(args[0]); if (argType == ctx.getBuiltins().caughtPanic().getType()) { assertThat(args[0].toString(), Matchers.containsString("Thrown")); return text; @@ -147,7 +143,7 @@ public class CatchPanicNodeTest { var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); assertEquals("One argument expected", 1, args.length); - var argType = TypeOfNode.getUncached().execute(args[0]); + var argType = TypeOfNode.getUncached().findTypeOrError(args[0]); if (argType == ctx.getBuiltins().caughtPanic().getType()) { assertThat(args[0].toString(), Matchers.containsString("Thrown")); return text; @@ -184,7 +180,7 @@ public class CatchPanicNodeTest { var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); assertEquals("One argument expected", 1, args.length); - var argType = TypeOfNode.getUncached().execute(args[0]); + var argType = TypeOfNode.getUncached().findTypeOrError(args[0]); if (argType == ctx.getBuiltins().caughtPanic().getType()) { assertThat(args[0].toString(), Matchers.containsString("Thrown")); return text; @@ -221,7 +217,7 @@ public class CatchPanicNodeTest { var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); assertEquals("One argument expected", 1, args.length); - var argType = TypeOfNode.getUncached().execute(args[0]); + var argType = TypeOfNode.getUncached().findTypeOrError(args[0]); if (argType == ctx.getBuiltins().caughtPanic().getType()) { assertThat(args[0].toString(), Matchers.containsString("Thrown")); return text; @@ -258,7 +254,7 @@ public class CatchPanicNodeTest { var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); assertEquals("One argument expected", 1, args.length); - var argType = TypeOfNode.getUncached().execute(args[0]); + var argType = TypeOfNode.getUncached().findTypeOrError(args[0]); if (argType == ctx.getBuiltins().caughtPanic().getType()) { assertThat(args[0].toString(), Matchers.containsString("Thrown")); return text; @@ -300,7 +296,7 @@ public class CatchPanicNodeTest { var args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments()); assertEquals("One argument expected", 1, args.length); - var argType = TypeOfNode.getUncached().execute(args[0]); + var argType = TypeOfNode.getUncached().findTypeOrError(args[0]); if (argType == ctx.getBuiltins().caughtPanic().getType()) { assertThat(args[0].toString(), Matchers.containsString("Thrown")); return text; diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeMultiValueTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeMultiValueTest.java new file mode 100644 index 0000000000..cda8670dd2 --- /dev/null +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeMultiValueTest.java @@ -0,0 +1,151 @@ +package org.enso.interpreter.node.expression.builtin.meta; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.oracle.truffle.api.RootCallTarget; +import java.util.ArrayList; +import java.util.Arrays; +import org.enso.interpreter.runtime.data.EnsoMultiValue; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.library.dispatch.TypeOfNode; +import org.enso.interpreter.test.ValuesGenerator; +import org.enso.test.utils.ContextUtils; +import org.enso.test.utils.TestRootNode; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Value; +import org.junit.AfterClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class TypeOfNodeMultiValueTest { + + private static RootCallTarget testTypesCall; + + @Parameterized.Parameter(0) + public Object value; + + @Parameterized.Parameter(1) + public String type; + + @Parameterized.Parameter(2) + public int typeIndex; + + private static Context ctx; + + private static Context ctx() { + if (ctx == null) { + ctx = ContextUtils.defaultContextBuilder().build(); + ContextUtils.executeInContext( + ctx, + () -> { + var node = TypeOfNode.create(); + var root = + new TestRootNode( + (frame) -> { + var arg = frame.getArguments()[0]; + var t = node.findTypeOrError(arg); + var all = node.findAllTypesOrNull(arg); + return new Object[] {t, all}; + }); + root.insertChildren(node); + testTypesCall = root.getCallTarget(); + return null; + }); + } + assertNotNull("Test types call initialized", testTypesCall); + return ctx; + } + + @Parameterized.Parameters + public static Object[][] allPossibleEnsoInterpreterValues() throws Exception { + var g = ValuesGenerator.create(ctx()); + var typeOf = + ContextUtils.evalModule( + ctx(), + """ + from Standard.Base import all + + typ obj = Meta.type_of obj + main = typ + """); + var data = new ArrayList(); + for (var polyValue : g.allValues()) { + registerValue(g, typeOf, polyValue, data); + } + return data.toArray(new Object[0][]); + } + + private static void registerValue( + ValuesGenerator g, Value typeOf, Value polyValue, ArrayList data) { + var t = typeOf.execute(polyValue); + if (!polyValue.isNull()) { + assertTrue("Type of " + polyValue + " is " + t, t.isMetaObject()); + var rawValue = ContextUtils.unwrapValue(ctx(), polyValue); + var rawType = ContextUtils.unwrapValue(ctx(), t); + if (rawType instanceof Type type) { + var singleMultiValue = EnsoMultiValue.create(new Type[] {type}, new Object[] {rawValue}); + var n = t.getMetaSimpleName(); + data.add(new Object[] {singleMultiValue, n, 0}); + var rawInt = (Type) ContextUtils.unwrapValue(ctx(), g.typeInteger()); + var secondMultiValue = + EnsoMultiValue.create(new Type[] {rawInt, type}, new Object[] {5L, rawValue}); + data.add(new Object[] {secondMultiValue, n, 1}); + var firstMultiValue = + EnsoMultiValue.create(new Type[] {type, rawInt}, new Object[] {rawValue, 6L}); + data.add(new Object[] {firstMultiValue, n, 0}); + } else { + if (!t.isHostObject()) { + data.add(new Object[] {rawValue, null, -1}); + } + } + } + } + + @AfterClass + public static void disposeCtx() throws Exception { + if (ctx != null) { + ctx.close(); + ctx = null; + } + } + + @Test + public void typeOfCheck() { + assertType(value, type, typeIndex); + } + + private static void assertType(Object value, String expectedTypeName, int typeIndex) { + assertNotNull("Value " + value + " should have a type", expectedTypeName); + ContextUtils.executeInContext( + ctx(), + () -> { + var pairResult = (Object[]) testTypesCall.call(value); + var t = pairResult[0]; + var all = (Object[]) pairResult[1]; + + Object symbolType; + if (t instanceof DataflowError) { + assertNull("No types for errors", all); + symbolType = t; + } else { + assertNotNull("All types found for " + value, all); + assertTrue( + "Size is at least " + typeIndex + " but was: " + Arrays.toString(all), + all.length >= typeIndex); + assertEquals("Major type is the same with first of allTypes for" + value, t, all[0]); + symbolType = all[typeIndex]; + } + + var symbolTypeValue = ctx.asValue(symbolType); + assertTrue("It is meta object: " + symbolTypeValue, symbolTypeValue.isMetaObject()); + assertEquals(expectedTypeName, symbolTypeValue.getMetaSimpleName()); + return null; + }); + } +} diff --git a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java index affc85f4e3..42abe899eb 100644 --- a/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java +++ b/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNodeTest.java @@ -7,6 +7,7 @@ import com.oracle.truffle.api.interop.TruffleObject; import java.util.ArrayList; import org.enso.interpreter.runtime.callable.UnresolvedConstructor; import org.enso.interpreter.runtime.callable.UnresolvedSymbol; +import org.enso.interpreter.runtime.data.EnsoObject; import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.library.dispatch.TypeOfNode; import org.enso.interpreter.test.ValuesGenerator; @@ -80,12 +81,29 @@ public class TypeOfNodeTest { assertType(value, type, true); } - private void assertType(Object symbol, String expectedTypeName, boolean withPriming) { + private static void assertType(Object symbol, String expectedTypeName, boolean withPriming) { ContextUtils.executeInContext( ctx(), () -> { var node = TypeOfNode.create(); - var root = new TestRootNode((frame) -> node.execute(frame.getArguments()[0])); + var root = + new TestRootNode( + (frame) -> { + var arg = frame.getArguments()[0]; + var typeOrNull = node.findTypeOrNull(arg); + var typeOrError = node.findTypeOrError(arg); + if (typeOrNull == null) { + if (typeOrError instanceof EnsoObject) { + assertTrue( + "Expecting error for " + arg, typeOrError instanceof DataflowError); + } else { + // probably HostMetaObject + } + } else { + assertEquals("Types should be the same for " + arg, typeOrNull, typeOrError); + } + return typeOrError; + }); root.insertChildren(node); var call = root.getCallTarget(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java index 106fff964d..c65a1c3b76 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/BinaryOperatorNode.java @@ -93,8 +93,7 @@ final class BinaryOperatorNode extends ExpressionNode { VirtualFrame frame, String symbol, Object self, Object that); static Type findType(TypeOfNode typeOfNode, Object obj) { - var rawType = typeOfNode.execute(obj); - return rawType instanceof Type type ? type : null; + return typeOfNode.findTypeOrNull(obj); } static Type findTypeUncached(Object obj) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeConversionNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeConversionNode.java index 3b1da77666..048057fee5 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeConversionNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/IndirectInvokeConversionNode.java @@ -20,7 +20,6 @@ import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.callable.UnresolvedConversion; import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo; import org.enso.interpreter.runtime.callable.function.Function; -import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.hash.EnsoHashMap; import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.error.DataflowError; @@ -57,7 +56,7 @@ abstract class IndirectInvokeConversionNode extends Node { int thatArgumentPosition); static boolean hasType(TypeOfNode typeOfNode, Object value) { - return typeOfNode.execute(value) instanceof Type; + return typeOfNode.hasType(value); } @Specialization(guards = {"hasType(typeOfNode, that)"}) @@ -81,7 +80,7 @@ abstract class IndirectInvokeConversionNode extends Node { conversionResolverNode.expectNonNull( that, InvokeConversionNode.extractType(this, self), - (Type) typeOfNode.execute(that), + typeOfNode.findTypeOrNull(that), conversion); return indirectInvokeFunctionNode.execute( function, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java index ab68a53316..deb02b3824 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/InvokeConversionNode.java @@ -101,7 +101,7 @@ public abstract class InvokeConversionNode extends BaseNode { } static boolean hasType(TypeOfNode typeOfNode, Object value) { - return typeOfNode.execute(value) instanceof Type; + return typeOfNode.hasType(value); } static boolean isDataflowError(Object value) { @@ -119,7 +119,7 @@ public abstract class InvokeConversionNode extends BaseNode { Object[] arguments, @Shared("typeOfNode") @Cached TypeOfNode dispatch, @Shared("conversionResolverNode") @Cached ConversionResolverNode resolveNode) { - var thatType = (Type) dispatch.execute(that); + var thatType = dispatch.findTypeOrNull(that); if (thatType == self) { return that; } else { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/PolyglotSymbolTypeBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/PolyglotSymbolTypeBranchNode.java index cdff9d73a2..c371a95a71 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/PolyglotSymbolTypeBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/PolyglotSymbolTypeBranchNode.java @@ -50,7 +50,7 @@ public abstract class PolyglotSymbolTypeBranchNode extends BranchNode { Object state, Object target, @CachedLibrary(limit = "3") InteropLibrary interop) { - Object tpeOfTarget = typeOfNode.execute(target); + Object tpeOfTarget = typeOfNode.findTypeOrError(target); boolean test = isSameObject.execute(polyglotSymbol, tpeOfTarget); if (profile.profile(test)) { accept(frame, state, new Object[] {target}); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsAtomNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsAtomNode.java index 88cd7f0eac..0e3d4568a8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsAtomNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsAtomNode.java @@ -124,7 +124,7 @@ abstract class EqualsAtomNode extends Node { @TruffleBoundary private static boolean orderingOrNullOrError( Node where, EnsoContext ctx, Object obj, Function fn) { - var type = TypeOfNode.getUncached().execute(obj); + var type = TypeOfNode.getUncached().findTypeOrError(obj); if (type == ctx.getBuiltins().ordering().getType()) { return true; } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsNode.java index 0ae998c038..3f1e57d9cf 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/EqualsNode.java @@ -69,8 +69,8 @@ public final class EqualsNode extends Node { public EqualsAndInfo execute(VirtualFrame frame, Object self, Object other) { var areEqual = node.execute(frame, self, other); if (!areEqual.isTrue()) { - var selfType = types.execute(self); - var otherType = types.execute(other); + var selfType = types.findTypeOrNull(self); + var otherType = types.findTypeOrNull(other); if (selfType != otherType) { if (convert == null) { CompilerDirectives.transferToInterpreter(); @@ -101,8 +101,7 @@ public final class EqualsNode extends Node { abstract EqualsAndInfo executeWithConversion(VirtualFrame frame, Object self, Object that); static Type findType(TypeOfNode typeOfNode, Object obj) { - var rawType = typeOfNode.execute(obj); - return rawType instanceof Type type ? type : null; + return typeOfNode.findTypeOrNull(obj); } static Type findTypeUncached(Object obj) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetAnnotationNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetAnnotationNode.java index ef91a3996b..9fe39e212c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetAnnotationNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetAnnotationNode.java @@ -39,7 +39,7 @@ public abstract class GetAnnotationNode extends BaseNode { @Cached ThunkExecutorNode thunkExecutorNode, @Cached ExpectStringNode expectStringNode, @Cached TypeOfNode typeOfNode) { - Object targetTypeResult = typeOfNode.execute(target); + Object targetTypeResult = typeOfNode.findTypeOrError(target); if (targetTypeResult instanceof DataflowError error) { return error; } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetQualifiedTypeNameNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetQualifiedTypeNameNode.java index 022ef2abd5..7c1062408e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetQualifiedTypeNameNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/GetQualifiedTypeNameNode.java @@ -21,7 +21,7 @@ public class GetQualifiedTypeNameNode extends Node { var maybeType = switch (value) { case Type type -> type; - default -> typeOfNode.execute(value); + default -> typeOfNode.findTypeOrError(value); }; if (maybeType instanceof Type type) { return Text.create(getQualifiedName(type)); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java index 8b4a6865d8..46431e3d16 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsValueOfTypeNode.java @@ -52,7 +52,7 @@ public abstract class IsValueOfTypeNode extends Node { TypeOfNode typeOfNode, IsSameObjectNode isSameObject, CountingConditionProfile isSameObjectProfile) { - Object tpeOfPayload = typeOfNode.execute(payload); + Object tpeOfPayload = typeOfNode.findTypeOrError(payload); if (isSameObjectProfile.profile(isSameObject.execute(expectedType, tpeOfPayload))) { return true; } else if (TypesGen.isType(tpeOfPayload)) { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfBuiltin.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfBuiltin.java index 1a651bbc61..4ce1367df0 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfBuiltin.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfBuiltin.java @@ -1,27 +1,34 @@ package org.enso.interpreter.node.expression.builtin.meta; -import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.AcceptsError; +import org.enso.interpreter.dsl.AcceptsWarning; import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.library.dispatch.TypeOfNode; +import org.enso.interpreter.runtime.warning.WarningsLibrary; @BuiltinMethod( type = "Meta", name = "type_of", description = "Returns the type of a value.", autoRegister = false) -@GenerateUncached -public final class TypeOfBuiltin extends Node { +final class TypeOfBuiltin extends Node { + private @Child WarningsLibrary warnings = WarningsLibrary.getFactory().createDispatched(11); private @Child TypeOfNode typeOf = TypeOfNode.create(); - private TypeOfBuiltin() {} + TypeOfBuiltin() {} - public Object execute(@AcceptsError Object value) { - return typeOf.execute(value); - } - - public static TypeOfBuiltin build() { - return new TypeOfBuiltin(); + public Object execute(@AcceptsError @AcceptsWarning Object value) { + if (warnings.hasWarnings(value)) { + try { + value = warnings.removeWarnings(value); + } catch (UnsupportedMessageException ex) { + var ctx = EnsoContext.get(this); + throw ctx.raiseAssertionPanic(this, null, ex); + } + } + return typeOf.findTypeOrError(value); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 22a07808e9..3e0aa7f396 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -672,11 +672,11 @@ public abstract class SortVectorNode extends Node { } private boolean isPrimitiveValue(Object object) { - return isBuiltinType(typeOfNode.execute(object)); + return isBuiltinType(typeOfNode.findTypeOrError(object)); } private String getQualifiedTypeName(Object object) { - var typeObj = typeOfNode.execute(object); + var typeObj = typeOfNode.findTypeOrError(object); return toTextNode.execute(typeObj).toString(); } @@ -686,7 +686,7 @@ public abstract class SortVectorNode extends Node { } else if (isNan(object)) { return 100; } else { - var type = typeOfNode.execute(object); + var type = typeOfNode.findTypeOrError(object); return getBuiltinTypeCost(type); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/util/TypeToDisplayTextNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/util/TypeToDisplayTextNode.java index ea1769c1ca..472715d579 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/util/TypeToDisplayTextNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/util/TypeToDisplayTextNode.java @@ -17,7 +17,7 @@ public final class TypeToDisplayTextNode extends Node { } public String execute(Object o) { - return switch (typeOfNode.execute(o)) { + return switch (typeOfNode.findTypeOrError(o)) { case Type t -> t.getName(); default -> fallbackDisplay(o); }; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java index 0996660009..fda4a75f41 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/typecheck/SingleTypeCheckNode.java @@ -178,7 +178,7 @@ non-sealed abstract class SingleTypeCheckNode extends AbstractTypeCheckNode { if (v instanceof UnresolvedConstructor) { return null; } - if (typeOfNode.execute(v) instanceof Type from) { + if (typeOfNode.findTypeOrError(v) instanceof Type from) { if (previous != null && previous.length == 1 && previous[0] == from) { return previous; } else { 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 119e6dc1a4..71df36ed73 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 @@ -68,6 +68,7 @@ public final class EnsoMultiValue extends EnsoObject { return types[0]; } + @ExportMessage public final Type[] allTypes() { return types.clone(); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java index dedd9e4e55..754218d229 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/Type.java @@ -166,6 +166,7 @@ public final class Type extends EnsoObject { * @param ctx contexts to get Any type (common super class) from * @return a compilation constant array with all types this type represents */ + @ExportMessage.Ignore public final Type[] allTypes(EnsoContext ctx) { var types = new Type[3]; var realCount = fillInTypes(this, types, ctx); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java index 5d5baa4c2a..0649a0de80 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypeOfNode.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.dsl.GenerateUncached; 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.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; @@ -24,55 +25,136 @@ import org.enso.interpreter.runtime.error.PanicSentinel; import org.enso.interpreter.runtime.number.EnsoBigInteger; import org.enso.interpreter.runtime.warning.WithWarnings; +/** + * Provides API (in form of {@code public} methods) for querying type information about a value. + * Contains non-{@code public} methods with implementation to handle such queries. + */ @GenerateUncached public abstract class TypeOfNode extends Node { TypeOfNode() {} - public abstract Object execute(Object value); + /** + * Check whether given value has an Enso {@link Type}. + * + * @param value the value to check + * @return {@code true} if there is (at least one) type associated with the value + */ + public final boolean hasType(Object value) { + return findTypeOrError(value) instanceof Type; + } + /** + * Finds (a primary) type associated with a given {@code value}. If no such type exists, an error + * value is returned that can normally flow thru the Enso interpreter without any conversions. + * + * @param value the value to check + * @return either {@link Type} of the value, or meta object of the value, or {@link DataflowError} + * if there is no such type + */ + public final TruffleObject findTypeOrError(Object value) { + try { + var types = executeTypes(value); + return types[0]; + } catch (NonTypeResult plain) { + return plain.result; + } + } + + /** + * Finds (a primary) type associated with a given {@code value} or returns {@code null}. Useful + * for internal checks in the Enso interpreter. + * + * @param value the value to check + * @return either Type of the value or {@code null} if there is no such type + */ + public final Type findTypeOrNull(Object value) { + return findTypeOrError(value) instanceof Type type ? type : null; + } + + /** + * Finds all types associated with a given {@code value} or returns {@code null}. It is guaranteed + * that the returned array is going to have at least one element, if it is non-{@code null}. + * + * @param value the value to check + * @return either types associated with the value or {@code null} if there is no such type + */ + public final Type[] findAllTypesOrNull(Object value) { + try { + var types = executeTypes(value); + assert types != null && types.length > 0; + return types; + } catch (NonTypeResult ex) { + return null; + } + } + + /** + * Internal implementation call to delegate to various {@link Specialization} methods. + * + * @param value the value to find type for + * @return array of types with at least one element, but possibly more + * @throws NonTypeResult when there is a interop value result, but it is not a {@link + * Type} + */ + abstract Type[] executeTypes(Object value) throws NonTypeResult; + + /** + * Creates new optimizing instance of this node. + * + * @return new instance of this node ready to be specialized + */ @NeverDefault public static TypeOfNode create() { return TypeOfNodeGen.create(); } + /** + * Returns default, non-optimizing implementation of this node. + * + * @return shared singleton instance of this node + */ @NeverDefault public static TypeOfNode getUncached() { return TypeOfNodeGen.getUncached(); } - @Specialization - Object doUnresolvedSymbol(UnresolvedSymbol value) { - return EnsoContext.get(this).getBuiltins().function(); + private static Type[] fromType(Type single) { + return new Type[] {single}; } @Specialization - Object doDouble(double value) { - return EnsoContext.get(this).getBuiltins().number().getFloat(); + Type[] doUnresolvedSymbol(UnresolvedSymbol value) { + return fromType(EnsoContext.get(this).getBuiltins().function()); } @Specialization - Object doLong(long value) { - return EnsoContext.get(this).getBuiltins().number().getInteger(); + Type[] doDouble(double value) { + return fromType(EnsoContext.get(this).getBuiltins().number().getFloat()); } @Specialization - Object doBigInteger(EnsoBigInteger value) { - return EnsoContext.get(this).getBuiltins().number().getInteger(); + Type[] doLong(long value) { + return fromType(EnsoContext.get(this).getBuiltins().number().getInteger()); } @Specialization - Object doPanicException(PanicException value) { - return EnsoContext.get(this).getBuiltins().panic(); + Type[] doBigInteger(EnsoBigInteger value) { + return fromType(EnsoContext.get(this).getBuiltins().number().getInteger()); } @Specialization - Object doPanicSentinel(PanicSentinel value) { - return EnsoContext.get(this).getBuiltins().panic(); + Type[] doPanicException(PanicException value) { + return fromType(EnsoContext.get(this).getBuiltins().panic()); } @Specialization - Object doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) { - return withoutWarning.execute(value.getValue()); + Type[] doPanicSentinel(PanicSentinel value) { + return fromType(EnsoContext.get(this).getBuiltins().panic()); + } + + @Specialization + Type[] doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) throws NonTypeResult { + return withoutWarning.executeTypes(value.getValue()); } static boolean isWithType(Object value, TypesLibrary types, InteropLibrary iop) { @@ -96,32 +178,45 @@ public abstract class TypeOfNode extends Node { } @Specialization(guards = {"isWithoutType(value, types)"}) - Object withoutType( + Type[] withoutType( Object value, @Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop, @Shared("types") @CachedLibrary(limit = "3") TypesLibrary types, - @Cached WithoutType delegate) { - var type = WithoutType.Interop.resolve(value, interop); - return delegate.execute(type, value); + @Cached WithoutType delegate) + throws NonTypeResult { + var kind = WithoutType.Interop.resolve(value, interop); + var typeOrPlain = delegate.execute(kind, value); + if (typeOrPlain instanceof Type type) { + return fromType(type); + } else if (typeOrPlain instanceof TruffleObject metaObject) { + throw new NonTypeResult(metaObject); + } else { + CompilerDirectives.transferToInterpreter(); + var ctx = EnsoContext.get(this); + throw ctx.raiseAssertionPanic( + this, "MetaObject should be a TruffleObject: " + typeOrPlain, null); + } } @Specialization(guards = {"isWithType(value, types, interop)"}) - Object doType( + Type[] doType( Object value, @Shared("interop") @CachedLibrary(limit = "3") InteropLibrary interop, @Shared("types") @CachedLibrary(limit = "3") TypesLibrary types) { - return types.getType(value); + return types.allTypes(value); } @Fallback @CompilerDirectives.TruffleBoundary - Object doAny(Object value) { - return DataflowError.withDefaultTrace( - EnsoContext.get(this) - .getBuiltins() - .error() - .makeCompileError("unknown type_of for " + value), - this); + Type[] doAny(Object value) throws NonTypeResult { + var err = + DataflowError.withDefaultTrace( + EnsoContext.get(this) + .getBuiltins() + .error() + .makeCompileError("unknown type_of for " + value), + this); + throw new NonTypeResult(err); } @GenerateUncached @@ -309,4 +404,17 @@ public abstract class TypeOfNode extends Node { } } } + + static final class NonTypeResult extends Exception { + final TruffleObject result; + + NonTypeResult(TruffleObject result) { + this.result = result; + } + + @Override + public Throwable fillInStackTrace() { + return this; + } + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java index 58c3527acc..eea5cd8a52 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/library/dispatch/TypesLibrary.java @@ -68,4 +68,17 @@ public abstract class TypesLibrary extends Library { public Type getType(Object receiver) { return null; } + + /** + * Returns all the receiver types. Default implementation delegates to {@link + * #getType(java.lang.Object)} and returns array with one element of that type + * + * @param receiver the typed object + * @return the corresponding types for the {@code receiver} + */ + public Type[] allTypes(Object receiver) { + var t = getType(receiver); + assert t != null : "null type for " + receiver; + return new Type[] {t}; + } }