Skip to content

Commit ea582a7

Browse files
authored
Merge pull request #2768 from dotty-staging/fix-@2677-2
Fix #2677: Several fixes involving super selection
2 parents 6ade705 + b03cb94 commit ea582a7

File tree

9 files changed

+181
-68
lines changed

9 files changed

+181
-68
lines changed

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,32 +1407,13 @@ object SymDenotations {
14071407
baseData._2
14081408

14091409
def computeBaseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = {
1410-
val seen = new BaseClassSetBuilder
1411-
def addBaseClasses(bcs: List[ClassSymbol], to: List[ClassSymbol])
1412-
: List[ClassSymbol] = bcs match {
1413-
case bc :: bcs1 =>
1414-
val bcs1added = addBaseClasses(bcs1, to)
1415-
if (seen contains bc) bcs1added
1416-
else {
1417-
seen.add(bc)
1418-
bc :: bcs1added
1419-
}
1420-
case nil =>
1421-
to
1422-
}
1423-
def addParentBaseClasses(ps: List[TypeRef], to: List[ClassSymbol]): List[ClassSymbol] = ps match {
1424-
case p :: ps1 =>
1425-
addParentBaseClasses(ps1,
1426-
addBaseClasses(p.symbol.asClass.baseClasses, to))
1427-
case nil =>
1428-
to
1429-
}
14301410
def emptyParentsExpected =
14311411
is(Package) || (symbol == defn.AnyClass) || ctx.erasedTypes && (symbol == defn.ObjectClass)
14321412
if (classParents.isEmpty && !emptyParentsExpected)
14331413
onBehalf.signalProvisional()
1434-
(classSymbol :: addParentBaseClasses(classParents, Nil),
1435-
seen.result)
1414+
val builder = new BaseDataBuilder
1415+
for (p <- classParents) builder.addAll(p.symbol.asClass.baseClasses)
1416+
(classSymbol :: builder.baseClasses, builder.baseClassSet)
14361417
}
14371418

14381419
final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean =
@@ -2096,7 +2077,14 @@ object SymDenotations {
20962077
def contains(sym: Symbol): Boolean = contains(sym, classIds.length)
20972078
}
20982079

2099-
private class BaseClassSetBuilder {
2080+
object BaseClassSet {
2081+
def apply(bcs: List[ClassSymbol]): BaseClassSet =
2082+
new BaseClassSet(bcs.toArray.map(_.id))
2083+
}
2084+
2085+
/** A class to combine base data from parent types */
2086+
class BaseDataBuilder {
2087+
private var classes: List[ClassSymbol] = Nil
21002088
private var classIds = new Array[Int](32)
21012089
private var length = 0
21022090

@@ -2106,19 +2094,32 @@ object SymDenotations {
21062094
classIds = classIds1
21072095
}
21082096

2109-
def contains(sym: Symbol): Boolean =
2110-
new BaseClassSet(classIds).contains(sym, length)
2111-
2112-
def add(sym: Symbol): Unit = {
2097+
private def add(sym: Symbol): Unit = {
21132098
if (length == classIds.length) resize(length * 2)
21142099
classIds(length) = sym.id
21152100
length += 1
21162101
}
21172102

2118-
def result = {
2103+
def addAll(bcs: List[ClassSymbol]): this.type = {
2104+
val len = length
2105+
bcs match {
2106+
case bc :: bcs1 =>
2107+
addAll(bcs1)
2108+
if (!new BaseClassSet(classIds).contains(bc, len)) {
2109+
add(bc)
2110+
classes = bc :: classes
2111+
}
2112+
case nil =>
2113+
}
2114+
this
2115+
}
2116+
2117+
def baseClassSet = {
21192118
if (length != classIds.length) resize(length)
21202119
new BaseClassSet(classIds)
21212120
}
2121+
2122+
def baseClasses: List[ClassSymbol] = classes
21222123
}
21232124

21242125
@sharable private var indent = 0 // for completions printing

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,11 +1143,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
11431143
case OrType(tp11, tp12) =>
11441144
tp11 & tp2 | tp12 & tp2
11451145
case _ =>
1146-
val t1 = mergeIfSub(tp1, tp2)
1147-
if (t1.exists) t1
1146+
val tp1a = dropIfSuper(tp1, tp2)
1147+
if (tp1a ne tp1) glb(tp1a, tp2)
11481148
else {
1149-
val t2 = mergeIfSub(tp2, tp1)
1150-
if (t2.exists) t2
1149+
val tp2a = dropIfSuper(tp2, tp1)
1150+
if (tp2a ne tp2) glb(tp1, tp2a)
11511151
else tp1 match {
11521152
case tp1: ConstantType =>
11531153
tp2 match {
@@ -1204,6 +1204,22 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
12041204
final def lub(tps: List[Type]): Type =
12051205
((defn.NothingType: Type) /: tps)(lub(_,_, canConstrain = false))
12061206

1207+
private def recombineAndOr(tp: AndOrType, tp1: Type, tp2: Type) =
1208+
if (!tp1.exists) tp2
1209+
else if (!tp2.exists) tp1
1210+
else tp.derivedAndOrType(tp1, tp2)
1211+
1212+
/** If some (&-operand of) this type is a supertype of `sub` replace it with `NoType`.
1213+
*/
1214+
private def dropIfSuper(tp: Type, sub: Type): Type =
1215+
if (isSubTypeWhenFrozen(sub, tp)) NoType
1216+
else tp match {
1217+
case tp @ AndType(tp1, tp2) =>
1218+
recombineAndOr(tp, dropIfSuper(tp1, sub), dropIfSuper(tp2, sub))
1219+
case _ =>
1220+
tp
1221+
}
1222+
12071223
/** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2.
12081224
*/
12091225
private def mergeIfSub(tp1: Type, tp2: Type): Type =

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -386,19 +386,15 @@ object Types {
386386

387387
/** The base classes of this type as determined by ClassDenotation
388388
* in linearization order, with the class itself as first element.
389-
* For AndTypes/OrTypes, the union/intersection of the operands' baseclasses.
390-
* Inherited by all type proxies. `Nil` for all other types.
389+
* Inherited by all type proxies. Overridden for And and Or types.
390+
* `Nil` for all other types.
391391
*/
392-
final def baseClasses(implicit ctx: Context): List[ClassSymbol] = track("baseClasses") {
392+
def baseClasses(implicit ctx: Context): List[ClassSymbol] = track("baseClasses") {
393393
this match {
394394
case tp: TypeProxy =>
395395
tp.underlying.baseClasses
396396
case tp: ClassInfo =>
397397
tp.cls.baseClasses
398-
case AndType(tp1, tp2) =>
399-
tp1.baseClasses union tp2.baseClasses
400-
case OrType(tp1, tp2) =>
401-
tp1.baseClasses intersect tp2.baseClasses
402398
case _ => Nil
403399
}
404400
}
@@ -474,22 +470,24 @@ object Types {
474470
*/
475471
final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = {
476472
@tailrec def go(tp: Type): Denotation = tp match {
477-
case tp: RefinedType =>
478-
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
479-
case tp: ThisType =>
480-
goThis(tp)
481-
case tp: TypeRef =>
482-
tp.denot.findMember(name, pre, excluded)
483473
case tp: TermRef =>
484474
go (tp.underlying match {
485475
case mt: MethodType
486476
if mt.paramInfos.isEmpty && (tp.symbol is Stable) => mt.resultType
487477
case tp1 => tp1
488478
})
489-
case tp: TypeParamRef =>
490-
goParam(tp)
479+
case tp: TypeRef =>
480+
tp.denot.findMember(name, pre, excluded)
481+
case tp: ThisType =>
482+
goThis(tp)
483+
case tp: RefinedType =>
484+
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
491485
case tp: RecType =>
492486
goRec(tp)
487+
case tp: TypeParamRef =>
488+
goParam(tp)
489+
case tp: SuperType =>
490+
goSuper(tp)
493491
case tp: HKApply =>
494492
goApply(tp)
495493
case tp: TypeProxy =>
@@ -613,6 +611,12 @@ object Types {
613611
go(next)
614612
}
615613
}
614+
def goSuper(tp: SuperType) = go(tp.underlying) match {
615+
case d: JointRefDenotation =>
616+
typr.println(i"redirecting super.$name from $tp to ${d.symbol.showLocated}")
617+
new UniqueRefDenotation(d.symbol, tp.memberInfo(d.symbol), d.validFor)
618+
case d => d
619+
}
616620
def goAnd(l: Type, r: Type) = {
617621
go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name))
618622
}
@@ -2298,6 +2302,37 @@ object Types {
22982302
def tp2: Type
22992303
def isAnd: Boolean
23002304
def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type // needed?
2305+
2306+
private[this] var myBaseClassesPeriod: Period = Nowhere
2307+
private[this] var myBaseClasses: List[ClassSymbol] = _
2308+
2309+
/** Base classes of And are the merge of the operand base classes
2310+
* For OrTypes, it's the intersection.
2311+
*/
2312+
override final def baseClasses(implicit ctx: Context) = {
2313+
if (myBaseClassesPeriod != ctx.period) {
2314+
val bcs1 = tp1.baseClasses
2315+
val bcs1set = BaseClassSet(bcs1)
2316+
def recur(bcs2: List[ClassSymbol]): List[ClassSymbol] = bcs2 match {
2317+
case bc2 :: bcs2rest =>
2318+
if (isAnd)
2319+
if (bcs1set contains bc2)
2320+
if (bc2.is(Trait)) recur(bcs2rest)
2321+
else bcs1 // common class, therefore rest is the same in both sequences
2322+
else bc2 :: recur(bcs2rest)
2323+
else
2324+
if (bcs1set contains bc2)
2325+
if (bc2.is(Trait)) bc2 :: recur(bcs2rest)
2326+
else bcs2
2327+
else recur(bcs2rest)
2328+
case nil =>
2329+
if (isAnd) bcs1 else bcs2
2330+
}
2331+
myBaseClasses = recur(tp2.baseClasses)
2332+
myBaseClassesPeriod = ctx.period
2333+
}
2334+
myBaseClasses
2335+
}
23012336
}
23022337

23032338
abstract case class AndType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType {

tests/neg/i2677.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
trait A { def x = "foo" }
2+
trait B { def x = 42 }
3+
object Test {
4+
val AB = new A with B { override def x = super.x } // error: wrong override
5+
AB.x
6+
}

tests/neg/overrides.scala

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,27 +79,6 @@ class X3 {
7979
override type T = A1 // error: overrides nothing
8080
}
8181

82-
package p3 {
83-
84-
// Dotty change of rules: Toverrider#f does not
85-
// override TCommon#f, hence the accidental override rule
86-
// applies.
87-
trait TCommon {
88-
def f: String
89-
}
90-
91-
class C1 extends TCommon {
92-
def f = "in C1"
93-
}
94-
95-
trait TOverrider { this: TCommon =>
96-
override def f = "in TOverrider" // The overridden self-type member...
97-
}
98-
99-
class C2 extends C1 with TOverrider // ... fails to override, here. // error: accidental override
100-
101-
}
102-
10382
package p4 {
10483

10584
abstract class C[T] { def head: T }

tests/pos/Orderings.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
object Orderings {
2+
3+
// A type class:
4+
trait Ord[T] { def less(x: T, y: T): Boolean }
5+
6+
implicit val intOrd: Ord[Int] = new {
7+
def less(x: Int, y: Int) = x < y
8+
}
9+
10+
implicit def listOrd[T](implicit ev: Ord[T]): Ord[List[T]] = new {
11+
def less(xs: List[T], ys: List[T]): Boolean =
12+
if ys.isEmpty then false
13+
else if xs.isEmpty then true
14+
else if xs.head == ys.head then less(xs.tail, ys.tail)
15+
else ev.less(xs.head, ys.head)
16+
}
17+
18+
def isLess[T]: T => T => implicit Ord[T] => Boolean =
19+
x => y => implicitly[Ord[T]].less(x, y)
20+
}

tests/pos/override-via-self.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Question: Does TOverrider#f override TCommon#f?
2+
// If not, the accidental override rule applies.
3+
// Dotty used to say no, but with the change to baseClasses in AndTypes says
4+
// yes. Not sure what the right answer is. But in any case we should
5+
// keep the test to notice if there's a difference in behavior.
6+
trait TCommon {
7+
def f: String
8+
}
9+
class C1 extends TCommon {
10+
def f = "in C1"
11+
}
12+
13+
trait TOverrider { this: TCommon =>
14+
override def f = "in TOverrider" // The overridden self-type member...
15+
}
16+
17+
class C2 extends C1 with TOverrider // ... failed to override, here. But now it is OK.
18+

tests/run/supercalls-traits.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
C1A1A2B1B2C2
2+
C1B3B4C3
3+
IT
4+
AT
5+
ER

tests/run/supercalls-traits.scala

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,42 @@ class Base[A](exp: => Option[A])
1414

1515
object Empty extends Base[Nothing](None)
1616

17+
18+
trait B1 extends C1 { override def f() = { super.f(); print("B1") }}
19+
trait B2 extends B1 { override def f() = { super.f(); print("B2") }}
20+
trait A1 extends C1 { override def f() = { super.f(); print("A1") }}
21+
trait A2 extends A1 { override def f() = { super.f(); print("A2") }}
22+
class C1 { def f() = print("C1") }
23+
class C2 extends A2 with B2 { override def f() = { super.f(); print("C2") }}
24+
25+
26+
trait B3 extends C1 { override def f() = { super.f(); print("B3") }}
27+
trait B4 extends C1 { this: B3 => override def f() = { super.f(); print("B4") }}
28+
class C3 extends C1 with B3 with B4 { override def f() = { super.f(); print("C3") }}
29+
30+
trait DT {
31+
def f(): Unit
32+
}
33+
trait IT extends DT {
34+
def f() = { println("IT") }
35+
}
36+
abstract class MPT {
37+
}
38+
trait AT extends MPT with DT {
39+
abstract override def f() = { super.f(); println("AT") }
40+
}
41+
class ER extends MPT with IT with AT {
42+
override def f() = { super.f(); println("ER") }
43+
}
44+
1745
object Test {
1846
def main(args: Array[String]): Unit = {
1947
assert(new C().foo == 3)
48+
new C2().f()
49+
println()
50+
new C3().f()
51+
println()
52+
new ER().f()
2053
Empty
2154
}
2255
}

0 commit comments

Comments
 (0)