Skip to content

Commit 767b4b8

Browse files
committed
Fix isAsSpecific
isAsSpecific is now dependent on whether we are looking for a method to apply or a value that does not get applied. Only in the first case, we should consider apply members of an alternative.
1 parent 487d2ae commit 767b4b8

File tree

4 files changed

+39
-10
lines changed

4 files changed

+39
-10
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,8 +1254,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12541254
* If that tournament yields a draw, a tiebreak is applied where
12551255
* an alternative that takes more implicit parameters wins over one
12561256
* that takes fewer.
1257+
*
1258+
* @param followApply if true consider `apply` members when comparing with a method reference
12571259
*/
1258-
def compare(alt1: TermRef, alt2: TermRef)(implicit ctx: Context): Int = track("compare") { trace(i"compare($alt1, $alt2)", overload) {
1260+
def compare(alt1: TermRef, alt2: TermRef, followApply: Boolean)(implicit ctx: Context): Int = track("compare") { trace(i"compare($alt1, $alt2)", overload) {
12591261

12601262
assert(alt1 ne alt2)
12611263

@@ -1279,8 +1281,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12791281
val formals1 =
12801282
if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos.map(_.repeatedToSingle)
12811283
else tp1.paramInfos
1282-
isApplicableMethodRef(alt2, formals1, WildcardType) ||
1283-
tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType]
1284+
val isAsSpecificMethod =
1285+
if (followApply) isApplicableType(alt2, formals1, WildcardType)
1286+
else isApplicableMethodRef(alt2, formals1, WildcardType)
1287+
isAsSpecificMethod || tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType]
12841288
case tp1: PolyType => // (2)
12851289
val nestedCtx = ctx.fresh.setExploreTyperState()
12861290

@@ -1426,20 +1430,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14261430
else compareWithTypes(fullType1, fullType2) // continue by comparing implicits parameters
14271431
}}
14281432

1429-
def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
1433+
def narrowMostSpecific(alts: List[TermRef], followApply: Boolean)(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
14301434
alts match {
14311435
case Nil => alts
14321436
case _ :: Nil => alts
14331437
case alt1 :: alt2 :: Nil =>
1434-
compare(alt1, alt2) match {
1438+
compare(alt1, alt2, followApply) match {
14351439
case 1 => alt1 :: Nil
14361440
case -1 => alt2 :: Nil
14371441
case 0 => alts
14381442
}
14391443
case alt :: alts1 =>
14401444
def survivors(previous: List[TermRef], alts: List[TermRef]): List[TermRef] = alts match {
14411445
case alt :: alts1 =>
1442-
compare(previous.head, alt) match {
1446+
compare(previous.head, alt, followApply) match {
14431447
case 1 => survivors(previous, alts1)
14441448
case -1 => survivors(alt :: previous.tail, alts1)
14451449
case 0 => survivors(alt :: previous, alts1)
@@ -1449,7 +1453,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14491453
val best :: rest = survivors(alt :: Nil, alts1)
14501454
def asGood(alts: List[TermRef]): List[TermRef] = alts match {
14511455
case alt :: alts1 =>
1452-
if (compare(alt, best) < 0) asGood(alts1) else alt :: asGood(alts1)
1456+
if (compare(alt, best, followApply) < 0) asGood(alts1) else alt :: asGood(alts1)
14531457
case nil =>
14541458
Nil
14551459
}
@@ -1550,9 +1554,13 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15501554
def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] =
15511555
alts filter (isApplicableType(_, argTypes, resultType))
15521556

1557+
val numArgs = pt match {
1558+
case pt @ FunProto(args, resultType) => args.length
1559+
case _ => 0
1560+
}
1561+
15531562
val candidates = pt match {
15541563
case pt @ FunProto(args, resultType) =>
1555-
val numArgs = args.length
15561564
val normArgs = args.mapConserve {
15571565
case Block(Nil, expr) => expr
15581566
case x => x
@@ -1660,7 +1668,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
16601668
candidates.flatMap(cloneCandidate)
16611669
}
16621670

1663-
val found = narrowMostSpecific(candidates)
1671+
val found = narrowMostSpecific(candidates, followApply = numArgs != 0)
16641672
if (found.length <= 1) found
16651673
else pt match {
16661674
case pt @ FunProto(_, resType: FunProto) =>

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,7 @@ trait Implicits { self: Typer =>
11421142
def compareCandidate(prev: SearchSuccess, ref: TermRef, level: Int): Int =
11431143
if (prev.ref eq ref) 0
11441144
else if (prev.level != level) prev.level - level
1145-
else nestedContext().test(implicit ctx => compare(prev.ref, ref))
1145+
else nestedContext().test(implicit ctx => compare(prev.ref, ref, followApply = false))
11461146

11471147
/** If `alt1` is also a search success, try to disambiguate as follows:
11481148
* - If alt2 is preferred over alt1, pick alt2, otherwise return an

tests/neg/i6450.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
trait A {
2+
def apply(x: Any): Int = 1
3+
}
4+
5+
object B {
6+
def f(x: Any): Int = 2
7+
lazy val f = new A {}
8+
val x = f(null) // error: ambiguous
9+
}

tests/pos/i6450.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,15 @@ object Test {
2424
val y1: Int = y
2525
val z1: Int = z
2626
}
27+
28+
object Test2 {
29+
30+
trait A {
31+
def apply(x: Any): Int = 1
32+
}
33+
34+
object B {
35+
def f(x: Any): A = new A {}
36+
lazy val f: A = f(null)
37+
}
38+
}

0 commit comments

Comments
 (0)