Skip to content

Commit

Permalink
Fast-track deprecating nullary method eta-expansion
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dwijnand committed Mar 27, 2020
1 parent bb894b2 commit 12a36b2
Show file tree
Hide file tree
Showing 15 changed files with 50 additions and 22 deletions.
8 changes: 8 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
Expand Up @@ -459,6 +459,14 @@ trait ContextErrors {
}

//typedEta
def UnderscoreNullaryEtaMessage(tpe: Type) =
s"Only function types can be followed by _ but the current expression has type $tpe"

def UnderscoreNullaryEtaError(tree: Tree, tpe: Type) = {
issueNormalTypeError(tree, UnderscoreNullaryEtaMessage(tpe))
setError(tree)
}

def UnderscoreEtaError(tree: Tree) = {
issueNormalTypeError(tree, "_ must follow method; cannot follow " + tree.tpe)
setError(tree)
Expand Down
10 changes: 9 additions & 1 deletion src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -4916,7 +4916,15 @@ 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)
val restpe = result.tpe.typeArgs.headOption.getOrElse(methodValue.tpe)

if (currentRun.isScala3) {
UnderscoreNullaryEtaError(methodValue, restpe)
} else {
context.deprecationWarning(pos, NoSymbol, UnderscoreNullaryEtaMessage(restpe), "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: Only function types can be followed by _ but the current expression has type Int
val t7 = m1 _ // error: Only function types can be followed by _ but the current expression has type Int
^
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: Only function types can be followed by _ but the current expression has type Int
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: Only function types can be followed by _ but the current expression has type Int
val t7 = m1 _ // error: Only function types can be followed by _ but the current expression has type Int
^
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: Only function types can be followed by _ but the current expression has type Int
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: Only function types can be followed by _ but the current expression has type String
val t2c: () => Any = bar _ // warning: Only function types can be followed by _ but the current expression has type String
^
t7187.scala:22: warning: Only function types can be followed by _ but the current expression has type String
val t2d: Any = bar _ // warning: Only function types can be followed by _ but the current expression has type String
^
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: Only function types can be followed by _ but the current expression has type String
val t2d: Any = bar _ // warning: Only function types can be followed by _ but the current expression has type String
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

0 comments on commit 12a36b2

Please sign in to comment.