From c8a27c7df6828b2d238f0995407da3fc6efa3379 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 27 Mar 2020 09:37:08 +0000 Subject: [PATCH] Fast-track deprecating nullary method eta-expansion Eta-expansion of nullary methods is dropped in Scala 3: https://dotty.epfl.ch/docs/reference/changed-features/eta-expansion.html So it must be deprecated in 2.13 first, and error under -Xsource:3. --- .../scala/tools/nsc/typechecker/ContextErrors.scala | 12 ++++++++++++ .../scala/tools/nsc/typechecker/Typers.scala | 9 ++++++++- test/files/neg/t7187-3.check | 5 ++++- test/files/neg/t7187-3.scala | 4 ++-- test/files/neg/t7187-deprecation.check | 5 ++++- test/files/neg/t7187-deprecation.scala | 4 ++-- test/files/neg/t7187.check | 8 +++++++- test/files/neg/t7187.scala | 6 +++--- test/files/run/amp.scala | 4 ++-- test/files/run/byname.scala | 2 +- test/files/run/existentials3-new.scala | 4 ++-- test/files/run/existentials3-old.scala | 4 ++-- test/files/run/pr7593.scala | 4 ++-- test/files/run/sammy_cbn.scala | 2 +- test/files/run/t1247.scala | 2 +- 15 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 6ab8691820c..6169f74a09c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -459,6 +459,18 @@ trait ContextErrors { } //typedEta + private def mkUnderscoreNullaryEtaMessage(what: String) = + s"Methods without a parameter list and by-name params can $what be converted to functions as `m _`, " + + "write a function literal `() => m` instead" + + final val UnderscoreNullaryEtaWarnMsg = mkUnderscoreNullaryEtaMessage("no longer") + final val UnderscoreNullaryEtaErrorMsg = mkUnderscoreNullaryEtaMessage("not") + + def UnderscoreNullaryEtaError(tree: Tree) = { + issueNormalTypeError(tree, UnderscoreNullaryEtaErrorMsg) + setError(tree) + } + def UnderscoreEtaError(tree: Tree) = { issueNormalTypeError(tree, "_ must follow method; cannot follow " + tree.tpe) setError(tree) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 5a9c2f8a1f7..e661ae91a85 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4916,7 +4916,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val funSym = context.owner.newAnonymousFunctionValue(pos) new ChangeOwnerTraverser(context.owner, funSym) traverse methodValue - typed(Function(List(), methodValue) setSymbol funSym setPos pos, mode, pt) + val result = typed(Function(List(), methodValue) setSymbol funSym setPos pos, mode, pt) + + if (currentRun.isScala3) { + UnderscoreNullaryEtaError(methodValue) + } else { + context.deprecationWarning(pos, NoSymbol, UnderscoreNullaryEtaWarnMsg, "2.13.2") + result + } case ErrorType => methodValue diff --git a/test/files/neg/t7187-3.check b/test/files/neg/t7187-3.check index f5b8c0ad963..315687ad03b 100644 --- a/test/files/neg/t7187-3.check +++ b/test/files/neg/t7187-3.check @@ -3,6 +3,9 @@ t7187-3.scala:13: error: type mismatch; required: () => Any val t1: () => Any = m1 // error ^ +t7187-3.scala:27: error: Methods without a parameter list and by-name params can not be converted to functions as `m _`, write a function literal `() => m` instead + val t7 = m1 _ // error: eta-expanding a nullary method + ^ t7187-3.scala:14: warning: An unapplied 0-arity method was eta-expanded (due to the expected type () => Any), rather than applied to `()`. Write m2() to invoke method m2, or change the expected type. val t2: () => Any = m2 // eta-expanded with lint warning @@ -21,4 +24,4 @@ Write m2() to invoke method m2, or change the expected type. val t2Sam: SamZero = m2 // eta-expanded with lint warning ^ 4 warnings -1 error +2 errors diff --git a/test/files/neg/t7187-3.scala b/test/files/neg/t7187-3.scala index 8b3a082cc35..7dfc7421598 100644 --- a/test/files/neg/t7187-3.scala +++ b/test/files/neg/t7187-3.scala @@ -24,11 +24,11 @@ class EtaExpand214 { val t5a: Int = t5 // ok val t6a: Int => Any = t6 // ok - val t7 = m1 _ + val t7 = m1 _ // error: eta-expanding a nullary method val t8 = m2 _ val t9 = m3 _ - val t7a: () => Any = t7 // ok + val t7a: () => Any = t7 // error: t7 is an error val t8a: () => Any = t8 // ok val t9a: Int => Any = t9 // ok } diff --git a/test/files/neg/t7187-deprecation.check b/test/files/neg/t7187-deprecation.check index f8b339ab82a..6060751e533 100644 --- a/test/files/neg/t7187-deprecation.check +++ b/test/files/neg/t7187-deprecation.check @@ -3,6 +3,9 @@ t7187-deprecation.scala:17: error: type mismatch; required: () => Any val t1: () => Any = m1 // error ^ +t7187-deprecation.scala:31: error: Methods without a parameter list and by-name params can not be converted to functions as `m _`, write a function literal `() => m` instead + val t7 = m1 _ // error: eta-expanding a nullary method + ^ t7187-deprecation.scala:24: warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method m2, or remove the empty argument list from its definition (Java-defined methods are exempt). In Scala 3, an unapplied method like this will be eta-expanded into a function. @@ -14,4 +17,4 @@ In Scala 3, an unapplied method like this will be eta-expanded into a function. a.boom // error ^ 2 warnings -1 error +2 errors diff --git a/test/files/neg/t7187-deprecation.scala b/test/files/neg/t7187-deprecation.scala index e48a7d71f31..a6d160f1d7a 100644 --- a/test/files/neg/t7187-deprecation.scala +++ b/test/files/neg/t7187-deprecation.scala @@ -28,11 +28,11 @@ class EtaExpand214 { val t5a: Int = t5 // ok val t6a: Int => Any = t6 // ok - val t7 = m1 _ + val t7 = m1 _ // error: eta-expanding a nullary method val t8 = m2 _ val t9 = m3 _ - val t7a: () => Any = t7 // ok + val t7a: () => Any = t7 // error: t7 is an error val t8a: () => Any = t8 // ok val t9a: Int => Any = t9 // ok diff --git a/test/files/neg/t7187.check b/test/files/neg/t7187.check index 08ca26ba405..8cb5201a0ca 100644 --- a/test/files/neg/t7187.check +++ b/test/files/neg/t7187.check @@ -26,6 +26,12 @@ t7187.scala:12: warning: An unapplied 0-arity method was eta-expanded (due to th Write foo() to invoke method foo, or change the expected type. val t1b: () => Any = foo // eta-expansion, but lint warning ^ +t7187.scala:21: warning: Methods without a parameter list and by-name params can no longer be converted to functions as `m _`, write a function literal `() => m` instead + val t2c: () => Any = bar _ // warning: eta-expanding a nullary method + ^ +t7187.scala:22: warning: Methods without a parameter list and by-name params can no longer be converted to functions as `m _`, write a function literal `() => m` instead + val t2d: Any = bar _ // warning: eta-expanding a nullary method + ^ t7187.scala:26: warning: An unapplied 0-arity method was eta-expanded (due to the expected type () => Any), rather than applied to `()`. Write baz() to invoke method baz, or change the expected type. val t3a: () => Any = baz // eta-expansion, but lint warning @@ -43,5 +49,5 @@ even though trait AcciSamOne is not annotated with `@FunctionalInterface`; to suppress warning, add the annotation or write out the equivalent function literal. val t5AcciSam: AcciSamOne = zup // ok, but warning ^ -5 warnings +7 warnings 6 errors diff --git a/test/files/neg/t7187.scala b/test/files/neg/t7187.scala index dbd736dead4..69fd0d3ee48 100644 --- a/test/files/neg/t7187.scala +++ b/test/files/neg/t7187.scala @@ -1,4 +1,4 @@ -// scalac: -Xlint:eta-zero -Xlint:eta-sam +// scalac: -deprecation -Xlint:eta-zero -Xlint:eta-sam // trait AcciSamOne { def apply(x: Int): Int } @@ -18,8 +18,8 @@ class EtaExpandZeroArg { def bar = "" val t2a: () => Any = bar // error: no eta-expansion of zero-arglist-methods (nullary methods) val t2b: () => Any = bar() // error: bar doesn't take arguments, so expanded to bar.apply(), which misses an argument - val t2c: () => Any = bar _ // ok - val t2d: Any = bar _ // ok + val t2c: () => Any = bar _ // warning: eta-expanding a nullary method + val t2d: Any = bar _ // warning: eta-expanding a nullary method val t2e: Any = bar() _ // error: not enough arguments for method apply def baz() = "" diff --git a/test/files/run/amp.scala b/test/files/run/amp.scala index a1924ef6370..1276a361c7b 100644 --- a/test/files/run/amp.scala +++ b/test/files/run/amp.scala @@ -2,12 +2,12 @@ object Test extends App { def foo() = { def f: Int = 1 - val x = f _ + val x = () => f x } def bar(g: => Int) = { - g _ + () => g } Console.println((bar{ Console.println("g called"); 42 })()) diff --git a/test/files/run/byname.scala b/test/files/run/byname.scala index e3570622e7d..ba0e1c0dab2 100644 --- a/test/files/run/byname.scala +++ b/test/files/run/byname.scala @@ -8,7 +8,7 @@ def test[A](name: String, expect: A, actual: => A): Unit = { def testNoBraces = 1 test("no braces", 1, testNoBraces) -val testNoBracesR = testNoBraces _ +val testNoBracesR = () => testNoBraces test("no braces r", 1, testNoBracesR()) def testPlain(x: String, y: String): String = x + y diff --git a/test/files/run/existentials3-new.scala b/test/files/run/existentials3-new.scala index 5dfd7fb3945..5420ae11054 100644 --- a/test/files/run/existentials3-new.scala +++ b/test/files/run/existentials3-new.scala @@ -77,6 +77,6 @@ object Misc { def f1 = { trait A extends Seq[U forSome { type U <: Bippy }] ; abstract class B extends A ; trait C extends B ; (null: C) } def f2 = f1.head.bippy } - def g1 = o1.f1 _ - def g2 = o1.f2 _ + def g1 = () => o1.f1 + def g2 = () => o1.f2 } diff --git a/test/files/run/existentials3-old.scala b/test/files/run/existentials3-old.scala index c021c0e71e1..bee1968755d 100644 --- a/test/files/run/existentials3-old.scala +++ b/test/files/run/existentials3-old.scala @@ -70,6 +70,6 @@ object Misc { def f1 = { trait A extends Seq[U forSome { type U <: Bippy }] ; abstract class B extends A ; trait C extends B ; (null: C) } def f2 = f1.head.bippy } - def g1 = o1.f1 _ - def g2 = o1.f2 _ + def g1 = () => o1.f1 + def g2 = () => o1.f2 } diff --git a/test/files/run/pr7593.scala b/test/files/run/pr7593.scala index eac03abf867..5e01d3c37aa 100644 --- a/test/files/run/pr7593.scala +++ b/test/files/run/pr7593.scala @@ -1,7 +1,7 @@ object Test { def main(args: Array[String]): Unit = { def foo = synchronized { "bar" } - val eta = foo _ - println(eta()) + val bar = () => foo + println(bar()) } } diff --git a/test/files/run/sammy_cbn.scala b/test/files/run/sammy_cbn.scala index b84b2fd8e53..f6124441c91 100644 --- a/test/files/run/sammy_cbn.scala +++ b/test/files/run/sammy_cbn.scala @@ -1,7 +1,7 @@ trait F0[T] { def apply(): T } object Test extends App { - def delay[T](v: => T) = (v _): F0[T] + def delay[T](v: => T) = (() => v): F0[T] // should not fail with ClassCastException: $$Lambda$6279/897871870 cannot be cast to F0 // (also, should not say boe!) diff --git a/test/files/run/t1247.scala b/test/files/run/t1247.scala index 9a511c25d34..ac262ebd310 100644 --- a/test/files/run/t1247.scala +++ b/test/files/run/t1247.scala @@ -1,7 +1,7 @@ object Test extends App { val f = () => 5 def test(g: => Int): Unit = { - val gFunc = g _ + val gFunc = () => g val isSameClosureClass = gFunc.getClass == f.getClass val isSame = gFunc eq f println("Is same closure class: "+isSameClosureClass+" is same closure: "+isSame)