diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 4ecdaeb83b5a..b8c5db87f855 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -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 @@ -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 diff --git a/compiler/test-resources/repl/i4536 b/compiler/test-resources/repl/i4536 deleted file mode 100644 index 97326fb45852..000000000000 --- a/compiler/test-resources/repl/i4536 +++ /dev/null @@ -1,14 +0,0 @@ -scala> object Foo { def apply() = 1; def apply()(implicit ord: Ordering[Int]) = 2; Foo() } -1 | object Foo { def apply() = 1; def apply()(implicit ord: Ordering[Int]) = 2; Foo() } - | ^^^ - |Ambiguous overload. The overloaded alternatives of method apply in object Foo with types - | ()(implicit ord: Ordering[Int]): Int - | (): Int - |both match arguments () -scala> object Foo { def apply() = 1; def apply()(implicit ord: Ordering[Int]) = 2; Foo.apply() } -1 | object Foo { def apply() = 1; def apply()(implicit ord: Ordering[Int]) = 2; Foo.apply() } - | ^^^^^^^^^ - |Ambiguous overload. The overloaded alternatives of method apply in object Foo with types - | ()(implicit ord: Ordering[Int]): Int - | (): Int - |both match arguments () diff --git a/tests/neg/i2378.scala b/tests/neg/i2378.scala deleted file mode 100644 index 1ec5c3c648a8..000000000000 --- a/tests/neg/i2378.scala +++ /dev/null @@ -1,30 +0,0 @@ -trait Cap - -trait Toolbox { - type Tree - - val tpd: TypedTrees - trait TypedTrees { - type Tree - } - - 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])] - } -} - -class Test(val tb: Toolbox) { - import tb._ - implicit val cap: Cap = null - - def foo(tree: Tree): Int = (tree: Any) match { - case tb.Apply(fun, args) => 3 // error, but error message is wrong - } - - def bar(tree: tpd.Tree): Int = (tree: Any) match { - case Apply(fun, args) => 3 // error, but error message is wrong - } -} diff --git a/tests/pos/i2378.scala b/tests/pos/i2378.scala index 26e95207c270..0dba1b951b3f 100644 --- a/tests/pos/i2378.scala +++ b/tests/pos/i2378.scala @@ -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])] @@ -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 } -} \ No newline at end of file +} diff --git a/tests/pos/i7401.scala b/tests/pos/i7401.scala new file mode 100644 index 000000000000..fea4bc18e04b --- /dev/null +++ b/tests/pos/i7401.scala @@ -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) +} \ No newline at end of file