Skip to content

Commit 5d6c102

Browse files
authored
Merge pull request #1393 from dotty-staging/fix-#1386
Fix #1386: Reduce double def errors
2 parents 61aa3d9 + 5c4496a commit 5d6c102

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

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

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,35 +295,65 @@ object Denotations {
295295
val sym1 = denot1.symbol
296296
val sym2 = denot2.symbol
297297

298-
if (isDoubleDef(sym1, sym2)) doubleDefError(denot1, denot2, pre)
299-
300298
val sym2Accessible = sym2.isAccessibleFrom(pre)
299+
301300
/** Does `sym1` come before `sym2` in the linearization of `pre`? */
302301
def precedes(sym1: Symbol, sym2: Symbol) = {
303302
def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
304303
case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
305304
case Nil => true
306305
}
307-
sym1.derivesFrom(sym2) ||
308-
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses)
306+
(sym1 ne sym2) &&
307+
(sym1.derivesFrom(sym2) ||
308+
!sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses))
309309
}
310310

311-
/** Preference according to partial pre-order (isConcrete, precedes) */
311+
/** Similar to SymDenotation#accessBoundary, but without the special cases. */
312+
def accessBoundary(sym: Symbol) =
313+
if (sym.is(Private)) sym.owner
314+
else sym.privateWithin.orElse(
315+
if (sym.is(Protected)) sym.owner.enclosingPackageClass
316+
else defn.RootClass
317+
)
318+
319+
/** Establish a partial order "preference" order between symbols.
320+
* Give preference to `sym1` over `sym2` if one of the following
321+
* conditions holds, in decreasing order of weight:
322+
* 1. sym1 is concrete and sym2 is abstract
323+
* 2. The owner of sym1 comes before the owner of sym2 in the linearization
324+
* of the type of the prefix `pre`.
325+
* 3. The access boundary of sym2 is properly contained in the access
326+
* boundary of sym1. For protected access, we count the enclosing
327+
* package as access boundary.
328+
* 4. sym1 a method but sym2 is not.
329+
* The aim of these criteria is to give some disambiguation on access which
330+
* - does not depend on textual order or other arbitrary choices
331+
* - minimizes raising of doubleDef errors
332+
*/
312333
def preferSym(sym1: Symbol, sym2: Symbol) =
313334
sym1.eq(sym2) ||
314335
sym1.isAsConcrete(sym2) &&
315-
(!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner))
336+
(!sym2.isAsConcrete(sym1) ||
337+
precedes(sym1.owner, sym2.owner) ||
338+
accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
339+
sym1.is(Method) && !sym2.is(Method))
316340

317341
/** Sym preference provided types also override */
318342
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
319343
preferSym(sym1, sym2) && info1.overrides(info2)
320344

345+
def handleDoubleDef =
346+
if (preferSym(sym1, sym2)) denot1
347+
else if (preferSym(sym2, sym1)) denot2
348+
else doubleDefError(denot1, denot2, pre)
349+
321350
if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
322351
else {
323352
val sym1Accessible = sym1.isAccessibleFrom(pre)
324353
if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
325354
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
326355
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
356+
else if (isDoubleDef(sym1, sym2)) handleDoubleDef
327357
else {
328358
val sym =
329359
if (!sym1.exists) sym2

tests/run/i1386.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Test {
2+
def main(args: Array[String]) =
3+
assert(new java.util.HashMap[Int, Int]().size == 0)
4+
}

0 commit comments

Comments
 (0)