Skip to content

Commit a635fc1

Browse files
committed
Fix #5376, fix #5434: Unpickle quotes eagerly
1 parent 5bdab50 commit a635fc1

File tree

23 files changed

+178
-91
lines changed

23 files changed

+178
-91
lines changed

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import dotty.tools.dotc.tastyreflect.ReflectionImpl
1919
import scala.internal.quoted._
2020
import scala.reflect.ClassTag
2121

22+
import scala.runtime.quoted.Unpickler._
23+
2224
object PickledQuotes {
2325
import tpd._
2426

2527
/** Pickle the tree of the quote into strings */
26-
def pickleQuote(tree: Tree)(implicit ctx: Context): scala.runtime.quoted.Unpickler.Pickled = {
28+
def pickleQuote(tree: Tree)(implicit ctx: Context): List[String] = {
2729
if (ctx.reporter.hasErrors) Nil
2830
else {
2931
assert(!tree.isInstanceOf[Hole]) // Should not be pickled as it represents `'{$x}` which should be optimized to `x`
@@ -33,33 +35,12 @@ object PickledQuotes {
3335
}
3436

3537
/** Transform the expression into its fully spliced Tree */
36-
def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree = expr match {
37-
case expr: TastyExpr[_] =>
38-
val unpickled = unpickleExpr(expr)
39-
/** Force unpickling of the tree, removes the spliced type `@quotedTypeTag type` definitions and dealiases references to `@quotedTypeTag type` */
40-
val forceAndCleanArtefacts = new TreeMap {
41-
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
42-
case Block(stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) =>
43-
assert(rest.forall { case tdef: TypeDef => tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) })
44-
transform(expr1)
45-
case tree => super.transform(tree).withType(dealiasTypeTags(tree.tpe))
46-
}
47-
}
48-
forceAndCleanArtefacts.transform(unpickled)
49-
case expr: TastyTreeExpr[Tree] @unchecked => healOwner(expr.tree)
50-
}
38+
def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree =
39+
healOwner(expr.asInstanceOf[TastyTreeExpr[Tree]].tree)
5140

5241
/** Transform the expression into its fully spliced TypeTree */
53-
def quotedTypeToTree(expr: quoted.Type[_])(implicit ctx: Context): Tree = expr match {
54-
case expr: TastyType[_] =>
55-
unpickleType(expr) match {
56-
case Block(aliases, tpt) =>
57-
// `@quoteTypeTag type` aliasses are not required after unpickling
58-
tpt
59-
case tpt => tpt
60-
}
61-
case expr: TreeType[Tree] @unchecked => healOwner(expr.typeTree)
62-
}
42+
def quotedTypeToTree(expr: quoted.Type[_])(implicit ctx: Context): Tree =
43+
healOwner(expr.asInstanceOf[TreeType[Tree]].typeTree)
6344

6445
private def dealiasTypeTags(tp: Type)(implicit ctx: Context): Type = new TypeMap() {
6546
override def apply(tp: Type): Type = {
@@ -72,15 +53,31 @@ object PickledQuotes {
7253
}.apply(tp)
7354

7455
/** Unpickle the tree contained in the TastyExpr */
75-
private def unpickleExpr(expr: TastyExpr[_])(implicit ctx: Context): Tree = {
76-
val tastyBytes = TastyString.unpickle(expr.tasty)
77-
unpickle(tastyBytes, expr.args, isType = false)(ctx.addMode(Mode.ReadPositions))
56+
def unpickleExpr(tasty: PickledExpr, args: PickledExprArgs)(implicit ctx: Context): Tree = {
57+
val tastyBytes = TastyString.unpickle(tasty)
58+
val unpickled = unpickle(tastyBytes, args, isType = false)(ctx.addMode(Mode.ReadPositions))
59+
/** Force unpickling of the tree, removes the spliced type `@quotedTypeTag type` definitions and dealiases references to `@quotedTypeTag type` */
60+
val forceAndCleanArtefacts = new TreeMap {
61+
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
62+
case Block(stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) =>
63+
assert(rest.forall { case tdef: TypeDef => tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) })
64+
transform(expr1)
65+
case tree => super.transform(tree).withType(dealiasTypeTags(tree.tpe))
66+
}
67+
}
68+
forceAndCleanArtefacts.transform(unpickled)
7869
}
7970

8071
/** Unpickle the tree contained in the TastyType */
81-
private def unpickleType(ttpe: TastyType[_])(implicit ctx: Context): Tree = {
82-
val tastyBytes = TastyString.unpickle(ttpe.tasty)
83-
unpickle(tastyBytes, ttpe.args, isType = true)(ctx.addMode(Mode.ReadPositions))
72+
def unpickleType(tasty: PickledType, args: PickledTypeArgs)(implicit ctx: Context): Tree = {
73+
val tastyBytes = TastyString.unpickle(tasty)
74+
val unpickled = unpickle(tastyBytes, args, isType = true)(ctx.addMode(Mode.ReadPositions))
75+
unpickled match {
76+
case Block(aliases, tpt) =>
77+
// `@quoteTypeTag type` aliasses are not required after unpickling
78+
tpt
79+
case tpt => tpt
80+
}
8481
}
8582

8683
// TASTY picklingtests/pos/quoteTest.scala

compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package dotty.tools.dotc.core.tasty
22

3-
import scala.runtime.quoted.Unpickler.Pickled
4-
53
import java.io._
64
import java.util.Base64
75
import java.nio.charset.StandardCharsets.UTF_8
@@ -12,14 +10,14 @@ object TastyString {
1210
// Max size of a string literal in the bytecode
1311
private final val maxStringSize = 65535
1412

15-
/** Encode TASTY bytes into an Seq of String */
16-
def pickle(bytes: Array[Byte]): Pickled = {
13+
/** Encode TASTY bytes into a List of String */
14+
def pickle(bytes: Array[Byte]): List[String] = {
1715
val str = new String(Base64.getEncoder().encode(bytes), UTF_8)
1816
str.sliding(maxStringSize, maxStringSize).toList
1917
}
2018

21-
/** Decode the TASTY String into TASTY bytes */
22-
def unpickle(strings: Pickled): Array[Byte] = {
19+
/** Decode the List of Strings into TASTY bytes */
20+
def unpickle(strings: List[String]): Array[Byte] = {
2321
val string = new StringBuilder
2422
strings.foreach(string.append)
2523
Base64.getDecoder().decode(string.result().getBytes(UTF_8))

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1281,7 +1281,7 @@ class TreeUnpickler(reader: TastyReader,
12811281
PickledQuotes.quotedTypeToTree(quotedType)
12821282
} else {
12831283
val splice1 = splice.asInstanceOf[Seq[Any] => given scala.quoted.QuoteContext => quoted.Expr[_]]
1284-
val quotedExpr = splice1(reifiedArgs) given new scala.quoted.QuoteContext(tastyreflect.ReflectionImpl(ctx))
1284+
val quotedExpr = splice1(reifiedArgs) given dotty.tools.dotc.quoted.QuoteContext()
12851285
PickledQuotes.quotedExprToTree(quotedExpr)
12861286
}
12871287
// We need to make sure a hole is created with the source file of the surrounding context, even if

compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ class QuoteCompiler extends Compiler {
6464
cls.enter(ctx.newDefaultConstructor(cls), EmptyScope)
6565
val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered
6666

67-
val quoted = PickledQuotes.quotedExprToTree(exprUnit.exprBuilder.apply(new QuoteContext(ReflectionImpl(ctx))))(ctx.withOwner(meth))
67+
val qctx = dotty.tools.dotc.quoted.QuoteContext()
68+
val quoted = PickledQuotes.quotedExprToTree(exprUnit.exprBuilder.apply(qctx))(ctx.withOwner(meth))
6869

6970
getLiteral(quoted) match {
7071
case Some(value) =>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import dotty.tools.dotc.core.Contexts.Context
4+
import dotty.tools.dotc.core.Contexts.Context
5+
import dotty.tools.dotc.core.quoted.PickledQuotes
6+
import dotty.tools.dotc.tastyreflect.ReflectionImpl
7+
8+
import scala.quoted.{Expr, Type}
9+
import scala.runtime.quoted.Unpickler._
10+
11+
class QuoteContext private (val tasty: scala.tasty.Reflection) extends scala.quoted.QuoteContext {
12+
13+
def unpickleExpr[T](pickledExpr: PickledExpr, args: PickledExprArgs): given scala.quoted.QuoteContext => Expr[T] = given qctx => {
14+
new scala.internal.quoted.TastyTreeExpr(
15+
PickledQuotes.unpickleExpr(pickledExpr, args) given qctx.tasty.rootContext.asInstanceOf[Context]
16+
).asInstanceOf[Expr[T]]
17+
}
18+
19+
def unpickleType[T](pickledType: PickledType, args: PickledTypeArgs): given scala.quoted.QuoteContext => Type[T] = given qctx => {
20+
new scala.internal.quoted.TreeType(
21+
PickledQuotes.unpickleType(pickledType, args) given qctx.tasty.rootContext.asInstanceOf[Context]
22+
).asInstanceOf[Type[T]]
23+
}
24+
25+
}
26+
27+
28+
object QuoteContext {
29+
30+
def apply() given Context: QuoteContext = apply(ReflectionImpl(the[Context]))
31+
32+
def apply(tastyInstance: scala.tasty.Reflection): QuoteContext { val tasty: tastyInstance.type } =
33+
new QuoteContext(tastyInstance).asInstanceOf[QuoteContext { val tasty: tastyInstance.type }]
34+
35+
}

compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ class KernelImpl(val rootContext: core.Contexts.Context) extends Kernel {
2828
def rootPosition: util.SourcePosition =
2929
tastyreflect.MacroExpansion.position.getOrElse(SourcePosition(rootContext.source, Spans.NoSpan))
3030

31+
def QuoteContext_from_Reflection(tastyInstance: scala.tasty.Reflection): scala.quoted.QuoteContext { val tasty: tastyInstance.type } = {
32+
dotty.tools.dotc.quoted.QuoteContext(tastyInstance)
33+
}
34+
3135
//
3236
// CONTEXT
3337
//

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import dotty.tools.repl.AbstractFileClassLoader
2525

2626
import scala.reflect.ClassTag
2727

28+
import dotty.tools.dotc.quoted.QuoteContext
29+
2830
/** Utility class to splice quoted expressions */
2931
object Splicer {
3032
import tpd._
@@ -42,7 +44,7 @@ object Splicer {
4244
try {
4345
// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
4446
val interpretedExpr = interpreter.interpret[scala.quoted.QuoteContext => scala.quoted.Expr[Any]](tree)
45-
interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(new scala.quoted.QuoteContext(ReflectionImpl(ctx)))))
47+
interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(QuoteContext())))
4648
}
4749
catch {
4850
case ex: StopInterpretation =>
@@ -261,7 +263,7 @@ object Splicer {
261263
args.toSeq
262264

263265
private def interpretQuoteContext()(implicit env: Env): Object =
264-
new scala.quoted.QuoteContext(ReflectionImpl(ctx))
266+
QuoteContext()
265267

266268
private def interpretedStaticMethodCall(moduleClass: Symbol, fn: Symbol)(implicit env: Env): List[Object] => Object = {
267269
val (inst, clazz) =

library/src-bootstrapped/dotty/internal/StringContextMacro.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ object StringContextMacro {
6666
}
6767
def splitParts(seq: Expr[Seq[String]]) = (seq, seq) match {
6868
case (ExprSeq(p1), ConstSeq(p2)) => Some((p1.toList, p2.toList))
69-
case _ => notStatic
69+
case (_, _) => notStatic
7070
}
7171
strCtxExpr match {
7272
case '{ StringContext($parts: _*) } => splitParts(parts)

library/src-bootstrapped/scala/tasty/reflect/TreeUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ trait TreeUtils
286286
/** Bind the `rhs` to a `val` and use it in `body` */
287287
def let(rhs: Term)(body: Ident => Term): Term = {
288288
import scala.quoted.QuoteContext
289-
given as QuoteContext = new QuoteContext(this)
289+
given as QuoteContext = QuoteContext.from(self)
290290
type T // TODO probably it is better to use the Sealed contruct rather than let the user create their own existential type
291291
implicit val rhsTpe: quoted.Type[T] = rhs.tpe.seal.asInstanceOf[quoted.Type[T]]
292292
val rhsExpr = rhs.seal.cast[T]

library/src/scala/quoted/Expr.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,7 @@ package quoted {
7979
package internal {
8080
package quoted {
8181

82-
import scala.quoted._
83-
84-
/** An Expr backed by a pickled TASTY tree */
85-
final class TastyExpr[+T](val tasty: scala.runtime.quoted.Unpickler.Pickled, val args: Seq[Any]) extends Expr[T] {
86-
override def toString: String = s"Expr(<pickled tasty>)"
87-
}
82+
import scala.quoted.{Expr, QuoteContext}
8883

8984
/** An Expr backed by a tree. Only the current compiler trees are allowed.
9085
*

library/src/scala/quoted/QuoteContext.scala

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package scala.quoted
22

33
import scala.quoted.show.SyntaxHighlight
44

5+
import scala.runtime.quoted.Unpickler._
6+
57
/** Quotation context provided by a macro expansion or in the scope of `scala.quoted.run`.
68
* Used to perform all operations on quoted `Expr` or `Type`.
79
*
@@ -10,7 +12,10 @@ import scala.quoted.show.SyntaxHighlight
1012
*
1113
* @param tasty Typed AST API. Usage: `def f(qctx: QuoteContext) = { import qctx.tasty._; ... }`.
1214
*/
13-
class QuoteContext(val tasty: scala.tasty.Reflection) {
15+
trait QuoteContext {
16+
17+
/** Typed AST API. Usage: `def f(qctx: QuoteContext) = { import qctx.tasty._; ... }`. */
18+
val tasty: scala.tasty.Reflection
1419

1520
def show(expr: Expr[_], syntaxHighlight: SyntaxHighlight): String = {
1621
import tasty._
@@ -46,8 +51,18 @@ class QuoteContext(val tasty: scala.tasty.Reflection) {
4651
tasty.warning(msg, expr.unseal.pos) given rootContext
4752
}
4853

54+
/** Unpickle `pickledExpr` which represents a pickled `Expr` tree
55+
*/
56+
private[scala] def unpickleExpr[T](pickledExpr: PickledExpr, args: PickledExprArgs): given QuoteContext => Expr[T]
57+
58+
/** Unpickle `pickledType` which represents a pickled `Type` tree
59+
*/
60+
private[scala] def unpickleType[T](pickledType: PickledType, args: PickledTypeArgs): given QuoteContext => Type[T]
61+
4962
}
5063

5164
object QuoteContext {
5265
def macroContext: QuoteContext = throw new Exception("Not in inline macro.")
66+
def from(tastyInstance: scala.tasty.Reflection): QuoteContext { val tasty: tastyInstance.type } =
67+
tastyInstance.kernel.QuoteContext_from_Reflection(tastyInstance)
5368
}

library/src/scala/quoted/Type.scala

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,9 @@ package quoted {
6868

6969
package internal {
7070
package quoted {
71-
import scala.quoted.Type
72-
import scala.runtime.quoted.Unpickler.Pickled
73-
74-
/** A Type backed by a pickled TASTY tree */
75-
final class TastyType[T](val tasty: Pickled, val args: Seq[Any]) extends Type[T] {
76-
override def toString(): String = s"Type(<pickled tasty>)"
77-
}
7871

7972
/** An Type backed by a tree */
80-
final class TreeType[Tree](val typeTree: Tree) extends Type[Any] {
73+
final class TreeType[Tree](val typeTree: Tree) extends scala.quoted.Type[Any] {
8174
override def toString: String = s"Type(<tasty tree>)"
8275
}
8376

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
package scala.runtime.quoted
22

3-
import scala.internal.quoted.{TastyExpr, TastyType}
43
import scala.quoted.{Expr, QuoteContext, Type}
4+
import scala.runtime.quoted.Unpickler.{PickledExpr, PickledType}
55

66
/** Provides methods to unpickle `Expr` and `Type` trees. */
77
object Unpickler {
88

9-
/** Representation of pickled trees. For now a List[String],
10-
* but it should be changed to some kind of TASTY bundle.
11-
*/
12-
type Pickled = List[String]
9+
type PickledExpr = List[String]
10+
type PickledExprArgs = Seq[Seq[Any] => ((given QuoteContext => Expr[Any]) | Type[_])]
11+
12+
type PickledType = List[String]
13+
type PickledTypeArgs = Seq[Seq[Any] => Type[_]]
1314

1415
/** Unpickle `repr` which represents a pickled `Expr` tree,
1516
* replacing splice nodes with `args`
1617
*/
17-
def unpickleExpr[T](repr: Pickled, args: Seq[Seq[Any] => ((given QuoteContext => Expr[Any]) | Type[_])]): given QuoteContext => Expr[T] = new TastyExpr[T](repr, args)
18+
def unpickleExpr[T](repr: PickledExpr, args: PickledExprArgs): given QuoteContext => Expr[T] =
19+
the[QuoteContext].unpickleExpr(repr, args)
1820

1921
/** Unpickle `repr` which represents a pickled `Type` tree,
2022
* replacing splice nodes with `args`
2123
*/
22-
def unpickleType[T](repr: Pickled, args: Seq[Seq[Any] => Type[_]]): given QuoteContext => Type[T] = new TastyType[T](repr, args)
24+
def unpickleType[T](repr: PickledType, args: PickledTypeArgs): given QuoteContext => Type[T] =
25+
the[QuoteContext].unpickleType(repr, args)
2326

2427
}

library/src/scala/tasty/Reflection.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package scala.tasty
22

3+
import scala.quoted.QuoteContext
34
import scala.tasty.reflect._
45

56
class Reflection(val kernel: Kernel)

library/src/scala/tasty/reflect/Kernel.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.tasty.reflect
22

33
import scala.quoted.QuoteContext
4+
import scala.tasty.Reflection
45

56
/** Tasty reflect abstract types
67
*
@@ -129,6 +130,8 @@ trait Kernel {
129130

130131
def settings: Settings
131132

133+
def QuoteContext_from_Reflection(tastyInstance: Reflection): QuoteContext { val tasty: tastyInstance.type }
134+
132135
//
133136
// CONTEXT
134137
//

tests/run-macros/quote-matching-optimize-1.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ Result: ()
1818

1919
Original: scala.List.apply[scala.Int]((1, 2, 3: scala.<repeated>[scala.Int])).map[scala.Int, scala.collection.immutable.List[scala.Int]](((a: scala.Int) => a.*(2)))(scala.collection.immutable.List.canBuildFrom[scala.Int]).map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((b: scala.Int) => b.toString()))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
2020
Optimized: scala.List.apply[scala.Int]((1, 2, 3: scala.<repeated>[scala.Int])).map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((x: scala.Int) => {
21-
val x$5: scala.Int = x.*(2)
22-
x$5.toString()
21+
val x$1: scala.Int = x.*(2)
22+
x$1.toString()
2323
}))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
2424
Result: List(2, 4, 6)
2525

2626
Original: scala.List.apply[scala.Int]((55, 67, 87: scala.<repeated>[scala.Int])).map[scala.Char, scala.collection.immutable.List[scala.Char]](((a: scala.Int) => a.toChar))(scala.collection.immutable.List.canBuildFrom[scala.Char]).map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((b: scala.Char) => b.toString()))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
2727
Optimized: scala.List.apply[scala.Int]((55, 67, 87: scala.<repeated>[scala.Int])).map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((x: scala.Int) => {
28-
val x$10: scala.Char = x.toChar
29-
x$10.toString()
28+
val x$2: scala.Char = x.toChar
29+
x$2.toString()
3030
}))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
3131
Result: List(7, C, W)
3232

tests/run-macros/quote-matching-optimize-2.check

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ Result: ()
1818

1919
Original: ls.map[scala.Int, scala.collection.immutable.List[scala.Int]](((a: scala.Int) => a.*(2)))(scala.collection.immutable.List.canBuildFrom[scala.Int]).map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((b: scala.Int) => b.toString()))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
2020
Optimized: ls.map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((x: scala.Int) => {
21-
val x$5: scala.Int = x.*(2)
22-
x$5.toString()
21+
val x$1: scala.Int = x.*(2)
22+
x$1.toString()
2323
}))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
2424
Result: List(2, 4, 6)
2525

2626
Original: ls.map[scala.Char, scala.collection.immutable.List[scala.Char]](((a: scala.Int) => a.toChar))(scala.collection.immutable.List.canBuildFrom[scala.Char]).map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((b: scala.Char) => b.toString()))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
2727
Optimized: ls.map[java.lang.String, scala.collection.immutable.List[java.lang.String]](((x: scala.Int) => {
28-
val x$10: scala.Char = x.toChar
29-
x$10.toString()
28+
val x$2: scala.Char = x.toChar
29+
x$2.toString()
3030
}))(scala.collection.immutable.List.canBuildFrom[java.lang.String])
3131
Result: List(, , )
3232

0 commit comments

Comments
 (0)