summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala17
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala2
-rw-r--r--test/junit/scala/lang/traits/BytecodeTest.scala27
3 files changed, 39 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index 49d892e04f..a38d21fe10 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -146,7 +146,22 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val intermediateClasses = clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)
intermediateClasses.map(sym.overridingSymbol).find(s => s.isDeferred && !s.isAbstractOverride && !s.owner.isTrait).foreach {
absSym =>
- reporter.error(sel.pos, s"${sym.fullLocationString} cannot be directly accessed from ${clazz} because ${absSym.owner} redeclares it as abstract")
+ reporter.error(sel.pos, s"${sym.fullLocationString} cannot be directly accessed from $clazz because ${absSym.owner} redeclares it as abstract")
+ }
+ } else if (mix != tpnme.EMPTY) {
+ // SD-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.
+ // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial
+ // - A is a java-defined interface and not listed as direct parent of the class. In this
+ // case, `invokespecial A.m` would be invalid.
+ val owner = sym.owner
+ if (!owner.isTrait && owner != clazz.superClass) {
+ reporter.error(sel.pos,
+ s"cannot emit super call: the selected $sym is declared in $owner, which is not the direct superclass of $clazz.\n" +
+ s"An unqualified super call (super.${sym.name}) would be allowed.")
+ } else if (owner.isInterface && owner.isJavaDefined && !clazz.parentSymbols.contains(owner)) {
+ reporter.error(sel.pos, s"unable to emit super call unless interface ${owner.name} (which declares $sym) is directly extended by $clazz.")
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index a95ecd360c..bf275af24f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2040,7 +2040,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
override def matches(sym: Symbol, sym1: Symbol) =
if (sym.isSkolem) matches(sym.deSkolemize, sym1)
else if (sym1.isSkolem) matches(sym, sym1.deSkolemize)
- else super[SubstTypeMap].matches(sym, sym1)
+ else super.matches(sym, sym1)
}
// allow defaults on by-name parameters
if (sym hasFlag BYNAMEPARAM)
diff --git a/test/junit/scala/lang/traits/BytecodeTest.scala b/test/junit/scala/lang/traits/BytecodeTest.scala
index a92d2244a2..c20fae2500 100644
--- a/test/junit/scala/lang/traits/BytecodeTest.scala
+++ b/test/junit/scala/lang/traits/BytecodeTest.scala
@@ -244,7 +244,6 @@ class BytecodeTest extends BytecodeTesting {
@Test
def sd143(): Unit = {
- // this tests the status quo, which is wrong.
val code =
"""class A { def m = 1 }
|class B extends A { override def m = 2 }
@@ -253,10 +252,28 @@ class BytecodeTest extends BytecodeTesting {
| override def m = super[T].m // should invoke A.m
|}
""".stripMargin
- val List(_, _, c, _) = compileClasses(code)
- // even though the bytecode refers to A.m, invokespecial will resolve to B.m
- assertSameCode(getMethod(c, "m"),
- List(VarOp(ALOAD, 0), Invoke(INVOKESPECIAL, "A", "m", "()I", false), Op(IRETURN)))
+
+ val err =
+ """cannot emit super call: the selected method m is declared in class A, which is not the direct superclass of class C.
+ |An unqualified super call (super.m) would be allowed.""".stripMargin
+ val cls = compileClasses(code, allowMessage = _.msg contains err)
+ assert(cls.isEmpty, cls.map(_.name))
+ }
+
+ @Test
+ def sd143b(): Unit = {
+ val jCode = List("interface A { default int m() { return 1; } }" -> "A.java")
+ val code =
+ """class B extends A { override def m = 2 }
+ |trait T extends A
+ |class C extends B with T {
+ | override def m = super[T].m
+ |}
+ """.stripMargin
+
+ val err = "unable to emit super call unless interface A (which declares method m) is directly extended by class C"
+ val cls = compileClasses(code, jCode, allowMessage = _.msg contains err)
+ assert(cls.isEmpty, cls.map(_.name))
}
@Test