From 36824fe2f20a8333dcc9510c6d9902f4870c6db7 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 19 Sep 2022 23:21:00 +0200 Subject: [PATCH 01/10] Implement `type_of` method This change implements a simple `type_of` method that returns a type of a given value, including for polyglot objects. The change also allows for patterning matching on various time-related instances. It is a nice-to-have on its own, but it was primarily needed here to write some tests. --- .../Base/0.0.0-dev/src/Data/Time/Date.enso | 8 ++- .../0.0.0-dev/src/Data/Time/Date_Time.enso | 8 ++- .../0.0.0-dev/src/Data/Time/Time_Of_Day.enso | 8 ++- .../0.0.0-dev/src/Data/Time/Time_Zone.enso | 5 +- .../lib/Standard/Base/0.0.0-dev/src/Meta.enso | 34 ++++++++++ .../controlflow/caseexpr/DateBranchNode.java | 59 +++++++++++++++++ .../caseexpr/DateTimeBranchNode.java | 64 +++++++++++++++++++ .../caseexpr/TimeOfDayBranchNode.java | 59 +++++++++++++++++ .../caseexpr/TimeZoneBranchNode.java | 59 +++++++++++++++++ .../expression/builtin/meta/TypeOfNode.java | 27 ++++++++ .../builtin/meta/TypeOfPolyglotNode.java | 37 +++++++++++ .../enso/compiler/codegen/IrToTruffle.scala | 12 ++++ test/Tests/src/Semantic/Meta_Spec.enso | 40 +++++++++++- 13 files changed, 412 insertions(+), 8 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index c08c7b3cde48..b4c9aec5ea50 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -502,8 +502,12 @@ type Date ## Compares two Dates for equality. == : Date -> Boolean == self that = - sign = Time_Utils.compare_to_localdate self that - 0 == sign + case that of + Date -> + sign = Time_Utils.compare_to_localdate self that + 0 == sign + _ -> + False ## PRIVATE week_days_between start end = diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index 6626de84d837..82ab926be374 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -564,5 +564,9 @@ type Date_Time ## Compares two Date_Time for equality. == : Date_Time -> Boolean == self that = - sign = Time_Utils.compare_to_zoneddatetime self that - 0 == sign + case that of + Date_Time -> + sign = Time_Utils.compare_to_zoneddatetime self that + 0 == sign + _ -> + False diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index 4020e65e48a0..169c4a91dea4 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -330,5 +330,9 @@ type Time_Of_Day ## Compares two Time_Of_Day for equality. == : Date -> Boolean == self that = - sign = Time_Utils.compare_to_localtime self that - 0 == sign + case that of + Time_Of_Day -> + sign = Time_Utils.compare_to_localtime self that + 0 == sign + _ -> + False diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso index 51e558187cda..4d17f2d7cd99 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso @@ -140,4 +140,7 @@ type Time_Zone ## Compares two Zones for equality. == : Time_Zone -> Boolean - == self that = Time_Utils.equals_zone self that + == self that = + case that of + Time_Zone -> Time_Utils.equals_zone self that + _ -> False diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index 4f1124cadcbb..3a0e732095f2 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -343,6 +343,40 @@ java_instance_check value typ = is_an : Any -> Any -> Boolean is_an value typ = is_a value typ +## UNSTABLE + ADVANCED + + Returns the type of the given value. + + Arguments: + - value: the value to get the type of. +type_of : Any -> Any +type_of value = + if is_error value then Base.Error else + case value of + Base.Polyglot -> Meta.type_of_polyglot_builtin value + Integer -> Integer + Decimal -> Decimal + Number -> Number + _ -> + Meta.type_of_builtin value + +## PRIVATE + + Returns the type of the polyglot value. + + Arguments: + - value: the value to get the type of. +type_of_polyglot_builtin value = @Builtin_Method "Meta.type_of_polyglot_builtin" + +## PRIVATE + + A builtin method returning the type of a non-polyglot value. + + Arguments: + - value: the value to get the type of. +type_of_builtin value = @Builtin_Method "Meta.type_of_builtin" + ## Represents a polyglot language. type Language diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java new file mode 100644 index 000000000000..5d58635548ef --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java @@ -0,0 +1,59 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.data.EnsoDate; + +/** An implementation of the case expression specialised to working on Date. */ +@NodeInfo(shortName = "DateMatch") +public abstract class DateBranchNode extends BranchNode { + private final Type date; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + DateBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.date = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param date the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static DateBranchNode build(Type date, RootCallTarget branch) { + return DateBranchNodeGen.create(date, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(date == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoDate(VirtualFrame frame, Object state, EnsoDate date) { + accept(frame, state, new Object[0]); + } + + @Specialization(guards = "interop.isDate(date)") + void doDate( + VirtualFrame frame, + Object state, + Object date, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java new file mode 100644 index 000000000000..55f0ce2e7047 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java @@ -0,0 +1,64 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.EnsoDateTime; +import org.enso.interpreter.runtime.data.Type; + +/** An implementation of the case expression specialised to working on Date_Time. */ +@NodeInfo(shortName = "DateMatch") +public abstract class DateTimeBranchNode extends BranchNode { + private final Type dateTime; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + DateTimeBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.dateTime = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param dateTime the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static DateTimeBranchNode build(Type dateTime, RootCallTarget branch) { + return DateTimeBranchNodeGen.create(dateTime, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(dateTime == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoDateTime(VirtualFrame frame, Object state, EnsoDateTime dateTime) { + accept(frame, state, new Object[0]); + } + + @Specialization( + guards = { + "interop.isDate(dateTime)", + "interop.isTime(dateTime)", + "interop.isTimeZone(dateTime)" + }) + void doDateTime( + VirtualFrame frame, + Object state, + Object dateTime, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java new file mode 100644 index 000000000000..204420352e18 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java @@ -0,0 +1,59 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.EnsoTimeOfDay; +import org.enso.interpreter.runtime.data.Type; + +/** An implementation of the case expression specialised to working on Time_Of_Day. */ +@NodeInfo(shortName = "TimeOfDayMatch") +public abstract class TimeOfDayBranchNode extends BranchNode { + private final Type timeOfDay; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + TimeOfDayBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.timeOfDay = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param timeOfDay the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static TimeOfDayBranchNode build(Type timeOfDay, RootCallTarget branch) { + return TimeOfDayBranchNodeGen.create(timeOfDay, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(timeOfDay == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoTimeOfDay(VirtualFrame frame, Object state, EnsoTimeOfDay timeOfDay) { + accept(frame, state, new Object[0]); + } + + @Specialization(guards = "interop.isTime(timeOfDay)") + void doTime( + VirtualFrame frame, + Object state, + Object timeOfDay, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java new file mode 100644 index 000000000000..c7888683bf68 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java @@ -0,0 +1,59 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.EnsoTimeZone; +import org.enso.interpreter.runtime.data.Type; + +/** An implementation of the case expression specialised to working on Time_Zone. */ +@NodeInfo(shortName = "DateMatch") +public abstract class TimeZoneBranchNode extends BranchNode { + private final Type timeZone; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + TimeZoneBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.timeZone = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param timeZone the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static TimeZoneBranchNode build(Type timeZone, RootCallTarget branch) { + return TimeZoneBranchNodeGen.create(timeZone, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(timeZone == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoZone(VirtualFrame frame, Object state, EnsoTimeZone timeZone) { + accept(frame, state, new Object[0]); + } + + @Specialization(guards = "interop.isTimeZone(zone)") + void doZone( + VirtualFrame frame, + Object state, + Object zone, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java new file mode 100644 index 000000000000..436a9122e185 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -0,0 +1,27 @@ +package org.enso.interpreter.node.expression.builtin.meta; + +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; + +@BuiltinMethod( + type = "Meta", + name = "type_of_builtin", + description = "Returns the type of a value.") +public class TypeOfNode extends Node { + private @Child TypesLibrary types = + TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + Object execute(Object value) { + if (types.hasType(value)) { + return types.getType(value); + } else { + Context ctx = Context.get(this); + return ctx.getBuiltins().any(); + } + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java new file mode 100644 index 000000000000..3983e5472e9e --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java @@ -0,0 +1,37 @@ +package org.enso.interpreter.node.expression.builtin.meta; + +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.builtin.Builtins; +import org.enso.interpreter.runtime.error.PanicException; + +@BuiltinMethod( + type = "Meta", + name = "type_of_polyglot_builtin", + description = "Returns the type of a polyglot value.") +public class TypeOfPolyglotNode extends Node { + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + Object execute(Object value) { + if (library.hasMetaObject(value)) { + try { + return library.getMetaObject(value); + } catch (UnsupportedMessageException e) { + err.enter(); + Builtins builtins = Context.get(this).getBuiltins(); + throw new PanicException( + builtins.error().makeTypeError(builtins.any(), value, "object"), this); + } + } else { + Context ctx = Context.get(this); + return ctx.getBuiltins().any(); + } + } +} diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 77125559c11c..c8c6befc2b26 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -951,6 +951,10 @@ class IrToTruffle( val any = context.getBuiltins.any val array = context.getBuiltins.array val vector = context.getBuiltins.vector + val date = context.getBuiltins.date + val dateTime = context.getBuiltins.dateTime + val timeOfDay = context.getBuiltins.timeOfDay + val timeZone = context.getBuiltins.timeZone val file = context.getBuiltins.file val builtinBool = context.getBuiltins.bool.getType val number = context.getBuiltins.number @@ -976,6 +980,14 @@ class IrToTruffle( ArrayBranchNode.build(tpe, branchCodeNode.getCallTarget) } else if (tpe == vector) { VectorBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == date) { + DateBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == dateTime) { + DateTimeBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == timeOfDay) { + TimeOfDayBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == timeZone) { + TimeZoneBranchNode.build(tpe, branchCodeNode.getCallTarget) } else if (tpe == file) { FileBranchNode.build(tpe, branchCodeNode.getCallTarget) } else if (tpe == polyglot) { diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index cceb7e36eba8..2f116b144e42 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -3,6 +3,10 @@ import Standard.Base import Standard.Base.System.Platform +polyglot java import java.lang.Object as JObject +polyglot java import java.lang.Exception as JException +polyglot java import java.io.IOException +polyglot java import java.util.ArrayList polyglot java import java.util.Random polyglot java import java.util.Locale as JavaLocale @@ -80,12 +84,46 @@ spec = Test.group "Meta-Value Manipulation" <| Meta.is_an err Error . should_be_true Meta.is_a err Text . should_be_false + Test.specify "should allow for returning the type of a value" <| + Meta.type_of 42 . should_equal Integer + Meta.type_of 2.81 . should_equal Decimal + Meta.type_of 2.81 . should_not_equal Integer + + # Can't compare like + # Meta.type_of [1,2,3] . should_equal Vector.Vector + # because it tries to compare lengths, where RHS is a type + Meta.is_a (Meta.type_of [1,2,3]) Vector.Vector . should_be_true + + Meta.type_of "foo" . should_equal Text + Meta.type_of "0" . should_not_equal Integer + + Meta.type_of True . should_equal Boolean + Meta.type_of False . should_not_equal Any + + Meta.is_a (Meta.type_of Date.now) Date.Date . should_be_true + Meta.is_a (Meta.type_of Date_Time.now) Date_Time.Date_Time . should_be_true + Meta.is_a (Meta.type_of Date_Time.now) Date.Date . should_be_false + Meta.is_a (Meta.type_of Time_Of_Day.now) Time_Of_Day.Time_Of_Day . should_be_true + Meta.is_a (Meta.type_of Time_Of_Day.now) Date.Date . should_be_false + Meta.is_a (Meta.type_of Date_Time.now.zone) Time_Zone.Time_Zone . should_be_true + Meta.is_a (Meta.type_of Date_Time.now.zone) Date.Date . should_be_false + + list = ArrayList.new + list.add 123 + Meta.type_of list . should_not_equal JObject + Meta.type_of list . should_equal ArrayList + + e = IOException.new "meh" + Meta.type_of list . should_not_equal JObject + Meta.type_of e . should_equal IOException + Meta.type_of e . should_not_equal JException + location_pending = case Platform.os of Platform.Windows -> "This test is disabled on Windows until issue 1561 is fixed." _ -> Nothing Test.specify "should allow to get the source location of a frame" pending=location_pending <| src = Meta.get_source_location 0 - loc = "Meta_Spec.enso:87:15-40" + loc = "Meta_Spec.enso:125:15-40" src.take (Last loc.length) . should_equal loc Test.specify "should allow to get qualified type names of values" <| From 0b2c25aea4ce8fc9fc7ad5b71b7f2d9a4b97ac52 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 22 Sep 2022 15:13:38 +0200 Subject: [PATCH 02/10] Added is_of_type method to easier test types Addressed PR comments - eliminated `Meta.type_of_polyglot_builtin` and merged its implementation into a single node. Added `is_of_type` to avoid using `==` checks on types, which doesn't work as expected, half the time. --- .../lib/Standard/Base/0.0.0-dev/src/Meta.enso | 33 ++++++++--- .../builtin/meta/IsOfTypeMetaNode.java | 38 ++++++++++++ .../expression/builtin/meta/TypeOfNode.java | 26 +++++++-- .../builtin/meta/TypeOfPolyglotNode.java | 37 ------------ .../enso/compiler/codegen/IrToTruffle.scala | 58 ++++--------------- test/Tests/src/Semantic/Meta_Spec.enso | 51 +++++++++++----- 6 files changed, 132 insertions(+), 111 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index 3a0e732095f2..ada8f9b43efc 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -270,6 +270,17 @@ Any.is_a self typ = is_a self typ Any.is_an : Any -> Boolean Any.is_an self typ = is_a self typ +## UNSTABLE + ADVANCED + PRIVATE + + Checks if `self` type matches `typ` type. + + Arguments: + - typ: The type to check `self` against. +Any.is_of_type : Any -> Boolean +Any.is_of_type self typ = is_of_type self typ + ## UNSTABLE ADVANCED @@ -303,12 +314,17 @@ is_a value typ = if is_same_object value typ then True else if typ == Any then True else if is_error value then typ == Base.Error else case value of - Array -> typ == Array + Vector.Vector -> typ.is_of_type Vector.Vector + Array -> typ.is_of_type Array Boolean -> if typ == Boolean then True else value == typ Text -> typ == Text Number -> if typ == Number then True else case value of Integer -> typ == Integer Decimal -> typ == Decimal + Date_Time.Date_Time -> typ.is_of_type Date_Time.Date_Time + Date.Date -> typ.is_of_type Date.Date + Time_Of_Day.Time_Of_Day -> typ.is_of_type Time_Of_Day.Time_Of_Day + Time_Zone.Time_Zone -> typ.is_of_type Time_Zone.Time_Zone Base.Polyglot -> typ==Base.Polyglot || java_instance_check value typ _ -> @@ -354,24 +370,27 @@ type_of : Any -> Any type_of value = if is_error value then Base.Error else case value of - Base.Polyglot -> Meta.type_of_polyglot_builtin value Integer -> Integer Decimal -> Decimal Number -> Number _ -> Meta.type_of_builtin value -## PRIVATE +## UNSTABLE + ADVANCED + PRIVATE - Returns the type of the polyglot value. + Checks if the given type agrees with the expected one Arguments: - - value: the value to get the type of. -type_of_polyglot_builtin value = @Builtin_Method "Meta.type_of_polyglot_builtin" + - tpe: the type to check + - expected: the expected type +is_of_type : Any -> Any -> Boolean +is_of_type tpe expected = @Builtin_Method "Meta.is_of_type_builtin" ## PRIVATE - A builtin method returning the type of a non-polyglot value. + A builtin method returning the type of a (polyglot or non-polyglot) value. Arguments: - value: the value to get the type of. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java new file mode 100644 index 000000000000..a607a592d88b --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java @@ -0,0 +1,38 @@ +package org.enso.interpreter.node.expression.builtin.meta; + +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.BranchProfile; +import org.enso.interpreter.Constants; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.builtin.Builtins; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; + +@BuiltinMethod( + type = "Meta", + name = "is_of_type_builtin", + description = "Checks if the given type matches the expected type.") +public class IsOfTypeMetaNode extends Node { + private @Child TypesLibrary types = + TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private @Child + InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); + + Object execute(Object tpe, Object expected) { + if (library.isMetaObject(tpe) && library.isMetaObject(expected)) { + return library.isIdentical(tpe, expected, InteropLibrary.getUncached()); + } else { + if (types.hasType(tpe) && types.hasType(expected)) { + return types.getType(tpe) == types.getType(expected); + } else { + Context ctx = Context.get(this); + return false; + } + } + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java index 436a9122e185..39c5bdda0baf 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -1,10 +1,14 @@ package org.enso.interpreter.node.expression.builtin.meta; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; import org.enso.interpreter.Constants; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.builtin.Builtins; +import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; @BuiltinMethod( @@ -12,16 +16,30 @@ name = "type_of_builtin", description = "Returns the type of a value.") public class TypeOfNode extends Node { + private @Child + InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); private @Child TypesLibrary types = TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); private final BranchProfile err = BranchProfile.create(); Object execute(Object value) { - if (types.hasType(value)) { - return types.getType(value); + if (library.hasMetaObject(value)) { + try { + return library.getMetaObject(value); + } catch (UnsupportedMessageException e) { + err.enter(); + Builtins builtins = Context.get(this).getBuiltins(); + throw new PanicException( + builtins.error().makeTypeError(builtins.any(), value, "object"), this); + } } else { - Context ctx = Context.get(this); - return ctx.getBuiltins().any(); + if (types.hasType(value)) { + return types.getType(value); + } else { + Context ctx = Context.get(this); + return ctx.getBuiltins().any(); + } } } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java deleted file mode 100644 index 3983e5472e9e..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfPolyglotNode.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.meta; - -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.BranchProfile; -import org.enso.interpreter.Constants; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.runtime.Context; -import org.enso.interpreter.runtime.builtin.Builtins; -import org.enso.interpreter.runtime.error.PanicException; - -@BuiltinMethod( - type = "Meta", - name = "type_of_polyglot_builtin", - description = "Returns the type of a polyglot value.") -public class TypeOfPolyglotNode extends Node { - private @Child InteropLibrary library = - InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - private final BranchProfile err = BranchProfile.create(); - - Object execute(Object value) { - if (library.hasMetaObject(value)) { - try { - return library.getMetaObject(value); - } catch (UnsupportedMessageException e) { - err.enter(); - Builtins builtins = Context.get(this).getBuiltins(); - throw new PanicException( - builtins.error().makeTypeError(builtins.any(), value, "object"), this); - } - } else { - Context ctx = Context.get(this); - return ctx.getBuiltins().any(); - } - } -} diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index c8c6befc2b26..85a61b165880 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -10,67 +10,29 @@ import org.enso.compiler.data.{BindingsMap, CompilerConfig} import org.enso.compiler.exception.{BadPatternMatch, CompilerError} import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Scope => AliasScope} import org.enso.compiler.pass.analyse.AliasAnalysis.{Graph => AliasGraph} -import org.enso.compiler.pass.analyse.{ - AliasAnalysis, - BindingAnalysis, - DataflowAnalysis, - TailCall -} +import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis, DataflowAnalysis, TailCall} import org.enso.compiler.pass.optimise.ApplicationSaturation -import org.enso.compiler.pass.resolve.{ - ExpressionAnnotations, - GlobalNames, - MethodDefinitions, - Patterns -} +import org.enso.compiler.pass.resolve.{ExpressionAnnotations, GlobalNames, MethodDefinitions, Patterns} import org.enso.interpreter.epb.EpbParser import org.enso.interpreter.node.callable.argument.ReadArgumentNode -import org.enso.interpreter.node.callable.function.{ - BlockNode, - CreateFunctionNode -} +import org.enso.interpreter.node.callable.function.{BlockNode, CreateFunctionNode} import org.enso.interpreter.node.callable.thunk.{CreateThunkNode, ForceNode} -import org.enso.interpreter.node.callable.{ - ApplicationNode, - InvokeCallableNode, - SequenceLiteralNode -} +import org.enso.interpreter.node.callable.{ApplicationNode, InvokeCallableNode, SequenceLiteralNode} import org.enso.interpreter.node.controlflow.caseexpr._ -import org.enso.interpreter.node.expression.atom.{ - ConstantNode, - QualifiedAccessorNode -} +import org.enso.interpreter.node.expression.atom.{ConstantNode, QualifiedAccessorNode} import org.enso.interpreter.node.expression.constant._ import org.enso.interpreter.node.expression.foreign.ForeignMethodCallNode import org.enso.interpreter.node.expression.literal.LiteralNode import org.enso.interpreter.node.scope.{AssignmentNode, ReadLocalVariableNode} -import org.enso.interpreter.node.{ - BaseNode, - ClosureRootNode, - MethodRootNode, - ExpressionNode => RuntimeExpression -} +import org.enso.interpreter.node.{BaseNode, ClosureRootNode, MethodRootNode, ExpressionNode => RuntimeExpression} import org.enso.interpreter.runtime.Context -import org.enso.interpreter.runtime.callable.{ - UnresolvedConversion, - UnresolvedSymbol -} -import org.enso.interpreter.runtime.callable.argument.{ - ArgumentDefinition, - CallArgument -} +import org.enso.interpreter.runtime.callable.{UnresolvedConversion, UnresolvedSymbol} +import org.enso.interpreter.runtime.callable.argument.{ArgumentDefinition, CallArgument} import org.enso.interpreter.runtime.callable.atom.Atom import org.enso.interpreter.runtime.callable.atom.AtomConstructor -import org.enso.interpreter.runtime.callable.function.{ - FunctionSchema, - Function => RuntimeFunction -} +import org.enso.interpreter.runtime.callable.function.{FunctionSchema, Function => RuntimeFunction} import org.enso.interpreter.runtime.data.text.Text -import org.enso.interpreter.runtime.scope.{ - FramePointer, - LocalScope, - ModuleScope -} +import org.enso.interpreter.runtime.scope.{FramePointer, LocalScope, ModuleScope} import org.enso.interpreter.{Constants, Language} import java.math.BigInteger diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index 2f116b144e42..e53fb4469f93 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -50,6 +50,8 @@ spec = Test.group "Meta-Value Manipulation" <| Array.is_an Array . should_be_true [].to_array.is_an Array . should_be_true [].to_array.is_a Decimal . should_be_false + [1,2,3].is_a Vector.Vector . should_be_true + [1,2,3].is_a Array . should_be_false Boolean.is_a Boolean . should_be_true True.is_a Boolean . should_be_true @@ -69,6 +71,8 @@ spec = Test.group "Meta-Value Manipulation" <| 1.0.is_an Integer . should_be_false 1.0.is_a Text . should_be_false + + random_gen = Random.new Meta.is_a random_gen Polyglot . should_be_true Meta.is_an random_gen Integer . should_be_false @@ -84,6 +88,15 @@ spec = Test.group "Meta-Value Manipulation" <| Meta.is_an err Error . should_be_true Meta.is_a err Text . should_be_false + Meta.is_a Date.now Date.Date . should_be_true + Meta.is_a Date_Time.now Date_Time.Date_Time . should_be_true + Meta.is_a Date_Time.now Date.Date . should_be_false + Meta.is_a Time_Of_Day.now Time_Of_Day.Time_Of_Day . should_be_true + Meta.is_a Time_Of_Day.now Date.Date . should_be_false + Meta.is_a Date_Time.now.zone Time_Zone.Time_Zone . should_be_true + Meta.is_a Date_Time.now.zone Date.Date . should_be_false + + Test.specify "should allow for returning the type of a value" <| Meta.type_of 42 . should_equal Integer Meta.type_of 2.81 . should_equal Decimal @@ -91,8 +104,11 @@ spec = Test.group "Meta-Value Manipulation" <| # Can't compare like # Meta.type_of [1,2,3] . should_equal Vector.Vector - # because it tries to compare lengths, where RHS is a type - Meta.is_a (Meta.type_of [1,2,3]) Vector.Vector . should_be_true + # because equality check tries to compare lengths, where RHS is a type + v_tpe = Meta.type_of [1,2,3] + v_tpe.is_of_type Vector.Vector . should_be_true + v_tpe.is_of_type Array . should_be_false + [1,2,3].to_array.is_of_type Array . should_be_true Meta.type_of "foo" . should_equal Text Meta.type_of "0" . should_not_equal Integer @@ -100,30 +116,35 @@ spec = Test.group "Meta-Value Manipulation" <| Meta.type_of True . should_equal Boolean Meta.type_of False . should_not_equal Any - Meta.is_a (Meta.type_of Date.now) Date.Date . should_be_true - Meta.is_a (Meta.type_of Date_Time.now) Date_Time.Date_Time . should_be_true - Meta.is_a (Meta.type_of Date_Time.now) Date.Date . should_be_false - Meta.is_a (Meta.type_of Time_Of_Day.now) Time_Of_Day.Time_Of_Day . should_be_true - Meta.is_a (Meta.type_of Time_Of_Day.now) Date.Date . should_be_false - Meta.is_a (Meta.type_of Date_Time.now.zone) Time_Zone.Time_Zone . should_be_true - Meta.is_a (Meta.type_of Date_Time.now.zone) Date.Date . should_be_false + (Meta.type_of Date.now).is_of_type Date.Date . should_be_true + (Meta.type_of Date.now).is_of_type Date_Time.Date_Time . should_be_false + (Meta.type_of Date_Time.now).is_of_type Date_Time.Date_Time . should_be_true + (Meta.type_of Date_Time.now).is_of_type Date.Date . should_be_false + (Meta.type_of Time_Of_Day.now).is_of_type Time_Of_Day.Time_Of_Day . should_be_true + (Meta.type_of Time_Of_Day.now).is_of_type Date.Date . should_be_false + (Meta.type_of Date_Time.now.zone).is_of_type Time_Zone.Time_Zone . should_be_true + (Meta.type_of Date_Time.now.zone).is_of_type Date.Date . should_be_false list = ArrayList.new list.add 123 - Meta.type_of list . should_not_equal JObject - Meta.type_of list . should_equal ArrayList + list_tpe = Meta.type_of list + list_tpe . should_not_equal JObject + list_tpe . should_equal ArrayList + list_tpe.is_of_type ArrayList . should_be_true e = IOException.new "meh" - Meta.type_of list . should_not_equal JObject - Meta.type_of e . should_equal IOException - Meta.type_of e . should_not_equal JException + e_tpe = Meta.type_of e + e_tpe . should_equal IOException + e_tpe . should_not_equal JException + e_tpe.is_of_type IOException . should_be_true + e_tpe.is_of_type JException . should_be_false location_pending = case Platform.os of Platform.Windows -> "This test is disabled on Windows until issue 1561 is fixed." _ -> Nothing Test.specify "should allow to get the source location of a frame" pending=location_pending <| src = Meta.get_source_location 0 - loc = "Meta_Spec.enso:125:15-40" + loc = "Meta_Spec.enso:146:15-40" src.take (Last loc.length) . should_equal loc Test.specify "should allow to get qualified type names of values" <| From e42ffe49e5cb2b66a8d4e3855fc2f33e83ebaa42 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 22 Sep 2022 15:49:18 +0200 Subject: [PATCH 03/10] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0580ebe59fd9..b3bd6a0789e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,6 +198,8 @@ - [Added `databases`, `schemas`, `tables` support to database Connection.][3632] - [Implemented `start_of` and `end_of` methods for date/time types allowing to find start and end of a period of time containing the provided time.][3695] +- [Implemented `type_of` and `is_of_type` methods for getting the type of a + value and comparing types, respectively][3722] - [Implemented `work_days_until` for counting work dys between dates and `add_work_days` which allows to shift a date by a number of work days.][3726] - [Added `query` and `read` functions to Database connections.][3727] @@ -320,6 +322,7 @@ [3684]: https://github.com/enso-org/enso/pull/3684 [3691]: https://github.com/enso-org/enso/pull/3691 [3695]: https://github.com/enso-org/enso/pull/3695 +[3722]: https://github.com/enso-org/enso/pull/3722 [3726]: https://github.com/enso-org/enso/pull/3726 [3727]: https://github.com/enso-org/enso/pull/3727 [3733]: https://github.com/enso-org/enso/pull/3733 From 639487ae524880d8d6fe53aa25a13010e85c2890 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 22 Sep 2022 16:07:58 +0200 Subject: [PATCH 04/10] formatting --- .../builtin/meta/IsOfTypeMetaNode.java | 39 ++++++------- .../expression/builtin/meta/TypeOfNode.java | 7 +-- .../enso/compiler/codegen/IrToTruffle.scala | 58 +++++++++++++++---- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java index a607a592d88b..9f7b4d881b20 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java @@ -12,27 +12,26 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; @BuiltinMethod( - type = "Meta", - name = "is_of_type_builtin", - description = "Checks if the given type matches the expected type.") + type = "Meta", + name = "is_of_type_builtin", + description = "Checks if the given type matches the expected type.") public class IsOfTypeMetaNode extends Node { - private @Child TypesLibrary types = - TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - private @Child - InteropLibrary library = - InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - private final BranchProfile err = BranchProfile.create(); + private @Child TypesLibrary types = + TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private final BranchProfile err = BranchProfile.create(); - Object execute(Object tpe, Object expected) { - if (library.isMetaObject(tpe) && library.isMetaObject(expected)) { - return library.isIdentical(tpe, expected, InteropLibrary.getUncached()); - } else { - if (types.hasType(tpe) && types.hasType(expected)) { - return types.getType(tpe) == types.getType(expected); - } else { - Context ctx = Context.get(this); - return false; - } - } + Object execute(Object tpe, Object expected) { + if (library.isMetaObject(tpe) && library.isMetaObject(expected)) { + return library.isIdentical(tpe, expected, InteropLibrary.getUncached()); + } else { + if (types.hasType(tpe) && types.hasType(expected)) { + return types.getType(tpe) == types.getType(expected); + } else { + Context ctx = Context.get(this); + return false; + } } + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java index 39c5bdda0baf..b19d0e9a7bfa 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -16,9 +16,8 @@ name = "type_of_builtin", description = "Returns the type of a value.") public class TypeOfNode extends Node { - private @Child - InteropLibrary library = - InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); + private @Child InteropLibrary library = + InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); private @Child TypesLibrary types = TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); private final BranchProfile err = BranchProfile.create(); @@ -31,7 +30,7 @@ Object execute(Object value) { err.enter(); Builtins builtins = Context.get(this).getBuiltins(); throw new PanicException( - builtins.error().makeTypeError(builtins.any(), value, "object"), this); + builtins.error().makeTypeError(builtins.any(), value, "object"), this); } } else { if (types.hasType(value)) { diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 85a61b165880..c8c6befc2b26 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -10,29 +10,67 @@ import org.enso.compiler.data.{BindingsMap, CompilerConfig} import org.enso.compiler.exception.{BadPatternMatch, CompilerError} import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Scope => AliasScope} import org.enso.compiler.pass.analyse.AliasAnalysis.{Graph => AliasGraph} -import org.enso.compiler.pass.analyse.{AliasAnalysis, BindingAnalysis, DataflowAnalysis, TailCall} +import org.enso.compiler.pass.analyse.{ + AliasAnalysis, + BindingAnalysis, + DataflowAnalysis, + TailCall +} import org.enso.compiler.pass.optimise.ApplicationSaturation -import org.enso.compiler.pass.resolve.{ExpressionAnnotations, GlobalNames, MethodDefinitions, Patterns} +import org.enso.compiler.pass.resolve.{ + ExpressionAnnotations, + GlobalNames, + MethodDefinitions, + Patterns +} import org.enso.interpreter.epb.EpbParser import org.enso.interpreter.node.callable.argument.ReadArgumentNode -import org.enso.interpreter.node.callable.function.{BlockNode, CreateFunctionNode} +import org.enso.interpreter.node.callable.function.{ + BlockNode, + CreateFunctionNode +} import org.enso.interpreter.node.callable.thunk.{CreateThunkNode, ForceNode} -import org.enso.interpreter.node.callable.{ApplicationNode, InvokeCallableNode, SequenceLiteralNode} +import org.enso.interpreter.node.callable.{ + ApplicationNode, + InvokeCallableNode, + SequenceLiteralNode +} import org.enso.interpreter.node.controlflow.caseexpr._ -import org.enso.interpreter.node.expression.atom.{ConstantNode, QualifiedAccessorNode} +import org.enso.interpreter.node.expression.atom.{ + ConstantNode, + QualifiedAccessorNode +} import org.enso.interpreter.node.expression.constant._ import org.enso.interpreter.node.expression.foreign.ForeignMethodCallNode import org.enso.interpreter.node.expression.literal.LiteralNode import org.enso.interpreter.node.scope.{AssignmentNode, ReadLocalVariableNode} -import org.enso.interpreter.node.{BaseNode, ClosureRootNode, MethodRootNode, ExpressionNode => RuntimeExpression} +import org.enso.interpreter.node.{ + BaseNode, + ClosureRootNode, + MethodRootNode, + ExpressionNode => RuntimeExpression +} import org.enso.interpreter.runtime.Context -import org.enso.interpreter.runtime.callable.{UnresolvedConversion, UnresolvedSymbol} -import org.enso.interpreter.runtime.callable.argument.{ArgumentDefinition, CallArgument} +import org.enso.interpreter.runtime.callable.{ + UnresolvedConversion, + UnresolvedSymbol +} +import org.enso.interpreter.runtime.callable.argument.{ + ArgumentDefinition, + CallArgument +} import org.enso.interpreter.runtime.callable.atom.Atom import org.enso.interpreter.runtime.callable.atom.AtomConstructor -import org.enso.interpreter.runtime.callable.function.{FunctionSchema, Function => RuntimeFunction} +import org.enso.interpreter.runtime.callable.function.{ + FunctionSchema, + Function => RuntimeFunction +} import org.enso.interpreter.runtime.data.text.Text -import org.enso.interpreter.runtime.scope.{FramePointer, LocalScope, ModuleScope} +import org.enso.interpreter.runtime.scope.{ + FramePointer, + LocalScope, + ModuleScope +} import org.enso.interpreter.{Constants, Language} import java.math.BigInteger From bb6560e55660b72a96068b56872798976e5d1758 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 22 Sep 2022 16:11:02 +0200 Subject: [PATCH 05/10] PR comment --- .../interpreter/node/expression/builtin/meta/TypeOfNode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java index b19d0e9a7bfa..6dd81ac59c3a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.meta; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.nodes.Node; @@ -20,14 +21,13 @@ public class TypeOfNode extends Node { InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); private @Child TypesLibrary types = TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - private final BranchProfile err = BranchProfile.create(); Object execute(Object value) { if (library.hasMetaObject(value)) { try { return library.getMetaObject(value); } catch (UnsupportedMessageException e) { - err.enter(); + CompilerDirectives.transferToInterpreter(); Builtins builtins = Context.get(this).getBuiltins(); throw new PanicException( builtins.error().makeTypeError(builtins.any(), value, "object"), this); From 013877d2c7c0e2b9aa9cb06f0263eb51065fe898 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 23 Sep 2022 21:11:28 +0200 Subject: [PATCH 06/10] Use `Meta.is_same_object` to test type equality Removed `is_of_type` and moved some of the logic to `is_same_object`. Turns out the latter was actually broken when comparing polyglot values with the same underlying java class. Additionally, discovered why I can't simply use `==` - the dispatch for types is broken and it's a known "feature". This will be improved in the upcoming statics change so I'm not going to duplicate that work. Once in, we can most likely get rid of `should_equal_type` and `should_not_equal_type`. Addressed comments re date/time-related branches in pattern matching so that it doesn't try to do semi-subtyping checks but rather checks for equality. Moved all logic of `type_of` to the builtin node. Added tests for `type_of` of Date, Date_Time, Time_Of_Day and Time_Zone. Might still change `type_of` to ensure that java classes aren't leaking. --- CHANGELOG.md | 2 +- .../lib/Standard/Base/0.0.0-dev/src/Meta.enso | 61 ++++---------- .../lib/Standard/Test/0.0.0-dev/src/Main.enso | 44 ++++++++++ .../controlflow/caseexpr/DateBranchNode.java | 2 +- .../caseexpr/DateTimeBranchNode.java | 1 - .../caseexpr/TimeOfDayBranchNode.java | 2 +- .../caseexpr/TimeZoneBranchNode.java | 3 +- .../builtin/meta/IsOfTypeMetaNode.java | 37 --------- .../builtin/meta/IsSameObjectNode.java | 21 ++++- .../expression/builtin/meta/TypeOfNode.java | 83 +++++++++++++------ .../runtime/type/TypesFromProxy.java | 10 +++ test/Tests/src/Data/Time/Date_Spec.enso | 17 ++-- test/Tests/src/Data/Time/Date_Time_Spec.enso | 17 ++-- .../Tests/src/Data/Time/Time_Of_Day_Spec.enso | 13 ++- test/Tests/src/Data/Time/Time_Zone_Spec.enso | 7 ++ test/Tests/src/Semantic/Meta_Spec.enso | 59 ++++++------- 16 files changed, 225 insertions(+), 154 deletions(-) delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java diff --git a/CHANGELOG.md b/CHANGELOG.md index b3bd6a0789e0..b6c019a1c695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -199,7 +199,7 @@ - [Implemented `start_of` and `end_of` methods for date/time types allowing to find start and end of a period of time containing the provided time.][3695] - [Implemented `type_of` and `is_of_type` methods for getting the type of a - value and comparing types, respectively][3722] + value and comparing types, respectively.][3722] - [Implemented `work_days_until` for counting work dys between dates and `add_work_days` which allows to shift a date by a number of work days.][3726] - [Added `query` and `read` functions to Database connections.][3727] diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index ada8f9b43efc..e941a17cb307 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -253,12 +253,13 @@ is_same_object value_1 value_2 = @Builtin_Method "Meta.is_same_object" ## UNSTABLE ADVANCED - Checks if `self` is an instance of `typ`. + Checks whether `self` represents the same underlying reference as `value`. Arguments: - - typ: The type to check `self` against. -Any.is_a : Any -> Boolean -Any.is_a self typ = is_a self typ + - value_1: The first value. + - value_2: The second value. +Any.is_same_object_as : Any -> Boolean +Any.is_same_object_as self value = is_same_object self value ## UNSTABLE ADVANCED @@ -267,19 +268,18 @@ Any.is_a self typ = is_a self typ Arguments: - typ: The type to check `self` against. -Any.is_an : Any -> Boolean -Any.is_an self typ = is_a self typ +Any.is_a : Any -> Boolean +Any.is_a self typ = is_a self typ ## UNSTABLE ADVANCED - PRIVATE - Checks if `self` type matches `typ` type. + Checks if `self` is an instance of `typ`. Arguments: - typ: The type to check `self` against. -Any.is_of_type : Any -> Boolean -Any.is_of_type self typ = is_of_type self typ +Any.is_an : Any -> Boolean +Any.is_an self typ = is_a self typ ## UNSTABLE ADVANCED @@ -314,17 +314,17 @@ is_a value typ = if is_same_object value typ then True else if typ == Any then True else if is_error value then typ == Base.Error else case value of - Vector.Vector -> typ.is_of_type Vector.Vector - Array -> typ.is_of_type Array + Vector.Vector -> typ.is_same_object_as Vector.Vector + Array -> typ.is_same_object_as Array Boolean -> if typ == Boolean then True else value == typ Text -> typ == Text Number -> if typ == Number then True else case value of Integer -> typ == Integer Decimal -> typ == Decimal - Date_Time.Date_Time -> typ.is_of_type Date_Time.Date_Time - Date.Date -> typ.is_of_type Date.Date - Time_Of_Day.Time_Of_Day -> typ.is_of_type Time_Of_Day.Time_Of_Day - Time_Zone.Time_Zone -> typ.is_of_type Time_Zone.Time_Zone + Date_Time.Date_Time -> typ.is_same_object_as Date_Time.Date_Time + Date.Date -> typ.is_same_object_as Date.Date + Time_Of_Day.Time_Of_Day -> typ.is_same_object_as Time_Of_Day.Time_Of_Day + Time_Zone.Time_Zone -> typ.is_same_object_as Time_Zone.Time_Zone Base.Polyglot -> typ==Base.Polyglot || java_instance_check value typ _ -> @@ -367,34 +367,7 @@ is_an value typ = is_a value typ Arguments: - value: the value to get the type of. type_of : Any -> Any -type_of value = - if is_error value then Base.Error else - case value of - Integer -> Integer - Decimal -> Decimal - Number -> Number - _ -> - Meta.type_of_builtin value - -## UNSTABLE - ADVANCED - PRIVATE - - Checks if the given type agrees with the expected one - - Arguments: - - tpe: the type to check - - expected: the expected type -is_of_type : Any -> Any -> Boolean -is_of_type tpe expected = @Builtin_Method "Meta.is_of_type_builtin" - -## PRIVATE - - A builtin method returning the type of a (polyglot or non-polyglot) value. - - Arguments: - - value: the value to get the type of. -type_of_builtin value = @Builtin_Method "Meta.type_of_builtin" +type_of value = @Builtin_Method "Meta.type_of_builtin" ## Represents a polyglot language. type Language diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso index 1c99434ca78c..3d62f4518dd9 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso @@ -286,6 +286,28 @@ Any.should_equal self that frames_to_skip=0 = case self == that of msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")." fail msg +## Asserts that `self` value is equal to the expected type value. + + Arguments: + - that: The type to check `self` for equality with. + - frames_to_skip (optional, advanced): used to alter the location which is + displayed as the source of this error. + + > Example + Assert that some type is equal to another., + + import Standard.Examples + import Standard.Test + + example_should_equal = Examples.some_tpe . should_equal_tpe Vector.Vector +Any.should_equal_type : Any -> Integer -> Assertion +Any.should_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that) of + True -> Success + False -> + loc = Meta.get_source_location 2+frames_to_skip + msg = self.to_text + " did not equal type " + that.to_text + " (at " + loc + ")." + fail msg + ## Asserts that `self` value is not equal to the expected value. Arguments: @@ -308,6 +330,28 @@ Any.should_not_equal self that frames_to_skip=0 = case self != that of msg = self.to_text + " did equal " + that.to_text + " (at " + loc + ")." fail msg +## Asserts that `self` value is not equal to the expected type value. + + Arguments: + - that: The type to check `self` for equality with. + - frames_to_skip (optional, advanced): used to alter the location which is + displayed as the source of this error. + + > Example + Assert that some type is equal to another., + + import Standard.Examples + import Standard.Test + + example_should_not_equal = Examples.some_tpe . should_not_equal_tpe Vector.Vector +Any.should_not_equal_type : Any -> Integer -> Assertion +Any.should_not_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that . not) of + True -> Success + False -> + loc = Meta.get_source_location 2+frames_to_skip + msg = self.to_text + " did equal type " + that.to_text + " (at " + loc + ")." + fail msg + ## Asserts that `self` value is equal to the expected value. Arguments: diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java index 5d58635548ef..f07f2ae840bd 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java @@ -45,7 +45,7 @@ void doEnsoDate(VirtualFrame frame, Object state, EnsoDate date) { accept(frame, state, new Object[0]); } - @Specialization(guards = "interop.isDate(date)") + @Specialization(guards = {"interop.isDate(date)", "!interop.isTime(date)"}) void doDate( VirtualFrame frame, Object state, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java index 55f0ce2e7047..a41ac95c038b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java @@ -49,7 +49,6 @@ void doEnsoDateTime(VirtualFrame frame, Object state, EnsoDateTime dateTime) { guards = { "interop.isDate(dateTime)", "interop.isTime(dateTime)", - "interop.isTimeZone(dateTime)" }) void doDateTime( VirtualFrame frame, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java index 204420352e18..453f87586bb9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java @@ -45,7 +45,7 @@ void doEnsoTimeOfDay(VirtualFrame frame, Object state, EnsoTimeOfDay timeOfDay) accept(frame, state, new Object[0]); } - @Specialization(guards = "interop.isTime(timeOfDay)") + @Specialization(guards = {"!interop.isDate(timeOfDay)", "interop.isTime(timeOfDay)"}) void doTime( VirtualFrame frame, Object state, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java index c7888683bf68..d4dde975ded5 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java @@ -45,7 +45,8 @@ void doEnsoZone(VirtualFrame frame, Object state, EnsoTimeZone timeZone) { accept(frame, state, new Object[0]); } - @Specialization(guards = "interop.isTimeZone(zone)") + @Specialization( + guards = {"!interop.isDate(zone)", "!interop.isTime(zone)", "interop.isTimeZone(zone)"}) void doZone( VirtualFrame frame, Object state, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java deleted file mode 100644 index 9f7b4d881b20..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsOfTypeMetaNode.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.meta; - -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.BranchProfile; -import org.enso.interpreter.Constants; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.runtime.Context; -import org.enso.interpreter.runtime.builtin.Builtins; -import org.enso.interpreter.runtime.error.PanicException; -import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; - -@BuiltinMethod( - type = "Meta", - name = "is_of_type_builtin", - description = "Checks if the given type matches the expected type.") -public class IsOfTypeMetaNode extends Node { - private @Child TypesLibrary types = - TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - private @Child InteropLibrary library = - InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - private final BranchProfile err = BranchProfile.create(); - - Object execute(Object tpe, Object expected) { - if (library.isMetaObject(tpe) && library.isMetaObject(expected)) { - return library.isIdentical(tpe, expected, InteropLibrary.getUncached()); - } else { - if (types.hasType(tpe) && types.hasType(expected)) { - return types.getType(tpe) == types.getType(expected); - } else { - Context ctx = Context.get(this); - return false; - } - } - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java index e14d899c7d6d..329935d958d6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java @@ -1,5 +1,8 @@ package org.enso.interpreter.node.expression.builtin.meta; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +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; @@ -8,8 +11,20 @@ type = "Meta", name = "is_same_object", description = "Checks if the two arguments share an underlying reference.") -public class IsSameObjectNode extends Node { - boolean execute(@AcceptsError Object value_1, @AcceptsError Object value_2) { - return value_1 == value_2; +public abstract class IsSameObjectNode extends Node { + + static IsSameObjectNode build() { + return IsSameObjectNodeGen.create(); + } + + abstract boolean execute(@AcceptsError Object left, @AcceptsError Object right); + + @Specialization(limit = "3") + boolean doExecute( + Object left, + Object right, + @CachedLibrary("left") InteropLibrary leftInterop, + @CachedLibrary("right") InteropLibrary rightInteropp) { + return (left == right) || leftInterop.isIdentical(left, right, rightInteropp); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java index 6dd81ac59c3a..5d07340b6ce9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -1,44 +1,79 @@ 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.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 com.oracle.truffle.api.profiles.BranchProfile; import org.enso.interpreter.Constants; +import org.enso.interpreter.dsl.AcceptsError; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.builtin.Builtins; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; +import org.enso.interpreter.runtime.number.EnsoBigInteger; +import org.enso.interpreter.runtime.type.TypesGen; @BuiltinMethod( type = "Meta", name = "type_of_builtin", description = "Returns the type of a value.") -public class TypeOfNode extends Node { - private @Child InteropLibrary library = - InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - private @Child TypesLibrary types = - TypesLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH); - - Object execute(Object value) { - if (library.hasMetaObject(value)) { - try { - return library.getMetaObject(value); - } catch (UnsupportedMessageException e) { - CompilerDirectives.transferToInterpreter(); - Builtins builtins = Context.get(this).getBuiltins(); - throw new PanicException( - builtins.error().makeTypeError(builtins.any(), value, "object"), this); - } - } else { - if (types.hasType(value)) { - return types.getType(value); - } else { - Context ctx = Context.get(this); - return ctx.getBuiltins().any(); - } +public abstract class TypeOfNode extends Node { + + abstract Object execute(@AcceptsError Object value); + + static TypeOfNode build() { + return TypeOfNodeGen.create(); + } + + @Specialization + Object doDouble(double value) { + Context ctx = Context.get(this); + return ctx.getBuiltins().number().getDecimal(); + } + + @Specialization + Object doLong(long value) { + Context ctx = Context.get(this); + return ctx.getBuiltins().number().getInteger(); + } + + @Specialization + Object doBigInteger(EnsoBigInteger value) { + Context ctx = Context.get(this); + return ctx.getBuiltins().number().getInteger(); + } + + @Specialization(guards = "isError(value)") + Object doError(Object value) { + return Context.get(this).getBuiltins().dataflowError(); + } + + boolean isError(Object value) { + return TypesGen.isDataflowError(value); + } + + @Specialization(guards = "interop.hasMetaObject(value)") + Object doMetaObject(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + try { + return interop.getMetaObject(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreter(); + Builtins builtins = Context.get(this).getBuiltins(); + throw new PanicException(builtins.error().makeCompileError("invalid meta object"), this); } } + + @Specialization(guards = "types.hasType(value)") + Object doType(Object value, @CachedLibrary(limit = "3") TypesLibrary types) { + return types.getType(value); + } + + @Fallback + Object doAny(Object value) { + return Context.get(this).getBuiltins().any(); + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java index b673e4142151..87b91904090c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java @@ -34,6 +34,10 @@ public static Type fromTypeSystem(Builtins builtins, String typeName) { return builtins.array(); case ConstantsGen.BOOLEAN: return builtins.bool().getType(); + case ConstantsGen.DATE: + return builtins.date(); + case ConstantsGen.DATE_TIME: + return builtins.dateTime(); case ConstantsGen.DECIMAL: return builtins.number().getDecimal(); case ConstantsGen.ERROR: @@ -56,6 +60,12 @@ public static Type fromTypeSystem(Builtins builtins, String typeName) { return builtins.ref(); case ConstantsGen.TEXT: return builtins.text(); + case ConstantsGen.TIME_OF_DAY: + return builtins.timeOfDay(); + case ConstantsGen.TIME_ZONE: + return builtins.timeZone(); + case ConstantsGen.VECTOR: + return builtins.vector(); default: throw new CompilerError("Invalid builtin type " + typeName); } diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 54e16493ffc8..fe124dbba1b9 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -13,12 +13,12 @@ polyglot java import java.time.LocalDate polyglot java import java.time.format.DateTimeFormatter spec = - spec_with "Date" Date.new Date.parse - spec_with "JavaScriptDate" js_date js_parse - spec_with "JavaDate" java_date java_parse - spec_with "JavaScriptArrayWithADate" js_array_date js_parse + spec_with "Date" Date.new Date.parse Date.Date + spec_with "JavaScriptDate" js_date js_parse Date.Date + spec_with "JavaDate" java_date java_parse LocalDate + spec_with "JavaScriptArrayWithADate" js_array_date js_parse Date.Date -spec_with name create_new_date parse_date = +spec_with name create_new_date parse_date date_tpe = Test.group name <| Test.specify "should create local date" <| @@ -133,6 +133,13 @@ spec_with name create_new_date parse_date = date_1>date_2 . should_be_true date_1time_2 . should_be_true time_1time_2 . should_be_true time_1 Test.fail ("Unexpected result: " + result.to_text) + Test.specify "should correctly determine the type of zone" <| + zone = Time_Zone.parse "Europe/Warsaw" + Meta.type_of zone . should_equal_type Time_Zone.Time_Zone Test.group "JavaZoneId" <| Test.specify "should get system zone id" <| defaultZone = ZoneId.systemDefault @@ -61,5 +65,8 @@ spec = Json.from_pairs [["type", "Time_Zone"], ["id", "+01:02:03"]] (ZoneId.of "UTC").to_json.should_equal <| Json.from_pairs [["type", "Time_Zone"], ["id", "UTC"]] + Test.specify "should correctly determine the type of zone" <| + zone = ZoneId.systemDefault + Meta.type_of zone . should_equal_type ZoneRegion main = Test.Suite.run_main spec diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index e53fb4469f93..13f047a8d7c6 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -4,6 +4,7 @@ import Standard.Base import Standard.Base.System.Platform polyglot java import java.lang.Object as JObject +polyglot java import java.lang.Long as JLong polyglot java import java.lang.Exception as JException polyglot java import java.io.IOException polyglot java import java.util.ArrayList @@ -71,8 +72,6 @@ spec = Test.group "Meta-Value Manipulation" <| 1.0.is_an Integer . should_be_false 1.0.is_a Text . should_be_false - - random_gen = Random.new Meta.is_a random_gen Polyglot . should_be_true Meta.is_an random_gen Integer . should_be_false @@ -96,55 +95,59 @@ spec = Test.group "Meta-Value Manipulation" <| Meta.is_a Date_Time.now.zone Time_Zone.Time_Zone . should_be_true Meta.is_a Date_Time.now.zone Date.Date . should_be_false - Test.specify "should allow for returning the type of a value" <| - Meta.type_of 42 . should_equal Integer - Meta.type_of 2.81 . should_equal Decimal - Meta.type_of 2.81 . should_not_equal Integer + n_1 = Meta.type_of 42 + n_1 . should_equal_type Integer + n_1 . should_not_equal_type Decimal + + n_2 = Meta.type_of 2.81 + n_2 . should_equal_type Decimal + n_2 . should_not_equal_type Integer + + n_3 = Meta.type_of (Long.MAX_VALUE * 2) + n_3 . should_equal_type Integer + n_3 . should_not_equal_type Decimal - # Can't compare like - # Meta.type_of [1,2,3] . should_equal Vector.Vector - # because equality check tries to compare lengths, where RHS is a type v_tpe = Meta.type_of [1,2,3] - v_tpe.is_of_type Vector.Vector . should_be_true - v_tpe.is_of_type Array . should_be_false - [1,2,3].to_array.is_of_type Array . should_be_true + v_tpe . should_equal_type Vector.Vector + v_tpe . should_not_equal_type Array + Meta.type_of [1,2,3].to_array . should_equal_type Array - Meta.type_of "foo" . should_equal Text - Meta.type_of "0" . should_not_equal Integer + Meta.type_of "foo" . should_equal_type Text + Meta.type_of "0" . should_not_equal_type Integer - Meta.type_of True . should_equal Boolean - Meta.type_of False . should_not_equal Any + Meta.type_of True . should_equal_type Boolean + Meta.type_of False . should_not_equal_type Any - (Meta.type_of Date.now).is_of_type Date.Date . should_be_true - (Meta.type_of Date.now).is_of_type Date_Time.Date_Time . should_be_false - (Meta.type_of Date_Time.now).is_of_type Date_Time.Date_Time . should_be_true - (Meta.type_of Date_Time.now).is_of_type Date.Date . should_be_false - (Meta.type_of Time_Of_Day.now).is_of_type Time_Of_Day.Time_Of_Day . should_be_true - (Meta.type_of Time_Of_Day.now).is_of_type Date.Date . should_be_false - (Meta.type_of Date_Time.now.zone).is_of_type Time_Zone.Time_Zone . should_be_true - (Meta.type_of Date_Time.now.zone).is_of_type Date.Date . should_be_false + (Meta.type_of Date.now) . should_equal_type Date.Date + (Meta.type_of Date.now) . should_not_equal_type Date_Time.Date_Time + (Meta.type_of Date_Time.now) . should_equal_type Date_Time.Date_Time + (Meta.type_of Date_Time.now) . should_not_equal_type Date.Date + (Meta.type_of Time_Of_Day.now) . should_equal_type Time_Of_Day.Time_Of_Day + (Meta.type_of Time_Of_Day.now) . should_not_equal_type Date.Date + (Meta.type_of Date_Time.now.zone) . should_equal_type Time_Zone.Time_Zone + (Meta.type_of Date_Time.now.zone) . should_not_equal_type Date.Date list = ArrayList.new list.add 123 list_tpe = Meta.type_of list list_tpe . should_not_equal JObject list_tpe . should_equal ArrayList - list_tpe.is_of_type ArrayList . should_be_true + list_tpe . should_equal_type ArrayList e = IOException.new "meh" e_tpe = Meta.type_of e e_tpe . should_equal IOException + e_tpe . should_equal_type IOException e_tpe . should_not_equal JException - e_tpe.is_of_type IOException . should_be_true - e_tpe.is_of_type JException . should_be_false + e_tpe . should_not_equal_type JException location_pending = case Platform.os of Platform.Windows -> "This test is disabled on Windows until issue 1561 is fixed." _ -> Nothing Test.specify "should allow to get the source location of a frame" pending=location_pending <| src = Meta.get_source_location 0 - loc = "Meta_Spec.enso:146:15-40" + loc = "Meta_Spec.enso:149:15-40" src.take (Last loc.length) . should_equal loc Test.specify "should allow to get qualified type names of values" <| From 06589b118f6f7cd48d54186734dce42dcd6db4e1 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 26 Sep 2022 12:39:48 +0200 Subject: [PATCH 07/10] Ensure polyglot date/time values are of Enso types We want to make sure that the type of Java's LocalTime or LocalDate etc is a corresponding Enso type, Enso's Time_Of_Day or Date, respectively. Added more tests, including those that ensure lack of subtyping between Enso's Date, Date_Time, Time_Of_Day and Time_Zone. --- .../Base/0.0.0-dev/src/Data/Time/Date.enso | 3 +- .../expression/builtin/meta/TypeOfNode.java | 47 +++++++++++++++---- test/Tests/src/Data/Time/Date_Spec.enso | 10 ++-- test/Tests/src/Data/Time/Date_Time_Spec.enso | 10 ++-- .../Tests/src/Data/Time/Time_Of_Day_Spec.enso | 6 +-- test/Tests/src/Data/Time/Time_Zone_Spec.enso | 2 +- test/Tests/src/Semantic/Case_Spec.enso | 33 ++++++++++++- test/Tests/src/Semantic/Meta_Spec.enso | 5 +- 8 files changed, 88 insertions(+), 28 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index b4c9aec5ea50..4f5520203066 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -506,8 +506,7 @@ type Date Date -> sign = Time_Utils.compare_to_localdate self that 0 == sign - _ -> - False + _ -> False ## PRIVATE week_days_between start end = diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java index 5d07340b6ce9..218a2031fee8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -47,17 +47,43 @@ Object doBigInteger(EnsoBigInteger value) { return ctx.getBuiltins().number().getInteger(); } - @Specialization(guards = "isError(value)") - Object doError(Object value) { - return Context.get(this).getBuiltins().dataflowError(); + @Specialization(guards = {"interop.isDate(value)", "interop.isTime(value)"}) + Object doDateTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().dateTime(); + } + + @Specialization( + guards = {"!interop.isDate(value)", "!interop.isTime(value)", "interop.isTimeZone(value)"}) + Object doTimeZone(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().timeZone(); } - boolean isError(Object value) { - return TypesGen.isDataflowError(value); + @Specialization(guards = {"interop.isDate(value)", "!interop.isTime(value)"}) + Object doDate(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().date(); + } + + @Specialization(guards = {"!interop.isDate(value)", "interop.isTime(value)"}) + Object doTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().timeOfDay(); } - @Specialization(guards = "interop.hasMetaObject(value)") - Object doMetaObject(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + @Specialization( + guards = { + "interop.hasMetaObject(value)", + "!types.hasType(value)", + "!interop.isDate(value)", + "!interop.isTime(value)", + "!interop.isTimeZone(value)" + }) + Object doMetaObject( + Object value, + @CachedLibrary(limit = "3") InteropLibrary interop, + @CachedLibrary(limit = "3") TypesLibrary types) { try { return interop.getMetaObject(value); } catch (UnsupportedMessageException e) { @@ -67,8 +93,11 @@ Object doMetaObject(Object value, @CachedLibrary(limit = "3") InteropLibrary int } } - @Specialization(guards = "types.hasType(value)") - Object doType(Object value, @CachedLibrary(limit = "3") TypesLibrary types) { + @Specialization(guards = {"types.hasType(value)", "!interop.isNumber(value)"}) + Object doType( + Object value, + @CachedLibrary(limit = "3") InteropLibrary interop, + @CachedLibrary(limit = "3") TypesLibrary types) { return types.getType(value); } diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index fe124dbba1b9..0a54db7386ff 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -13,12 +13,12 @@ polyglot java import java.time.LocalDate polyglot java import java.time.format.DateTimeFormatter spec = - spec_with "Date" Date.new Date.parse Date.Date - spec_with "JavaScriptDate" js_date js_parse Date.Date - spec_with "JavaDate" java_date java_parse LocalDate - spec_with "JavaScriptArrayWithADate" js_array_date js_parse Date.Date + spec_with "Date" Date.new Date.parse + spec_with "JavaScriptDate" js_date js_parse + spec_with "JavaDate" java_date java_parse + spec_with "JavaScriptArrayWithADate" js_array_date js_parse -spec_with name create_new_date parse_date date_tpe = +spec_with name create_new_date parse_date date_tpe=Date.Date = Test.group name <| Test.specify "should create local date" <| diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index 05bebf06d799..0b24492c1e7f 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -14,10 +14,10 @@ polyglot java import java.time.ZoneOffset polyglot java import java.time.format.DateTimeFormatter spec = - spec_with "Date_Time" Date_Time.new Date_Time.parse datetime_tpe=Date_Time.Date_Time - spec_with "JavascriptDate" js_datetime js_parse datetime_tpe=ZonedDateTime nanoseconds_loss_in_precision=True - spec_with "JavaZonedDateTime" java_datetime java_parse datetime_tpe=ZonedDateTime - spec_with "JavascriptDataInArray" js_array_datetime js_parse datetime_tpe=ZonedDateTime nanoseconds_loss_in_precision=True + spec_with "Date_Time" Date_Time.new Date_Time.parse + spec_with "JavascriptDate" js_datetime js_parse nanoseconds_loss_in_precision=True + spec_with "JavaZonedDateTime" java_datetime java_parse + spec_with "JavascriptDataInArray" js_array_datetime js_parse nanoseconds_loss_in_precision=True Test.group "Date_Time equality" <| Test.specify "should work with values coming from various sources" <| @@ -32,7 +32,7 @@ spec = d3 . should_equal d4 d4 . should_equal d5 -spec_with name create_new_datetime parse_datetime datetime_tpe nanoseconds_loss_in_precision=False = +spec_with name create_new_datetime parse_datetime datetime_tpe=Date_Time.Date_Time nanoseconds_loss_in_precision=False = Test.group name <| Test.specify "should create time" <| diff --git a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso index 5f3bda48d0c0..5ee20b93ebf4 100644 --- a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso @@ -9,10 +9,10 @@ polyglot java import java.time.LocalTime polyglot java import java.time.format.DateTimeFormatter spec = - specWith "Time_Of_Day" enso_time Time_Of_Day.parse Time_Of_Day.Time_Of_Day - specWith "JavaLocalTime" java_time java_parse LocalTime + specWith "Time_Of_Day" enso_time Time_Of_Day.parse + specWith "JavaLocalTime" java_time java_parse -specWith name create_new_time parse_time timeofday_tpe = +specWith name create_new_time parse_time timeofday_tpe=Time_Of_Day.Time_Of_Day = Test.group name <| Test.specify "should create local time" <| diff --git a/test/Tests/src/Data/Time/Time_Zone_Spec.enso b/test/Tests/src/Data/Time/Time_Zone_Spec.enso index 2ba488929c3d..56768ae594d4 100644 --- a/test/Tests/src/Data/Time/Time_Zone_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Zone_Spec.enso @@ -67,6 +67,6 @@ spec = Json.from_pairs [["type", "Time_Zone"], ["id", "UTC"]] Test.specify "should correctly determine the type of zone" <| zone = ZoneId.systemDefault - Meta.type_of zone . should_equal_type ZoneRegion + Meta.type_of zone . should_equal_type Time_Zone.Time_Zone main = Test.Suite.run_main spec diff --git a/test/Tests/src/Semantic/Case_Spec.enso b/test/Tests/src/Semantic/Case_Spec.enso index 2c513ccc537d..41dfa93d1e72 100644 --- a/test/Tests/src/Semantic/Case_Spec.enso +++ b/test/Tests/src/Semantic/Case_Spec.enso @@ -88,6 +88,37 @@ spec = Test.group "Pattern Matches" <| case Any of Any -> Nothing _ -> Test.fail "Expected the Any constructor to match." + Test.specify "should be able to match on date/time values" <| + new_date = Date.new 2020 6 1 + new_date_time = Date_Time.new 2020 6 1 + new_time = Time_Of_Day.new 11 11 + new_zone = Time_Zone.system + + case new_date of + Date_Time.Date_Time -> Test.fail "Expected date value to match Date." + Time_Of_Day.Time_Of_Day -> Test.fail "Expected date value to match Date." + Date.Date -> Nothing + _ -> Test.fail "Expected date value to match Date." + + case new_date_time of + Date.Date -> Test.fail "Expected datetime value to match Date_Time." + Time_Of_Day.Time_Of_Day -> Test.fail "Expected datetime value to match Date_Time." + Date_Time.Date_Time -> Nothing + _ -> Test.fail "Expected datetime value to match Date_Time." + + case new_time of + Date.Date -> Test.fail "Expected time value to match Time_Of_Day." + Date_Time.Date_Time -> Test.fail "Expected time value to match Time_Of_Day." + Time_Of_Day.Time_Of_Day -> Nothing + _ -> Test.fail "Expected time value to match Time_Of_Day." + + case new_zone of + Date.Date -> Test.fail "Expected timezone value to match Time_Zone." + Date_Time.Date_Time -> Test.fail "Expected timezone to match Time_Zone." + Time_Of_Day.Time_Of_Day -> Test.fail "Expected timezone to match Time_Zone." + Time_Zone.Time_Zone -> Nothing + _ -> Test.fail "Expected timezone value to match Time_Zone." + Test.specify "should be able to match on literal values" <| value_1 = 42 value_2 = "foo" @@ -112,8 +143,6 @@ spec = Test.group "Pattern Matches" <| "ę" -> Test.fail "Expected value to match constant." '\u0065\u{301}' -> Nothing _ -> Test.fail "Expected value to match constant." - - Test.specify "should be able to match on literal values nested in constructors" <| value_1 = Cons 42 Nil value_2 = Cons (Cons 42 Nil) Nil diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index 13f047a8d7c6..7df530bf88f2 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -127,6 +127,8 @@ spec = Test.group "Meta-Value Manipulation" <| (Meta.type_of Time_Of_Day.now) . should_not_equal_type Date.Date (Meta.type_of Date_Time.now.zone) . should_equal_type Time_Zone.Time_Zone (Meta.type_of Date_Time.now.zone) . should_not_equal_type Date.Date + (Meta.type_of Time_Zone.local) . should_equal_type Time_Zone.Time_Zone + (Meta.type_of Time_Zone.system) . should_equal_type Time_Zone.Time_Zone list = ArrayList.new list.add 123 @@ -145,9 +147,10 @@ spec = Test.group "Meta-Value Manipulation" <| location_pending = case Platform.os of Platform.Windows -> "This test is disabled on Windows until issue 1561 is fixed." _ -> Nothing + Test.specify "should allow to get the source location of a frame" pending=location_pending <| src = Meta.get_source_location 0 - loc = "Meta_Spec.enso:149:15-40" + loc = "Meta_Spec.enso:152:15-40" src.take (Last loc.length) . should_equal loc Test.specify "should allow to get qualified type names of values" <| From 097bfbb6c499141575bca2ff4b2e74a623d92f98 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 26 Sep 2022 16:27:47 +0200 Subject: [PATCH 08/10] PR review --- .../node/expression/builtin/meta/TypeOfNode.java | 8 ++++---- test/Tests/src/Data/Time/Date_Spec.enso | 6 +++--- test/Tests/src/Data/Time/Date_Time_Spec.enso | 6 +++--- test/Tests/src/Data/Time/Time_Of_Day_Spec.enso | 6 +++--- test/Tests/src/Semantic/Case_Spec.enso | 3 +++ test/Tests/src/Semantic/Meta_Spec.enso | 7 ++----- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java index 218a2031fee8..c4fec7c7bfb2 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -47,14 +47,14 @@ Object doBigInteger(EnsoBigInteger value) { return ctx.getBuiltins().number().getInteger(); } - @Specialization(guards = {"interop.isDate(value)", "interop.isTime(value)"}) + @Specialization(guards = {"interop.isTime(value)", "interop.isDate(value)"}) Object doDateTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { Context ctx = Context.get(this); return ctx.getBuiltins().dateTime(); } @Specialization( - guards = {"!interop.isDate(value)", "!interop.isTime(value)", "interop.isTimeZone(value)"}) + guards = {"interop.isTimeZone(value)", "!interop.isDate(value)", "!interop.isTime(value)"}) Object doTimeZone(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { Context ctx = Context.get(this); return ctx.getBuiltins().timeZone(); @@ -66,7 +66,7 @@ Object doDate(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) return ctx.getBuiltins().date(); } - @Specialization(guards = {"!interop.isDate(value)", "interop.isTime(value)"}) + @Specialization(guards = {"interop.isTime(value)", "!interop.isDate(value)"}) Object doTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { Context ctx = Context.get(this); return ctx.getBuiltins().timeOfDay(); @@ -103,6 +103,6 @@ Object doType( @Fallback Object doAny(Object value) { - return Context.get(this).getBuiltins().any(); + return Context.get(this).getBuiltins().error().makeCompileError("unknown type_of for " + value); } } diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 0a54db7386ff..f8d0aa61d222 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -18,7 +18,7 @@ spec = spec_with "JavaDate" java_date java_parse spec_with "JavaScriptArrayWithADate" js_array_date js_parse -spec_with name create_new_date parse_date date_tpe=Date.Date = +spec_with name create_new_date parse_date = Test.group name <| Test.specify "should create local date" <| @@ -137,8 +137,8 @@ spec_with name create_new_date parse_date date_tpe=Date.Date = new_date = create_new_date 2020 6 1 parsed_date = parse_date "2021-01-02" - Meta.type_of new_date . should_equal_type date_tpe - Meta.type_of parsed_date . should_equal_type date_tpe + Meta.type_of new_date . should_equal_type Date.Date + Meta.type_of parsed_date . should_equal_type Date.Date Test.specify "should allow to find start and end of a Date_Period containing the current date" <| d1 = create_new_date 2022 9 12 diff --git a/test/Tests/src/Data/Time/Date_Time_Spec.enso b/test/Tests/src/Data/Time/Date_Time_Spec.enso index 0b24492c1e7f..9ce0d4c14409 100644 --- a/test/Tests/src/Data/Time/Date_Time_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Time_Spec.enso @@ -32,7 +32,7 @@ spec = d3 . should_equal d4 d4 . should_equal d5 -spec_with name create_new_datetime parse_datetime datetime_tpe=Date_Time.Date_Time nanoseconds_loss_in_precision=False = +spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=False = Test.group name <| Test.specify "should create time" <| @@ -319,8 +319,8 @@ spec_with name create_new_datetime parse_datetime datetime_tpe=Date_Time.Date_Ti new_datetime = create_new_datetime 2020 6 1 10 0 0 parsed_datetime = parse_datetime "2021-02-01T00:30:12.7102[UTC]" - Meta.type_of new_datetime . should_equal_type datetime_tpe - Meta.type_of parsed_datetime . should_equal_type datetime_tpe + Meta.type_of new_datetime . should_equal_type Date_Time.Date_Time + Meta.type_of parsed_datetime . should_equal_type Date_Time.Date_Time max_nanos = 999999999 Test.specify "should allow to find start/end of a Date_Period containing the current datetime" <| diff --git a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso index 5ee20b93ebf4..04c17a10591d 100644 --- a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso @@ -12,7 +12,7 @@ spec = specWith "Time_Of_Day" enso_time Time_Of_Day.parse specWith "JavaLocalTime" java_time java_parse -specWith name create_new_time parse_time timeofday_tpe=Time_Of_Day.Time_Of_Day = +specWith name create_new_time parse_time = Test.group name <| Test.specify "should create local time" <| @@ -121,8 +121,8 @@ specWith name create_new_time parse_time timeofday_tpe=Time_Of_Day.Time_Of_Day = new_timeofday = create_new_time 15 37 58 parsed_timeofday = parse_time "10:00:00" - Meta.type_of new_timeofday . should_equal_type timeofday_tpe - Meta.type_of parsed_timeofday . should_equal_type timeofday_tpe + Meta.type_of new_timeofday . should_equal_type Time_Of_Day.Time_Of_Day + Meta.type_of parsed_timeofday . should_equal_type Time_Of_Day.Time_Of_Day max_nanos = 999999999 Test.specify "should allow to find start/end of a Time_Period containing the current time of day" <| diff --git a/test/Tests/src/Semantic/Case_Spec.enso b/test/Tests/src/Semantic/Case_Spec.enso index 41dfa93d1e72..69ac27aed71b 100644 --- a/test/Tests/src/Semantic/Case_Spec.enso +++ b/test/Tests/src/Semantic/Case_Spec.enso @@ -97,18 +97,21 @@ spec = Test.group "Pattern Matches" <| case new_date of Date_Time.Date_Time -> Test.fail "Expected date value to match Date." Time_Of_Day.Time_Of_Day -> Test.fail "Expected date value to match Date." + Time_Zone.Time_Zone -> Test.fail "Expected date value to match Date." Date.Date -> Nothing _ -> Test.fail "Expected date value to match Date." case new_date_time of Date.Date -> Test.fail "Expected datetime value to match Date_Time." Time_Of_Day.Time_Of_Day -> Test.fail "Expected datetime value to match Date_Time." + Time_Zone.Time_Zone -> Test.fail "Expected datetime value to match Date_Time." Date_Time.Date_Time -> Nothing _ -> Test.fail "Expected datetime value to match Date_Time." case new_time of Date.Date -> Test.fail "Expected time value to match Time_Of_Day." Date_Time.Date_Time -> Test.fail "Expected time value to match Time_Of_Day." + Time_Zone.Time_Zone -> Test.fail "Expected time value to match Time_Of_Day." Time_Of_Day.Time_Of_Day -> Nothing _ -> Test.fail "Expected time value to match Time_Of_Day." diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index 7df530bf88f2..5b76e13f27bf 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -133,15 +133,12 @@ spec = Test.group "Meta-Value Manipulation" <| list = ArrayList.new list.add 123 list_tpe = Meta.type_of list - list_tpe . should_not_equal JObject - list_tpe . should_equal ArrayList + list_tpe . should_not_equal_type JObject list_tpe . should_equal_type ArrayList e = IOException.new "meh" e_tpe = Meta.type_of e - e_tpe . should_equal IOException e_tpe . should_equal_type IOException - e_tpe . should_not_equal JException e_tpe . should_not_equal_type JException location_pending = case Platform.os of @@ -150,7 +147,7 @@ spec = Test.group "Meta-Value Manipulation" <| Test.specify "should allow to get the source location of a frame" pending=location_pending <| src = Meta.get_source_location 0 - loc = "Meta_Spec.enso:152:15-40" + loc = "Meta_Spec.enso:149:15-40" src.take (Last loc.length) . should_equal loc Test.specify "should allow to get qualified type names of values" <| From 228500e3a3aa7409e117c2f8ab84a4b8ead13c0f Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 26 Sep 2022 16:29:40 +0200 Subject: [PATCH 09/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Radosław Waśko --- .../node/controlflow/caseexpr/DateTimeBranchNode.java | 2 +- .../node/controlflow/caseexpr/TimeZoneBranchNode.java | 2 +- test/Tests/src/Semantic/Meta_Spec.enso | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java index a41ac95c038b..07475b5b11cb 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java @@ -12,7 +12,7 @@ import org.enso.interpreter.runtime.data.Type; /** An implementation of the case expression specialised to working on Date_Time. */ -@NodeInfo(shortName = "DateMatch") +@NodeInfo(shortName = "DateTimeMatch") public abstract class DateTimeBranchNode extends BranchNode { private final Type dateTime; private final ConditionProfile profile = ConditionProfile.createCountingProfile(); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java index d4dde975ded5..dbcfc9bf9e36 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java @@ -12,7 +12,7 @@ import org.enso.interpreter.runtime.data.Type; /** An implementation of the case expression specialised to working on Time_Zone. */ -@NodeInfo(shortName = "DateMatch") +@NodeInfo(shortName = "TimeZoneMatch") public abstract class TimeZoneBranchNode extends BranchNode { private final Type timeZone; private final ConditionProfile profile = ConditionProfile.createCountingProfile(); diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index 5b76e13f27bf..073b6acdd810 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -52,7 +52,7 @@ spec = Test.group "Meta-Value Manipulation" <| [].to_array.is_an Array . should_be_true [].to_array.is_a Decimal . should_be_false [1,2,3].is_a Vector.Vector . should_be_true - [1,2,3].is_a Array . should_be_false + [1,2,3].is_an Array . should_be_false Boolean.is_a Boolean . should_be_true True.is_a Boolean . should_be_true From ba4f1bfdecb8ca47d7ec6f2804a6675f0620011c Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 26 Sep 2022 16:47:20 +0200 Subject: [PATCH 10/10] Remove is_an completely --- .../lib/Standard/Base/0.0.0-dev/src/Meta.enso | 33 +------------------ .../Database/0.0.0-dev/src/Data/Table.enso | 2 +- .../Table/0.0.0-dev/src/Excel/Range.enso | 2 +- test/Table_Tests/src/Aggregate_Spec.enso | 4 +-- test/Tests/src/Semantic/Meta_Spec.enso | 28 ++++++++-------- 5 files changed, 19 insertions(+), 50 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index e941a17cb307..c80cf47b0a79 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -271,16 +271,6 @@ Any.is_same_object_as self value = is_same_object self value Any.is_a : Any -> Boolean Any.is_a self typ = is_a self typ -## UNSTABLE - ADVANCED - - Checks if `self` is an instance of `typ`. - - Arguments: - - typ: The type to check `self` against. -Any.is_an : Any -> Boolean -Any.is_an self typ = is_a self typ - ## UNSTABLE ADVANCED @@ -289,17 +279,7 @@ Any.is_an self typ = is_a self typ Arguments: - typ: The type to check `self` against. Base.Error.is_a : Any -> Boolean -Base.Error.is_a self typ = self.is_an typ - -## UNSTABLE - ADVANCED - - Checks if `self` is an instance of `typ`. - - Arguments: - - typ: The type to check `self` against. -Base.Error.is_an : Any -> Boolean -Base.Error.is_an self typ = typ==Any || typ==Base.Error +Base.Error.is_a self typ = typ==Any || typ==Base.Error ## UNSTABLE ADVANCED @@ -348,17 +328,6 @@ java_instance_check value typ = typ_java = get_polyglot_language typ == "java" val_java && typ_java && Base.Java.is_instance value typ -## UNSTABLE - ADVANCED - - Checks if `value` is an instance of `typ`. - - Arguments: - - value: The value to check for being an instance of `typ`. - - typ: The type to check `self` against. -is_an : Any -> Any -> Boolean -is_an value typ = is_a value typ - ## UNSTABLE ADVANCED diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index 50e2bb831866..abc147df8255 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -705,7 +705,7 @@ type Table agg = p.second new_name = p.first Aggregate_Helper.make_aggregate_column self agg new_name . catch - partitioned = results.partition (_.is_an Internal_Column_Data) + partitioned = results.partition (_.is_a Internal_Column_Data) ## When working on join we may encounter further issues with having aggregate columns exposed directly, it may be useful to re-use the `lift_aggregate` method to push the aggregates into a diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso index ff3d5b7a4d50..e945601095d2 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso @@ -79,7 +79,7 @@ type Excel_Range unchanged. column_index : (Text|Integer) -> Integer column_index column = - if column.is_an Integer then column else Java_Range.parseA1Column column + if column.is_a Integer then column else Java_Range.parseA1Column column ## Creates a Range from an address. from_address : Text -> Excel_Range diff --git a/test/Table_Tests/src/Aggregate_Spec.enso b/test/Table_Tests/src/Aggregate_Spec.enso index 2c2ffe6b4800..38e786ba9fa4 100644 --- a/test/Table_Tests/src/Aggregate_Spec.enso +++ b/test/Table_Tests/src/Aggregate_Spec.enso @@ -1326,14 +1326,14 @@ aggregate_spec prefix table empty_table table_builder materialize is_database te new_table = table.aggregate [Group_By "Key", Concatenate "Value"] problems = Warning.get_all new_table . map .value problems.length . should_equal 1 - problems.at 0 . is_an Invalid_Aggregation_Data . should_be_true + problems.at 0 . is_a Invalid_Aggregation_Data . should_be_true problems.at 0 . rows . length . should_equal 15 Test.specify "should merge Floating Point Grouping warnings" <| new_table = table.aggregate [Group_By "Float", Count Nothing] problems = Warning.get_all new_table . map .value problems.length . should_equal 1 - problems.at 0 . is_an Floating_Point_Grouping_Data . should_be_true + problems.at 0 . is_a Floating_Point_Grouping_Data . should_be_true problems.at 0 . rows . length . should_equal 15 if is_database then diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index 073b6acdd810..1dd189d3ba0c 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -44,47 +44,47 @@ spec = Test.group "Meta-Value Manipulation" <| meta_err.is_a Meta.Error_Data . should_be_true meta_err.value . should_equal "My Error" Test.specify "should allow checking if a value is of a certain type" <| - 1.is_an Any . should_be_true - 1.2.is_an Any . should_be_true - (My_Type_Data 1 "foo" Nothing).is_an Any . should_be_true + 1.is_a Any . should_be_true + 1.2.is_a Any . should_be_true + (My_Type_Data 1 "foo" Nothing).is_a Any . should_be_true - Array.is_an Array . should_be_true - [].to_array.is_an Array . should_be_true + Array.is_a Array . should_be_true + [].to_array.is_a Array . should_be_true [].to_array.is_a Decimal . should_be_false [1,2,3].is_a Vector.Vector . should_be_true - [1,2,3].is_an Array . should_be_false + [1,2,3].is_a Array . should_be_false Boolean.is_a Boolean . should_be_true True.is_a Boolean . should_be_true False.is_a Boolean . should_be_true - True.is_an Integer . should_be_false + True.is_a Integer . should_be_false "".is_a Text . should_be_true "".is_a Decimal . should_be_false - 1.is_an Array . should_be_false - 1.is_an Integer . should_be_true + 1.is_a Array . should_be_false + 1.is_a Integer . should_be_true 1.is_a Number . should_be_true 1.is_a Decimal . should_be_false 1.0.is_a Number . should_be_true 1.0.is_a Decimal . should_be_true - 1.0.is_an Integer . should_be_false + 1.0.is_a Integer . should_be_false 1.0.is_a Text . should_be_false random_gen = Random.new Meta.is_a random_gen Polyglot . should_be_true - Meta.is_an random_gen Integer . should_be_false + Meta.is_a random_gen Integer . should_be_false (My_Type_Data 1 "foo" Nothing).is_a My_Type_Data . should_be_true (My_Type_Data 1 "foo" Nothing).is_a Test_Type_Data . should_be_false (My_Type_Data 1 "foo" Nothing).is_a Number . should_be_false err = Error.throw "Error Value" - 1.is_an Error . should_be_false - err.is_an Error . should_be_true + 1.is_a Error . should_be_false + err.is_a Error . should_be_true err.is_a Text . should_be_false - Meta.is_an err Error . should_be_true + Meta.is_a err Error . should_be_true Meta.is_a err Text . should_be_false Meta.is_a Date.now Date.Date . should_be_true