Skip to content

Fix #7401: Refine overloading resolution #7989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 35 additions & 21 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1616,7 +1616,7 @@ trait Applications extends Compatibility {
* called twice from the public `resolveOverloaded` method, once with
* implicits and SAM conversions enabled, and once without.
*/
private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = {
private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] =
record("resolveOverloaded/2")

def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty
Expand Down Expand Up @@ -1785,28 +1785,42 @@ trait Applications extends Compatibility {
candidates.flatMap(cloneCandidate)
}

def resultIsMethod(tp: Type): Boolean = tp.widen.stripPoly match
case tp: MethodType => tp.resultType.isInstanceOf[MethodType]
case _ => false

val found = narrowMostSpecific(candidates)
if (found.length <= 1) found
else pt match {
case pt @ FunProto(_, resType: FunProto) =>
// try to narrow further with snd argument list
val advanced = advanceCandidates(pt.typedArgs().tpes)
resolveOverloaded(advanced.map(_._1), resType, Nil) // resolve with candidates where first params are stripped
.map(advanced.toMap) // map surviving result(s) back to original candidates
case _ =>
val noDefaults = alts.filter(!_.symbol.hasDefaultParams)
val noDefaultsCount = noDefaults.length
if (noDefaultsCount == 1)
noDefaults // return unique alternative without default parameters if it exists
else if (noDefaultsCount > 1 && noDefaultsCount < alts.length)
resolveOverloaded(noDefaults, pt, targs) // try again, dropping defult arguments
else {
val deepPt = pt.deepenProto
if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs)
else candidates
}
}
}
else
val deepPt = pt.deepenProto
deepPt match
case pt @ FunProto(_, resType: FunProto) =>
// try to narrow further with snd argument list
val advanced = advanceCandidates(pt.typedArgs().tpes)
resolveOverloaded(advanced.map(_._1), resType, Nil) // resolve with candidates where first params are stripped
.map(advanced.toMap) // map surviving result(s) back to original candidates
case _ =>
// prefer alternatives that need no eta expansion
val noCurried = alts.filter(!resultIsMethod(_))
val noCurriedCount = noCurried.length
if noCurriedCount == 1 then
noCurried
else if noCurriedCount > 1 && noCurriedCount < alts.length then
resolveOverloaded(noCurried, pt, targs)
else
// prefer alternatves that match without default parameters
val noDefaults = alts.filter(!_.symbol.hasDefaultParams)
val noDefaultsCount = noDefaults.length
if noDefaultsCount == 1 then
noDefaults
else if noDefaultsCount > 1 && noDefaultsCount < alts.length then
resolveOverloaded(noDefaults, pt, targs)
else if deepPt ne pt then
// try again with a deeper known expected type
resolveOverloaded(alts, deepPt, targs)
else
candidates
end resolveOverloaded

/** Try to typecheck any arguments in `pt` that are function values missing a
* parameter type. If the formal parameter types corresponding to a closure argument
Expand Down
14 changes: 0 additions & 14 deletions compiler/test-resources/repl/i4536

This file was deleted.

30 changes: 0 additions & 30 deletions tests/neg/i2378.scala

This file was deleted.

9 changes: 5 additions & 4 deletions tests/pos/i2378.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ trait Toolbox {
}

val Apply: ApplyImpl

trait ApplyImpl {
def unapply(tree: Tree): Option[(Tree, Seq[Tree])]
def unapply(tree: tpd.Tree)(implicit c: Cap): Option[(tpd.Tree, Seq[tpd.Tree])]
Expand All @@ -19,11 +20,11 @@ class Test(val tb: Toolbox) {
import tb._
implicit val cap: Cap = null

def foo(tree: Tree): Int = tree match {
case Apply(fun, args) => 3
def foo(tree: Tree): Int = (tree: Any) match {
case tb.Apply(fun, args) => 3
}

def bar(tree: tpd.Tree): Int = tree match {
def bar(tree: tpd.Tree): Int = (tree: Any) match {
case Apply(fun, args) => 3
}
}
}
11 changes: 11 additions & 0 deletions tests/pos/i7401.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
object Test {
given ops: (a: Int) extended with {
def foo(i: Int): Unit = ()
def foo: Unit = ()
}
val x: Int = 5
x.foo(4)
x.foo
ops.foo(x)(4)
ops.foo(x)
}