Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 24 additions & 26 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
31 changes: 21 additions & 10 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand All @@ -394,7 +394,10 @@ object Types {
case tp: ClassInfo =>
tp.cls.baseClasses
case AndType(tp1, tp2) =>
tp1.baseClasses union tp2.baseClasses
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good reason to not have union be defined on List :)

(new BaseDataBuilder)
.addAll(tp1.baseClasses)
.addAll(tp2.baseClasses)
.baseClasses
case OrType(tp1, tp2) =>
tp1.baseClasses intersect tp2.baseClasses
case _ => Nil
Expand Down Expand Up @@ -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 =>
Expand Down Expand Up @@ -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))
}
Expand Down
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions tests/neg/i2677.scala
Original file line number Diff line number Diff line change
@@ -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
}
21 changes: 0 additions & 21 deletions tests/neg/overrides.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
18 changes: 18 additions & 0 deletions tests/pos/override-via-self.scala
Original file line number Diff line number Diff line change
@@ -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.