Skip to content

Commit 6f89125

Browse files
authored
Merge pull request #6407 from dotty-staging/fix-super-intermediate
Fix #1392: Disallow super-calls that cannot be implemented correctly
2 parents 5a7d668 + 31024d2 commit 6f89125

File tree

5 files changed

+85
-7
lines changed

5 files changed

+85
-7
lines changed

compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala

+31-7
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,39 @@ class SuperAccessors(thisPhase: DenotTransformer) {
115115
sel.sourcePos)
116116
else ctx.log(i"ok super $sel ${sym.showLocated} $member $clazz ${member.isIncompleteIn(clazz)}")
117117
}
118-
else if (mix.name.isEmpty && !(sym.owner is Trait))
119-
// SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract.
120-
for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) {
121-
val overriding = sym.overridingSymbol(intermediateClass)
122-
if ((overriding is (Deferred, butNot = AbsOverride)) && !(overriding.owner is Trait))
123-
ctx.error(
124-
s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract",
118+
else {
119+
val owner = sym.owner
120+
if (!owner.is(Trait)) {
121+
if (mix.name.isEmpty) {
122+
// scala/bug#4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract.
123+
for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) {
124+
val overriding = sym.overridingSymbol(intermediateClass)
125+
if ((overriding is (Deferred, butNot = AbsOverride)) && !(overriding.owner is Trait))
126+
ctx.error(
127+
s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract",
128+
sel.sourcePos)
129+
}
130+
} else {
131+
// scala/scala-dev#143:
132+
// a call `super[T].m` that resolves to `A.m` cannot be translated to correct bytecode if
133+
// `A` is a class (not a trait / interface), but not the direct superclass. Invokespecial
134+
// would select an overriding method in the direct superclass, rather than `A.m`.
135+
// We allow this if there are statically no intervening overrides.
136+
def hasClassOverride(member: Symbol, subCls: ClassSymbol): Boolean = {
137+
if (subCls == defn.ObjectClass || subCls == member.owner) false
138+
else if (member.overridingSymbol(subCls).exists) true
139+
else hasClassOverride(member, subCls.superClass.asClass)
140+
}
141+
val superCls = clazz.asClass.superClass.asClass
142+
if (owner != superCls && hasClassOverride(sym, superCls)) {
143+
ctx.error(
144+
hl"""Super call cannot be emitted: the selected $sym is declared in $owner, which is not the direct superclass of $clazz.
145+
|An unqualified super call (super.${sym.name}) would be allowed.""",
125146
sel.sourcePos)
147+
}
148+
}
126149
}
150+
}
127151
if (name.isTermName && mix.name.isEmpty &&
128152
((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass))
129153
superAccessorCall(sel)(ctx.withPhase(thisPhase.next))

tests/neg/i1392.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class A { def m = 1 }
2+
class B extends A { override def m = 2 }
3+
trait T extends A
4+
class C extends B with T {
5+
override def m = super[T].m // error: Super call cannot be emitted: the selected method m is declared in class A, which is not the direct superclass of class C.
6+
}

tests/run/i1392a.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trait A { def m = 1 }
2+
class B extends A { override def m = 2 }
3+
trait T extends A
4+
class C extends B with T {
5+
def t1 = super[B].m
6+
def t2 = super.m
7+
def t3 = super[T].m
8+
}
9+
object Test {
10+
def main(args: Array[String]): Unit = {
11+
val c = new C
12+
assert(c.t1 == 2)
13+
assert(c.t2 == 2)
14+
assert(c.t3 == 1)
15+
}
16+
}

tests/run/i1392b.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class A { def m = 1 }
2+
class B extends A
3+
trait T extends A { override def m = 2 }
4+
class C extends B with T {
5+
def t1 = super[B].m
6+
def t2 = super.m
7+
def t3 = super[T].m
8+
}
9+
object Test {
10+
def main(args: Array[String]): Unit = {
11+
val c = new C
12+
assert(c.t1 == 1)
13+
assert(c.t2 == 2)
14+
assert(c.t3 == 2)
15+
}
16+
}

tests/run/i1392c.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
trait A { def m = 1 }
2+
class B extends A
3+
trait T extends A { override def m = 2 }
4+
class C extends B with T {
5+
def t1 = super[B].m
6+
def t2 = super.m
7+
def t3 = super[T].m
8+
}
9+
object Test {
10+
def main(args: Array[String]): Unit = {
11+
val c = new C
12+
assert(c.t1 == 1)
13+
assert(c.t2 == 2)
14+
assert(c.t3 == 2)
15+
}
16+
}

0 commit comments

Comments
 (0)