Skip to content

Commit

Permalink
Keep EnsoMultiValue as self when dispatching Any instance metho…
Browse files Browse the repository at this point in the history
…ds (#12170)

Fixes #12143.
  • Loading branch information
JaroslavTulach authored Jan 29, 2025
1 parent fe49064 commit caec1e6
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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());
Expand All @@ -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());
}
Expand All @@ -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
Expand All @@ -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));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -595,16 +595,21 @@ public final Object findTypeOrNull(
public final Pair<Function, Type> resolveSymbol(
MethodResolverNode node, UnresolvedSymbol symbol) {
var ctx = EnsoContext.get(node);
Pair<Function, Type> foundAnyMethod = null;
Pair<Function, Type> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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"
Expand All @@ -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"

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions test/Base_Tests/src/Semantic/Multi_Value_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit caec1e6

Please sign in to comment.