Giving TypeOfNode richer query API (#11618)

This commit is contained in:
Jaroslav Tulach 2024-11-22 07:58:30 +01:00 committed by GitHub
parent d611939132
commit 7c5e69219c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 367 additions and 76 deletions

View File

@ -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 {

View File

@ -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}",

View File

@ -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;

View File

@ -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<Object[]>();
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<Object[]> 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;
});
}
}

View File

@ -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();

View File

@ -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) {

View File

@ -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,

View File

@ -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 {

View File

@ -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});

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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));

View File

@ -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)) {

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
};

View File

@ -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 {

View File

@ -68,6 +68,7 @@ public final class EnsoMultiValue extends EnsoObject {
return types[0];
}
@ExportMessage
public final Type[] allTypes() {
return types.clone();
}

View File

@ -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);

View File

@ -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 <em>interop value</em> 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 <em>specialized</em>
*/
@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;
}
}
}

View File

@ -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};
}
}