Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eta-expansion, via trailing underscore, of methods with no argument lists is now deprecated #8836

Merged
merged 1 commit into from Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion test/files/neg/t7187-3.check
Expand Up @@ -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
Expand All @@ -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
4 changes: 2 additions & 2 deletions test/files/neg/t7187-3.scala
Expand Up @@ -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
}
5 changes: 4 additions & 1 deletion test/files/neg/t7187-deprecation.check
Expand Up @@ -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.
Expand All @@ -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
4 changes: 2 additions & 2 deletions test/files/neg/t7187-deprecation.scala
Expand Up @@ -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

Expand Down
8 changes: 7 additions & 1 deletion test/files/neg/t7187.check
Expand Up @@ -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
Expand All @@ -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
6 changes: 3 additions & 3 deletions 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 }
Expand All @@ -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() = ""
Expand Down
4 changes: 2 additions & 2 deletions test/files/run/amp.scala
Expand Up @@ -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 })())
Expand Down
2 changes: 1 addition & 1 deletion test/files/run/byname.scala
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions test/files/run/existentials3-new.scala
Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions test/files/run/existentials3-old.scala
Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions 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())
}
}
2 changes: 1 addition & 1 deletion 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!)
Expand Down
2 changes: 1 addition & 1 deletion 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)
Expand Down