From 299c9d48ea601fbef9a5d35ab7c811433f283779 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 17 Mar 2023 18:43:09 +0100 Subject: [PATCH 01/49] Update type ascriptions in some operators in Any --- distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso index 3ab82fda6a2b..b78e2bbfe230 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso @@ -165,7 +165,7 @@ type Any example_greater = a = 7 * 28 a > 147 - > : Any -> Boolean ! Incomparable_Values + > : Any -> Boolean ! (Incomparable_Values | Type_Error) > self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of @@ -194,7 +194,7 @@ type Any example_greater_eq = a = 6 * 21 a >= 147 - >= : Any -> Boolean ! Incomparable_Values + >= : Any -> Boolean ! (Incomparable_Values | Type_Error) >= self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of @@ -223,7 +223,7 @@ type Any example_less = a = 7 * 21 a < 147 - < : Any -> Boolean ! Incomparable_Values + < : Any -> Boolean ! (Incomparable_Values | Type_Error) < self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of @@ -254,7 +254,7 @@ type Any example_less_eq = a = 7 * 21 a < 147 - <= : Any -> Boolean ! Incomparable_Values + <= : Any -> Boolean ! (Incomparable_Values | Type_Error) <= self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of From b8a9c9e8df495006c42e315459b1c5210bac0efa Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 17 Mar 2023 18:43:36 +0100 Subject: [PATCH 02/49] Add @GenerateUncached to AnyToTextNode. Will be used in another node with @GenerateUncached. --- .../builtin/text/AnyToTextNode.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/AnyToTextNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/AnyToTextNode.java index 7f54a1b89838..878fdeed5f6c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/AnyToTextNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/AnyToTextNode.java @@ -2,6 +2,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; @@ -14,12 +15,8 @@ import org.enso.interpreter.runtime.data.text.Text; @BuiltinMethod(type = "Any", name = "to_text", description = "Generic text conversion.") +@GenerateUncached public abstract class AnyToTextNode extends Node { - private static final int DISPATCH_CACHE = 3; - private @Child InteropLibrary displays = - InteropLibrary.getFactory().createDispatched(DISPATCH_CACHE); - private @Child InteropLibrary strings = - InteropLibrary.getFactory().createDispatched(DISPATCH_CACHE); public static AnyToTextNode build() { return AnyToTextNodeGen.create(); @@ -28,19 +25,22 @@ public static AnyToTextNode build() { public abstract Text execute(Object self); @Specialization - Text doAtom(Atom at, @CachedLibrary(limit = "10") StructsLibrary structs) { + Text doAtom( + Atom at, + @CachedLibrary(limit = "10") StructsLibrary structs, + @CachedLibrary(limit = "10") InteropLibrary interop) { var fields = structs.getFields(at); if (fields.length == 0) { return consName(at.getConstructor()); } else { - return doComplexAtom(at, fields); + return doComplexAtom(at, fields, interop); } } @Fallback - Text doOther(Object object) { + Text doOther(Object object, @CachedLibrary(limit = "5") InteropLibrary interop) { try { - return Text.create(showObject(object)); + return Text.create(showObject(object, interop)); } catch (UnsupportedMessageException e) { CompilerDirectives.transferToInterpreter(); return Text.create(object.toString()); @@ -53,18 +53,18 @@ private Text consName(AtomConstructor constructor) { } @CompilerDirectives.TruffleBoundary - private Text doComplexAtom(Atom atom, Object[] fields) { + private Text doComplexAtom(Atom atom, Object[] fields, InteropLibrary interop) { Text res = Text.create("(", consName(atom.getConstructor())); res = Text.create(res, " "); try { - res = Text.create(res, showObject(fields[0])); + res = Text.create(res, showObject(fields[0], interop)); } catch (UnsupportedMessageException e) { res = Text.create(res, fields[0].toString()); } for (int i = 1; i < fields.length; i++) { res = Text.create(res, " "); try { - res = Text.create(res, showObject(fields[i])); + res = Text.create(res, showObject(fields[i], interop)); } catch (UnsupportedMessageException e) { res = Text.create(res, fields[i].toString()); } @@ -74,7 +74,8 @@ private Text doComplexAtom(Atom atom, Object[] fields) { } @CompilerDirectives.TruffleBoundary - private String showObject(Object child) throws UnsupportedMessageException { + private String showObject(Object child, InteropLibrary interop) + throws UnsupportedMessageException { if (child == null) { // TODO [RW] This is a temporary workaround to make it possible to display errors related to // https://www.pivotaltracker.com/story/show/181652974 @@ -83,7 +84,7 @@ private String showObject(Object child) throws UnsupportedMessageException { } else if (child instanceof Boolean) { return (boolean) child ? "True" : "False"; } else { - return strings.asString(displays.toDisplayString(child)); + return interop.asString(interop.toDisplayString(child)); } } } From 38b6b8aa7dd8237c876ff6b33265f23cf5f89cda Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 17 Mar 2023 18:44:33 +0100 Subject: [PATCH 03/49] Add tests for "sort handles incomparable types" --- test/Tests/src/Data/Ordering_Spec.enso | 65 +++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index a9d9385edd6b..3be7f830507a 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -2,7 +2,7 @@ from Standard.Base import all import Standard.Base.Error.Common.Incomparable_Values import Standard.Base.Error.Common.Type_Error -from Standard.Test import Test, Test_Suite +from Standard.Test import Test, Test_Suite, Problems import Standard.Test.Extensions # === Test Resources === @@ -98,5 +98,68 @@ spec = Ordering.compare Ordering.Less Nothing . should_fail_with Type_Error Ordering.compare Ordering.Less "Hello" . should_fail_with Type_Error + Test.group "Sorting" <| + ## Expects that `result` contains incomparable values warning. + The values within the warning message can be switched - the order + does not matter. Iterates through all the warnings of result. + expect_incomparable_warn : Any -> Any -> Any -> Nothing + expect_incomparable_warn left_val right_val result = + # Incomparable values warning wraps Text values in simple quotes + left_val_text = if Meta.is_a left_val Text then "'" + left_val + "'" else left_val.to_text + right_val_text = if Meta.is_a right_val Text then "'" + right_val + "'" else right_val.to_text + expected_warn_msg_left = "Values " + left_val_text + " and " + right_val_text + " are incomparable" + expected_warn_msg_right = "Values " + right_val_text + " and " + left_val_text + " are incomparable" + has_expected_warning = Warning.get_all result . map (_.value) . any (it-> it == expected_warn_msg_left || it == expected_warn_msg_right) + has_expected_warning . should_be_true + + Test.specify "should be able to sort primitive types" <| + [3, 2, 1, Nothing].sort . should_equal [1, 2, 3, Nothing] + [Nothing, Number.nan].sort . at 0 . is_nan . should_be_true + [Nothing, Number.nan].sort . at 1 . is_nothing . should_be_true + [3, 2.5].sort . should_equal [2.5, 3] + ["hello", 3].sort . should_equal [3, "hello"] + ["hello", "ahoj", 3].sort . should_equal [3, "ahoj", "hello"] + ["hello", "ahoj", 3, 2].sort . should_equal [2, 3, "ahoj", "hello"] + ["hello", "ahoj", Number.nan, 3].sort . take 3 . should_equal [3, "ahoj", "hello"] + ["hello", "ahoj", Number.nan, 3].sort . at 3 . is_nan . should_be_true + [100, Date.new 2020, 50].sort . should_equal [50, 100, Date.new 2020] + [100, Nothing, Date.new 2020, 50].sort . should_equal [50, 100, Date.new 2020, Nothing] + [3, 2, True, False].sort . should_equal [2, 3, False, True] + [3, True, 2, False].sort . should_equal [2, 3, False, True] + [Nothing, False].sort . should_equal [False, Nothing] + + Test.specify "should attach warning when trying to sort incomparable values" <| + expect_incomparable_warn Nothing Number.nan <| [Nothing, Number.nan].sort + expect_incomparable_warn 1 "hello" <| [1, "hello"].sort + + Test.specify "should respect previous warnings on a vector" <| + Problems.expect_warning "my_warn" <| (Warning.attach "my_warn" [3, 2]) . sort + Problems.expect_warning "my_warn" <| (Warning.attach "my_warn" [3, Number.nan]) . sort + expect_incomparable_warn 3 Number.nan <| (Warning.attach "my_warn" [3, Number.nan]) . sort + + Test.specify "should respect previous warnings on vector elements" <| + Problems.expect_warning "my_warn" <| [3, Warning.attach "my_warn" 2].sort . at 0 + expect_incomparable_warn 1 Number.nan [1, Warning.attach "my_warn" Number.nan].sort + Problems.expect_warning "my_warn" <| [1, Warning.attach "my_warn" Number.nan].sort . at 1 + + Test.specify "should be able to sort types with different comparators" <| + [Ord.Value 4, Ord.Value 3, 20, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20] + [Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20] + [20, Ord.Value 4, Ord.Value 3, 10].sort . should_equal [10, 20, Ord.Value 3, Ord.Value 4] + [Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20] + [Nothing, Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20, Nothing] + + Test.specify "should produce warning when sorting types with different comparators" <| + [Ord.Value 1, 1].sort . should_equal [Ord.Value 1, 1] + Problems.expect_warning "Different comparators: [Ord_Comparator, Default_Comparator]" <| [Ord.Value 1, 1].sort + + Test.specify "should be able to sort primitive values in atoms" <| + [Ord.Value Number.nan, Ord.Value 20, Ord.Value 10].sort . should_equal [Ord.Value 10, Ord.Value 20, Ord.Value Number.nan] + + Test.specify "should produce warnings when sorting primitive values in atoms" <| + expect_incomparable_warn 1 Number.nan [Ord.Value 1, Ord.Value Number.nan].sort + + + main = Test_Suite.run_main spec From 3f66d8ab2b3dee8c8bf1cb55d62eb4b8e2f1388a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 17 Mar 2023 18:45:22 +0100 Subject: [PATCH 04/49] Vector.sort handles incomparable types --- .../Base/0.0.0-dev/src/Data/Vector.enso | 79 ++++-- .../Standard/Test/0.0.0-dev/src/Problems.enso | 2 +- .../expression/builtin/meta/TypeOfNode.java | 2 + .../builtin/ordering/SortVectorNode.java | 227 ++++++++++++++++++ .../interpreter/runtime/util/Collections.java | 78 ++++++ 5 files changed, 372 insertions(+), 16 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index 32068ae0c920..58036210cb13 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -4,7 +4,6 @@ import project.Data.Filter_Condition.Filter_Condition import project.Data.List.List import project.Data.Map.Map import project.Data.Numbers.Integer -import project.Data.Ordering.Ordering import project.Data.Ordering.Sort_Direction.Sort_Direction import project.Data.Pair.Pair import project.Data.Range.Range @@ -22,7 +21,11 @@ import project.Math import project.Nothing.Nothing import project.Panic.Panic import project.Random +import project.Warning.Warning +# We have to import also conversion methods, therefore, we import all from the Ordering +# module +from project.Data.Ordering import all from project.Data.Boolean import Boolean, True, False from project.Data.Index_Sub_Range import Index_Sub_Range, take_helper, drop_helper @@ -842,11 +845,12 @@ type Vector a - on: A projection from the element type to the value of that element being sorted on. - by: A function that compares the result of applying `on` to two - elements, returning an Ordering to compare them. + elements, returning an an `Ordering` if the two elements are comparable + or `Nothing` if they are not. If set to `Nothing` (the default argument), + `Ordering.compare _ _` method will be used. + + By default, elements are sorted in ascending order. - By default, elements are sorted in ascending order, using the comparator - acquired from each element. A custom compare function may be passed to - the sort method. This is a stable sort, meaning that items that compare the same will not have their order changed by the sorting process. @@ -864,6 +868,18 @@ type Vector a is partially sorted. When the vector is randomly ordered, the performance is equivalent to a standard mergesort. + ? Multiple comparators + Elements with different comparators are incomparable by definition. + This case is handled by first grouping the `self` vector into groups + with the same comparator, recursively sorting these groups, and then + merging them back together. The order of the sorted groups in the + resulting vector is based on the order of elements' comparators in the + `self` vector, with the exception of the group for the default + comparator, which is always the first group. + + Additionally, a warning will be attached, explaining that incomparable + values were encountered. + It takes equal advantage of ascending and descending runs in the array, making it much simpler to merge two or more sorted arrays: simply concatenate them and sort. @@ -877,16 +893,49 @@ type Vector a Sorting a vector of `Pair`s on the first element, descending. [Pair 1 2, Pair -1 8].sort Sort_Direction.Descending (_.first) - sort : Sort_Direction -> (Any -> Any) -> (Any -> Any -> Ordering) -> Vector Any ! Incomparable_Values - sort self (order = Sort_Direction.Ascending) (on = x -> x) (by = (Ordering.compare _ _)) = - comp_ascending l r = by (on l) (on r) - comp_descending l r = by (on r) (on l) - compare = if order == Sort_Direction.Ascending then comp_ascending else - comp_descending - - new_vec_arr = self.to_array.sort compare - if new_vec_arr.is_error then Error.throw new_vec_arr else - Vector.from_polyglot_array new_vec_arr + + > Example + Sorting a vector with elements with different comparators. Values 1 + and My_Type have different comparators. 1 will be sorted before My_Type + because it has the default comparator, and warning will be attached to + the resulting vector. + + [My_Type.Value 'hello', 1].sort == [1, My_Type.Value 'hello'] + sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Vector Any ! Incomparable_Values + sort self (order = Sort_Direction.Ascending) on=x->x by=Nothing = + comps = self.map (it-> Comparable.from (on it)) . distinct + optimized_case = comps == [Default_Comparator] && by == Nothing + # In optimize_case, forward to Vector.sort_builtin, otherwise split to groups + # based on different comparators, and forward to Array.sort + case optimized_case of + True -> + elems = if on == Nothing then self else self.map it-> on it + elems.sort_builtin order.to_sign + False -> + # Groups of elements with different comparators + groups = comps.distinct.map comp-> + self.filter it-> + Comparable.from (on it) == comp + # TODO: Runtime.assert groups.reduce 0 acc->it-> acc + it.length == self.length + case groups.length of + # self consists only of elements with the same comparator. + # Forward to Array.sort + 1 -> + # The default value of `by` parameter is `Ordering.compare _ _` + by_non_null = if by == Nothing then (Ordering.compare _ _) else by + comp_ascending l r = by_non_null (on l) (on r) + comp_descending l r = by_non_null (on r) (on l) + compare = if order == Sort_Direction.Ascending then comp_ascending else + comp_descending + new_vec_arr = self.to_array.sort compare + if new_vec_arr.is_error then Error.throw new_vec_arr else + Vector.from_polyglot_array new_vec_arr + _ -> + # Recurse on each group, and attach a warning + # TODO: Sort Default_Comparator group as the first one? + sorted_groups = groups.map it-> it.sort order on by + comparators_text = comps.distinct.to_text + Warning.attach ("Different comparators: " + comparators_text) sorted_groups.flatten ## UNSTABLE Keeps only unique elements within the Vector, removing any duplicates. diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Problems.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Problems.enso index a5aff8e873d8..375a2b99c2fe 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Problems.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Problems.enso @@ -3,7 +3,7 @@ from Standard.Base import all from project import Test import project.Extensions -## Returns values of warnings attached to the value.Nothing +## Returns values of warnings attached to the value. get_attached_warnings v = Warning.get_all v . map .value 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 f8c2c8077b7a..3371fa1fefaa 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 @@ -2,6 +2,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; @@ -26,6 +27,7 @@ name = "type_of", description = "Returns the type of a value.", autoRegister = false) +@GenerateUncached public abstract class TypeOfNode extends Node { public abstract Object execute(@AcceptsError Object value); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java new file mode 100644 index 000000000000..525616482a13 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -0,0 +1,227 @@ +package org.enso.interpreter.node.expression.builtin.ordering; + +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; +import java.util.Arrays; +import org.enso.interpreter.dsl.AcceptsError; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; +import org.enso.interpreter.node.expression.builtin.meta.EqualsNode; +import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode; +import org.enso.interpreter.node.expression.builtin.text.AnyToTextNode; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.data.Array; +import org.enso.interpreter.runtime.data.ArrayRope; +import org.enso.interpreter.runtime.data.Vector; +import org.enso.interpreter.runtime.data.text.Text; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.error.Warning; +import org.enso.interpreter.runtime.error.WarningsLibrary; +import org.enso.interpreter.runtime.error.WithWarnings; +import org.enso.interpreter.runtime.util.Collections.ArrayListObj; + +/** + * Sorts a vector with elements that have only Default_Comparator, thus, only elements with a + * builtin type, which is the most common scenario for sorting. + * + * TODO: Max number of attached Incomparable values warnings? + * - hardcode or pass via a new parameter to Vector.sort? + */ +@BuiltinMethod( + type = "Vector", + name = "sort_builtin", + description = "Returns a sorted vector." +) +@GenerateUncached +public abstract class SortVectorNode extends Node { + public static SortVectorNode build() { + return SortVectorNodeGen.create(); + } + + /** + * Sorts a vector with elements that have only Default_Comparator, thus, only builtin types. + * + * @param self Vector that has elements with only Default_Comparator, that are elements with + * builtin types. + * @param ascending -1 for descending, 1 for ascending + * @return A new, sorted vector + */ + public abstract Object execute(@AcceptsError Object self, long ascending); + + @Specialization(guards = { + "interop.hasArrayElements(self)" + }) + Object sortCached(Object self, long ascending, + @Cached LessThanNode lessThanNode, + @Cached EqualsNode equalsNode, + @Cached HostValueToEnsoNode hostValueToEnsoNode, + @Cached TypeOfNode typeOfNode, + @Cached AnyToTextNode toTextNode, + @CachedLibrary(limit = "10") InteropLibrary interop, + @CachedLibrary(limit = "5") WarningsLibrary warningsLib) { + EnsoContext ctx = EnsoContext.get(this); + Object[] elems; + try { + long size = interop.getArraySize(self); + assert size < Integer.MAX_VALUE; + elems = new Object[(int) size]; + for (int i = 0; i < size; i++) { + if (interop.isArrayElementReadable(self, i)) { + elems[i] = hostValueToEnsoNode.execute( + interop.readArrayElement(self, i) + ); + } else { + throw new PanicException( + ctx.getBuiltins().error().makeUnsupportedArgumentsError( + new Object[]{self}, + "Cannot read array element at index " + i + " of " + self + ), + this + ); + } + } + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + throw new IllegalStateException(e); + } + var comparator = new Comparator(lessThanNode, equalsNode, typeOfNode, toTextNode, ascending > 0); + Arrays.sort(elems, comparator); + var vector = Vector.fromArray(new Array(elems)); + + // Check for the warnings attached from the Comparator + Warning[] currWarns = null; + if (comparator.encounteredWarnings()) { + currWarns = (Warning[]) comparator.getWarnings(); + } + if (currWarns != null) { + return WithWarnings.appendTo(vector, new ArrayRope<>(currWarns)); + } else { + return vector; + } + } + + private int typeOrder(Object object, TypeOfNode typeOfNode) { + var ctx = EnsoContext.get(this); + var builtins = ctx.getBuiltins(); + if (isNothing(object, ctx)) { + return 200; + } + var type = typeOfNode.execute(object); + if (type == builtins.number().getNumber() + || type == builtins.number().getInteger() + || type == builtins.number().getDecimal()) { + if (object instanceof Double dbl && dbl.isNaN()) { + return 100; + } else { + return 1; + } + } + else if (type == builtins.text()) { + return 2; + } + else if (type == builtins.bool().getType()) { + return 3; + } + else if (type == builtins.date()) { + return 4; + } + else if (type == builtins.dateTime()) { + return 5; + } + else if (type == builtins.duration()) { + return 6; + } else { + throw new IllegalStateException("Unexpected type: " + type); + } + } + + private boolean isTrue(Object object) { + return Boolean.TRUE.equals(object); + } + + private boolean isNothing(Object object) { + return isNothing(object, EnsoContext.get(this)); + } + + private boolean isNothing(Object object, EnsoContext ctx) { + return object == ctx.getBuiltins().nothing(); + } + + private final class Comparator implements java.util.Comparator { + + private final LessThanNode lessThanNode; + private final EqualsNode equalsNode; + private final TypeOfNode typeOfNode; + private final AnyToTextNode toTextNode; + private final boolean ascending; + private final ArrayListObj warnings = new ArrayListObj<>(); + + private Comparator(LessThanNode lessThanNode, EqualsNode equalsNode, TypeOfNode typeOfNode, + AnyToTextNode toTextNode, boolean ascending) { + this.lessThanNode = lessThanNode; + this.equalsNode = equalsNode; + this.typeOfNode = typeOfNode; + this.toTextNode = toTextNode; + this.ascending = ascending; + } + + @Override + public int compare(Object x, Object y) { + if (equalsNode.execute(x, y)) { + return 0; + } else { + // Check if x < y + Object xLessThanYRes = lessThanNode.execute(x, y); + if (isNothing(xLessThanYRes)) { + // x and y are incomparable - this can happen if x and y are different types + attachIncomparableValuesWarning(x, y); + return compareTypes(x, y); + } else if (isTrue(xLessThanYRes)) { + return ascending ? -1 : 1; + } else { + // Check if x > y + Object yLessThanXRes = lessThanNode.execute(y, x); + if (isTrue(yLessThanXRes)) { + return ascending ? 1 : -1; + } else { + // yLessThanXRes is either Nothing or False + attachIncomparableValuesWarning(y, x); + return compareTypes(y, x); + } + } + } + } + + private int compareTypes(Object x, Object y) { + int res =Integer.compare( + typeOrder(x, typeOfNode), + typeOrder(y, typeOfNode) + ); + return ascending ? res : -res; + } + + private void attachIncomparableValuesWarning(Object x, Object y) { + var xStr = toTextNode.execute(x).toString(); + var yStr = toTextNode.execute(y).toString(); + var payload = Text.create("Values " + xStr + " and " + yStr + " are incomparable"); + var sortNode = SortVectorNode.this; + var warn = Warning.create(EnsoContext.get(sortNode), payload, sortNode); + warnings.add(warn); + } + + private boolean encounteredWarnings() { + return warnings.size() > 0; + } + + private Object[] getWarnings() { + Warning[] warns = new Warning[warnings.size()]; + warns = warnings.toArray(warns.getClass()); + return warns; + } + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java new file mode 100644 index 000000000000..0917b46c635e --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java @@ -0,0 +1,78 @@ +package org.enso.interpreter.runtime.util; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.profiles.BranchProfile; +import java.util.Arrays; + +public class Collections { + + /** PE-friendly implementation of ArrayList. */ + public static final class ArrayListObj { + private Object[] data; + private int size; + + public ArrayListObj(int capacity) { + this.data = new Object[capacity]; + } + + public ArrayListObj() { + this(8); + } + + public void add(T value) { + add(value, BranchProfile.getUncached()); + } + + public void add(T value, BranchProfile capacityExceededProfile) { + if (size == data.length) { + capacityExceededProfile.enter(); + data = Arrays.copyOf(data, size * 2); + } + data[size++] = value; + } + + public void set(int index, T value) { + checkIndex(index); + data[index] = value; + } + + @SuppressWarnings("unchecked") + public T get(int index) { + checkIndex(index); + return (T) data[index]; + } + + @SuppressWarnings("unchecked") + public T remove(int index) { + checkIndex(index); + T result = (T) data[index]; + int lastIdx = size - 1; + int toMoveLen = lastIdx - index; + if (toMoveLen > 0) { + System.arraycopy(data, index + 1, data, index, toMoveLen); + } + data[lastIdx] = null; + size--; + return result; + } + + public int size() { + return size; + } + + public Object[] toArray() { + return Arrays.copyOf(data, size); + } + + public T[] toArray(Class newType) { + return Arrays.copyOf(data, size, newType); + } + + private void checkIndex(int index) { + if (!(0 <= index && index < size)) { + CompilerDirectives.transferToInterpreter(); + throw new IndexOutOfBoundsException(index); + } + } + } +} From e57c4ae5bf2e8e33379038488126320df799a022 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 20 Mar 2023 19:16:14 +0100 Subject: [PATCH 05/49] Implement sort handling for different comparators --- .../Base/0.0.0-dev/src/Data/Vector.enso | 40 ++++++--- .../builtin/ordering/SortVectorNode.java | 45 ++++++---- test/Tests/src/Data/Ordering_Spec.enso | 89 ++++++++++++++++--- 3 files changed, 129 insertions(+), 45 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index 58036210cb13..3f0a90d18303 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -18,11 +18,14 @@ import project.Error.Error import project.Error.Illegal_Argument.Illegal_Argument import project.Function.Function import project.Math +import project.Meta import project.Nothing.Nothing import project.Panic.Panic import project.Random import project.Warning.Warning +import project.IO + # We have to import also conversion methods, therefore, we import all from the Ordering # module from project.Data.Ordering import all @@ -873,9 +876,9 @@ type Vector a This case is handled by first grouping the `self` vector into groups with the same comparator, recursively sorting these groups, and then merging them back together. The order of the sorted groups in the - resulting vector is based on the order of elements' comparators in the - `self` vector, with the exception of the group for the default - comparator, which is always the first group. + resulting vector is based on the order of fully qualified names of + the comparators in the `self` vector, with the exception of the group + for the default comparator, which is always the first group. Additionally, a warning will be attached, explaining that incomparable values were encountered. @@ -912,17 +915,18 @@ type Vector a elems = if on == Nothing then self else self.map it-> on it elems.sort_builtin order.to_sign False -> + # The "default value" of `by` parameter is `Ordering.compare _ _`, but because + # there is no function equality, the real default value is Nothing. + by_non_null = if by == Nothing then (Ordering.compare _ _) else by # Groups of elements with different comparators - groups = comps.distinct.map comp-> + groups = comps.map comp-> self.filter it-> Comparable.from (on it) == comp - # TODO: Runtime.assert groups.reduce 0 acc->it-> acc + it.length == self.length case groups.length of - # self consists only of elements with the same comparator. + # self consists only of elements with the same comparator, that + # is not `Default_Comparator`. # Forward to Array.sort 1 -> - # The default value of `by` parameter is `Ordering.compare _ _` - by_non_null = if by == Nothing then (Ordering.compare _ _) else by comp_ascending l r = by_non_null (on l) (on r) comp_descending l r = by_non_null (on r) (on l) compare = if order == Sort_Direction.Ascending then comp_ascending else @@ -931,11 +935,21 @@ type Vector a if new_vec_arr.is_error then Error.throw new_vec_arr else Vector.from_polyglot_array new_vec_arr _ -> - # Recurse on each group, and attach a warning - # TODO: Sort Default_Comparator group as the first one? - sorted_groups = groups.map it-> it.sort order on by - comparators_text = comps.distinct.to_text - Warning.attach ("Different comparators: " + comparators_text) sorted_groups.flatten + # Partition into groups sorted by FQN of their comparator's name, + # with the default comparator as the first group. + groups_with_comps_sorted_by_fqn = groups.zip comps . sort by=pair_1->pair_2-> + comp_1 = pair_1.last + comp_2 = pair_2.last + if comp_1 == Default_Comparator then Ordering.Less else + if comp_2 == Default_Comparator then Ordering.Greater else + comp_1_fqn = Meta.get_qualified_type_name comp_1 + comp_2_fqn = Meta.get_qualified_type_name comp_2 + Ordering.compare comp_1_fqn comp_2_fqn + # Recurse on each group + sorted_groups = groups_with_comps_sorted_by_fqn.map it-> it.first.sort order on by + # Merge the groups and attach a warning + comparators_warn_text = groups_with_comps_sorted_by_fqn.map (it-> it.last) . to_text + Warning.attach ("Different comparators: " + comparators_warn_text) sorted_groups.flatten ## UNSTABLE Keeps only unique elements within the Vector, removing any duplicates. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 525616482a13..fe5ea13a988b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -1,5 +1,7 @@ package org.enso.interpreter.node.expression.builtin.ordering; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; @@ -8,7 +10,10 @@ 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 java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import org.enso.interpreter.dsl.AcceptsError; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; @@ -24,7 +29,6 @@ import org.enso.interpreter.runtime.error.Warning; import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.error.WithWarnings; -import org.enso.interpreter.runtime.util.Collections.ArrayListObj; /** * Sorts a vector with elements that have only Default_Comparator, thus, only elements with a @@ -63,6 +67,7 @@ Object sortCached(Object self, long ascending, @Cached HostValueToEnsoNode hostValueToEnsoNode, @Cached TypeOfNode typeOfNode, @Cached AnyToTextNode toTextNode, + @Cached BranchProfile warningEncounteredProfile, @CachedLibrary(limit = "10") InteropLibrary interop, @CachedLibrary(limit = "5") WarningsLibrary warningsLib) { EnsoContext ctx = EnsoContext.get(this); @@ -93,13 +98,15 @@ Object sortCached(Object self, long ascending, Arrays.sort(elems, comparator); var vector = Vector.fromArray(new Array(elems)); - // Check for the warnings attached from the Comparator - Warning[] currWarns = null; if (comparator.encounteredWarnings()) { - currWarns = (Warning[]) comparator.getWarnings(); - } - if (currWarns != null) { - return WithWarnings.appendTo(vector, new ArrayRope<>(currWarns)); + warningEncounteredProfile.enter(); + CompilerDirectives.transferToInterpreter(); + Warning[] warns = comparator.getWarnings() + .stream() + .map(Text::create) + .map(text -> Warning.create(EnsoContext.get(this), text, this)) + .toArray(Warning[]::new); + return WithWarnings.appendTo(vector, new ArrayRope<>(warns)); } else { return vector; } @@ -135,7 +142,12 @@ else if (type == builtins.dateTime()) { } else if (type == builtins.duration()) { return 6; - } else { + } + else if (type == builtins.vector()) { + // vectors are incomparable, but we want to sort them before Nothings and NaNs. + return 50; + } + else { throw new IllegalStateException("Unexpected type: " + type); } } @@ -159,7 +171,7 @@ private final class Comparator implements java.util.Comparator { private final TypeOfNode typeOfNode; private final AnyToTextNode toTextNode; private final boolean ascending; - private final ArrayListObj warnings = new ArrayListObj<>(); + private final Set warnings = new HashSet<>(); private Comparator(LessThanNode lessThanNode, EqualsNode equalsNode, TypeOfNode typeOfNode, AnyToTextNode toTextNode, boolean ascending) { @@ -205,23 +217,20 @@ private int compareTypes(Object x, Object y) { return ascending ? res : -res; } + @TruffleBoundary private void attachIncomparableValuesWarning(Object x, Object y) { var xStr = toTextNode.execute(x).toString(); var yStr = toTextNode.execute(y).toString(); - var payload = Text.create("Values " + xStr + " and " + yStr + " are incomparable"); - var sortNode = SortVectorNode.this; - var warn = Warning.create(EnsoContext.get(sortNode), payload, sortNode); - warnings.add(warn); + String warnText = "Values " + xStr + " and " + yStr + " are incomparable"; + warnings.add(warnText); } private boolean encounteredWarnings() { - return warnings.size() > 0; + return !warnings.isEmpty(); } - private Object[] getWarnings() { - Warning[] warns = new Warning[warnings.size()]; - warns = warnings.toArray(warns.getClass()); - return warns; + private Set getWarnings() { + return warnings; } } } diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index 3be7f830507a..7e66431e948c 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -16,7 +16,18 @@ type Ord_Comparator Comparable.from (_:Ord) = Ord_Comparator -## Unordered pair + +type My_Type + Value val + +type My_Type_Comparator + compare x y = (Comparable.from x.val).compare x.val y.val + hash x = (Comparable.from x.val).hash x.val + +Comparable.from (_:My_Type) = My_Type_Comparator + + +## Unordered pair - its `compare` method returns either `Nothing` or `Ordering.Equal`. type UPair Value x y @@ -98,7 +109,7 @@ spec = Ordering.compare Ordering.Less Nothing . should_fail_with Type_Error Ordering.compare Ordering.Less "Hello" . should_fail_with Type_Error - Test.group "Sorting" <| + Test.group "Sorting with the default comparator" <| ## Expects that `result` contains incomparable values warning. The values within the warning message can be switched - the order does not matter. Iterates through all the warnings of result. @@ -112,6 +123,16 @@ spec = has_expected_warning = Warning.get_all result . map (_.value) . any (it-> it == expected_warn_msg_left || it == expected_warn_msg_right) has_expected_warning . should_be_true + # Same as `expect_incomparable_warn`, but does not check for a particular + # warning message + expect_some_incomparable_warn result = + expected_pattern = "Values .+ and .+ are incomparable" + has_expected_warning = Warning.get_all result . map (_.value) . any (it-> ) TODO... + + expect_no_warns : Any -> Nothing + expect_no_warns result = + Warning.get_all result . length . should_equal 0 + Test.specify "should be able to sort primitive types" <| [3, 2, 1, Nothing].sort . should_equal [1, 2, 3, Nothing] [Nothing, Number.nan].sort . at 0 . is_nan . should_be_true @@ -128,6 +149,26 @@ spec = [3, True, 2, False].sort . should_equal [2, 3, False, True] [Nothing, False].sort . should_equal [False, Nothing] + Test.specify "should be able to sort any single-element vector without any warnings" <| + [Nothing].sort . should_equal [Nothing] + expect_no_warns [Nothing].sort + [[Nothing]].sort . should_equal [[Nothing]] + expect_no_warns [[Nothing]].sort + [Number.nan].sort . should_equal [Number.nan] + expect_no_warns [Nothing].sort + [[Number.nan]].sort . should_equal [[Number.nan]] + expect_no_warns [[Number.nan]].sort + + Test.specify "should produce warnings when sorting nested vectors" <| + [[1], [2]].sort . should_equal [[1], [2]] + [[2], [1]].sort . should_equal [[2], [1]] + + Test.specify "should be able to sort primitive values in atoms" <| + [Ord.Value Number.nan, Ord.Value 20, Ord.Value 10].sort . should_equal [Ord.Value 10, Ord.Value 20, Ord.Value Number.nan] + + Test.specify "should produce warnings when sorting primitive values in atoms" <| + expect_incomparable_warn 1 Number.nan [Ord.Value 1, Ord.Value Number.nan].sort + Test.specify "should attach warning when trying to sort incomparable values" <| expect_incomparable_warn Nothing Number.nan <| [Nothing, Number.nan].sort expect_incomparable_warn 1 "hello" <| [1, "hello"].sort @@ -142,22 +183,42 @@ spec = expect_incomparable_warn 1 Number.nan [1, Warning.attach "my_warn" Number.nan].sort Problems.expect_warning "my_warn" <| [1, Warning.attach "my_warn" Number.nan].sort . at 1 - Test.specify "should be able to sort types with different comparators" <| - [Ord.Value 4, Ord.Value 3, 20, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20] - [Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20] + Test.group "Sorting with multiple comparators" <| + Test.specify "should sort primitive values with the default comparator as the first group" <| + [Ord.Value 4, Ord.Value 3, 20, 10].sort . should_equal [10, 20, Ord.Value 3, Ord.Value 4] + [Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [10, 20, Ord.Value 3, Ord.Value 4] [20, Ord.Value 4, Ord.Value 3, 10].sort . should_equal [10, 20, Ord.Value 3, Ord.Value 4] - [Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20] - [Nothing, Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [Ord.Value 3, Ord.Value 4, 10, 20, Nothing] + [Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [10, 20, Ord.Value 3, Ord.Value 4] + [Nothing, Ord.Value 4, 20, Ord.Value 3, 10].sort . should_equal [10, 20, Nothing, Ord.Value 3, Ord.Value 4] + [Ord.Value 4, 20, Ord.Value 3, Nothing, 10].sort . should_equal [10, 20, Nothing, Ord.Value 3, Ord.Value 4] Test.specify "should produce warning when sorting types with different comparators" <| [Ord.Value 1, 1].sort . should_equal [Ord.Value 1, 1] - Problems.expect_warning "Different comparators: [Ord_Comparator, Default_Comparator]" <| [Ord.Value 1, 1].sort - - Test.specify "should be able to sort primitive values in atoms" <| - [Ord.Value Number.nan, Ord.Value 20, Ord.Value 10].sort . should_equal [Ord.Value 10, Ord.Value 20, Ord.Value Number.nan] - - Test.specify "should produce warnings when sorting primitive values in atoms" <| - expect_incomparable_warn 1 Number.nan [Ord.Value 1, Ord.Value Number.nan].sort + Problems.expect_warning "Different comparators: [Default_Comparator, Ord_Comparator]" <| [Ord.Value 1, 1].sort + + Test.specify "should merge groups of values with custom comparators based on the comparators FQN" <| + [Ord.Value 1, My_Type.Value 1].sort . should_equal [My_Type.Value 1, Ord.Value 1] + Problems.expect_warning "Different comparators: [My_Type_Comparator, Ord_Comparator]" <| [Ord.Value 1, My_Type.Value 1].sort + + Test.specify "should be stable when sorting values with different comparators" <| + [Ord.Value 1, 20, My_Type.Value 1, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] + [20, Ord.Value 1, My_Type.Value 1, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] + [20, My_Type.Value 1, Ord.Value 1, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] + [20, 10, My_Type.Value 1, Ord.Value 1].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] + [My_Type.Value 1, Ord.Value 1, 20, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] + [Ord.Value 1, 20, 10, My_Type.Value 1].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] + + # In other words, if there are incomparable values, make sure that warning for + # "incomparable value X Y" is attached just once. + Test.specify "should not duplicate warnings" <| + Problems.get_attached_warnings ([Nothing, (Ord.Value 1), 20].sort) . should_equal ["Different comparators: [Default_Comparator, Ord_Comparator]", "Values 20 and Nothing are incomparable"] + + Test.specify "should be able to sort even unordered values" pending="https://github.com/enso-org/enso/issues/5834" <| + [Ord.Value 2, UPair.Value "a" "b", Ord.Value 1, UPair.Value "c" "d"].sort . should_equal [Ord.Value 2, Ord.Value 1, UPair.Value "a" "b", UPair.Value "c" "d"] + [Ord.Value 2, UPair.Value "X" "Y", Ord.Value 1, UPair.Value "c" "d"].sort . should_equal [Ord.Value 2, Ord.Value 1, UPair.Value "X" "Y", UPair.Value "c" "d"] + + Test.specify "should produce warning when sorting unordered values" pending="https://github.com/enso-org/enso/issues/5834" <| + expect_incomparable_warn (UPair.Value 1 2) (UPair.Value 3 4) [UPair.Value 1 2, UPair.Value 3 4].sort From 603ab95a184871a49ac8b21fe5f8bd4122647aca Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 21 Mar 2023 15:59:46 +0100 Subject: [PATCH 06/49] Comparison operators in Any do not throw Type_Error --- .../lib/Standard/Base/0.0.0-dev/src/Any.enso | 15 +++++---- .../Base/0.0.0-dev/src/Data/Ordering.enso | 3 +- test/Tests/src/Data/Numbers_Spec.enso | 31 +++++++++---------- test/Tests/src/Data/Ordering_Spec.enso | 15 +++++---- test/Tests/src/Data/Text_Spec.enso | 2 +- 5 files changed, 31 insertions(+), 35 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso index b78e2bbfe230..9df3f1702c15 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso @@ -4,7 +4,6 @@ import project.Data.Text.Text import project.Error.Error import project.Error.Common.Incomparable_Values import project.Error.Common.No_Such_Conversion -import project.Error.Common.Type_Error import project.Nothing.Nothing import project.Meta import project.Panic.Panic @@ -165,7 +164,7 @@ type Any example_greater = a = 7 * 28 a > 147 - > : Any -> Boolean ! (Incomparable_Values | Type_Error) + > : Any -> Boolean ! Incomparable_Values > self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of @@ -194,7 +193,7 @@ type Any example_greater_eq = a = 6 * 21 a >= 147 - >= : Any -> Boolean ! (Incomparable_Values | Type_Error) + >= : Any -> Boolean ! Incomparable_Values >= self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of @@ -223,7 +222,7 @@ type Any example_less = a = 7 * 21 a < 147 - < : Any -> Boolean ! (Incomparable_Values | Type_Error) + < : Any -> Boolean ! Incomparable_Values < self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of @@ -254,7 +253,7 @@ type Any example_less_eq = a = 7 * 21 a < 147 - <= : Any -> Boolean ! (Incomparable_Values | Type_Error) + <= : Any -> Boolean ! Incomparable_Values <= self that = assert_same_comparators self that <| case (Comparable.from self).compare self that of @@ -452,11 +451,11 @@ type Any ## PRIVATE Checks if the comparators for the given objects are both of the same type. If so, - proceeds with the given action, and if not, throws `Type_Error`. -assert_same_comparators : Any -> Any -> (Any -> Any) -> Any ! Type_Error + proceeds with the given action, and if not, throws `Incomparable_Values` error. +assert_same_comparators : Any -> Any -> (Any -> Any) -> Any ! Incomparable_Values assert_same_comparators this that ~action = comp_this = Comparable.from this comp_that = Comparable.from that case Meta.is_same_object comp_this comp_that of True -> action - False -> Error.throw (Type_Error.Error (Meta.type_of comp_this) comp_that "comp_that") + False -> Error.throw (Incomparable_Values.Error this that) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso index d37b60aef8ba..53ea44886c4c 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso @@ -3,7 +3,6 @@ import project.Data.Numbers.Decimal import project.Data.Numbers.Integer import project.Data.Numbers.Number import project.Error.Common.Incomparable_Values -import project.Error.Common.Type_Error import project.Error.Error import project.Error.Illegal_State.Illegal_State import project.Error.Unimplemented.Unimplemented @@ -162,7 +161,7 @@ type Ordering Greater ## Compares to values and returns an Ordering - compare : Any -> Any -> Ordering ! (Incomparable_Values | Type_Error) + compare : Any -> Any -> Ordering ! Incomparable_Values compare x y = if x < y then Ordering.Less else if x == y then Ordering.Equal else diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index 0a4539432aec..a0af0390e9f5 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -1,6 +1,5 @@ from Standard.Base import all import Standard.Base.Error.Common.Arithmetic_Error -import Standard.Base.Error.Common.Type_Error from Standard.Base.Data.Numbers import Number_Parse_Error @@ -297,21 +296,21 @@ spec = (1 < 0.99).should_be_false (3 < hundred_factorial).should_be_true (3 < very_negative).should_be_false - (3 < Nothing).should_fail_with Type_Error + (3 < Nothing).should_fail_with Incomparable_Values (1.01 < 0.99).should_be_false (1.01 < 1.02).should_be_true (1.01 < 1).should_be_false (1.01 < 2).should_be_true (3.14 < hundred_factorial).should_be_true (3.14 < very_negative).should_be_false - (1.5 < Nothing).should_fail_with Type_Error + (1.5 < Nothing).should_fail_with Incomparable_Values (hundred_factorial < 1).should_be_false (hundred_factorial < 1.5).should_be_false (very_negative < 1).should_be_true (very_negative < 1.5).should_be_true (hundred_factorial < very_negative).should_be_false (very_negative < hundred_factorial).should_be_true - (very_negative < Nothing).should_fail_with Type_Error + (very_negative < Nothing).should_fail_with Incomparable_Values Test.specify "should support less than or equal to operator" <| (1 <= 2).should_be_true @@ -321,21 +320,21 @@ spec = (1 <= 0.99).should_be_false (3 <= hundred_factorial).should_be_true (3 <= very_negative).should_be_false - (3 <= Nothing).should_fail_with Type_Error + (3 <= Nothing).should_fail_with Incomparable_Values (1.01 <= 0.99).should_be_false (1.01 <= 1.02).should_be_true (1.01 <= 1).should_be_false (1.01 <= 2).should_be_true (3.14 <= hundred_factorial).should_be_true (3.14 <= very_negative).should_be_false - (1.5 <= Nothing).should_fail_with Type_Error + (1.5 <= Nothing).should_fail_with Incomparable_Values (hundred_factorial <= 1).should_be_false (hundred_factorial <= 1.5).should_be_false (very_negative <= 1).should_be_true (very_negative <= 1.5).should_be_true (hundred_factorial <= very_negative).should_be_false (very_negative <= hundred_factorial).should_be_true - (very_negative <= Nothing).should_fail_with Type_Error + (very_negative <= Nothing).should_fail_with Incomparable_Values Test.specify "should support greater than operator" <| (1 > 2).should_be_false @@ -345,21 +344,21 @@ spec = (1 > 0.99).should_be_true (3 > hundred_factorial).should_be_false (3 > very_negative).should_be_true - (3 > Nothing).should_fail_with Type_Error + (3 > Nothing).should_fail_with Incomparable_Values (1.01 > 0.99).should_be_true (1.01 > 1.02).should_be_false (1.01 > 1).should_be_true (1.01 > 2).should_be_false (3.14 > hundred_factorial).should_be_false (3.14 > very_negative).should_be_true - (1.5 > Nothing).should_fail_with Type_Error + (1.5 > Nothing).should_fail_with Incomparable_Values (hundred_factorial > 1).should_be_true (hundred_factorial > 1.5).should_be_true (very_negative > 1).should_be_false (very_negative > 1.5).should_be_false (hundred_factorial > very_negative).should_be_true (very_negative > hundred_factorial).should_be_false - (very_negative > Nothing).should_fail_with Type_Error + (very_negative > Nothing).should_fail_with Incomparable_Values Test.specify "should support greater than or equal to operator" <| (1 >= 2).should_be_false @@ -369,21 +368,21 @@ spec = (1 >= 0.99).should_be_true (3 >= hundred_factorial).should_be_false (3 >= very_negative).should_be_true - (3 >= Nothing).should_fail_with Type_Error + (3 >= Nothing).should_fail_with Incomparable_Values (1.01 >= 0.99).should_be_true (1.01 >= 1.02).should_be_false (1.01 >= 1).should_be_true (1.01 >= 2).should_be_false (3.14 >= hundred_factorial).should_be_false (3.14 >= very_negative).should_be_true - (1.5 >= Nothing).should_fail_with Type_Error + (1.5 >= Nothing).should_fail_with Incomparable_Values (hundred_factorial >= 1).should_be_true (hundred_factorial >= 1.5).should_be_true (very_negative >= 1).should_be_false (very_negative >= 1.5).should_be_false (hundred_factorial >= very_negative).should_be_true (very_negative >= hundred_factorial).should_be_false - (very_negative >= Nothing).should_fail_with Type_Error + (very_negative >= Nothing).should_fail_with Incomparable_Values Test.specify "should be ordered by the default comparator" <| Ordering.compare 1 2 . should_equal Ordering.Less @@ -393,21 +392,21 @@ spec = Ordering.compare 1 0.99 . should_equal Ordering.Greater Ordering.compare 3 hundred_factorial . should_equal Ordering.Less Ordering.compare 3 very_negative . should_equal Ordering.Greater - Ordering.compare 3 Nothing . should_fail_with Type_Error + Ordering.compare 3 Nothing . should_fail_with Incomparable_Values Ordering.compare 1.01 0.99 . should_equal Ordering.Greater Ordering.compare 1.01 1.02 . should_equal Ordering.Less Ordering.compare 1.01 1 . should_equal Ordering.Greater Ordering.compare 1.01 2 . should_equal Ordering.Less Ordering.compare 3.14 hundred_factorial . should_equal Ordering.Less Ordering.compare 3.14 very_negative . should_equal Ordering.Greater - Ordering.compare 1.5 Nothing . should_fail_with Type_Error + Ordering.compare 1.5 Nothing . should_fail_with Incomparable_Values Ordering.compare hundred_factorial 1 . should_equal Ordering.Greater Ordering.compare hundred_factorial 1.5 . should_equal Ordering.Greater Ordering.compare very_negative 1 . should_equal Ordering.Less Ordering.compare very_negative 1.5 . should_equal Ordering.Less Ordering.compare hundred_factorial very_negative . should_equal Ordering.Greater Ordering.compare very_negative hundred_factorial . should_equal Ordering.Less - Ordering.compare very_negative Nothing . should_fail_with Type_Error + Ordering.compare very_negative Nothing . should_fail_with Incomparable_Values Test.specify "should expose exponentiation operations" <| (3.14 ^ 2.71).should_equal 22.216689546 epsilon=eps diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index 7e66431e948c..ae190ce206b9 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -1,6 +1,5 @@ from Standard.Base import all import Standard.Base.Error.Common.Incomparable_Values -import Standard.Base.Error.Common.Type_Error from Standard.Test import Test, Test_Suite, Problems import Standard.Test.Extensions @@ -61,9 +60,9 @@ spec = ((Parent.Value (Ord.Value 1)) == (Parent.Value (Ord.Value 1))) . should_be_true ((Parent.Value (Ord.Value 1)) == (Parent.Value (Ord.Value 22))) . should_be_false - Test.specify "should throw Type_Error when comparing different types" <| - Ordering.compare (UPair.Value 1 2) (Ord.Value 2) . should_fail_with Type_Error - Ordering.compare 1 Nothing . should_fail_with Type_Error + Test.specify "should throw Incomparable_Values when comparing different types" <| + Ordering.compare (UPair.Value 1 2) (Ord.Value 2) . should_fail_with Incomparable_Values + Ordering.compare 1 Nothing . should_fail_with Incomparable_Values Test.group "Ordering" <| Test.specify "should allow conversion to sign representation" <| @@ -104,10 +103,10 @@ spec = Ordering.compare 42.5 67.9 . should_equal Ordering.Less Meta.is_same_object (Comparable.from Number.nan) (Comparable.from 42.0) . should_be_true - Test.specify "should fail with Type_Error for wrong type of that" <| - Ordering.compare Ordering.Less 1 . should_fail_with Type_Error - Ordering.compare Ordering.Less Nothing . should_fail_with Type_Error - Ordering.compare Ordering.Less "Hello" . should_fail_with Type_Error + Test.specify "should fail with Incomparable_Values for wrong type of that" <| + Ordering.compare Ordering.Less 1 . should_fail_with Incomparable_Values + Ordering.compare Ordering.Less Nothing . should_fail_with Incomparable_Values + Ordering.compare Ordering.Less "Hello" . should_fail_with Incomparable_Values Test.group "Sorting with the default comparator" <| ## Expects that `result` contains incomparable values warning. diff --git a/test/Tests/src/Data/Text_Spec.enso b/test/Tests/src/Data/Text_Spec.enso index a5e7a5a5ef7d..c510359a3017 100644 --- a/test/Tests/src/Data/Text_Spec.enso +++ b/test/Tests/src/Data/Text_Spec.enso @@ -119,7 +119,7 @@ spec = (accent_1 != Nothing) . should_be_true Ordering.compare accent_1 Nothing . should_fail_with Incomparable_Values (accent_1 > Nothing) . should_fail_with Incomparable_Values - accent_1 . compare_to_ignore_case Nothing . should_fail_with Type_Error + accent_1 . compare_to_ignore_case Nothing . should_fail_with Incomparable_Values earlier_suffix = "aooooz" later_suffix = "bo" From 95c0878f48aeafa9967322927bc4f7caa77601bb Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 21 Mar 2023 16:09:51 +0100 Subject: [PATCH 07/49] Fix some issues in Ordering_Spec --- test/Tests/src/Data/Ordering_Spec.enso | 50 ++++++++++++-------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index ae190ce206b9..d55d98653877 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -46,8 +46,26 @@ Comparable.from (_ : UPair) = UPair_Comparator type Parent Value child -# === The Tests === +## Expects that `result` contains incomparable values warning. + The values within the warning message can be switched - the order + does not matter. Iterates through all the warnings of result. +expect_incomparable_warn : Any -> Any -> Any -> Nothing +expect_incomparable_warn left_val right_val result = + # Incomparable values warning wraps Text values in simple quotes + left_val_text = if Meta.is_a left_val Text then "'" + left_val + "'" else left_val.to_text + right_val_text = if Meta.is_a right_val Text then "'" + right_val + "'" else right_val.to_text + expected_warn_msg_left = "Values " + left_val_text + " and " + right_val_text + " are incomparable" + expected_warn_msg_right = "Values " + right_val_text + " and " + left_val_text + " are incomparable" + has_expected_warning = Warning.get_all result . map (_.value) . any (it-> it == expected_warn_msg_left || it == expected_warn_msg_right) + has_expected_warning . should_be_true + +expect_no_warns : Any -> Nothing +expect_no_warns result = + Warning.get_all result . length . should_equal 0 + + +# === The Tests === spec = Test.group "Default comparator" <| Test.specify "should support custom comparator" <| @@ -109,28 +127,6 @@ spec = Ordering.compare Ordering.Less "Hello" . should_fail_with Incomparable_Values Test.group "Sorting with the default comparator" <| - ## Expects that `result` contains incomparable values warning. - The values within the warning message can be switched - the order - does not matter. Iterates through all the warnings of result. - expect_incomparable_warn : Any -> Any -> Any -> Nothing - expect_incomparable_warn left_val right_val result = - # Incomparable values warning wraps Text values in simple quotes - left_val_text = if Meta.is_a left_val Text then "'" + left_val + "'" else left_val.to_text - right_val_text = if Meta.is_a right_val Text then "'" + right_val + "'" else right_val.to_text - expected_warn_msg_left = "Values " + left_val_text + " and " + right_val_text + " are incomparable" - expected_warn_msg_right = "Values " + right_val_text + " and " + left_val_text + " are incomparable" - has_expected_warning = Warning.get_all result . map (_.value) . any (it-> it == expected_warn_msg_left || it == expected_warn_msg_right) - has_expected_warning . should_be_true - - # Same as `expect_incomparable_warn`, but does not check for a particular - # warning message - expect_some_incomparable_warn result = - expected_pattern = "Values .+ and .+ are incomparable" - has_expected_warning = Warning.get_all result . map (_.value) . any (it-> ) TODO... - - expect_no_warns : Any -> Nothing - expect_no_warns result = - Warning.get_all result . length . should_equal 0 Test.specify "should be able to sort primitive types" <| [3, 2, 1, Nothing].sort . should_equal [1, 2, 3, Nothing] @@ -153,10 +149,8 @@ spec = expect_no_warns [Nothing].sort [[Nothing]].sort . should_equal [[Nothing]] expect_no_warns [[Nothing]].sort - [Number.nan].sort . should_equal [Number.nan] - expect_no_warns [Nothing].sort - [[Number.nan]].sort . should_equal [[Number.nan]] - expect_no_warns [[Number.nan]].sort + [[1]].sort . should_equal [[1]] + expect_no_warns [[1]].sort Test.specify "should produce warnings when sorting nested vectors" <| [[1], [2]].sort . should_equal [[1], [2]] @@ -166,7 +160,7 @@ spec = [Ord.Value Number.nan, Ord.Value 20, Ord.Value 10].sort . should_equal [Ord.Value 10, Ord.Value 20, Ord.Value Number.nan] Test.specify "should produce warnings when sorting primitive values in atoms" <| - expect_incomparable_warn 1 Number.nan [Ord.Value 1, Ord.Value Number.nan].sort + expect_incomparable_warn (Ord.Value 1) (Ord.Value Nothing) [Ord.Value 1, Ord.Value Nothing].sort Test.specify "should attach warning when trying to sort incomparable values" <| expect_incomparable_warn Nothing Number.nan <| [Nothing, Number.nan].sort From 0df66b10806036594cf51b13df6062ab32680689 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 10 Feb 2023 20:31:13 +0100 Subject: [PATCH 08/49] Remove the remaining comparison operator overrides for numbers. --- .../Base/0.0.0-dev/src/Data/Numbers.enso | 97 +------------------ .../number/bigInteger/GreaterNode.java | 42 -------- .../number/bigInteger/GreaterOrEqualNode.java | 42 -------- .../builtin/number/bigInteger/LessNode.java | 42 -------- .../number/bigInteger/LessOrEqualNode.java | 42 -------- .../builtin/number/decimal/GreaterNode.java | 42 -------- .../number/decimal/GreaterOrEqualNode.java | 42 -------- .../builtin/number/decimal/LessNode.java | 42 -------- .../number/decimal/LessOrEqualNode.java | 42 -------- .../number/smallInteger/GreaterNode.java | 41 -------- .../smallInteger/GreaterOrEqualNode.java | 41 -------- .../builtin/number/smallInteger/LessNode.java | 41 -------- .../number/smallInteger/LessOrEqualNode.java | 41 -------- 13 files changed, 1 insertion(+), 596 deletions(-) delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index 083e0c4f8191..c107a5b7d0a5 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -457,54 +457,6 @@ type Decimal ^ : Number -> Number ^ self that = @Builtin_Method "Decimal.^" - ## Checks if this is greater than that. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10 is greater than 7.3. - - 10 > 7.3 - > : Number -> Boolean - > self that = @Builtin_Method "Decimal.>" - - ## Checks if this is greater than or equal to thatthat. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10 is greater than or equal to 7.3. - - 10 >= 7.3 - >= : Number -> Boolean - >= self that = @Builtin_Method "Decimal.>=" - - ## Checks if this is less than that. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10 is less than 7.3. - - 10 < 7.3 - < : Number -> Boolean - < self that = @Builtin_Method "Decimal.<" - - ## Checks if this is less than or equal to thatthat. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10.4 is less than or equal to 7. - - 10.4 <= 7 - <= : Number -> Boolean - <= self that = @Builtin_Method "Decimal.<=" - ## Computes the absolute value of this. The absolute value of a positive number is itself, while the absolute @@ -682,54 +634,6 @@ type Integer ^ : Number -> Number ^ self that = @Builtin_Method "Integer.^" - ## Checks if this is greater than that. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10 is greater than 7. - - 10 > 7 - > : Number -> Boolean - > self that = @Builtin_Method "Integer.>" - - ## Checks if this is greater than or equal to thatthat. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10 is greater than or equal to 7. - - 10 >= 7 - >= : Number -> Boolean - >= self that = @Builtin_Method "Integer.>=" - - ## Checks if this is less than that. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10 is less than 7. - - 10 < 7 - < : Number -> Boolean - < self that = @Builtin_Method "Integer.<" - - ## Checks if this is less than or equal to thatthat. - - Arguments: - - that: The number to compare this against. - - > Example - Checking if 10 is less than or equal to 7. - - 10 <= 7 - <= : Number -> Boolean - <= self that = @Builtin_Method "Integer.<=" - ## Computes the absolute value of this. The absolute value of a positive number is itself, while the absolute @@ -937,6 +841,7 @@ type Integer parse_builtin text radix = @Builtin_Method "Integer.parse" + ## UNSTABLE A syntax error when parsing a double. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java deleted file mode 100644 index 963c004ec802..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.bigInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Big_Integer", name = ">", description = "Comparison of numbers.") -public abstract class GreaterNode extends Node { - - abstract Object execute(EnsoBigInteger self, Object that); - - static GreaterNode build() { - return GreaterNodeGen.create(); - } - - @Specialization - boolean doDouble(EnsoBigInteger self, double that) { - return BigIntegerOps.toDouble(self.getValue()) > that; - } - - @Specialization - boolean doLong(EnsoBigInteger self, long that) { - return self.getValue().signum() > 0; - } - - @Specialization - boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return BigIntegerOps.compare(self.getValue(), that.getValue()) > 0; - } - - @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java deleted file mode 100644 index c615457d1a15..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.bigInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Big_Integer", name = ">=", description = "Comparison of numbers.") -public abstract class GreaterOrEqualNode extends Node { - - abstract Object execute(EnsoBigInteger self, Object that); - - static GreaterOrEqualNode build() { - return GreaterOrEqualNodeGen.create(); - } - - @Specialization - boolean doDouble(EnsoBigInteger self, double that) { - return BigIntegerOps.toDouble(self.getValue()) >= that; - } - - @Specialization - boolean doLong(EnsoBigInteger self, long that) { - return self.getValue().signum() > 0; - } - - @Specialization - boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return BigIntegerOps.compare(self.getValue(), that.getValue()) >= 0; - } - - @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java deleted file mode 100644 index 71e4a55e74ac..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.bigInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Big_Integer", name = "<", description = "Comparison of numbers.") -public abstract class LessNode extends Node { - - abstract Object execute(EnsoBigInteger self, Object that); - - static LessNode build() { - return LessNodeGen.create(); - } - - @Specialization - boolean doDouble(EnsoBigInteger self, double that) { - return BigIntegerOps.toDouble(self.getValue()) < that; - } - - @Specialization - boolean doLong(EnsoBigInteger self, long that) { - return self.getValue().signum() < 0; - } - - @Specialization - boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return BigIntegerOps.compare(self.getValue(), that.getValue()) < 0; - } - - @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java deleted file mode 100644 index 5fd408ba79f7..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.bigInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Big_Integer", name = "<=", description = "Comparison of numbers.") -public abstract class LessOrEqualNode extends Node { - - abstract Object execute(EnsoBigInteger self, Object that); - - static LessOrEqualNode build() { - return LessOrEqualNodeGen.create(); - } - - @Specialization - boolean doDouble(EnsoBigInteger self, double that) { - return BigIntegerOps.toDouble(self.getValue()) <= that; - } - - @Specialization - boolean doLong(EnsoBigInteger self, long that) { - return self.getValue().signum() < 0; - } - - @Specialization - boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { - return BigIntegerOps.compare(self.getValue(), that.getValue()) <= 0; - } - - @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java deleted file mode 100644 index cf1d301edb4d..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.decimal; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Decimal", name = ">", description = "Comparison of numbers.") -public abstract class GreaterNode extends Node { - - abstract Object execute(double self, Object that); - - static GreaterNode build() { - return GreaterNodeGen.create(); - } - - @Specialization - boolean doDouble(double self, double that) { - return self > that; - } - - @Specialization - boolean doLong(double self, long that) { - return self > (double) that; - } - - @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self > BigIntegerOps.toDouble(that.getValue()); - } - - @Fallback - DataflowError doOther(double self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java deleted file mode 100644 index 00683e4cf399..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.decimal; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Decimal", name = ">=", description = "Comparison of numbers.") -public abstract class GreaterOrEqualNode extends Node { - - abstract Object execute(double self, Object that); - - static GreaterOrEqualNode build() { - return GreaterOrEqualNodeGen.create(); - } - - @Specialization - boolean doDouble(double self, double that) { - return self >= that; - } - - @Specialization - boolean doLong(double self, long that) { - return self >= (double) that; - } - - @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self >= BigIntegerOps.toDouble(that.getValue()); - } - - @Fallback - DataflowError doOther(double self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java deleted file mode 100644 index 914f28259971..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.decimal; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Decimal", name = "<", description = "Comparison of numbers.") -public abstract class LessNode extends Node { - - abstract Object execute(double self, Object that); - - static LessNode build() { - return LessNodeGen.create(); - } - - @Specialization - boolean doDouble(double self, double that) { - return self < that; - } - - @Specialization - boolean doLong(double self, long that) { - return self < (double) that; - } - - @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self < BigIntegerOps.toDouble(that.getValue()); - } - - @Fallback - DataflowError doOther(double self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java deleted file mode 100644 index 9e58ffed5818..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.decimal; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Decimal", name = "<=", description = "Comparison of numbers.") -public abstract class LessOrEqualNode extends Node { - - abstract Object execute(double self, Object that); - - static LessOrEqualNode build() { - return LessOrEqualNodeGen.create(); - } - - @Specialization - boolean doDouble(double self, double that) { - return self <= that; - } - - @Specialization - boolean doLong(double self, long that) { - return self <= (double) that; - } - - @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self <= BigIntegerOps.toDouble(that.getValue()); - } - - @Fallback - DataflowError doOther(double self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java deleted file mode 100644 index e444e925bac4..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.smallInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Small_Integer", name = ">", description = "Comparison of numbers.") -public abstract class GreaterNode extends Node { - - abstract Object execute(long self, Object that); - - static GreaterNode build() { - return GreaterNodeGen.create(); - } - - @Specialization - boolean doLong(long self, long that) { - return self > that; - } - - @Specialization - boolean doDouble(long self, double that) { - return (double) self > that; - } - - @Specialization - boolean doBigInteger(long self, EnsoBigInteger that) { - return that.getValue().signum() < 0; - } - - @Fallback - DataflowError doOther(long self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java deleted file mode 100644 index dcb316b7ff6d..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.smallInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Small_Integer", name = ">=", description = "Comparison of numbers.") -public abstract class GreaterOrEqualNode extends Node { - - abstract Object execute(long self, Object that); - - static GreaterOrEqualNode build() { - return GreaterOrEqualNodeGen.create(); - } - - @Specialization - boolean doLong(long self, long that) { - return self >= that; - } - - @Specialization - boolean doDouble(long self, double that) { - return (double) self >= that; - } - - @Specialization - boolean doBigInteger(long self, EnsoBigInteger that) { - return that.getValue().signum() < 0; - } - - @Fallback - DataflowError doOther(long self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java deleted file mode 100644 index 2bf8362b6ee3..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.smallInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Small_Integer", name = "<", description = "Comparison of numbers.") -public abstract class LessNode extends Node { - - abstract Object execute(long self, Object that); - - static LessNode build() { - return LessNodeGen.create(); - } - - @Specialization - boolean doLong(long self, long that) { - return self < that; - } - - @Specialization - boolean doDouble(long self, double that) { - return (double) self < that; - } - - @Specialization - boolean doBigInteger(long self, EnsoBigInteger that) { - return that.getValue().signum() > 0; - } - - @Fallback - DataflowError doOther(long self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java deleted file mode 100644 index e74a28297f9d..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.number.smallInteger; - -import com.oracle.truffle.api.dsl.Fallback; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.nodes.Node; -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.number.EnsoBigInteger; - -@BuiltinMethod(type = "Small_Integer", name = "<=", description = "Comparison of numbers.") -public abstract class LessOrEqualNode extends Node { - - abstract Object execute(long self, Object that); - - static LessOrEqualNode build() { - return LessOrEqualNodeGen.create(); - } - - @Specialization - boolean doLong(long self, long that) { - return self <= that; - } - - @Specialization - boolean doDouble(long self, double that) { - return (double) self <= that; - } - - @Specialization - boolean doBigInteger(long self, EnsoBigInteger that) { - return that.getValue().signum() > 0; - } - - @Fallback - DataflowError doOther(long self, Object that) { - var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); - } -} From b5692b8259428dcded97ccf4ec0f057f961fafc4 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 22 Mar 2023 19:13:01 +0100 Subject: [PATCH 09/49] Consolidate all sorting functionality into a single builtin node. --- .../Base/0.0.0-dev/src/Data/Ordering.enso | 2 +- .../Base/0.0.0-dev/src/Data/Vector.enso | 49 +- .../expression/builtin/mutable/SortNode.java | 1 + .../builtin/ordering/SortVectorNode.java | 431 +++++++++++++++--- test/Tests/src/Data/Ordering_Spec.enso | 8 +- 5 files changed, 388 insertions(+), 103 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso index 53ea44886c4c..23a4e6d7639a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso @@ -26,7 +26,7 @@ from project.Data.Boolean import all ``` type Comparator T - compare : T -> T -> (Ordering|Nothing) + compare : T -> T -> (Ordering|Nothing) ! Incomparable_Values hash : T -> Integer ``` diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index 3f0a90d18303..7951d5ecf3ca 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -851,6 +851,7 @@ type Vector a elements, returning an an `Ordering` if the two elements are comparable or `Nothing` if they are not. If set to `Nothing` (the default argument), `Ordering.compare _ _` method will be used. + Must be a static method. By default, elements are sorted in ascending order. @@ -906,50 +907,10 @@ type Vector a [My_Type.Value 'hello', 1].sort == [1, My_Type.Value 'hello'] sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Vector Any ! Incomparable_Values sort self (order = Sort_Direction.Ascending) on=x->x by=Nothing = - comps = self.map (it-> Comparable.from (on it)) . distinct - optimized_case = comps == [Default_Comparator] && by == Nothing - # In optimize_case, forward to Vector.sort_builtin, otherwise split to groups - # based on different comparators, and forward to Array.sort - case optimized_case of - True -> - elems = if on == Nothing then self else self.map it-> on it - elems.sort_builtin order.to_sign - False -> - # The "default value" of `by` parameter is `Ordering.compare _ _`, but because - # there is no function equality, the real default value is Nothing. - by_non_null = if by == Nothing then (Ordering.compare _ _) else by - # Groups of elements with different comparators - groups = comps.map comp-> - self.filter it-> - Comparable.from (on it) == comp - case groups.length of - # self consists only of elements with the same comparator, that - # is not `Default_Comparator`. - # Forward to Array.sort - 1 -> - comp_ascending l r = by_non_null (on l) (on r) - comp_descending l r = by_non_null (on r) (on l) - compare = if order == Sort_Direction.Ascending then comp_ascending else - comp_descending - new_vec_arr = self.to_array.sort compare - if new_vec_arr.is_error then Error.throw new_vec_arr else - Vector.from_polyglot_array new_vec_arr - _ -> - # Partition into groups sorted by FQN of their comparator's name, - # with the default comparator as the first group. - groups_with_comps_sorted_by_fqn = groups.zip comps . sort by=pair_1->pair_2-> - comp_1 = pair_1.last - comp_2 = pair_2.last - if comp_1 == Default_Comparator then Ordering.Less else - if comp_2 == Default_Comparator then Ordering.Greater else - comp_1_fqn = Meta.get_qualified_type_name comp_1 - comp_2_fqn = Meta.get_qualified_type_name comp_2 - Ordering.compare comp_1_fqn comp_2_fqn - # Recurse on each group - sorted_groups = groups_with_comps_sorted_by_fqn.map it-> it.first.sort order on by - # Merge the groups and attach a warning - comparators_warn_text = groups_with_comps_sorted_by_fqn.map (it-> it.last) . to_text - Warning.attach ("Different comparators: " + comparators_warn_text) sorted_groups.flatten + elems = self.map (it-> on it) + comps = elems.map (it-> Comparable.from it) + compare_funcs = comps.map (it-> it.compare) + elems.sort_builtin order.to_sign comps compare_funcs by ## UNSTABLE Keeps only unique elements within the Vector, removing any duplicates. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java index ff825b69f04a..a9c070922c76 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java @@ -142,6 +142,7 @@ public int compare(Object o1, Object o2) { } else if (res == greater) { return 1; } else { + // res is either null, or Incomparable_Values was thrown. invalidCompareResultProfile.enter(); throw new CompareException(o1, o2); } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index fe5ea13a988b..49b5ec79f17c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -7,33 +7,46 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.BranchProfile; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.stream.Collectors; import org.enso.interpreter.dsl.AcceptsError; import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.callable.dispatch.CallOptimiserNode; import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; import org.enso.interpreter.node.expression.builtin.meta.EqualsNode; import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode; import org.enso.interpreter.node.expression.builtin.text.AnyToTextNode; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.callable.atom.Atom; +import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.Array; import org.enso.interpreter.runtime.data.ArrayRope; +import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.data.Vector; import org.enso.interpreter.runtime.data.text.Text; +import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.Warning; -import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.error.WithWarnings; +import org.enso.interpreter.runtime.state.State; /** * Sorts a vector with elements that have only Default_Comparator, thus, only elements with a * builtin type, which is the most common scenario for sorting. - * + *

* TODO: Max number of attached Incomparable values warnings? * - hardcode or pass via a new parameter to Vector.sort? */ @@ -44,6 +57,7 @@ ) @GenerateUncached public abstract class SortVectorNode extends Node { + public static SortVectorNode build() { return SortVectorNodeGen.create(); } @@ -51,25 +65,41 @@ public static SortVectorNode build() { /** * Sorts a vector with elements that have only Default_Comparator, thus, only builtin types. * - * @param self Vector that has elements with only Default_Comparator, that are elements with - * builtin types. - * @param ascending -1 for descending, 1 for ascending + * @param self Vector that has elements with only Default_Comparator, that are + * elements with builtin types. + * @param ascending -1 for descending, 1 for ascending + * @param comparators Vector of comparators, with the same length of self. This is gather in + * the Enso code, because doing that in this builtin would be difficult. + * @param compareFunctions Vector of `Comparator.compare` functions gathered from the comparators + * @param byFunc If Nothing, then the default `by` function should be used. The default + * `by` function is `Ordering.compare`. * @return A new, sorted vector */ - public abstract Object execute(@AcceptsError Object self, long ascending); + public abstract Object execute(State state, @AcceptsError Object self, long ascending, + Object comparators, + Object compareFunctions, Object byFunc); + /** + * Sorts primitive values, i.e., values with only Default_Comparator. We can optimize this case. + * It is important that `byFunc` is Nothing, i.e., has the default value. In that case, we can + * hard code the partial ordering for the primitive values. If `byFunc` is a custom user function, + * it can redefine the default partial ordering of the primitive values, which requires + * topological sort. + */ @Specialization(guards = { - "interop.hasArrayElements(self)" + "interop.hasArrayElements(self)", + "areAllDefaultComparators(interop, comparators)", + "isNothing(byFunc)" }) - Object sortCached(Object self, long ascending, + Object sortPrimitives(State state, Object self, long ascending, Object comparators, + Object compareFunctions, Object byFunc, @Cached LessThanNode lessThanNode, @Cached EqualsNode equalsNode, @Cached HostValueToEnsoNode hostValueToEnsoNode, @Cached TypeOfNode typeOfNode, @Cached AnyToTextNode toTextNode, @Cached BranchProfile warningEncounteredProfile, - @CachedLibrary(limit = "10") InteropLibrary interop, - @CachedLibrary(limit = "5") WarningsLibrary warningsLib) { + @CachedLibrary(limit = "10") InteropLibrary interop) { EnsoContext ctx = EnsoContext.get(this); Object[] elems; try { @@ -92,71 +122,251 @@ Object sortCached(Object self, long ascending, } } } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw new IllegalStateException(e); + throw new IllegalStateException("Should not reach here", e); } - var comparator = new Comparator(lessThanNode, equalsNode, typeOfNode, toTextNode, ascending > 0); - Arrays.sort(elems, comparator); - var vector = Vector.fromArray(new Array(elems)); + var javaComparator = new PrimitiveValueComparator(lessThanNode, equalsNode, typeOfNode, + toTextNode, ascending > 0); + try { + return sortPrimitiveVector(elems, javaComparator, warningEncounteredProfile); + } catch (CompareException e) { + return DataflowError.withoutTrace( + incomparableValuesError(e.leftOperand, e.rightOperand), this); + } + } - if (comparator.encounteredWarnings()) { + private TruffleObject sortPrimitiveVector(Object[] elems, + PrimitiveValueComparator javaComparator, BranchProfile warningEncounteredProfile) + throws CompareException { + Arrays.sort(elems, javaComparator); + var sortedVector = Vector.fromArray(new Array(elems)); + + if (javaComparator.encounteredWarnings()) { warningEncounteredProfile.enter(); CompilerDirectives.transferToInterpreter(); - Warning[] warns = comparator.getWarnings() + Warning[] warns = javaComparator.getWarnings() .stream() .map(Text::create) .map(text -> Warning.create(EnsoContext.get(this), text, this)) .toArray(Warning[]::new); - return WithWarnings.appendTo(vector, new ArrayRope<>(warns)); + return WithWarnings.appendTo(sortedVector, new ArrayRope<>(warns)); } else { - return vector; + return sortedVector; } } - private int typeOrder(Object object, TypeOfNode typeOfNode) { + @TruffleBoundary + @Specialization(guards = { + "interop.hasArrayElements(self)", + }) + Object sortGeneric(State state, Object self, long ascending, Object comparatorsArray, + Object compareFuncsArray, Object byFunc, + @CachedLibrary(limit = "10") InteropLibrary interop, + @Cached LessThanNode lessThanNode, + @Cached EqualsNode equalsNode, + @Cached TypeOfNode typeOfNode, + @Cached AnyToTextNode toTextNode, + @Cached(value = "build()", uncached = "build()") CallOptimiserNode callNode) { + // Split into groups + List elems = readInteropArray(interop, self); + List comparators = readInteropArray(interop, comparatorsArray); + List compareFuncs = readInteropArray(interop, compareFuncsArray); + List groups = splitByComparators(elems, comparators, compareFuncs); + + // TODO: Attach warnings + // Prepare input for PrimitiveValueComparator and GenericComparator and sort the elements within groups var ctx = EnsoContext.get(this); - var builtins = ctx.getBuiltins(); - if (isNothing(object, ctx)) { - return 200; - } - var type = typeOfNode.execute(object); - if (type == builtins.number().getNumber() - || type == builtins.number().getInteger() - || type == builtins.number().getDecimal()) { - if (object instanceof Double dbl && dbl.isNaN()) { - return 100; - } else { - return 1; + Atom less = ctx.getBuiltins().ordering().newLess(); + Atom equal = ctx.getBuiltins().ordering().newEqual(); + Atom greater = ctx.getBuiltins().ordering().newGreater(); + List resultVec = new ArrayList<>(); + try { + for (var group : groups) { + Comparator javaComparator; + if (isPrimitiveGroup(group)) { + javaComparator = new PrimitiveValueComparator( + lessThanNode, + equalsNode, + typeOfNode, + toTextNode, + ascending > 0 + ); + } else { + Function compareFunc = isNothing(byFunc) ? group.compareFunc : (Function) byFunc; + javaComparator = new GenericComparator( + ascending > 0, + compareFunc, + group.comparator, + callNode, + state, + less, + equal, + greater + ); + } + group.elems.sort(javaComparator); + resultVec.addAll(group.elems); } + return Vector.fromArray(new Array(resultVec.toArray())); + } catch (CompareException e) { + return DataflowError.withoutTrace( + incomparableValuesError(e.leftOperand, e.rightOperand), this); } - else if (type == builtins.text()) { - return 2; + } + + private List splitByComparators(List elements, List comparators, + List compareFuncs) { + assert elements.size() == comparators.size(); + assert elements.size() == compareFuncs.size(); + // Mapping of FQN of comparator to groups + Map groupMap = new HashMap<>(); + for (int i = 0; i < elements.size(); i++) { + Object elem = elements.get(i); + Type comparator = comparators.get(i); + Function compareFunc = compareFuncs.get(i); + String qualifiedName = comparator.getQualifiedName().toString(); + + if (!groupMap.containsKey(qualifiedName)) { + groupMap.put( + qualifiedName, + new Group(new ArrayList<>(), comparator, compareFunc) + ); + } + var group = groupMap.get(qualifiedName); + group.elems.add(elem); } - else if (type == builtins.bool().getType()) { - return 3; + + // Sort groups by the FQN of their comparator, with the default comparator + // being the first one (the first group). + String defCompFQN = getDefaultComparatorQualifiedName(); + return groupMap + .entrySet() + .stream() + .sorted((entry1, entry2) -> { + var fqn1 = entry1.getKey(); + var fqn2 = entry2.getKey(); + if (fqn1.equals(defCompFQN)) { + return -1; + } else if (fqn2.equals(defCompFQN)) { + return 1; + } else { + return fqn1.compareTo(fqn2); + } + }) + .map(Entry::getValue) + .collect(Collectors.toList()); + } + + private String getDefaultComparatorQualifiedName() { + return EnsoContext.get(this).getBuiltins().defaultComparator().getType().getQualifiedName() + .toString(); + } + + /** + * A group is "primitive" iff its comparator is the default comparator. + */ + private boolean isPrimitiveGroup(Group group) { + return group.comparator.getQualifiedName().toString().equals( + getDefaultComparatorQualifiedName() + ); + } + + private Object incomparableValuesError(Object left, Object right) { + return EnsoContext.get(this).getBuiltins().error().makeIncomparableValues(left, right); + } + + /** + * Helper slow-path method to conveniently gather elements from interop arrays into a java list + */ + @SuppressWarnings("unchecked") + private List readInteropArray(InteropLibrary interop, Object vector) { + try { + int size = (int) interop.getArraySize(vector); + List res = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + T elem = (T) interop.readArrayElement(vector, i); + res.add(elem); + } + return res; + } catch (UnsupportedMessageException | InvalidArrayIndexException | ClassCastException e) { + throw new IllegalStateException("Should not be reachable", e); } - else if (type == builtins.date()) { + } + + private int getBuiltinTypeCost(Object builtinType) { + assert isBuiltinType(builtinType); + var builtins = EnsoContext.get(this).getBuiltins(); + if (builtinType == builtins.number().getNumber() + || builtinType == builtins.number().getInteger() + || builtinType == builtins.number().getDecimal()) { + return 1; + } else if (builtinType == builtins.text()) { + return 2; + } else if (builtinType == builtins.bool().getType()) { + return 3; + } else if (builtinType == builtins.date()) { return 4; - } - else if (type == builtins.dateTime()) { + } else if (builtinType == builtins.dateTime()) { return 5; - } - else if (type == builtins.duration()) { + } else if (builtinType == builtins.duration()) { return 6; - } - else if (type == builtins.vector()) { + } else if (builtinType == builtins.vector()) { // vectors are incomparable, but we want to sort them before Nothings and NaNs. return 50; + } else { + // Type is not a builtin type + throw new IllegalStateException("Should be a builtin type: " + builtinType); } - else { - throw new IllegalStateException("Unexpected type: " + type); - } + } + + private boolean isBuiltinType(Object type) { + var builtins = EnsoContext.get(this).getBuiltins(); + return + builtins.number().getNumber() == type || + builtins.number().getDecimal() == type || + builtins.number().getInteger() == type || + builtins.nothing() == type || + builtins.text() == type || + builtins.bool().getType() == type || + builtins.date() == type || + builtins.dateTime() == type || + builtins.duration() == type || + builtins.vector() == type; } private boolean isTrue(Object object) { return Boolean.TRUE.equals(object); } - private boolean isNothing(Object object) { + /** + * Returns true iff the given array of comparators is all Default_Comparator + */ + boolean areAllDefaultComparators(InteropLibrary interop, Object comparators) { + assert interop.hasArrayElements(comparators); + var ctx = EnsoContext.get(this); + try { + int compSize = (int) interop.getArraySize(comparators); + for (int i = 0; i < compSize; i++) { + assert interop.isArrayElementReadable(comparators, i); + Object comparator = interop.readArrayElement(comparators, i); + if (!isDefaultComparator(comparator, ctx)) { + return false; + } + } + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + throw new IllegalStateException("Should not be reachable", e); + } + return true; + } + + boolean isDefaultComparator(Object object, EnsoContext ctx) { + return ctx.getBuiltins().defaultComparator().getType() == object; + } + + private boolean isNan(Object object) { + return object instanceof Double dbl && dbl.isNaN(); + } + + boolean isNothing(Object object) { return isNothing(object, EnsoContext.get(this)); } @@ -164,7 +374,27 @@ private boolean isNothing(Object object, EnsoContext ctx) { return object == ctx.getBuiltins().nothing(); } - private final class Comparator implements java.util.Comparator { + /** + * Group of elements grouped by comparator. + * + * @param elems Elements of the group. + * @param comparator Comparator for the elems, i.e., it should hold that + * {@code elems.each it-> (Comparable.from it) == comparator}. + * @param compareFunc `Comparator.compare` function extracted from the comparator. + */ + private record Group( + List elems, + Type comparator, + Function compareFunc + ) { + + } + + /** + * Comparator for comparing primitive values (which implies that they have Default_Comparator), + * which the default `by` method parameter. + */ + private class PrimitiveValueComparator implements java.util.Comparator { private final LessThanNode lessThanNode; private final EqualsNode equalsNode; @@ -173,7 +403,8 @@ private final class Comparator implements java.util.Comparator { private final boolean ascending; private final Set warnings = new HashSet<>(); - private Comparator(LessThanNode lessThanNode, EqualsNode equalsNode, TypeOfNode typeOfNode, + private PrimitiveValueComparator(LessThanNode lessThanNode, EqualsNode equalsNode, + TypeOfNode typeOfNode, AnyToTextNode toTextNode, boolean ascending) { this.lessThanNode = lessThanNode; this.equalsNode = equalsNode; @@ -184,6 +415,10 @@ private Comparator(LessThanNode lessThanNode, EqualsNode equalsNode, TypeOfNode @Override public int compare(Object x, Object y) { + return comparePrimitiveValues(x, y); + } + + int comparePrimitiveValues(Object x, Object y) { if (equalsNode.execute(x, y)) { return 0; } else { @@ -192,7 +427,7 @@ public int compare(Object x, Object y) { if (isNothing(xLessThanYRes)) { // x and y are incomparable - this can happen if x and y are different types attachIncomparableValuesWarning(x, y); - return compareTypes(x, y); + return handleIncomparablePrimitives(x, y); } else if (isTrue(xLessThanYRes)) { return ascending ? -1 : 1; } else { @@ -203,20 +438,35 @@ public int compare(Object x, Object y) { } else { // yLessThanXRes is either Nothing or False attachIncomparableValuesWarning(y, x); - return compareTypes(y, x); + return handleIncomparablePrimitives(y, x); } } } } - private int compareTypes(Object x, Object y) { - int res =Integer.compare( - typeOrder(x, typeOfNode), - typeOrder(y, typeOfNode) - ); + /** + * Incomparable primitive values have either different builtin types, or are Nothing or NaN. All + * these cases are handled specifically - we hardcode the order of these incomparable values. + */ + private int handleIncomparablePrimitives(Object x, Object y) { + // "Nothing > NaN" + int xCost = getPrimitiveValueCost(x); + int yCost = getPrimitiveValueCost(y); + int res = Integer.compare(xCost, yCost); return ascending ? res : -res; } + private int getPrimitiveValueCost(Object object) { + if (isNothing(object)) { + return 200; + } else if (isNan(object)) { + return 100; + } else { + var type = typeOfNode.execute(object); + return getBuiltinTypeCost(type); + } + } + @TruffleBoundary private void attachIncomparableValuesWarning(Object x, Object y) { var xStr = toTextNode.execute(x).toString(); @@ -233,4 +483,75 @@ private Set getWarnings() { return warnings; } } + + /** + * Comparator for any values. This comparator compares the values by calling back to Enso (by + * {@link #compareFunc}), rather than using compare nodes (i.e. {@link LessThanNode}). directly, + * as opposed to {@link PrimitiveValueComparator}. + */ + private class GenericComparator implements java.util.Comparator { + + private final boolean ascending; + /** + * Either function from `by` parameter to the `Vector.sort` method, or the `compare` function + * extracted from the comparator for the appropriate group. + */ + private final Function compareFunc; + private final Type comparator; + private final CallOptimiserNode callNode; + private final State state; + private final Atom less; + private final Atom equal; + private final Atom greater; + + + private GenericComparator( + boolean ascending, + Function compareFunc, + Type comparator, CallOptimiserNode callNode, State state, Atom less, Atom equal, + Atom greater) { + assert compareFunc != null; + assert comparator != null; + this.comparator = comparator; + this.state = state; + this.ascending = ascending; + this.compareFunc = compareFunc; + this.callNode = callNode; + this.less = less; + this.equal = equal; + this.greater = greater; + } + + @Override + public int compare(Object x, Object y) { + // We are calling a static method here, so we need to pass the Comparator type as the + // self (first) argument. + Object res = callNode.executeDispatch(compareFunc, null, state, + new Object[]{comparator, x, y}); + if (res == less) { + return ascending ? -1 : 1; + } else if (res == equal) { + return 0; + } else if (res == greater) { + return ascending ? 1 : -1; + } else { + // res is either Nothing, or Incomparable_Values. Either way, it means that x and y are incomparable. + // This case is not supported yet, as it requires topological sorting. + // We cannot detect if the result was actually returned from the default comparator (it + // could have been transitively called), so we just bailout. + throw new CompareException(x, y); + } + } + } + + private static final class CompareException extends RuntimeException { + + final Object leftOperand; + final Object rightOperand; + + private CompareException(Object leftOperand, Object rightOperand) { + this.leftOperand = leftOperand; + this.rightOperand = rightOperand; + } + } } diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index d55d98653877..105559656fb4 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -67,6 +67,8 @@ expect_no_warns result = # === The Tests === spec = + topo_sort_pending = "Waiting for implementation of topological sort (https://github.com/enso-org/enso/issues/5742)" + Test.group "Default comparator" <| Test.specify "should support custom comparator" <| Ordering.compare (Ord.Value 1) (Ord.Value 2) . should_equal Ordering.Less @@ -156,10 +158,10 @@ spec = [[1], [2]].sort . should_equal [[1], [2]] [[2], [1]].sort . should_equal [[2], [1]] - Test.specify "should be able to sort primitive values in atoms" <| - [Ord.Value Number.nan, Ord.Value 20, Ord.Value 10].sort . should_equal [Ord.Value 10, Ord.Value 20, Ord.Value Number.nan] + Test.specify "should be able to sort primitive values in atoms" pending=topo_sort_pending <| + [Ord.Value Nothing, Ord.Value 20, Ord.Value 10].sort . should_equal [Ord.Value 10, Ord.Value 20, Ord.Value Nothing] - Test.specify "should produce warnings when sorting primitive values in atoms" <| + Test.specify "should produce warnings when sorting primitive values in atoms" pending=topo_sort_pending <| expect_incomparable_warn (Ord.Value 1) (Ord.Value Nothing) [Ord.Value 1, Ord.Value Nothing].sort Test.specify "should attach warning when trying to sort incomparable values" <| From 75d317864c10fc90aac95d85daf8d7a8cabff86d Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 24 Mar 2023 16:02:48 +0100 Subject: [PATCH 10/49] Fix warnings attachment in sort --- .../builtin/ordering/SortVectorNode.java | 130 ++++++++++++------ test/Tests/src/Data/Ordering_Spec.enso | 15 +- 2 files changed, 94 insertions(+), 51 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 49b5ec79f17c..1766e04a5423 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -14,7 +14,6 @@ import com.oracle.truffle.api.profiles.BranchProfile; import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -40,6 +39,7 @@ import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.Warning; +import org.enso.interpreter.runtime.error.WarningsLibrary; import org.enso.interpreter.runtime.error.WithWarnings; import org.enso.interpreter.runtime.state.State; @@ -99,7 +99,8 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato @Cached TypeOfNode typeOfNode, @Cached AnyToTextNode toTextNode, @Cached BranchProfile warningEncounteredProfile, - @CachedLibrary(limit = "10") InteropLibrary interop) { + @CachedLibrary(limit = "10") InteropLibrary interop, + @CachedLibrary(limit = "5") WarningsLibrary warningsLib) { EnsoContext ctx = EnsoContext.get(this); Object[] elems; try { @@ -134,21 +135,16 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato } } - private TruffleObject sortPrimitiveVector(Object[] elems, + private Object sortPrimitiveVector(Object[] elems, PrimitiveValueComparator javaComparator, BranchProfile warningEncounteredProfile) throws CompareException { Arrays.sort(elems, javaComparator); var sortedVector = Vector.fromArray(new Array(elems)); - if (javaComparator.encounteredWarnings()) { + if (javaComparator.hasWarnings()) { warningEncounteredProfile.enter(); CompilerDirectives.transferToInterpreter(); - Warning[] warns = javaComparator.getWarnings() - .stream() - .map(Text::create) - .map(text -> Warning.create(EnsoContext.get(this), text, this)) - .toArray(Warning[]::new); - return WithWarnings.appendTo(sortedVector, new ArrayRope<>(warns)); + return attachWarnings(sortedVector, javaComparator.getEncounteredWarnings()); } else { return sortedVector; } @@ -161,27 +157,28 @@ private TruffleObject sortPrimitiveVector(Object[] elems, Object sortGeneric(State state, Object self, long ascending, Object comparatorsArray, Object compareFuncsArray, Object byFunc, @CachedLibrary(limit = "10") InteropLibrary interop, + @CachedLibrary(limit = "5") WarningsLibrary warningsLib, @Cached LessThanNode lessThanNode, @Cached EqualsNode equalsNode, @Cached TypeOfNode typeOfNode, @Cached AnyToTextNode toTextNode, @Cached(value = "build()", uncached = "build()") CallOptimiserNode callNode) { // Split into groups - List elems = readInteropArray(interop, self); - List comparators = readInteropArray(interop, comparatorsArray); - List compareFuncs = readInteropArray(interop, compareFuncsArray); + List elems = readInteropArray(interop, warningsLib, self); + List comparators = readInteropArray(interop, warningsLib, comparatorsArray); + List compareFuncs = readInteropArray(interop, warningsLib, compareFuncsArray); List groups = splitByComparators(elems, comparators, compareFuncs); - // TODO: Attach warnings // Prepare input for PrimitiveValueComparator and GenericComparator and sort the elements within groups var ctx = EnsoContext.get(this); Atom less = ctx.getBuiltins().ordering().newLess(); Atom equal = ctx.getBuiltins().ordering().newEqual(); Atom greater = ctx.getBuiltins().ordering().newGreater(); + Set gatheredWarnings = new HashSet<>(); List resultVec = new ArrayList<>(); try { for (var group : groups) { - Comparator javaComparator; + Comparator javaComparator; if (isPrimitiveGroup(group)) { javaComparator = new PrimitiveValueComparator( lessThanNode, @@ -196,17 +193,24 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA ascending > 0, compareFunc, group.comparator, - callNode, + callNode, toTextNode, state, less, equal, - greater - ); + greater); } group.elems.sort(javaComparator); + if (javaComparator.hasWarnings()) { + gatheredWarnings.addAll(javaComparator.getEncounteredWarnings()); + } resultVec.addAll(group.elems); } - return Vector.fromArray(new Array(resultVec.toArray())); + var sortedVector = Vector.fromArray(new Array(resultVec.toArray())); + // Attach gathered warnings and different comparators warning + return attachDifferentComparatorsWarning( + attachWarnings(sortedVector, gatheredWarnings), + groups + ); } catch (CompareException e) { return DataflowError.withoutTrace( incomparableValuesError(e.leftOperand, e.rightOperand), this); @@ -256,6 +260,25 @@ private List splitByComparators(List elements, List compara .collect(Collectors.toList()); } + private Object attachWarnings(Object vector, Set warnings) { + var warnArray = warnings + .stream() + .map(Text::create) + .map(text -> Warning.create(EnsoContext.get(this), text, this)) + .toArray(Warning[]::new); + return WithWarnings.appendTo(vector, new ArrayRope<>(warnArray)); + } + + private Object attachDifferentComparatorsWarning(Object vector, List groups) { + var diffCompsMsg = groups.stream() + .map(Group::comparator) + .map(comparator -> comparator.getQualifiedName().toString()) + .collect(Collectors.joining(", ")); + var text = Text.create("Different comparators: [" + diffCompsMsg + "]"); + var warn = Warning.create(EnsoContext.get(this), text, this); + return WithWarnings.appendTo(vector, new ArrayRope<>(warn)); + } + private String getDefaultComparatorQualifiedName() { return EnsoContext.get(this).getBuiltins().defaultComparator().getType().getQualifiedName() .toString(); @@ -278,13 +301,17 @@ private Object incomparableValuesError(Object left, Object right) { * Helper slow-path method to conveniently gather elements from interop arrays into a java list */ @SuppressWarnings("unchecked") - private List readInteropArray(InteropLibrary interop, Object vector) { + private List readInteropArray(InteropLibrary interop, WarningsLibrary warningsLib, + Object vector) { try { int size = (int) interop.getArraySize(vector); List res = new ArrayList<>(size); for (int i = 0; i < size; i++) { - T elem = (T) interop.readArrayElement(vector, i); - res.add(elem); + Object elem = interop.readArrayElement(vector, i); + if (warningsLib.hasWarnings(elem)) { + elem = warningsLib.removeWarnings(elem); + } + res.add((T) elem); } return res; } catch (UnsupportedMessageException | InvalidArrayIndexException | ClassCastException e) { @@ -390,26 +417,55 @@ private record Group( } + /** + * Convenient base class that implements java.util.Comparator, and gathers warnings about + * incomparable values. The warnings are gathered as pure Strings in a hash set, so that they are + * not duplicated. + */ + private static abstract class Comparator implements java.util.Comparator { + + private final Set warnings = new HashSet<>(); + private final AnyToTextNode toTextNode; + + protected Comparator(AnyToTextNode toTextNode) { + this.toTextNode = toTextNode; + } + + @TruffleBoundary + protected void attachIncomparableValuesWarning(Object x, Object y) { + var xStr = toTextNode.execute(x).toString(); + var yStr = toTextNode.execute(y).toString(); + String warnText = "Values " + xStr + " and " + yStr + " are incomparable"; + warnings.add(warnText); + } + + public Set getEncounteredWarnings() { + return warnings; + } + + public boolean hasWarnings() { + return !warnings.isEmpty(); + } + } + /** * Comparator for comparing primitive values (which implies that they have Default_Comparator), * which the default `by` method parameter. */ - private class PrimitiveValueComparator implements java.util.Comparator { + private final class PrimitiveValueComparator extends Comparator { private final LessThanNode lessThanNode; private final EqualsNode equalsNode; private final TypeOfNode typeOfNode; - private final AnyToTextNode toTextNode; private final boolean ascending; - private final Set warnings = new HashSet<>(); private PrimitiveValueComparator(LessThanNode lessThanNode, EqualsNode equalsNode, TypeOfNode typeOfNode, AnyToTextNode toTextNode, boolean ascending) { + super(toTextNode); this.lessThanNode = lessThanNode; this.equalsNode = equalsNode; this.typeOfNode = typeOfNode; - this.toTextNode = toTextNode; this.ascending = ascending; } @@ -466,22 +522,6 @@ private int getPrimitiveValueCost(Object object) { return getBuiltinTypeCost(type); } } - - @TruffleBoundary - private void attachIncomparableValuesWarning(Object x, Object y) { - var xStr = toTextNode.execute(x).toString(); - var yStr = toTextNode.execute(y).toString(); - String warnText = "Values " + xStr + " and " + yStr + " are incomparable"; - warnings.add(warnText); - } - - private boolean encounteredWarnings() { - return !warnings.isEmpty(); - } - - private Set getWarnings() { - return warnings; - } } /** @@ -489,7 +529,7 @@ private Set getWarnings() { * {@link #compareFunc}), rather than using compare nodes (i.e. {@link LessThanNode}). directly, * as opposed to {@link PrimitiveValueComparator}. */ - private class GenericComparator implements java.util.Comparator { + private final class GenericComparator extends Comparator { private final boolean ascending; /** @@ -508,8 +548,10 @@ private class GenericComparator implements java.util.Comparator { private GenericComparator( boolean ascending, Function compareFunc, - Type comparator, CallOptimiserNode callNode, State state, Atom less, Atom equal, + Type comparator, CallOptimiserNode callNode, AnyToTextNode toTextNode, State state, + Atom less, Atom equal, Atom greater) { + super(toTextNode); assert compareFunc != null; assert comparator != null; this.comparator = comparator; diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index 105559656fb4..de00e8fb8dc4 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -173,10 +173,10 @@ spec = Problems.expect_warning "my_warn" <| (Warning.attach "my_warn" [3, Number.nan]) . sort expect_incomparable_warn 3 Number.nan <| (Warning.attach "my_warn" [3, Number.nan]) . sort - Test.specify "should respect previous warnings on vector elements" <| - Problems.expect_warning "my_warn" <| [3, Warning.attach "my_warn" 2].sort . at 0 + Test.specify "should respect previous warnings on vectors" pending="https://github.com/enso-org/enso/issues/6070" <| + Problems.expect_warning "my_warn" <| [3, Warning.attach "my_warn" 2].sort expect_incomparable_warn 1 Number.nan [1, Warning.attach "my_warn" Number.nan].sort - Problems.expect_warning "my_warn" <| [1, Warning.attach "my_warn" Number.nan].sort . at 1 + Problems.expect_warning "my_warn" <| [1, Warning.attach "my_warn" Number.nan].sort Test.group "Sorting with multiple comparators" <| Test.specify "should sort primitive values with the default comparator as the first group" <| @@ -188,12 +188,13 @@ spec = [Ord.Value 4, 20, Ord.Value 3, Nothing, 10].sort . should_equal [10, 20, Nothing, Ord.Value 3, Ord.Value 4] Test.specify "should produce warning when sorting types with different comparators" <| - [Ord.Value 1, 1].sort . should_equal [Ord.Value 1, 1] - Problems.expect_warning "Different comparators: [Default_Comparator, Ord_Comparator]" <| [Ord.Value 1, 1].sort + [Ord.Value 1, 1].sort . should_equal [1, Ord.Value 1] + Problems.expect_warning "Different comparators: [Standard.Base.Data.Ordering.Default_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, 1].sort Test.specify "should merge groups of values with custom comparators based on the comparators FQN" <| [Ord.Value 1, My_Type.Value 1].sort . should_equal [My_Type.Value 1, Ord.Value 1] - Problems.expect_warning "Different comparators: [My_Type_Comparator, Ord_Comparator]" <| [Ord.Value 1, My_Type.Value 1].sort + [My_Type.Value 1, Ord.Value 1].sort . should_equal [My_Type.Value 1, Ord.Value 1] + Problems.expect_warning "Different comparators: [Ordering_Spec.My_Type_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, My_Type.Value 1].sort Test.specify "should be stable when sorting values with different comparators" <| [Ord.Value 1, 20, My_Type.Value 1, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] @@ -206,7 +207,7 @@ spec = # In other words, if there are incomparable values, make sure that warning for # "incomparable value X Y" is attached just once. Test.specify "should not duplicate warnings" <| - Problems.get_attached_warnings ([Nothing, (Ord.Value 1), 20].sort) . should_equal ["Different comparators: [Default_Comparator, Ord_Comparator]", "Values 20 and Nothing are incomparable"] + Problems.get_attached_warnings ([Nothing, (Ord.Value 1), 20].sort) . should_equal ["Different comparators: [Standard.Base.Data.Ordering.Default_Comparator, Ordering_Spec.Ord_Comparator]", "Values 20 and Nothing are incomparable"] Test.specify "should be able to sort even unordered values" pending="https://github.com/enso-org/enso/issues/5834" <| [Ord.Value 2, UPair.Value "a" "b", Ord.Value 1, UPair.Value "c" "d"].sort . should_equal [Ord.Value 2, Ord.Value 1, UPair.Value "a" "b", UPair.Value "c" "d"] From f8deb718c2231801ab967877d35eb2fc5cc07370 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 24 Mar 2023 16:45:04 +0100 Subject: [PATCH 11/49] PrimitiveValuesComparator handles other types than primitives --- .../builtin/ordering/SortVectorNode.java | 69 +++++++++++++++---- test/Tests/src/Data/Ordering_Spec.enso | 10 +++ 2 files changed, 64 insertions(+), 15 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 1766e04a5423..564f9dd6d5ab 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -7,7 +7,6 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; @@ -125,7 +124,7 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato } catch (UnsupportedMessageException | InvalidArrayIndexException e) { throw new IllegalStateException("Should not reach here", e); } - var javaComparator = new PrimitiveValueComparator(lessThanNode, equalsNode, typeOfNode, + var javaComparator = new DefaultComparator(lessThanNode, equalsNode, typeOfNode, toTextNode, ascending > 0); try { return sortPrimitiveVector(elems, javaComparator, warningEncounteredProfile); @@ -136,7 +135,7 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato } private Object sortPrimitiveVector(Object[] elems, - PrimitiveValueComparator javaComparator, BranchProfile warningEncounteredProfile) + DefaultComparator javaComparator, BranchProfile warningEncounteredProfile) throws CompareException { Arrays.sort(elems, javaComparator); var sortedVector = Vector.fromArray(new Array(elems)); @@ -169,7 +168,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA List compareFuncs = readInteropArray(interop, warningsLib, compareFuncsArray); List groups = splitByComparators(elems, comparators, compareFuncs); - // Prepare input for PrimitiveValueComparator and GenericComparator and sort the elements within groups + // Prepare input for DefaultComparator and GenericComparator and sort the elements within groups var ctx = EnsoContext.get(this); Atom less = ctx.getBuiltins().ordering().newLess(); Atom equal = ctx.getBuiltins().ordering().newEqual(); @@ -180,7 +179,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA for (var group : groups) { Comparator javaComparator; if (isPrimitiveGroup(group)) { - javaComparator = new PrimitiveValueComparator( + javaComparator = new DefaultComparator( lessThanNode, equalsNode, typeOfNode, @@ -449,17 +448,23 @@ public boolean hasWarnings() { } /** - * Comparator for comparing primitive values (which implies that they have Default_Comparator), - * which the default `by` method parameter. + * Comparator for comparing all values that have Default_Comparator. These are either primitive + * types, or the types that do not provide their own comparator. + *

+ * Note that it is essential for this class that the {@code by} method parameter to + * {@code Vector.sort} is set to the default value, which is {@code Ordering.compare}, because + * then, we know that the partial ordering for primitive types was not redefined by the user (we + * handle partial ordering for primitive types specifically, partial ordering for other types is + * not implemented yet - that requires topological sorting). */ - private final class PrimitiveValueComparator extends Comparator { + private final class DefaultComparator extends Comparator { private final LessThanNode lessThanNode; private final EqualsNode equalsNode; private final TypeOfNode typeOfNode; private final boolean ascending; - private PrimitiveValueComparator(LessThanNode lessThanNode, EqualsNode equalsNode, + private DefaultComparator(LessThanNode lessThanNode, EqualsNode equalsNode, TypeOfNode typeOfNode, AnyToTextNode toTextNode, boolean ascending) { super(toTextNode); @@ -471,10 +476,10 @@ private PrimitiveValueComparator(LessThanNode lessThanNode, EqualsNode equalsNod @Override public int compare(Object x, Object y) { - return comparePrimitiveValues(x, y); + return compareValuesWithDefaultComparator(x, y); } - int comparePrimitiveValues(Object x, Object y) { + int compareValuesWithDefaultComparator(Object x, Object y) { if (equalsNode.execute(x, y)) { return 0; } else { @@ -483,7 +488,7 @@ int comparePrimitiveValues(Object x, Object y) { if (isNothing(xLessThanYRes)) { // x and y are incomparable - this can happen if x and y are different types attachIncomparableValuesWarning(x, y); - return handleIncomparablePrimitives(x, y); + return handleIncomparableValues(x, y); } else if (isTrue(xLessThanYRes)) { return ascending ? -1 : 1; } else { @@ -494,24 +499,58 @@ int comparePrimitiveValues(Object x, Object y) { } else { // yLessThanXRes is either Nothing or False attachIncomparableValuesWarning(y, x); - return handleIncomparablePrimitives(y, x); + return handleIncomparableValues(y, x); } } } } + private int handleIncomparableValues(Object x, Object y) { + if (isPrimitiveValue(x) || isPrimitiveValue(y)) { + if (isPrimitiveValue(x) && isPrimitiveValue(y)) { + return handleIncomparablePrimitives(x, y); + } else if (isPrimitiveValue(x)) { + // Primitive values are always before non-primitive values - Default_Comparator + // group should be the first one. + return ascending ? -1 : 1; + } else if (isPrimitiveValue(y)) { + return ascending ? 1 : -1; + } else { + throw new IllegalStateException("Should not be reachable"); + } + } else { + // Values other than primitives are compared just by their type's FQN. + var xTypeName = getQualifiedTypeName(x); + var yTypeName = getQualifiedTypeName(y); + return xTypeName.compareTo(yTypeName); + } + } + /** * Incomparable primitive values have either different builtin types, or are Nothing or NaN. All * these cases are handled specifically - we hardcode the order of these incomparable values. */ private int handleIncomparablePrimitives(Object x, Object y) { - // "Nothing > NaN" int xCost = getPrimitiveValueCost(x); int yCost = getPrimitiveValueCost(y); int res = Integer.compare(xCost, yCost); return ascending ? res : -res; } + private boolean isPrimitiveValue(Object object) { + return isBuiltinType(typeOfNode.execute(object)); + } + + private String getQualifiedTypeName(Object object) { + var typeObj = typeOfNode.execute(object); + if (typeObj instanceof Type type) { + return type.getQualifiedName().toString(); + } else { + throw new IllegalStateException( + "Object " + object + " must be an Atom, therefore, it must have Type"); + } + } + private int getPrimitiveValueCost(Object object) { if (isNothing(object)) { return 200; @@ -527,7 +566,7 @@ private int getPrimitiveValueCost(Object object) { /** * Comparator for any values. This comparator compares the values by calling back to Enso (by * {@link #compareFunc}), rather than using compare nodes (i.e. {@link LessThanNode}). directly, - * as opposed to {@link PrimitiveValueComparator}. + * as opposed to {@link DefaultComparator}. */ private final class GenericComparator extends Comparator { diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index de00e8fb8dc4..e8dc58393b2b 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -46,6 +46,10 @@ Comparable.from (_ : UPair) = UPair_Comparator type Parent Value child +# Just a type without custom comparator +type No_Comp_Type + Value val + ## Expects that `result` contains incomparable values warning. The values within the warning message can be switched - the order @@ -178,6 +182,12 @@ spec = expect_incomparable_warn 1 Number.nan [1, Warning.attach "my_warn" Number.nan].sort Problems.expect_warning "my_warn" <| [1, Warning.attach "my_warn" Number.nan].sort + Test.specify "should not fail when sorting incomparable types without custom comparator" <| + # Parent, and No_Comp_Type do not have custom comparators + [No_Comp_Type.Value 42, "hello"].sort . should_equal ["hello", No_Comp_Type.Value 42] + [Parent.Value 42, No_Comp_Type.Value 42].sort . should_equal [No_Comp_Type.Value 42, Parent.Value 42] + [No_Comp_Type.Value 42, Parent.Value 42].sort . should_equal [No_Comp_Type.Value 42, Parent.Value 42] + Test.group "Sorting with multiple comparators" <| Test.specify "should sort primitive values with the default comparator as the first group" <| [Ord.Value 4, Ord.Value 3, 20, 10].sort . should_equal [10, 20, Ord.Value 3, Ord.Value 4] From e695e1f3a2d86d19089dd91afa4a0ed083ae0b45 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 28 Mar 2023 09:03:18 +0200 Subject: [PATCH 12/49] Fix byFunc calling --- .../builtin/ordering/SortVectorNode.java | 46 +++++++++++++++---- test/Tests/src/Data/Vector_Spec.enso | 6 +-- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 564f9dd6d5ab..9f20d949417b 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -178,7 +178,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA try { for (var group : groups) { Comparator javaComparator; - if (isPrimitiveGroup(group)) { + if (isNothing(byFunc) && isPrimitiveGroup(group)) { javaComparator = new DefaultComparator( lessThanNode, equalsNode, @@ -187,7 +187,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA ascending > 0 ); } else { - Function compareFunc = isNothing(byFunc) ? group.compareFunc : (Function) byFunc; + Object compareFunc = isNothing(byFunc) ? group.compareFunc : byFunc; javaComparator = new GenericComparator( ascending > 0, compareFunc, @@ -216,6 +216,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA } } + private List splitByComparators(List elements, List comparators, List compareFuncs) { assert elements.size() == comparators.size(); @@ -576,6 +577,7 @@ private final class GenericComparator extends Comparator { * extracted from the comparator for the appropriate group. */ private final Function compareFunc; + private final boolean compareFuncHasSelf; private final Type comparator; private final CallOptimiserNode callNode; private final State state; @@ -586,7 +588,7 @@ private final class GenericComparator extends Comparator { private GenericComparator( boolean ascending, - Function compareFunc, + Object compareFunc, Type comparator, CallOptimiserNode callNode, AnyToTextNode toTextNode, State state, Atom less, Atom equal, Atom greater) { @@ -596,19 +598,22 @@ private GenericComparator( this.comparator = comparator; this.state = state; this.ascending = ascending; - this.compareFunc = compareFunc; + this.compareFunc = checkAndConvertByParameter(compareFunc); this.callNode = callNode; this.less = less; this.equal = equal; this.greater = greater; + assert this.compareFunc.getSchema().getArgumentsCount() + >= 2 : "compareFunc should take more than 2 arguments"; + this.compareFuncHasSelf = this.compareFunc.getSchema().getArgumentInfos()[0].getName() + .equals("self"); } @Override public int compare(Object x, Object y) { - // We are calling a static method here, so we need to pass the Comparator type as the - // self (first) argument. - Object res = callNode.executeDispatch(compareFunc, null, state, - new Object[]{comparator, x, y}); + // If compareFunc takes self parameter, it is `comparator`. + Object[] args = compareFuncHasSelf ? new Object[]{comparator, x, y} : new Object[]{x, y}; + Object res = callNode.executeDispatch(compareFunc, null, state, args); if (res == less) { return ascending ? -1 : 1; } else if (res == equal) { @@ -623,6 +628,31 @@ public int compare(Object x, Object y) { throw new CompareException(x, y); } } + + /** + * Checks value given for {@code by} parameter and converts it to {@link Function}. Throw a + * dataflow error otherwise. + */ + private Function checkAndConvertByParameter(Object byFuncObj) { + var ctx = EnsoContext.get(SortVectorNode.this); + var err = DataflowError.withoutTrace( + ctx.getBuiltins().error().makeUnsupportedArgumentsError( + new Object[]{byFuncObj}, + "Unsupported argument for `by`, expected a method with two arguments" + ), + SortVectorNode.this + ); + if (byFuncObj instanceof Function byFunc) { + var argCount = byFunc.getSchema().getArgumentsCount(); + if (argCount == 2 || argCount == 3) { + return byFunc; + } else { + throw err; + } + } else { + throw err; + } + } } private static final class CompareException extends RuntimeException { diff --git a/test/Tests/src/Data/Vector_Spec.enso b/test/Tests/src/Data/Vector_Spec.enso index 10e37ff7d4f4..dc28ea03d583 100644 --- a/test/Tests/src/Data/Vector_Spec.enso +++ b/test/Tests/src/Data/Vector_Spec.enso @@ -558,10 +558,8 @@ spec = Test.group "Vectors" <| [Time_Of_Day.new 12, Time_Of_Day.new 10 30].sort . should_equal [Time_Of_Day.new 10 30, Time_Of_Day.new 12] [Date_Time.new 2000 12 30 12 30, Date_Time.new 2000 12 30 12 00].sort . should_equal [Date_Time.new 2000 12 30 12 00, Date_Time.new 2000 12 30 12 30] - ["aa", 2].sort . should_fail_with Incomparable_Values - [2, Date.new 1999].sort . should_fail_with Incomparable_Values - [Date_Time.new 1999 1 1 12 30, Date.new 1999].sort . should_fail_with Incomparable_Values - [Date_Time.new 1999 1 1 12 30, Time_Of_Day.new 12 30].sort . should_fail_with Incomparable_Values + ["aa", 2].sort . should_equal [2, "aa"] + [2, Date.new 1999].sort . should_equal [2, Date.new 1999] Test.expect_panic_with ([3,2,1].to_array.sort 42) Type_Error Test.specify "should leave the original vector unchanged" <| From 219aa4af8006943152af287b0a401f657fbe59dc Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Tue, 28 Mar 2023 17:37:32 +0200 Subject: [PATCH 13/49] on function can be called from the builtin --- .../Base/0.0.0-dev/src/Data/Vector.enso | 12 +- .../builtin/ordering/SortVectorNode.java | 120 ++++++++++++------ 2 files changed, 90 insertions(+), 42 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index ef9662b11314..df70521c3e04 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -846,7 +846,8 @@ type Vector a Arguments: - order: The order in which the vector elements are sorted. - on: A projection from the element type to the value of that element - being sorted on. + being sorted on. If set to `Nothing` (the default argument), + identity function will be used. - by: A function that compares the result of applying `on` to two elements, returning an an `Ordering` if the two elements are comparable or `Nothing` if they are not. If set to `Nothing` (the default argument), @@ -906,11 +907,12 @@ type Vector a [My_Type.Value 'hello', 1].sort == [1, My_Type.Value 'hello'] sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Vector Any ! Incomparable_Values - sort self (order = Sort_Direction.Ascending) on=x->x by=Nothing = - elems = self.map (it-> on it) - comps = elems.map (it-> Comparable.from it) + sort self (order = Sort_Direction.Ascending) on=Nothing by=Nothing = + comps = case on == Nothing of + True -> self.map it-> Comparable.from it + False -> self.map it-> Comparable.from (on it) compare_funcs = comps.map (it-> it.compare) - elems.sort_builtin order.to_sign comps compare_funcs by + self.sort_builtin order.to_sign comps compare_funcs by on ## UNSTABLE Keeps only unique elements within the Vector, removing any duplicates. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 9f20d949417b..df0e6390600d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -69,14 +69,17 @@ public static SortVectorNode build() { * @param ascending -1 for descending, 1 for ascending * @param comparators Vector of comparators, with the same length of self. This is gather in * the Enso code, because doing that in this builtin would be difficult. + * If {@code onFunc} parameter is not {@code Nothing}, comparators are + * gathered from the result of {@code onFunc} projection. * @param compareFunctions Vector of `Comparator.compare` functions gathered from the comparators * @param byFunc If Nothing, then the default `by` function should be used. The default * `by` function is `Ordering.compare`. - * @return A new, sorted vector + * @param onFunc If Nothing, then the default identity function should be used. + * @return A new, sorted vector. */ public abstract Object execute(State state, @AcceptsError Object self, long ascending, Object comparators, - Object compareFunctions, Object byFunc); + Object compareFunctions, Object byFunc, Object onFunc); /** * Sorts primitive values, i.e., values with only Default_Comparator. We can optimize this case. @@ -88,10 +91,11 @@ public abstract Object execute(State state, @AcceptsError Object self, long asce @Specialization(guards = { "interop.hasArrayElements(self)", "areAllDefaultComparators(interop, comparators)", - "isNothing(byFunc)" + "isNothing(byFunc)", + "isNothing(onFunc)" }) Object sortPrimitives(State state, Object self, long ascending, Object comparators, - Object compareFunctions, Object byFunc, + Object compareFunctions, Object byFunc, Object onFunc, @Cached LessThanNode lessThanNode, @Cached EqualsNode equalsNode, @Cached HostValueToEnsoNode hostValueToEnsoNode, @@ -129,32 +133,17 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato try { return sortPrimitiveVector(elems, javaComparator, warningEncounteredProfile); } catch (CompareException e) { - return DataflowError.withoutTrace( + throw DataflowError.withoutTrace( incomparableValuesError(e.leftOperand, e.rightOperand), this); } } - private Object sortPrimitiveVector(Object[] elems, - DefaultComparator javaComparator, BranchProfile warningEncounteredProfile) - throws CompareException { - Arrays.sort(elems, javaComparator); - var sortedVector = Vector.fromArray(new Array(elems)); - - if (javaComparator.hasWarnings()) { - warningEncounteredProfile.enter(); - CompilerDirectives.transferToInterpreter(); - return attachWarnings(sortedVector, javaComparator.getEncounteredWarnings()); - } else { - return sortedVector; - } - } - @TruffleBoundary @Specialization(guards = { "interop.hasArrayElements(self)", }) Object sortGeneric(State state, Object self, long ascending, Object comparatorsArray, - Object compareFuncsArray, Object byFunc, + Object compareFuncsArray, Object byFunc, Object onFunc, @CachedLibrary(limit = "10") InteropLibrary interop, @CachedLibrary(limit = "5") WarningsLibrary warningsLib, @Cached LessThanNode lessThanNode, @@ -178,7 +167,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA try { for (var group : groups) { Comparator javaComparator; - if (isNothing(byFunc) && isPrimitiveGroup(group)) { + if (isNothing(byFunc) && isNothing(onFunc) && isPrimitiveGroup(group)) { javaComparator = new DefaultComparator( lessThanNode, equalsNode, @@ -191,6 +180,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA javaComparator = new GenericComparator( ascending > 0, compareFunc, + onFunc, group.comparator, callNode, toTextNode, state, @@ -216,6 +206,20 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA } } + private Object sortPrimitiveVector(Object[] elems, + DefaultComparator javaComparator, BranchProfile warningEncounteredProfile) + throws CompareException { + Arrays.sort(elems, javaComparator); + var sortedVector = Vector.fromArray(new Array(elems)); + + if (javaComparator.hasWarnings()) { + warningEncounteredProfile.enter(); + CompilerDirectives.transferToInterpreter(); + return attachWarnings(sortedVector, javaComparator.getEncounteredWarnings()); + } else { + return sortedVector; + } + } private List splitByComparators(List elements, List comparators, List compareFuncs) { @@ -577,7 +581,8 @@ private final class GenericComparator extends Comparator { * extracted from the comparator for the appropriate group. */ private final Function compareFunc; - private final boolean compareFuncHasSelf; + private final Function onFunc; + private final boolean hasCustomOnFunc; private final Type comparator; private final CallOptimiserNode callNode; private final State state; @@ -589,6 +594,7 @@ private final class GenericComparator extends Comparator { private GenericComparator( boolean ascending, Object compareFunc, + Object onFunc, Type comparator, CallOptimiserNode callNode, AnyToTextNode toTextNode, State state, Atom less, Atom equal, Atom greater) { @@ -598,21 +604,38 @@ private GenericComparator( this.comparator = comparator; this.state = state; this.ascending = ascending; - this.compareFunc = checkAndConvertByParameter(compareFunc); + this.compareFunc = checkAndConvertByFunc(compareFunc); + if (isNothing(onFunc)) { + this.hasCustomOnFunc = false; + this.onFunc = null; + } else { + this.hasCustomOnFunc = true; + this.onFunc = checkAndConvertOnFunc(onFunc); + } this.callNode = callNode; this.less = less; this.equal = equal; this.greater = greater; - assert this.compareFunc.getSchema().getArgumentsCount() - >= 2 : "compareFunc should take more than 2 arguments"; - this.compareFuncHasSelf = this.compareFunc.getSchema().getArgumentInfos()[0].getName() - .equals("self"); } @Override public int compare(Object x, Object y) { - // If compareFunc takes self parameter, it is `comparator`. - Object[] args = compareFuncHasSelf ? new Object[]{comparator, x, y} : new Object[]{x, y}; + Object xConverted; + Object yConverted; + if (hasCustomOnFunc) { + // onFunc cannot have `self` argument, we assume it has just one argument. + xConverted = callNode.executeDispatch(onFunc, null, state, new Object[]{x}); + yConverted = callNode.executeDispatch(onFunc, null, state, new Object[]{y}); + } else { + xConverted = x; + yConverted = y; + } + Object[] args; + if (hasFunctionSelfArgument(compareFunc)) { + args = new Object[]{comparator, xConverted, yConverted}; + } else { + args = new Object[]{xConverted, yConverted}; + } Object res = callNode.executeDispatch(compareFunc, null, state, args); if (res == less) { return ascending ? -1 : 1; @@ -629,23 +652,46 @@ public int compare(Object x, Object y) { } } + private boolean hasFunctionSelfArgument(Function function) { + if (function.getSchema().getArgumentsCount() > 0) { + return function.getSchema().getArgumentInfos()[0].getName().equals("self"); + } else { + return false; + } + } + /** * Checks value given for {@code by} parameter and converts it to {@link Function}. Throw a * dataflow error otherwise. */ - private Function checkAndConvertByParameter(Object byFuncObj) { + private Function checkAndConvertByFunc(Object byFuncObj) { + return checkAndConvertFunction(byFuncObj, + "Unsupported argument for `by`, expected a method with two arguments", 2, 3); + } + + /** + * Checks the value given for {@code on} parameter and converts it to {@link Function}. Throws a + * dataflow error otherwise. + */ + private Function checkAndConvertOnFunc(Object onFuncObj) { + return checkAndConvertFunction(onFuncObj, + "Unsupported argument for `on`, expected a method with one argument", 1, 1); + } + + private Function checkAndConvertFunction(Object funcObj, String errMsg, int minArgCount, + int maxArgCount) { var ctx = EnsoContext.get(SortVectorNode.this); var err = DataflowError.withoutTrace( ctx.getBuiltins().error().makeUnsupportedArgumentsError( - new Object[]{byFuncObj}, - "Unsupported argument for `by`, expected a method with two arguments" + new Object[]{funcObj}, + errMsg ), SortVectorNode.this ); - if (byFuncObj instanceof Function byFunc) { - var argCount = byFunc.getSchema().getArgumentsCount(); - if (argCount == 2 || argCount == 3) { - return byFunc; + if (funcObj instanceof Function func) { + var argCount = func.getSchema().getArgumentsCount(); + if (minArgCount <= argCount && argCount <= maxArgCount) { + return func; } else { throw err; } From 1360be67bb5cbdade3d091dea4c729594fd2faa6 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 3 Apr 2023 13:44:18 +0200 Subject: [PATCH 14/49] Fix build of native image --- .../builtin/ordering/SortVectorNode.java | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index df0e6390600d..249eb7e92802 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -116,6 +116,7 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato interop.readArrayElement(self, i) ); } else { + CompilerDirectives.transferToInterpreter(); throw new PanicException( ctx.getBuiltins().error().makeUnsupportedArgumentsError( new Object[]{self}, @@ -128,16 +129,28 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato } catch (UnsupportedMessageException | InvalidArrayIndexException e) { throw new IllegalStateException("Should not reach here", e); } - var javaComparator = new DefaultComparator(lessThanNode, equalsNode, typeOfNode, - toTextNode, ascending > 0); + var javaComparator = createDefaultComparator(lessThanNode, equalsNode, typeOfNode, toTextNode, + ascending); try { - return sortPrimitiveVector(elems, javaComparator, warningEncounteredProfile); + return sortPrimitiveVector(elems, javaComparator); } catch (CompareException e) { throw DataflowError.withoutTrace( incomparableValuesError(e.leftOperand, e.rightOperand), this); } } + @TruffleBoundary + private DefaultSortComparator createDefaultComparator( + LessThanNode lessThanNode, + EqualsNode equalsNode, + TypeOfNode typeOfNode, + AnyToTextNode toTextNode, + long ascending + ) { + return new DefaultSortComparator(lessThanNode, equalsNode, typeOfNode, toTextNode, + ascending > 0); + } + @TruffleBoundary @Specialization(guards = { "interop.hasArrayElements(self)", @@ -157,7 +170,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA List compareFuncs = readInteropArray(interop, warningsLib, compareFuncsArray); List groups = splitByComparators(elems, comparators, compareFuncs); - // Prepare input for DefaultComparator and GenericComparator and sort the elements within groups + // Prepare input for DefaultSortComparator and GenericSortComparator and sort the elements within groups var ctx = EnsoContext.get(this); Atom less = ctx.getBuiltins().ordering().newLess(); Atom equal = ctx.getBuiltins().ordering().newEqual(); @@ -166,9 +179,9 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA List resultVec = new ArrayList<>(); try { for (var group : groups) { - Comparator javaComparator; + SortComparator javaComparator; if (isNothing(byFunc) && isNothing(onFunc) && isPrimitiveGroup(group)) { - javaComparator = new DefaultComparator( + javaComparator = new DefaultSortComparator( lessThanNode, equalsNode, typeOfNode, @@ -177,7 +190,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA ); } else { Object compareFunc = isNothing(byFunc) ? group.compareFunc : byFunc; - javaComparator = new GenericComparator( + javaComparator = new GenericSortComparator( ascending > 0, compareFunc, onFunc, @@ -206,15 +219,14 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA } } + @TruffleBoundary(allowInlining = true) private Object sortPrimitiveVector(Object[] elems, - DefaultComparator javaComparator, BranchProfile warningEncounteredProfile) + DefaultSortComparator javaComparator) throws CompareException { Arrays.sort(elems, javaComparator); var sortedVector = Vector.fromArray(new Array(elems)); if (javaComparator.hasWarnings()) { - warningEncounteredProfile.enter(); - CompilerDirectives.transferToInterpreter(); return attachWarnings(sortedVector, javaComparator.getEncounteredWarnings()); } else { return sortedVector; @@ -409,9 +421,9 @@ private boolean isNothing(Object object, EnsoContext ctx) { * Group of elements grouped by comparator. * * @param elems Elements of the group. - * @param comparator Comparator for the elems, i.e., it should hold that + * @param comparator SortComparator for the elems, i.e., it should hold that * {@code elems.each it-> (Comparable.from it) == comparator}. - * @param compareFunc `Comparator.compare` function extracted from the comparator. + * @param compareFunc `SortComparator.compare` function extracted from the comparator. */ private record Group( List elems, @@ -426,12 +438,12 @@ private record Group( * incomparable values. The warnings are gathered as pure Strings in a hash set, so that they are * not duplicated. */ - private static abstract class Comparator implements java.util.Comparator { + private static abstract class SortComparator implements java.util.Comparator { private final Set warnings = new HashSet<>(); private final AnyToTextNode toTextNode; - protected Comparator(AnyToTextNode toTextNode) { + protected SortComparator(AnyToTextNode toTextNode) { this.toTextNode = toTextNode; } @@ -447,14 +459,15 @@ public Set getEncounteredWarnings() { return warnings; } + @TruffleBoundary public boolean hasWarnings() { return !warnings.isEmpty(); } } /** - * Comparator for comparing all values that have Default_Comparator. These are either primitive - * types, or the types that do not provide their own comparator. + * SortComparator for comparing all values that have Default_Comparator. These are either + * primitive types, or the types that do not provide their own comparator. *

* Note that it is essential for this class that the {@code by} method parameter to * {@code Vector.sort} is set to the default value, which is {@code Ordering.compare}, because @@ -462,14 +475,14 @@ public boolean hasWarnings() { * handle partial ordering for primitive types specifically, partial ordering for other types is * not implemented yet - that requires topological sorting). */ - private final class DefaultComparator extends Comparator { + final class DefaultSortComparator extends SortComparator { private final LessThanNode lessThanNode; private final EqualsNode equalsNode; private final TypeOfNode typeOfNode; private final boolean ascending; - private DefaultComparator(LessThanNode lessThanNode, EqualsNode equalsNode, + private DefaultSortComparator(LessThanNode lessThanNode, EqualsNode equalsNode, TypeOfNode typeOfNode, AnyToTextNode toTextNode, boolean ascending) { super(toTextNode); @@ -571,9 +584,9 @@ private int getPrimitiveValueCost(Object object) { /** * Comparator for any values. This comparator compares the values by calling back to Enso (by * {@link #compareFunc}), rather than using compare nodes (i.e. {@link LessThanNode}). directly, - * as opposed to {@link DefaultComparator}. + * as opposed to {@link DefaultSortComparator}. */ - private final class GenericComparator extends Comparator { + private final class GenericSortComparator extends SortComparator { private final boolean ascending; /** @@ -591,7 +604,7 @@ private final class GenericComparator extends Comparator { private final Atom greater; - private GenericComparator( + private GenericSortComparator( boolean ascending, Object compareFunc, Object onFunc, From 4fbdc0ddf3c686fb23832977d63fd1e32f9e6493 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Mon, 3 Apr 2023 15:47:13 +0200 Subject: [PATCH 15/49] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 586fdf1c62fc..44db8f6c9bb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -666,6 +666,7 @@ - [Don't install Python component on Windows][5900] - [Detect potential name conflicts between exported types and FQNs][5966] - [Ensure calls involving warnings remain instrumented][6067] +- [Vector.sort handles incomparable types][5998] [3227]: https://github.com/enso-org/enso/pull/3227 [3248]: https://github.com/enso-org/enso/pull/3248 @@ -768,8 +769,11 @@ [5791]: https://github.com/enso-org/enso/pull/5791 [5900]: https://github.com/enso-org/enso/pull/5900 [5966]: https://github.com/enso-org/enso/pull/5966 + [6067]: https://github.com/enso-org/enso/pull/6067 +[5998]: https://github.com/enso-org/enso/pull/5998 + # Enso 2.0.0-alpha.18 (2021-10-12)
![New Features](/docs/assets/tags/new_features.svg) From 8dfb8731271e85cc6d0484b200cde80cecde99a7 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 10:42:01 +0200 Subject: [PATCH 16/49] Add VectorSortTest --- .../org/enso/interpreter/test/TestBase.java | 11 ++ .../interpreter/test/ValuesGenerator.java | 11 +- .../enso/interpreter/test/VectorSortTest.java | 102 ++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/TestBase.java b/engine/runtime/src/test/java/org/enso/interpreter/test/TestBase.java index 8c39fff912c0..4394b4959136 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/TestBase.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/TestBase.java @@ -116,6 +116,17 @@ protected static Value evalModule(Context ctx, String src) { return mainMethod.execute(); } + /** + * Parses the given module and returns a method by the given name from the module. + * + * @param moduleSrc Source of the whole module + * @return Reference to the method. + */ + protected static Value getMethodFromModule(Context ctx, String moduleSrc, String methodName) { + Value module = ctx.eval(Source.create("enso", moduleSrc)); + return module.invokeMember(Module.EVAL_EXPRESSION, methodName); + } + /** * An artificial RootNode. Used for tests of nodes that need to be adopted. Just create this root * node inside a context, all the other nodes, and insert them via {@link diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/ValuesGenerator.java b/engine/runtime/src/test/java/org/enso/interpreter/test/ValuesGenerator.java index 1dc3f5fcdc2d..99b194ee894b 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/ValuesGenerator.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/ValuesGenerator.java @@ -301,10 +301,17 @@ public List numbers() { collect.add(v(null, "", "42").type()); collect.add(v(null, "", "6.7").type()); collect.add(v(null, "", "40321 * 43202").type()); - collect.add(v(null, """ + collect.add( + v( + null, + """ + from Standard.Base.Data.Ordering import all + fac s n = if n <= 1 then s else @Tail_Call fac n*s n-1 - """, "fac 1 100").type()); + """, + "fac 1 100") + .type()); collect.add(v(null, "", "123 * 10^40").type()); collect.add(v(null, "", "123 * 10^40 + 0.0").type()); collect.add(v(null, "", "123 * 10^40 + 1.0").type()); diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java new file mode 100644 index 000000000000..56c9a428724b --- /dev/null +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java @@ -0,0 +1,102 @@ +package org.enso.interpreter.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Set; +import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; +import org.enso.interpreter.node.expression.builtin.meta.EqualsNode; +import org.enso.interpreter.node.expression.builtin.ordering.SortVectorNode; +import org.enso.interpreter.test.ValuesGenerator.Language; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Value; +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(Theories.class) +public class VectorSortTest extends TestBase { + private static Context context; + private static Value sortFunc; + private static Value equalsFunc; + + @BeforeClass + public static void initCtxAndNodes() { + context = createDefaultContext(); + var code = + """ + from Standard.Base import all + + sort val1 val2 = [val1, val2].sort + equals val1 val2 = val1 == val2 + """; + sortFunc = getMethodFromModule(context, code, "sort"); + equalsFunc = getMethodFromModule(context, code, "equals"); + + values = new ArrayList<>(); + var valuesGenerator = + ValuesGenerator.create(context, Language.ENSO, Language.JAVA, Language.JAVASCRIPT); + values.addAll(valuesGenerator.numbers()); + values.addAll(valuesGenerator.vectors()); + values.addAll(valuesGenerator.arrayLike()); + values.addAll(valuesGenerator.booleans()); + values.addAll(valuesGenerator.durations()); + values.addAll(valuesGenerator.maps()); + } + + @AfterClass + public static void disposeCtx() { + context.close(); + } + + @DataPoints public static List values; + + @Theory + public void testSortHandlesAllValues(Value value1, Value value2) { + Assume.assumeFalse(isNan(value1) || isNan(value2)); + Value res = sortFunc.execute(value1, value2); + assertTrue(res.hasArrayElements()); + assertEquals(2, res.getArraySize()); + List resArray = readPolyglotArray(res); + // check that value1 is there unchanged on some index, and the same for value2 + assertTrue( + "Sorted vector should contain the first value at any index", + invokeEquals(value1, resArray.get(0)) || invokeEquals(value1, resArray.get(1))); + assertTrue( + "Sorted vector should contain the second value at any index", + invokeEquals(value2, resArray.get(0)) || invokeEquals(value2, resArray.get(1))); + } + + private boolean isNan(Value value) { + if (value.isNumber() && value.fitsInDouble()) { + return Double.isNaN(value.asDouble()); + } else { + return false; + } + } + + private List readPolyglotArray(Value array) { + assertTrue(array.hasArrayElements()); + assertTrue(array.hasIterator()); + Value iterator = array.getIterator(); + assertTrue(iterator.isIterator()); + List res = new ArrayList<>(); + while (iterator.hasIteratorNextElement()) { + res.add(iterator.getIteratorNextElement()); + } + return res; + } + + private boolean invokeEquals(Value val1, Value val2) { + Value res = equalsFunc.execute(val1, val2); + assertTrue("Result from Any.== should be boolean", res.isBoolean()); + return res.asBoolean(); + } +} From 80c69d0e5ab928125d012925e28b9c816de4cae5 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 15:28:58 +0200 Subject: [PATCH 17/49] Builtin method should not throw DataflowError. If yes, the message is discarded (a bug?) --- .../node/expression/builtin/ordering/SortVectorNode.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 249eb7e92802..9f49a2a19d39 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -693,14 +693,7 @@ private Function checkAndConvertOnFunc(Object onFuncObj) { private Function checkAndConvertFunction(Object funcObj, String errMsg, int minArgCount, int maxArgCount) { - var ctx = EnsoContext.get(SortVectorNode.this); - var err = DataflowError.withoutTrace( - ctx.getBuiltins().error().makeUnsupportedArgumentsError( - new Object[]{funcObj}, - errMsg - ), - SortVectorNode.this - ); + var err = new IllegalArgumentException(errMsg + ", got " + funcObj); if (funcObj instanceof Function func) { var argCount = func.getSchema().getArgumentsCount(); if (minArgCount <= argCount && argCount <= maxArgCount) { From e0881f48c4ee13a0d1037013ef1c743b2916d152 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 15:29:50 +0200 Subject: [PATCH 18/49] TypeOfNode may not return only Type --- .../node/expression/builtin/ordering/SortVectorNode.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 9f49a2a19d39..e7fef0540cea 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -441,7 +441,7 @@ private record Group( private static abstract class SortComparator implements java.util.Comparator { private final Set warnings = new HashSet<>(); - private final AnyToTextNode toTextNode; + final AnyToTextNode toTextNode; protected SortComparator(AnyToTextNode toTextNode) { this.toTextNode = toTextNode; @@ -561,12 +561,7 @@ private boolean isPrimitiveValue(Object object) { private String getQualifiedTypeName(Object object) { var typeObj = typeOfNode.execute(object); - if (typeObj instanceof Type type) { - return type.getQualifiedName().toString(); - } else { - throw new IllegalStateException( - "Object " + object + " must be an Atom, therefore, it must have Type"); - } + return toTextNode.execute(typeObj).toString(); } private int getPrimitiveValueCost(Object object) { From 19d2d6f9784c9dec96a5de8225f2138ef6ae7191 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 15:30:27 +0200 Subject: [PATCH 19/49] UnresolvedSymbol is not supported as `on` argument to Vector.sort_builtin --- .../lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso index 4ee181879da9..9f70c32dbec7 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Index_Sub_Range.enso @@ -117,7 +117,7 @@ invert_range_selection ranges length needs_sorting = Empty subranges are discarded. sort_and_merge_ranges : Vector Range -> Vector Range sort_and_merge_ranges ranges = - sorted = ranges.filter (range-> range.is_empty.not) . sort on=(.start) + sorted = ranges.filter (range-> range.is_empty.not) . sort on=(_.start) if sorted.is_empty then [] else current_ref = Ref.new sorted.first builder = Vector.new_builder From 9278734d8d93203953f48e6815795e69dade65bf Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 15:30:42 +0200 Subject: [PATCH 20/49] Fix docs --- .../src/main/java/org/enso/polyglot/HostEnsoUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/polyglot-api/src/main/java/org/enso/polyglot/HostEnsoUtils.java b/engine/polyglot-api/src/main/java/org/enso/polyglot/HostEnsoUtils.java index 6e1fe5439133..7c782dc4802f 100644 --- a/engine/polyglot-api/src/main/java/org/enso/polyglot/HostEnsoUtils.java +++ b/engine/polyglot-api/src/main/java/org/enso/polyglot/HostEnsoUtils.java @@ -9,7 +9,7 @@ private HostEnsoUtils() { /** * Extracts a string representation for a polyglot exception. * - * @param exexception the exception + * @param ex the exception * @return message representing the exception */ public static String findExceptionMessage(Throwable ex) { From af9dfa35821156414e341a0d6695011bf5821250 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 15:47:22 +0200 Subject: [PATCH 21/49] Fix bigint spec in LessThanNode --- .../expression/builtin/ordering/LessThanNode.java | 12 ++---------- test/Tests/src/Data/Numbers_Spec.enso | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/LessThanNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/LessThanNode.java index f9406681732f..ba6bd3fe2fc2 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/LessThanNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/LessThanNode.java @@ -116,21 +116,13 @@ Object lessDoubleBigInt(double self, EnsoBigInteger other) { @Specialization @TruffleBoundary boolean lessLongBigInt(long self, EnsoBigInteger other) { - if (BigIntegerOps.fitsInLong(other.getValue())) { - return BigInteger.valueOf(self).compareTo(other.getValue()) < 0; - } else { - return true; - } + return BigInteger.valueOf(self).compareTo(other.getValue()) < 0; } @Specialization @TruffleBoundary boolean lessBigIntLong(EnsoBigInteger self, long other) { - if (BigIntegerOps.fitsInLong(self.getValue())) { - return self.getValue().compareTo(BigInteger.valueOf(other)) < 0; - } else { - return false; - } + return self.getValue().compareTo(BigInteger.valueOf(other)) < 0; } /** diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index d6a4ec2dbd91..97571ee717c9 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -1,6 +1,6 @@ from Standard.Base import all import Standard.Base.Errors.Common.Arithmetic_Error -import Standard.Base.Errors.Common.Type_Error +import Standard.Base.Errors.Common.Incomparable_Values from Standard.Base.Data.Numbers import Number_Parse_Error From 7f2af250eee88648667690daad85dc543082524b Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 17:58:09 +0200 Subject: [PATCH 22/49] Small fixes --- .../lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso | 3 ++- .../node/expression/builtin/ordering/SortVectorNode.java | 2 +- test/Tests/src/Data/Numbers_Spec.enso | 4 ++-- test/Tests/src/Data/Ordering_Spec.enso | 5 ----- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index 038227674cbd..15e695859fb1 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -1,6 +1,7 @@ import project.Data.Text.Text import project.Data.Locale.Locale import project.Errors.Common.Arithmetic_Error +import project.Errors.Common.Incomparable_Values import project.Error.Error import project.Nothing.Nothing import project.Panic.Panic @@ -288,7 +289,7 @@ type Number Check if 1 is equal to 1.0000001 within 0.001. 1.equals 1.0000001 epsilon=0.001 - equals : Number -> Number -> Boolean + equals : Number -> Number -> Boolean ! Incomparable_Values equals self that epsilon=0.0 = (self == that) || ((self - that).abs <= epsilon) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index e7fef0540cea..9e2437fbb457 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -208,7 +208,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA resultVec.addAll(group.elems); } var sortedVector = Vector.fromArray(new Array(resultVec.toArray())); - // Attach gathered warnings and different comparators warning + // Attach gathered warnings along with different comparators warning return attachDifferentComparatorsWarning( attachWarnings(sortedVector, gatheredWarnings), groups diff --git a/test/Tests/src/Data/Numbers_Spec.enso b/test/Tests/src/Data/Numbers_Spec.enso index 97571ee717c9..314d948806a1 100644 --- a/test/Tests/src/Data/Numbers_Spec.enso +++ b/test/Tests/src/Data/Numbers_Spec.enso @@ -467,7 +467,7 @@ spec = Number.negative_infinity . should_equal (-Number.positive_infinity) Number.negative_infinity . equals (-Number.positive_infinity) . should_be_true - Number.nan . equals Number.nan . should_be_false - Number.nan . equals 0 . should_be_false + Number.nan . equals Number.nan . should_fail_with Incomparable_Values + Number.nan . equals 0 . should_fail_with Incomparable_Values main = Test_Suite.run_main spec diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index 846f91373553..2f574dfc4fbc 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -215,11 +215,6 @@ spec = [My_Type.Value 1, Ord.Value 1, 20, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] [Ord.Value 1, 20, 10, My_Type.Value 1].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] - # In other words, if there are incomparable values, make sure that warning for - # "incomparable value X Y" is attached just once. - Test.specify "should not duplicate warnings" <| - Problems.get_attached_warnings ([Nothing, (Ord.Value 1), 20].sort) . should_equal ["Different comparators: [Standard.Base.Data.Ordering.Default_Comparator, Ordering_Spec.Ord_Comparator]", "Values 20 and Nothing are incomparable"] - Test.specify "should be able to sort even unordered values" pending="https://github.com/enso-org/enso/issues/5834" <| [Ord.Value 2, UPair.Value "a" "b", Ord.Value 1, UPair.Value "c" "d"].sort . should_equal [Ord.Value 2, Ord.Value 1, UPair.Value "a" "b", UPair.Value "c" "d"] [Ord.Value 2, UPair.Value "X" "Y", Ord.Value 1, UPair.Value "c" "d"].sort . should_equal [Ord.Value 2, Ord.Value 1, UPair.Value "X" "Y", UPair.Value "c" "d"] From ed97b044beec0287eea9dd908aead0249de44470 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 17:58:14 +0200 Subject: [PATCH 23/49] Small fixes --- .../node/expression/builtin/ordering/SortVectorNode.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 9e2437fbb457..4b17dd7a1ddd 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -210,9 +210,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA var sortedVector = Vector.fromArray(new Array(resultVec.toArray())); // Attach gathered warnings along with different comparators warning return attachDifferentComparatorsWarning( - attachWarnings(sortedVector, gatheredWarnings), - groups - ); + attachWarnings(sortedVector, gatheredWarnings), groups); } catch (CompareException e) { return DataflowError.withoutTrace( incomparableValuesError(e.leftOperand, e.rightOperand), this); From c7267538953379c1efa6bc346fc40588c0113093 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 17:59:01 +0200 Subject: [PATCH 24/49] Nothings and Nans are sorted at the end of default comparator group. But not at the whole end of the resulting vector. --- test/Table_Tests/src/In_Memory/Table_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Table_Tests/src/In_Memory/Table_Spec.enso b/test/Table_Tests/src/In_Memory/Table_Spec.enso index 3361aeaaf6dd..78b2fa0a6457 100644 --- a/test/Table_Tests/src/In_Memory/Table_Spec.enso +++ b/test/Table_Tests/src/In_Memory/Table_Spec.enso @@ -472,7 +472,7 @@ spec = c = Column.from_vector 'foo' [My.Data 1 2, My.Data 2 5, My.Data 3 4, My.Data 6 3, Nothing, My.Data 1 0] cmp a b = Ordering.compare (a.x-a.y).abs (b.x-b.y).abs r = c.sort by=cmp - r.to_vector.should_equal [My.Data 1 2, My.Data 3 4, My.Data 1 0, My.Data 2 5, My.Data 6 3, Nothing] + r.to_vector.should_equal [Nothing, My.Data 1 2, My.Data 3 4, My.Data 1 0, My.Data 2 5, My.Data 6 3] d = Column.from_vector 'foo' [1,3,2,4,Nothing,5] cmp2 a b = Ordering.compare -a -b From 92a10a82b22049dc74fcb2a5dc863b0b5482ffa9 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 5 Apr 2023 18:45:16 +0200 Subject: [PATCH 25/49] Fix checking of `by` parameter - now accepts functions with default arguments. --- .../builtin/ordering/SortVectorNode.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 4b17dd7a1ddd..0e399b2907fa 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -28,6 +28,7 @@ import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode; import org.enso.interpreter.node.expression.builtin.text.AnyToTextNode; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.Array; @@ -680,15 +681,19 @@ private Function checkAndConvertByFunc(Object byFuncObj) { * dataflow error otherwise. */ private Function checkAndConvertOnFunc(Object onFuncObj) { - return checkAndConvertFunction(onFuncObj, - "Unsupported argument for `on`, expected a method with one argument", 1, 1); + return checkAndConvertFunction( + onFuncObj, "Unsupported argument for `on`, expected a method with one argument", 1, 1); } - private Function checkAndConvertFunction(Object funcObj, String errMsg, int minArgCount, - int maxArgCount) { + /** + * @param minArgCount Minimal count of arguments without a default value. + * @param maxArgCount Maximal count of argument without a default value. + */ + private Function checkAndConvertFunction( + Object funcObj, String errMsg, int minArgCount, int maxArgCount) { var err = new IllegalArgumentException(errMsg + ", got " + funcObj); if (funcObj instanceof Function func) { - var argCount = func.getSchema().getArgumentsCount(); + var argCount = getNumberOfNonDefaultArguments(func); if (minArgCount <= argCount && argCount <= maxArgCount) { return func; } else { @@ -700,6 +705,13 @@ private Function checkAndConvertFunction(Object funcObj, String errMsg, int minA } } + private static int getNumberOfNonDefaultArguments(Function function) { + return (int) + Arrays.stream(function.getSchema().getArgumentInfos()) + .filter(argInfo -> !argInfo.hasDefaultValue()) + .count(); + } + private static final class CompareException extends RuntimeException { final Object leftOperand; From 8f78728e68c33664e0389259e32d7d781f6e01fa Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 6 Apr 2023 14:28:45 +0200 Subject: [PATCH 26/49] Fix changelog formatting --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d7a6dedf5d5..c343548c981e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -773,9 +773,7 @@ [5791]: https://github.com/enso-org/enso/pull/5791 [5900]: https://github.com/enso-org/enso/pull/5900 [5966]: https://github.com/enso-org/enso/pull/5966 - [6067]: https://github.com/enso-org/enso/pull/6067 - [5998]: https://github.com/enso-org/enso/pull/5998 # Enso 2.0.0-alpha.18 (2021-10-12) From e2075d9dd3095ee32406baea88f72b1586d8894e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 6 Apr 2023 14:42:22 +0200 Subject: [PATCH 27/49] Fix imports in DebuggingEnsoTest --- .../test/java/org/enso/interpreter/test/DebuggingEnsoTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java index f36036b2e117..4330e59119c3 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/DebuggingEnsoTest.java @@ -113,6 +113,8 @@ private Value createEnsoMethod(String source, String methodName) { @Test public void recursiveFactorialCall() { final Value facFn = createEnsoMethod(""" + from Standard.Base.Data.Ordering import all + fac : Number -> Number fac n = facacc : Number -> Number -> Number From 6983769288245a90b14b765ea2cafc3c192ad5f3 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 6 Apr 2023 14:45:30 +0200 Subject: [PATCH 28/49] Remove Array.sort_builtin --- .../Base/0.0.0-dev/src/Data/Array.enso | 15 -- .../expression/builtin/mutable/SortNode.java | 161 ------------------ 2 files changed, 176 deletions(-) delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso index 9e80e81f3bf6..7cff48c19adf 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso @@ -134,21 +134,6 @@ type Array length : Integer length self = @Builtin_Method "Array.length" - ## Sorts the Array. - - Arguments: - - comparator: A comparison function that takes two elements and returns - an Ordering that describes how the first element is ordered with - respect to the second. - - > Example - Getting a sorted array of numbers. - - [3,2,1].to_array.sort - sort : (Any -> Any -> Ordering) -> Array - sort self comparator=(Ordering.compare _ _) = - self.sort_builtin comparator - ## Identity. This method is implemented purely for completeness with the runtime's diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java deleted file mode 100644 index a9c070922c76..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/mutable/SortNode.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.enso.interpreter.node.expression.builtin.mutable; - -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Cached; -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.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.library.CachedLibrary; -import com.oracle.truffle.api.nodes.LoopNode; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.profiles.BranchProfile; - -import java.util.Arrays; -import java.util.Comparator; - -import org.enso.interpreter.dsl.BuiltinMethod; -import org.enso.interpreter.node.callable.dispatch.CallOptimiserNode; -import org.enso.interpreter.node.callable.dispatch.SimpleCallOptimiserNode; -import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; -import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.callable.atom.Atom; -import org.enso.interpreter.runtime.callable.function.Function; -import org.enso.interpreter.runtime.data.Array; -import org.enso.interpreter.runtime.error.DataflowError; -import org.enso.interpreter.runtime.error.PanicException; -import org.enso.interpreter.runtime.state.State; - -@BuiltinMethod(type = "Array", name = "sort_builtin", description = "Returns a sorted array.") -public abstract class SortNode extends Node { - private @Child CallOptimiserNode callOptimiserNode = SimpleCallOptimiserNode.build(); - private @Child InvalidComparisonNode invalidComparisonNode = InvalidComparisonNode.build(); - private final BranchProfile invalidCompareResultProfile = BranchProfile.create(); - - abstract Object execute(State state, Object self, Object comparator); - - static SortNode build() { - return SortNodeGen.create(); - } - - @Specialization - Object doArray(State state, Array self, Function comparator) { - EnsoContext context = EnsoContext.get(this); - int size = self.getItems().length; - Object[] newArr = new Object[size]; - System.arraycopy(self.getItems(), 0, newArr, 0, size); - - try { - return getComparatorAndSort(state, newArr, comparator, context); - } catch (CompareException e) { - return DataflowError.withoutTrace( - incomparableValuesError(e.leftOperand, e.rightOperand), this); - } - } - - @Specialization(guards = "arrays.hasArrayElements(self)") - Object doPolyglotArray( - State state, - Object self, - Function comparator, - @CachedLibrary(limit = "3") InteropLibrary arrays, - @Cached HostValueToEnsoNode hostValueToEnsoNode) { - long size; - Object[] newArray; - try { - size = arrays.getArraySize(self); - newArray = new Object[(int) size]; - for (int i = 0; i < size; i++) { - newArray[i] = hostValueToEnsoNode.execute(arrays.readArrayElement(self, i)); - } - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw new IllegalStateException(e); - } - - try { - return getComparatorAndSort(state, newArray, comparator, EnsoContext.get(this)); - } catch (CompareException e) { - return DataflowError.withoutTrace( - incomparableValuesError(e.leftOperand, e.rightOperand), this); - } - } - - @Fallback - Object doOther(State state, Object self, Object comparator) { - CompilerDirectives.transferToInterpreter(); - var fun = EnsoContext.get(this).getBuiltins().function(); - throw new PanicException( - EnsoContext.get(this).getBuiltins().error().makeTypeError(fun, comparator, "comparator"), - this); - } - - private Object getComparatorAndSort( - State state, Object[] rawItems, Function comparator, EnsoContext context) { - Comparator compare = new SortComparator(comparator, context, this, state); - runSort(compare, rawItems); - return new Array(rawItems); - } - - Object[] runSort(Comparator compare, Object[] self) { - doSort(self, compare); - LoopNode.reportLoopCount(this, self.length); - return self; - } - - @TruffleBoundary - void doSort(Object[] items, Comparator compare) { - Arrays.sort(items, compare); - } - - private Object incomparableValuesError(Object left, Object right) { - return EnsoContext.get(this).getBuiltins().error().makeIncomparableValues(left, right); - } - - private class SortComparator implements Comparator { - private final Function compFn; - private final EnsoContext context; - private final Atom less; - private final Atom equal; - private final Atom greater; - private final SortNode outerThis; - private final State state; - - SortComparator(Function compFn, EnsoContext context, SortNode outerThis, State state) { - this.compFn = compFn; - this.context = context; - this.less = context.getBuiltins().ordering().newLess(); - this.equal = context.getBuiltins().ordering().newEqual(); - this.greater = context.getBuiltins().ordering().newGreater(); - this.outerThis = outerThis; - this.state = state; - } - - @Override - public int compare(Object o1, Object o2) { - Object res = callOptimiserNode.executeDispatch(compFn, null, state, new Object[] {o1, o2}); - if (res == less) { - return -1; - } else if (res == equal) { - return 0; - } else if (res == greater) { - return 1; - } else { - // res is either null, or Incomparable_Values was thrown. - invalidCompareResultProfile.enter(); - throw new CompareException(o1, o2); - } - } - } - - private static final class CompareException extends RuntimeException { - final Object leftOperand; - final Object rightOperand; - - private CompareException(Object leftOperand, Object rightOperand) { - this.leftOperand = leftOperand; - this.rightOperand = rightOperand; - } - } -} From a2bfc8bf38be19168ffb1b8dd42a9780c174de8e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 6 Apr 2023 16:38:37 +0200 Subject: [PATCH 29/49] Add comparison operators to micro-distribution --- .../lib/Standard/Base/0.0.0-dev/src/Any.enso | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso index 93f4db8155c4..43498585cecb 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso @@ -3,8 +3,12 @@ type Any catch_primitive handler = @Builtin_Method "Any.catch_primitive" to_text self = @Builtin_Method "Any.to_text" to_display_text self = @Builtin_Method "Any.to_display_text" - # Access EqualsNode directly == self other = Comparable.equals_builtin self other + != self other = (self == other).not + < self other = Comparable.less_than_builtin self other + <= self other = Comparable.less_than_builtin self other || Comparable.equals_builtin self other + > self other = Comparable.less_than_builtin other self + >= self other = Comparable.less_than_builtin other self || Comparable.equals_builtin other self @Builtin_Type type Comparable From 310f1df42ef4a54bab67c4b5b794ce62db314b0a Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 12 Apr 2023 09:54:10 +0200 Subject: [PATCH 30/49] Remove Array.sort_builtin --- test/Tests/src/Data/Vector_Spec.enso | 1 - test/Tests/src/Semantic/Python_Interop_Spec.enso | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/test/Tests/src/Data/Vector_Spec.enso b/test/Tests/src/Data/Vector_Spec.enso index d63905c52acf..a6b3035e1c72 100644 --- a/test/Tests/src/Data/Vector_Spec.enso +++ b/test/Tests/src/Data/Vector_Spec.enso @@ -559,7 +559,6 @@ spec = Test.group "Vectors" <| ["aa", 2].sort . should_equal [2, "aa"] [2, Date.new 1999].sort . should_equal [2, Date.new 1999] - Test.expect_panic_with ([3,2,1].to_array.sort 42) Type_Error Test.specify "should leave the original vector unchanged" <| non_empty_vec = [2, 4, 2, 3, 2, 3] diff --git a/test/Tests/src/Semantic/Python_Interop_Spec.enso b/test/Tests/src/Semantic/Python_Interop_Spec.enso index d3ad1b1570c5..0fbd49f6b75c 100644 --- a/test/Tests/src/Semantic/Python_Interop_Spec.enso +++ b/test/Tests/src/Semantic/Python_Interop_Spec.enso @@ -92,9 +92,9 @@ spec = vec = Vector.from_polyglot_array make_array vec.map .x . should_equal [30, 10, 20] - arr = vec.map .x . to_array - sorted = arr.sort - arr . should_equal [30, 10, 20] + mapped_vec = vec.map .x + sorted = mapped_vec.sort + mapped_vec . should_equal [30, 10, 20] sorted . should_equal [10, 20, 30] arr_2 = make_num_array From fee458ae9ee40d21fd5a95bc6ab0cdebc9d9275e Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Wed, 12 Apr 2023 09:54:46 +0200 Subject: [PATCH 31/49] Replace Incomparable_Values by Type_Error in some tests --- test/Tests/src/Data/Text_Spec.enso | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Tests/src/Data/Text_Spec.enso b/test/Tests/src/Data/Text_Spec.enso index a0e92ee7befd..d0d20f136ca3 100644 --- a/test/Tests/src/Data/Text_Spec.enso +++ b/test/Tests/src/Data/Text_Spec.enso @@ -119,7 +119,7 @@ spec = (accent_1 != Nothing) . should_be_true Ordering.compare accent_1 Nothing . should_fail_with Incomparable_Values (accent_1 > Nothing) . should_fail_with Incomparable_Values - accent_1 . compare_to_ignore_case Nothing . should_fail_with Incomparable_Values + accent_1 . compare_to_ignore_case Nothing . should_fail_with Type_Error earlier_suffix = "aooooz" later_suffix = "bo" From 7f933dd5e5d8632f4c2ac580f3107344fc89eabe Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 10:49:03 +0200 Subject: [PATCH 32/49] Add on_incomparable argument to Vector.sort_builtin --- .../Base/0.0.0-dev/src/Data/Vector.enso | 14 +- .../builtin/ordering/SortVectorNode.java | 310 ++++++++++++------ 2 files changed, 221 insertions(+), 103 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index 8ace5ba7b33b..e8eb5b3d1992 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -16,6 +16,7 @@ import project.Errors.Common.Not_Found import project.Errors.Common.Type_Error import project.Error.Error import project.Errors.Illegal_Argument.Illegal_Argument +import project.Errors.Problem_Behavior.Problem_Behavior import project.Function.Function import project.Math import project.Meta @@ -853,7 +854,8 @@ type Vector a elements, returning an an `Ordering` if the two elements are comparable or `Nothing` if they are not. If set to `Nothing` (the default argument), `Ordering.compare _ _` method will be used. - Must be a static method. + - on_incomparable: A `Problem_Behavior` specifying what should happen if + two incomparable values are encountered. By default, elements are sorted in ascending order. @@ -907,13 +909,17 @@ type Vector a the resulting vector. [My_Type.Value 'hello', 1].sort == [1, My_Type.Value 'hello'] - sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Vector Any ! Incomparable_Values - sort self (order = Sort_Direction.Ascending) on=Nothing by=Nothing = + sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Problem_Behavior -> Vector Any ! Incomparable_Values + sort self (order = Sort_Direction.Ascending) on=Nothing by=Nothing on_incomparable=Problem_Behavior.Ignore = comps = case on == Nothing of True -> self.map it-> Comparable.from it False -> self.map it-> Comparable.from (on it) compare_funcs = comps.map (it-> it.compare) - self.sort_builtin order.to_sign comps compare_funcs by on + problem_behavior = case on_incomparable of + Problem_Behavior.Ignore -> 0 + Problem_Behavior.Report_Warning -> 1 + Problem_Behavior.Report_Error -> 2 + self.sort_builtin order.to_sign comps compare_funcs by on problem_behavior ## UNSTABLE Keeps only unique elements within the Vector, removing any duplicates. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 0e399b2907fa..d1a0e5b2048a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -2,6 +2,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; @@ -28,7 +29,6 @@ import org.enso.interpreter.node.expression.builtin.meta.TypeOfNode; import org.enso.interpreter.node.expression.builtin.text.AnyToTextNode; import org.enso.interpreter.runtime.EnsoContext; -import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.atom.Atom; import org.enso.interpreter.runtime.callable.function.Function; import org.enso.interpreter.runtime.data.Array; @@ -46,18 +46,16 @@ /** * Sorts a vector with elements that have only Default_Comparator, thus, only elements with a * builtin type, which is the most common scenario for sorting. - *

- * TODO: Max number of attached Incomparable values warnings? - * - hardcode or pass via a new parameter to Vector.sort? + * + *

TODO: Max number of attached Incomparable values warnings? - hardcode or pass via a new + * parameter to Vector.sort? */ -@BuiltinMethod( - type = "Vector", - name = "sort_builtin", - description = "Returns a sorted vector." -) +@BuiltinMethod(type = "Vector", name = "sort_builtin", description = "Returns a sorted vector.") @GenerateUncached public abstract class SortVectorNode extends Node { + private static final TruffleLogger logger = TruffleLogger.getLogger("enso", SortVectorNode.class); + public static SortVectorNode build() { return SortVectorNodeGen.create(); } @@ -65,22 +63,30 @@ public static SortVectorNode build() { /** * Sorts a vector with elements that have only Default_Comparator, thus, only builtin types. * - * @param self Vector that has elements with only Default_Comparator, that are - * elements with builtin types. - * @param ascending -1 for descending, 1 for ascending - * @param comparators Vector of comparators, with the same length of self. This is gather in - * the Enso code, because doing that in this builtin would be difficult. - * If {@code onFunc} parameter is not {@code Nothing}, comparators are - * gathered from the result of {@code onFunc} projection. + * @param self Vector that has elements with only Default_Comparator, that are elements with + * builtin types. + * @param ascending -1 for descending, 1 for ascending + * @param comparators Vector of comparators, with the same length of self. This is gather in the + * Enso code, because doing that in this builtin would be difficult. If {@code onFunc} + * parameter is not {@code Nothing}, comparators are gathered from the result of {@code + * onFunc} projection. * @param compareFunctions Vector of `Comparator.compare` functions gathered from the comparators - * @param byFunc If Nothing, then the default `by` function should be used. The default - * `by` function is `Ordering.compare`. + * @param byFunc If Nothing, then the default `by` function should be used. The default `by` + * function is `Ordering.compare`. * @param onFunc If Nothing, then the default identity function should be used. + * @param problemBehavior A long representation of `Problem_Behavior`. Ignore is 0, Report_warning + * is 1, and Report_Error is 2. * @return A new, sorted vector. */ - public abstract Object execute(State state, @AcceptsError Object self, long ascending, + public abstract Object execute( + State state, + @AcceptsError Object self, + long ascending, Object comparators, - Object compareFunctions, Object byFunc, Object onFunc); + Object compareFunctions, + Object byFunc, + Object onFunc, + long problemBehavior); /** * Sorts primitive values, i.e., values with only Default_Comparator. We can optimize this case. @@ -89,14 +95,22 @@ public abstract Object execute(State state, @AcceptsError Object self, long asce * it can redefine the default partial ordering of the primitive values, which requires * topological sort. */ - @Specialization(guards = { - "interop.hasArrayElements(self)", - "areAllDefaultComparators(interop, comparators)", - "isNothing(byFunc)", - "isNothing(onFunc)" - }) - Object sortPrimitives(State state, Object self, long ascending, Object comparators, - Object compareFunctions, Object byFunc, Object onFunc, + @Specialization( + guards = { + "interop.hasArrayElements(self)", + "areAllDefaultComparators(interop, comparators)", + "isNothing(byFunc)", + "isNothing(onFunc)" + }) + Object sortPrimitives( + State state, + Object self, + long ascending, + Object comparators, + Object compareFunctions, + Object byFunc, + Object onFunc, + long problemBehavior, @Cached LessThanNode lessThanNode, @Cached EqualsNode equalsNode, @Cached HostValueToEnsoNode hostValueToEnsoNode, @@ -105,6 +119,11 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato @Cached BranchProfile warningEncounteredProfile, @CachedLibrary(limit = "10") InteropLibrary interop, @CachedLibrary(limit = "5") WarningsLibrary warningsLib) { + logger.fine( + () -> + String.format( + "SortVectorNode.sortPrimitives: byFunc = %s, onFunc = %s, problemBehaviorNum = %s", + byFunc, onFunc, ProblemBehavior.fromInt((int) problemBehavior))); EnsoContext ctx = EnsoContext.get(this); Object[] elems; try { @@ -113,29 +132,28 @@ Object sortPrimitives(State state, Object self, long ascending, Object comparato elems = new Object[(int) size]; for (int i = 0; i < size; i++) { if (interop.isArrayElementReadable(self, i)) { - elems[i] = hostValueToEnsoNode.execute( - interop.readArrayElement(self, i) - ); + elems[i] = hostValueToEnsoNode.execute(interop.readArrayElement(self, i)); } else { CompilerDirectives.transferToInterpreter(); throw new PanicException( - ctx.getBuiltins().error().makeUnsupportedArgumentsError( - new Object[]{self}, - "Cannot read array element at index " + i + " of " + self - ), - this - ); + ctx.getBuiltins() + .error() + .makeUnsupportedArgumentsError( + new Object[] {self}, + "Cannot read array element at index " + i + " of " + self), + this); } } } catch (UnsupportedMessageException | InvalidArrayIndexException e) { throw new IllegalStateException("Should not reach here", e); } - var javaComparator = createDefaultComparator(lessThanNode, equalsNode, typeOfNode, toTextNode, - ascending); + var javaComparator = + createDefaultComparator( + lessThanNode, equalsNode, typeOfNode, toTextNode, ascending, problemBehavior); try { return sortPrimitiveVector(elems, javaComparator); } catch (CompareException e) { - throw DataflowError.withoutTrace( + return DataflowError.withoutTrace( incomparableValuesError(e.leftOperand, e.rightOperand), this); } } @@ -146,18 +164,31 @@ private DefaultSortComparator createDefaultComparator( EqualsNode equalsNode, TypeOfNode typeOfNode, AnyToTextNode toTextNode, - long ascending - ) { - return new DefaultSortComparator(lessThanNode, equalsNode, typeOfNode, toTextNode, - ascending > 0); + long ascending, + long problemBehaviorNum) { + return new DefaultSortComparator( + lessThanNode, + equalsNode, + typeOfNode, + toTextNode, + ascending > 0, + ProblemBehavior.fromInt((int) problemBehaviorNum)); } @TruffleBoundary - @Specialization(guards = { - "interop.hasArrayElements(self)", - }) - Object sortGeneric(State state, Object self, long ascending, Object comparatorsArray, - Object compareFuncsArray, Object byFunc, Object onFunc, + @Specialization( + guards = { + "interop.hasArrayElements(self)", + }) + Object sortGeneric( + State state, + Object self, + long ascending, + Object comparatorsArray, + Object compareFuncsArray, + Object byFunc, + Object onFunc, + long problemBehaviorNum, @CachedLibrary(limit = "10") InteropLibrary interop, @CachedLibrary(limit = "5") WarningsLibrary warningsLib, @Cached LessThanNode lessThanNode, @@ -165,13 +196,24 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA @Cached TypeOfNode typeOfNode, @Cached AnyToTextNode toTextNode, @Cached(value = "build()", uncached = "build()") CallOptimiserNode callNode) { + var problemBehavior = ProblemBehavior.fromInt((int) problemBehaviorNum); + logger.finer( + () -> + String.format( + "SortVectorNode.sortGeneric: onFunc=%s, byFunc=%s, problemBehavior=%s", + onFunc, byFunc, problemBehavior)); // Split into groups List elems = readInteropArray(interop, warningsLib, self); List comparators = readInteropArray(interop, warningsLib, comparatorsArray); List compareFuncs = readInteropArray(interop, warningsLib, compareFuncsArray); List groups = splitByComparators(elems, comparators, compareFuncs); + logger.fine( + () -> + String.format( + "sortGeneric: elems.length=%d, groups.length=%d", elems.size(), groups.size())); - // Prepare input for DefaultSortComparator and GenericSortComparator and sort the elements within groups + // Prepare input for DefaultSortComparator and GenericSortComparator and sort the elements + // within groups var ctx = EnsoContext.get(this); Atom less = ctx.getBuiltins().ordering().newLess(); Atom equal = ctx.getBuiltins().ordering().newEqual(); @@ -182,26 +224,26 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA for (var group : groups) { SortComparator javaComparator; if (isNothing(byFunc) && isNothing(onFunc) && isPrimitiveGroup(group)) { - javaComparator = new DefaultSortComparator( - lessThanNode, - equalsNode, - typeOfNode, - toTextNode, - ascending > 0 - ); + javaComparator = + new DefaultSortComparator( + lessThanNode, equalsNode, typeOfNode, toTextNode, ascending > 0, problemBehavior); } else { Object compareFunc = isNothing(byFunc) ? group.compareFunc : byFunc; - javaComparator = new GenericSortComparator( - ascending > 0, - compareFunc, - onFunc, - group.comparator, - callNode, toTextNode, - state, - less, - equal, - greater); + javaComparator = + new GenericSortComparator( + ascending > 0, + compareFunc, + onFunc, + problemBehavior, + group.comparator, + callNode, + toTextNode, + state, + less, + equal, + greater); } + logger.fine(() -> "Sorting Group@" + Integer.toHexString(group.hashCode())); group.elems.sort(javaComparator); if (javaComparator.hasWarnings()) { gatheredWarnings.addAll(javaComparator.getEncounteredWarnings()); @@ -210,8 +252,51 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA } var sortedVector = Vector.fromArray(new Array(resultVec.toArray())); // Attach gathered warnings along with different comparators warning - return attachDifferentComparatorsWarning( - attachWarnings(sortedVector, gatheredWarnings), groups); + switch (problemBehavior) { + case REPORT_ERROR -> { + if (groups.size() > 1) { + logger.fine( + () -> + "Have more groups than 1 and problem_behavior is Report_Error --> throw Dataflow_Error"); + var allComparatorNames = + groups.stream() + .map(Group::comparator) + .map(comp -> comp.getQualifiedName().toString()); + var err = + ctx.getBuiltins() + .error() + .makeUnsupportedArgumentsError( + new Object[] {self}, + "self argument has more than one comparator - All comparators are: " + + allComparatorNames + + ". " + + "See the documentation of Vector.sort method."); + return DataflowError.withoutTrace(err, this); + } else { + // Just one comparator, different from Default_Comparator + if (!gatheredWarnings.isEmpty()) { + logger.fine( + () -> + String.format( + "Have one comparator different from Default_Comparator and problem_behavior is Report_Error" + + " gatheredWarnings not empty --> throw Dataflow_Error. gatheredWarning.size()=%d", + gatheredWarnings.size())); + throw new UnsupportedOperationException("unimplemented"); + } else { + return sortedVector; + } + } + } + case REPORT_WARNING -> { + logger.fine(() -> String.format("Attaching %d warnings", gatheredWarnings.size())); + return attachDifferentComparatorsWarning( + attachWarnings(sortedVector, gatheredWarnings), groups); + } + case IGNORE -> { + return sortedVector; + } + default -> throw new IllegalStateException("unreachable"); + } } catch (CompareException e) { return DataflowError.withoutTrace( incomparableValuesError(e.leftOperand, e.rightOperand), this); @@ -222,6 +307,7 @@ Object sortGeneric(State state, Object self, long ascending, Object comparatorsA private Object sortPrimitiveVector(Object[] elems, DefaultSortComparator javaComparator) throws CompareException { + logger.fine(() -> "Sorting primitive elems = " + Arrays.toString(elems)); Arrays.sort(elems, javaComparator); var sortedVector = Vector.fromArray(new Array(elems)); @@ -285,11 +371,14 @@ private Object attachWarnings(Object vector, Set warnings) { } private Object attachDifferentComparatorsWarning(Object vector, List groups) { - var diffCompsMsg = groups.stream() - .map(Group::comparator) - .map(comparator -> comparator.getQualifiedName().toString()) - .collect(Collectors.joining(", ")); + var diffCompsMsg = + groups.stream() + .map(Group::comparator) + .map(comparator -> comparator.getQualifiedName().toString()) + .collect(Collectors.joining(", ")); var text = Text.create("Different comparators: [" + diffCompsMsg + "]"); + logger.finer( + () -> String.format("Will attach different comparators warning '%s'", diffCompsMsg)); var warn = Warning.create(EnsoContext.get(this), text, this); return WithWarnings.appendTo(vector, new ArrayRope<>(warn)); } @@ -416,21 +505,30 @@ private boolean isNothing(Object object, EnsoContext ctx) { return object == ctx.getBuiltins().nothing(); } + private enum ProblemBehavior { + IGNORE, + REPORT_WARNING, + REPORT_ERROR; + + static ProblemBehavior fromInt(int intValue) { + return switch (intValue) { + case 0 -> IGNORE; + case 1 -> REPORT_WARNING; + case 2 -> REPORT_ERROR; + default -> throw new IllegalArgumentException(Integer.toString(intValue)); + }; + } + } + /** * Group of elements grouped by comparator. * - * @param elems Elements of the group. - * @param comparator SortComparator for the elems, i.e., it should hold that - * {@code elems.each it-> (Comparable.from it) == comparator}. + * @param elems Elements of the group. + * @param comparator SortComparator for the elems, i.e., it should hold that {@code elems.each + * it-> (Comparable.from it) == comparator}. * @param compareFunc `SortComparator.compare` function extracted from the comparator. */ - private record Group( - List elems, - Type comparator, - Function compareFunc - ) { - - } + private record Group(List elems, Type comparator, Function compareFunc) {} /** * Convenient base class that implements java.util.Comparator, and gathers warnings about @@ -441,9 +539,11 @@ private static abstract class SortComparator implements java.util.Comparator warnings = new HashSet<>(); final AnyToTextNode toTextNode; + final ProblemBehavior problemBehavior; - protected SortComparator(AnyToTextNode toTextNode) { + protected SortComparator(AnyToTextNode toTextNode, ProblemBehavior problemBehavior) { this.toTextNode = toTextNode; + this.problemBehavior = problemBehavior; } @TruffleBoundary @@ -467,12 +567,12 @@ public boolean hasWarnings() { /** * SortComparator for comparing all values that have Default_Comparator. These are either * primitive types, or the types that do not provide their own comparator. - *

- * Note that it is essential for this class that the {@code by} method parameter to - * {@code Vector.sort} is set to the default value, which is {@code Ordering.compare}, because - * then, we know that the partial ordering for primitive types was not redefined by the user (we - * handle partial ordering for primitive types specifically, partial ordering for other types is - * not implemented yet - that requires topological sorting). + * + *

Note that it is essential for this class that the {@code by} method parameter to {@code + * Vector.sort} is set to the default value, which is {@code Ordering.compare}, because then, we + * know that the partial ordering for primitive types was not redefined by the user (we handle + * partial ordering for primitive types specifically, partial ordering for other types is not + * implemented yet - that requires topological sorting). */ final class DefaultSortComparator extends SortComparator { @@ -481,10 +581,14 @@ final class DefaultSortComparator extends SortComparator { private final TypeOfNode typeOfNode; private final boolean ascending; - private DefaultSortComparator(LessThanNode lessThanNode, EqualsNode equalsNode, + private DefaultSortComparator( + LessThanNode lessThanNode, + EqualsNode equalsNode, TypeOfNode typeOfNode, - AnyToTextNode toTextNode, boolean ascending) { - super(toTextNode); + AnyToTextNode toTextNode, + boolean ascending, + ProblemBehavior problemBehavior) { + super(toTextNode, problemBehavior); this.lessThanNode = lessThanNode; this.equalsNode = equalsNode; this.typeOfNode = typeOfNode; @@ -504,7 +608,6 @@ int compareValuesWithDefaultComparator(Object x, Object y) { Object xLessThanYRes = lessThanNode.execute(x, y); if (isNothing(xLessThanYRes)) { // x and y are incomparable - this can happen if x and y are different types - attachIncomparableValuesWarning(x, y); return handleIncomparableValues(x, y); } else if (isTrue(xLessThanYRes)) { return ascending ? -1 : 1; @@ -515,7 +618,6 @@ int compareValuesWithDefaultComparator(Object x, Object y) { return ascending ? 1 : -1; } else { // yLessThanXRes is either Nothing or False - attachIncomparableValuesWarning(y, x); return handleIncomparableValues(y, x); } } @@ -523,6 +625,10 @@ int compareValuesWithDefaultComparator(Object x, Object y) { } private int handleIncomparableValues(Object x, Object y) { + switch (problemBehavior) { + case REPORT_ERROR -> throw new CompareException(x, y); + case REPORT_WARNING -> attachIncomparableValuesWarning(x, y); + } if (isPrimitiveValue(x) || isPrimitiveValue(y)) { if (isPrimitiveValue(x) && isPrimitiveValue(y)) { return handleIncomparablePrimitives(x, y); @@ -597,15 +703,19 @@ private final class GenericSortComparator extends SortComparator { private final Atom equal; private final Atom greater; - private GenericSortComparator( boolean ascending, Object compareFunc, Object onFunc, - Type comparator, CallOptimiserNode callNode, AnyToTextNode toTextNode, State state, - Atom less, Atom equal, + ProblemBehavior problemBehavior, + Type comparator, + CallOptimiserNode callNode, + AnyToTextNode toTextNode, + State state, + Atom less, + Atom equal, Atom greater) { - super(toTextNode); + super(toTextNode, problemBehavior); assert compareFunc != null; assert comparator != null; this.comparator = comparator; @@ -720,6 +830,8 @@ private static final class CompareException extends RuntimeException { private CompareException(Object leftOperand, Object rightOperand) { this.leftOperand = leftOperand; this.rightOperand = rightOperand; + logger.finer( + () -> String.format("CompareException(left=%s, right=%s)", leftOperand, rightOperand)); } } } From 08f0e319ba8c9b7400ebcdc2e6bdcd81fdf27644 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 12:26:50 +0200 Subject: [PATCH 33/49] Fix after merge - Array.sort delegates to Vector.sort --- .../Base/0.0.0-dev/src/Data/Array.enso | 40 +++++++++++++++---- .../builtin/ordering/SortArrayNode.java | 32 +++++++++++++++ test/Tests/src/Data/Array_Spec.enso | 5 +-- 3 files changed, 66 insertions(+), 11 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso index 93968dc473fe..b92f572d49d4 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso @@ -14,6 +14,7 @@ import project.Error.Error import project.Errors.Common.Incomparable_Values import project.Errors.Common.Not_Found import project.Errors.Common.Index_Out_Of_Bounds +import project.Errors.Problem_Behavior.Problem_Behavior import project.Meta import project.Nothing.Nothing import project.Panic.Panic @@ -160,13 +161,17 @@ type Array Arguments: - order: The order in which the array elements are sorted. - on: A projection from the element type to the value of that element - being sorted on. + being sorted on. If set to `Nothing` (the default argument), + identity function will be used. - by: A function that compares the result of applying `on` to two - elements, returning an Ordering to compare them. + elements, returning an an `Ordering` if the two elements are comparable + or `Nothing` if they are not. If set to `Nothing` (the default argument), + `Ordering.compare _ _` method will be used. + - on_incomparable: A `Problem_Behavior` specifying what should happen if + two incomparable values are encountered. + + By default, elements are sorted in ascending order. - By default, elements are sorted in ascending order, using the comparator - acquired from each element. A custom compare function may be passed to - the sort method. This is a stable sort, meaning that items that compare the same will not have their order changed by the sorting process. @@ -184,6 +189,18 @@ type Array is partially sorted. When the array is randomly ordered, the performance is equivalent to a standard mergesort. + ? Multiple comparators + Elements with different comparators are incomparable by definition. + This case is handled by first grouping the `self` array into groups + with the same comparator, recursively sorting these groups, and then + merging them back together. The order of the sorted groups in the + resulting array is based on the order of fully qualified names of + the comparators in the `self` array, with the exception of the group + for the default comparator, which is always the first group. + + Additionally, a warning will be attached, explaining that incomparable + values were encountered. + It takes equal advantage of ascending and descending runs in the array, making it much simpler to merge two or more sorted arrays: simply concatenate them and sort. @@ -197,8 +214,17 @@ type Array Sorting an array of `Pair`s on the first element, descending. [Pair 1 2, Pair -1 8].to_array.sort Sort_Direction.Descending (_.first) - sort : Sort_Direction -> (Any -> Any) -> (Any -> Any -> Ordering) -> Vector Any ! Incomparable_Values - sort self (order = Sort_Direction.Ascending) (on = x -> x) (by = (Ordering.compare _ _)) = Vector.sort self order on by + + > Example + Sorting an array with elements with different comparators. Values 1 + and My_Type have different comparators. 1 will be sorted before My_Type + because it has the default comparator, and warning will be attached to + the resulting array. + + [My_Type.Value 'hello', 1].to_array.sort == [1, My_Type.Value 'hello'].to_array + sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Problem_Behavior -> Vector Any ! Incomparable_Values + sort self (order = Sort_Direction.Ascending) on=Nothing by=Nothing on_incomparable=Problem_Behavior.Ignore = + Vector.sort self order on by on_incomparable ## Creates a new `Vector` with only the specified range of elements from the input, removing any elements outside the range. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java new file mode 100644 index 000000000000..f4f9c9c02f5c --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java @@ -0,0 +1,32 @@ +package org.enso.interpreter.node.expression.builtin.ordering; + +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.AcceptsError; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.state.State; + +/** + * Just a wrapper for {@code Array.sort_builtin} that delegates to {@code Vector.sort_builtin}. This + * is a temporary workaround until Array and Vector are fully merged into a single type. + */ +@BuiltinMethod(type = "Array", name = "sort_builtin") +public final class SortArrayNode extends Node { + @Child private SortVectorNode sortVectorNode = SortVectorNode.build(); + + public Object execute( + State state, + @AcceptsError Object self, + long ascending, + Object comparators, + Object compareFunctions, + Object byFunc, + Object onFunc, + long problemBehavior) { + return sortVectorNode.execute( + state, self, ascending, comparators, compareFunctions, byFunc, onFunc, problemBehavior); + } + + public static SortArrayNode build() { + return new SortArrayNode(); + } +} diff --git a/test/Tests/src/Data/Array_Spec.enso b/test/Tests/src/Data/Array_Spec.enso index 3df0bf8c154c..86581f37d9f0 100644 --- a/test/Tests/src/Data/Array_Spec.enso +++ b/test/Tests/src/Data/Array_Spec.enso @@ -52,10 +52,7 @@ spec = Test.group "Compare functionality with Vector" <| Test.specify "compare methods" <| vector_methods = Meta.meta Vector . methods . sort - array_methods_all = Meta.meta Array . methods . sort - - array_methods = array_methods_all.filter (name -> name != "sort_builtin") - + array_methods = Meta.meta Array . methods . sort vector_methods . should_equal array_methods Test.group "ArrayOverBuffer" <| From 5bc344e17e8af3dde9ebffb008447d237d05c688 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 17:48:33 +0200 Subject: [PATCH 34/49] Add more tests for problem_behavior on Vector.sort --- test/Tests/src/Data/Ordering_Spec.enso | 10 +++++----- test/Tests/src/Data/Vector_Spec.enso | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index 2f574dfc4fbc..4d9288255fef 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -170,13 +170,13 @@ spec = expect_incomparable_warn (Ord.Value 1) (Ord.Value Nothing) [Ord.Value 1, Ord.Value Nothing].sort Test.specify "should attach warning when trying to sort incomparable values" <| - expect_incomparable_warn Nothing Number.nan <| [Nothing, Number.nan].sort - expect_incomparable_warn 1 "hello" <| [1, "hello"].sort + expect_incomparable_warn Nothing Number.nan <| [Nothing, Number.nan].sort on_incomparable=Problem_Behavior.Report_Warning + expect_incomparable_warn 1 "hello" <| [1, "hello"].sort on_incomparable=Problem_Behavior.Report_Warning Test.specify "should respect previous warnings on a vector" <| Problems.expect_warning "my_warn" <| (Warning.attach "my_warn" [3, 2]) . sort Problems.expect_warning "my_warn" <| (Warning.attach "my_warn" [3, Number.nan]) . sort - expect_incomparable_warn 3 Number.nan <| (Warning.attach "my_warn" [3, Number.nan]) . sort + expect_incomparable_warn 3 Number.nan <| (Warning.attach "my_warn" [3, Number.nan]) . sort on_incomparable=Problem_Behavior.Report_Warning Test.specify "should respect previous warnings on vectors" pending="https://github.com/enso-org/enso/issues/6070" <| Problems.expect_warning "my_warn" <| [3, Warning.attach "my_warn" 2].sort @@ -200,12 +200,12 @@ spec = Test.specify "should produce warning when sorting types with different comparators" <| [Ord.Value 1, 1].sort . should_equal [1, Ord.Value 1] - Problems.expect_warning "Different comparators: [Standard.Base.Data.Ordering.Default_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, 1].sort + Problems.expect_warning "Different comparators: [Standard.Base.Data.Ordering.Default_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, 1].sort on_incomparable=Problem_Behavior.Report_Warning Test.specify "should merge groups of values with custom comparators based on the comparators FQN" <| [Ord.Value 1, My_Type.Value 1].sort . should_equal [My_Type.Value 1, Ord.Value 1] [My_Type.Value 1, Ord.Value 1].sort . should_equal [My_Type.Value 1, Ord.Value 1] - Problems.expect_warning "Different comparators: [Ordering_Spec.My_Type_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, My_Type.Value 1].sort + Problems.expect_warning "Different comparators: [Ordering_Spec.My_Type_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, My_Type.Value 1].sort on_incomparable=Problem_Behavior.Report_Warning Test.specify "should be stable when sorting values with different comparators" <| [Ord.Value 1, 20, My_Type.Value 1, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] diff --git a/test/Tests/src/Data/Vector_Spec.enso b/test/Tests/src/Data/Vector_Spec.enso index 5d945a007a6a..65da4d248471 100644 --- a/test/Tests/src/Data/Vector_Spec.enso +++ b/test/Tests/src/Data/Vector_Spec.enso @@ -571,6 +571,10 @@ type_spec name alter = Test.group name <| small_expected = [T.Value -20 0, T.Value -1 1, T.Value -1 10, T.Value 1 8, T.Value 1 3, T.Value 4 0] small_vec.sort . should_equal small_expected + Test.specify "should fail the sort if Report_Error problem_behavior specified" <| + [T.Value 1 8, Nothing].sort on_incomparable=Problem_Behavior.Report_Error . should_fail_with Incomparable_Values + [Nothing, Number.nan].sort on_incomparable=Problem_Behavior.Report_Error . should_fail_with Incomparable_Values + Test.specify "should be able to use a custom element projection" <| small_vec = alter [T.Value 1 8, T.Value 1 3, T.Value -20 0, T.Value -1 1, T.Value -1 10, T.Value 4 0] small_expected = [T.Value -20 0, T.Value 4 0, T.Value -1 1, T.Value 1 3, T.Value 1 8, T.Value -1 10] From a8091d106c0052548c4e50feec12e4739dc2b2f4 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 18:00:16 +0200 Subject: [PATCH 35/49] SortVectorNode throws only Incomparable_Values. --- .../builtin/ordering/SortVectorNode.java | 129 +++++++++--------- 1 file changed, 61 insertions(+), 68 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index d1a0e5b2048a..84938959a71a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -258,19 +258,14 @@ Object sortGeneric( logger.fine( () -> "Have more groups than 1 and problem_behavior is Report_Error --> throw Dataflow_Error"); - var allComparatorNames = - groups.stream() - .map(Group::comparator) - .map(comp -> comp.getQualifiedName().toString()); + assert groups.get(0).elems.size() > 0 : "Groups should not be empty"; + assert groups.get(1).elems.size() > 0 : "Groups should not be empty"; + var firstIncomparableElem = groups.get(0).elems.get(0); + var secondIncomparableElem = groups.get(1).elems.get(0); var err = ctx.getBuiltins() .error() - .makeUnsupportedArgumentsError( - new Object[] {self}, - "self argument has more than one comparator - All comparators are: " - + allComparatorNames - + ". " - + "See the documentation of Vector.sort method."); + .makeIncomparableValues(firstIncomparableElem, secondIncomparableElem); return DataflowError.withoutTrace(err, this); } else { // Just one comparator, different from Default_Comparator @@ -304,8 +299,7 @@ Object sortGeneric( } @TruffleBoundary(allowInlining = true) - private Object sortPrimitiveVector(Object[] elems, - DefaultSortComparator javaComparator) + private Object sortPrimitiveVector(Object[] elems, DefaultSortComparator javaComparator) throws CompareException { logger.fine(() -> "Sorting primitive elems = " + Arrays.toString(elems)); Arrays.sort(elems, javaComparator); @@ -318,8 +312,8 @@ private Object sortPrimitiveVector(Object[] elems, } } - private List splitByComparators(List elements, List comparators, - List compareFuncs) { + private List splitByComparators( + List elements, List comparators, List compareFuncs) { assert elements.size() == comparators.size(); assert elements.size() == compareFuncs.size(); // Mapping of FQN of comparator to groups @@ -331,10 +325,7 @@ private List splitByComparators(List elements, List compara String qualifiedName = comparator.getQualifiedName().toString(); if (!groupMap.containsKey(qualifiedName)) { - groupMap.put( - qualifiedName, - new Group(new ArrayList<>(), comparator, compareFunc) - ); + groupMap.put(qualifiedName, new Group(new ArrayList<>(), comparator, compareFunc)); } var group = groupMap.get(qualifiedName); group.elems.add(elem); @@ -343,30 +334,29 @@ private List splitByComparators(List elements, List compara // Sort groups by the FQN of their comparator, with the default comparator // being the first one (the first group). String defCompFQN = getDefaultComparatorQualifiedName(); - return groupMap - .entrySet() - .stream() - .sorted((entry1, entry2) -> { - var fqn1 = entry1.getKey(); - var fqn2 = entry2.getKey(); - if (fqn1.equals(defCompFQN)) { - return -1; - } else if (fqn2.equals(defCompFQN)) { - return 1; - } else { - return fqn1.compareTo(fqn2); - } - }) + return groupMap.entrySet().stream() + .sorted( + (entry1, entry2) -> { + var fqn1 = entry1.getKey(); + var fqn2 = entry2.getKey(); + if (fqn1.equals(defCompFQN)) { + return -1; + } else if (fqn2.equals(defCompFQN)) { + return 1; + } else { + return fqn1.compareTo(fqn2); + } + }) .map(Entry::getValue) .collect(Collectors.toList()); } private Object attachWarnings(Object vector, Set warnings) { - var warnArray = warnings - .stream() - .map(Text::create) - .map(text -> Warning.create(EnsoContext.get(this), text, this)) - .toArray(Warning[]::new); + var warnArray = + warnings.stream() + .map(Text::create) + .map(text -> Warning.create(EnsoContext.get(this), text, this)) + .toArray(Warning[]::new); return WithWarnings.appendTo(vector, new ArrayRope<>(warnArray)); } @@ -384,17 +374,21 @@ private Object attachDifferentComparatorsWarning(Object vector, List grou } private String getDefaultComparatorQualifiedName() { - return EnsoContext.get(this).getBuiltins().defaultComparator().getType().getQualifiedName() + return EnsoContext.get(this) + .getBuiltins() + .defaultComparator() + .getType() + .getQualifiedName() .toString(); } - /** - * A group is "primitive" iff its comparator is the default comparator. - */ + /** A group is "primitive" iff its comparator is the default comparator. */ private boolean isPrimitiveGroup(Group group) { - return group.comparator.getQualifiedName().toString().equals( - getDefaultComparatorQualifiedName() - ); + return group + .comparator + .getQualifiedName() + .toString() + .equals(getDefaultComparatorQualifiedName()); } private Object incomparableValuesError(Object left, Object right) { @@ -405,8 +399,8 @@ private Object incomparableValuesError(Object left, Object right) { * Helper slow-path method to conveniently gather elements from interop arrays into a java list */ @SuppressWarnings("unchecked") - private List readInteropArray(InteropLibrary interop, WarningsLibrary warningsLib, - Object vector) { + private List readInteropArray( + InteropLibrary interop, WarningsLibrary warningsLib, Object vector) { try { int size = (int) interop.getArraySize(vector); List res = new ArrayList<>(size); @@ -451,26 +445,23 @@ private int getBuiltinTypeCost(Object builtinType) { private boolean isBuiltinType(Object type) { var builtins = EnsoContext.get(this).getBuiltins(); - return - builtins.number().getNumber() == type || - builtins.number().getDecimal() == type || - builtins.number().getInteger() == type || - builtins.nothing() == type || - builtins.text() == type || - builtins.bool().getType() == type || - builtins.date() == type || - builtins.dateTime() == type || - builtins.duration() == type || - builtins.vector() == type; + return builtins.number().getNumber() == type + || builtins.number().getDecimal() == type + || builtins.number().getInteger() == type + || builtins.nothing() == type + || builtins.text() == type + || builtins.bool().getType() == type + || builtins.date() == type + || builtins.dateTime() == type + || builtins.duration() == type + || builtins.vector() == type; } private boolean isTrue(Object object) { return Boolean.TRUE.equals(object); } - /** - * Returns true iff the given array of comparators is all Default_Comparator - */ + /** Returns true iff the given array of comparators is all Default_Comparator */ boolean areAllDefaultComparators(InteropLibrary interop, Object comparators) { assert interop.hasArrayElements(comparators); var ctx = EnsoContext.get(this); @@ -535,7 +526,7 @@ private record Group(List elems, Type comparator, Function compareFunc) * incomparable values. The warnings are gathered as pure Strings in a hash set, so that they are * not duplicated. */ - private static abstract class SortComparator implements java.util.Comparator { + private abstract static class SortComparator implements java.util.Comparator { private final Set warnings = new HashSet<>(); final AnyToTextNode toTextNode; @@ -694,6 +685,7 @@ private final class GenericSortComparator extends SortComparator { * extracted from the comparator for the appropriate group. */ private final Function compareFunc; + private final Function onFunc; private final boolean hasCustomOnFunc; private final Type comparator; @@ -741,17 +733,17 @@ public int compare(Object x, Object y) { Object yConverted; if (hasCustomOnFunc) { // onFunc cannot have `self` argument, we assume it has just one argument. - xConverted = callNode.executeDispatch(onFunc, null, state, new Object[]{x}); - yConverted = callNode.executeDispatch(onFunc, null, state, new Object[]{y}); + xConverted = callNode.executeDispatch(onFunc, null, state, new Object[] {x}); + yConverted = callNode.executeDispatch(onFunc, null, state, new Object[] {y}); } else { xConverted = x; yConverted = y; } Object[] args; if (hasFunctionSelfArgument(compareFunc)) { - args = new Object[]{comparator, xConverted, yConverted}; + args = new Object[] {comparator, xConverted, yConverted}; } else { - args = new Object[]{xConverted, yConverted}; + args = new Object[] {xConverted, yConverted}; } Object res = callNode.executeDispatch(compareFunc, null, state, args); if (res == less) { @@ -761,7 +753,8 @@ public int compare(Object x, Object y) { } else if (res == greater) { return ascending ? 1 : -1; } else { - // res is either Nothing, or Incomparable_Values. Either way, it means that x and y are incomparable. + // res is either Nothing, or Incomparable_Values. Either way, it means that x and y are + // incomparable. // This case is not supported yet, as it requires topological sorting. // We cannot detect if the result was actually returned from the default comparator (it // could have been transitively called), so we just bailout. @@ -782,8 +775,8 @@ private boolean hasFunctionSelfArgument(Function function) { * dataflow error otherwise. */ private Function checkAndConvertByFunc(Object byFuncObj) { - return checkAndConvertFunction(byFuncObj, - "Unsupported argument for `by`, expected a method with two arguments", 2, 3); + return checkAndConvertFunction( + byFuncObj, "Unsupported argument for `by`, expected a method with two arguments", 2, 3); } /** From d4b487e90840797b78fbdf111a3efd666a52ebc4 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 18:03:02 +0200 Subject: [PATCH 36/49] Delete Collections helper class --- .../interpreter/runtime/util/Collections.java | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100644 engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java deleted file mode 100644 index 0917b46c635e..000000000000 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/util/Collections.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.enso.interpreter.runtime.util; - -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.profiles.BranchProfile; -import java.util.Arrays; - -public class Collections { - - /** PE-friendly implementation of ArrayList. */ - public static final class ArrayListObj { - private Object[] data; - private int size; - - public ArrayListObj(int capacity) { - this.data = new Object[capacity]; - } - - public ArrayListObj() { - this(8); - } - - public void add(T value) { - add(value, BranchProfile.getUncached()); - } - - public void add(T value, BranchProfile capacityExceededProfile) { - if (size == data.length) { - capacityExceededProfile.enter(); - data = Arrays.copyOf(data, size * 2); - } - data[size++] = value; - } - - public void set(int index, T value) { - checkIndex(index); - data[index] = value; - } - - @SuppressWarnings("unchecked") - public T get(int index) { - checkIndex(index); - return (T) data[index]; - } - - @SuppressWarnings("unchecked") - public T remove(int index) { - checkIndex(index); - T result = (T) data[index]; - int lastIdx = size - 1; - int toMoveLen = lastIdx - index; - if (toMoveLen > 0) { - System.arraycopy(data, index + 1, data, index, toMoveLen); - } - data[lastIdx] = null; - size--; - return result; - } - - public int size() { - return size; - } - - public Object[] toArray() { - return Arrays.copyOf(data, size); - } - - public T[] toArray(Class newType) { - return Arrays.copyOf(data, size, newType); - } - - private void checkIndex(int index) { - if (!(0 <= index && index < size)) { - CompilerDirectives.transferToInterpreter(); - throw new IndexOutOfBoundsException(index); - } - } - } -} From b39ea29d5b7f8d4cab83a79cb932fa6042e1df3c Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 18:10:37 +0200 Subject: [PATCH 37/49] Add test for expected failure for custom incomparable values --- test/Tests/src/Data/Ordering_Spec.enso | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index 4d9288255fef..b385f365c29d 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -169,6 +169,9 @@ spec = Test.specify "should produce warnings when sorting primitive values in atoms" pending=topo_sort_pending <| expect_incomparable_warn (Ord.Value 1) (Ord.Value Nothing) [Ord.Value 1, Ord.Value Nothing].sort + Test.specify "should fail to sort custom incomparable values until topological sorting is implemented" <| + [(UPair.Value 1 2), (UPair.Value 3 4)].sort . should_fail_with Incomparable_Values + Test.specify "should attach warning when trying to sort incomparable values" <| expect_incomparable_warn Nothing Number.nan <| [Nothing, Number.nan].sort on_incomparable=Problem_Behavior.Report_Warning expect_incomparable_warn 1 "hello" <| [1, "hello"].sort on_incomparable=Problem_Behavior.Report_Warning From 2a41993e6ad606d4ad4e491233aa7575281c355d Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 18:30:31 +0200 Subject: [PATCH 38/49] Cosmetics. --- .../org/enso/interpreter/test/VectorSortTest.java | 12 +++++------- test/Tests/src/Semantic/Python_Interop_Spec.enso | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java b/engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java index 56c9a428724b..cceda051e360 100644 --- a/engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java +++ b/engine/runtime/src/test/java/org/enso/interpreter/test/VectorSortTest.java @@ -3,10 +3,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.util.Set; -import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode; -import org.enso.interpreter.node.expression.builtin.meta.EqualsNode; -import org.enso.interpreter.node.expression.builtin.ordering.SortVectorNode; +import java.util.ArrayList; +import java.util.List; import org.enso.interpreter.test.ValuesGenerator.Language; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Value; @@ -18,9 +16,9 @@ import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; -import java.util.ArrayList; -import java.util.List; - +/** + * This test ensures that any combination of values can be sorted - no attempt to sort should fail. + */ @RunWith(Theories.class) public class VectorSortTest extends TestBase { private static Context context; diff --git a/test/Tests/src/Semantic/Python_Interop_Spec.enso b/test/Tests/src/Semantic/Python_Interop_Spec.enso index 0fbd49f6b75c..d3ad1b1570c5 100644 --- a/test/Tests/src/Semantic/Python_Interop_Spec.enso +++ b/test/Tests/src/Semantic/Python_Interop_Spec.enso @@ -92,9 +92,9 @@ spec = vec = Vector.from_polyglot_array make_array vec.map .x . should_equal [30, 10, 20] - mapped_vec = vec.map .x - sorted = mapped_vec.sort - mapped_vec . should_equal [30, 10, 20] + arr = vec.map .x . to_array + sorted = arr.sort + arr . should_equal [30, 10, 20] sorted . should_equal [10, 20, 30] arr_2 = make_num_array From 9e0493539b938567e3db7617a69af26622554fc9 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Thu, 13 Apr 2023 21:14:30 +0200 Subject: [PATCH 39/49] Fix test expecting different comparators warning --- test/Tests/src/Data/Ordering_Spec.enso | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index b385f365c29d..8672d48b9060 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -203,12 +203,14 @@ spec = Test.specify "should produce warning when sorting types with different comparators" <| [Ord.Value 1, 1].sort . should_equal [1, Ord.Value 1] - Problems.expect_warning "Different comparators: [Standard.Base.Data.Ordering.Default_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, 1].sort on_incomparable=Problem_Behavior.Report_Warning + sorted = [Ord.Value 1, 1].sort on_incomparable=Problem_Behavior.Report_Warning + Warning.get_all sorted . at 0 . value . starts_with "Different comparators" . should_be_true Test.specify "should merge groups of values with custom comparators based on the comparators FQN" <| [Ord.Value 1, My_Type.Value 1].sort . should_equal [My_Type.Value 1, Ord.Value 1] [My_Type.Value 1, Ord.Value 1].sort . should_equal [My_Type.Value 1, Ord.Value 1] - Problems.expect_warning "Different comparators: [Ordering_Spec.My_Type_Comparator, Ordering_Spec.Ord_Comparator]" <| [Ord.Value 1, My_Type.Value 1].sort on_incomparable=Problem_Behavior.Report_Warning + sorted = [Ord.Value 1, My_Type.Value 1].sort on_incomparable=Problem_Behavior.Report_Warning + Warning.get_all sorted . at 0 . value . starts_with "Different comparators" . should_be_true Test.specify "should be stable when sorting values with different comparators" <| [Ord.Value 1, 20, My_Type.Value 1, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] From cc1f5274345004f8d4518812766cb9e270c68b8f Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 14 Apr 2023 09:30:23 +0200 Subject: [PATCH 40/49] isNothing is checked via interop --- .../builtin/ordering/SortVectorNode.java | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 84938959a71a..8eea59ebac7e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -99,8 +99,8 @@ public abstract Object execute( guards = { "interop.hasArrayElements(self)", "areAllDefaultComparators(interop, comparators)", - "isNothing(byFunc)", - "isNothing(onFunc)" + "interop.isNull(byFunc)", + "interop.isNull(onFunc)" }) Object sortPrimitives( State state, @@ -149,7 +149,7 @@ Object sortPrimitives( } var javaComparator = createDefaultComparator( - lessThanNode, equalsNode, typeOfNode, toTextNode, ascending, problemBehavior); + lessThanNode, equalsNode, typeOfNode, toTextNode, ascending, problemBehavior, interop); try { return sortPrimitiveVector(elems, javaComparator); } catch (CompareException e) { @@ -165,14 +165,16 @@ private DefaultSortComparator createDefaultComparator( TypeOfNode typeOfNode, AnyToTextNode toTextNode, long ascending, - long problemBehaviorNum) { + long problemBehaviorNum, + InteropLibrary interop) { return new DefaultSortComparator( lessThanNode, equalsNode, typeOfNode, toTextNode, ascending > 0, - ProblemBehavior.fromInt((int) problemBehaviorNum)); + ProblemBehavior.fromInt((int) problemBehaviorNum), + interop); } @TruffleBoundary @@ -223,12 +225,18 @@ Object sortGeneric( try { for (var group : groups) { SortComparator javaComparator; - if (isNothing(byFunc) && isNothing(onFunc) && isPrimitiveGroup(group)) { + if (interop.isNull(byFunc) && interop.isNull(onFunc) && isPrimitiveGroup(group)) { javaComparator = new DefaultSortComparator( - lessThanNode, equalsNode, typeOfNode, toTextNode, ascending > 0, problemBehavior); + lessThanNode, + equalsNode, + typeOfNode, + toTextNode, + ascending > 0, + problemBehavior, + interop); } else { - Object compareFunc = isNothing(byFunc) ? group.compareFunc : byFunc; + Object compareFunc = interop.isNull(byFunc) ? group.compareFunc : byFunc; javaComparator = new GenericSortComparator( ascending > 0, @@ -241,7 +249,8 @@ Object sortGeneric( state, less, equal, - greater); + greater, + interop); } logger.fine(() -> "Sorting Group@" + Integer.toHexString(group.hashCode())); group.elems.sort(javaComparator); @@ -488,14 +497,6 @@ private boolean isNan(Object object) { return object instanceof Double dbl && dbl.isNaN(); } - boolean isNothing(Object object) { - return isNothing(object, EnsoContext.get(this)); - } - - private boolean isNothing(Object object, EnsoContext ctx) { - return object == ctx.getBuiltins().nothing(); - } - private enum ProblemBehavior { IGNORE, REPORT_WARNING, @@ -531,10 +532,13 @@ private abstract static class SortComparator implements java.util.Comparator warnings = new HashSet<>(); final AnyToTextNode toTextNode; final ProblemBehavior problemBehavior; + final InteropLibrary interop; - protected SortComparator(AnyToTextNode toTextNode, ProblemBehavior problemBehavior) { + protected SortComparator( + AnyToTextNode toTextNode, ProblemBehavior problemBehavior, InteropLibrary interop) { this.toTextNode = toTextNode; this.problemBehavior = problemBehavior; + this.interop = interop; } @TruffleBoundary @@ -578,8 +582,9 @@ private DefaultSortComparator( TypeOfNode typeOfNode, AnyToTextNode toTextNode, boolean ascending, - ProblemBehavior problemBehavior) { - super(toTextNode, problemBehavior); + ProblemBehavior problemBehavior, + InteropLibrary interop) { + super(toTextNode, problemBehavior, interop); this.lessThanNode = lessThanNode; this.equalsNode = equalsNode; this.typeOfNode = typeOfNode; @@ -597,7 +602,7 @@ int compareValuesWithDefaultComparator(Object x, Object y) { } else { // Check if x < y Object xLessThanYRes = lessThanNode.execute(x, y); - if (isNothing(xLessThanYRes)) { + if (interop.isNull(xLessThanYRes)) { // x and y are incomparable - this can happen if x and y are different types return handleIncomparableValues(x, y); } else if (isTrue(xLessThanYRes)) { @@ -661,7 +666,7 @@ private String getQualifiedTypeName(Object object) { } private int getPrimitiveValueCost(Object object) { - if (isNothing(object)) { + if (interop.isNull(object)) { return 200; } else if (isNan(object)) { return 100; @@ -706,15 +711,16 @@ private GenericSortComparator( State state, Atom less, Atom equal, - Atom greater) { - super(toTextNode, problemBehavior); + Atom greater, + InteropLibrary interop) { + super(toTextNode, problemBehavior, interop); assert compareFunc != null; assert comparator != null; this.comparator = comparator; this.state = state; this.ascending = ascending; this.compareFunc = checkAndConvertByFunc(compareFunc); - if (isNothing(onFunc)) { + if (interop.isNull(onFunc)) { this.hasCustomOnFunc = false; this.onFunc = null; } else { From 715fe9423bd67ac0fffb9cdf236885c1fdb3eca8 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 14 Apr 2023 15:26:18 +0200 Subject: [PATCH 41/49] Remove TruffleLogger from SortVectorNode --- .../builtin/ordering/SortVectorNode.java | 38 +------------------ 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 8eea59ebac7e..0e3e9f578ed2 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -2,7 +2,6 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; @@ -11,7 +10,6 @@ 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 java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -54,8 +52,6 @@ @GenerateUncached public abstract class SortVectorNode extends Node { - private static final TruffleLogger logger = TruffleLogger.getLogger("enso", SortVectorNode.class); - public static SortVectorNode build() { return SortVectorNodeGen.create(); } @@ -116,14 +112,7 @@ Object sortPrimitives( @Cached HostValueToEnsoNode hostValueToEnsoNode, @Cached TypeOfNode typeOfNode, @Cached AnyToTextNode toTextNode, - @Cached BranchProfile warningEncounteredProfile, - @CachedLibrary(limit = "10") InteropLibrary interop, - @CachedLibrary(limit = "5") WarningsLibrary warningsLib) { - logger.fine( - () -> - String.format( - "SortVectorNode.sortPrimitives: byFunc = %s, onFunc = %s, problemBehaviorNum = %s", - byFunc, onFunc, ProblemBehavior.fromInt((int) problemBehavior))); + @CachedLibrary(limit = "10") InteropLibrary interop) { EnsoContext ctx = EnsoContext.get(this); Object[] elems; try { @@ -199,20 +188,11 @@ Object sortGeneric( @Cached AnyToTextNode toTextNode, @Cached(value = "build()", uncached = "build()") CallOptimiserNode callNode) { var problemBehavior = ProblemBehavior.fromInt((int) problemBehaviorNum); - logger.finer( - () -> - String.format( - "SortVectorNode.sortGeneric: onFunc=%s, byFunc=%s, problemBehavior=%s", - onFunc, byFunc, problemBehavior)); // Split into groups List elems = readInteropArray(interop, warningsLib, self); List comparators = readInteropArray(interop, warningsLib, comparatorsArray); List compareFuncs = readInteropArray(interop, warningsLib, compareFuncsArray); List groups = splitByComparators(elems, comparators, compareFuncs); - logger.fine( - () -> - String.format( - "sortGeneric: elems.length=%d, groups.length=%d", elems.size(), groups.size())); // Prepare input for DefaultSortComparator and GenericSortComparator and sort the elements // within groups @@ -252,7 +232,6 @@ Object sortGeneric( greater, interop); } - logger.fine(() -> "Sorting Group@" + Integer.toHexString(group.hashCode())); group.elems.sort(javaComparator); if (javaComparator.hasWarnings()) { gatheredWarnings.addAll(javaComparator.getEncounteredWarnings()); @@ -264,9 +243,6 @@ Object sortGeneric( switch (problemBehavior) { case REPORT_ERROR -> { if (groups.size() > 1) { - logger.fine( - () -> - "Have more groups than 1 and problem_behavior is Report_Error --> throw Dataflow_Error"); assert groups.get(0).elems.size() > 0 : "Groups should not be empty"; assert groups.get(1).elems.size() > 0 : "Groups should not be empty"; var firstIncomparableElem = groups.get(0).elems.get(0); @@ -279,12 +255,6 @@ Object sortGeneric( } else { // Just one comparator, different from Default_Comparator if (!gatheredWarnings.isEmpty()) { - logger.fine( - () -> - String.format( - "Have one comparator different from Default_Comparator and problem_behavior is Report_Error" - + " gatheredWarnings not empty --> throw Dataflow_Error. gatheredWarning.size()=%d", - gatheredWarnings.size())); throw new UnsupportedOperationException("unimplemented"); } else { return sortedVector; @@ -292,7 +262,6 @@ Object sortGeneric( } } case REPORT_WARNING -> { - logger.fine(() -> String.format("Attaching %d warnings", gatheredWarnings.size())); return attachDifferentComparatorsWarning( attachWarnings(sortedVector, gatheredWarnings), groups); } @@ -310,7 +279,6 @@ Object sortGeneric( @TruffleBoundary(allowInlining = true) private Object sortPrimitiveVector(Object[] elems, DefaultSortComparator javaComparator) throws CompareException { - logger.fine(() -> "Sorting primitive elems = " + Arrays.toString(elems)); Arrays.sort(elems, javaComparator); var sortedVector = Vector.fromArray(new Array(elems)); @@ -376,8 +344,6 @@ private Object attachDifferentComparatorsWarning(Object vector, List grou .map(comparator -> comparator.getQualifiedName().toString()) .collect(Collectors.joining(", ")); var text = Text.create("Different comparators: [" + diffCompsMsg + "]"); - logger.finer( - () -> String.format("Will attach different comparators warning '%s'", diffCompsMsg)); var warn = Warning.create(EnsoContext.get(this), text, this); return WithWarnings.appendTo(vector, new ArrayRope<>(warn)); } @@ -829,8 +795,6 @@ private static final class CompareException extends RuntimeException { private CompareException(Object leftOperand, Object rightOperand) { this.leftOperand = leftOperand; this.rightOperand = rightOperand; - logger.finer( - () -> String.format("CompareException(left=%s, right=%s)", leftOperand, rightOperand)); } } } From 8268e02389fbda633907e66080062f8df5f2e522 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Fri, 14 Apr 2023 16:39:34 +0200 Subject: [PATCH 42/49] Small review refactorings --- .../Base/0.0.0-dev/src/Data/Array.enso | 18 ++++++++---- .../Base/0.0.0-dev/src/Data/Numbers.enso | 3 ++ .../Base/0.0.0-dev/src/Data/Vector.enso | 28 +++++++++++-------- .../src/Errors/Problem_Behavior.enso | 8 ++++++ .../builtin/ordering/SortArrayNode.java | 5 +--- test/Tests/src/Data/Ordering_Spec.enso | 10 +++---- 6 files changed, 46 insertions(+), 26 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso index b92f572d49d4..684003f2d83b 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso @@ -161,7 +161,7 @@ type Array Arguments: - order: The order in which the array elements are sorted. - on: A projection from the element type to the value of that element - being sorted on. If set to `Nothing` (the default argument), + being sorted on. If set to `Nothing` (the default), identity function will be used. - by: A function that compares the result of applying `on` to two elements, returning an an `Ordering` if the two elements are comparable @@ -183,6 +183,11 @@ type Array - *Average Time:* `O(n * log n)` - *Worst-Case Space:* `O(n)` additional + ? Incomparable values + Incomparable values are either values with different comparators or with + the same comparator returning `Nothing` from its `compare` method. + See the documentation of the `Ordering` module for more info. + ? Implementation Note The sort implementation is based upon an adaptive, iterative mergesort that requires far fewer than `n * log(n)` comparisons when the array @@ -198,8 +203,11 @@ type Array the comparators in the `self` array, with the exception of the group for the default comparator, which is always the first group. - Additionally, a warning will be attached, explaining that incomparable - values were encountered. + Additionally, an `Incomparable_Values` dataflow error will be returned + if the `on_incomparable` parameter is set to `Problem_Behavior.Report_Error`, + or a warning attached if the `on_incomparable` parameter is set to + `Problem_Behavior.Report_Warning` in case of encountering incomparable + values. It takes equal advantage of ascending and descending runs in the array, making it much simpler to merge two or more sorted arrays: simply @@ -216,8 +224,8 @@ type Array [Pair 1 2, Pair -1 8].to_array.sort Sort_Direction.Descending (_.first) > Example - Sorting an array with elements with different comparators. Values 1 - and My_Type have different comparators. 1 will be sorted before My_Type + Sorting an array with elements with different comparators. Values `1` + and `My_Type` have different comparators. `1` will be sorted before `My_Type` because it has the default comparator, and warning will be attached to the resulting array. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index 9c4fbb77224f..af99337638c8 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -280,6 +280,9 @@ type Number ## Checks equality of numbers, using an `epsilon` value. + ! Error Conditions + If either of the arguments is `Number.nan`, an `Incomparable_Values` error is raised. + Arguments: - that: The number to check equality against. - epsilon: The value by which `self` and `that` can be separated by before diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index ba7917e37d64..6c714aa2419c 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -27,8 +27,8 @@ import project.Warning.Warning import project.IO -# We have to import also conversion methods, therefore, we import all from the Ordering -# module +## We have to import also conversion methods, therefore, we import all from the Ordering + module from project.Data.Ordering import all from project.Data.Boolean import Boolean, True, False from project.Data.Index_Sub_Range import Index_Sub_Range, take_helper, drop_helper @@ -848,7 +848,7 @@ type Vector a Arguments: - order: The order in which the vector elements are sorted. - on: A projection from the element type to the value of that element - being sorted on. If set to `Nothing` (the default argument), + being sorted on. If set to `Nothing` (the default), identity function will be used. - by: A function that compares the result of applying `on` to two elements, returning an an `Ordering` if the two elements are comparable @@ -870,6 +870,11 @@ type Vector a - *Average Time:* `O(n * log n)` - *Worst-Case Space:* `O(n)` additional + ? Incomparable values + Incomparable values are either values with different comparators or with + the same comparator returning `Nothing` from its `compare` method. + See the documentation of the `Ordering` module for more info. + ? Implementation Note The sort implementation is based upon an adaptive, iterative mergesort that requires far fewer than `n * log(n)` comparisons when the vector @@ -885,8 +890,11 @@ type Vector a the comparators in the `self` vector, with the exception of the group for the default comparator, which is always the first group. - Additionally, a warning will be attached, explaining that incomparable - values were encountered. + Additionally, an `Incomparable_Values` dataflow error will be returned + if the `on_incomparable` parameter is set to `Problem_Behavior.Report_Error`, + or a warning attached if the `on_incomparable` parameter is set to + `Problem_Behavior.Report_Warning` in case of encountering incomparable + values. It takes equal advantage of ascending and descending runs in the array, making it much simpler to merge two or more sorted arrays: simply @@ -903,8 +911,8 @@ type Vector a [Pair 1 2, Pair -1 8].sort Sort_Direction.Descending (_.first) > Example - Sorting a vector with elements with different comparators. Values 1 - and My_Type have different comparators. 1 will be sorted before My_Type + Sorting a vector with elements with different comparators. Values `1` + and `My_Type` have different comparators. `1` will be sorted before `My_Type` because it has the default comparator, and warning will be attached to the resulting vector. @@ -915,11 +923,7 @@ type Vector a True -> self.map it-> Comparable.from it False -> self.map it-> Comparable.from (on it) compare_funcs = comps.map (it-> it.compare) - problem_behavior = case on_incomparable of - Problem_Behavior.Ignore -> 0 - Problem_Behavior.Report_Warning -> 1 - Problem_Behavior.Report_Error -> 2 - self.sort_builtin order.to_sign comps compare_funcs by on problem_behavior + self.sort_builtin order.to_sign comps compare_funcs by on on_incomparable.to_number ## UNSTABLE Keeps only unique elements within the vector, removing any duplicates. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Problem_Behavior.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Problem_Behavior.enso index 2b05336ffe73..969d8a78010b 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Problem_Behavior.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Problem_Behavior.enso @@ -164,3 +164,11 @@ type Problem_Behavior warnings = Warning.get_all result . map .value cleared_result = Warning.set result [] self.attach_problems_after cleared_result warnings + + ## PRIVATE + Returns a mapping of Problem_Behavior constructors to an integer. + Used for sending the number to Java, rather than sending the atom. + to_number self = case self of + Ignore -> 0 + Report_Warning -> 1 + Report_Error -> 2 diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java index f4f9c9c02f5c..1ed278790603 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortArrayNode.java @@ -5,10 +5,7 @@ import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.runtime.state.State; -/** - * Just a wrapper for {@code Array.sort_builtin} that delegates to {@code Vector.sort_builtin}. This - * is a temporary workaround until Array and Vector are fully merged into a single type. - */ +/** Just a wrapper for {@code Array.sort_builtin} that delegates to {@code Vector.sort_builtin}. */ @BuiltinMethod(type = "Array", name = "sort_builtin") public final class SortArrayNode extends Node { @Child private SortVectorNode sortVectorNode = SortVectorNode.build(); diff --git a/test/Tests/src/Data/Ordering_Spec.enso b/test/Tests/src/Data/Ordering_Spec.enso index 8672d48b9060..098e44f34cc3 100644 --- a/test/Tests/src/Data/Ordering_Spec.enso +++ b/test/Tests/src/Data/Ordering_Spec.enso @@ -58,8 +58,8 @@ type No_Comp_Type expect_incomparable_warn : Any -> Any -> Any -> Nothing expect_incomparable_warn left_val right_val result = # Incomparable values warning wraps Text values in simple quotes - left_val_text = if Meta.is_a left_val Text then "'" + left_val + "'" else left_val.to_text - right_val_text = if Meta.is_a right_val Text then "'" + right_val + "'" else right_val.to_text + left_val_text = left_val.pretty + right_val_text = right_val.pretty expected_warn_msg_left = "Values " + left_val_text + " and " + right_val_text + " are incomparable" expected_warn_msg_right = "Values " + right_val_text + " and " + left_val_text + " are incomparable" has_expected_warning = Warning.get_all result . map (_.value) . any (it-> it == expected_warn_msg_left || it == expected_warn_msg_right) @@ -72,7 +72,7 @@ expect_no_warns result = # === The Tests === spec = - topo_sort_pending = "Waiting for implementation of topological sort (https://github.com/enso-org/enso/issues/5742)" + topo_sort_pending = "Waiting for implementation of topological sort (https://github.com/enso-org/enso/issues/5834)" Test.group "Default comparator" <| Test.specify "should support custom comparator" <| @@ -220,11 +220,11 @@ spec = [My_Type.Value 1, Ord.Value 1, 20, 10].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] [Ord.Value 1, 20, 10, My_Type.Value 1].sort . should_equal [10, 20, My_Type.Value 1, Ord.Value 1] - Test.specify "should be able to sort even unordered values" pending="https://github.com/enso-org/enso/issues/5834" <| + Test.specify "should be able to sort even unordered values" pending=topo_sort_pending <| [Ord.Value 2, UPair.Value "a" "b", Ord.Value 1, UPair.Value "c" "d"].sort . should_equal [Ord.Value 2, Ord.Value 1, UPair.Value "a" "b", UPair.Value "c" "d"] [Ord.Value 2, UPair.Value "X" "Y", Ord.Value 1, UPair.Value "c" "d"].sort . should_equal [Ord.Value 2, Ord.Value 1, UPair.Value "X" "Y", UPair.Value "c" "d"] - Test.specify "should produce warning when sorting unordered values" pending="https://github.com/enso-org/enso/issues/5834" <| + Test.specify "should produce warning when sorting unordered values" pending=topo_sort_pending <| expect_incomparable_warn (UPair.Value 1 2) (UPair.Value 3 4) [UPair.Value 1 2, UPair.Value 3 4].sort From 254d57982dcab53a1717238a408a40e8855bd087 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 15 Apr 2023 17:34:47 +0200 Subject: [PATCH 43/49] Revert "Remove the remaining comparison operator overrides for numbers." This reverts commit 0df66b10806036594cf51b13df6062ab32680689. --- .../Base/0.0.0-dev/src/Data/Numbers.enso | 97 ++++++++++++++++++- .../number/bigInteger/GreaterNode.java | 42 ++++++++ .../number/bigInteger/GreaterOrEqualNode.java | 42 ++++++++ .../builtin/number/bigInteger/LessNode.java | 42 ++++++++ .../number/bigInteger/LessOrEqualNode.java | 42 ++++++++ .../builtin/number/decimal/GreaterNode.java | 42 ++++++++ .../number/decimal/GreaterOrEqualNode.java | 42 ++++++++ .../builtin/number/decimal/LessNode.java | 42 ++++++++ .../number/decimal/LessOrEqualNode.java | 42 ++++++++ .../number/smallInteger/GreaterNode.java | 41 ++++++++ .../smallInteger/GreaterOrEqualNode.java | 41 ++++++++ .../builtin/number/smallInteger/LessNode.java | 41 ++++++++ .../number/smallInteger/LessOrEqualNode.java | 41 ++++++++ 13 files changed, 596 insertions(+), 1 deletion(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso index af99337638c8..60cb49f039ca 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Numbers.enso @@ -461,6 +461,54 @@ type Decimal ^ : Number -> Number ^ self that = @Builtin_Method "Decimal.^" + ## Checks if this is greater than that. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10 is greater than 7.3. + + 10 > 7.3 + > : Number -> Boolean + > self that = @Builtin_Method "Decimal.>" + + ## Checks if this is greater than or equal to thatthat. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10 is greater than or equal to 7.3. + + 10 >= 7.3 + >= : Number -> Boolean + >= self that = @Builtin_Method "Decimal.>=" + + ## Checks if this is less than that. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10 is less than 7.3. + + 10 < 7.3 + < : Number -> Boolean + < self that = @Builtin_Method "Decimal.<" + + ## Checks if this is less than or equal to thatthat. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10.4 is less than or equal to 7. + + 10.4 <= 7 + <= : Number -> Boolean + <= self that = @Builtin_Method "Decimal.<=" + ## Computes the absolute value of this. The absolute value of a positive number is itself, while the absolute @@ -638,6 +686,54 @@ type Integer ^ : Number -> Number ^ self that = @Builtin_Method "Integer.^" + ## Checks if this is greater than that. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10 is greater than 7. + + 10 > 7 + > : Number -> Boolean + > self that = @Builtin_Method "Integer.>" + + ## Checks if this is greater than or equal to thatthat. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10 is greater than or equal to 7. + + 10 >= 7 + >= : Number -> Boolean + >= self that = @Builtin_Method "Integer.>=" + + ## Checks if this is less than that. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10 is less than 7. + + 10 < 7 + < : Number -> Boolean + < self that = @Builtin_Method "Integer.<" + + ## Checks if this is less than or equal to thatthat. + + Arguments: + - that: The number to compare this against. + + > Example + Checking if 10 is less than or equal to 7. + + 10 <= 7 + <= : Number -> Boolean + <= self that = @Builtin_Method "Integer.<=" + ## Computes the absolute value of this. The absolute value of a positive number is itself, while the absolute @@ -845,7 +941,6 @@ type Integer parse_builtin text radix = @Builtin_Method "Integer.parse" - ## UNSTABLE A syntax error when parsing a double. diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java new file mode 100644 index 000000000000..963c004ec802 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.bigInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Big_Integer", name = ">", description = "Comparison of numbers.") +public abstract class GreaterNode extends Node { + + abstract Object execute(EnsoBigInteger self, Object that); + + static GreaterNode build() { + return GreaterNodeGen.create(); + } + + @Specialization + boolean doDouble(EnsoBigInteger self, double that) { + return BigIntegerOps.toDouble(self.getValue()) > that; + } + + @Specialization + boolean doLong(EnsoBigInteger self, long that) { + return self.getValue().signum() > 0; + } + + @Specialization + boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { + return BigIntegerOps.compare(self.getValue(), that.getValue()) > 0; + } + + @Fallback + DataflowError doOther(EnsoBigInteger self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java new file mode 100644 index 000000000000..c615457d1a15 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.bigInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Big_Integer", name = ">=", description = "Comparison of numbers.") +public abstract class GreaterOrEqualNode extends Node { + + abstract Object execute(EnsoBigInteger self, Object that); + + static GreaterOrEqualNode build() { + return GreaterOrEqualNodeGen.create(); + } + + @Specialization + boolean doDouble(EnsoBigInteger self, double that) { + return BigIntegerOps.toDouble(self.getValue()) >= that; + } + + @Specialization + boolean doLong(EnsoBigInteger self, long that) { + return self.getValue().signum() > 0; + } + + @Specialization + boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { + return BigIntegerOps.compare(self.getValue(), that.getValue()) >= 0; + } + + @Fallback + DataflowError doOther(EnsoBigInteger self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java new file mode 100644 index 000000000000..71e4a55e74ac --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.bigInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Big_Integer", name = "<", description = "Comparison of numbers.") +public abstract class LessNode extends Node { + + abstract Object execute(EnsoBigInteger self, Object that); + + static LessNode build() { + return LessNodeGen.create(); + } + + @Specialization + boolean doDouble(EnsoBigInteger self, double that) { + return BigIntegerOps.toDouble(self.getValue()) < that; + } + + @Specialization + boolean doLong(EnsoBigInteger self, long that) { + return self.getValue().signum() < 0; + } + + @Specialization + boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { + return BigIntegerOps.compare(self.getValue(), that.getValue()) < 0; + } + + @Fallback + DataflowError doOther(EnsoBigInteger self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java new file mode 100644 index 000000000000..5fd408ba79f7 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.bigInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Big_Integer", name = "<=", description = "Comparison of numbers.") +public abstract class LessOrEqualNode extends Node { + + abstract Object execute(EnsoBigInteger self, Object that); + + static LessOrEqualNode build() { + return LessOrEqualNodeGen.create(); + } + + @Specialization + boolean doDouble(EnsoBigInteger self, double that) { + return BigIntegerOps.toDouble(self.getValue()) <= that; + } + + @Specialization + boolean doLong(EnsoBigInteger self, long that) { + return self.getValue().signum() < 0; + } + + @Specialization + boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { + return BigIntegerOps.compare(self.getValue(), that.getValue()) <= 0; + } + + @Fallback + DataflowError doOther(EnsoBigInteger self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java new file mode 100644 index 000000000000..cf1d301edb4d --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.decimal; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Decimal", name = ">", description = "Comparison of numbers.") +public abstract class GreaterNode extends Node { + + abstract Object execute(double self, Object that); + + static GreaterNode build() { + return GreaterNodeGen.create(); + } + + @Specialization + boolean doDouble(double self, double that) { + return self > that; + } + + @Specialization + boolean doLong(double self, long that) { + return self > (double) that; + } + + @Specialization + boolean doBigInteger(double self, EnsoBigInteger that) { + return self > BigIntegerOps.toDouble(that.getValue()); + } + + @Fallback + DataflowError doOther(double self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java new file mode 100644 index 000000000000..00683e4cf399 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.decimal; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Decimal", name = ">=", description = "Comparison of numbers.") +public abstract class GreaterOrEqualNode extends Node { + + abstract Object execute(double self, Object that); + + static GreaterOrEqualNode build() { + return GreaterOrEqualNodeGen.create(); + } + + @Specialization + boolean doDouble(double self, double that) { + return self >= that; + } + + @Specialization + boolean doLong(double self, long that) { + return self >= (double) that; + } + + @Specialization + boolean doBigInteger(double self, EnsoBigInteger that) { + return self >= BigIntegerOps.toDouble(that.getValue()); + } + + @Fallback + DataflowError doOther(double self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java new file mode 100644 index 000000000000..914f28259971 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.decimal; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Decimal", name = "<", description = "Comparison of numbers.") +public abstract class LessNode extends Node { + + abstract Object execute(double self, Object that); + + static LessNode build() { + return LessNodeGen.create(); + } + + @Specialization + boolean doDouble(double self, double that) { + return self < that; + } + + @Specialization + boolean doLong(double self, long that) { + return self < (double) that; + } + + @Specialization + boolean doBigInteger(double self, EnsoBigInteger that) { + return self < BigIntegerOps.toDouble(that.getValue()); + } + + @Fallback + DataflowError doOther(double self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java new file mode 100644 index 000000000000..9e58ffed5818 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java @@ -0,0 +1,42 @@ +package org.enso.interpreter.node.expression.builtin.number.decimal; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Decimal", name = "<=", description = "Comparison of numbers.") +public abstract class LessOrEqualNode extends Node { + + abstract Object execute(double self, Object that); + + static LessOrEqualNode build() { + return LessOrEqualNodeGen.create(); + } + + @Specialization + boolean doDouble(double self, double that) { + return self <= that; + } + + @Specialization + boolean doLong(double self, long that) { + return self <= (double) that; + } + + @Specialization + boolean doBigInteger(double self, EnsoBigInteger that) { + return self <= BigIntegerOps.toDouble(that.getValue()); + } + + @Fallback + DataflowError doOther(double self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java new file mode 100644 index 000000000000..e444e925bac4 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java @@ -0,0 +1,41 @@ +package org.enso.interpreter.node.expression.builtin.number.smallInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Small_Integer", name = ">", description = "Comparison of numbers.") +public abstract class GreaterNode extends Node { + + abstract Object execute(long self, Object that); + + static GreaterNode build() { + return GreaterNodeGen.create(); + } + + @Specialization + boolean doLong(long self, long that) { + return self > that; + } + + @Specialization + boolean doDouble(long self, double that) { + return (double) self > that; + } + + @Specialization + boolean doBigInteger(long self, EnsoBigInteger that) { + return that.getValue().signum() < 0; + } + + @Fallback + DataflowError doOther(long self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java new file mode 100644 index 000000000000..dcb316b7ff6d --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java @@ -0,0 +1,41 @@ +package org.enso.interpreter.node.expression.builtin.number.smallInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Small_Integer", name = ">=", description = "Comparison of numbers.") +public abstract class GreaterOrEqualNode extends Node { + + abstract Object execute(long self, Object that); + + static GreaterOrEqualNode build() { + return GreaterOrEqualNodeGen.create(); + } + + @Specialization + boolean doLong(long self, long that) { + return self >= that; + } + + @Specialization + boolean doDouble(long self, double that) { + return (double) self >= that; + } + + @Specialization + boolean doBigInteger(long self, EnsoBigInteger that) { + return that.getValue().signum() < 0; + } + + @Fallback + DataflowError doOther(long self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java new file mode 100644 index 000000000000..2bf8362b6ee3 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java @@ -0,0 +1,41 @@ +package org.enso.interpreter.node.expression.builtin.number.smallInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Small_Integer", name = "<", description = "Comparison of numbers.") +public abstract class LessNode extends Node { + + abstract Object execute(long self, Object that); + + static LessNode build() { + return LessNodeGen.create(); + } + + @Specialization + boolean doLong(long self, long that) { + return self < that; + } + + @Specialization + boolean doDouble(long self, double that) { + return (double) self < that; + } + + @Specialization + boolean doBigInteger(long self, EnsoBigInteger that) { + return that.getValue().signum() > 0; + } + + @Fallback + DataflowError doOther(long self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java new file mode 100644 index 000000000000..e74a28297f9d --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java @@ -0,0 +1,41 @@ +package org.enso.interpreter.node.expression.builtin.number.smallInteger; + +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.DataflowError; +import org.enso.interpreter.runtime.number.EnsoBigInteger; + +@BuiltinMethod(type = "Small_Integer", name = "<=", description = "Comparison of numbers.") +public abstract class LessOrEqualNode extends Node { + + abstract Object execute(long self, Object that); + + static LessOrEqualNode build() { + return LessOrEqualNodeGen.create(); + } + + @Specialization + boolean doLong(long self, long that) { + return self <= that; + } + + @Specialization + boolean doDouble(long self, double that) { + return (double) self <= that; + } + + @Specialization + boolean doBigInteger(long self, EnsoBigInteger that) { + return that.getValue().signum() > 0; + } + + @Fallback + DataflowError doOther(long self, Object that) { + var builtins = EnsoContext.get(this).getBuiltins(); + var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); + return DataflowError.withoutTrace(typeError, this); + } +} From 8174ca6a6d752abdcf7ee0f74b647a189883e416 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 15 Apr 2023 22:10:17 +0200 Subject: [PATCH 44/49] Improve bench_download.py tool's `--compare` functionality. - Output table is sorted by benchmark labels. - Do not fail when there are different benchmark labels in both runs. --- .../engine-benchmarks/bench_download.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tools/performance/engine-benchmarks/bench_download.py b/tools/performance/engine-benchmarks/bench_download.py index 1794b47ee0dc..8743c296e20a 100755 --- a/tools/performance/engine-benchmarks/bench_download.py +++ b/tools/performance/engine-benchmarks/bench_download.py @@ -588,16 +588,28 @@ def commit_to_str(commit: Commit) -> str: bench_run_2 = _parse_bench_run_from_json(res_2) bench_report_1 = get_bench_report(bench_run_1, cache, tmp_dir) bench_report_2 = get_bench_report(bench_run_2, cache, tmp_dir) - bench_labels: List[str] = list(bench_report_1.label_score_dict.keys()) + # Check that the runs have the same labels, and get their intersection + bench_labels_1 = set(bench_report_1.label_score_dict.keys()) + bench_labels_2 = set(bench_report_2.label_score_dict.keys()) + if bench_labels_1 != bench_labels_2: + logging.warning( + f"Benchmark labels are not the same in both runs. This means that " + f"there will be some missing numbers in one of the runs. " + f"The set difference is {bench_labels_1.difference(bench_labels_2)}") + all_labels: List[str] = sorted( + list(bench_labels_1.intersection(bench_labels_2))) + bench_report_2.label_score_dict.keys() + df = pd.DataFrame(columns=["bench_label", "score-run-1", "score-run-2"]) - for bench_label in bench_labels: + for bench_label in all_labels: df = pd.concat([df, pd.DataFrame([{ "bench_label": bench_label, "score-run-1": bench_report_1.label_score_dict[bench_label], "score-run-2": bench_report_2.label_score_dict[bench_label], }])], ignore_index=True) df["score-diff"] = np.diff(df[["score-run-1", "score-run-2"]], axis=1) - df["score-diff-perc"] = df.apply(lambda row: perc_str(percent_change(row["score-run-1"], row["score-run-2"])), + df["score-diff-perc"] = df.apply(lambda row: perc_str( + percent_change(row["score-run-1"], row["score-run-2"])), axis=1) print("================================") print(df.to_string(index=False, header=True, justify="center", float_format="%.5f")) From 672afb00f5e41f1c2121a760c369269acc2b4009 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 15 Apr 2023 22:38:20 +0200 Subject: [PATCH 45/49] Wrap potential interop values with `HostValueToEnsoNode` --- .../builtin/ordering/SortVectorNode.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java index 0e3e9f578ed2..3a77eb80fa7a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/ordering/SortVectorNode.java @@ -44,9 +44,6 @@ /** * Sorts a vector with elements that have only Default_Comparator, thus, only elements with a * builtin type, which is the most common scenario for sorting. - * - *

TODO: Max number of attached Incomparable values warnings? - hardcode or pass via a new - * parameter to Vector.sort? */ @BuiltinMethod(type = "Vector", name = "sort_builtin", description = "Returns a sorted vector.") @GenerateUncached @@ -94,7 +91,7 @@ public abstract Object execute( @Specialization( guards = { "interop.hasArrayElements(self)", - "areAllDefaultComparators(interop, comparators)", + "areAllDefaultComparators(interop, hostValueToEnsoNode, comparators)", "interop.isNull(byFunc)", "interop.isNull(onFunc)" }) @@ -186,12 +183,15 @@ Object sortGeneric( @Cached EqualsNode equalsNode, @Cached TypeOfNode typeOfNode, @Cached AnyToTextNode toTextNode, + @Cached(value = "build()", uncached = "build()") HostValueToEnsoNode hostValueToEnsoNode, @Cached(value = "build()", uncached = "build()") CallOptimiserNode callNode) { var problemBehavior = ProblemBehavior.fromInt((int) problemBehaviorNum); // Split into groups - List elems = readInteropArray(interop, warningsLib, self); - List comparators = readInteropArray(interop, warningsLib, comparatorsArray); - List compareFuncs = readInteropArray(interop, warningsLib, compareFuncsArray); + List elems = readInteropArray(interop, hostValueToEnsoNode, warningsLib, self); + List comparators = + readInteropArray(interop, hostValueToEnsoNode, warningsLib, comparatorsArray); + List compareFuncs = + readInteropArray(interop, hostValueToEnsoNode, warningsLib, compareFuncsArray); List groups = splitByComparators(elems, comparators, compareFuncs); // Prepare input for DefaultSortComparator and GenericSortComparator and sort the elements @@ -375,12 +375,15 @@ private Object incomparableValuesError(Object left, Object right) { */ @SuppressWarnings("unchecked") private List readInteropArray( - InteropLibrary interop, WarningsLibrary warningsLib, Object vector) { + InteropLibrary interop, + HostValueToEnsoNode hostValueToEnsoNode, + WarningsLibrary warningsLib, + Object vector) { try { int size = (int) interop.getArraySize(vector); List res = new ArrayList<>(size); for (int i = 0; i < size; i++) { - Object elem = interop.readArrayElement(vector, i); + Object elem = hostValueToEnsoNode.execute(interop.readArrayElement(vector, i)); if (warningsLib.hasWarnings(elem)) { elem = warningsLib.removeWarnings(elem); } @@ -437,14 +440,15 @@ private boolean isTrue(Object object) { } /** Returns true iff the given array of comparators is all Default_Comparator */ - boolean areAllDefaultComparators(InteropLibrary interop, Object comparators) { + boolean areAllDefaultComparators( + InteropLibrary interop, HostValueToEnsoNode hostValueToEnsoNode, Object comparators) { assert interop.hasArrayElements(comparators); var ctx = EnsoContext.get(this); try { int compSize = (int) interop.getArraySize(comparators); for (int i = 0; i < compSize; i++) { assert interop.isArrayElementReadable(comparators, i); - Object comparator = interop.readArrayElement(comparators, i); + Object comparator = hostValueToEnsoNode.execute(interop.readArrayElement(comparators, i)); if (!isDefaultComparator(comparator, ctx)) { return false; } From 6b8f147024d91df7377204e6b7c06c5dc9672678 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 15 Apr 2023 22:39:36 +0200 Subject: [PATCH 46/49] Use alter function in Vector_Spec --- test/Tests/src/Data/Vector_Spec.enso | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Tests/src/Data/Vector_Spec.enso b/test/Tests/src/Data/Vector_Spec.enso index 65da4d248471..e5cd7c8c4886 100644 --- a/test/Tests/src/Data/Vector_Spec.enso +++ b/test/Tests/src/Data/Vector_Spec.enso @@ -557,8 +557,8 @@ type_spec name alter = Test.group name <| alter [Time_Of_Day.new 12, Time_Of_Day.new 10 30] . sort . should_equal [Time_Of_Day.new 10 30, Time_Of_Day.new 12] alter [Date_Time.new 2000 12 30 12 30, Date_Time.new 2000 12 30 12 00] . sort . should_equal [Date_Time.new 2000 12 30 12 00, Date_Time.new 2000 12 30 12 30] - ["aa", 2].sort . should_equal [2, "aa"] - [2, Date.new 1999].sort . should_equal [2, Date.new 1999] + alter ["aa", 2] . sort . should_equal [2, "aa"] + alter [2, Date.new 1999] . sort . should_equal [2, Date.new 1999] Test.specify "should leave the original vector unchanged" <| non_empty_vec = alter [2, 4, 2, 3, 2, 3] @@ -572,8 +572,8 @@ type_spec name alter = Test.group name <| small_vec.sort . should_equal small_expected Test.specify "should fail the sort if Report_Error problem_behavior specified" <| - [T.Value 1 8, Nothing].sort on_incomparable=Problem_Behavior.Report_Error . should_fail_with Incomparable_Values - [Nothing, Number.nan].sort on_incomparable=Problem_Behavior.Report_Error . should_fail_with Incomparable_Values + alter [T.Value 1 8, Nothing] . sort on_incomparable=Problem_Behavior.Report_Error . should_fail_with Incomparable_Values + alter [Nothing, Number.nan] . sort on_incomparable=Problem_Behavior.Report_Error . should_fail_with Incomparable_Values Test.specify "should be able to use a custom element projection" <| small_vec = alter [T.Value 1 8, T.Value 1 3, T.Value -20 0, T.Value -1 1, T.Value -1 10, T.Value 4 0] From d9a12a0653da2ffbf6cfb3a9162aa08397fc8694 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 15 Apr 2023 22:39:44 +0200 Subject: [PATCH 47/49] Update docs --- distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso | 3 +-- .../lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso | 2 +- distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso index 684003f2d83b..e5d742c9d493 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso @@ -226,8 +226,7 @@ type Array > Example Sorting an array with elements with different comparators. Values `1` and `My_Type` have different comparators. `1` will be sorted before `My_Type` - because it has the default comparator, and warning will be attached to - the resulting array. + because it has the default comparator. [My_Type.Value 'hello', 1].to_array.sort == [1, My_Type.Value 'hello'].to_array sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Problem_Behavior -> Vector Any ! Incomparable_Values diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso index 06f7bd043c97..d293c85e3084 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Ordering.enso @@ -27,7 +27,7 @@ from project.Data.Boolean import all ``` type Comparator T - compare : T -> T -> (Ordering|Nothing) ! Incomparable_Values + compare : T -> T -> (Ordering|Nothing) hash : T -> Integer ``` diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index 6c714aa2419c..cbc488eb4c4a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -913,8 +913,7 @@ type Vector a > Example Sorting a vector with elements with different comparators. Values `1` and `My_Type` have different comparators. `1` will be sorted before `My_Type` - because it has the default comparator, and warning will be attached to - the resulting vector. + because it has the default comparator. [My_Type.Value 'hello', 1].sort == [1, My_Type.Value 'hello'] sort : Sort_Direction -> (Any -> Any)|Nothing -> (Any -> Any -> (Ordering|Nothing))|Nothing -> Problem_Behavior -> Vector Any ! Incomparable_Values From 1f23d83774ace1bd3aa5cde268c5ed4711555138 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sat, 15 Apr 2023 22:55:19 +0200 Subject: [PATCH 48/49] Invalid comparison throws Incomparable_Values rather than Type_Error --- .../expression/builtin/number/bigInteger/GreaterNode.java | 4 ++-- .../builtin/number/bigInteger/GreaterOrEqualNode.java | 4 ++-- .../node/expression/builtin/number/bigInteger/LessNode.java | 4 ++-- .../expression/builtin/number/bigInteger/LessOrEqualNode.java | 4 ++-- .../node/expression/builtin/number/decimal/GreaterNode.java | 4 ++-- .../expression/builtin/number/decimal/GreaterOrEqualNode.java | 4 ++-- .../node/expression/builtin/number/decimal/LessNode.java | 4 ++-- .../expression/builtin/number/decimal/LessOrEqualNode.java | 4 ++-- .../expression/builtin/number/smallInteger/GreaterNode.java | 4 ++-- .../builtin/number/smallInteger/GreaterOrEqualNode.java | 4 ++-- .../node/expression/builtin/number/smallInteger/LessNode.java | 4 ++-- .../builtin/number/smallInteger/LessOrEqualNode.java | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java index 963c004ec802..46afcca0b24d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { @Fallback DataflowError doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java index c615457d1a15..099ad9eee91e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { @Fallback DataflowError doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java index 71e4a55e74ac..4fd6e79522f2 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { @Fallback DataflowError doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java index 5fd408ba79f7..a3131c803cb2 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { @Fallback DataflowError doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java index cf1d301edb4d..d01ba71977cf 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(double self, EnsoBigInteger that) { @Fallback DataflowError doOther(double self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java index 00683e4cf399..3409b9821f30 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(double self, EnsoBigInteger that) { @Fallback DataflowError doOther(double self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java index 914f28259971..c8526c3cf33f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(double self, EnsoBigInteger that) { @Fallback DataflowError doOther(double self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java index 9e58ffed5818..2fa12b139739 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java @@ -36,7 +36,7 @@ boolean doBigInteger(double self, EnsoBigInteger that) { @Fallback DataflowError doOther(double self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java index e444e925bac4..73d4899f0ff7 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java @@ -35,7 +35,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { @Fallback DataflowError doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java index dcb316b7ff6d..9f32e168d03a 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java @@ -35,7 +35,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { @Fallback DataflowError doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java index 2bf8362b6ee3..cbeef290bc2e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java @@ -35,7 +35,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { @Fallback DataflowError doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java index e74a28297f9d..1cd5c643b3ad 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java @@ -35,7 +35,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { @Fallback DataflowError doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var typeError = builtins.error().makeTypeError(builtins.number().getNumber(), that, "that"); - return DataflowError.withoutTrace(typeError, this); + var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableValsErr, this); } } From ab2f95a45f07baf5545b546aaacdec5485e0e9d2 Mon Sep 17 00:00:00 2001 From: Pavel Marek Date: Sun, 16 Apr 2023 13:13:43 +0200 Subject: [PATCH 49/49] Number comparison builtin methods return Nothing in case of incomparables --- .../number/bigInteger/GreaterNode.java | 2 +- .../number/bigInteger/GreaterOrEqualNode.java | 2 +- .../builtin/number/bigInteger/LessNode.java | 2 +- .../number/bigInteger/LessOrEqualNode.java | 2 +- .../builtin/number/decimal/GreaterNode.java | 35 ++++++++++++++----- .../number/decimal/GreaterOrEqualNode.java | 35 ++++++++++++++----- .../builtin/number/decimal/LessNode.java | 35 ++++++++++++++----- .../number/decimal/LessOrEqualNode.java | 35 ++++++++++++++----- .../number/smallInteger/GreaterNode.java | 2 +- .../smallInteger/GreaterOrEqualNode.java | 2 +- .../builtin/number/smallInteger/LessNode.java | 2 +- .../number/smallInteger/LessOrEqualNode.java | 2 +- 12 files changed, 112 insertions(+), 44 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java index 46afcca0b24d..ea3bb9ab4074 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterNode.java @@ -34,7 +34,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { + Object doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java index 099ad9eee91e..2e7ec44d239e 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/GreaterOrEqualNode.java @@ -34,7 +34,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { + Object doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java index 4fd6e79522f2..1e145ba5b4c4 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessNode.java @@ -34,7 +34,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { + Object doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java index a3131c803cb2..c1c71ec8b352 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/bigInteger/LessOrEqualNode.java @@ -34,7 +34,7 @@ boolean doBigInteger(EnsoBigInteger self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(EnsoBigInteger self, Object that) { + Object doOther(EnsoBigInteger self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java index d01ba71977cf..4be5d441099f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterNode.java @@ -6,6 +6,7 @@ import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.number.EnsoBigInteger; @@ -19,24 +20,40 @@ static GreaterNode build() { } @Specialization - boolean doDouble(double self, double that) { - return self > that; + Object doDouble(double self, double that) { + if (Double.isNaN(self) || Double.isNaN(that)) { + return incomparableError(self, that); + } else { + return self > that; + } } @Specialization - boolean doLong(double self, long that) { - return self > (double) that; + Object doLong(double self, long that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self > (double) that; + } } @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self > BigIntegerOps.toDouble(that.getValue()); + Object doBigInteger(double self, EnsoBigInteger that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self > BigIntegerOps.toDouble(that.getValue()); + } } @Fallback - DataflowError doOther(double self, Object that) { + Object doOther(double self, Object that) { + return incomparableError(self, that); + } + + private DataflowError incomparableError(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); - return DataflowError.withoutTrace(incomparableValsErr, this); + var incomparableErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java index 3409b9821f30..0fac10d57671 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/GreaterOrEqualNode.java @@ -6,6 +6,7 @@ import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.number.EnsoBigInteger; @@ -19,24 +20,40 @@ static GreaterOrEqualNode build() { } @Specialization - boolean doDouble(double self, double that) { - return self >= that; + Object doDouble(double self, double that) { + if (Double.isNaN(self) || Double.isNaN(that)) { + return incomparableError(self, that); + } else { + return self >= that; + } } @Specialization - boolean doLong(double self, long that) { - return self >= (double) that; + Object doLong(double self, long that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self >= (double) that; + } } @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self >= BigIntegerOps.toDouble(that.getValue()); + Object doBigInteger(double self, EnsoBigInteger that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self >= BigIntegerOps.toDouble(that.getValue()); + } } @Fallback - DataflowError doOther(double self, Object that) { + Object doOther(double self, Object that) { + return incomparableError(self, that); + } + + private DataflowError incomparableError(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); - return DataflowError.withoutTrace(incomparableValsErr, this); + var incomparableErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java index c8526c3cf33f..437dc5a854d6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessNode.java @@ -6,6 +6,7 @@ import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.number.EnsoBigInteger; @@ -19,24 +20,40 @@ static LessNode build() { } @Specialization - boolean doDouble(double self, double that) { - return self < that; + Object doDouble(double self, double that) { + if (Double.isNaN(self) || Double.isNaN(that)) { + return incomparableError(self, that); + } else { + return self < that; + } } @Specialization - boolean doLong(double self, long that) { - return self < (double) that; + Object doLong(double self, long that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self < (double) that; + } } @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self < BigIntegerOps.toDouble(that.getValue()); + Object doBigInteger(double self, EnsoBigInteger that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self < BigIntegerOps.toDouble(that.getValue()); + } } @Fallback - DataflowError doOther(double self, Object that) { + Object doOther(double self, Object that) { + return incomparableError(self, that); + } + + private DataflowError incomparableError(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); - return DataflowError.withoutTrace(incomparableValsErr, this); + var incomparableErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java index 2fa12b139739..e52a14a6b306 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/decimal/LessOrEqualNode.java @@ -6,6 +6,7 @@ import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.data.Type; import org.enso.interpreter.runtime.error.DataflowError; import org.enso.interpreter.runtime.number.EnsoBigInteger; @@ -19,24 +20,40 @@ static LessOrEqualNode build() { } @Specialization - boolean doDouble(double self, double that) { - return self <= that; + Object doDouble(double self, double that) { + if (Double.isNaN(self) || Double.isNaN(that)) { + return incomparableError(self, that); + } else { + return self <= that; + } } @Specialization - boolean doLong(double self, long that) { - return self <= (double) that; + Object doLong(double self, long that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self <= (double) that; + } } @Specialization - boolean doBigInteger(double self, EnsoBigInteger that) { - return self <= BigIntegerOps.toDouble(that.getValue()); + Object doBigInteger(double self, EnsoBigInteger that) { + if (Double.isNaN(self)) { + return incomparableError(self, that); + } else { + return self <= BigIntegerOps.toDouble(that.getValue()); + } } @Fallback - DataflowError doOther(double self, Object that) { + Object doOther(double self, Object that) { + return incomparableError(self, that); + } + + private DataflowError incomparableError(Object self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); - var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); - return DataflowError.withoutTrace(incomparableValsErr, this); + var incomparableErr = builtins.error().makeIncomparableValues(self, that); + return DataflowError.withoutTrace(incomparableErr, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java index 73d4899f0ff7..690d7bfc3c21 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterNode.java @@ -33,7 +33,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(long self, Object that) { + Object doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java index 9f32e168d03a..0e54883b476d 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/GreaterOrEqualNode.java @@ -33,7 +33,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(long self, Object that) { + Object doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java index cbeef290bc2e..598251aaa2bf 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessNode.java @@ -33,7 +33,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(long self, Object that) { + Object doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this); diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java index 1cd5c643b3ad..089d31fc0fc9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/number/smallInteger/LessOrEqualNode.java @@ -33,7 +33,7 @@ boolean doBigInteger(long self, EnsoBigInteger that) { } @Fallback - DataflowError doOther(long self, Object that) { + Object doOther(long self, Object that) { var builtins = EnsoContext.get(this).getBuiltins(); var incomparableValsErr = builtins.error().makeIncomparableValues(self, that); return DataflowError.withoutTrace(incomparableValsErr, this);