Skip to content

Commit 8bdc690

Browse files
committed
Merge pull request #228 from dotty-staging/fix/overriding
Fix/overriding
2 parents 7a1f630 + ea06d66 commit 8bdc690

14 files changed

+202
-77
lines changed

src/dotty/tools/dotc/core/Denotations.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,7 @@ object Denotations {
241241
val sym2 = denot2.symbol
242242
val sym2Accessible = sym2.isAccessibleFrom(pre)
243243
def prefer(info1: Type, sym1: Symbol, info2: Type, sym2: Symbol) =
244-
info1 <:< info2 &&
245-
(sym1.isAsConcrete(sym2) || !(info2 <:< info1))
244+
info1.overrides(info2) && (sym1.isAsConcrete(sym2) || !info2.overrides(info1))
246245
if (sym2Accessible && prefer(info2, sym2, info1, sym1)) denot2
247246
else {
248247
val sym1Accessible = sym1.isAccessibleFrom(pre)

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,13 @@ object SymDenotations {
805805
* either as overrider or overridee.
806806
*/
807807
final def canMatchInheritedSymbols(implicit ctx: Context): Boolean =
808-
maybeOwner.isClass && !isConstructor && !is(Private)
808+
maybeOwner.isClass && memberCanMatchInheritedSymbols
809+
810+
/** If false, this class member cannot possibly participate in an override,
811+
* either as overrider or overridee.
812+
*/
813+
final def memberCanMatchInheritedSymbols(implicit ctx: Context): Boolean =
814+
!isConstructor && !is(Private)
809815

810816
/** The symbol, in class `inClass`, that is overridden by this denotation. */
811817
final def overriddenSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol =

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,6 @@ trait TypeOps { this: Context =>
197197
}
198198
}
199199

200-
201-
202200
private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = {
203201
val lazyInfo = new LazyType { // needed so we do not force `formal`.
204202
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
@@ -207,7 +205,11 @@ trait TypeOps { this: Context =>
207205
}
208206
}
209207
val typeArgFlag = if (formal is Local) TypeArgument else EmptyFlags
210-
val sym = ctx.newSymbol(cls, formal.name, formal.flagsUNSAFE & RetainedTypeArgFlags | typeArgFlag, lazyInfo)
208+
val sym = ctx.newSymbol(
209+
cls, formal.name,
210+
formal.flagsUNSAFE & RetainedTypeArgFlags | typeArgFlag | Override,
211+
lazyInfo,
212+
coord = cls.coord)
211213
cls.enter(sym, decls)
212214
}
213215

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,22 @@ object Types {
542542
ctx.typeComparer.isSameType(this, that)
543543
}
544544

545+
/** Is this type a legal type for a member that overrides another
546+
* member of type `that`? This is the same as `<:<`, except that
547+
* the types ()T and => T are identified, and T is seen as overriding
548+
* either type.
549+
*/
550+
final def overrides(that: Type)(implicit ctx: Context) = {
551+
def result(tp: Type): Type = tp match {
552+
case ExprType(_) | MethodType(Nil, _) => tp.resultType
553+
case _ => tp
554+
}
555+
this <:< that || {
556+
val rthat = result(that)
557+
(rthat ne that) && result(this) <:< rthat
558+
}
559+
}
560+
545561
/** Is this type close enough to that type so that members
546562
* with the two type would override each other?d
547563
* This means:

src/dotty/tools/dotc/transform/OverridingPairs.scala

Lines changed: 43 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package dotty.tools.dotc
22
package transform
33

44
import core._
5-
import Flags._, Symbols._, Contexts._, Types._, Scopes._
5+
import Flags._, Symbols._, Contexts._, Types._, Scopes._, Decorators._
66
import util.HashSet
7-
import collection.mutable.HashMap
7+
import collection.mutable
88
import collection.immutable.BitSet
99
import scala.annotation.tailrec
1010

@@ -28,8 +28,7 @@ object OverridingPairs {
2828
/** Symbols to exclude: Here these are constructors and private locals.
2929
* But it may be refined in subclasses.
3030
*/
31-
protected def exclude(sym: Symbol): Boolean =
32-
sym.isConstructor || sym.is(PrivateLocal)
31+
protected def exclude(sym: Symbol): Boolean = !sym.memberCanMatchInheritedSymbols
3332

3433
/** The parents of base (may also be refined).
3534
*/
@@ -70,20 +69,20 @@ object OverridingPairs {
7069
}
7170

7271
private val subParents = {
73-
val subParents = new HashMap[Symbol, BitSet]
72+
val subParents = new mutable.HashMap[Symbol, BitSet]
7473
for (bc <- base.info.baseClasses)
7574
subParents(bc) = BitSet(parents.indices.filter(parents(_).derivesFrom(bc)): _*)
7675
subParents
7776
}
7877

7978
private def hasCommonParentAsSubclass(cls1: Symbol, cls2: Symbol): Boolean =
80-
(subParents(cls1) intersect subParents(cls2)).isEmpty
79+
(subParents(cls1) intersect subParents(cls2)).nonEmpty
8180

8281
/** The scope entries that have already been visited as overridden
8382
* (maybe excluded because of hasCommonParentAsSubclass).
8483
* These will not appear as overriding
8584
*/
86-
private val visited = new HashSet[ScopeEntry](64)
85+
private val visited = new mutable.HashSet[Symbol]
8786

8887
/** The current entry candidate for overriding
8988
*/
@@ -99,48 +98,48 @@ object OverridingPairs {
9998
var overridden: Symbol = _
10099

101100
//@M: note that next is called once during object initialization
102-
def hasNext: Boolean = curEntry ne null
101+
final def hasNext: Boolean = nextEntry ne null
103102

104-
@tailrec
105-
final def next(): Unit = {
106-
if (curEntry ne null) {
107-
overriding = curEntry.sym
103+
/** @post
104+
* curEntry = the next candidate that may override something else
105+
* nextEntry = curEntry
106+
* overriding = curEntry.sym
107+
*/
108+
private def nextOverriding(): Unit = {
109+
@tailrec def loop(): Unit =
110+
if (curEntry ne null) {
111+
overriding = curEntry.sym
112+
if (visited.contains(overriding)) {
113+
curEntry = curEntry.prev
114+
loop()
115+
}
116+
}
117+
loop()
118+
nextEntry = curEntry
119+
}
120+
121+
/** @post
122+
* hasNext = there is another overriding pair
123+
* overriding = overriding member of the pair, provided hasNext is true
124+
* overridden = overridden member of the pair, provided hasNext is true
125+
*/
126+
@tailrec final def next(): Unit =
127+
if (nextEntry ne null) {
128+
nextEntry = decls.lookupNextEntry(nextEntry)
108129
if (nextEntry ne null) {
109-
val overridingOwner = overriding.owner
110-
do {
111-
do {
112-
nextEntry = decls.lookupNextEntry(nextEntry);
113-
/* DEBUG
114-
if ((nextEntry ne null) &&
115-
!(nextEntry.sym hasFlag PRIVATE) &&
116-
!(overriding.owner == nextEntry.sym.owner) &&
117-
!matches(overriding, nextEntry.sym))
118-
println("skipping "+overriding+":"+self.memberType(overriding)+overriding.locationString+" to "+nextEntry.sym+":"+self.memberType(nextEntry.sym)+nextEntry.sym.locationString)
119-
*/
120-
} while ((nextEntry ne null) &&
121-
(//!!!!nextEntry.sym.canMatchInheritedSymbols ||
122-
(overriding.owner == nextEntry.sym.owner) ||
123-
(!matches(overriding, nextEntry.sym)) ||
124-
(exclude(overriding))))
125-
if (nextEntry ne null) visited.addEntry(nextEntry)
126-
// skip nextEntry if a class in `parents` is a subclass of the owners of both
127-
// overriding and nextEntry.sym
128-
} while ((nextEntry ne null) &&
129-
hasCommonParentAsSubclass(overridingOwner, nextEntry.sym.owner))
130-
if (nextEntry ne null) {
131-
overridden = nextEntry.sym;
132-
//Console.println("yield: " + overriding + overriding.locationString + " / " + overridden + overridden.locationString);//DEBUG
133-
} else {
134-
do {
135-
curEntry = curEntry.prev
136-
} while ((curEntry ne null) && visited.contains(curEntry))
137-
nextEntry = curEntry
138-
next
130+
overridden = nextEntry.sym
131+
if (overriding.owner != overridden.owner && matches(overriding, overridden)) {
132+
visited += overridden
133+
if (!hasCommonParentAsSubclass(overriding.owner, overridden.owner)) return
139134
}
135+
} else {
136+
curEntry = curEntry.prev
137+
nextOverriding()
140138
}
139+
next()
141140
}
142-
}
143141

144-
next
142+
nextOverriding()
143+
next()
145144
}
146145
}

src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ class SymUtils(val self: Symbol) extends AnyVal {
2929

3030
def isVolatile(implicit ctx: Context) = self.hasAnnotation(defn.VolatileAnnot)
3131

32+
def isAnyOverride(implicit ctx: Context) = self.is(Override) || self.is(AbsOverride)
33+
// careful: AbsOverride is a term only flag. combining with Override would catch only terms.
34+
3235
/** If this is a constructor, its owner: otherwise this. */
3336
final def skipConstructor(implicit ctx: Context): Symbol =
3437
if (self.isConstructor) self.owner else self

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

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import transform._
55
import core._
66
import config._
77
import Symbols._, SymDenotations._, Types._, Contexts._, Decorators._, Flags._, Names._, NameOps._
8-
import StdNames._, Denotations._, Scopes._, Constants.Constant
8+
import StdNames._, Denotations._, Scopes._, Constants.Constant, SymUtils._
99
import Annotations._
1010
import util.Positions._
1111
import scala.collection.{ mutable, immutable }
@@ -29,9 +29,6 @@ object RefChecks {
2929
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = isDefaultGetter(name)
3030
}
3131

32-
private val AnyOverride = Override | AbsOverride
33-
private val AnyOverrideOrSynthetic = AnyOverride | Synthetic
34-
3532
/** Only one overloaded alternative is allowed to define default arguments */
3633
private def checkOverloadedRestrictions(clazz: Symbol)(implicit ctx: Context): Unit = {
3734
// Using the default getters (such as methodName$default$1) as a cheap way of
@@ -107,6 +104,7 @@ object RefChecks {
107104
*/
108105
private def checkAllOverrides(clazz: Symbol)(implicit ctx: Context): Unit = {
109106
val self = clazz.thisType
107+
var hasErrors = false
110108

111109
case class MixinOverrideError(member: Symbol, msg: String)
112110

@@ -135,14 +133,14 @@ object RefChecks {
135133

136134
def infoString0(sym: Symbol, showLocation: Boolean) = {
137135
val sym1 = sym.underlyingSymbol
138-
if (showLocation) sym1.showLocated
139-
else
140-
sym1.show +
141-
(if (sym1.isAliasType) ", which equals " + self.memberInfo(sym1)
142-
else if (sym1.isAbstractType) " with bounds" + self.memberInfo(sym1)
143-
else if (sym1.is(Module)) ""
144-
else if (sym1.isTerm) " of type " + self.memberInfo(sym1)
145-
else "")
136+
def info = self.memberInfo(sym1)
137+
i"${if (showLocation) sym1.showLocated else sym1}${
138+
if (sym1.isAliasType) i", which equals $info"
139+
else if (sym1.isAbstractType) i" with bounds $info"
140+
else if (sym1.is(Module)) ""
141+
else if (sym1.isTerm) i" of type $info"
142+
else ""
143+
}"
146144
}
147145

148146
/* Check that all conditions for overriding `other` by `member`
@@ -171,10 +169,15 @@ object RefChecks {
171169
"overriding %s;\n %s %s%s".format(
172170
infoStringWithLocation(other), infoString(member), msg, addendum)
173171
}
174-
def emitOverrideError(fullmsg: String) = {
175-
if (member.owner == clazz) ctx.error(fullmsg, member.pos)
176-
else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
177-
}
172+
173+
def emitOverrideError(fullmsg: String) =
174+
if (!(hasErrors && member.is(Synthetic) && member.is(Module))) {
175+
// suppress errors relating toi synthetic companion objects if other override
176+
// errors (e.g. relating to the companion class) have already been reported.
177+
if (member.owner == clazz) ctx.error(fullmsg, member.pos)
178+
else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
179+
hasErrors = true
180+
}
178181

179182
def overrideError(msg: String) = {
180183
if (noErrorType)
@@ -246,9 +249,11 @@ object RefChecks {
246249
} else if (!other.is(Deferred) && member.isClass) {
247250
overrideError("cannot be used here - classes can only override abstract types")
248251
} else if (other.isEffectivelyFinal) { // (1.2)
249-
overrideError("cannot override final member")
250-
} else if (!other.is(Deferred) && !isDefaultGetter(other.name) && !member.is(AnyOverrideOrSynthetic)) {
251-
// (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to
252+
overrideError(i"cannot override final member ${other.showLocated}")
253+
} else if (!other.is(Deferred) &&
254+
!isDefaultGetter(other.name) &&
255+
!member.isAnyOverride) {
256+
// (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to
252257
// the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket.
253258
if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner))
254259
emitOverrideError(
@@ -259,13 +264,13 @@ object RefChecks {
259264
overrideError("needs `override' modifier")
260265
} else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) {
261266
overrideError("needs `abstract override' modifiers")
262-
} else if (member.is(AnyOverride) && other.is(Accessor) &&
267+
} else if (member.is(Override) && other.is(Accessor) &&
263268
other.accessedFieldOrGetter.is(Mutable, butNot = Lazy)) {
264269
// !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here.
265270
// !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches.
266271
if (!ctx.settings.overrideVars.value)
267272
overrideError("cannot override a mutable variable")
268-
} else if (member.is(AnyOverride) &&
273+
} else if (member.isAnyOverride &&
269274
!(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) &&
270275
!member.is(Deferred) && !other.is(Deferred) &&
271276
intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) {
@@ -281,6 +286,10 @@ object RefChecks {
281286
overrideError("cannot be used here - term macros cannot override abstract methods")
282287
} else if (other.is(Macro) && !member.is(Macro)) { // (1.10)
283288
overrideError("cannot be used here - only term macros can override term macros")
289+
} else if (member.isTerm && !isDefaultGetter(member.name) && !(memberTp overrides otherTp)) {
290+
// types don't need to have their bounds in an overriding relationship
291+
// since we automatically form their intersection when selecting.
292+
overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp))
284293
} else {
285294
checkOverrideDeprecated()
286295
}
@@ -543,7 +552,7 @@ object RefChecks {
543552

544553
// 4. Check that every defined member with an `override` modifier overrides some other member.
545554
for (member <- clazz.info.decls)
546-
if (member.is(AnyOverride) && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) {
555+
if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) {
547556
// for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG
548557

549558
val nonMatching = clazz.info.member(member.name).altsWith(alt => alt.owner != clazz && !alt.is(Final))
@@ -556,7 +565,8 @@ object RefChecks {
556565
val superSigs = ms.map(_.showDcl).mkString("\n")
557566
issueError(s".\nNote: the super classes of ${member.owner} contain the following, non final members named ${member.name}:\n${superSigs}")
558567
}
559-
member.resetFlag(AnyOverride)
568+
member.resetFlag(Override)
569+
member.resetFlag(AbsOverride)
560570
}
561571
}
562572

test/dotc/tests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class tests extends CompilerTest {
7474
@Test def neg_autoTupling = compileFile(posDir, "autoTuplingTest", "-language:noAutoTupling" :: Nil, xerrors = 4)
7575
@Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 4)
7676
@Test def neg_companions = compileFile(negDir, "companions", xerrors = 1)
77+
@Test def neg_over = compileFile(negDir, "over", xerrors = 1)
78+
@Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 5)
7779
@Test def neg_projections = compileFile(negDir, "projections", xerrors = 1)
7880
@Test def neg_i39 = compileFile(negDir, "i39", xerrors = 1)
7981
@Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 4)

tests/neg/over.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
trait T {
2+
def x = 1
3+
}
4+
5+
class C extends T {
6+
7+
val x = 2
8+
override val y = 2
9+
10+
}

0 commit comments

Comments
 (0)