Skip to content

Commit caec1e6

Browse files
Keep EnsoMultiValue as self when dispatching Any instance methods (#12170)
Fixes #12143.
1 parent fe49064 commit caec1e6

File tree

5 files changed

+60
-25
lines changed

5 files changed

+60
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
parentheses. [This is now a syntax error.][11856]
4646
- [Native libraries of projects can be added to `polyglot/lib` directory][11874]
4747
- [Prefer module methods over `Any` instance ones][12048]
48+
- [Keep intersection type's self when dispatching Any instance methods][12170]
4849
- [Types without constructors can be public][12052]
4950
- Symetric, transitive and reflexive [equality for intersection types][11897]
5051
- [IR definitions are generated by an annotation processor][11770]
@@ -55,6 +56,7 @@
5556
[11856]: https://github.com/enso-org/enso/pull/11856
5657
[11874]: https://github.com/enso-org/enso/pull/11874
5758
[12048]: https://github.com/enso-org/enso/pull/12048
59+
[12170]: https://github.com/enso-org/enso/pull/12170
5860
[12052]: https://github.com/enso-org/enso/pull/12052
5961
[11897]: https://github.com/enso-org/enso/pull/11897
6062
[11770]: https://github.com/enso-org/enso/pull/11770

engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/text/AnyToTextNode.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package org.enso.interpreter.node.expression.builtin.text;
22

33
import com.oracle.truffle.api.CompilerDirectives;
4+
import com.oracle.truffle.api.dsl.Cached;
45
import com.oracle.truffle.api.dsl.Fallback;
56
import com.oracle.truffle.api.dsl.GenerateUncached;
67
import com.oracle.truffle.api.dsl.Specialization;
78
import com.oracle.truffle.api.interop.InteropLibrary;
89
import com.oracle.truffle.api.interop.UnsupportedMessageException;
9-
import com.oracle.truffle.api.library.CachedLibrary;
1010
import com.oracle.truffle.api.nodes.Node;
1111
import org.enso.interpreter.dsl.BuiltinMethod;
12+
import org.enso.interpreter.runtime.EnsoContext;
13+
import org.enso.interpreter.runtime.data.EnsoMultiValue;
1214
import org.enso.interpreter.runtime.data.atom.Atom;
1315
import org.enso.interpreter.runtime.data.atom.AtomConstructor;
1416
import org.enso.interpreter.runtime.data.atom.StructsLibrary;
@@ -33,10 +35,22 @@ Text doAtom(Atom at) {
3335
}
3436
}
3537

38+
@Specialization
39+
Text doMultiValue(
40+
EnsoMultiValue mv,
41+
@Cached EnsoMultiValue.CastToNode castToNode,
42+
@Cached AnyToTextNode toTextNode) {
43+
var ctx = EnsoContext.get(this);
44+
var any = ctx.getBuiltins().any();
45+
var first = castToNode.findTypeOrNull(any, mv, false, false);
46+
assert first != null;
47+
return toTextNode.execute(first);
48+
}
49+
3650
@Fallback
37-
Text doOther(Object object, @CachedLibrary(limit = "5") InteropLibrary interop) {
51+
Text doOther(Object object) {
3852
try {
39-
return Text.create(showObject(object, interop));
53+
return Text.create(showObject(object));
4054
} catch (UnsupportedMessageException e) {
4155
CompilerDirectives.transferToInterpreter();
4256
return Text.create(object.toString());
@@ -55,14 +69,14 @@ private Text doComplexAtom(Atom atom) {
5569
Text res = Text.create("(", consName(atom.getConstructor()));
5670
res = Text.create(res, " ");
5771
try {
58-
res = Text.create(res, showObject(structs.getField(atom, 0), interop));
72+
res = Text.create(res, showObject(structs.getField(atom, 0)));
5973
} catch (UnsupportedMessageException e) {
6074
res = Text.create(res, structs.getField(atom, 0).toString());
6175
}
6276
for (int i = 1; i < atom.getConstructor().getArity(); i++) {
6377
res = Text.create(res, " ");
6478
try {
65-
res = Text.create(res, showObject(structs.getField(atom, i), interop));
79+
res = Text.create(res, showObject(structs.getField(atom, i)));
6680
} catch (UnsupportedMessageException e) {
6781
res = Text.create(res, structs.getField(atom, i).toString());
6882
}
@@ -72,8 +86,7 @@ private Text doComplexAtom(Atom atom) {
7286
}
7387

7488
@CompilerDirectives.TruffleBoundary
75-
private String showObject(Object child, InteropLibrary interop)
76-
throws UnsupportedMessageException {
89+
private String showObject(Object child) throws UnsupportedMessageException {
7790
if (child == null) {
7891
// TODO [RW] This is a temporary workaround to make it possible to display errors related to
7992
// https://www.pivotaltracker.com/story/show/181652974
@@ -82,6 +95,7 @@ private String showObject(Object child, InteropLibrary interop)
8295
} else if (child instanceof Boolean) {
8396
return (boolean) child ? "True" : "False";
8497
} else {
98+
var interop = InteropLibrary.getUncached();
8599
return interop.asString(interop.toDisplayString(child));
86100
}
87101
}

engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoMultiValue.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -595,16 +595,21 @@ public final Object findTypeOrNull(
595595
public final Pair<Function, Type> resolveSymbol(
596596
MethodResolverNode node, UnresolvedSymbol symbol) {
597597
var ctx = EnsoContext.get(node);
598-
Pair<Function, Type> foundAnyMethod = null;
598+
Pair<Function, Type> fallbackToAnyMethod = null;
599599
for (var t : EnsoMultiType.AllTypesWith.getUncached().executeAllTypes(dispatch, null, 0)) {
600600
var fnAndType = node.execute(t, symbol);
601601
if (fnAndType != null) {
602-
if (dispatch.typesLength() == 1 || fnAndType.getRight() != ctx.getBuiltins().any()) {
603-
return Pair.create(fnAndType.getLeft(), t);
602+
if (fnAndType.getRight() != ctx.getBuiltins().any()) {
603+
// if there is a non-Any method available in any of the
604+
// dispach types, then use it!
605+
return fnAndType;
606+
}
607+
if (fallbackToAnyMethod == null) {
608+
// remember a suitable method on Any
609+
fallbackToAnyMethod = fnAndType;
604610
}
605-
foundAnyMethod = fnAndType;
606611
}
607612
}
608-
return foundAnyMethod;
613+
return fallbackToAnyMethod;
609614
}
610615
}

test/Base_Tests/src/Semantic/Multi_Value_As_Type_Refinement_Spec.enso

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ add_specs suite_builder =
4747
ab = make_a_and_b
4848
a2 = id_a ab
4949
a2.is_a A . should_be_true
50-
a2.is_a B . should_be_false
50+
a2.is_a B . should_be_true # B is a hidden type
51+
Test.expect_panic No_Such_Method (a2.b_method)
52+
5153

5254
# Passing a2 to a function expecting B fails because B part was hidden
5355
Test.expect_panic Type_Error (id_b a2)
@@ -61,7 +63,8 @@ add_specs suite_builder =
6163
ab = make_a_and_b
6264
a2 = ab:A
6365
a2.is_a A . should_be_true
64-
a2.is_a B . should_be_false
66+
a2.is_a B . should_be_true # B is hidden type
67+
Test.expect_panic No_Such_Method (a2.b_method)
6568

6669
# Passing a2 to a function expecting B fails because B part was hidden
6770
Test.expect_panic Type_Error (id_b a2)
@@ -75,7 +78,8 @@ add_specs suite_builder =
7578
b2 = id_b ab
7679
a2 = b2:A
7780
a2.is_a A . should_be_true
78-
a2.is_a B . should_be_false
81+
a2.is_a B . should_be_true # B is hidden type
82+
Test.expect_panic No_Such_Method (a2.b_method)
7983

8084
a2.a_method.should_equal "A method"
8185
Test.expect_panic No_Such_Method (a2.b_method)
@@ -84,10 +88,11 @@ add_specs suite_builder =
8488
ab = make_a_and_b
8589
a2 = id_a ab
8690
b2 = a2:B
87-
b2.is_a A . should_be_false
88-
b2.is_a B . should_be_true
8991

92+
b2.is_a B . should_be_true
93+
b2.is_a A . should_be_true # A is hidden type
9094
Test.expect_panic No_Such_Method (b2.a_method)
95+
9196
b2.b_method.should_equal "B method"
9297
# We can still explicitly cast back to A
9398
(b2:A).a_method.should_equal "A method"
@@ -98,13 +103,15 @@ add_specs suite_builder =
98103
ab_as_a : A ->
99104
ab_as_a.a_method . should_equal "A method"
100105
ab_as_a.is_a A . should_be_true
101-
ab_as_a.is_a B . should_be_false
106+
ab_as_a.is_a B . should_be_true # B is hidden type
107+
Test.expect_panic No_Such_Method (ab_as_a.b_method)
102108
_ -> Test.fail "Expected ab to go to `: A` branch"
103109

104110
case ab of
105111
ab_as_b : B ->
106112
ab_as_b.b_method . should_equal "B method"
107-
ab_as_b.is_a A . should_be_false
113+
ab_as_b.is_a A . should_be_true # A is hidden type
114+
Test.expect_panic No_Such_Method (ab_as_b.a_method)
108115
ab_as_b.is_a B . should_be_true
109116
_ -> Test.fail "Expected ab to go to `: B` branch"
110117

@@ -141,12 +148,13 @@ add_specs suite_builder =
141148

142149
# We hide A&B parts by casting to C
143150
c = abc:C
144-
c.is_a A . should_be_false
145-
c.is_a B . should_be_false
146-
c.is_a C . should_be_true
147151

152+
c.is_a A . should_be_true # A is a hidden type
148153
Test.expect_panic No_Such_Method (c.a_method)
154+
c.is_a B . should_be_true # B is a hidden type
149155
Test.expect_panic No_Such_Method (c.b_method)
156+
c.is_a C . should_be_true
157+
150158
c.c_method . should_equal "C method"
151159

152160
# But because the structure was not lost, only hidden, we can cast back to A/B
@@ -269,7 +277,7 @@ add_specs suite_builder =
269277
r.a_method . should_equal "A method"
270278
r.b_method . should_equal "B method"
271279

272-
group_builder.specify "calling `.catch` on an intersection type should not lose even the hidden refinements" pending=dispatch_pending <|
280+
group_builder.specify "calling `.catch` on an intersection type should not lose even the hidden refinements" <|
273281
ab = make_a_and_b
274282
x = ab:A
275283
r = x.catch Any _->"catched"
@@ -283,7 +291,7 @@ add_specs suite_builder =
283291
y.a_method . should_equal "A method"
284292
y.b_method . should_equal "B method"
285293

286-
group_builder.specify "calling `.throw_on_warning` on an intersection type should not lose even the hidden refinements" pending=dispatch_pending <|
294+
group_builder.specify "calling `.throw_on_warning` on an intersection type should not lose even the hidden refinements" <|
287295
ab = make_a_and_b
288296
x = ab:A
289297
r = x.throw_on_warning
@@ -306,7 +314,7 @@ add_specs suite_builder =
306314
y.b_method . should_equal "B method"
307315
Problems.expect_only_warning Illegal_State y
308316

309-
group_builder.specify "removing warnings from an intersection type should not lose even the hidden refinements" pending=dispatch_pending <|
317+
group_builder.specify "removing warnings from an intersection type should not lose even the hidden refinements" <|
310318
ab = make_a_and_b
311319
x1 = Warning.attach (Illegal_State.Error "my warning") (ab:A)
312320
x2 = (Warning.attach (Illegal_State.Error "my warning") ab):A

test/Base_Tests/src/Semantic/Multi_Value_Spec.enso

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ add_specs suite_builder =
3434
c = Complex.new 1.5 0.0
3535
(c:Complex).re . should_equal 1.5
3636
(c:Float) . should_equal 1.5
37+
group_builder.specify "Complex & Float remain after instance method invocation" <|
38+
c = Complex.new 1.5 0.0 . confuse_me
39+
(c:Complex).re . should_equal 1.5
40+
(c:Float) . should_equal 1.5
3741

3842
suite_builder.group "Chain Multi Value" group_builder->
3943
to_b_to_c obj =
@@ -173,6 +177,8 @@ add_specs suite_builder =
173177
Test.expect_panic Type_Error <|
174178
a : C
175179

180+
Any.confuse_me self = self
181+
176182
main filter=Nothing =
177183
suite = Test.build suite_builder->
178184
add_specs suite_builder

0 commit comments

Comments
 (0)