Skip to content

Commit 1005733

Browse files
committed
Treat functional interfaces specially in overloading resolution
Overloading resolution had two stages to determine when an alternative is applicable" 1. Match with subtyping only 2. Match with implicit conversions and SAM conversions If some alternatives are eligible under (1), only those alternatives are considered. If no alternatives are eligible under (1), alternatives are searched using (2). This behavior is different from Scala 2, which seems to use SAM conversions more aggressively. I am not sure what Scala 2 does. One obvious change would be to allow SAM conversions under (1). But I am reluctant to do that, since a SAM conversion can easily be introduced by accident. For instance, we might have two overloaded variants like this: ```scala def foo(x: C) = ... def foo(x: A => B) = ... foo((x: A) => x.toB) ``` Now, if `C` happens to be a SAM type that has an abstract method from A to B, this would be ambiguous. Generally, it feels like a SAM conversion could too easily be considered by accident here. On the other hand, if `C` was marked with `@FunctionalInterface` it would explicitly expect function arguments, so then we should treat it as morally equivalent to a function type in Scala. This is what this commit does. It refines the priority for searching applicable methods as follows: 1. Match with subtyping and with SAM conversions to functional interfaces 2. Match with implicit conversions and SAM conversions to other types
1 parent cadf3bf commit 1005733

File tree

2 files changed

+23
-8
lines changed

2 files changed

+23
-8
lines changed

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -635,15 +635,16 @@ trait Applications extends Compatibility {
635635
// matches expected type
636636
false
637637
case argtpe =>
638-
def SAMargOK = formal match {
639-
case SAMType(sam) => argtpe <:< sam.toFunctionType(isJava = formal.classSymbol.is(JavaDefined))
640-
case _ => false
641-
}
638+
def SAMargOK(onlyFunctionalInterface: Boolean) =
639+
(!onlyFunctionalInterface || formal.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot))
640+
&& formal.match
641+
case SAMType(sam) => argtpe <:< sam.toFunctionType(isJava = formal.classSymbol.is(JavaDefined))
642+
case _ => false
642643
if argMatch == ArgMatch.SubType then
643-
argtpe relaxed_<:< formal.widenExpr
644+
(argtpe relaxed_<:< formal.widenExpr) || SAMargOK(onlyFunctionalInterface = true)
644645
else
645646
isCompatible(argtpe, formal)
646-
|| ctx.mode.is(Mode.ImplicitsEnabled) && SAMargOK
647+
|| ctx.mode.is(Mode.ImplicitsEnabled) && SAMargOK(onlyFunctionalInterface = false)
647648
|| argMatch == ArgMatch.CompatibleCAP
648649
&& {
649650
val argtpe1 = argtpe.widen
@@ -1877,12 +1878,12 @@ trait Applications extends Compatibility {
18771878

18781879
record("resolveOverloaded.FunProto", alts.length)
18791880
val alts1 = narrowBySize(alts)
1880-
//report.log(i"narrowed by size: ${alts1.map(_.symbol.showDcl)}%, %")
1881+
overload.println(i"narrowed by size: ${alts1.map(_.symbol.showDcl)}%, %")
18811882
if isDetermined(alts1) then alts1
18821883
else
18831884
record("resolveOverloaded.narrowedBySize", alts1.length)
18841885
val alts2 = narrowByShapes(alts1)
1885-
//report.log(i"narrowed by shape: ${alts2.map(_.symbol.showDcl)}%, %")
1886+
overload.println(i"narrowed by shape: ${alts2.map(_.symbol.showDcl)}%, %")
18861887
if isDetermined(alts2) then alts2
18871888
else
18881889
record("resolveOverloaded.narrowedByShape", alts2.length)

tests/run/i11938.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import java.util.function.Function
2+
3+
class Future[T](val initial: T) {
4+
def map[V](v: V): Unit = println(v)
5+
//def map(v: String): Unit = println(v)
6+
def map[U](fn: Function[T, U]): Unit = println(fn(initial))
7+
}
8+
9+
object Test {
10+
val f = new Future(42)
11+
val fn = (i: Int) => i.toString
12+
def main(args: Array[String]): Unit =
13+
f.map((i: Int) => i.toString)
14+
}

0 commit comments

Comments
 (0)