From 0057d4d83cb40116b76296505f15075002e4b2e0 Mon Sep 17 00:00:00 2001 From: James Iry Date: Mon, 22 Jul 2013 16:23:31 -0700 Subject: Make future-spec tests not spawn threads in constructors. The future-spec tests were spawning threads in object constructors which meant they were on the ragged edge of entering deadlock from the static initialization lock acquired during the static initialization blocks we use to construct object reference fields. My work on restructuring lambdas pushed it over the edge. This commit refactors the tests to use class constructors rather than object constructors. --- test/files/jvm/future-spec/FutureTests.scala | 2 +- test/files/jvm/future-spec/PromiseTests.scala | 2 +- test/files/jvm/future-spec/TryTests.scala | 2 +- test/files/jvm/future-spec/main.scala | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index 5d213691df..1595b2c862 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -10,7 +10,7 @@ import scala.util.{Try,Success,Failure} -object FutureTests extends MinimalScalaTest { +class FutureTests extends MinimalScalaTest { /* some utils */ diff --git a/test/files/jvm/future-spec/PromiseTests.scala b/test/files/jvm/future-spec/PromiseTests.scala index 6e613bf3ec..49350586b8 100644 --- a/test/files/jvm/future-spec/PromiseTests.scala +++ b/test/files/jvm/future-spec/PromiseTests.scala @@ -9,7 +9,7 @@ import scala.runtime.NonLocalReturnControl import scala.util.{Try,Success,Failure} -object PromiseTests extends MinimalScalaTest { +class PromiseTests extends MinimalScalaTest { import ExecutionContext.Implicits._ val defaultTimeout = Inf diff --git a/test/files/jvm/future-spec/TryTests.scala b/test/files/jvm/future-spec/TryTests.scala index 5d1b9b84b4..01bb3c9d36 100644 --- a/test/files/jvm/future-spec/TryTests.scala +++ b/test/files/jvm/future-spec/TryTests.scala @@ -5,7 +5,7 @@ import scala.util.{Try,Success,Failure} -object TryTests extends MinimalScalaTest { +class TryTests extends MinimalScalaTest { class MyException extends Exception val e = new Exception("this is an exception") diff --git a/test/files/jvm/future-spec/main.scala b/test/files/jvm/future-spec/main.scala index 132263e2e8..697d0fe91f 100644 --- a/test/files/jvm/future-spec/main.scala +++ b/test/files/jvm/future-spec/main.scala @@ -10,9 +10,9 @@ import java.util.concurrent.{ TimeoutException, CountDownLatch, TimeUnit } object Test { def main(args: Array[String]) { - FutureTests.check() - PromiseTests.check() - TryTests.check() + (new FutureTests).check() + (new PromiseTests).check() + (new TryTests).check() } } -- cgit v1.2.3 From 9136e76ca1a9827e1a8c90fa4f7f63c2967cb019 Mon Sep 17 00:00:00 2001 From: James Iry Date: Mon, 22 Jul 2013 16:51:46 -0700 Subject: Add a skeletal Delambdafy phase. This commit adds a do-nothing phase called "Delambdafy" that will eventually be responsible for doing the final translation of lambdas into classes. --- src/compiler/scala/tools/ant/Scalac.scala | 2 +- src/compiler/scala/tools/nsc/Global.scala | 11 ++++++++++ .../scala/tools/nsc/transform/Delambdafy.scala | 16 ++++++++++++++ src/repl/scala/tools/nsc/interpreter/Phased.scala | 3 ++- test/files/jvm/t7006.check | 1 + test/files/neg/t6446-additional.check | 25 +++++++++++----------- test/files/neg/t6446-missing.check | 21 +++++++++--------- test/files/neg/t6446-show-phases.check | 21 +++++++++--------- test/files/neg/t7494-no-options.check | 25 +++++++++++----------- test/files/run/programmatic-main.check | 7 +++--- test/files/run/t6102.check | 1 + 11 files changed, 84 insertions(+), 49 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/transform/Delambdafy.scala diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index b2cedf6338..1747405f03 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -91,7 +91,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { val values = List("namer", "typer", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "lazyvals", "lambdalift", "constructors", - "flatten", "mixin", "cleanup", "icode", "inliner", + "flatten", "mixin", "delambdafy", "cleanup", "icode", "inliner", "closelim", "dce", "jvm", "terminal") } diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index e765c9165a..1852e670e4 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -575,6 +575,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val runsRightAfter = None } with CleanUp + // phaseName = "delambdafy" + object delambdafy extends { + val global: Global.this.type = Global.this + val runsAfter = List("cleanup") + val runsRightAfter = None + } with Delambdafy + // phaseName = "icode" object genicode extends { val global: Global.this.type = Global.this @@ -695,6 +702,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) lambdaLift -> "move nested functions to top level", constructors -> "move field definitions into constructors", mixer -> "mixin composition", + delambdafy -> "remove lambdas", cleanup -> "platform-specific cleanups, generate reflective calls", genicode -> "generate portable intermediate code", inliner -> "optimization: do inlining", @@ -1068,6 +1076,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) @inline final def exitingExplicitOuter[T](op: => T): T = exitingPhase(currentRun.explicitouterPhase)(op) @inline final def exitingFlatten[T](op: => T): T = exitingPhase(currentRun.flattenPhase)(op) @inline final def exitingMixin[T](op: => T): T = exitingPhase(currentRun.mixinPhase)(op) + @inline final def exitingDelambdafy[T](op: => T): T = exitingPhase(currentRun.delambdafyPhase)(op) @inline final def exitingPickler[T](op: => T): T = exitingPhase(currentRun.picklerPhase)(op) @inline final def exitingRefchecks[T](op: => T): T = exitingPhase(currentRun.refchecksPhase)(op) @inline final def exitingSpecialize[T](op: => T): T = exitingPhase(currentRun.specializePhase)(op) @@ -1078,6 +1087,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) @inline final def enteringFlatten[T](op: => T): T = enteringPhase(currentRun.flattenPhase)(op) @inline final def enteringIcode[T](op: => T): T = enteringPhase(currentRun.icodePhase)(op) @inline final def enteringMixin[T](op: => T): T = enteringPhase(currentRun.mixinPhase)(op) + @inline final def enteringDelambdafy[T](op: => T): T = enteringPhase(currentRun.delambdafyPhase)(op) @inline final def enteringPickler[T](op: => T): T = enteringPhase(currentRun.picklerPhase)(op) @inline final def enteringSpecialize[T](op: => T): T = enteringPhase(currentRun.specializePhase)(op) @inline final def enteringTyper[T](op: => T): T = enteringPhase(currentRun.typerPhase)(op) @@ -1415,6 +1425,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) // val constructorsPhase = phaseNamed("constructors") val flattenPhase = phaseNamed("flatten") val mixinPhase = phaseNamed("mixin") + val delambdafyPhase = phaseNamed("delambdafy") val cleanupPhase = phaseNamed("cleanup") val icodePhase = phaseNamed("icode") val inlinerPhase = phaseNamed("inliner") diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala new file mode 100644 index 0000000000..f39fd2eecb --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -0,0 +1,16 @@ +package scala.tools.nsc +package transform + +abstract class Delambdafy extends Transform with TypingTransformers with ast.TreeDSL { + import global._ + + /** the following two members override abstract members in Transform */ + val phaseName: String = "delambdafy" + + protected def newTransformer(unit: CompilationUnit): Transformer = + new DelambdafyTransformer(unit) + + class DelambdafyTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + override def transform(tree: Tree): Tree = tree + } +} diff --git a/src/repl/scala/tools/nsc/interpreter/Phased.scala b/src/repl/scala/tools/nsc/interpreter/Phased.scala index f625124e70..1cdbd65949 100644 --- a/src/repl/scala/tools/nsc/interpreter/Phased.scala +++ b/src/repl/scala/tools/nsc/interpreter/Phased.scala @@ -91,7 +91,7 @@ trait Phased { Parser, Namer, Packageobjects, Typer, Superaccessors, Pickler, Refchecks, Selectiveanf, Liftcode, Selectivecps, Uncurry, Tailcalls, Specialize, Explicitouter, Erasure, Lazyvals, Lambdalift, Constructors, Flatten, Mixin, - Cleanup, Icode, Inliner, Closelim, Dce, Jvm, Terminal + Cleanup, Delambdafy, Icode, Inliner, Closelim, Dce, Jvm, Terminal ) lazy val nameMap = all.map(x => x.name -> x).toMap withDefaultValue NoPhaseName multi = all @@ -127,6 +127,7 @@ trait Phased { case object Flatten extends PhaseName case object Mixin extends PhaseName case object Cleanup extends PhaseName + case object Delambdafy extends PhaseName case object Icode extends PhaseName case object Inliner extends PhaseName case object Closelim extends PhaseName diff --git a/test/files/jvm/t7006.check b/test/files/jvm/t7006.check index 7c99eba30c..6294b14d62 100644 --- a/test/files/jvm/t7006.check +++ b/test/files/jvm/t7006.check @@ -19,6 +19,7 @@ [running phase flatten on Foo_1.scala] [running phase mixin on Foo_1.scala] [running phase cleanup on Foo_1.scala] +[running phase delambdafy on Foo_1.scala] [running phase icode on Foo_1.scala] [running phase inliner on Foo_1.scala] [running phase inlinehandlers on Foo_1.scala] diff --git a/test/files/neg/t6446-additional.check b/test/files/neg/t6446-additional.check index c91333830a..a87af2f1e5 100755 --- a/test/files/neg/t6446-additional.check +++ b/test/files/neg/t6446-additional.check @@ -21,18 +21,19 @@ superaccessors 6 add super accessors in traits and nested classes flatten 19 eliminate inner classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls - icode 22 generate portable intermediate code + delambdafy 22 remove lambdas + icode 23 generate portable intermediate code #partest -optimise - inliner 23 optimization: do inlining -inlinehandlers 24 optimization: inline exception handlers - closelim 25 optimization: eliminate uncalled closures - constopt 26 optimization: optimize null and other constants - dce 27 optimization: eliminate dead code - jvm 28 generate JVM bytecode - ploogin 29 A sample phase that does so many things it's kind of hard... - terminal 30 the last phase during a compilation run + inliner 24 optimization: do inlining +inlinehandlers 25 optimization: inline exception handlers + closelim 26 optimization: eliminate uncalled closures + constopt 27 optimization: optimize null and other constants + dce 28 optimization: eliminate dead code + jvm 29 generate JVM bytecode + ploogin 30 A sample phase that does so many things it's kind of hard... + terminal 31 the last phase during a compilation run #partest !-optimise - jvm 23 generate JVM bytecode - ploogin 24 A sample phase that does so many things it's kind of hard... - terminal 25 the last phase during a compilation run + jvm 24 generate JVM bytecode + ploogin 25 A sample phase that does so many things it's kind of hard... + terminal 26 the last phase during a compilation run #partest diff --git a/test/files/neg/t6446-missing.check b/test/files/neg/t6446-missing.check index b2d5ddd686..cd867289c3 100755 --- a/test/files/neg/t6446-missing.check +++ b/test/files/neg/t6446-missing.check @@ -22,16 +22,17 @@ superaccessors 6 add super accessors in traits and nested classes flatten 19 eliminate inner classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls - icode 22 generate portable intermediate code + delambdafy 22 remove lambdas + icode 23 generate portable intermediate code #partest !-optimise - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run #partest -optimise - inliner 23 optimization: do inlining -inlinehandlers 24 optimization: inline exception handlers - closelim 25 optimization: eliminate uncalled closures - constopt 26 optimization: optimize null and other constants - dce 27 optimization: eliminate dead code - jvm 28 generate JVM bytecode - terminal 29 the last phase during a compilation run + inliner 24 optimization: do inlining +inlinehandlers 25 optimization: inline exception handlers + closelim 26 optimization: eliminate uncalled closures + constopt 27 optimization: optimize null and other constants + dce 28 optimization: eliminate dead code + jvm 29 generate JVM bytecode + terminal 30 the last phase during a compilation run #partest diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check index 48d4f37b3e..3ae3f96ef2 100644 --- a/test/files/neg/t6446-show-phases.check +++ b/test/files/neg/t6446-show-phases.check @@ -21,16 +21,17 @@ superaccessors 6 add super accessors in traits and nested classes flatten 19 eliminate inner classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls - icode 22 generate portable intermediate code + delambdafy 22 remove lambdas + icode 23 generate portable intermediate code #partest !-optimise - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run #partest -optimise - inliner 23 optimization: do inlining -inlinehandlers 24 optimization: inline exception handlers - closelim 25 optimization: eliminate uncalled closures - constopt 26 optimization: optimize null and other constants - dce 27 optimization: eliminate dead code - jvm 28 generate JVM bytecode - terminal 29 the last phase during a compilation run + inliner 24 optimization: do inlining +inlinehandlers 25 optimization: inline exception handlers + closelim 26 optimization: eliminate uncalled closures + constopt 27 optimization: optimize null and other constants + dce 28 optimization: eliminate dead code + jvm 29 generate JVM bytecode + terminal 30 the last phase during a compilation run #partest diff --git a/test/files/neg/t7494-no-options.check b/test/files/neg/t7494-no-options.check index b5dc0e3d4f..e3316f590a 100644 --- a/test/files/neg/t7494-no-options.check +++ b/test/files/neg/t7494-no-options.check @@ -22,18 +22,19 @@ superaccessors 6 add super accessors in traits and nested classes flatten 19 eliminate inner classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls - icode 22 generate portable intermediate code + delambdafy 22 remove lambdas + icode 23 generate portable intermediate code #partest !-optimise - jvm 23 generate JVM bytecode - ploogin 24 A sample phase that does so many things it's kind of hard... - terminal 25 the last phase during a compilation run + jvm 24 generate JVM bytecode + ploogin 25 A sample phase that does so many things it's kind of hard... + terminal 26 the last phase during a compilation run #partest -optimise - inliner 23 optimization: do inlining -inlinehandlers 24 optimization: inline exception handlers - closelim 25 optimization: eliminate uncalled closures - constopt 26 optimization: optimize null and other constants - dce 27 optimization: eliminate dead code - jvm 28 generate JVM bytecode - ploogin 29 A sample phase that does so many things it's kind of hard... - terminal 30 the last phase during a compilation run + inliner 24 optimization: do inlining +inlinehandlers 25 optimization: inline exception handlers + closelim 26 optimization: eliminate uncalled closures + constopt 27 optimization: optimize null and other constants + dce 28 optimization: eliminate dead code + jvm 29 generate JVM bytecode + ploogin 30 A sample phase that does so many things it's kind of hard... + terminal 31 the last phase during a compilation run #partest diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check index cfa3ed3fb4..1cd94ccb45 100644 --- a/test/files/run/programmatic-main.check +++ b/test/files/run/programmatic-main.check @@ -21,6 +21,7 @@ superaccessors 6 add super accessors in traits and nested classes flatten 19 eliminate inner classes mixin 20 mixin composition cleanup 21 platform-specific cleanups, generate reflective calls - icode 22 generate portable intermediate code - jvm 23 generate JVM bytecode - terminal 24 the last phase during a compilation run + delambdafy 22 remove lambdas + icode 23 generate portable intermediate code + jvm 24 generate JVM bytecode + terminal 25 the last phase during a compilation run diff --git a/test/files/run/t6102.check b/test/files/run/t6102.check index 4e8efa7b6d..aa3e6cc9e2 100644 --- a/test/files/run/t6102.check +++ b/test/files/run/t6102.check @@ -19,6 +19,7 @@ [running phase flatten on t6102.scala] [running phase mixin on t6102.scala] [running phase cleanup on t6102.scala] +[running phase delambdafy on t6102.scala] [running phase icode on t6102.scala] #partest -optimise [running phase inliner on t6102.scala] -- cgit v1.2.3 From 10a061d425857c9e7bf4fa9aba9923b90467e24e Mon Sep 17 00:00:00 2001 From: James Iry Date: Mon, 22 Jul 2013 17:45:36 -0700 Subject: Adds a setting to delay delambdafication. If set then uncurry lifts the body of a lambda into a local def. Tests are included to show the different tree shapes. --- .../scala/tools/nsc/settings/ScalaSettings.scala | 2 + .../scala/tools/nsc/transform/UnCurry.scala | 127 +++++++++++++++++---- .../run/delambdafy_uncurry_byname_inline.check | 21 ++++ .../run/delambdafy_uncurry_byname_inline.scala | 20 ++++ .../run/delambdafy_uncurry_byname_method.check | 15 +++ .../run/delambdafy_uncurry_byname_method.scala | 20 ++++ test/files/run/delambdafy_uncurry_inline.check | 23 ++++ test/files/run/delambdafy_uncurry_inline.scala | 20 ++++ test/files/run/delambdafy_uncurry_method.check | 17 +++ test/files/run/delambdafy_uncurry_method.scala | 20 ++++ 10 files changed, 264 insertions(+), 21 deletions(-) create mode 100644 test/files/run/delambdafy_uncurry_byname_inline.check create mode 100644 test/files/run/delambdafy_uncurry_byname_inline.scala create mode 100644 test/files/run/delambdafy_uncurry_byname_method.check create mode 100644 test/files/run/delambdafy_uncurry_byname_method.scala create mode 100644 test/files/run/delambdafy_uncurry_inline.check create mode 100644 test/files/run/delambdafy_uncurry_inline.scala create mode 100644 test/files/run/delambdafy_uncurry_method.check create mode 100644 test/files/run/delambdafy_uncurry_method.scala diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 01d5791f60..b8ca4adc14 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -184,6 +184,8 @@ trait ScalaSettings extends AbsScalaSettings val YnoLoadImplClass = BooleanSetting ("-Yno-load-impl-class", "Do not load $class.class files.") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() + // the current standard is "inline" but we are moving towards "method" + val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "inline") private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index ccf2266540..3d648ccbac 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -63,6 +63,7 @@ abstract class UnCurry extends InfoTransform // uncurry and uncurryType expand type aliases class UnCurryTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { + private val inlineFunctionExpansion = settings.Ydelambdafy.value == "inline" private var needTryLift = false private var inConstructorFlag = 0L private val byNameArgs = mutable.HashSet[Tree]() @@ -223,34 +224,110 @@ abstract class UnCurry extends InfoTransform val targs = fun.tpe.typeArgs val (formals, restpe) = (targs.init, targs.last) - val applyMethodDef = { - val methSym = anonClass.newMethod(nme.apply, fun.pos, FINAL) - val paramSyms = map2(formals, fun.vparams) { - (tp, param) => methSym.newSyntheticValueParam(tp, param.name) + if (inlineFunctionExpansion) { + val applyMethodDef = { + val methSym = anonClass.newMethod(nme.apply, fun.pos, FINAL) + val paramSyms = map2(formals, fun.vparams) { + (tp, param) => methSym.newSyntheticValueParam(tp, param.name) + } + methSym setInfoAndEnter MethodType(paramSyms, restpe) + + fun.vparams foreach (_.symbol.owner = methSym) + fun.body changeOwner (fun.symbol -> methSym) + + val body = localTyper.typedPos(fun.pos)(fun.body) + val methDef = DefDef(methSym, List(fun.vparams), body) + + // Have to repack the type to avoid mismatches when existentials + // appear in the result - see SI-4869. + methDef.tpt setType localTyper.packedType(body, methSym) + methDef } - methSym setInfoAndEnter MethodType(paramSyms, restpe) - fun.vparams foreach (_.symbol.owner = methSym) - fun.body changeOwner (fun.symbol -> methSym) + localTyper.typedPos(fun.pos) { + Block( + List(ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos)), + Typed(New(anonClass.tpe), TypeTree(fun.tpe))) + } + } else { + /** + * Abstracts away the common functionality required to create both + * the lifted function and the apply method on the anonymous class + * It creates a method definition with value params cloned from the + * original lambda. Then it calls a supplied function to create + * the body and types the result. Finally + * everything is wrapped up in a MethodDef + * + * TODO it is intended that this common functionality be used + * whether inlineFunctionExpansion is true or not. However, it + * seems to introduce subtle ownwership changes that produce + * binary incompatible changes and so it is completely + * hidden behind the inlineFunctionExpansion for now. + * + * @param owner The owner for the new method + * @param name name for the new method + * @param additionalFlags flags to be put on the method in addition to FINAL + * @bodyF function that turns the method symbol and list of value params + * into a body for the method + */ + def createMethod(owner: Symbol, name: TermName, additionalFlags: Long)(bodyF: (Symbol, List[ValDef]) => Tree) = { + val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags) + val vparams = fun.vparams map (_.duplicate) + + val paramSyms = map2(formals, vparams) { + (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name) + } + foreach2(vparams, paramSyms){(valdef, sym) => valdef.symbol = sym} + vparams foreach (_.symbol.owner = methSym) - val body = localTyper.typedPos(fun.pos)(fun.body) - val methDef = DefDef(methSym, List(fun.vparams), body) + val methodType = MethodType(paramSyms, restpe.deconst) + methSym setInfo methodType - // Have to repack the type to avoid mismatches when existentials - // appear in the result - see SI-4869. - methDef.tpt setType localTyper.packedType(body, methSym) - methDef - } + // TODO this is probably cleaner if bodyF only works with symbols rather than parameter ValDefs + val tempBody = bodyF(methSym, vparams) + val body = localTyper.typedPos(fun.pos)(tempBody) + val methDef = DefDef(methSym, List(vparams), body) - localTyper.typedPos(fun.pos) { - Block( - List(ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos)), - Typed(New(anonClass.tpe), TypeTree(fun.tpe))) - } + // Have to repack the type to avoid mismatches when existentials + // appear in the result - see SI-4869. + methDef.tpt setType localTyper.packedType(body, methSym).deconst + methDef + } - } + val methodFlags = ARTIFACT + // method definition with the same arguments, return type, and body as the original lambda + val liftedMethod = createMethod(fun.symbol.owner, tpnme.ANON_FUN_NAME.toTermName, methodFlags){ + case(methSym, vparams) => + fun.body.substituteSymbols(fun.vparams map (_.symbol), vparams map (_.symbol)) + fun.body changeOwner (fun.symbol -> methSym) + } + + // callsite for the lifted method + val args = fun.vparams map { vparam => + val ident = Ident(vparam.symbol) + // if -Yeta-expand-keeps-star is turned on then T* types can get through. In order + // to forward them we need to forward x: T* ascribed as "x:_*" + if (settings.etaExpandKeepsStar && definitions.isRepeatedParamType(vparam.tpt.tpe)) + gen.wildcardStar(ident) + else + ident + } + + val funTyper = localTyper.typedPos(fun.pos) _ + + val liftedMethodCall = funTyper(Apply(liftedMethod.symbol, args:_*)) + + // new function whose body is just a call to the lifted method + val newFun = treeCopy.Function(fun, fun.vparams, liftedMethodCall) + funTyper(Block( + List(funTyper(liftedMethod)), + super.transform(newFun) + )) + } + } } + def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = { val isJava = fun.isJavaDefined def transformVarargs(varargsElemType: Type) = { @@ -381,7 +458,7 @@ abstract class UnCurry extends InfoTransform deriveDefDef(dd)(_ => body) case _ => tree } - def isNonLocalReturn(ret: Return) = ret.symbol != currentOwner.enclMethod || currentOwner.isLazy + def isNonLocalReturn(ret: Return) = ret.symbol != currentOwner.enclMethod || currentOwner.isLazy || currentOwner.isAnonymousFunction // ------ The tree transformers -------------------------------------------------------- @@ -413,6 +490,10 @@ abstract class UnCurry extends InfoTransform } val sym = tree.symbol + + // true if the taget is a lambda body that's been lifted into a method + def isLiftedLambdaBody(target: Tree) = target.symbol.isLocal && target.symbol.isArtifact && target.symbol.name.containsName(nme.ANON_FUN_NAME) + val result = ( // TODO - settings.noassertions.value temporarily retained to avoid // breakage until a reasonable interface is settled upon. @@ -494,6 +575,10 @@ abstract class UnCurry extends InfoTransform val pat1 = transform(pat) treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) + // if a lambda is already the right shape we don't need to transform it again + case fun @ Function(_, Apply(target, _)) if (!inlineFunctionExpansion) && isLiftedLambdaBody(target) => + super.transform(fun) + case fun @ Function(_, _) => mainTransform(transformFunction(fun)) diff --git a/test/files/run/delambdafy_uncurry_byname_inline.check b/test/files/run/delambdafy_uncurry_byname_inline.check new file mode 100644 index 0000000000..0dc69b379a --- /dev/null +++ b/test/files/run/delambdafy_uncurry_byname_inline.check @@ -0,0 +1,21 @@ +[[syntax trees at end of uncurry]] // newSource1.scala +package { + class Foo extends Object { + def (): Foo = { + Foo.super.(); + () + }; + def bar(x: () => Int): Int = x.apply(); + def foo(): Int = Foo.this.bar({ + @SerialVersionUID(0) final class $anonfun extends scala.runtime.AbstractFunction0[Int] with Serializable { + def (): <$anon: () => Int> = { + $anonfun.super.(); + () + }; + final def apply(): Int = 1 + }; + (new <$anon: () => Int>(): () => Int) + }) + } +} + diff --git a/test/files/run/delambdafy_uncurry_byname_inline.scala b/test/files/run/delambdafy_uncurry_byname_inline.scala new file mode 100644 index 0000000000..8f480fa804 --- /dev/null +++ b/test/files/run/delambdafy_uncurry_byname_inline.scala @@ -0,0 +1,20 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:inline -d " + testOutput.path + + override def code = """class Foo { + | def bar(x: => Int) = x + | + | def foo = bar(1) + |} + |""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} diff --git a/test/files/run/delambdafy_uncurry_byname_method.check b/test/files/run/delambdafy_uncurry_byname_method.check new file mode 100644 index 0000000000..cd3edc7d6f --- /dev/null +++ b/test/files/run/delambdafy_uncurry_byname_method.check @@ -0,0 +1,15 @@ +[[syntax trees at end of uncurry]] // newSource1.scala +package { + class Foo extends Object { + def (): Foo = { + Foo.super.(); + () + }; + def bar(x: () => Int): Int = x.apply(); + def foo(): Int = Foo.this.bar({ + final def $anonfun(): Int = 1; + (() => $anonfun()) + }) + } +} + diff --git a/test/files/run/delambdafy_uncurry_byname_method.scala b/test/files/run/delambdafy_uncurry_byname_method.scala new file mode 100644 index 0000000000..1adeec8433 --- /dev/null +++ b/test/files/run/delambdafy_uncurry_byname_method.scala @@ -0,0 +1,20 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:method -Ystop-after:uncurry -d " + testOutput.path + + override def code = """class Foo { + | def bar(x: => Int) = x + | + | def foo = bar(1) + |} + |""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} diff --git a/test/files/run/delambdafy_uncurry_inline.check b/test/files/run/delambdafy_uncurry_inline.check new file mode 100644 index 0000000000..e2b024b462 --- /dev/null +++ b/test/files/run/delambdafy_uncurry_inline.check @@ -0,0 +1,23 @@ +[[syntax trees at end of uncurry]] // newSource1.scala +package { + class Foo extends Object { + def (): Foo = { + Foo.super.(); + () + }; + def bar(): Unit = { + val f: Int => Int = { + @SerialVersionUID(0) final class $anonfun extends scala.runtime.AbstractFunction1[Int,Int] with Serializable { + def (): <$anon: Int => Int> = { + $anonfun.super.(); + () + }; + final def apply(x: Int): Int = x.+(1) + }; + (new <$anon: Int => Int>(): Int => Int) + }; + () + } + } +} + diff --git a/test/files/run/delambdafy_uncurry_inline.scala b/test/files/run/delambdafy_uncurry_inline.scala new file mode 100644 index 0000000000..b42b65f5bb --- /dev/null +++ b/test/files/run/delambdafy_uncurry_inline.scala @@ -0,0 +1,20 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:inline -d " + testOutput.path + + override def code = """class Foo { + | def bar = { + | val f = {x: Int => x + 1} + | } + |} + |""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} diff --git a/test/files/run/delambdafy_uncurry_method.check b/test/files/run/delambdafy_uncurry_method.check new file mode 100644 index 0000000000..5ee3d174b3 --- /dev/null +++ b/test/files/run/delambdafy_uncurry_method.check @@ -0,0 +1,17 @@ +[[syntax trees at end of uncurry]] // newSource1.scala +package { + class Foo extends Object { + def (): Foo = { + Foo.super.(); + () + }; + def bar(): Unit = { + val f: Int => Int = { + final def $anonfun(x: Int): Int = x.+(1); + ((x: Int) => $anonfun(x)) + }; + () + } + } +} + diff --git a/test/files/run/delambdafy_uncurry_method.scala b/test/files/run/delambdafy_uncurry_method.scala new file mode 100644 index 0000000000..a988fb2ee7 --- /dev/null +++ b/test/files/run/delambdafy_uncurry_method.scala @@ -0,0 +1,20 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:method -Ystop-after:uncurry -d " + testOutput.path + + override def code = """class Foo { + | def bar = { + | val f = {x: Int => x + 1} + | } + |} + |""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} -- cgit v1.2.3 From 510b8cecc4951ff8092cfa931c2dc3717e21dded Mon Sep 17 00:00:00 2001 From: James Iry Date: Tue, 30 Jul 2013 11:22:55 -0700 Subject: Refactor Erasure for delambdafication. This commit is purely a refactor. It pulls code needed to adapt a tree of one type into a tree of another type (by casting, boxing, coercing, etc) out of Erasure and into common locations that will be usable from the Delambdafy phase. --- .../scala/tools/nsc/transform/Erasure.scala | 170 +------------------ .../tools/nsc/transform/ExtensionMethods.scala | 2 +- .../nsc/transform/TypeAdaptingTransformer.scala | 187 +++++++++++++++++++++ .../scala/reflect/internal/Definitions.scala | 4 + .../scala/reflect/internal/transform/Erasure.scala | 3 - 5 files changed, 198 insertions(+), 168 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index f0d3db1296..68f1c81c59 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -17,11 +17,15 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer with TypingTransformers with ast.TreeDSL + with TypeAdaptingTransformer { import global._ import definitions._ import CODE._ + val analyzer: typechecker.Analyzer { val global: Erasure.this.global.type } = + this.asInstanceOf[typechecker.Analyzer { val global: Erasure.this.global.type }] + val phaseName: String = "erasure" def newTransformer(unit: CompilationUnit): Transformer = @@ -352,13 +356,6 @@ abstract class Erasure extends AddInterfaces override def newTyper(context: Context) = new Eraser(context) - private def isSafelyRemovableUnbox(fn: Tree, arg: Tree): Boolean = { - isUnbox(fn.symbol) && { - val cls = arg.tpe.typeSymbol - (cls == definitions.NullClass) || isBoxedValueClass(cls) - } - } - class ComputeBridges(unit: CompilationUnit, root: Symbol) { assert(phase == currentRun.erasurePhase, phase) @@ -522,158 +519,8 @@ abstract class Erasure extends AddInterfaces } /** The modifier typer which retypes with erased types. */ - class Eraser(_context: Context) extends Typer(_context) { - - private def isPrimitiveValueType(tpe: Type) = isPrimitiveValueClass(tpe.typeSymbol) - - private def isDifferentErasedValueType(tpe: Type, other: Type) = - isErasedValueType(tpe) && (tpe ne other) - - private def isPrimitiveValueMember(sym: Symbol) = isPrimitiveValueClass(sym.owner) - - @inline private def box(tree: Tree, target: => String): Tree = { - val result = box1(tree) - if (tree.tpe =:= UnitTpe) () - else log(s"boxing ${tree.summaryString}: ${tree.tpe} into $target: ${result.tpe}") - result - } - - /** Box `tree` of unboxed type */ - private def box1(tree: Tree): Tree = tree match { - case LabelDef(_, _, _) => - val ldef = deriveLabelDef(tree)(box1) - ldef setType ldef.rhs.tpe - case _ => - val tree1 = tree.tpe match { - case ErasedValueType(clazz, _) => - New(clazz, cast(tree, underlyingOfValueClass(clazz))) - case _ => - tree.tpe.typeSymbol match { - case UnitClass => - if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) - else BLOCK(tree, REF(BoxedUnit_UNIT)) - case NothingClass => tree // a non-terminating expression doesn't need boxing - case x => - assert(x != ArrayClass) - tree match { - /* Can't always remove a Box(Unbox(x)) combination because the process of boxing x - * may lead to throwing an exception. - * - * This is important for specialization: calls to the super constructor should not box/unbox specialized - * fields (see TupleX). (ID) - */ - case Apply(boxFun, List(arg)) if isSafelyRemovableUnbox(tree, arg) => - log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}") - arg - case _ => - (REF(boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectTpe - } - } - } - typedPos(tree.pos)(tree1) - } - - private def unbox(tree: Tree, pt: Type): Tree = { - val result = unbox1(tree, pt) - log(s"unboxing ${tree.shortClass}: ${tree.tpe} as a ${result.tpe}") - result - } - - /** Unbox `tree` of boxed type to expected type `pt`. - * - * @param tree the given tree - * @param pt the expected type. - * @return the unboxed tree - */ - private def unbox1(tree: Tree, pt: Type): Tree = tree match { -/* - case Boxed(unboxed) => - println("unbox shorten: "+tree) // this never seems to kick in during build and test; therefore disabled. - adaptToType(unboxed, pt) - */ - case LabelDef(_, _, _) => - val ldef = deriveLabelDef(tree)(unbox(_, pt)) - ldef setType ldef.rhs.tpe - case _ => - val tree1 = pt match { - case ErasedValueType(clazz, underlying) => - val tree0 = - if (tree.tpe.typeSymbol == NullClass && - isPrimitiveValueClass(underlying.typeSymbol)) { - // convert `null` directly to underlying type, as going - // via the unboxed type would yield a NPE (see SI-5866) - unbox1(tree, underlying) - } else - Apply(Select(adaptToType(tree, clazz.tpe), clazz.derivedValueClassUnbox), List()) - cast(tree0, pt) - case _ => - pt.typeSymbol match { - case UnitClass => - if (treeInfo isExprSafeToInline tree) UNIT - else BLOCK(tree, UNIT) - case x => - assert(x != ArrayClass) - // don't `setType pt` the Apply tree, as the Apply's fun won't be typechecked if the Apply tree already has a type - Apply(unboxMethod(pt.typeSymbol), tree) - } - } - typedPos(tree.pos)(tree1) - } - - /** Generate a synthetic cast operation from tree.tpe to pt. - * @pre pt eq pt.normalize - */ - private def cast(tree: Tree, pt: Type): Tree = { - if ((tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { - def word = ( - if (tree.tpe <:< pt) "upcast" - else if (pt <:< tree.tpe) "downcast" - else if (pt weak_<:< tree.tpe) "coerce" - else if (tree.tpe weak_<:< pt) "widen" - else "cast" - ) - log(s"erasure ${word}s from ${tree.tpe} to $pt") - } - if (pt =:= UnitTpe) { - // See SI-4731 for one example of how this occurs. - log("Attempted to cast to Unit: " + tree) - tree.duplicate setType pt - } else if (tree.tpe != null && tree.tpe.typeSymbol == ArrayClass && pt.typeSymbol == ArrayClass) { - // See SI-2386 for one example of when this might be necessary. - val needsExtraCast = isPrimitiveValueType(tree.tpe.typeArgs.head) && !isPrimitiveValueType(pt.typeArgs.head) - val tree1 = if (needsExtraCast) gen.mkRuntimeCall(nme.toObjectArray, List(tree)) else tree - gen.mkAttributedCast(tree1, pt) - } else gen.mkAttributedCast(tree, pt) - } - - /** Adapt `tree` to expected type `pt`. - * - * @param tree the given tree - * @param pt the expected type - * @return the adapted tree - */ - private def adaptToType(tree: Tree, pt: Type): Tree = { - if (settings.debug && pt != WildcardType) - log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug - if (tree.tpe <:< pt) - tree - else if (isDifferentErasedValueType(tree.tpe, pt)) - adaptToType(box(tree, pt.toString), pt) - else if (isDifferentErasedValueType(pt, tree.tpe)) - adaptToType(unbox(tree, pt), pt) - else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { - adaptToType(box(tree, pt.toString), pt) - } else if (isMethodTypeWithEmptyParams(tree.tpe)) { - // [H] this assert fails when trying to typecheck tree !(SomeClass.this.bitmap) for single lazy val - //assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) - adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) -// } else if (pt <:< tree.tpe) -// cast(tree, pt) - } else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe)) - adaptToType(unbox(tree, pt), pt) - else - cast(tree, pt) - } + class Eraser(_context: Context) extends Typer(_context) with TypeAdapter { + val typer = this.asInstanceOf[analyzer.Typer] /** Replace member references as follows: * @@ -834,11 +681,6 @@ abstract class Erasure extends AddInterfaces tree1 } } - - private def isMethodTypeWithEmptyParams(tpe: Type) = tpe match { - case MethodType(Nil, _) => true - case _ => false - } } /** The erasure transformer */ diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 9e8cbe6c03..2235a93ca4 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -129,7 +129,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { if (seen contains clazz) unit.error(pos, "value class may not unbox to itself") else { - val unboxed = erasure.underlyingOfValueClass(clazz).typeSymbol + val unboxed = definitions.underlyingOfValueClass(clazz).typeSymbol if (unboxed.isDerivedValueClass) checkNonCyclic(pos, seen + clazz, unboxed) } diff --git a/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala new file mode 100644 index 0000000000..41b8461c46 --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala @@ -0,0 +1,187 @@ +package scala.tools.nsc +package transform + +import scala.reflect.internal._ +import scala.tools.nsc.ast.TreeDSL +import scala.tools.nsc.Global + +/** + * A trait usable by transforms that need to adapt trees of one type to another type + */ +trait TypeAdaptingTransformer { + self: TreeDSL => + + val analyzer: typechecker.Analyzer { val global: self.global.type } + + trait TypeAdapter { + val typer: analyzer.Typer + import global._ + import definitions._ + import CODE._ + + def isMethodTypeWithEmptyParams(tpe: Type) = tpe match { + case MethodType(Nil, _) => true + case _ => false + } + + private def isSafelyRemovableUnbox(fn: Tree, arg: Tree): Boolean = { + isUnbox(fn.symbol) && { + val cls = arg.tpe.typeSymbol + (cls == definitions.NullClass) || isBoxedValueClass(cls) + } + } + + private def isPrimitiveValueType(tpe: Type) = isPrimitiveValueClass(tpe.typeSymbol) + + private def isErasedValueType(tpe: Type) = tpe.isInstanceOf[ErasedValueType] + + private def isDifferentErasedValueType(tpe: Type, other: Type) = + isErasedValueType(tpe) && (tpe ne other) + + def isPrimitiveValueMember(sym: Symbol) = isPrimitiveValueClass(sym.owner) + + @inline def box(tree: Tree, target: => String): Tree = { + val result = box1(tree) + if (tree.tpe =:= UnitTpe) () + else log(s"boxing ${tree.summaryString}: ${tree.tpe} into $target: ${result.tpe}") + result + } + + /** Box `tree` of unboxed type */ + private def box1(tree: Tree): Tree = tree match { + case LabelDef(_, _, _) => + val ldef = deriveLabelDef(tree)(box1) + ldef setType ldef.rhs.tpe + case _ => + val tree1 = tree.tpe match { + case ErasedValueType(clazz, _) => + New(clazz, cast(tree, underlyingOfValueClass(clazz))) + case _ => + tree.tpe.typeSymbol match { + case UnitClass => + if (treeInfo isExprSafeToInline tree) REF(BoxedUnit_UNIT) + else BLOCK(tree, REF(BoxedUnit_UNIT)) + case NothingClass => tree // a non-terminating expression doesn't need boxing + case x => + assert(x != ArrayClass) + tree match { + /* Can't always remove a Box(Unbox(x)) combination because the process of boxing x + * may lead to throwing an exception. + * + * This is important for specialization: calls to the super constructor should not box/unbox specialized + * fields (see TupleX). (ID) + */ + case Apply(boxFun, List(arg)) if isSafelyRemovableUnbox(tree, arg) => + log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}") + arg + case _ => + (REF(boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectTpe + } + } + } + typer.typedPos(tree.pos)(tree1) + } + + def unbox(tree: Tree, pt: Type): Tree = { + val result = unbox1(tree, pt) + log(s"unboxing ${tree.shortClass}: ${tree.tpe} as a ${result.tpe}") + result + } + + /** Unbox `tree` of boxed type to expected type `pt`. + * + * @param tree the given tree + * @param pt the expected type. + * @return the unboxed tree + */ + private def unbox1(tree: Tree, pt: Type): Tree = tree match { +/* + case Boxed(unboxed) => + println("unbox shorten: "+tree) // this never seems to kick in during build and test; therefore disabled. + adaptToType(unboxed, pt) + */ + case LabelDef(_, _, _) => + val ldef = deriveLabelDef(tree)(unbox(_, pt)) + ldef setType ldef.rhs.tpe + case _ => + val tree1 = pt match { + case ErasedValueType(clazz, underlying) => + val tree0 = + if (tree.tpe.typeSymbol == NullClass && + isPrimitiveValueClass(underlying.typeSymbol)) { + // convert `null` directly to underlying type, as going + // via the unboxed type would yield a NPE (see SI-5866) + unbox1(tree, underlying) + } else + Apply(Select(adaptToType(tree, clazz.tpe), clazz.derivedValueClassUnbox), List()) + cast(tree0, pt) + case _ => + pt.typeSymbol match { + case UnitClass => + if (treeInfo isExprSafeToInline tree) UNIT + else BLOCK(tree, UNIT) + case x => + assert(x != ArrayClass) + // don't `setType pt` the Apply tree, as the Apply's fun won't be typechecked if the Apply tree already has a type + Apply(unboxMethod(pt.typeSymbol), tree) + } + } + typer.typedPos(tree.pos)(tree1) + } + + /** Generate a synthetic cast operation from tree.tpe to pt. + * @pre pt eq pt.normalize + */ + def cast(tree: Tree, pt: Type): Tree = { + if ((tree.tpe ne null) && !(tree.tpe =:= ObjectTpe)) { + def word = ( + if (tree.tpe <:< pt) "upcast" + else if (pt <:< tree.tpe) "downcast" + else if (pt weak_<:< tree.tpe) "coerce" + else if (tree.tpe weak_<:< pt) "widen" + else "cast" + ) + log(s"erasure ${word}s from ${tree.tpe} to $pt") + } + if (pt =:= UnitTpe) { + // See SI-4731 for one example of how this occurs. + log("Attempted to cast to Unit: " + tree) + tree.duplicate setType pt + } else if (tree.tpe != null && tree.tpe.typeSymbol == ArrayClass && pt.typeSymbol == ArrayClass) { + // See SI-2386 for one example of when this might be necessary. + val needsExtraCast = isPrimitiveValueType(tree.tpe.typeArgs.head) && !isPrimitiveValueType(pt.typeArgs.head) + val tree1 = if (needsExtraCast) gen.mkRuntimeCall(nme.toObjectArray, List(tree)) else tree + gen.mkAttributedCast(tree1, pt) + } else gen.mkAttributedCast(tree, pt) + } + + /** Adapt `tree` to expected type `pt`. + * + * @param tree the given tree + * @param pt the expected type + * @return the adapted tree + */ + def adaptToType(tree: Tree, pt: Type): Tree = { + if (settings.debug && pt != WildcardType) + log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug + if (tree.tpe <:< pt) + tree + else if (isDifferentErasedValueType(tree.tpe, pt)) + adaptToType(box(tree, pt.toString), pt) + else if (isDifferentErasedValueType(pt, tree.tpe)) + adaptToType(unbox(tree, pt), pt) + else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt)) { + adaptToType(box(tree, pt.toString), pt) + } else if (isMethodTypeWithEmptyParams(tree.tpe)) { + // [H] this assert fails when trying to typecheck tree !(SomeClass.this.bitmap) for single lazy val + //assert(tree.symbol.isStable, "adapt "+tree+":"+tree.tpe+" to "+pt) + adaptToType(Apply(tree, List()) setPos tree.pos setType tree.tpe.resultType, pt) +// } else if (pt <:< tree.tpe) +// cast(tree, pt) + } else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe)) + adaptToType(unbox(tree, pt), pt) + else + cast(tree, pt) + } + } +} \ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 7b88514429..ea680867da 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -153,6 +153,10 @@ trait Definitions extends api.StandardDefinitions { DoubleClass ) def ScalaPrimitiveValueClasses: List[ClassSymbol] = ScalaValueClasses + + def underlyingOfValueClass(clazz: Symbol): Type = + clazz.derivedValueClassUnbox.tpe.resultType + } abstract class DefinitionsClass extends DefinitionsApi with ValueClassDefinitions { diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 4aefc105e3..3eb3a4cdf4 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -77,9 +77,6 @@ trait Erasure { if (cls.owner.isClass) cls.owner.tpe_* else pre // why not cls.isNestedClass? } - def underlyingOfValueClass(clazz: Symbol): Type = - clazz.derivedValueClassUnbox.tpe.resultType - /** The type of the argument of a value class reference after erasure * This method needs to be called at a phase no later than erasurephase */ -- cgit v1.2.3 From 5d29697365245707af1a037678a7b48b0fef341c Mon Sep 17 00:00:00 2001 From: James Iry Date: Wed, 28 Aug 2013 20:41:12 -0700 Subject: Flesh out the Delambdafy phase. This commit puts a real body on the Delambdafy phase. From a lambda, Delambdafy will create 1) a static forwarder at the top level of the class that contained the lambda 2) a new top level class that a) has fields and a constructor taking the captured environment (including possbily the "this" reference) b) an apply method that calls the static forwarder c) if needed a bridge method for the apply method 3) an instantiation of the newly created class which replaces the lambda Trees.scala is modified to add two more convenient factories for templates and classdefs. A few basic tests are included to verify that it works as expected. Further commits will have additional tests. --- .../scala/tools/nsc/transform/Delambdafy.scala | 439 ++++++++++++++++++++- src/reflect/scala/reflect/internal/StdNames.scala | 1 + src/reflect/scala/reflect/internal/Trees.scala | 22 +- test/files/pos/delambdafy-lambdalift.scala | 8 + test/files/pos/delambdafy-patterns.scala | 15 + test/files/run/delambdafy-nested-by-name.check | 2 + test/files/run/delambdafy-nested-by-name.scala | 11 + test/files/run/delambdafy-two-lambdas.check | 2 + test/files/run/delambdafy-two-lambdas.scala | 12 + 9 files changed, 508 insertions(+), 4 deletions(-) create mode 100644 test/files/pos/delambdafy-lambdalift.scala create mode 100644 test/files/pos/delambdafy-patterns.scala create mode 100644 test/files/run/delambdafy-nested-by-name.check create mode 100644 test/files/run/delambdafy-nested-by-name.scala create mode 100644 test/files/run/delambdafy-two-lambdas.check create mode 100644 test/files/run/delambdafy-two-lambdas.scala diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index f39fd2eecb..c546c21d48 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -1,8 +1,38 @@ package scala.tools.nsc package transform -abstract class Delambdafy extends Transform with TypingTransformers with ast.TreeDSL { +import symtab._ +import Flags._ +import scala.collection._ +import scala.language.postfixOps +import scala.reflect.internal.Symbols +import scala.collection.mutable.LinkedHashMap + +/** + * This transformer is responisble for turning lambdas into anonymous classes. + * The main assumption it makes is that a lambda {args => body} has been turned into + * {args => liftedBody()} where lifted body is a top level method that implements the body of the lambda. + * Currently Uncurry is responsible for that transformation. + * + * From a lambda, Delambdafy will create + * 1) a static forwarder at the top level of the class that contained the lambda + * 2) a new top level class that + a) has fields and a constructor taking the captured environment (including possbily the "this" + * reference) + * b) an apply method that calls the static forwarder + * c) if needed a bridge method for the apply method + * 3) an instantiation of the newly created class which replaces the lambda + * + * TODO the main work left to be done is to plug into specialization. Primarily that means choosing a + * specialized FunctionN trait instead of the generic FunctionN trait as a parent and creating the + * appropriately named applysp method + */ +abstract class Delambdafy extends Transform with TypingTransformers with ast.TreeDSL with TypeAdaptingTransformer { import global._ + import definitions._ + import CODE._ + + val analyzer: global.analyzer.type = global.analyzer /** the following two members override abstract members in Transform */ val phaseName: String = "delambdafy" @@ -10,7 +40,410 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre protected def newTransformer(unit: CompilationUnit): Transformer = new DelambdafyTransformer(unit) - class DelambdafyTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { - override def transform(tree: Tree): Tree = tree + class DelambdafyTransformer(unit: CompilationUnit) extends TypingTransformer(unit) with TypeAdapter { + private val lambdaClassDefs = new mutable.LinkedHashMap[Symbol, List[Tree]] withDefaultValue Nil + + + val typer = localTyper + + // we need to know which methods refer to the 'this' reference so that we can determine + // which lambdas need access to it + val thisReferringMethods: Set[Symbol] = { + val thisReferringMethodsTraverser = new ThisReferringMethodsTraverser() + thisReferringMethodsTraverser traverse unit.body + val methodReferringMap = thisReferringMethodsTraverser.liftedMethodReferences + val referrers = thisReferringMethodsTraverser.thisReferringMethods + // recursively find methods that refer to 'this' directly or indirectly via references to other methods + // for each method found add it to the referrers set + def refersToThis(symbol: Symbol): Boolean = { + if (referrers contains symbol) true + else if (methodReferringMap(symbol) exists refersToThis) { + // add it early to memoize + debuglog(s"$symbol indirectly refers to 'this'") + referrers += symbol + true + } else false + } + methodReferringMap.keys foreach refersToThis + referrers + } + + val accessorMethods = mutable.ArrayBuffer[Tree]() + + // the result of the transformFunction method. A class definition for the lambda, an expression + // insantiating the lambda class, and an accessor method for the lambda class to be able to + // call the implementation + case class TransformedFunction(lambdaClassDef: ClassDef, newExpr: Tree, accessorMethod: Tree) + + // here's the main entry point of the transform + override def transform(tree: Tree): Tree = tree match { + // the main thing we care about is lambdas + case fun @ Function(_, _) => + // a lambda beccomes a new class, an instantiation expression, and an + // accessor method + val TransformedFunction(lambdaClassDef, newExpr, accessorMethod) = transformFunction(fun) + // we'll add accessor methods to the current template later + accessorMethods += accessorMethod + val pkg = lambdaClassDef.symbol.owner + + // we'll add the lambda class to the package later + lambdaClassDefs(pkg) = lambdaClassDef :: lambdaClassDefs(pkg) + + super.transform(newExpr) + // when we encounter a template (basically the thing that holds body of a class/trait) + // we need to updated it to include newly created accesor methods after transforming it + case Template(_, _, _) => + try { + // during this call accessorMethods will be populated from the Function case + val Template(parents, self, body) = super.transform(tree) + Template(parents, self, body ++ accessorMethods) + } finally accessorMethods.clear() + case _ => super.transform(tree) + } + + // this entry point is aimed at the statements in the compilation unit. + // after working on the entire compilation until we'll have a set of + // new class definitions to add to the top level + override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { + super.transformStats(stats, exprOwner) ++ lambdaClassDefs(exprOwner) + } + + private def optionSymbol(sym: Symbol): Option[Symbol] = if (sym.exists) Some(sym) else None + + // turns a lambda into a new class def, a New expression instantiating that class, and an + // accessor method fo the body of the lambda + private def transformFunction(originalFunction: Function): TransformedFunction = { + val functionTpe = originalFunction.tpe + val targs = functionTpe.typeArgs + val formals :+ restpe = targs + val oldClass = originalFunction.symbol.enclClass + + // find which variables are free in the lambda because those are captures that need to be + // passed into the constructor of the anonymous function class + val captures = FreeVarTraverser.freeVarsOf(originalFunction) + + /** + * Creates the apply method for the anonymous subclass of FunctionN + */ + def createAccessorMethod(thisProxy: Symbol, fun: Function): DefDef = { + val target = targetMethod(fun) + if (!thisProxy.exists) { + target setFlag STATIC + } + val params = ((optionSymbol(thisProxy) map {proxy:Symbol => ValDef(proxy)}) ++ (target.paramss.flatten map ValDef)).toList + + val methSym = oldClass.newMethod(unit.freshTermName(nme.accessor.toString()), target.pos, FINAL | BRIDGE | SYNTHETIC | PROTECTED | STATIC) + + val paramSyms = params map {param => methSym.newSyntheticValueParam(param.symbol.tpe, param.name) } + + params zip paramSyms foreach { case (valdef, sym) => valdef.symbol = sym } + params foreach (_.symbol.owner = methSym) + + val methodType = MethodType(paramSyms, restpe) + methSym setInfo methodType + + oldClass.info.decls enter methSym + + val body = localTyper.typed { + val newTarget = Select(if (thisProxy.exists) gen.mkAttributedRef(paramSyms(0)) else gen.mkAttributedThis(oldClass), target) + val newParams = paramSyms drop (if (thisProxy.exists) 1 else 0) map Ident + Apply(newTarget, newParams) + } setPos fun.pos + val methDef = DefDef(methSym, List(params), body) + + // Have to repack the type to avoid mismatches when existentials + // appear in the result - see SI-4869. + // TODO probably don't need packedType + methDef.tpt setType localTyper.packedType(body, methSym) + methDef + } + + /** + * Creates the apply method for the anonymous subclass of FunctionN + */ + def createApplyMethod(newClass: Symbol, fun: Function, accessor: DefDef, thisProxy: Symbol): DefDef = { + val methSym = newClass.newMethod(nme.apply, fun.pos, FINAL | SYNTHETIC) + val params = fun.vparams map (_.duplicate) + + val paramSyms = map2(formals, params) { + (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name) + } + params zip paramSyms foreach { case (valdef, sym) => valdef.symbol = sym } + params foreach (_.symbol.owner = methSym) + + val methodType = MethodType(paramSyms, restpe) + methSym setInfo methodType + + newClass.info.decls enter methSym + + val Apply(_, oldParams) = fun.body + + val body = localTyper typed Apply(Select(gen.mkAttributedThis(oldClass), accessor.symbol), (optionSymbol(thisProxy) map {tp => Select(gen.mkAttributedThis(newClass), tp)}).toList ++ oldParams) + body.substituteSymbols(fun.vparams map (_.symbol), params map (_.symbol)) + body changeOwner (fun.symbol -> methSym) + + val methDef = DefDef(methSym, List(params), body) + + // Have to repack the type to avoid mismatches when existentials + // appear in the result - see SI-4869. + // TODO probably don't need packedType + methDef.tpt setType localTyper.packedType(body, methSym) + methDef + } + + /** + * Creates the constructor on the newly created class. It will handle + * initialization of members that represent the captured environment + */ + def createConstructor(newClass: Symbol, members: List[ValDef]): DefDef = { + val constrSym = newClass.newConstructor(originalFunction.pos, SYNTHETIC) + + val (paramSymbols, params, assigns) = (members map {member => + val paramSymbol = newClass.newVariable(member.symbol.name.toTermName, newClass.pos, 0) + paramSymbol.setInfo(member.symbol.info) + val paramVal = ValDef(paramSymbol) + val paramIdent = Ident(paramSymbol) + val assign = Assign(Select(gen.mkAttributedThis(newClass), member.symbol), paramIdent) + + (paramSymbol, paramVal, assign) + }).unzip3 + + val constrType = MethodType(paramSymbols, newClass.thisType) + constrSym setInfoAndEnter constrType + + val body = + Block( + List( + Apply(Select(Super(gen.mkAttributedThis(newClass), tpnme.EMPTY) setPos newClass.pos, nme.CONSTRUCTOR) setPos newClass.pos, Nil) setPos newClass.pos + ) ++ assigns, + Literal(Constant(())): Tree + ) setPos newClass.pos + + (localTyper typed DefDef(constrSym, List(params), body) setPos newClass.pos).asInstanceOf[DefDef] + } + + val pkg = oldClass.owner + + // Parent for anonymous class def + val abstractFunctionErasedType = AbstractFunctionClass(formals.length).tpe + + // anonymous subclass of FunctionN with an apply method + def makeAnonymousClass = { + val parents = addSerializable(abstractFunctionErasedType) + val funOwner = originalFunction.symbol.owner + + val suffix = "$lambda$" + ( + if (funOwner.isPrimaryConstructor) "" + else "$" + funOwner.name + ) + val name = unit.freshTypeName(s"${oldClass.name.decode}$suffix") + + val anonClass = pkg newClassSymbol(name, originalFunction.pos, FINAL | SYNTHETIC) addAnnotation SerialVersionUIDAnnotation + anonClass setInfo ClassInfoType(parents, newScope, anonClass) + + val captureProxies2 = new LinkedHashMap[Symbol, TermSymbol] + captures foreach {capture => + val sym = anonClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC) + sym setInfo capture.info + captureProxies2 += ((capture, sym)) + } + + // the Optional proxy that will hold a reference to the 'this' + // object used by the lambda, if any. NoSymbol if there is no this proxy + val thisProxy = { + val target = targetMethod(originalFunction) + if (thisReferringMethods contains target) { + val sym = anonClass.newVariable(nme.FAKE_LOCAL_THIS, originalFunction.pos, SYNTHETIC) + sym.info = oldClass.tpe + sym + } else NoSymbol + } + + val decapturify = new DeCapturifyTransformer(captureProxies2, unit, oldClass, anonClass, originalFunction.symbol.pos, thisProxy) + + val accessorMethod = createAccessorMethod(thisProxy, originalFunction) + + val decapturedFunction = decapturify.transform(originalFunction).asInstanceOf[Function] + + val members = (optionSymbol(thisProxy).toList ++ (captureProxies2 map (_._2))) map {member => + anonClass.info.decls enter member + ValDef(member, gen.mkZero(member.tpe)) setPos decapturedFunction.pos + } + + // constructor + val constr = createConstructor(anonClass, members) + + // apply method with same arguments and return type as original lambda. + val applyMethodDef = createApplyMethod(anonClass, decapturedFunction, accessorMethod, thisProxy) + + val bridgeMethod = createBridgeMethod(anonClass, originalFunction, applyMethodDef) + + def fulldef(sym: Symbol) = + if (sym == NoSymbol) sym.toString + else s"$sym: ${sym.tpe} in ${sym.owner}" + + def clashError(bm: Symbol) = { + unit.error( + applyMethodDef.symbol.pos, + sm"""bridge generated for member ${fulldef(applyMethodDef.symbol)} + |which overrides ${fulldef(getMember(abstractFunctionErasedType.typeSymbol, nme.apply))} + |clashes with definition of the member itself; + |both have erased type ${exitingPostErasure(bm.tpe)}""") + } + + bridgeMethod foreach (bm => + if (bm.symbol.tpe =:= applyMethodDef.symbol.tpe) + clashError(bm.symbol) + ) + + val body = members ++ List(constr, applyMethodDef) ++ bridgeMethod + + // TODO if member fields are private this complains that they're not accessible + (localTyper.typedPos(decapturedFunction.pos)(ClassDef(anonClass, body)).asInstanceOf[ClassDef], thisProxy, accessorMethod) + } + + val (anonymousClassDef, thisProxy, accessorMethod) = makeAnonymousClass + + pkg.info.decls enter anonymousClassDef.symbol + + val thisArg = optionSymbol(thisProxy) map (_ => gen.mkAttributedThis(oldClass) setPos originalFunction.pos) + val captureArgs = captures map (capture => Ident(capture) setPos originalFunction.pos) + + val newStat = + Typed(New(anonymousClassDef.symbol, (thisArg.toList ++ captureArgs): _*), TypeTree(abstractFunctionErasedType)) + + val typedNewStat = localTyper.typedPos(originalFunction.pos)(newStat) + + TransformedFunction(anonymousClassDef, typedNewStat, accessorMethod) + } + + /** + * Creates a bridge method if needed. The bridge method forwards from apply(x1: Object, x2: Object...xn: Object): Object to + * apply(x1: T1, x2: T2...xn: Tn): T0 using type adaptation on each input and output. The only time a bridge isn't needed + * is when the original lambda is already erased to type Object, Object, Object... => Object + */ + def createBridgeMethod(newClass:Symbol, originalFunction: Function, applyMethod: DefDef): Option[DefDef] = { + val bridgeMethSym = newClass.newMethod(nme.apply, applyMethod.pos, FINAL | SYNTHETIC | BRIDGE) + val originalParams = applyMethod.vparamss(0) + val bridgeParams = originalParams map { originalParam => + val bridgeSym = bridgeMethSym.newSyntheticValueParam(ObjectTpe, originalParam.name) + ValDef(bridgeSym) + } + + val bridgeSyms = bridgeParams map (_.symbol) + + val methodType = MethodType(bridgeSyms, ObjectTpe) + bridgeMethSym setInfo methodType + + def adapt(tree: Tree, expectedTpe: Type): (Boolean, Tree) = { + if (tree.tpe =:= expectedTpe) (false, tree) + else (true, adaptToType(tree, expectedTpe)) + } + + enteringPhase(currentRun.posterasurePhase) { + val liftedBodyDefTpe: MethodType = { + val liftedBodySymbol = { + val Apply(method, _) = originalFunction.body + method.symbol + } + liftedBodySymbol.info.asInstanceOf[MethodType] + } + val (paramNeedsAdaptation, adaptedParams) = (bridgeSyms zip liftedBodyDefTpe.params map {case (bridgeSym, param) => adapt(Ident(bridgeSym) setType bridgeSym.tpe, param.tpe)}).unzip + val body = Apply(gen.mkAttributedSelect(gen.mkAttributedThis(newClass), applyMethod.symbol), adaptedParams) setType applyMethod.symbol.tpe.resultType + val (needsReturnAdaptation, adaptedBody) = adapt(typer.typed(body), ObjectTpe) + val needsBridge = (paramNeedsAdaptation contains true) || needsReturnAdaptation + if (needsBridge) { + val methDef = DefDef(bridgeMethSym, List(bridgeParams), adaptedBody) + newClass.info.decls enter bridgeMethSym + Some((localTyper typed methDef).asInstanceOf[DefDef]) + } else None + } + } + } // DelambdafyTransformer + + // A traverser that finds symbols used but not defined in the given Tree + // TODO freeVarTraverser in LambdaLift does a very similar task. With some + // analysis this could probably be unified with it + class FreeVarTraverser extends Traverser { + val freeVars = mutable.LinkedHashSet[Symbol]() + val declared = mutable.LinkedHashSet[Symbol]() + + override def traverse(tree: Tree) = { + tree match { + case Function(args, _) => + args foreach {arg => declared += arg.symbol} + case ValDef(_, _, _, _) => + declared += tree.symbol + case _: Bind => + declared += tree.symbol + case Ident(_) => + val sym = tree.symbol + if ((sym != NoSymbol) && sym.isLocal && sym.isTerm && !sym.isMethod && !declared.contains(sym)) freeVars += sym + case _ => + } + super.traverse(tree) + } + } + + object FreeVarTraverser { + def freeVarsOf(function: Function) = { + val freeVarsTraverser = new FreeVarTraverser + freeVarsTraverser.traverse(function) + freeVarsTraverser.freeVars + } + } + + // A transformer that converts specified captured symbols into other symbols + // TODO this transform could look more like ThisSubstituter and TreeSymSubstituter. It's not clear that it needs that level of sophistication since the types + // at this point are always very simple flattened/erased types, but it would probably be more robust if it tried to take more complicated types into account + class DeCapturifyTransformer(captureProxies: Map[Symbol, TermSymbol], unit: CompilationUnit, oldClass: Symbol, newClass:Symbol, pos: Position, thisProxy: Symbol) extends TypingTransformer(unit) { + override def transform(tree: Tree) = tree match { + case tree@This(encl) if tree.symbol == oldClass && thisProxy.exists => + gen mkAttributedSelect (gen mkAttributedThis newClass, thisProxy) + case Ident(name) if (captureProxies contains tree.symbol) => + gen mkAttributedSelect (gen mkAttributedThis newClass, captureProxies(tree.symbol)) + case _ => super.transform(tree) + } + } + + /** + * Get the symbol of the target lifted lambad body method from a function. I.e. if + * the function is {args => anonfun(args)} then this method returns anonfun's symbol + */ + private def targetMethod(fun: Function): Symbol = fun match { + case Function(_, Apply(target, _)) => + target.symbol + case _ => + // any other shape of Function is unexpected at this point + abort(s"could not understand function with tree $fun") + } + + // finds all methods that reference 'this' + class ThisReferringMethodsTraverser() extends Traverser { + private var currentMethod: Symbol = NoSymbol + // the set of methods that refer to this + val thisReferringMethods = mutable.Set[Symbol]() + // the set of lifted lambda body methods that each method refers to + val liftedMethodReferences = mutable.Map[Symbol, Set[Symbol]]().withDefault(_ => mutable.Set()) + override def traverse(tree: Tree) = tree match { + case DefDef(_, _, _, _, _, _) => + // we don't expect defs within defs. At this phase trees should be very flat + if (currentMethod.exists) devWarning("Found a def within a def at a phase where defs are expected to be flattened out.") + currentMethod = tree.symbol + super.traverse(tree) + currentMethod = NoSymbol + case fun@Function(_, _) => + // we don't drill into functions because at the beginning of this phase they will always refer to 'this'. + // They'll be of the form {(args...) => this.anonfun(args...)} + // but we do need to make note of the lifted body method in case it refers to 'this' + if (currentMethod.exists) liftedMethodReferences(currentMethod) += targetMethod(fun) + case This(_) => + if (currentMethod.exists && tree.symbol == currentMethod.enclClass) { + debuglog(s"$currentMethod directly refers to 'this'") + thisReferringMethods add currentMethod + } + case _ => + super.traverse(tree) + } } } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 9f56e78059..02f22a16f6 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -609,6 +609,7 @@ trait StdNames { val TypeRef: NameType = "TypeRef" val TypeTree: NameType = "TypeTree" val UNIT : NameType = "UNIT" + val accessor: NameType = "accessor" val add_ : NameType = "add" val annotation: NameType = "annotation" val anyValClass: NameType = "anyValClass" diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 9adddeed50..743c674eea 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1001,7 +1001,8 @@ trait Trees extends api.Trees { // ---- values and creators --------------------------------------- /** @param sym the class symbol - * @return the implementation template + * @param impl the implementation template + * @return the class definition */ def ClassDef(sym: Symbol, impl: Template): ClassDef = atPos(sym.pos) { @@ -1011,6 +1012,25 @@ trait Trees extends api.Trees { impl) setSymbol sym } + /** @param sym the class symbol + * @param body trees that constitute the body of the class + * @return the class definition + */ + def ClassDef(sym: Symbol, body: List[Tree]): ClassDef = + ClassDef(sym, Template(sym, body)) + + /** @param sym the template's symbol + * @param body trees that constitute the body of the template + * @return the template + */ + def Template(sym: Symbol, body: List[Tree]): Template = { + atPos(sym.pos) { + Template(sym.info.parents map TypeTree, + if (sym.thisSym == sym) noSelfType else ValDef(sym), + body) + } + } + /** * @param sym the class symbol * @param impl the implementation template diff --git a/test/files/pos/delambdafy-lambdalift.scala b/test/files/pos/delambdafy-lambdalift.scala new file mode 100644 index 0000000000..e9da24ef37 --- /dev/null +++ b/test/files/pos/delambdafy-lambdalift.scala @@ -0,0 +1,8 @@ +class LambdaLift { + + def enclosingMethod(capturedArg: Int): Unit = { + def innerMethod(x: Int): Int = x + capturedArg + val f = (y: Int) => innerMethod(y) + } + +} diff --git a/test/files/pos/delambdafy-patterns.scala b/test/files/pos/delambdafy-patterns.scala new file mode 100644 index 0000000000..95d498629b --- /dev/null +++ b/test/files/pos/delambdafy-patterns.scala @@ -0,0 +1,15 @@ +class DelambdafyPatterns { + def bar: Unit = () + def wildcardPatternInTryCatch: Unit => Unit = (x: Unit) => + // patterns in try..catch are preserved so we need to be + // careful when it comes to free variable detction + // in particular a is _not_ free variable, also the + // `_` identifier has no symbol attached to it + try bar catch { + case a@(_:java.lang.reflect.InvocationTargetException) => + // refer to a so we trigger a bug where a is considered + // to be a free variable for enclosing lambda + val b = a + () + } +} diff --git a/test/files/run/delambdafy-nested-by-name.check b/test/files/run/delambdafy-nested-by-name.check new file mode 100644 index 0000000000..94954abda4 --- /dev/null +++ b/test/files/run/delambdafy-nested-by-name.check @@ -0,0 +1,2 @@ +hello +world diff --git a/test/files/run/delambdafy-nested-by-name.scala b/test/files/run/delambdafy-nested-by-name.scala new file mode 100644 index 0000000000..4498b3308d --- /dev/null +++ b/test/files/run/delambdafy-nested-by-name.scala @@ -0,0 +1,11 @@ +// during development of delayed delambdafication I created a bug where calling a by-name method with a by-name argument that +// itself contained a by-name argument would cause a class cast exception. That bug wasn't found in the existing test suite +// so this test covers that case +object Test { + def meth1(arg1: => String) = arg1 + def meth2(arg2: => String) = meth1({println("hello"); arg2}) + + def main(args: Array[String]) { + println(meth2("world")) + } +} \ No newline at end of file diff --git a/test/files/run/delambdafy-two-lambdas.check b/test/files/run/delambdafy-two-lambdas.check new file mode 100644 index 0000000000..ed9ea404dd --- /dev/null +++ b/test/files/run/delambdafy-two-lambdas.check @@ -0,0 +1,2 @@ +13 +24 diff --git a/test/files/run/delambdafy-two-lambdas.scala b/test/files/run/delambdafy-two-lambdas.scala new file mode 100644 index 0000000000..decede74a4 --- /dev/null +++ b/test/files/run/delambdafy-two-lambdas.scala @@ -0,0 +1,12 @@ +/* + * Tests if two lambdas defined in the same class do not lead to + * name clashes. + */ +object Test { + def takeLambda(f: Int => Int ): Int = f(12) + + def main(args: Array[String]): Unit = { + println(takeLambda(x => x+1)) + println(takeLambda(x => x*2)) + } +} -- cgit v1.2.3 From f27f2d80d759130597b652d6cc9d789f8c472e5a Mon Sep 17 00:00:00 2001 From: James Iry Date: Wed, 28 Aug 2013 20:45:49 -0700 Subject: Make GenASM not eliminate loadmodule on static methods. During development of delayed delambdafy there was a problem where GenASM would eliminate a loadmodule for all methods defined within that module even if those methods were static. The result would be broken byte code that failed verification. This commit fixes that and adds a test. --- src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 3 ++- test/files/run/static-module-method.check | 1 + test/files/run/static-module-method.scala | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/files/run/static-module-method.check create mode 100644 test/files/run/static-module-method.scala diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 2f6f9620a8..8bbc382251 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -2370,7 +2370,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { case LOAD_MODULE(module) => // assert(module.isModule, "Expected module: " + module) debuglog("generating LOAD_MODULE for: " + module + " flags: " + module.flagString) - if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString) { + def inStaticMethod = this.method != null && this.method.symbol.isStaticMember + if (clasz.symbol == module.moduleClass && jMethodName != nme.readResolve.toString && !inStaticMethod) { jmethod.visitVarInsn(Opcodes.ALOAD, 0) } else { jmethod.visitFieldInsn( diff --git a/test/files/run/static-module-method.check b/test/files/run/static-module-method.check new file mode 100644 index 0000000000..ce01362503 --- /dev/null +++ b/test/files/run/static-module-method.check @@ -0,0 +1 @@ +hello diff --git a/test/files/run/static-module-method.scala b/test/files/run/static-module-method.scala new file mode 100644 index 0000000000..a8691300de --- /dev/null +++ b/test/files/run/static-module-method.scala @@ -0,0 +1,14 @@ +// During development of delayed delambdafy there was a problem where +// GenASM would eliminate a loadmodule for all methods defined within that module +// even if those methods were static. This test would thus fail +// with a verify error under -Ydelambdafy:method + +object Test { + def moduleMethod(x: String) = x + + def map(x: String, f: String => String) = f(x) + + def main(args: Array[String]) { + println(map("hello", Test.moduleMethod)) + } +} \ No newline at end of file -- cgit v1.2.3 From 13ea5909ea25e4bad184e9e4581d94230dc11d9f Mon Sep 17 00:00:00 2001 From: James Iry Date: Wed, 28 Aug 2013 20:48:47 -0700 Subject: Make specialization aware of anonymous functions. During development of late delmabdafying there was a problem where specialization would undo some of the work done in uncurry if the body of the lambda had a constant type. That would result in a compiler crash as when the delambdafy phase got a tree shape it didn't understand. This commit has a fix and a test. --- .../scala/tools/nsc/transform/SpecializeTypes.scala | 8 +++++++- .../scala/tools/nsc/typechecker/Duplicators.scala | 13 +++++++++++-- test/files/specialized/constant_lambda.check | 2 ++ test/files/specialized/constant_lambda.scala | 16 ++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 test/files/specialized/constant_lambda.check create mode 100644 test/files/specialized/constant_lambda.scala diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 2d4269a3bc..4cf3bef939 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -55,6 +55,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { import definitions._ import Flags._ + private val inlineFunctionExpansion = settings.Ydelambdafy.value == "inline" + /** the name of the phase: */ val phaseName: String = "specialize" @@ -1318,7 +1320,11 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } private def isAccessible(sym: Symbol): Boolean = - (currentClass == sym.owner.enclClass) && (currentClass != targetClass) + if (currentOwner.isAnonymousFunction) { + if (inlineFunctionExpansion) devWarning("anonymous function made it to specialization even though inline expansion is set.") + false + } + else (currentClass == sym.owner.enclClass) && (currentClass != targetClass) private def shouldMakePublic(sym: Symbol): Boolean = sym.hasFlag(PRIVATE | PROTECTED) && (addressFields || !nme.isLocalName(sym.name)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 396f3407f3..69ae6ec0c8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -136,8 +136,8 @@ abstract class Duplicators extends Analyzer { sym private def invalidate(tree: Tree, owner: Symbol = NoSymbol) { - debuglog("attempting to invalidate " + tree.symbol) - if (tree.isDef && tree.symbol != NoSymbol) { + debuglog(s"attempting to invalidate symbol = ${tree.symbol}") + if ((tree.isDef || tree.isInstanceOf[Function]) && tree.symbol != NoSymbol) { debuglog("invalid " + tree.symbol) invalidSyms(tree.symbol) = tree @@ -166,6 +166,11 @@ abstract class Duplicators extends Analyzer { invalidateAll(tparams ::: vparamss.flatten) tree.symbol = NoSymbol + case Function(vparams, _) => + // invalidate parameters + invalidateAll(vparams) + tree.symbol = NoSymbol + case _ => tree.symbol = NoSymbol } @@ -226,6 +231,10 @@ abstract class Duplicators extends Analyzer { ddef.tpt modifyType fixType super.typed(ddef.clearType(), mode, pt) + case fun: Function => + debuglog("Clearing the type and retyping Function: " + fun) + super.typed(fun.clearType, mode, pt) + case vdef @ ValDef(mods, name, tpt, rhs) => // log("vdef fixing tpe: " + tree.tpe + " with sym: " + tree.tpe.typeSymbol + " and " + invalidSyms) //if (mods.hasFlag(Flags.LAZY)) vdef.symbol.resetFlag(Flags.MUTABLE) // Martin to Iulian: lazy vars can now appear because they are no longer boxed; Please check that deleting this statement is OK. diff --git a/test/files/specialized/constant_lambda.check b/test/files/specialized/constant_lambda.check new file mode 100644 index 0000000000..4b095fd0ff --- /dev/null +++ b/test/files/specialized/constant_lambda.check @@ -0,0 +1,2 @@ +false +false diff --git a/test/files/specialized/constant_lambda.scala b/test/files/specialized/constant_lambda.scala new file mode 100644 index 0000000000..bb9a97403e --- /dev/null +++ b/test/files/specialized/constant_lambda.scala @@ -0,0 +1,16 @@ +// during development of late delmabdafying there was a problem where +// specialization would undo some of the work done in uncurry if the body of the +// lambda had a constant type. That would result in a compiler crash as +// when the delambdafy phase got a tree shape it didn't understand +class X[@specialized(Int) A] { + val f = { x: A => false } +} + +object Test { + def main(args: Array[String]) { + val xInt = new X[Int] + println(xInt.f(42)) + val xString = new X[String] + println(xString.f("hello")) + } +} \ No newline at end of file -- cgit v1.2.3 From 8d4a5bab2666c7006f31645b59cf063f07a8a12d Mon Sep 17 00:00:00 2001 From: James Iry Date: Wed, 28 Aug 2013 20:51:31 -0700 Subject: Create test variants where delambdafication alters signatures. This commit includes several tests where there's a variation in signatures between inline delambdafication and method based delambdafication. --- test/files/neg/delambdafy_t6260_method.check | 13 +++++++ test/files/neg/delambdafy_t6260_method.flags | 1 + test/files/neg/delambdafy_t6260_method.scala | 17 +++++++++ test/files/neg/t6260.flags | 1 + test/files/run/delambdafy_t6028.check | 57 ++++++++++++++++++++++++++++ test/files/run/delambdafy_t6028.scala | 21 ++++++++++ test/files/run/delambdafy_t6555.check | 15 ++++++++ test/files/run/delambdafy_t6555.scala | 15 ++++++++ test/files/run/t6028.scala | 2 +- test/files/run/t6555.scala | 2 +- 10 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/delambdafy_t6260_method.check create mode 100644 test/files/neg/delambdafy_t6260_method.flags create mode 100644 test/files/neg/delambdafy_t6260_method.scala create mode 100644 test/files/neg/t6260.flags create mode 100644 test/files/run/delambdafy_t6028.check create mode 100644 test/files/run/delambdafy_t6028.scala create mode 100644 test/files/run/delambdafy_t6555.check create mode 100644 test/files/run/delambdafy_t6555.scala diff --git a/test/files/neg/delambdafy_t6260_method.check b/test/files/neg/delambdafy_t6260_method.check new file mode 100644 index 0000000000..f5cd6947d1 --- /dev/null +++ b/test/files/neg/delambdafy_t6260_method.check @@ -0,0 +1,13 @@ +delambdafy_t6260_method.scala:3: error: bridge generated for member method apply: (bx: Object)Object in class map$extension1 +which overrides method apply: (v1: Object)Object in trait Function1 +clashes with definition of the member itself; +both have erased type (bx: Object)Object + ((bx: Box[X]) => new Box(f(bx.x)))(this) + ^ +delambdafy_t6260_method.scala:8: error: bridge generated for member method apply: (bx: Object)Object in class map21 +which overrides method apply: (v1: Object)Object in trait Function1 +clashes with definition of the member itself; +both have erased type (bx: Object)Object + ((bx: Box[X]) => new Box(f(bx.x)))(self) + ^ +two errors found diff --git a/test/files/neg/delambdafy_t6260_method.flags b/test/files/neg/delambdafy_t6260_method.flags new file mode 100644 index 0000000000..48b438ddf8 --- /dev/null +++ b/test/files/neg/delambdafy_t6260_method.flags @@ -0,0 +1 @@ +-Ydelambdafy:method diff --git a/test/files/neg/delambdafy_t6260_method.scala b/test/files/neg/delambdafy_t6260_method.scala new file mode 100644 index 0000000000..93b5448227 --- /dev/null +++ b/test/files/neg/delambdafy_t6260_method.scala @@ -0,0 +1,17 @@ +class Box[X](val x: X) extends AnyVal { + def map[Y](f: X => Y): Box[Y] = + ((bx: Box[X]) => new Box(f(bx.x)))(this) +} + +object Test { + def map2[X, Y](self: Box[X], f: X => Y): Box[Y] = + ((bx: Box[X]) => new Box(f(bx.x)))(self) + + def main(args: Array[String]) { + val f = (x: Int) => x + 1 + val g = (x: String) => x + x + + map2(new Box(42), f) + new Box("abc") map g + } +} diff --git a/test/files/neg/t6260.flags b/test/files/neg/t6260.flags new file mode 100644 index 0000000000..2349d8294d --- /dev/null +++ b/test/files/neg/t6260.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline diff --git a/test/files/run/delambdafy_t6028.check b/test/files/run/delambdafy_t6028.check new file mode 100644 index 0000000000..92cfbaefb6 --- /dev/null +++ b/test/files/run/delambdafy_t6028.check @@ -0,0 +1,57 @@ +[[syntax trees at end of lambdalift]] // newSource1.scala +package { + class T extends Object { + private[this] val classParam: Int = _; + def (classParam: Int): T = { + T.super.(); + () + }; + private[this] val field: Int = 0; + def field(): Int = T.this.field; + def foo(methodParam: Int): Function0 = { + val methodLocal: Int = 0; + { + (() => T.this.$anonfun$1(methodParam, methodLocal)).$asInstanceOf[Function0]() + } + }; + def bar(barParam: Int): Object = { + @volatile var MethodLocalObject$module: runtime.VolatileObjectRef = scala.runtime.VolatileObjectRef.zero(); + T.this.MethodLocalObject$1(barParam, MethodLocalObject$module) + }; + def tryy(tryyParam: Int): Function0 = { + var tryyLocal: runtime.IntRef = scala.runtime.IntRef.create(0); + { + (() => T.this.$anonfun$2(tryyParam, tryyLocal)).$asInstanceOf[Function0]() + } + }; + final private[this] def $anonfun$1(methodParam$1: Int, methodLocal$1: Int): Int = T.this.classParam.+(T.this.field()).+(methodParam$1).+(methodLocal$1); + abstract trait MethodLocalTrait$1 extends Object { + def $outer(): T + }; + object MethodLocalObject$2 extends Object with T#MethodLocalTrait$1 { + def ($outer: T, barParam$1: Int): T#MethodLocalObject$2.type = { + MethodLocalObject$2.super.(); + MethodLocalObject$2.this.$asInstanceOf[T#MethodLocalTrait$1$class]()./*MethodLocalTrait$1$class*/$init$(barParam$1); + () + }; + private[this] val $outer: T = _; + def $outer(): T = MethodLocalObject$2.this.$outer; + def $outer(): T = MethodLocalObject$2.this.$outer + }; + final private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = { + MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1); + MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]() + }; + abstract trait MethodLocalTrait$1$class extends Object with T#MethodLocalTrait$1 { + def /*MethodLocalTrait$1$class*/$init$(barParam$1: Int): Unit = { + () + }; + scala.this.Predef.print(scala.Int.box(barParam$1)) + }; + final private[this] def $anonfun$2(tryyParam$1: Int, tryyLocal$1: runtime.IntRef): Unit = try { + tryyLocal$1.elem = tryyParam$1 + } finally () + } +} + +warning: there were 1 feature warning(s); re-run with -feature for details diff --git a/test/files/run/delambdafy_t6028.scala b/test/files/run/delambdafy_t6028.scala new file mode 100644 index 0000000000..0b7ef48c3d --- /dev/null +++ b/test/files/run/delambdafy_t6028.scala @@ -0,0 +1,21 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp -Ydelambdafy:method -Xprint:lambdalift -d " + testOutput.path + + override def code = """class T(classParam: Int) { + | val field: Int = 0 + | def foo(methodParam: Int) = {val methodLocal = 0 ; () => classParam + field + methodParam + methodLocal } + | def bar(barParam: Int) = { trait MethodLocalTrait { print(barParam) }; object MethodLocalObject extends MethodLocalTrait; MethodLocalObject } + | def tryy(tryyParam: Int) = { var tryyLocal = 0; () => try { tryyLocal = tryyParam } finally () } + |} + |""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} diff --git a/test/files/run/delambdafy_t6555.check b/test/files/run/delambdafy_t6555.check new file mode 100644 index 0000000000..6b174c0d2a --- /dev/null +++ b/test/files/run/delambdafy_t6555.check @@ -0,0 +1,15 @@ +[[syntax trees at end of specialize]] // newSource1.scala +package { + class Foo extends Object { + def (): Foo = { + Foo.super.(); + () + }; + private[this] val f: Int => Int = { + final def $anonfun(param: Int): Int = param; + ((param: Int) => $anonfun(param)) + }; + def f(): Int => Int = Foo.this.f + } +} + diff --git a/test/files/run/delambdafy_t6555.scala b/test/files/run/delambdafy_t6555.scala new file mode 100644 index 0000000000..a1dcfe790c --- /dev/null +++ b/test/files/run/delambdafy_t6555.scala @@ -0,0 +1,15 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp -Xprint:specialize -Ydelambdafy:method -d " + testOutput.path + + override def code = "class Foo { val f = (param: Int) => param } " + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} diff --git a/test/files/run/t6028.scala b/test/files/run/t6028.scala index cab17535fc..a6f920c5bb 100644 --- a/test/files/run/t6028.scala +++ b/test/files/run/t6028.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:lambdalift -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Ydelambdafy:inline -Xprint:lambdalift -d " + testOutput.path override def code = """class T(classParam: Int) { | val field: Int = 0 diff --git a/test/files/run/t6555.scala b/test/files/run/t6555.scala index b1a6137786..cc0e4d1bfa 100644 --- a/test/files/run/t6555.scala +++ b/test/files/run/t6555.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:specialize -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Xprint:specialize -Ydelambdafy:inline -d " + testOutput.path override def code = "class Foo { val f = (param: Int) => param } " -- cgit v1.2.3 From ef995ac6b4030e3cd10e487d414c941e5794666f Mon Sep 17 00:00:00 2001 From: James Iry Date: Wed, 28 Aug 2013 20:55:05 -0700 Subject: Force several tests to run using inline delambdafication. The differences when running with method based delambdafication aren't important enough yet to create specialized versions that use method based delambdafication. --- test/files/instrumented/inline-in-constructors.flags | 2 +- test/files/neg/t6231.flags | 1 + test/files/neg/t6666.flags | 1 + test/files/neg/t6666c.flags | 1 + test/files/run/origins.flags | 2 +- test/files/run/primitive-sigs-2-new.flags | 1 + test/files/run/primitive-sigs-2-old.flags | 1 + test/files/run/t1167.flags | 1 + test/files/run/t3897.flags | 1 + 9 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 test/files/neg/t6231.flags create mode 100644 test/files/neg/t6666.flags create mode 100644 test/files/neg/t6666c.flags create mode 100644 test/files/run/primitive-sigs-2-new.flags create mode 100644 test/files/run/primitive-sigs-2-old.flags create mode 100644 test/files/run/t1167.flags create mode 100644 test/files/run/t3897.flags diff --git a/test/files/instrumented/inline-in-constructors.flags b/test/files/instrumented/inline-in-constructors.flags index c9b68d70dc..068318e8ac 100644 --- a/test/files/instrumented/inline-in-constructors.flags +++ b/test/files/instrumented/inline-in-constructors.flags @@ -1 +1 @@ --optimise +-optimise -Ydelambdafy:inline diff --git a/test/files/neg/t6231.flags b/test/files/neg/t6231.flags new file mode 100644 index 0000000000..ac96850b69 --- /dev/null +++ b/test/files/neg/t6231.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline \ No newline at end of file diff --git a/test/files/neg/t6666.flags b/test/files/neg/t6666.flags new file mode 100644 index 0000000000..2349d8294d --- /dev/null +++ b/test/files/neg/t6666.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline diff --git a/test/files/neg/t6666c.flags b/test/files/neg/t6666c.flags new file mode 100644 index 0000000000..2349d8294d --- /dev/null +++ b/test/files/neg/t6666c.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline diff --git a/test/files/run/origins.flags b/test/files/run/origins.flags index a7e64e4f0c..690753d807 100644 --- a/test/files/run/origins.flags +++ b/test/files/run/origins.flags @@ -1 +1 @@ --no-specialization \ No newline at end of file +-no-specialization -Ydelambdafy:inline \ No newline at end of file diff --git a/test/files/run/primitive-sigs-2-new.flags b/test/files/run/primitive-sigs-2-new.flags new file mode 100644 index 0000000000..2349d8294d --- /dev/null +++ b/test/files/run/primitive-sigs-2-new.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline diff --git a/test/files/run/primitive-sigs-2-old.flags b/test/files/run/primitive-sigs-2-old.flags new file mode 100644 index 0000000000..ac96850b69 --- /dev/null +++ b/test/files/run/primitive-sigs-2-old.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline \ No newline at end of file diff --git a/test/files/run/t1167.flags b/test/files/run/t1167.flags new file mode 100644 index 0000000000..ac96850b69 --- /dev/null +++ b/test/files/run/t1167.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline \ No newline at end of file diff --git a/test/files/run/t3897.flags b/test/files/run/t3897.flags new file mode 100644 index 0000000000..ac96850b69 --- /dev/null +++ b/test/files/run/t3897.flags @@ -0,0 +1 @@ +-Ydelambdafy:inline \ No newline at end of file -- cgit v1.2.3