diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 154f6ad90c41..bb0d353ce29f 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1407,32 +1407,13 @@ object SymDenotations { baseData._2 def computeBaseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = { - val seen = new BaseClassSetBuilder - def addBaseClasses(bcs: List[ClassSymbol], to: List[ClassSymbol]) - : List[ClassSymbol] = bcs match { - case bc :: bcs1 => - val bcs1added = addBaseClasses(bcs1, to) - if (seen contains bc) bcs1added - else { - seen.add(bc) - bc :: bcs1added - } - case nil => - to - } - def addParentBaseClasses(ps: List[TypeRef], to: List[ClassSymbol]): List[ClassSymbol] = ps match { - case p :: ps1 => - addParentBaseClasses(ps1, - addBaseClasses(p.symbol.asClass.baseClasses, to)) - case nil => - to - } def emptyParentsExpected = is(Package) || (symbol == defn.AnyClass) || ctx.erasedTypes && (symbol == defn.ObjectClass) if (classParents.isEmpty && !emptyParentsExpected) onBehalf.signalProvisional() - (classSymbol :: addParentBaseClasses(classParents, Nil), - seen.result) + val builder = new BaseDataBuilder + for (p <- classParents) builder.addAll(p.symbol.asClass.baseClasses) + (classSymbol :: builder.baseClasses, builder.baseClassSet) } final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean = @@ -2096,7 +2077,9 @@ object SymDenotations { def contains(sym: Symbol): Boolean = contains(sym, classIds.length) } - private class BaseClassSetBuilder { + /** A class to combine base data from parent types */ + class BaseDataBuilder { + private var classes: List[ClassSymbol] = Nil private var classIds = new Array[Int](32) private var length = 0 @@ -2106,19 +2089,34 @@ object SymDenotations { classIds = classIds1 } - def contains(sym: Symbol): Boolean = + private def contains(sym: Symbol): Boolean = new BaseClassSet(classIds).contains(sym, length) - def add(sym: Symbol): Unit = { + private def add(sym: Symbol): Unit = { if (length == classIds.length) resize(length * 2) classIds(length) = sym.id length += 1 } - def result = { + def addAll(bcs: List[ClassSymbol]): this.type = { + bcs match { + case bc :: bcs1 => + addAll(bcs1) + if (!contains(bc)) { + add(bc) + classes = bc :: classes + } + case nil => + } + this + } + + def baseClassSet = { if (length != classIds.length) resize(length) new BaseClassSet(classIds) } + + def baseClasses: List[ClassSymbol] = classes } @sharable private var indent = 0 // for completions printing diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 6d66cacdd067..319fe69097a1 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -384,7 +384,7 @@ object Types { /** The base classes of this type as determined by ClassDenotation * in linearization order, with the class itself as first element. - * For AndTypes/OrTypes, the union/intersection of the operands' baseclasses. + * For AndTypes/OrTypes, the merge/intersection of the operands' baseclasses. * Inherited by all type proxies. `Nil` for all other types. */ final def baseClasses(implicit ctx: Context): List[ClassSymbol] = track("baseClasses") { @@ -394,7 +394,10 @@ object Types { case tp: ClassInfo => tp.cls.baseClasses case AndType(tp1, tp2) => - tp1.baseClasses union tp2.baseClasses + (new BaseDataBuilder) + .addAll(tp1.baseClasses) + .addAll(tp2.baseClasses) + .baseClasses case OrType(tp1, tp2) => tp1.baseClasses intersect tp2.baseClasses case _ => Nil @@ -472,22 +475,24 @@ object Types { */ final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = { @tailrec def go(tp: Type): Denotation = tp match { - case tp: RefinedType => - if (name eq tp.refinedName) goRefined(tp) else go(tp.parent) - case tp: ThisType => - goThis(tp) - case tp: TypeRef => - tp.denot.findMember(name, pre, excluded) case tp: TermRef => go (tp.underlying match { case mt: MethodType if mt.paramInfos.isEmpty && (tp.symbol is Stable) => mt.resultType case tp1 => tp1 }) - case tp: TypeParamRef => - goParam(tp) + case tp: TypeRef => + tp.denot.findMember(name, pre, excluded) + case tp: ThisType => + goThis(tp) + case tp: RefinedType => + if (name eq tp.refinedName) goRefined(tp) else go(tp.parent) case tp: RecType => goRec(tp) + case tp: TypeParamRef => + goParam(tp) + case tp: SuperType => + goSuper(tp) case tp: HKApply => goApply(tp) case tp: TypeProxy => @@ -611,6 +616,12 @@ object Types { go(next) } } + def goSuper(tp: SuperType) = go(tp.underlying) match { + case d: JointRefDenotation => + typr.println(i"redirecting super.$name from $tp to ${d.symbol.showLocated}") + new UniqueRefDenotation(d.symbol, tp.memberInfo(d.symbol), d.validFor) + case d => d + } def goAnd(l: Type, r: Type) = { go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name)) } diff --git a/project/Build.scala b/project/Build.scala index 013b7daf24a2..241686ba5051 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -167,7 +167,7 @@ object Build { // otherwise sbt 0.13 incremental compilation breaks (https://github.com/sbt/sbt/issues/3142) scalacOptions ++= Seq("-bootclasspath", sys.props("sun.boot.class.path")), - scalacOptions += "-optimise", + // scalacOptions += "-optimise", // sbt gets very unhappy if two projects use the same target target := baseDirectory.value / ".." / "out" / "bootstrap" / name.value, diff --git a/tests/neg/i2677.scala b/tests/neg/i2677.scala new file mode 100644 index 000000000000..72940bf02628 --- /dev/null +++ b/tests/neg/i2677.scala @@ -0,0 +1,6 @@ +trait A { def x = "foo" } +trait B { def x = 42 } +object Test { + val AB = new A with B { override def x = super.x } // error: wrong override + AB.x +} \ No newline at end of file diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index 149220bd560d..89e20e94302f 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -79,27 +79,6 @@ class X3 { override type T = A1 // error: overrides nothing } -package p3 { - -// Dotty change of rules: Toverrider#f does not -// override TCommon#f, hence the accidental override rule -// applies. -trait TCommon { - def f: String -} - -class C1 extends TCommon { - def f = "in C1" -} - -trait TOverrider { this: TCommon => - override def f = "in TOverrider" // The overridden self-type member... -} - -class C2 extends C1 with TOverrider // ... fails to override, here. // error: accidental override - -} - package p4 { abstract class C[T] { def head: T } diff --git a/tests/pos/override-via-self.scala b/tests/pos/override-via-self.scala new file mode 100644 index 000000000000..0a8147b81f42 --- /dev/null +++ b/tests/pos/override-via-self.scala @@ -0,0 +1,18 @@ +// Question: Does TOverrider#f override TCommon#f? +// If not, the accidental override rule applies. +// Dotty used to say no, but with the change to baseClasses in AndTypes says +// yes. Not sure what the right answer is. But in any case we should +// keep the test to notice if there's a difference in behavior. +trait TCommon { + def f: String +} +class C1 extends TCommon { + def f = "in C1" +} + +trait TOverrider { this: TCommon => + override def f = "in TOverrider" // The overridden self-type member... +} + +class C2 extends C1 with TOverrider // ... failed to override, here. But now it is OK. +