From 251514b2d1489fdfa71a19161814e74d6d192810 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Fri, 20 Jul 2018 18:52:55 +0200 Subject: [PATCH 1/8] Fix #4364: Try SAM type when no candidates found --- .../dotty/tools/dotc/core/Definitions.scala | 2 ++ .../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, 50 insertions(+), 2 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 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..1fb526d2d0e8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1517,7 +1517,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 98fc833b2525333aba07ca18d3a706e7c7467ad3 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 11:51:47 +0200 Subject: [PATCH 2/8] 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 1fb526d2d0e8..21fbb86b8f5d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1364,7 +1364,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` @@ -1409,9 +1409,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 @@ -1423,7 +1423,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 @@ -1511,7 +1511,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.tpes) + resolveOverloaded(alts1, pt1, targs1.tpes, pos) case defn.FunctionOf(args, resultType, _, _) => narrowByTypes(alts, args, resultType) @@ -1519,11 +1519,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 } @@ -1536,7 +1544,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 e35b0c9bba68..0c3f59da6697 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2268,7 +2268,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 b77c21dab8ed4af531fe967e6c5c56b33a26e8d3 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 13:08:18 +0200 Subject: [PATCH 3/8] 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 0d481d53188311c6a69433b3b6565c4b33826992 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 16:59:43 +0200 Subject: [PATCH 4/8] fix test, change val name, & change warning point --- compiler/src/dotty/tools/dotc/typer/Applications.scala | 8 ++++---- compiler/src/dotty/tools/dotc/typer/Typer.scala | 8 +++++--- tests/neg-custom-args/fatal-warnings/i4364.scala | 9 ++------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 21fbb86b8f5d..331f95077550 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1364,7 +1364,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` @@ -1409,9 +1409,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 @@ -1423,7 +1423,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 diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0c3f59da6697..06488e58257e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2268,7 +2268,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 => @@ -2480,9 +2480,11 @@ class Typer extends Namer !tree.symbol.isConstructor && !tree.symbol.is(InlineMethod) && !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 f3f8edef9655bbe99d813b5ce01811ba71afad41 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 18:03:54 +0200 Subject: [PATCH 5/8] 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 06488e58257e..a3e851be12fc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2481,8 +2481,8 @@ class Typer extends Namer !tree.symbol.is(InlineMethod) && !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 c5f533ebefbfce386a9b4abe647724dcd750b660 Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Mon, 23 Jul 2018 23:01:25 +0200 Subject: [PATCH 6/8] 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 a3e851be12fc..76462592f9d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2481,8 +2481,11 @@ class Typer extends Namer !tree.symbol.is(InlineMethod) && !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 6fb6680cb187c3c8a20c6b2b23e3d381ec7440ff Mon Sep 17 00:00:00 2001 From: Jaemin Hong Date: Tue, 24 Jul 2018 23:01:19 +0200 Subject: [PATCH 7/8] 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 76462592f9d4..7842b45bfc69 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2481,11 +2481,10 @@ class Typer extends Namer !tree.symbol.is(InlineMethod) && !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)) From 0cc60eb4ce780a34ffc0fe56dfbf4cabfba389ee Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Jan 2019 15:45:36 +0100 Subject: [PATCH 8/8] Simplifications --- .../dotty/tools/dotc/typer/Applications.scala | 20 ++++++++----------- .../src/dotty/tools/dotc/typer/Typer.scala | 10 ++++++---- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 331f95077550..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 @@ -1511,14 +1511,14 @@ 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.tpes, pos) + resolveOverloaded(alts1, pt1, targs1.tpes) case defn.FunctionOf(args, resultType, _, _) => narrowByTypes(alts, args, resultType) case pt => - val noSam = alts filter (normalizedCompatible(_, pt)) - if (noSam.isEmpty) { + 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 @@ -1528,14 +1528,10 @@ trait Applications extends Compatibility { self: Typer with Dynamic => * 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"${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 _ => compat } - } else noSam + else compat } val found = narrowMostSpecific(candidates) if (found.length <= 1) found @@ -1544,7 +1540,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 7842b45bfc69..65a9e3882695 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2481,10 +2481,12 @@ class Typer extends Namer !tree.symbol.is(InlineMethod) && !ctx.mode.is(Mode.Pattern) && !(isSyntheticApply(tree) && !isExpandableApply)) { - 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) + 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)) readaptSimplified(tpd.Apply(tree, Nil))