backend visibility error calling java.lang.StringBuilder.length()
StringBuilder.length() is defined by the non-visible AbstractStringBuilder, and normally AbstractStringBuilder.length() would not be visible.
But due to Java's visibility rules, AbstractStringBuilder.length() is visible when accessed via the public StringBuilder which extends AbstractStringBuilder.
Usually the backend cooperates (perhaps by chance?) and casts the receiver to StringBuilder, but I it is possible to trip it up. In the code below, length's defining class (AbstractStringBuilder) is used:
import java.lang { StringBuilder }
shared void run() {
printLength(StringBuilder());
}
void printLength<T>(T t) given T satisfies StringBuilder {
print(t.length());
}
// source/simple/run.ceylon:8: error: Ceylon backend error: AbstractStringBuilder is not public in java.lang; cannot be accessed from outside package
// print(t.length());
// ^
as we can see:
.ceylon.language.print_.print(.ceylon.language.Integer.instance(((.java.lang.AbstractStringBuilder)t).length()));
Sounds like it could be fixed by removal of the unnecessary typecast to AbstractStringBuilder.
Not that I think this issue is all that critical (I imagine it's pretty rare), but... come to think of it, a generalized solution isn't that straight forward.
Given:
-
protected class Awith memberpublic foo() -
public class B extends A -
public class C extends A
In Ceylon, you might have (o of <B|C>).foo(). In the generated Java code, you'd have to have if (o instanceof B) ((B)o).foo() else ((C)o).foo()
So I think it would be:
- If the Ceylon receiver type extends a class or interface that is a) accessible and b) inherits the member-to-invoke's container, use it. Otherwise,
- a union must be involved, so switch over all of the possible public types that inherit the member-to-invoke's container.