Skip to content

Commit c1b8bd0

Browse files
authored
Merge pull request #5717 from dotty-staging/fix-issue4364
Fix #4364: Try SAM type when no candidates are found
2 parents 4aa00a7 + 0cc60eb commit c1b8bd0

File tree

7 files changed

+61
-5
lines changed

7 files changed

+61
-5
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,8 @@ class Definitions {
816816
def SetterMetaAnnot(implicit ctx: Context): ClassSymbol = SetterMetaAnnotType.symbol.asClass
817817
lazy val ShowAsInfixAnotType: TypeRef = ctx.requiredClassRef("scala.annotation.showAsInfix")
818818
def ShowAsInfixAnnot(implicit ctx: Context): ClassSymbol = ShowAsInfixAnotType.symbol.asClass
819+
lazy val FunctionalInterfaceAnnotType = ctx.requiredClassRef("java.lang.FunctionalInterface")
820+
def FunctionalInterfaceAnnot(implicit ctx: Context) = FunctionalInterfaceAnnotType.symbol.asClass
819821

820822
// convenient one-parameter method types
821823
def methOfAny(tp: Type): MethodType = MethodType(List(AnyType), tp)

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ object Applications {
174174
def productArity: Int = app.productArity
175175
def productElement(n: Int): Any = app.productElement(n)
176176
}
177-
177+
178178
/** The unapply method of this extractor also recognizes ExtMethodApplys in closure blocks.
179179
* This is necessary to deal with closures as left arguments of extension method applications.
180180
* A test case is i5606.scala
@@ -1517,7 +1517,21 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
15171517
narrowByTypes(alts, args, resultType)
15181518

15191519
case pt =>
1520-
alts filter (normalizedCompatible(_, pt))
1520+
val compat = alts.filter(normalizedCompatible(_, pt))
1521+
if (compat.isEmpty)
1522+
/*
1523+
* the case should not be moved to the enclosing match
1524+
* since SAM type must be considered only if there are no candidates
1525+
* For example, the second f should be chosen for the following code:
1526+
* def f(x: String): Unit = ???
1527+
* def f: java.io.OutputStream = ???
1528+
* new java.io.ObjectOutputStream(f)
1529+
*/
1530+
pt match {
1531+
case SAMType(mtp) => narrowByTypes(alts, mtp.paramInfos, mtp.resultType)
1532+
case _ => compat
1533+
}
1534+
else compat
15211535
}
15221536
val found = narrowMostSpecific(candidates)
15231537
if (found.length <= 1) found

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,9 +2481,15 @@ class Typer extends Namer
24812481
!tree.symbol.isConstructor &&
24822482
!tree.symbol.is(InlineMethod) &&
24832483
!ctx.mode.is(Mode.Pattern) &&
2484-
!(isSyntheticApply(tree) && !isExpandableApply))
2484+
!(isSyntheticApply(tree) && !isExpandableApply)) {
2485+
if (!defn.isFunctionType(pt))
2486+
pt match {
2487+
case SAMType(_) if !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) =>
2488+
ctx.warning(ex"${tree.symbol} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", tree.pos)
2489+
case _ =>
2490+
}
24852491
simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked)
2486-
else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
2492+
} else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
24872493
readaptSimplified(tpd.Apply(tree, Nil))
24882494
else if (wtp.isImplicitMethod)
24892495
err.typeMismatch(tree, pt)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Test {
2+
def foo(c: java.util.function.Consumer[Integer]) = c.accept(0)
3+
def f(x: Int): Unit = ()
4+
5+
def main(args: Array[String]) = {
6+
foo(f) // Ok: Consumer is @FunctionalInterface
7+
new java.io.ObjectOutputStream(f) // error: OutputStream is not @FunctionalInterface
8+
}
9+
}

tests/neg/i2033.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import collection._
33
object Test {
44
def check(obj: AnyRef): Unit = {
55
val bos = new ByteArrayOutputStream()
6-
val out = new ObjectOutputStream(println) // error
6+
val out = new ObjectOutputStream(println)
77
val arr = bos toByteArray ()
88
val in = (())
99
val deser = ()

tests/run/i4364a.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import java.util.function.Consumer
2+
3+
object Test {
4+
def f(): Unit = assert(false)
5+
def f(x: Int): Unit = assert(false)
6+
def f(x: String): Unit = ()
7+
8+
def foo(c: Consumer[String]) = c.accept("")
9+
10+
def main(args: Array[String]) = {
11+
foo(f)
12+
}
13+
}

tests/run/i4364b.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import java.util.function.Consumer
2+
3+
object Test {
4+
def f(x: String): Unit = assert(false)
5+
def f: Consumer[String] = new Consumer { def accept(s: String) = () }
6+
7+
def foo(c: Consumer[String]) = c.accept("")
8+
9+
def main(args: Array[String]) = {
10+
foo(f)
11+
}
12+
}

0 commit comments

Comments
 (0)