Skip to content

Commit 0a84038

Browse files
retronymlrytz
authored andcommitted
SD-143 allow super calls to methods defined in indirect super classes
The restriction for super calls to methods defined in indirect super classes introduced in a980fde was over-restrictive. In particular, it ruled out a valid code pattern to select a method from a superclass when an unqualified `super` would resolve to an override defined in a trait (example in the commit as a test).
1 parent d29df86 commit 0a84038

File tree

2 files changed

+30
-1
lines changed

2 files changed

+30
-1
lines changed

src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala

+7-1
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,17 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
152152
// SD-143: a call super[T].m that resolves to A.m cannot be translated to correct bytecode if
153153
// - A is a class (not a trait / interface), but not the direct superclass. Invokespecial
154154
// would select an overriding method in the direct superclass, rather than A.m.
155+
// We allow this if there are statically no intervening overrides.
155156
// https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial
156157
// - A is a java-defined interface and not listed as direct parent of the class. In this
157158
// case, `invokespecial A.m` would be invalid.
159+
def hasClassOverride(member: Symbol, subclass: Symbol): Boolean = {
160+
if (subclass == ObjectClass || subclass == member.owner) false
161+
else if (member.overridingSymbol(subclass) != NoSymbol) true
162+
else hasClassOverride(member, subclass.superClass)
163+
}
158164
val owner = sym.owner
159-
if (!owner.isTrait && owner != clazz.superClass) {
165+
if (!owner.isTrait && owner != clazz.superClass && hasClassOverride(sym, clazz.superClass)) {
160166
reporter.error(sel.pos,
161167
s"cannot emit super call: the selected $sym is declared in $owner, which is not the direct superclass of $clazz.\n" +
162168
s"An unqualified super call (super.${sym.name}) would be allowed.")

test/junit/scala/lang/traits/BytecodeTest.scala

+23
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,29 @@ class BytecodeTest extends BytecodeTesting {
276276
assert(cls.isEmpty, cls.map(_.name))
277277
}
278278

279+
@Test
280+
def sd143c(): Unit = {
281+
// Allow super calls to class methods of indirect super classes
282+
val code =
283+
"""class A { def f = 1 }
284+
|class B extends A
285+
|trait T extends A { override def f = 2 }
286+
|class C extends B with T {
287+
| def t1 = super[B].f
288+
| def t2 = super.f
289+
| def t3 = super[T].f
290+
|}
291+
""".stripMargin
292+
val List(_, _, c, _) = compileClasses(code)
293+
val t1 = getInstructions(c, "t1")
294+
assert(t1 contains Invoke(INVOKESPECIAL, "A", "f", "()I", false), t1.stringLines)
295+
val t2 = getInstructions(c, "t2")
296+
val invStat = Invoke(INVOKESTATIC, "T", "f$", "(LT;)I", true)
297+
assert(t2 contains invStat, t2.stringLines)
298+
val t3 = getInstructions(c, "t3")
299+
assert(t3 contains invStat, t3.stringLines)
300+
}
301+
279302
@Test
280303
def sd210(): Unit = {
281304
val forwardersCompiler = newCompiler(extraArgs = "-Xmixin-force-forwarders:true")

0 commit comments

Comments
 (0)