Skip to content

Commit d602ff4

Browse files
authored
Merge pull request #7609 from som-snytt/issue/10662
A foreign definition induces ambiguity
2 parents ead235c + c0e349a commit d602ff4

File tree

7 files changed

+111
-13
lines changed

7 files changed

+111
-13
lines changed

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ trait Contexts { self: Analyzer =>
5656
LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2")
5757
def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) =
5858
LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp")
59+
def ambiguousDefinitions(owner: Symbol, other: Symbol) =
60+
LookupAmbiguous(s"it is both defined in $owner and available as ${other.fullLocationString}")
5961

6062
private lazy val startContext = NoContext.make(
6163
Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType,
@@ -1410,15 +1412,17 @@ trait Contexts { self: Analyzer =>
14101412
}
14111413

14121414
// cx.scope eq null arises during FixInvalidSyms in Duplicators
1413-
while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) {
1414-
pre = cx.enclClass.prefix
1415-
defSym = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope) match {
1416-
case NoSymbol => searchPrefix
1417-
case found => found
1415+
def nextDefinition(): Unit =
1416+
while (defSym == NoSymbol && (cx ne NoContext) && (cx.scope ne null)) {
1417+
pre = cx.enclClass.prefix
1418+
defSym = lookupInScope(cx.owner, cx.enclClass.prefix, cx.scope) match {
1419+
case NoSymbol => searchPrefix
1420+
case found => found
1421+
}
1422+
if (!defSym.exists) cx = cx.outer // push further outward
14181423
}
1419-
if (!defSym.exists)
1420-
cx = cx.outer // push further outward
1421-
}
1424+
nextDefinition()
1425+
14221426
if (symbolDepth < 0)
14231427
symbolDepth = cx.depth
14241428

@@ -1458,24 +1462,50 @@ trait Contexts { self: Analyzer =>
14581462
importCursor.advanceImp1Imp2()
14591463
}
14601464

1461-
if (defSym.exists && impSym.exists) {
1465+
val preferDef: Boolean = defSym.exists && (!impSym.exists || {
14621466
// 4) root imported symbols have same (lowest) precedence as package-owned symbols in different compilation units.
14631467
if (imp1.depth < symbolDepth && imp1.isRootImport && foreignDefined)
1464-
impSym = NoSymbol
1468+
true
14651469
// 4) imported symbols have higher precedence than package-owned symbols in different compilation units.
14661470
else if (imp1.depth >= symbolDepth && foreignDefined)
1467-
defSym = NoSymbol
1471+
false
14681472
// Defined symbols take precedence over erroneous imports.
14691473
else if (impSym.isError || impSym.name == nme.CONSTRUCTOR)
1470-
impSym = NoSymbol
1474+
true
14711475
// Try to reconcile them before giving up, at least if the def is not visible
14721476
else if (foreignDefined && thisContext.reconcileAmbiguousImportAndDef(name, impSym, defSym))
1473-
impSym = NoSymbol
1477+
true
14741478
// Otherwise they are irreconcilably ambiguous
14751479
else
14761480
return ambiguousDefnAndImport(defSym.alternatives.head.owner, imp1)
1481+
})
1482+
1483+
// If the defSym is at 4, and there is a def at 1 in scope, then the reference is ambiguous.
1484+
if (foreignDefined && !defSym.isPackage) {
1485+
val defSym0 = defSym
1486+
val pre0 = pre
1487+
val cx0 = cx
1488+
while ((cx ne NoContext) && cx.depth >= symbolDepth) cx = cx.outer
1489+
var done = false
1490+
while (!done) {
1491+
defSym = NoSymbol
1492+
nextDefinition()
1493+
done = (cx eq NoContext) || defSym.exists && !foreignDefined
1494+
if (!done && (cx ne NoContext)) cx = cx.outer
1495+
}
1496+
if (defSym.exists && (defSym ne defSym0)) {
1497+
val ambiguity =
1498+
if (preferDef) ambiguousDefinitions(owner = defSym.owner, defSym0)
1499+
else ambiguousDefnAndImport(owner = defSym.owner, imp1)
1500+
return ambiguity
1501+
}
1502+
defSym = defSym0
1503+
pre = pre0
1504+
cx = cx0
14771505
}
14781506

1507+
if (preferDef) impSym = NoSymbol else defSym = NoSymbol
1508+
14791509
// At this point only one or the other of defSym and impSym might be set.
14801510
if (defSym.exists) finishDefSym(defSym, pre)
14811511
else if (impSym.exists) {

test/files/neg/t10662.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
px_2.scala:19: error: reference to X is ambiguous;
2+
it is both defined in package p and available as class X in package q
3+
implicitly[T[X]] // ambiguous
4+
^
5+
one error found

test/files/neg/t10662/pqx_1.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
package p.q
3+
4+
class X {
5+
override def toString() = "p.q.X"
6+
}

test/files/neg/t10662/px_2.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
package p {
3+
4+
trait T[A]
5+
6+
class X {
7+
override def toString() = "p.X"
8+
}
9+
object X {
10+
implicit val tx: T[X] = new T[X] { }
11+
}
12+
13+
package q {
14+
//import p.X // "permanently hidden"
15+
object Test {
16+
// previously, picked p.q.X
17+
// This file compiles by itself;
18+
// from our perspective, the other X renders our X ambiguous
19+
implicitly[T[X]] // ambiguous
20+
}
21+
}
22+
}

test/files/neg/t10662b.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
px_2.scala:16: error: reference to X is ambiguous;
2+
it is both defined in package p and imported subsequently by
3+
import r.X
4+
implicitly[T[X]] // ambiguous
5+
^
6+
one error found

test/files/neg/t10662b/pqx_1.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
package p.q
3+
4+
class X {
5+
override def toString() = "p.q.X"
6+
}

test/files/neg/t10662b/px_2.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
package p {
3+
4+
trait T[A]
5+
6+
class X {
7+
override def toString() = "p.X"
8+
}
9+
object X {
10+
implicit val tx: T[X] = new T[X] { }
11+
}
12+
13+
package q {
14+
import r.X
15+
object Test {
16+
implicitly[T[X]] // ambiguous
17+
}
18+
}
19+
20+
package r {
21+
class X
22+
}
23+
}

0 commit comments

Comments
 (0)