diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index a3f4191b7af1..e6c72c49b728 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -816,6 +816,8 @@ class Definitions { def SetterMetaAnnot(implicit ctx: Context): ClassSymbol = SetterMetaAnnotType.symbol.asClass lazy val ShowAsInfixAnotType: TypeRef = ctx.requiredClassRef("scala.annotation.showAsInfix") def ShowAsInfixAnnot(implicit ctx: Context): ClassSymbol = ShowAsInfixAnotType.symbol.asClass + lazy val FunctionalInterfaceAnnotType = ctx.requiredClassRef("java.lang.FunctionalInterface") + def FunctionalInterfaceAnnot(implicit ctx: Context) = FunctionalInterfaceAnnotType.symbol.asClass // convenient one-parameter method types def methOfAny(tp: Type): MethodType = MethodType(List(AnyType), tp) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 438249f89124..a0793360827c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -174,7 +174,7 @@ object Applications { def productArity: Int = app.productArity def productElement(n: Int): Any = app.productElement(n) } - + /** The unapply method of this extractor also recognizes ExtMethodApplys in closure blocks. * This is necessary to deal with closures as left arguments of extension method applications. * A test case is i5606.scala @@ -1517,7 +1517,21 @@ trait Applications extends Compatibility { self: Typer with Dynamic => narrowByTypes(alts, args, resultType) case pt => - alts filter (normalizedCompatible(_, pt)) + val compat = alts.filter(normalizedCompatible(_, pt)) + if (compat.isEmpty) + /* + * the case should not be moved to the enclosing match + * since SAM type must be considered only if there are no candidates + * For example, the second f should be chosen for the following code: + * def f(x: String): Unit = ??? + * def f: java.io.OutputStream = ??? + * new java.io.ObjectOutputStream(f) + */ + pt match { + case SAMType(mtp) => narrowByTypes(alts, mtp.paramInfos, mtp.resultType) + case _ => compat + } + else compat } val found = narrowMostSpecific(candidates) if (found.length <= 1) found diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e35b0c9bba68..65a9e3882695 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2480,9 +2480,15 @@ class Typer extends Namer !tree.symbol.isConstructor && !tree.symbol.is(InlineMethod) && !ctx.mode.is(Mode.Pattern) && - !(isSyntheticApply(tree) && !isExpandableApply)) + !(isSyntheticApply(tree) && !isExpandableApply)) { + if (!defn.isFunctionType(pt)) + pt match { + case SAMType(_) if !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) => + ctx.warning(ex"${tree.symbol} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", tree.pos) + case _ => + } simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked) - else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol)) + } else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol)) readaptSimplified(tpd.Apply(tree, Nil)) else if (wtp.isImplicitMethod) err.typeMismatch(tree, pt) diff --git a/tests/neg-custom-args/fatal-warnings/i4364.scala b/tests/neg-custom-args/fatal-warnings/i4364.scala new file mode 100644 index 000000000000..5ec3f9cd169d --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i4364.scala @@ -0,0 +1,9 @@ +object Test { + def foo(c: java.util.function.Consumer[Integer]) = c.accept(0) + def f(x: Int): Unit = () + + def main(args: Array[String]) = { + foo(f) // Ok: Consumer is @FunctionalInterface + new java.io.ObjectOutputStream(f) // error: OutputStream is not @FunctionalInterface + } +} diff --git a/tests/neg/i2033.scala b/tests/neg/i2033.scala index b28a0d99e5be..c21bf80d35dc 100644 --- a/tests/neg/i2033.scala +++ b/tests/neg/i2033.scala @@ -3,7 +3,7 @@ import collection._ object Test { def check(obj: AnyRef): Unit = { val bos = new ByteArrayOutputStream() - val out = new ObjectOutputStream(println) // error + val out = new ObjectOutputStream(println) val arr = bos toByteArray () val in = (()) val deser = () diff --git a/tests/run/i4364a.scala b/tests/run/i4364a.scala new file mode 100644 index 000000000000..8bf62e545807 --- /dev/null +++ b/tests/run/i4364a.scala @@ -0,0 +1,13 @@ +import java.util.function.Consumer + +object Test { + def f(): Unit = assert(false) + def f(x: Int): Unit = assert(false) + def f(x: String): Unit = () + + def foo(c: Consumer[String]) = c.accept("") + + def main(args: Array[String]) = { + foo(f) + } +} diff --git a/tests/run/i4364b.scala b/tests/run/i4364b.scala new file mode 100644 index 000000000000..900d7d331434 --- /dev/null +++ b/tests/run/i4364b.scala @@ -0,0 +1,12 @@ +import java.util.function.Consumer + +object Test { + def f(x: String): Unit = assert(false) + def f: Consumer[String] = new Consumer { def accept(s: String) = () } + + def foo(c: Consumer[String]) = c.accept("") + + def main(args: Array[String]) = { + foo(f) + } +}