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 ae5c93bbc3..616433f000 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 @@ -50,7 +50,12 @@ public abstract class TypeOfNode extends Node { * @return either {@link Type} of the value or {@link DataflowError} if there is no such type */ public final Object findTypeOrError(Object value) { - return executeSingleType(value); + try { + var types = executeTypes(value); + return types[0]; + } catch (NonTypeResult plain) { + return plain.result; + } } /** @@ -64,58 +69,90 @@ public abstract class TypeOfNode extends Node { return findTypeOrError(value) instanceof Type type ? type : null; } - abstract Object executeSingleType(Object value); + /** + * 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[] findAllTypes(Object value) { + try { + var types = executeTypes(value); + assert types != null && types.length > 0; + return types; + } catch (NonTypeResult ex) { + return null; + } + } - /** Creates new optimizing instance of this node. */ + /** + * 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. */ + /** + * 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.executeSingleType(value.getValue()); + Type[] doPanicSentinel(PanicSentinel value) { + return fromType(EnsoContext.get(this).getBuiltins().panic()); } @Specialization - Object doEnsoMultiValue(EnsoMultiValue value) { - return value.allTypes()[0]; + Type[] doWarning(WithWarnings value, @Cached TypeOfNode withoutWarning) throws NonTypeResult { + return withoutWarning.executeTypes(value.getValue()); } static boolean isWithType(Object value, TypesLibrary types, InteropLibrary iop) { @@ -139,32 +176,40 @@ 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 { + throw new NonTypeResult(typeOrPlain); + } } @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 @@ -352,4 +397,17 @@ public abstract class TypeOfNode extends Node { } } } + + static final class NonTypeResult extends Exception { + final Object result; + + NonTypeResult(Object 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}; + } }