Skip to content

Commit 6c734fb

Browse files
committed
Third attempt: Model SAM conversions precisely
Applicability testing as used in overloading resolution now considers a SAM conversion only if the argument is a lambda (possibly an eta-converted one). This matches exactly what normal adaptation does. It turns #11899 into a pos test (now part of i11938.scala). That is Scala 3 will fallback to a generic alternative where Scala 2 failed with a type error.
1 parent 1d62597 commit 6c734fb

File tree

3 files changed

+27
-24
lines changed

3 files changed

+27
-24
lines changed

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

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -629,17 +629,10 @@ trait Applications extends Compatibility {
629629

630630
def applyKind = ApplyKind.Regular
631631

632-
/** Is `tp1` a function type that can be converted by a SAM comversion to `tp2`,
633-
* and that is not at the same time a supertype of `tp2`?
632+
/** Is `arg` with widened type `argType` a function literal that can be converted by a SAM
633+
* conversion to `pt`? Overridden in ApplicableToTrees.
634634
*/
635-
private def isSamCompatible(tp1: Type, tp2: Type)(using Context): Boolean =
636-
defn.isFunctionType(tp1)
637-
&& {
638-
tp2.match
639-
case SAMType(sam) => tp1 <:< sam.toFunctionType(isJava = tp2.classSymbol.is(JavaDefined))
640-
case _ => false
641-
}
642-
&& !(tp2 relaxed_<:< tp1)
635+
def isSamCompatible(arg: Arg, argType: Type, pt: Type)(using Context): Boolean = false
643636

644637
protected def argOK(arg: TypedArg, formal: Type): Boolean = argType(arg, formal) match
645638
case ref: TermRef if ref.denot.isOverloaded =>
@@ -652,18 +645,15 @@ trait Applications extends Compatibility {
652645
&& formal.match
653646
case SAMType(sam) => argtpe <:< sam.toFunctionType(isJava = formal.classSymbol.is(JavaDefined))
654647
case _ => false
655-
if argMatch == ArgMatch.SubType then
656-
(argtpe relaxed_<:< formal.widenExpr)
657-
|| isSamCompatible(argtpe.widen, formal)
658-
else
659-
isCompatible(argtpe, formal)
660-
|| isSamCompatible(argtpe.widen, formal)
661-
|| argMatch == ArgMatch.CompatibleCAP
662-
&& {
663-
val argtpe1 = argtpe.widen
664-
val captured = captureWildcards(argtpe1)
665-
(captured ne argtpe1) && isCompatible(captured, formal.widenExpr)
666-
}
648+
( if argMatch == ArgMatch.SubType then argtpe relaxed_<:< formal.widenExpr
649+
else isCompatible(argtpe, formal) )
650+
|| isSamCompatible(arg, argtpe.widen, formal)
651+
|| argMatch == ArgMatch.CompatibleCAP
652+
&& {
653+
val argtpe1 = argtpe.widen
654+
val captured = captureWildcards(argtpe1)
655+
(captured ne argtpe1) && isCompatible(captured, formal.widenExpr)
656+
}
667657

668658
/** The type of the given argument */
669659
protected def argType(arg: Arg, formal: Type): Type
@@ -692,6 +682,14 @@ trait Applications extends Compatibility {
692682
def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg)
693683
def typeOfArg(arg: Tree): Type = arg.tpe
694684
def harmonizeArgs(args: List[Tree]): List[Tree] = harmonize(args)
685+
686+
override def isSamCompatible(arg: Tree, argType: Type, pt: Type)(using Context): Boolean =
687+
arg match
688+
case closure(_) =>
689+
pt match
690+
case SAMType(sam) => argType <:< sam.toFunctionType(isJava = pt.classSymbol.is(JavaDefined))
691+
case _ => false
692+
case _ => false
695693
}
696694

697695
/** Subclass of Application for applicability tests with value argument types. */

tests/run/i11938.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
42
2+
generic
3+
42

tests/run/i11938.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import java.util.function.Function
22

33
class Future[T](val initial: T) {
4-
def map[V](v: V): Unit = println(v)
5-
//def map(v: String): Unit = println(v)
4+
def map[V](v: V): Unit = println("generic")
65
def map[U](fn: Function[T, U]): Unit = println(fn(initial))
76
}
87

98
object Test {
109
val f = new Future(42)
1110
val fn = (i: Int) => i.toString
11+
def fn2(i: Int) = i.toString
1212
def main(args: Array[String]): Unit =
1313
f.map((i: Int) => i.toString)
14+
f.map(fn)
15+
f.map(fn2)
1416
}

0 commit comments

Comments
 (0)