Skip to content

Commit 0ed163e

Browse files
smarterlrytz
andcommitted
Hide problematic static forwarders
Static forwarders for bridges lead to ambiguous errors in some Java compilers, and the trait setters aren't meant to be called by users. Since we can't remove them without breaking binary-compatibility, we mark them ACC_SYNTHETIC so that Java compilers will ignore them. See also the discussion in #12767 which implements an alternate fix. Fixes #12753. Co-Authored-By: Lukas Rytz <[email protected]>
1 parent b1d5760 commit 0ed163e

File tree

5 files changed

+113
-3
lines changed

5 files changed

+113
-3
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import dotty.tools.dotc.core.Names.Name
2121
import dotty.tools.dotc.core.NameKinds.ExpandedName
2222
import dotty.tools.dotc.core.Signature
2323
import dotty.tools.dotc.core.StdNames._
24+
import dotty.tools.dotc.core.NameKinds
2425
import dotty.tools.dotc.core.Symbols._
2526
import dotty.tools.dotc.core.Types
2627
import dotty.tools.dotc.core.Types._
@@ -507,7 +508,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
507508
*
508509
* must-single-thread
509510
*/
510-
private def addForwarder(jclass: asm.ClassVisitor, module: Symbol, m: Symbol): Unit = {
511+
private def addForwarder(jclass: asm.ClassVisitor, module: Symbol, m: Symbol, isSynthetic: Boolean): Unit = {
511512
val moduleName = internalName(module)
512513
val methodInfo = module.thisType.memberInfo(m)
513514
val paramJavaTypes: List[BType] = methodInfo.firstParamTypes map toTypeKind
@@ -518,9 +519,10 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
518519
* and we don't know what classes might be subclassing the companion class. See SI-4827.
519520
*/
520521
// TODO: evaluate the other flags we might be dropping on the floor here.
521-
// TODO: ACC_SYNTHETIC ?
522522
val flags = GenBCodeOps.PublicStatic | (
523523
if (m.is(JavaVarargs)) asm.Opcodes.ACC_VARARGS else 0
524+
) | (
525+
if (isSynthetic) asm.Opcodes.ACC_SYNTHETIC else 0
524526
)
525527

526528
// TODO needed? for(ann <- m.annotations) { ann.symbol.initialize }
@@ -595,7 +597,16 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
595597
report.log(s"No forwarder for non-public member $m")
596598
else {
597599
report.log(s"Adding static forwarder for '$m' from $jclassName to '$moduleClass'")
598-
addForwarder(jclass, moduleClass, m)
600+
// It would be simpler to not generate forwarders for these methods,
601+
// but that wouldn't be binary-compatible with Scala 3.0.0, so instead
602+
// we generate ACC_SYNTHETIC forwarders so Java compilers ignore them.
603+
val isSynthetic =
604+
m0.name.is(NameKinds.SyntheticSetterName) ||
605+
// Only hide bridges generated at Erasure, mixin forwarders are also
606+
// marked as bridge but shouldn't be hidden since they don't have a
607+
// non-bridge overload.
608+
m0.is(Bridge) && m0.initial.validFor.firstPhaseId == erasurePhase.next.id
609+
addForwarder(jclass, moduleClass, m, isSynthetic)
599610
}
600611
}
601612
}

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ typeclass-derivation3.scala
3434
varargs-abstract
3535
zero-arity-case-class.scala
3636
i12194.scala
37+
i12753

tests/run/i12753.check

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
1
2+
Dbr
3+
1
4+
1
5+
2
6+
1
7+
1
8+
1
9+
1
10+
2
11+
1
12+
1
13+
synthetic public static C D.foo(int)
14+
public static D D.foo(int)
15+
public static D D.t()
16+
synthetic public static java.lang.Object D.bar()
17+
public static java.lang.String D.bar()
18+
public static int O.a()
19+
public static int O.b()
20+
public static int O.c()
21+
public static int O.d()
22+
public static int O.i()
23+
public static int O.j()
24+
public static int O.k()
25+
public static int O.l()
26+
synthetic public static void O.T$_setter_$a_$eq(int)
27+
public static void O.b_$eq(int)
28+
public static void O.j_$eq(int)

tests/run/i12753/C.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
trait C[This <: C[This]]
2+
3+
trait COps[This <: C[This]] {
4+
def t: This
5+
def foo(x: Int): This = t
6+
def bar: Object = "Cbr"
7+
}
8+
9+
class D extends C[D] {
10+
def x = 1
11+
}
12+
object D extends COps[D] {
13+
def t = new D
14+
override def foo(x: Int): D = super.foo(x)
15+
override def bar: String = "Dbr"
16+
}
17+
18+
trait T {
19+
val a = 1
20+
var b = 1
21+
lazy val c = 1
22+
def d = 1
23+
24+
val i: Int
25+
var j: Int
26+
lazy val k: Int = 1
27+
def l: Int
28+
}
29+
object O extends T {
30+
val i: Int = 1
31+
var j: Int = 1
32+
override lazy val k: Int = 1
33+
def l: Int = 1
34+
}

tests/run/i12753/Test.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
public class Test {
2+
public static void s(Object s) {
3+
System.out.println(s);
4+
}
5+
6+
public static void statics(Class<?> c) {
7+
java.lang.reflect.Method[] ms = c.getDeclaredMethods();
8+
java.util.Arrays.sort(ms, (a, b) -> a.toString().compareTo(b.toString()));
9+
for (java.lang.reflect.Method a : ms) {
10+
if (java.lang.reflect.Modifier.isStatic(a.getModifiers()))
11+
s((a.isSynthetic() ? "synthetic " : "") + a);
12+
}
13+
}
14+
15+
public static void main(String[] args) {
16+
s(D.foo(1).x());
17+
s(D.bar().trim());
18+
19+
s(O.a());
20+
s(O.b());
21+
O.b_$eq(2);
22+
s(O.b());
23+
s(O.c());
24+
s(O.d());
25+
26+
s(O.i());
27+
s(O.j());
28+
O.j_$eq(2);
29+
s(O.j());
30+
s(O.k());
31+
s(O.l());
32+
33+
statics(D.class);
34+
statics(O.class);
35+
}
36+
}

0 commit comments

Comments
 (0)