Skip to content

Commit 109f3f0

Browse files
committed
Fix check for unimplemented members in a concrete class
Before this commit, this check was done using Type#matchesLoosely, which behaves differently from Denotation#matchesLoosely, in particular the latter requires matching signatures and the former does not, and because bridge generation at Erasure relies on denotation matching, we could end up with unimplemented members at runtime. Fixed by switching to denotation matching which should now be used uniformly everywhere we check for overrides. This commit also improves the error message of this check to list where the abstract member and non-matching concrete members come from, since this can be difficult to figure out in a complex hierarchy. Fixes #10666.
1 parent abbbcef commit 109f3f0

File tree

9 files changed

+50
-19
lines changed

9 files changed

+50
-19
lines changed

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,11 @@ object RefChecks {
541541
|| mbr.is(JavaDefined) && hasJavaErasedOverriding(mbr)
542542

543543
def isImplemented(mbr: Symbol) =
544-
val mbrType = clazz.thisType.memberInfo(mbr)
544+
val mbrDenot = mbr.asSeenFrom(clazz.thisType)
545545
def isConcrete(sym: Symbol) = sym.exists && !sym.isOneOf(NotConcrete)
546546
clazz.nonPrivateMembersNamed(mbr.name)
547547
.filterWithPredicate(
548-
impl => isConcrete(impl.symbol) && mbrType.matchesLoosely(impl.info))
548+
impl => isConcrete(impl.symbol) && mbrDenot.matchesLoosely(impl))
549549
.exists
550550

551551
/** The term symbols in this class and its baseclasses that are
@@ -602,8 +602,10 @@ object RefChecks {
602602
}
603603

604604
for (member <- missing) {
605+
def showDclAndLocation(sym: Symbol) =
606+
s"${sym.showDcl} in ${sym.owner.showLocated}"
605607
def undefined(msg: String) =
606-
abstractClassError(false, s"${member.showDcl} is not defined $msg")
608+
abstractClassError(false, s"${showDclAndLocation(member)} is not defined $msg")
607609
val underlying = member.underlyingSymbol
608610

609611
// Give a specific error message for abstract vars based on why it fails:
@@ -641,13 +643,13 @@ object RefChecks {
641643
val abstractSym = pa.typeSymbol
642644
val concreteSym = pc.typeSymbol
643645
def subclassMsg(c1: Symbol, c2: Symbol) =
644-
s": ${c1.showLocated} is a subclass of ${c2.showLocated}, but method parameter types must match exactly."
646+
s"${c1.showLocated} is a subclass of ${c2.showLocated}, but method parameter types must match exactly."
645647
val addendum =
646648
if (abstractSym == concreteSym)
647649
(pa.typeConstructor, pc.typeConstructor) match {
648650
case (TypeRef(pre1, _), TypeRef(pre2, _)) =>
649-
if (pre1 =:= pre2) ": their type parameters differ"
650-
else ": their prefixes (i.e. enclosing instances) differ"
651+
if (pre1 =:= pre2) "their type parameters differ"
652+
else "their prefixes (i.e. enclosing instances) differ"
651653
case _ =>
652654
""
653655
}
@@ -657,18 +659,22 @@ object RefChecks {
657659
subclassMsg(concreteSym, abstractSym)
658660
else ""
659661

660-
undefined(s"\n(Note that ${pa.show} does not match ${pc.show}$addendum)")
662+
undefined(s"""
663+
|(Note that
664+
| parameter ${pa.show} in ${showDclAndLocation(underlying)} does not match
665+
| parameter ${pc.show} in ${showDclAndLocation(concrete.symbol)}
666+
| $addendum)""".stripMargin)
661667
case xs =>
662668
undefined(
663669
if concrete.symbol.is(AbsOverride) then
664-
s"\n(The class implements ${concrete.showDcl} but that definition still needs an implementation)"
670+
s"\n(The class implements ${showDclAndLocation(concrete.symbol)} but that definition still needs an implementation)"
665671
else
666-
s"\n(The class implements a member with a different type: ${concrete.showDcl})")
672+
s"\n(The class implements a member with a different type: ${showDclAndLocation(concrete.symbol)})")
667673
}
668674
case Nil =>
669675
undefined("")
670676
case concretes =>
671-
undefined(s"\n(The class implements members with different types: ${concretes.map(_.showDcl)}%\n %)")
677+
undefined(s"\n(The class implements members with different types: ${concretes.map(c => showDclAndLocation(c.symbol))}%\n %)")
672678
}
673679
}
674680
else undefined("")

tests/explicit-nulls/neg/override-java-object-arg.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import javax.management.{Notification, NotificationEmitter, NotificationListener
77
class Foo {
88

99
def bar(): Unit = {
10-
val listener = new NotificationListener() { // error: object creation impossible
10+
val listener = new NotificationListener() {
1111
override def handleNotification(n: Notification|Null, emitter: Object): Unit = { // error: method handleNotification overrides nothing
1212
}
1313
}
@@ -17,7 +17,7 @@ class Foo {
1717
}
1818
}
1919

20-
val listener3 = new NotificationListener() { // error: object creation impossible
20+
val listener3 = new NotificationListener() {
2121
override def handleNotification(n: Notification, emitter: Object|Null): Unit = { // error: method handleNotification overrides nothing
2222
}
2323
}

tests/explicit-nulls/neg/override-java-object-arg2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import javax.management.{Notification, NotificationEmitter, NotificationListener
44
class Foo {
55

66
def bar(): Unit = {
7-
val listener4 = new NotificationListener() { // error: duplicate symbol error
7+
val listener4 = new NotificationListener() {
88
def handleNotification(n: Notification|Null, emitter: Object): Unit = { // error
99
}
1010
}

tests/neg/abstract-givens.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/neg/abstract-givens.scala:11:8 -------------------------------------------------------------------------
22
11 | given s[T](using T): Seq[T] with // error
33
| ^
4-
| instance cannot be created, since def iterator: => Iterator[A] is not defined
4+
|instance cannot be created, since def iterator: => Iterator[A] in trait IterableOnce in package scala.collection is not defined
55
-- Error: tests/neg/abstract-givens.scala:8:8 --------------------------------------------------------------------------
66
8 | given y(using Int): String = summon[Int].toString * 22 // error
77
| ^

tests/neg/i10666.check

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Error: tests/neg/i10666.scala:8:6 -----------------------------------------------------------------------------------
2+
8 |class Bar extends Foo { // error
3+
| ^
4+
| class Bar needs to be abstract, since def foo: [T <: B](tx: T): Unit in trait Foo is not defined
5+
| (Note that
6+
| parameter T in def foo: [T <: B](tx: T): Unit in trait Foo does not match
7+
| parameter T in def foo: [T <: A](tx: T): Unit in class Bar
8+
| class B is a subclass of class A, but method parameter types must match exactly.)

tests/neg/i10666.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class A
2+
class B extends A
3+
4+
trait Foo {
5+
def foo[T <: B](tx: T): Unit
6+
}
7+
8+
class Bar extends Foo { // error
9+
def foo[T <: A](tx: T): Unit = {}
10+
}
11+
12+
object Test {
13+
def main(args: Array[String]): Unit = {
14+
val f: Foo = new Bar
15+
f.foo(new B)
16+
}
17+
}

tests/neg/i7597.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
object Test extends App {
22
def foo[S <: String]: String => Int =
3-
new (String => Int) { def apply(s: S): Int = 0 } // error // error
3+
new (String => Int) { def apply(s: S): Int = 0 } // error
44

55
trait Fn[A, B] {
66
def apply(x: A): B
77
}
88

9-
class C[S <: String] extends Fn[String, Int] { // error
9+
class C[S <: String] extends Fn[String, Int] {
1010
def apply(s: S): Int = 0 // error
1111
}
1212

tests/neg/i9329.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
-- Error: tests/neg/i9329.scala:8:6 ------------------------------------------------------------------------------------
22
8 |class GrandSon extends Son // error
33
| ^
4-
| class GrandSon needs to be abstract, since def name: => String is not defined
5-
| (The class implements abstract override def name: => String but that definition still needs an implementation)
4+
|class GrandSon needs to be abstract, since def name: => String in trait Parent is not defined
5+
|(The class implements abstract override def name: => String in trait Son but that definition still needs an implementation)

0 commit comments

Comments
 (0)