Skip to content

Commit

Permalink
Fix scala#7401: Refine overloading resolution
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
odersky committed Jan 15, 2020
1 parent 193f7de commit d8bcc16
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 55 deletions.
52 changes: 31 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,38 @@ 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(_))
if noCurried.length == 1 then
noCurried
else
// prefer alternatves that match without default parameters
val noDefaults = noCurried.filter(!_.symbol.hasDefaultParams)
val noDefaultsCount = noDefaults.length
if noDefaultsCount == 1 then
noDefaults // return unique alternative without default parameters if it exists
else if noDefaultsCount > 1 && noDefaultsCount < alts.length then
resolveOverloaded(noDefaults, pt, targs) // try again, dropping defult arguments
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
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)
}

0 comments on commit d8bcc16

Please sign in to comment.