mirror of
https://github.com/enso-org/enso.git
synced 2024-11-25 10:43:02 +03:00
Giving TypeOfNode richer query API (#11618)
This commit is contained in:
parent
d611939132
commit
7c5e69219c
@ -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 {
|
||||
|
@ -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}",
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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 {
|
||||
|
@ -68,6 +68,7 @@ public final class EnsoMultiValue extends EnsoObject {
|
||||
return types[0];
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
public final Type[] allTypes() {
|
||||
return types.clone();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user