Skip to content

Commit e147f22

Browse files
smarterodersky
authored andcommitted
Fix #5433: use a valid member to implement a super-accessor
Relying on `matchingDenotation` is not enough as demonstrated by i5433.scala: in `Fail`, `B#foo` matches `C$$super$foo` but it cannot implement it since `X` is a supertype of `Y` Note that scalac seems to have the same bug (but at least in Dotty this is detected by -Ycheck:all). For reference, here's what the spec says on resolving super accessors: A reference super.m refers statically to a method or type m in the least proper supertype of the innermost template containing the reference. It evaluates to the member m′ in the actual supertype of that template which is equal to m or which overrides m. This seems like it should be clarified to indicate that the member m' may be found not only in "the actual supertype" (defined elsewhere) but in any supertype of the actual supertype.
1 parent 845fd59 commit e147f22

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,13 @@ object ResolveSuper {
103103
val other = bcs.head.info.nonPrivateDecl(memberName)
104104
if (ctx.settings.Ydebug.value)
105105
ctx.log(i"rebindsuper ${bcs.head} $other deferred = ${other.symbol.is(Deferred)}")
106-
sym = other.matchingDenotation(base.thisType, base.thisType.memberInfo(acc)).symbol
106+
val otherMember = other.matchingDenotation(base.thisType, base.thisType.memberInfo(acc))
107+
108+
// Having a matching denotation is not enough: it should also be a subtype
109+
// of the superaccessor's type, see i5433.scala for an example where this matters
110+
if (otherMember.asSeenFrom(base.typeRef).info <:< acc.asSeenFrom(base.typeRef).info)
111+
sym = otherMember.symbol
112+
107113
bcs = bcs.tail
108114
}
109115
assert(sym.exists)

tests/run/i5433.scala

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class X
2+
class Y extends X
3+
4+
trait A[+T] {
5+
def foo: T = null.asInstanceOf[T]
6+
}
7+
8+
trait B extends A[X] {
9+
override def foo: X = new X
10+
}
11+
12+
trait C extends A[Y] {
13+
override def foo: Y = new Y
14+
def superFoo: Y = super.foo // C will have an abstract `def C$$super$foo: Y` because of this call
15+
}
16+
17+
class Fail extends B with C
18+
// Should generate `def C$$super$foo: Y = super[A].foo` and not `= super[B].foo`
19+
20+
object Test {
21+
def main(args: Array[String]): Unit = {
22+
val y: Y = (new Fail).superFoo // Used to fail with a ClassCastException because of `Fail#C$$super$foo` being incorrect above
23+
assert(y == null)
24+
}
25+
}

0 commit comments

Comments
 (0)