summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2013-11-07 10:56:47 -0800
committerAdriaan Moors <adriaan.moors@typesafe.com>2013-11-07 10:56:47 -0800
commite72c11e18062b4e281cc38fb7727b3bd144c8594 (patch)
treec73549ee81ec9aacab999fc68673133940d5ae26
parentbb02fb350f7ec4aefcc830fec36a964ae45cfdd4 (diff)
parentef995ac6b4030e3cd10e487d414c941e5794666f (diff)
downloadscala-e72c11e18062b4e281cc38fb7727b3bd144c8594.tar.gz
scala-e72c11e18062b4e281cc38fb7727b3bd144c8594.tar.bz2
scala-e72c11e18062b4e281cc38fb7727b3bd144c8594.zip
Merge pull request #3081 from JamesIry/wip_delayed_delambdafy_cleanup
Delay delambdafication and put the lambda's body into the containing class
-rw-r--r--src/compiler/scala/tools/ant/Scalac.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala11
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala3
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala449
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala170
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala8
-rw-r--r--src/compiler/scala/tools/nsc/transform/TypeAdaptingTransformer.scala187
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala127
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala13
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala4
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala22
-rw-r--r--src/reflect/scala/reflect/internal/transform/Erasure.scala3
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Phased.scala3
-rw-r--r--test/files/instrumented/inline-in-constructors.flags2
-rw-r--r--test/files/jvm/future-spec/FutureTests.scala2
-rw-r--r--test/files/jvm/future-spec/PromiseTests.scala2
-rw-r--r--test/files/jvm/future-spec/TryTests.scala2
-rw-r--r--test/files/jvm/future-spec/main.scala6
-rw-r--r--test/files/jvm/t7006.check1
-rw-r--r--test/files/neg/delambdafy_t6260_method.check13
-rw-r--r--test/files/neg/delambdafy_t6260_method.flags1
-rw-r--r--test/files/neg/delambdafy_t6260_method.scala17
-rw-r--r--test/files/neg/t6231.flags1
-rw-r--r--test/files/neg/t6260.flags1
-rwxr-xr-xtest/files/neg/t6446-additional.check25
-rwxr-xr-xtest/files/neg/t6446-missing.check21
-rw-r--r--test/files/neg/t6446-show-phases.check21
-rw-r--r--test/files/neg/t6666.flags1
-rw-r--r--test/files/neg/t6666c.flags1
-rw-r--r--test/files/neg/t7494-no-options.check25
-rw-r--r--test/files/pos/delambdafy-lambdalift.scala8
-rw-r--r--test/files/pos/delambdafy-patterns.scala15
-rw-r--r--test/files/run/delambdafy-nested-by-name.check2
-rw-r--r--test/files/run/delambdafy-nested-by-name.scala11
-rw-r--r--test/files/run/delambdafy-two-lambdas.check2
-rw-r--r--test/files/run/delambdafy-two-lambdas.scala12
-rw-r--r--test/files/run/delambdafy_t6028.check57
-rw-r--r--test/files/run/delambdafy_t6028.scala21
-rw-r--r--test/files/run/delambdafy_t6555.check15
-rw-r--r--test/files/run/delambdafy_t6555.scala15
-rw-r--r--test/files/run/delambdafy_uncurry_byname_inline.check21
-rw-r--r--test/files/run/delambdafy_uncurry_byname_inline.scala20
-rw-r--r--test/files/run/delambdafy_uncurry_byname_method.check15
-rw-r--r--test/files/run/delambdafy_uncurry_byname_method.scala20
-rw-r--r--test/files/run/delambdafy_uncurry_inline.check23
-rw-r--r--test/files/run/delambdafy_uncurry_inline.scala20
-rw-r--r--test/files/run/delambdafy_uncurry_method.check17
-rw-r--r--test/files/run/delambdafy_uncurry_method.scala20
-rw-r--r--test/files/run/origins.flags2
-rw-r--r--test/files/run/primitive-sigs-2-new.flags1
-rw-r--r--test/files/run/primitive-sigs-2-old.flags1
-rw-r--r--test/files/run/programmatic-main.check7
-rw-r--r--test/files/run/static-module-method.check1
-rw-r--r--test/files/run/static-module-method.scala14
-rw-r--r--test/files/run/t1167.flags1
-rw-r--r--test/files/run/t3897.flags1
-rw-r--r--test/files/run/t6028.scala2
-rw-r--r--test/files/run/t6102.check1
-rw-r--r--test/files/run/t6555.scala2
-rw-r--r--test/files/specialized/constant_lambda.check2
-rw-r--r--test/files/specialized/constant_lambda.scala16
64 files changed, 1261 insertions, 253 deletions
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/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/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/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
new file mode 100644
index 0000000000..c546c21d48
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -0,0 +1,449 @@
+package scala.tools.nsc
+package transform
+
+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"
+
+ protected def newTransformer(unit: CompilationUnit): Transformer =
+ new DelambdafyTransformer(unit)
+
+ 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/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/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/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/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/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/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/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/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
*/
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/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/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()
}
}
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/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/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/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/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/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/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/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))
+ }
+}
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 <empty> {
+ class T extends Object {
+ <paramaccessor> private[this] val classParam: Int = _;
+ def <init>(classParam: Int): T = {
+ T.super.<init>();
+ ()
+ };
+ private[this] val field: Int = 0;
+ <stable> <accessor> 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 <artifact> 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 {
+ <synthetic> <stable> <artifact> def $outer(): T
+ };
+ object MethodLocalObject$2 extends Object with T#MethodLocalTrait$1 {
+ def <init>($outer: T, barParam$1: Int): T#MethodLocalObject$2.type = {
+ MethodLocalObject$2.super.<init>();
+ MethodLocalObject$2.this.$asInstanceOf[T#MethodLocalTrait$1$class]()./*MethodLocalTrait$1$class*/$init$(barParam$1);
+ ()
+ };
+ <synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _;
+ <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
+ <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
+ };
+ final <stable> 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 <artifact> 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 <empty> {
+ class Foo extends Object {
+ def <init>(): Foo = {
+ Foo.super.<init>();
+ ()
+ };
+ private[this] val f: Int => Int = {
+ final <artifact> def $anonfun(param: Int): Int = param;
+ ((param: Int) => $anonfun(param))
+ };
+ <stable> <accessor> 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/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 <empty> {
+ class Foo extends Object {
+ def <init>(): Foo = {
+ Foo.super.<init>();
+ ()
+ };
+ def bar(x: () => Int): Int = x.apply();
+ def foo(): Int = Foo.this.bar({
+ @SerialVersionUID(0) final <synthetic> class $anonfun extends scala.runtime.AbstractFunction0[Int] with Serializable {
+ def <init>(): <$anon: () => Int> = {
+ $anonfun.super.<init>();
+ ()
+ };
+ 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 <empty> {
+ class Foo extends Object {
+ def <init>(): Foo = {
+ Foo.super.<init>();
+ ()
+ };
+ def bar(x: () => Int): Int = x.apply();
+ def foo(): Int = Foo.this.bar({
+ final <artifact> 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 <empty> {
+ class Foo extends Object {
+ def <init>(): Foo = {
+ Foo.super.<init>();
+ ()
+ };
+ def bar(): Unit = {
+ val f: Int => Int = {
+ @SerialVersionUID(0) final <synthetic> class $anonfun extends scala.runtime.AbstractFunction1[Int,Int] with Serializable {
+ def <init>(): <$anon: Int => Int> = {
+ $anonfun.super.<init>();
+ ()
+ };
+ 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 <empty> {
+ class Foo extends Object {
+ def <init>(): Foo = {
+ Foo.super.<init>();
+ ()
+ };
+ def bar(): Unit = {
+ val f: Int => Int = {
+ final <artifact> 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()
+ }
+ }
+}
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/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/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
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
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/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]
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 } "
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