Skip to content

Commit

Permalink
Adds a setting to delay delambdafication. If set then uncurry lifts
Browse files Browse the repository at this point in the history
the body of a lambda into a local def. Tests are included to show the
different tree shapes.
  • Loading branch information
James Iry committed Jul 23, 2013
1 parent 15ef90d commit 52cdea1
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Expand Up @@ -177,6 +177,7 @@ trait ScalaSettings extends AbsScalaSettings
val YdisableUnreachablePrevention = BooleanSetting("-Ydisable-unreachable-prevention", "Disable the prevention of unreachable blocks in code generation.")

val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly()
val Ydelambdafy = ChoiceSetting ("-Ydelambdafy", "strategy", "Strategy used for translating lambdas into JVM code.", List("inline", "method"), "inline")

/** Area-specific debug output.
*/
Expand Down
38 changes: 36 additions & 2 deletions src/compiler/scala/tools/nsc/transform/UnCurry.scala
Expand Up @@ -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 inPattern = false
private var inConstructorFlag = 0L
Expand Down Expand Up @@ -278,7 +279,10 @@ abstract class UnCurry extends InfoTransform

val funTyper = localTyper.typedPos(fun.pos) _

val block = {
val block = if (inlineFunctionExpansion) {
// if delambdafy strategy is inline then
// fall back to putting the body of the lambda directly in the anonymous class
// anonymous subclass of FunctionN with an apply method
val anonymousClassDef = {
val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe))
val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation
Expand All @@ -298,6 +302,32 @@ abstract class UnCurry extends InfoTransform
List(anonymousClassDef),
Typed(New(anonymousClassDef.symbol), TypeTree(fun.tpe))
)
} else {
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 liftedMethodCall = funTyper(Apply(liftedMethod.symbol, args:_*))

// new function whose body is just a call to the lifted method
val newFun = fun.replace(fun.body, liftedMethodCall)
Block(
List(funTyper(liftedMethod)),
super.transform(newFun)
)
}
funTyper(block)
}
Expand Down Expand Up @@ -433,7 +463,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 --------------------------------------------------------

Expand Down Expand Up @@ -546,6 +576,10 @@ abstract class UnCurry extends InfoTransform
val pat1 = withInPattern(value = true)(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) && target.symbol.isLocal && target.symbol.isArtifact && target.symbol.name.containsName(nme.ANON_FUN_NAME) =>
super.transform(fun)

case fun @ Function(_, _) =>
mainTransform(transformFunction(fun))

Expand Down
23 changes: 23 additions & 0 deletions test/files/run/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>(): anonymous class $anonfun = {
$anonfun.super.<init>();
()
};
final def apply(x: Int): Int = x.+(1)
};
(new anonymous class $anonfun(): Int => Int)
};
()
}
}
}

20 changes: 20 additions & 0 deletions test/files/run/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()
}
}
}
17 changes: 17 additions & 0 deletions test/files/run/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))
};
()
}
}
}

20 changes: 20 additions & 0 deletions test/files/run/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 -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()
}
}
}

1 comment on commit 52cdea1

@scala-jenkins
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Job pr-scala failed for 52cdea1 Took 4 min. (results):


To retry exactly this commit (if the failure was spurious), comment "PLS REBUILD/pr-scala@52cdea193ee91301197b40823ac7a9354bd60a14" on PR 2887.NOTE: new commits are rebuilt automatically as they appear. There's no need to force a rebuild if you're updating the PR.

Please sign in to comment.