Skip to content

Fix #1392: Disallow super-calls that cannot be implemented correctly #6407

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

Merged
merged 1 commit into from
May 1, 2019
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
38 changes: 31 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,39 @@ class SuperAccessors(thisPhase: DenotTransformer) {
sel.sourcePos)
else ctx.log(i"ok super $sel ${sym.showLocated} $member $clazz ${member.isIncompleteIn(clazz)}")
}
else if (mix.name.isEmpty && !(sym.owner is Trait))
// SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract.
for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) {
val overriding = sym.overridingSymbol(intermediateClass)
if ((overriding is (Deferred, butNot = AbsOverride)) && !(overriding.owner is Trait))
ctx.error(
s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract",
else {
val owner = sym.owner
if (!owner.is(Trait)) {
if (mix.name.isEmpty) {
// scala/bug#4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract.
for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) {
val overriding = sym.overridingSymbol(intermediateClass)
if ((overriding is (Deferred, butNot = AbsOverride)) && !(overriding.owner is Trait))
ctx.error(
s"${sym.showLocated} cannot be directly accessed from ${clazz} because ${overriding.owner} redeclares it as abstract",
sel.sourcePos)
}
} else {
// scala/scala-dev#143:
// a call `super[T].m` that resolves to `A.m` cannot be translated to correct bytecode if
// `A` is a class (not a trait / interface), but not the direct superclass. Invokespecial
// would select an overriding method in the direct superclass, rather than `A.m`.
// We allow this if there are statically no intervening overrides.
def hasClassOverride(member: Symbol, subCls: ClassSymbol): Boolean = {
if (subCls == defn.ObjectClass || subCls == member.owner) false
else if (member.overridingSymbol(subCls).exists) true
else hasClassOverride(member, subCls.superClass.asClass)
}
val superCls = clazz.asClass.superClass.asClass
if (owner != superCls && hasClassOverride(sym, superCls)) {
ctx.error(
hl"""Super call cannot be emitted: the selected $sym is declared in $owner, which is not the direct superclass of $clazz.
|An unqualified super call (super.${sym.name}) would be allowed.""",
sel.sourcePos)
}
}
}
}
if (name.isTermName && mix.name.isEmpty &&
((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass))
superAccessorCall(sel)(ctx.withPhase(thisPhase.next))
Expand Down
6 changes: 6 additions & 0 deletions tests/neg/i1392.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class A { def m = 1 }
class B extends A { override def m = 2 }
trait T extends A
class C extends B with T {
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.
}
16 changes: 16 additions & 0 deletions tests/run/i1392a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
trait A { def m = 1 }
class B extends A { override def m = 2 }
trait T extends A
class C extends B with T {
def t1 = super[B].m
def t2 = super.m
def t3 = super[T].m
}
object Test {
def main(args: Array[String]): Unit = {
val c = new C
assert(c.t1 == 2)
assert(c.t2 == 2)
assert(c.t3 == 1)
}
}
16 changes: 16 additions & 0 deletions tests/run/i1392b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class A { def m = 1 }
class B extends A
trait T extends A { override def m = 2 }
class C extends B with T {
def t1 = super[B].m
def t2 = super.m
def t3 = super[T].m
}
object Test {
def main(args: Array[String]): Unit = {
val c = new C
assert(c.t1 == 1)
assert(c.t2 == 2)
assert(c.t3 == 2)
}
}
16 changes: 16 additions & 0 deletions tests/run/i1392c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
trait A { def m = 1 }
class B extends A
trait T extends A { override def m = 2 }
class C extends B with T {
def t1 = super[B].m
def t2 = super.m
def t3 = super[T].m
}
object Test {
def main(args: Array[String]): Unit = {
val c = new C
assert(c.t1 == 1)
assert(c.t2 == 2)
assert(c.t3 == 2)
}
}