Skip to content

Commit

Permalink
Merge pull request #5717 from dotty-staging/fix-issue4364
Browse files Browse the repository at this point in the history
Fix #4364: Try SAM type when no candidates are found
  • Loading branch information
odersky committed Jan 16, 2019
2 parents 4aa00a7 + 0cc60eb commit c1b8bd0
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 5 deletions.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 16 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2481,9 +2481,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)
Expand Down
9 changes: 9 additions & 0 deletions tests/neg-custom-args/fatal-warnings/i4364.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
2 changes: 1 addition & 1 deletion tests/neg/i2033.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ()
Expand Down
13 changes: 13 additions & 0 deletions tests/run/i4364a.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
12 changes: 12 additions & 0 deletions tests/run/i4364b.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit c1b8bd0

Please sign in to comment.