Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clearly select single specialization with enum dispatch pattern #6819

Merged
merged 7 commits into from
May 24, 2023
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package org.enso.interpreter.node.expression.builtin.meta;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.epb.runtime.PolyglotProxy;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.PanicSentinel;
import org.enso.interpreter.runtime.error.Warning;
import org.enso.interpreter.runtime.error.WithWarnings;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.number.EnsoBigInteger;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.GenerateUncached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;

@BuiltinMethod(
type = "Meta",
name = "type_of",
Expand Down Expand Up @@ -77,102 +77,14 @@ Object doWarning(WithWarnings value) {
return execute(value.getValue());
}

@Specialization(
guards = {
"interop.hasArrayElements(proxy)",
"!interop.isString(proxy)", // R string value is an array and a string
"!types.hasType(proxy)",
"!interop.hasMetaObject(proxy)"
})
Object doPolyglotArray(
PolyglotProxy proxy,
@CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") TypesLibrary types) {
return EnsoContext.get(this).getBuiltins().array();
}

@Specialization(
guards = {
"interop.isString(proxy)",
"!types.hasType(proxy)",
"!interop.hasMetaObject(proxy)"
})
Object doPolyglotString(
PolyglotProxy proxy,
@CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") TypesLibrary types) {
return EnsoContext.get(this).getBuiltins().text();
}

@Specialization(
guards = {
"interop.isNumber(proxy)",
"!types.hasType(proxy)",
"!interop.hasMetaObject(proxy)"
})
Object doPolyglotNumber(
PolyglotProxy proxy,
@CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") TypesLibrary types) {
Builtins builtins = EnsoContext.get(this).getBuiltins();
if (interop.fitsInInt(proxy)) {
return builtins.number().getInteger();
} else if (interop.fitsInDouble(proxy)) {
return builtins.number().getDecimal();
} else {
return EnsoContext.get(this).getBuiltins().number();
}
}

@Specialization(guards = {"interop.isTime(value)", "interop.isDate(value)"})
Object doDateTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
return EnsoContext.get(this).getBuiltins().dateTime();
}

@Specialization(
guards = {"interop.isTimeZone(value)", "!interop.isDate(value)", "!interop.isTime(value)"})
Object doTimeZone(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().timeZone();
}

@Specialization(guards = {"interop.isDate(value)", "!interop.isTime(value)"})
Object doDate(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().date();
}

@Specialization(guards = {"interop.isTime(value)", "!interop.isDate(value)"})
Object doTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().timeOfDay();
}

@Specialization(guards = "interop.isDuration(value)")
Object doDuration(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().duration();
}

@Specialization(
guards = {
"interop.hasMetaObject(value)",
"!types.hasType(value)",
"!interop.isDate(value)",
"!interop.isTime(value)",
"!interop.isTimeZone(value)"
})
Object doMetaObject(
@Specialization(guards = {"!types.hasType(value)"})
Object withoutType(
Object value,
@CachedLibrary(limit = "3") InteropLibrary interop,
@CachedLibrary(limit = "3") TypesLibrary types) {
try {
return interop.getMetaObject(value);
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
Builtins builtins = EnsoContext.get(this).getBuiltins();
throw new PanicException(builtins.error().makeCompileError("invalid meta object"), this);
}
@CachedLibrary(limit = "3") TypesLibrary types,
@Cached WithoutType delegate) {
var type = WithoutType.Interop.resolve(value, interop);
return delegate.execute(type, value);
}

@Specialization(guards = {"types.hasType(value)", "!interop.isNumber(value)"})
Expand All @@ -193,4 +105,169 @@ Object doAny(Object value) {
.makeCompileError("unknown type_of for " + value),
this);
}

@GenerateUncached
abstract static class WithoutType extends Node {
abstract Object execute(Interop op, Object value);

@Specialization(guards = {"type.isArray()"})
Type doPolyglotArray(Interop type, Object value) {
return EnsoContext.get(this).getBuiltins().array();
}

@Specialization(guards = {"type.isString()"})
Type doPolyglotString(Interop type, Object value) {
return EnsoContext.get(this).getBuiltins().text();
}

@Specialization(guards = {"type.isNumber()"})
Type doPolyglotNumber(
Interop type, Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
Builtins builtins = EnsoContext.get(this).getBuiltins();
if (interop.fitsInInt(value)) {
return builtins.number().getInteger();
} else if (interop.fitsInDouble(value)) {
return builtins.number().getDecimal();
} else {
return EnsoContext.get(this).getBuiltins().number().getNumber();
}
}

@Specialization(guards = {"type.isDateTime()"})
Type doDateTime(Interop type, Object value) {
return EnsoContext.get(this).getBuiltins().dateTime();
}

@Specialization(guards = {"type.isTimeZone()"})
Type doTimeZone(Interop type, Object value) {
EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().timeZone();
}

@Specialization(guards = {"type.isDate()"})
Type doDate(Interop type, Object value) {

EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().date();
}

@Specialization(guards = {"type.isTime()"})
Type doTime(Interop type, Object value) {

EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().timeOfDay();
}

@Specialization(guards = "type.isDuration()")
Type doDuration(Interop type, Object value) {
EnsoContext ctx = EnsoContext.get(this);
return ctx.getBuiltins().duration();
}

@Specialization(guards = {"type.isMetaObject()"})
Object doMetaObject(
Interop type, Object value, @CachedLibrary(limit = "3") InteropLibrary interop) {
try {
return interop.getMetaObject(value);
} catch (UnsupportedMessageException e) {
CompilerDirectives.transferToInterpreter();
Builtins builtins = EnsoContext.get(this).getBuiltins();
throw new PanicException(builtins.error().makeCompileError("invalid meta object"), this);
}
}

@Fallback
@CompilerDirectives.TruffleBoundary
Object doAny(Interop any, Object value) {
return DataflowError.withoutTrace(
EnsoContext.get(this)
.getBuiltins()
.error()
.makeCompileError("unknown type_of for " + value),
this);
}

enum Interop {
NONE,
STRING,
NUMBER,
ARRAY,
DATE_TIME,
TIME_ZONE,
DATE,
TIME,
DURATION,
META_OBJECT;

static Interop resolve(Object value, InteropLibrary interop) {
if (interop.isString(value)) {
return STRING;
}
if (interop.isNumber(value)) {
return NUMBER;
}
if (interop.hasArrayElements(value)) {
return ARRAY;
}
boolean time = interop.isTime(value);
boolean date = interop.isDate(value);
if (time) {
return date ? DATE_TIME : TIME;
}
if (date) {
return DATE;
}
if (interop.isTimeZone(value)) {
return TIME_ZONE;
}
if (interop.isDuration(value)) {
return DURATION;
}
if (interop.hasMetaObject(value)) {
return META_OBJECT;
}
return NONE;
}

boolean isString() {
return this == STRING;
}

boolean isNumber() {
return this == NUMBER;
}

boolean isArray() {
return this == ARRAY;
}

boolean isDateTime() {
return this == DATE_TIME;
}

boolean isTimeZone() {
return this == TIME_ZONE;
}

boolean isTime() {
return this == TIME;
}

boolean isDate() {
return this == DATE;
}

boolean isDuration() {
return this == DURATION;
}

boolean isMetaObject() {
return this == META_OBJECT;
}

boolean isNone() {
return this == NONE;
}
}
}
}
19 changes: 9 additions & 10 deletions test/Tests/src/Semantic/Case_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -257,18 +257,17 @@ spec = Test.group "Pattern Matches" <|
_ -> Test.fail "Expected to match on ArrayList type."

case Meta.type_of list of
_ : Vector -> Test.fail "Expected to match in java.lang.Class case."
_ : Array -> Test.fail "Expected to match in java.lang.Class case."
_ : ArrayList -> Test.fail "Expected to match in java.lang.Class case."
_ : Class -> Nothing
_ : ArrayList -> Test.fail "Expected to match in java.lang.Class case."
_ : Vector -> Test.fail "Expected to match Array case."
_ : ArrayList -> Test.fail "Expected to match Array case."
_ : Class -> Test.fail "Expected to match Array case."
Array -> Nothing
_ -> Test.fail "Expected to match Array case."

case Meta.type_of list of
Vector -> Test.fail "Expected to match on a polyglot symbol."
Array -> Test.fail "Expected to match on a polyglot symbol."
ArrayList -> Nothing
Class -> Test.fail "Expected to match on a polyglot symbol."
_ -> Test.fail "Expected to match on a polyglot symbol."
Vector -> Test.fail "Expected to match Array case."
Class -> Test.fail "Expected to match Array case."
Array -> Nothing
_ -> Test.fail "Expected to match Array case."

# Tests a bug where array matching a polyglot Object[] array would fail if the same branch mis-matched earlier.
foo x = case x of
Expand Down
3 changes: 2 additions & 1 deletion test/Tests/src/Semantic/Meta_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ spec =
list.add 123
list_tpe = Meta.type_of list
list_tpe . should_not_equal_type JObject
list_tpe . should_equal_type ArrayList
list_tpe . should_not_equal_type ArrayList
list_tpe . should_equal_type Array

e = IOException.new "meh"
e_tpe = Meta.type_of e
Expand Down