Skip to content

Commit

Permalink
Clearly select single specialization with enum dispatch pattern (#6819)
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach authored May 24, 2023
1 parent 4cbd5f4 commit 792cbc4
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 116 deletions.
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

0 comments on commit 792cbc4

Please sign in to comment.