Skip to content

Commit 744d5ca

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 b34307b commit 744d5ca

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
@@ -1252,8 +1252,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
12521252
* If that tournament yields a draw, a tiebreak is applied where
12531253
* an alternative that takes more implicit parameters wins over one
12541254
* that takes fewer.
1255+
*
1256+
* @param followApply if true consider `apply` members when comparing with a method reference
12551257
*/
1256-
def compare(alt1: TermRef, alt2: TermRef)(implicit ctx: Context): Int = track("compare") { trace(i"compare($alt1, $alt2)", overload) {
1258+
def compare(alt1: TermRef, alt2: TermRef, followApply: Boolean)(implicit ctx: Context): Int = track("compare") { trace(i"compare($alt1, $alt2)", overload) {
12571259

12581260
assert(alt1 ne alt2)
12591261

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

@@ -1424,20 +1428,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14241428
else compareWithTypes(fullType1, fullType2) // continue by comparing implicits parameters
14251429
}}
14261430

1427-
def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
1431+
def narrowMostSpecific(alts: List[TermRef], followApply: Boolean)(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
14281432
alts match {
14291433
case Nil => alts
14301434
case _ :: Nil => alts
14311435
case alt1 :: alt2 :: Nil =>
1432-
compare(alt1, alt2) match {
1436+
compare(alt1, alt2, followApply) match {
14331437
case 1 => alt1 :: Nil
14341438
case -1 => alt2 :: Nil
14351439
case 0 => alts
14361440
}
14371441
case alt :: alts1 =>
14381442
def survivors(previous: List[TermRef], alts: List[TermRef]): List[TermRef] = alts match {
14391443
case alt :: alts1 =>
1440-
compare(previous.head, alt) match {
1444+
compare(previous.head, alt, followApply) match {
14411445
case 1 => survivors(previous, alts1)
14421446
case -1 => survivors(alt :: previous.tail, alts1)
14431447
case 0 => survivors(alt :: previous, alts1)
@@ -1447,7 +1451,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
14471451
val best :: rest = survivors(alt :: Nil, alts1)
14481452
def asGood(alts: List[TermRef]): List[TermRef] = alts match {
14491453
case alt :: alts1 =>
1450-
if (compare(alt, best) < 0) asGood(alts1) else alt :: asGood(alts1)
1454+
if (compare(alt, best, followApply) < 0) asGood(alts1) else alt :: asGood(alts1)
14511455
case nil =>
14521456
Nil
14531457
}
@@ -1548,9 +1552,13 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15481552
def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] =
15491553
alts filter (isApplicableType(_, argTypes, resultType))
15501554

1555+
val numArgs = pt match {
1556+
case pt @ FunProto(args, resultType) => args.length
1557+
case _ => 0
1558+
}
1559+
15511560
val candidates = pt match {
15521561
case pt @ FunProto(args, resultType) =>
1553-
val numArgs = args.length
15541562
val normArgs = args.mapConserve {
15551563
case Block(Nil, expr) => expr
15561564
case x => x
@@ -1658,7 +1666,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
16581666
candidates.flatMap(cloneCandidate)
16591667
}
16601668

1661-
val found = narrowMostSpecific(candidates)
1669+
val found = narrowMostSpecific(candidates, followApply = numArgs != 0)
16621670
if (found.length <= 1) found
16631671
else pt match {
16641672
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)