Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep EnsoMultiValue as self when dispatching Any instance methods #12170

Merged
merged 13 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
radeusgd marked this conversation as resolved.
Show resolved Hide resolved

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
Loading