You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -78,17 +78,17 @@ Note that the compiler still has quite a bit of magic to perform behind the scen
78
78
79
79
#### Java 8-style lambdas
80
80
81
+
-- TODO: break this up into SAM and IndyLambda
82
+
81
83
Scala 2.12 emits closures in the same style as Java 8, whether they target a `FunctionN` class from the standard library or a user-defined Single Abstract Method (SAM) type. The type checker accepts a function literal as a valid expression for either kind of "function-like" type (built-in or SAM). This improves the experience of using libraries written for Java 8 in Scala.
82
84
83
85
For example, in the REPL:
84
86
85
-
```
86
-
scala> val runRunnable: Runnable = () => println("Run!")
For each lambda the compiler generates a method containing the lambda body, and emits an `invokedynamic` that will spin up a lightweight class for this closure using the JDK's `LambdaMetaFactory`. Note that in the following situations, an anonymous function class is still synthesized at compile-time:
94
94
@@ -115,13 +115,11 @@ For now, we recommend using `-Ypartial-unification` over `-Xexperimental`, as th
115
115
116
116
With [JEP-118](http://openjdk.java.net/jeps/118), parameter names can be stored in class files and be queried at runtime using Java reflection. A quick REPL session shows this in action:
117
117
118
-
```
119
-
scala> case class Person(name: String, age: Int)
120
-
defined class Person
121
-
122
-
scala> val paramNames = classOf[Person].getConstructors.head.getParameters.toList
123
-
paramNames: List[java.lang.reflect.Parameter] = List(final java.lang.String name, final int age)
124
-
```
118
+
scala> case class Person(name: String, age: Int)
119
+
defined class Person
120
+
121
+
scala> val paramNames = classOf[Person].getConstructors.head.getParameters.toList
122
+
paramNames: List[java.lang.reflect.Parameter] = List(final java.lang.String name, final int age)
125
123
126
124
### Tooling improvements
127
125
@@ -143,22 +141,18 @@ The following optimizations are available:
143
141
144
142
For example, the following code
145
143
146
-
```scala
147
-
deff(a: Int, b: Boolean) = (a, b) match {
148
-
case (0, true) =>-1
149
-
case _ if a <0=>-a
150
-
case _ => a
151
-
}
152
-
```
144
+
def f(a: Int, b: Boolean) = (a, b) match {
145
+
case (0, true) => -1
146
+
case _ if a < 0 => -a
147
+
case _ => a
148
+
}
153
149
154
150
produces, when compiled with `-opt:l:method`, the following bytecode (decompiled using [cfr](http://www.benf.org/other/cfr/)):
155
151
156
-
```java
157
-
publicint f(int a, boolean b) {
158
-
int n =0== a &&true== b ?-1: (a <0?- a : a);
159
-
return n;
160
-
}
161
-
```
152
+
public int f(int a, boolean b) {
153
+
int n = 0 == a && true == b ? -1 : (a < 0 ? - a : a);
154
+
return n;
155
+
}
162
156
163
157
The optimizer supports inlining (disabled by default). With `-opt:l:project` code from source files currently being compiled is inlined, while `-opt:l:classpath` enables inlining code from libraries on the compiler's classpath. Other than methods marked [`@inline`](http://www.scala-lang.org/files/archive/api/2.12.0/scala/inline.html), higher-order methods are inlined if the function argument is a lambda, or a parameter of the caller.
164
158
@@ -215,20 +209,101 @@ The Scala library is [free](https://github.com/scala/scala/pull/4443) of [refere
215
209
216
210
## Breaking changes
217
211
218
-
### Lambdas and locks
219
-
Our new lambda encoding lifts the lambda body to a method in the enclosing class. If your code relies on the old behavior, which spins up a new class for each lambda, you may notice this difference because a lambda invocation will now indirect through the instance of the enclosing class (or in case of an object, its module field). When a lock is held on this instance (e.g., during object initialization), deadlocks may occur that did not happen before. One example of this [surprised users of ScalaCheck](https://github.com/rickynils/scalacheck/issues/290) -- now [fixed](https://github.com/rickynils/scalacheck/pull/294).
212
+
### Object initialization locks and lambdas
213
+
214
+
In Scala 2.11, the body of a lambda is in the `apply` method of the anonymous function class generated at compile time. The new lambda encoding in 2.12 lifts the lambda body into a method in the enclosing class. An invocation of the lambda will therefore indirect through the enclosing class, which may cause deadlocks that did not happen before.
215
+
216
+
For example, the following code
217
+
218
+
import scala.concurrent._
219
+
import scala.concurrent.duration._
220
+
import ExecutionContext.Implicits.global
221
+
object O { Await.result(Future(1), 5.seconds) }
222
+
223
+
compiles to (simplified):
224
+
225
+
public final class O$ {
226
+
public static O$ MODULE$;
227
+
public static final int $anonfun$new$1() { return 1; }
Accessing `O` for the first time initializes the `O$` class and executes the static initializer (which invokes the instance constructor). Class initialization is guarded by an initialization lock ([Chapter 5.5 in the JVM specification](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.5)).
236
+
237
+
The main thread locks class initialization and spawns the Future. The Future, executed on a different thread, attempts to execute the static lambda body method `$anonfun$new$1`, which also requires initialization of the class `O$`. Because initialization is locked by the main thread, the thread running the future will block. In the meantime, the main thread continues to run `Await.result`, which will block until the future completes, causing the deadlock.
238
+
239
+
One example of this [surprised the authors of ScalaCheck](https://github.com/rickynils/scalacheck/issues/290) -- now [fixed](https://github.com/rickynils/scalacheck/pull/294).
240
+
241
+
### Lambdas capturing outer instances
242
+
243
+
Because lambda bodies are emitted as methods in the enclosing class, a lambda can capture the outer instance in cases where this did not happen in 2.11. This can affect serialization.
244
+
245
+
The Scala compiler analyzes classes and methods to prevent unnecessary outer captures: unused outer parameters are removed from classes ([#4652](https://github.com/scala/scala/pull/4652)), and methods not accessing any instance members are made static ([#5099](https://github.com/scala/scala/pull/5099)). One known limitation is that the analysis is local to a class and does not cover subclasses.
220
246
221
-
### SAM types
247
+
class C {
248
+
def f = () => {
249
+
class A extends Serializable
250
+
class B extends A
251
+
serialize(new A)
252
+
}
253
+
}
222
254
223
-
Implicit conversion of function types to SAM types won't kick in as often now, since the compiler's own SAM conversion takes priority:
255
+
In this example, the classes `A` and `B` are first lifted into `C`. When flattening the classes to the package level, the `A` obtains an outer pointer to capture the `A` instance. Because `A` has a subclass `B`, the class-level analysis of `A` cannot conclude that the outer parameter is unused (it might be used in `B`).
224
256
225
-
trait MySam { def apply(x: Int): String }
226
-
implicit def unused(fun: Int => String): MySam =
227
-
new MySam { def apply(x: Int) = fun(x) }
228
-
// uses SAM conversion, not the `unused` implicit
229
-
val sammy: MySam = (_: Int).toString
257
+
Serializing the `A` instance attempts to serialize the outer field, which causes a `NotSerializableException: C`.
258
+
259
+
260
+
### SAM conversion precedes implicits
261
+
262
+
The [SAM conversion](http://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#sam-conversion) built into the type system takes priority over implicit conversion of function types to SAM types. This can change the semantics of existing code relying on implicit conversion to SAM types:
val sam1: MySam = () => 2 // Uses SAM conversion, not the implicit
267
+
sam1.i() // Returns 2
268
+
269
+
To retain the old behavior, you may compile under `-Xsource:2.11`, use an explicit call to the conversion method, or disqualify the type from being a SAM (e.g. by adding a second abstract method).
270
+
271
+
Note that SAM conversion only applies to lambda expressions, not to arbitrary expressions with Scala `FunctionN` types:
272
+
273
+
val fun = () => 2 // Type Function0[Int]
274
+
val sam2: MySam = fun // Uses implicit conversion
275
+
sam2.i() // Returns 1
276
+
277
+
278
+
### SAM conversion in overloading resolution
279
+
280
+
In order to improve source compatibility, overloading resolution has been adapted to prefer methods with `Function`-typed arguments over methods with parameters of SAM types. The following example is identical in Scala 2.11 and 2.12:
281
+
282
+
scala> object T {
283
+
| def m(f: () => Unit) = 0
284
+
| def m(r: Runnable) = 1
285
+
| }
286
+
287
+
scala> val f = () => ()
288
+
289
+
scala> T.m(f)
290
+
res0: Int = 0
291
+
292
+
In Scala 2.11, the first alternative is chosen because it is the only applicable method. In Scala 2.12, both methods are applicable, therefore [overloading resolution](http://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#overloading-resolution) needs to pick the most specific alternative. The specification for [*compatibility*](http://www.scala-lang.org/files/archive/spec/2.12/03-types.html#compatibility) has been updated to consider SAM conversion, so that the first alternative is more specific.
293
+
294
+
Note that SAM conversion in overloading resolution is always considered, also if the argument expression is not a function literals. This is unlike SAM conversions of expressions themselves, see the previous section. See also the discussion in [scala-dev#158](https://github.com/scala/scala-dev/issues/158).
295
+
296
+
While the adjustment to overloading resolution improves compatibility, there can be code that compiles in 2.11, but is ambiguous in 2.12:
297
+
298
+
scala> object T {
299
+
| def m(f: () => Unit, o: Object) = 0
300
+
| def m(r: Runnable, s: String) = 1
301
+
| }
302
+
defined object T
303
+
304
+
scala> T.m(() => (), "")
305
+
<console>:13: error: ambiguous reference to overloaded definition
230
306
231
-
To retain the old behavior, you may compile under `-Xsource:2.11`, or disqualify the type from being a SAM (e.g. by adding a second abstract method).
232
307
233
308
### Inferred types for `val` (and `lazy val`)
234
309
@@ -240,6 +315,8 @@ You can get the old behavior with `-Xsource:2.11`. This may be useful for testin
240
315
241
316
[Lazy vals and objects](https://github.com/scala/scala/pull/5294) have been reworked, and those defined in methods now use a [more efficient representation](https://github.com/scala/scala/pull/5374) that allows synchronization on the holder of the `lazy val`, instead of the surrounding class (as in Dotty).
242
317
318
+
-- TODO: why is this in the "inferred types" section, why under "breaking changes"? This should go somewhere else.
319
+
243
320
### Changed syntax trees (affects macro and compiler plugin authors)
244
321
245
322
PR [#4794](https://github.com/scala/scala/pull/4749) changed the syntax trees for selections of statically accessible symbols. For example, a selection of `Predef` no longer has the shape `q"scala.this.Predef"` but simply `q"scala.Predef"`. Macros and compiler plugins matching on the old tree shape need to be adjusted.
0 commit comments