From 0ef2206cb9e0747c4414fe2566c91bbd31a0b23a Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Fri, 20 Jul 2018 18:52:55 +0200 Subject: [PATCH 1/7] Fix #4364: Try SAM type when no candidates found --- .../dotty/tools/dotc/core/Definitions.scala | 6 +++-- .../dotty/tools/dotc/typer/Applications.scala | 12 +++++++++- tests/neg/i2033.scala | 2 +- tests/run/i4364a.scala | 13 +++++++++++ tests/run/i4364b.scala | 23 +++++++++++++++++++ 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 tests/run/i4364a.scala create mode 100644 tests/run/i4364b.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index d8e9ebef4258..d45a0d0ad58a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -782,8 +782,10 @@ class Definitions { def GetterMetaAnnot(implicit ctx: Context) = GetterMetaAnnotType.symbol.asClass lazy val SetterMetaAnnotType = ctx.requiredClassRef("scala.annotation.meta.setter") def SetterMetaAnnot(implicit ctx: Context) = SetterMetaAnnotType.symbol.asClass - lazy val ShowAsInfixAnotType = ctx.requiredClassRef("scala.annotation.showAsInfix") - def ShowAsInfixAnnot(implicit ctx: Context) = ShowAsInfixAnotType.symbol.asClass + lazy val ShowAsInfixAnnotType = ctx.requiredClassRef("scala.annotation.showAsInfix") + def ShowAsInfixAnnot(implicit ctx: Context) = ShowAsInfixAnnotType.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(List(AnyType), tp) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index c74c0a1d05c8..54fea2b718b9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1430,7 +1430,17 @@ trait Applications extends Compatibility { self: Typer with Dynamic => narrowByTypes(alts, args, resultType) case pt => - alts filter (normalizedCompatible(_, pt)) + val noSam = alts filter (normalizedCompatible(_, pt)) + if (noSam.isEmpty) { + pt match { + case SAMType(mtp) => + val sam = narrowByTypes(alts, mtp.paramInfos, mtp.resultType) + if (sam.nonEmpty && !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot)) + ctx.warning(ex"$pt does not have the @FunctionalInterface annotation.", ctx.tree.pos) + sam + case _ => noSam + } + } else noSam } val found = narrowMostSpecific(candidates) if (found.length <= 1) found 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..ca57331d83a1 --- /dev/null +++ b/tests/run/i4364a.scala @@ -0,0 +1,13 @@ +object Test { + var flag = false + + def f(): Unit = assert(false) + def f(x: Int): Unit = assert(false) + def f(x: String): Unit = flag = true + + def foo(c: java.util.function.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..7337211b445c --- /dev/null +++ b/tests/run/i4364b.scala @@ -0,0 +1,23 @@ +object Test { + var flag = false + + def f(x: Int): Unit = assert(false) + def f(x: String): Unit = assert(false) + def f: java.io.OutputStream = new java.io.OutputStream { + def write(x: Int) = () + } + + def g(x: Int): Unit = flag = true + def g(x: String): Unit = assert(false) + + def main(args: Array[String]) = { + val oosF = new java.io.ObjectOutputStream(f) + oosF.write(0) + oosF.close() + + val oosG = new java.io.ObjectOutputStream(g) // need warning + oosG.write(0) + oosG.close() + assert(flag) + } +} From 770e49f4a59c96e56dd7b119d8bab6a2cd44b585 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 11:51:47 +0200 Subject: [PATCH 2/7] better warning, fix tests, & add comments --- .../dotty/tools/dotc/typer/Applications.scala | 22 +++++++++++++------ .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../fatal-warnings/i4364.scala | 14 ++++++++++++ tests/run/i4364a.scala | 4 +--- tests/run/i4364b.scala | 16 +++----------- 5 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i4364.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 54fea2b718b9..efd77df94c71 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1277,7 +1277,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * Two trials: First, without implicits or SAM conversions enabled. Then, * if the fist finds no eligible candidates, with implicits and SAM conversions enabled. */ - def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { + def resolveOverloaded(alts: List[TermRef], pt: Type, pos: Position = NoPosition)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { /** Is `alt` a method or polytype whose result type after the first value parameter * section conforms to the expected type `resultType`? If `resultType` @@ -1322,9 +1322,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case _ => chosen } - var found = resolveOverloaded(alts, pt, Nil)(ctx.retractMode(Mode.ImplicitsEnabled)) + var found = resolveOverloaded(alts, pt, Nil, pos)(ctx.retractMode(Mode.ImplicitsEnabled)) if (found.isEmpty && ctx.mode.is(Mode.ImplicitsEnabled)) - found = resolveOverloaded(alts, pt, Nil) + found = resolveOverloaded(alts, pt, Nil, pos) found match { case alt :: Nil => adaptByResult(alt) :: Nil case _ => found @@ -1336,7 +1336,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * called twice from the public `resolveOverloaded` method, once with * implicits and SAM conversions enabled, and once without. */ - private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { + private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type], pos: Position)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty @@ -1424,7 +1424,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case pt @ PolyProto(targs1, pt1) if targs.isEmpty => val alts1 = alts filter pt.isMatchedBy - resolveOverloaded(alts1, pt1, targs1) + resolveOverloaded(alts1, pt1, targs1, pos) case defn.FunctionOf(args, resultType, _, _) => narrowByTypes(alts, args, resultType) @@ -1432,11 +1432,19 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case pt => val noSam = alts filter (normalizedCompatible(_, pt)) if (noSam.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) => val sam = narrowByTypes(alts, mtp.paramInfos, mtp.resultType) if (sam.nonEmpty && !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot)) - ctx.warning(ex"$pt does not have the @FunctionalInterface annotation.", ctx.tree.pos) + ctx.warning(ex"${sam.head.designator} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", pos) sam case _ => noSam } @@ -1449,7 +1457,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => if (noDefaults.length == 1) noDefaults // return unique alternative without default parameters if it exists else { val deepPt = pt.deepenProto - if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) + if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs, pos) else alts } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b367146e3ffc..aa4dc14f89fc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2154,7 +2154,7 @@ class Typer extends Namer val altDenots = ref.denot.alternatives typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%, %") val alts = altDenots.map(TermRef(ref.prefix, ref.name, _)) - resolveOverloaded(alts, pt) match { + resolveOverloaded(alts, pt, tree.pos) match { case alt :: Nil => readaptSimplified(tree.withType(alt)) case Nil => 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..e03b436e25de --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i4364.scala @@ -0,0 +1,14 @@ +object Test { + def foo(c: java.util.function.Consumer[String]) = c.accept("") + + def f(x: String): Unit = () + def f(x: Int): Unit = () + + def main(args: Array[String]) = { + foo(f) // Ok: Consumer is @FunctionalInterface + + val oos = new java.io.ObjectOutputStream(f) // error: OutputStream is not @FunctionalInterface + oos.write(0) + oos.close() + } +} diff --git a/tests/run/i4364a.scala b/tests/run/i4364a.scala index ca57331d83a1..e944da5216f1 100644 --- a/tests/run/i4364a.scala +++ b/tests/run/i4364a.scala @@ -1,9 +1,7 @@ object Test { - var flag = false - def f(): Unit = assert(false) def f(x: Int): Unit = assert(false) - def f(x: String): Unit = flag = true + def f(x: String): Unit = () def foo(c: java.util.function.Consumer[String]) = c.accept("") diff --git a/tests/run/i4364b.scala b/tests/run/i4364b.scala index 7337211b445c..51f6e6df3338 100644 --- a/tests/run/i4364b.scala +++ b/tests/run/i4364b.scala @@ -1,23 +1,13 @@ object Test { - var flag = false - def f(x: Int): Unit = assert(false) def f(x: String): Unit = assert(false) def f: java.io.OutputStream = new java.io.OutputStream { def write(x: Int) = () } - def g(x: Int): Unit = flag = true - def g(x: String): Unit = assert(false) - def main(args: Array[String]) = { - val oosF = new java.io.ObjectOutputStream(f) - oosF.write(0) - oosF.close() - - val oosG = new java.io.ObjectOutputStream(g) // need warning - oosG.write(0) - oosG.close() - assert(flag) + val oos = new java.io.ObjectOutputStream(f) + oos.write(0) + oos.close() } } From b4d8776b8207056b268a7641ac82659b538c5f76 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 13:08:18 +0200 Subject: [PATCH 3/7] simpler tests --- tests/run/i4364a.scala | 4 +++- tests/run/i4364b.scala | 13 ++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/run/i4364a.scala b/tests/run/i4364a.scala index e944da5216f1..8bf62e545807 100644 --- a/tests/run/i4364a.scala +++ b/tests/run/i4364a.scala @@ -1,9 +1,11 @@ +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: java.util.function.Consumer[String]) = c.accept("") + 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 index 51f6e6df3338..900d7d331434 100644 --- a/tests/run/i4364b.scala +++ b/tests/run/i4364b.scala @@ -1,13 +1,12 @@ +import java.util.function.Consumer + object Test { - def f(x: Int): Unit = assert(false) def f(x: String): Unit = assert(false) - def f: java.io.OutputStream = new java.io.OutputStream { - def write(x: Int) = () - } + def f: Consumer[String] = new Consumer { def accept(s: String) = () } + + def foo(c: Consumer[String]) = c.accept("") def main(args: Array[String]) = { - val oos = new java.io.ObjectOutputStream(f) - oos.write(0) - oos.close() + foo(f) } } From 5685a9993cf2b68f7e88f7bbf13000b2f8ca9999 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 16:59:43 +0200 Subject: [PATCH 4/7] fix test, change val name, & change warning point --- .../dotty/tools/dotc/typer/Applications.scala | 42 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 8 ++-- .../fatal-warnings/i4364.scala | 9 +--- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index efd77df94c71..0fb8a1ca5e06 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1277,7 +1277,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * Two trials: First, without implicits or SAM conversions enabled. Then, * if the fist finds no eligible candidates, with implicits and SAM conversions enabled. */ - def resolveOverloaded(alts: List[TermRef], pt: Type, pos: Position = NoPosition)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { + def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { /** Is `alt` a method or polytype whose result type after the first value parameter * section conforms to the expected type `resultType`? If `resultType` @@ -1322,9 +1322,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case _ => chosen } - var found = resolveOverloaded(alts, pt, Nil, pos)(ctx.retractMode(Mode.ImplicitsEnabled)) + var found = resolveOverloaded(alts, pt, Nil)(ctx.retractMode(Mode.ImplicitsEnabled)) if (found.isEmpty && ctx.mode.is(Mode.ImplicitsEnabled)) - found = resolveOverloaded(alts, pt, Nil, pos) + found = resolveOverloaded(alts, pt, Nil) found match { case alt :: Nil => adaptByResult(alt) :: Nil case _ => found @@ -1336,7 +1336,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * called twice from the public `resolveOverloaded` method, once with * implicits and SAM conversions enabled, and once without. */ - private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type], pos: Position)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { + private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") { def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty @@ -1424,31 +1424,27 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case pt @ PolyProto(targs1, pt1) if targs.isEmpty => val alts1 = alts filter pt.isMatchedBy - resolveOverloaded(alts1, pt1, targs1, pos) + resolveOverloaded(alts1, pt1, targs1) case defn.FunctionOf(args, resultType, _, _) => narrowByTypes(alts, args, resultType) case pt => - val noSam = alts filter (normalizedCompatible(_, pt)) - if (noSam.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) - */ + val alts1 = alts filter (normalizedCompatible(_, pt)) + if (alts1.isEmpty) { + // We only consider SAM types when there is no alternative. + // In the example below, the second overload of `bar` should + // be preferred over the first one: + // + // def foo(c: Consumer[String]) = ... + // def bar(x: String): Unit = ... + // def bar: Consumer[String] = ... + // foo(bar) pt match { - case SAMType(mtp) => - val sam = narrowByTypes(alts, mtp.paramInfos, mtp.resultType) - if (sam.nonEmpty && !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot)) - ctx.warning(ex"${sam.head.designator} is eta-expanded even though $pt does not have the @FunctionalInterface annotation.", pos) - sam - case _ => noSam + case SAMType(mtp) => narrowByTypes(alts, mtp.paramInfos, mtp.resultType) + case _ => alts1 } - } else noSam + } else alts1 } val found = narrowMostSpecific(candidates) if (found.length <= 1) found @@ -1457,7 +1453,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => if (noDefaults.length == 1) noDefaults // return unique alternative without default parameters if it exists else { val deepPt = pt.deepenProto - if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs, pos) + if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) else alts } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index aa4dc14f89fc..d3aa3406f0e1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2154,7 +2154,7 @@ class Typer extends Namer val altDenots = ref.denot.alternatives typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%, %") val alts = altDenots.map(TermRef(ref.prefix, ref.name, _)) - resolveOverloaded(alts, pt, tree.pos) match { + resolveOverloaded(alts, pt) match { case alt :: Nil => readaptSimplified(tree.withType(alt)) case Nil => @@ -2353,9 +2353,11 @@ class Typer extends Namer !tree.symbol.isConstructor && !tree.symbol.is(TransparentMethod) && !ctx.mode.is(Mode.Pattern) && - !(isSyntheticApply(tree) && !isExpandableApply)) + !(isSyntheticApply(tree) && !isExpandableApply)) { + 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) 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 index e03b436e25de..5ec3f9cd169d 100644 --- a/tests/neg-custom-args/fatal-warnings/i4364.scala +++ b/tests/neg-custom-args/fatal-warnings/i4364.scala @@ -1,14 +1,9 @@ object Test { - def foo(c: java.util.function.Consumer[String]) = c.accept("") - - def f(x: String): Unit = () + 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 - - val oos = new java.io.ObjectOutputStream(f) // error: OutputStream is not @FunctionalInterface - oos.write(0) - oos.close() + new java.io.ObjectOutputStream(f) // error: OutputStream is not @FunctionalInterface } } From 653b2e4187823aec4d80c06b2fd74fcd344432c2 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 18:03:54 +0200 Subject: [PATCH 5/7] check function type before warning --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d3aa3406f0e1..8b673d43dd71 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2354,8 +2354,8 @@ class Typer extends Namer !tree.symbol.is(TransparentMethod) && !ctx.mode.is(Mode.Pattern) && !(isSyntheticApply(tree) && !isExpandableApply)) { - 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) + if (!defn.isFunctionType(pt) && !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot)) + ctx.warning(ex"${tree.symbol} is eta-expanded even though ${pt.classSymbol} does not have the @FunctionalInterface annotation.", tree.pos) simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked) } else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol)) readaptSimplified(tpd.Apply(tree, Nil)) From f5758a5a9ddfc8152002a786bc1862bd8a85e855 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 23:01:25 +0200 Subject: [PATCH 6/7] check SAM type also --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8b673d43dd71..904897fadf87 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2354,8 +2354,11 @@ class Typer extends Namer !tree.symbol.is(TransparentMethod) && !ctx.mode.is(Mode.Pattern) && !(isSyntheticApply(tree) && !isExpandableApply)) { - if (!defn.isFunctionType(pt) && !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot)) - ctx.warning(ex"${tree.symbol} is eta-expanded even though ${pt.classSymbol} does not have the @FunctionalInterface annotation.", tree.pos) + pt match { + case SAMType(_) if !defn.isFunctionType(pt) && !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) => + ctx.warning(ex"${tree.symbol} is eta-expanded even though ${pt.classSymbol} 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)) readaptSimplified(tpd.Apply(tree, Nil)) From ac97d906ff6340375fbe6ff194554362fa4b3414 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Tue, 24 Jul 2018 23:01:19 +0200 Subject: [PATCH 7/7] reordered checks --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 904897fadf87..08541f5747bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2354,11 +2354,10 @@ class Typer extends Namer !tree.symbol.is(TransparentMethod) && !ctx.mode.is(Mode.Pattern) && !(isSyntheticApply(tree) && !isExpandableApply)) { - pt match { - case SAMType(_) if !defn.isFunctionType(pt) && !pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) => - ctx.warning(ex"${tree.symbol} is eta-expanded even though ${pt.classSymbol} does not have the @FunctionalInterface annotation.", tree.pos) - case _ => - } + if (!pt.classSymbol.hasAnnotation(defn.FunctionalInterfaceAnnot) && + !defn.isFunctionType(pt) && + SAMType.unapply(pt).isDefined) + ctx.warning(ex"${tree.symbol} is eta-expanded even though ${pt.classSymbol} does not have the @FunctionalInterface annotation.", tree.pos) simplify(typed(etaExpand(tree, wtp, arity), pt), pt, locked) } else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol)) readaptSimplified(tpd.Apply(tree, Nil))