diff --git a/CHANGELOG.md b/CHANGELOG.md index 1142678cc01e..afacd0805975 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ parentheses. [This is now a syntax error.][11856] - [Native libraries of projects can be added to `polyglot/lib` directory][11874] - [Prefer module methods over `Any` instance ones][12048] +- [Keep intersection type's self when dispatching Any instance methods][12170] - [Types without constructors can be public][12052] - Symetric, transitive and reflexive [equality for intersection types][11897] - [IR definitions are generated by an annotation processor][11770] @@ -55,6 +56,7 @@ [11856]: https://github.com/enso-org/enso/pull/11856 [11874]: https://github.com/enso-org/enso/pull/11874 [12048]: https://github.com/enso-org/enso/pull/12048 +[12170]: https://github.com/enso-org/enso/pull/12170 [12052]: https://github.com/enso-org/enso/pull/12052 [11897]: https://github.com/enso-org/enso/pull/11897 [11770]: https://github.com/enso-org/enso/pull/11770 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 d398f7fe4456..b5ff158f000d 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 @@ -1,14 +1,16 @@ package org.enso.interpreter.node.expression.builtin.text; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.data.EnsoMultiValue; import org.enso.interpreter.runtime.data.atom.Atom; import org.enso.interpreter.runtime.data.atom.AtomConstructor; import org.enso.interpreter.runtime.data.atom.StructsLibrary; @@ -33,10 +35,22 @@ Text doAtom(Atom at) { } } + @Specialization + Text doMultiValue( + EnsoMultiValue mv, + @Cached EnsoMultiValue.CastToNode castToNode, + @Cached AnyToTextNode toTextNode) { + var ctx = EnsoContext.get(this); + var any = ctx.getBuiltins().any(); + var first = castToNode.findTypeOrNull(any, mv, false, false); + assert first != null; + return toTextNode.execute(first); + } + @Fallback - Text doOther(Object object, @CachedLibrary(limit = "5") InteropLibrary interop) { + Text doOther(Object object) { try { - return Text.create(showObject(object, interop)); + return Text.create(showObject(object)); } catch (UnsupportedMessageException e) { CompilerDirectives.transferToInterpreter(); return Text.create(object.toString()); @@ -55,14 +69,14 @@ private Text doComplexAtom(Atom atom) { Text res = Text.create("(", consName(atom.getConstructor())); res = Text.create(res, " "); try { - res = Text.create(res, showObject(structs.getField(atom, 0), interop)); + res = Text.create(res, showObject(structs.getField(atom, 0))); } catch (UnsupportedMessageException e) { res = Text.create(res, structs.getField(atom, 0).toString()); } for (int i = 1; i < atom.getConstructor().getArity(); i++) { res = Text.create(res, " "); try { - res = Text.create(res, showObject(structs.getField(atom, i), interop)); + res = Text.create(res, showObject(structs.getField(atom, i))); } catch (UnsupportedMessageException e) { res = Text.create(res, structs.getField(atom, i).toString()); } @@ -72,8 +86,7 @@ private Text doComplexAtom(Atom atom) { } @CompilerDirectives.TruffleBoundary - private String showObject(Object child, InteropLibrary interop) - throws UnsupportedMessageException { + private String showObject(Object child) 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 @@ -82,6 +95,7 @@ private String showObject(Object child, InteropLibrary interop) } else if (child instanceof Boolean) { return (boolean) child ? "True" : "False"; } else { + var interop = InteropLibrary.getUncached(); return interop.asString(interop.toDisplayString(child)); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java index 06257b80987e..861e32d26a6c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java @@ -595,16 +595,21 @@ public final Object findTypeOrNull( public final Pair resolveSymbol( MethodResolverNode node, UnresolvedSymbol symbol) { var ctx = EnsoContext.get(node); - Pair foundAnyMethod = null; + Pair fallbackToAnyMethod = null; for (var t : EnsoMultiType.AllTypesWith.getUncached().executeAllTypes(dispatch, null, 0)) { var fnAndType = node.execute(t, symbol); if (fnAndType != null) { - if (dispatch.typesLength() == 1 || fnAndType.getRight() != ctx.getBuiltins().any()) { - return Pair.create(fnAndType.getLeft(), t); + if (fnAndType.getRight() != ctx.getBuiltins().any()) { + // if there is a non-Any method available in any of the + // dispach types, then use it! + return fnAndType; + } + if (fallbackToAnyMethod == null) { + // remember a suitable method on Any + fallbackToAnyMethod = fnAndType; } - foundAnyMethod = fnAndType; } } - return foundAnyMethod; + return fallbackToAnyMethod; } } diff --git a/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso b/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso index 37bc78622039..317503d59974 100644 --- a/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso +++ b/test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso @@ -47,7 +47,9 @@ add_specs suite_builder = ab = make_a_and_b a2 = id_a ab a2.is_a A . should_be_true - a2.is_a B . should_be_false + a2.is_a B . should_be_true # B is a hidden type + Test.expect_panic No_Such_Method (a2.b_method) + # Passing a2 to a function expecting B fails because B part was hidden Test.expect_panic Type_Error (id_b a2) @@ -61,7 +63,8 @@ add_specs suite_builder = ab = make_a_and_b a2 = ab:A a2.is_a A . should_be_true - a2.is_a B . should_be_false + a2.is_a B . should_be_true # B is hidden type + Test.expect_panic No_Such_Method (a2.b_method) # Passing a2 to a function expecting B fails because B part was hidden Test.expect_panic Type_Error (id_b a2) @@ -75,7 +78,8 @@ add_specs suite_builder = b2 = id_b ab a2 = b2:A a2.is_a A . should_be_true - a2.is_a B . should_be_false + a2.is_a B . should_be_true # B is hidden type + Test.expect_panic No_Such_Method (a2.b_method) a2.a_method.should_equal "A method" Test.expect_panic No_Such_Method (a2.b_method) @@ -84,10 +88,11 @@ add_specs suite_builder = ab = make_a_and_b a2 = id_a ab b2 = a2:B - b2.is_a A . should_be_false - b2.is_a B . should_be_true + b2.is_a B . should_be_true + b2.is_a A . should_be_true # A is hidden type Test.expect_panic No_Such_Method (b2.a_method) + b2.b_method.should_equal "B method" # We can still explicitly cast back to A (b2:A).a_method.should_equal "A method" @@ -98,13 +103,15 @@ add_specs suite_builder = ab_as_a : A -> ab_as_a.a_method . should_equal "A method" ab_as_a.is_a A . should_be_true - ab_as_a.is_a B . should_be_false + ab_as_a.is_a B . should_be_true # B is hidden type + Test.expect_panic No_Such_Method (ab_as_a.b_method) _ -> Test.fail "Expected ab to go to `: A` branch" case ab of ab_as_b : B -> ab_as_b.b_method . should_equal "B method" - ab_as_b.is_a A . should_be_false + ab_as_b.is_a A . should_be_true # A is hidden type + Test.expect_panic No_Such_Method (ab_as_b.a_method) ab_as_b.is_a B . should_be_true _ -> Test.fail "Expected ab to go to `: B` branch" @@ -141,12 +148,13 @@ add_specs suite_builder = # We hide A&B parts by casting to C c = abc:C - c.is_a A . should_be_false - c.is_a B . should_be_false - c.is_a C . should_be_true + c.is_a A . should_be_true # A is a hidden type Test.expect_panic No_Such_Method (c.a_method) + c.is_a B . should_be_true # B is a hidden type Test.expect_panic No_Such_Method (c.b_method) + c.is_a C . should_be_true + c.c_method . should_equal "C method" # But because the structure was not lost, only hidden, we can cast back to A/B @@ -269,7 +277,7 @@ add_specs suite_builder = r.a_method . should_equal "A method" r.b_method . should_equal "B method" - group_builder.specify "calling `.catch` on an intersection type should not lose even the hidden refinements" pending=dispatch_pending <| + group_builder.specify "calling `.catch` on an intersection type should not lose even the hidden refinements" <| ab = make_a_and_b x = ab:A r = x.catch Any _->"catched" @@ -283,7 +291,7 @@ add_specs suite_builder = y.a_method . should_equal "A method" y.b_method . should_equal "B method" - group_builder.specify "calling `.throw_on_warning` on an intersection type should not lose even the hidden refinements" pending=dispatch_pending <| + group_builder.specify "calling `.throw_on_warning` on an intersection type should not lose even the hidden refinements" <| ab = make_a_and_b x = ab:A r = x.throw_on_warning @@ -306,7 +314,7 @@ add_specs suite_builder = y.b_method . should_equal "B method" Problems.expect_only_warning Illegal_State y - group_builder.specify "removing warnings from an intersection type should not lose even the hidden refinements" pending=dispatch_pending <| + group_builder.specify "removing warnings from an intersection type should not lose even the hidden refinements" <| ab = make_a_and_b x1 = Warning.attach (Illegal_State.Error "my warning") (ab:A) x2 = (Warning.attach (Illegal_State.Error "my warning") ab):A diff --git a/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso b/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso index b78a1be127f6..28df4479ea8e 100644 --- a/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso +++ b/test/Base_Tests/src/Semantic/Multi_Value_Spec.enso @@ -34,6 +34,10 @@ add_specs suite_builder = c = Complex.new 1.5 0.0 (c:Complex).re . should_equal 1.5 (c:Float) . should_equal 1.5 + group_builder.specify "Complex & Float remain after instance method invocation" <| + c = Complex.new 1.5 0.0 . confuse_me + (c:Complex).re . should_equal 1.5 + (c:Float) . should_equal 1.5 suite_builder.group "Chain Multi Value" group_builder-> to_b_to_c obj = @@ -173,6 +177,8 @@ add_specs suite_builder = Test.expect_panic Type_Error <| a : C +Any.confuse_me self = self + main filter=Nothing = suite = Test.build suite_builder-> add_specs suite_builder