Skip to content

Commit d8bcc16

Browse files
committed
Fix #7401: Refine overloading resolution
If we are left with several alternatives after normal overloadiong resolution and the expected result type is not a FunProto, prefer the alternatives that do not have a method result type. This makes i2378 compile, so it got moved to a pos test.
1 parent 193f7de commit d8bcc16

File tree

4 files changed

+47
-55
lines changed

4 files changed

+47
-55
lines changed

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

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,7 +1616,7 @@ trait Applications extends Compatibility {
16161616
* called twice from the public `resolveOverloaded` method, once with
16171617
* implicits and SAM conversions enabled, and once without.
16181618
*/
1619-
private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = {
1619+
private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] =
16201620
record("resolveOverloaded/2")
16211621

16221622
def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty
@@ -1785,28 +1785,38 @@ trait Applications extends Compatibility {
17851785
candidates.flatMap(cloneCandidate)
17861786
}
17871787

1788+
def resultIsMethod(tp: Type): Boolean = tp.widen.stripPoly match
1789+
case tp: MethodType => tp.resultType.isInstanceOf[MethodType]
1790+
case _ => false
1791+
17881792
val found = narrowMostSpecific(candidates)
17891793
if (found.length <= 1) found
1790-
else pt match {
1791-
case pt @ FunProto(_, resType: FunProto) =>
1792-
// try to narrow further with snd argument list
1793-
val advanced = advanceCandidates(pt.typedArgs().tpes)
1794-
resolveOverloaded(advanced.map(_._1), resType, Nil) // resolve with candidates where first params are stripped
1795-
.map(advanced.toMap) // map surviving result(s) back to original candidates
1796-
case _ =>
1797-
val noDefaults = alts.filter(!_.symbol.hasDefaultParams)
1798-
val noDefaultsCount = noDefaults.length
1799-
if (noDefaultsCount == 1)
1800-
noDefaults // return unique alternative without default parameters if it exists
1801-
else if (noDefaultsCount > 1 && noDefaultsCount < alts.length)
1802-
resolveOverloaded(noDefaults, pt, targs) // try again, dropping defult arguments
1803-
else {
1804-
val deepPt = pt.deepenProto
1805-
if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs)
1806-
else candidates
1807-
}
1808-
}
1809-
}
1794+
else
1795+
val deepPt = pt.deepenProto
1796+
deepPt match
1797+
case pt @ FunProto(_, resType: FunProto) =>
1798+
// try to narrow further with snd argument list
1799+
val advanced = advanceCandidates(pt.typedArgs().tpes)
1800+
resolveOverloaded(advanced.map(_._1), resType, Nil) // resolve with candidates where first params are stripped
1801+
.map(advanced.toMap) // map surviving result(s) back to original candidates
1802+
case _ =>
1803+
// prefer alternatives that need no eta expansion
1804+
val noCurried = alts.filter(!resultIsMethod(_))
1805+
if noCurried.length == 1 then
1806+
noCurried
1807+
else
1808+
// prefer alternatves that match without default parameters
1809+
val noDefaults = noCurried.filter(!_.symbol.hasDefaultParams)
1810+
val noDefaultsCount = noDefaults.length
1811+
if noDefaultsCount == 1 then
1812+
noDefaults // return unique alternative without default parameters if it exists
1813+
else if noDefaultsCount > 1 && noDefaultsCount < alts.length then
1814+
resolveOverloaded(noDefaults, pt, targs) // try again, dropping defult arguments
1815+
else if deepPt ne pt then
1816+
// try again with a deeper known expected type
1817+
resolveOverloaded(alts, deepPt, targs)
1818+
else candidates
1819+
end resolveOverloaded
18101820

18111821
/** Try to typecheck any arguments in `pt` that are function values missing a
18121822
* parameter type. If the formal parameter types corresponding to a closure argument

tests/neg/i2378.scala

Lines changed: 0 additions & 30 deletions
This file was deleted.

tests/pos/i2378.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ trait Toolbox {
99
}
1010

1111
val Apply: ApplyImpl
12+
1213
trait ApplyImpl {
1314
def unapply(tree: Tree): Option[(Tree, Seq[Tree])]
1415
def unapply(tree: tpd.Tree)(implicit c: Cap): Option[(tpd.Tree, Seq[tpd.Tree])]
@@ -19,11 +20,11 @@ class Test(val tb: Toolbox) {
1920
import tb._
2021
implicit val cap: Cap = null
2122

22-
def foo(tree: Tree): Int = tree match {
23-
case Apply(fun, args) => 3
23+
def foo(tree: Tree): Int = (tree: Any) match {
24+
case tb.Apply(fun, args) => 3
2425
}
2526

26-
def bar(tree: tpd.Tree): Int = tree match {
27+
def bar(tree: tpd.Tree): Int = (tree: Any) match {
2728
case Apply(fun, args) => 3
2829
}
29-
}
30+
}

tests/pos/i7401.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object Test {
2+
given ops: (a: Int) extended with {
3+
def foo(i: Int): Unit = ()
4+
def foo: Unit = ()
5+
}
6+
val x: Int = 5
7+
x.foo(4)
8+
x.foo
9+
ops.foo(x)(4)
10+
ops.foo(x)
11+
}

0 commit comments

Comments
 (0)