summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2016-02-05 13:33:39 -0800
committerAdriaan Moors <adriaan.moors@typesafe.com>2016-03-26 22:49:10 -0700
commitad90614c391328bdc9d13795d26f339f67e7f4d5 (patch)
tree57b130ab18b2d607422e0a6c77d92af19ddae6a4
parent0d89ab1fe4adfe7165a2e3812d76dbb586c79798 (diff)
downloadscala-ad90614c391328bdc9d13795d26f339f67e7f4d5.tar.gz
scala-ad90614c391328bdc9d13795d26f339f67e7f4d5.tar.bz2
scala-ad90614c391328bdc9d13795d26f339f67e7f4d5.zip
Refactoring. Sweep Sammy's backyard.
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala46
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala200
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala132
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala39
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala5
5 files changed, 190 insertions, 232 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 7edac76b91..7b0216a6d7 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -278,26 +278,44 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
* @param additionalFlags flags to be put on the method in addition to FINAL
*/
def mkMethodFromFunction(localTyper: analyzer.Typer)
- (fun: Function, owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags) = {
- val funParams = fun.vparams map (_.symbol)
- val formals :+ restpe = fun.tpe.typeArgs
+ (owner: Symbol, fun: Function, name: TermName = nme.apply, additionalFlags: FlagSet = NoFlags) = {
+ val funParamSyms = fun.vparams.map(_.symbol)
val methSym = owner.newMethod(name, fun.pos, FINAL | additionalFlags)
+ val methParamSyms = funParamSyms.map { param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName) }
- val paramSyms = map2(formals, fun.vparams) {
- (tp, vparam) => methSym.newSyntheticValueParam(tp, vparam.name)
- }
+ val body = fun.body
+ body substituteSymbols (funParamSyms, methParamSyms)
+ body changeOwner ((fun.symbol, methSym))
- methSym setInfo MethodType(paramSyms, restpe.deconst)
+ // Have to repack the type to avoid mismatches when existentials
+ // appear in the result - see SI-4869.
+ val resTp = localTyper.packedType(body, methSym).deconst
+ methSym setInfo MethodType(methParamSyms, fun.tpe.typeArgs.last.deconst) // TODO: use `resTp` -- preserving wrong status quo because refactoring-only
- fun.body.substituteSymbols(funParams, paramSyms)
- fun.body changeOwner (fun.symbol -> methSym)
+ newDefDef(methSym, body)(tpt = TypeTree(resTp))
+ }
- val methDef = DefDef(methSym, fun.body)
+ def functionClassType(fun: Function): Type =
+ abstractFunctionType(fun.vparams.map(_.symbol.tpe), fun.tpe.typeArgs.last.deconst) // TODO: use `fun.body.tpe.deconst` -- preserving wrong status quo because refactoring-only
- // Have to repack the type to avoid mismatches when existentials
- // appear in the result - see SI-4869.
- methDef.tpt setType localTyper.packedType(fun.body, methSym).deconst
- methDef
+ def expandFunction(localTyper: analyzer.Typer)(fun: Function, inConstructorFlag: Long): Tree = {
+ val parents = addSerializable(functionClassType(fun))
+ val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation
+
+ // The original owner is used in the backend for the EnclosingMethod attribute. If fun is
+ // nested in a value-class method, its owner was already changed to the extension method.
+ // Saving the original owner allows getting the source structure from the class symbol.
+ defineOriginalOwner(anonClass, fun.symbol.originalOwner)
+ anonClass setInfo ClassInfoType(parents, newScope, anonClass)
+
+ val applyMethodDef = mkMethodFromFunction(localTyper)(anonClass, fun)
+ anonClass.info.decls enter applyMethodDef.symbol
+
+ localTyper.typedPos(fun.pos) {
+ Block(
+ ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos),
+ Typed(New(anonClass.tpe), TypeTree(fun.tpe)))
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 8799ed2e04..43d24be4d5 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -29,6 +29,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
/** the following two members override abstract members in Transform */
val phaseName: String = "delambdafy"
+ final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol)
+
override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = {
if (settings.Ydelambdafy.value == "method") new Phase(prev)
else new SkipPhase(prev)
@@ -46,9 +48,9 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
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] = {
+ // we need to know which methods refer to the 'this' reference so that we can determine which lambdas need access to it
+ // TODO: this looks expensive, so I made it a lazy val. Can we make it more pay-as-you-go / optimize for common shapes?
+ lazy val thisReferringMethods: Set[Symbol] = {
val thisReferringMethodsTraverser = new ThisReferringMethodsTraverser()
thisReferringMethodsTraverser traverse unit.body
val methodReferringMap = thisReferringMethodsTraverser.liftedMethodReferences
@@ -92,92 +94,79 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
super.transformStats(stats, exprOwner) ++ lambdaClassDefs.remove(exprOwner).getOrElse(Nil)
}
- private def optionSymbol(sym: Symbol): Option[Symbol] = if (sym.exists) Some(sym) else None
+ def createBoxingBridgeMethod(oldClass: Symbol, target: Symbol, functionParamTypes: List[Type], functionResultType: Type, pos: Position): Symbol = {
+ // Note: we bail out of this method and return EmptyTree if we find there is no adaptation required.
+ // If we need to improve performance, we could check the types first before creating the
+ // method and parameter symbols.
+ val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT)
+ var neededAdaptation = false
+ def boxedType(tpe: Type): Type = {
+ if (isPrimitiveValueClass(tpe.typeSymbol)) {neededAdaptation = true; ObjectTpe}
+ else if (enteringErasure(tpe.typeSymbol.isDerivedValueClass)) {neededAdaptation = true; ObjectTpe}
+ else tpe
+ }
+ val targetParams: List[Symbol] = target.paramss.head
+ val numCaptures = targetParams.length - functionParamTypes.length
+ val (targetCaptureParams, targetFunctionParams) = targetParams.splitAt(numCaptures)
+ val bridgeParams: List[Symbol] =
+ targetCaptureParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName)) :::
+ map2(targetFunctionParams, functionParamTypes)((param, tp) => methSym.newSyntheticValueParam(boxedType(tp), param.name.toTermName))
+
+ val bridgeResultType: Type = {
+ if (target.info.resultType == UnitTpe && functionResultType != UnitTpe) {
+ neededAdaptation = true
+ ObjectTpe
+ } else
+ boxedType(functionResultType)
+ }
+ val methodType = MethodType(bridgeParams, bridgeResultType)
+ methSym setInfo methodType
+ if (!neededAdaptation) target
+ else {
+ val bridgeParamTrees = bridgeParams.map(ValDef(_))
+
+ oldClass.info.decls enter methSym
+
+ val body = localTyper.typedPos(pos) {
+ val newTarget = Select(gen.mkAttributedThis(oldClass), target)
+ val args: List[Tree] = mapWithIndex(bridgeParams) { (param, i) =>
+ if (i < numCaptures) {
+ gen.mkAttributedRef(param)
+ } else {
+ val functionParam = functionParamTypes(i - numCaptures)
+ val targetParam = targetParams(i)
+ if (enteringErasure(functionParam.typeSymbol.isDerivedValueClass)) {
+ val casted = cast(gen.mkAttributedRef(param), functionParam)
+ val unboxed = unbox(casted, ErasedValueType(functionParam.typeSymbol, targetParam.tpe)).modifyType(postErasure.elimErasedValueType)
+ unboxed
+ } else adaptToType(gen.mkAttributedRef(param), targetParam.tpe)
+ }
+ }
+ gen.mkMethodCall(newTarget, args)
+ }
+ val body1 = if (enteringErasure(functionResultType.typeSymbol.isDerivedValueClass))
+ adaptToType(box(body.setType(ErasedValueType(functionResultType.typeSymbol, body.tpe)), "boxing lambda target"), bridgeResultType)
+ else adaptToType(body, bridgeResultType)
+ val methDef0 = DefDef(methSym, List(bridgeParamTrees), body1)
+ val bridge = postErasure.newTransformer(unit).transform(methDef0).asInstanceOf[DefDef]
+ boxingBridgeMethods += bridge
+ bridge.symbol
+ }
+ }
// turns a lambda into a new class def, a New expression instantiating that class
private def transformFunction(originalFunction: Function): Tree = {
- val formals = originalFunction.vparams.map(_.tpe)
- val restpe = originalFunction.body.tpe.deconst
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)
+ val arity = originalFunction.vparams.length
val target = targetMethod(originalFunction)
target.makeNotPrivate(target.owner)
+
if (!thisReferringMethods.contains(target))
target setFlag STATIC
val isStatic = target.hasFlag(STATIC)
- def createBoxingBridgeMethod(functionParamTypes: List[Type], functionResultType: Type): Tree = {
- // Note: we bail out of this method and return EmptyTree if we find there is no adaptation required.
- // If we need to improve performance, we could check the types first before creating the
- // method and parameter symbols.
- val methSym = oldClass.newMethod(target.name.append("$adapted").toTermName, target.pos, target.flags | FINAL | ARTIFACT)
- var neededAdaptation = false
- def boxedType(tpe: Type): Type = {
- if (isPrimitiveValueClass(tpe.typeSymbol)) {neededAdaptation = true; ObjectTpe}
- else if (enteringErasure(tpe.typeSymbol.isDerivedValueClass)) {neededAdaptation = true; ObjectTpe}
- else tpe
- }
- val targetParams: List[Symbol] = target.paramss.head
- val numCaptures = targetParams.length - functionParamTypes.length
- val (targetCaptureParams, targetFunctionParams) = targetParams.splitAt(numCaptures)
- val bridgeParams: List[Symbol] =
- targetCaptureParams.map(param => methSym.newSyntheticValueParam(param.tpe, param.name.toTermName)) :::
- map2(targetFunctionParams, functionParamTypes)((param, tp) => methSym.newSyntheticValueParam(boxedType(tp), param.name.toTermName))
-
- val bridgeResultType: Type = {
- if (target.info.resultType == UnitTpe && functionResultType != UnitTpe) {
- neededAdaptation = true
- ObjectTpe
- } else
- boxedType(functionResultType)
- }
- val methodType = MethodType(bridgeParams, bridgeResultType)
- methSym setInfo methodType
- if (!neededAdaptation)
- EmptyTree
- else {
- val bridgeParamTrees = bridgeParams.map(ValDef(_))
-
- oldClass.info.decls enter methSym
-
- val body = localTyper.typedPos(originalFunction.pos) {
- val newTarget = Select(gen.mkAttributedThis(oldClass), target)
- val args: List[Tree] = mapWithIndex(bridgeParams) { (param, i) =>
- if (i < numCaptures) {
- gen.mkAttributedRef(param)
- } else {
- val functionParam = functionParamTypes(i - numCaptures)
- val targetParam = targetParams(i)
- if (enteringErasure(functionParam.typeSymbol.isDerivedValueClass)) {
- val casted = cast(gen.mkAttributedRef(param), functionParam)
- val unboxed = unbox(casted, ErasedValueType(functionParam.typeSymbol, targetParam.tpe)).modifyType(postErasure.elimErasedValueType)
- unboxed
- } else adaptToType(gen.mkAttributedRef(param), targetParam.tpe)
- }
- }
- gen.mkMethodCall(newTarget, args)
- }
- val body1 = if (enteringErasure(functionResultType.typeSymbol.isDerivedValueClass))
- adaptToType(box(body.setType(ErasedValueType(functionResultType.typeSymbol, body.tpe)), "boxing lambda target"), bridgeResultType)
- else adaptToType(body, bridgeResultType)
- val methDef0 = DefDef(methSym, List(bridgeParamTrees), body1)
- postErasure.newTransformer(unit).transform(methDef0).asInstanceOf[DefDef]
- }
- }
-
- val allCaptureArgs: List[Tree] = {
- val thisArg = if (isStatic) Nil else (gen.mkAttributedThis(oldClass) setPos originalFunction.pos) :: Nil
- val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList
- thisArg ::: captureArgs
- }
-
- val arity = originalFunction.vparams.length
-
// Reconstruct the type of the function entering erasure.
// We do this by taking the type after erasure, and re-boxing `ErasedValueType`.
//
@@ -211,21 +200,29 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val lambdaTarget =
if (isSpecialized) target
- else createBoxingBridgeMethod(functionParamTypes, functionResultType) match {
- case EmptyTree => target
- case bridge => boxingBridgeMethods += bridge; bridge.symbol
+ else createBoxingBridgeMethod(oldClass, target, functionParamTypes, functionResultType, originalFunction.pos)
+
+ // We then apply this symbol to the captures.
+ val apply = {
+ val allCaptureArgs: List[Tree] = {
+ // 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)
+ val thisArg = if (isStatic) Nil else (gen.mkAttributedThis(oldClass) setPos originalFunction.pos) :: Nil
+ val captureArgs = captures.iterator.map(capture => gen.mkAttributedRef(capture) setPos originalFunction.pos).toList
+ thisArg ::: captureArgs
}
- // Create a symbol representing a fictional lambda factory method that accepts the captured
- // arguments and returns a Function.
- val msym = {
- val meth = currentOwner.newMethod(nme.ANON_FUN_NAME, originalFunction.pos, ARTIFACT)
- val capturedParams = meth.newSyntheticValueParams(allCaptureArgs.map(_.tpe))
- meth.setInfo(MethodType(capturedParams, functionType))
- }
+ // Create a symbol representing a fictional lambda factory method that accepts the captured
+ // arguments and returns a Function.
+ val msym = {
+ val meth = currentOwner.newMethod(nme.ANON_FUN_NAME, originalFunction.pos, ARTIFACT)
+ val capturedParams = meth.newSyntheticValueParams(allCaptureArgs.map(_.tpe))
+ meth.setInfo(MethodType(capturedParams, functionType))
+ }
- // We then apply this symbol to the captures.
- val apply = localTyper.typedPos(originalFunction.pos)(Apply(Ident(msym), allCaptureArgs)).asInstanceOf[Apply]
+ localTyper.typedPos(originalFunction.pos)(Apply(Ident(msym), allCaptureArgs)).asInstanceOf[Apply]
+ }
// The backend needs to know the target of the lambda and the functional interface in order
// to emit the invokedynamic instruction. We pass this information as tree attachment.
@@ -235,6 +232,17 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
}
} // DelambdafyTransformer
+ /**
+ * Get the symbol of the target lifted lambda 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")
+ }
+
// 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
@@ -267,18 +275,6 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
}
}
- /**
- * Get the symbol of the target lifted lambda 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
@@ -307,6 +303,4 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
super.traverse(tree)
}
}
-
- final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol)
}
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 5a75332881..c142c96e66 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -7,6 +7,7 @@ package scala
package tools.nsc
package transform
+import scala.annotation.tailrec
import scala.language.postfixOps
import symtab.Flags._
@@ -65,7 +66,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 val forceExpandFunction = settings.Ydelambdafy.value == "inline"
private var needTryLift = false
private var inConstructorFlag = 0L
private val byNameArgs = mutable.HashSet[Tree]()
@@ -73,7 +74,7 @@ abstract class UnCurry extends InfoTransform
private val newMembers = mutable.Map[Symbol, mutable.Buffer[Tree]]()
// Expand `Function`s in constructors to class instance creation (SI-6666, SI-8363)
- private def mustExpandFunction = inlineFunctionExpansion || (inConstructorFlag != 0)
+ private def mustExpandFunction = forceExpandFunction || inConstructorFlag != 0
/** Add a new synthetic member for `currentOwner` */
private def addNewMember(t: Tree): Unit =
@@ -83,25 +84,17 @@ abstract class UnCurry extends InfoTransform
@inline private def useNewMembers[T](owner: Symbol)(f: List[Tree] => T): T =
f(newMembers.remove(owner).getOrElse(Nil).toList)
- private def newFunction0(body: Tree): Tree = {
- val result = localTyper.typedPos(body.pos)(Function(Nil, body)).asInstanceOf[Function]
- log("Change owner from %s to %s in %s".format(currentOwner, result.symbol, result.body))
- result.body changeOwner (currentOwner -> result.symbol)
- transformFunction(result)
- }
-
// I don't have a clue why I'm catching TypeErrors here, but it's better
// than spewing stack traces at end users for internal errors. Examples
// which hit at this point should not be hard to come by, but the immediate
// motivation can be seen in continuations-neg/t3718.
- override def transform(tree: Tree): Tree = (
+ override def transform(tree: Tree): Tree =
try postTransform(mainTransform(tree))
catch { case ex: TypeError =>
reporter.error(ex.pos, ex.msg)
debugStack(ex)
EmptyTree
}
- )
/* Is tree a reference `x` to a call by name parameter that needs to be converted to
* x.apply()? Note that this is not the case if `x` is used as an argument to another
@@ -110,7 +103,7 @@ abstract class UnCurry extends InfoTransform
def isByNameRef(tree: Tree) = (
tree.isTerm
&& (tree.symbol ne null)
- && (isByName(tree.symbol))
+ && isByName(tree.symbol)
&& !byNameArgs(tree)
)
@@ -187,16 +180,6 @@ abstract class UnCurry extends InfoTransform
// ------ Transforming anonymous functions and by-name-arguments ----------------
- /** Undo eta expansion for parameterless and nullary methods */
- def deEta(fun: Function): Tree = fun match {
- case Function(List(), expr) if isByNameRef(expr) =>
- noApply += expr
- expr
- case _ =>
- fun
- }
-
-
/** Transform a function node (x_1,...,x_n) => body of type FunctionN[T_1, .., T_N, R] to
*
* class $anon() extends AbstractFunctionN[T_1, .., T_N, R] with Serializable {
@@ -205,57 +188,21 @@ abstract class UnCurry extends InfoTransform
* new $anon()
*
*/
- def transformFunction(fun: Function): Tree = {
- fun.tpe match {
- // can happen when analyzer plugins assign refined types to functions, e.g.
- // (() => Int) { def apply(): Int @typeConstraint }
- case RefinedType(List(funTp), decls) =>
- debuglog(s"eliminate refinement from function type ${fun.tpe}")
- fun.setType(funTp)
- case _ =>
- ()
- }
+ def transformFunction(fun: Function): Tree =
+ // Undo eta expansion for parameterless and nullary methods
+ if (fun.vparams.isEmpty && isByNameRef(fun.body)) { noApply += fun.body ; fun.body }
+ else if (mustExpandFunction) gen.expandFunction(localTyper)(fun, inConstructorFlag)
+ else {
+ // method definition with the same arguments, return type, and body as the original lambda
+ val liftedMethod = gen.mkMethodFromFunction(localTyper)(fun.symbol.owner, fun, nme.ANON_FUN_NAME, ARTIFACT)
- deEta(fun) match {
- // nullary or parameterless
- case fun1 if fun1 ne fun => fun1
- case _ =>
- def typedFunPos(t: Tree) = localTyper.typedPos(fun.pos)(t)
- val funParams = fun.vparams map (_.symbol)
- def mkMethod(owner: Symbol, name: TermName, additionalFlags: FlagSet = NoFlags): DefDef =
- gen.mkMethodFromFunction(localTyper)(fun, owner, name, additionalFlags)
-
- if (mustExpandFunction) {
- assert(isFunctionType(fun.tpe), s"Not a FunctionN? $fun: ${fun.tpe}")
- val funTpArgs = fun.tpe.typeArgs
- val parents = addSerializable(abstractFunctionType(funTpArgs.init, funTpArgs.last))
- val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation SerialVersionUIDAnnotation
- // The original owner is used in the backend for the EnclosingMethod attribute. If fun is
- // nested in a value-class method, its owner was already changed to the extension method.
- // Saving the original owner allows getting the source structure from the class symbol.
- defineOriginalOwner(anonClass, fun.symbol.originalOwner)
- anonClass setInfo ClassInfoType(parents, newScope, anonClass)
-
- val applyMethodDef = mkMethod(anonClass, nme.apply)
- anonClass.info.decls enter applyMethodDef.symbol
-
- typedFunPos {
- Block(
- ClassDef(anonClass, NoMods, ListOfNil, List(applyMethodDef), fun.pos),
- Typed(New(anonClass.tpe), TypeTree(fun.tpe)))
- }
- } else {
- // method definition with the same arguments, return type, and body as the original lambda
- val liftedMethod = mkMethod(fun.symbol.owner, nme.ANON_FUN_NAME, additionalFlags = ARTIFACT)
-
- // new function whose body is just a call to the lifted method
- val newFun = deriveFunction(fun)(_ => typedFunPos(
- gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), funParams :: Nil)
- ))
- typedFunPos(Block(liftedMethod, super.transform(newFun)))
- }
- }
- }
+ // new function whose body is just a call to the lifted method
+ val newFun = deriveFunction(fun)(_ => localTyper.typedPos(fun.pos)(
+ gen.mkForwarder(gen.mkAttributedRef(liftedMethod.symbol), (fun.vparams map (_.symbol)) :: Nil)
+ ))
+
+ localTyper.typedPos(fun.pos)(Block(liftedMethod, super.transform(newFun)))
+ }
def transformArgs(pos: Position, fun: Symbol, args: List[Tree], formals: List[Type]) = {
val isJava = fun.isJavaDefined
@@ -333,25 +280,22 @@ abstract class UnCurry extends InfoTransform
val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head) else args
map2(formals, args1) { (formal, arg) =>
- if (!isByNameParamType(formal))
- arg
- else if (isByNameRef(arg)) {
+ if (!isByNameParamType(formal)) arg
+ else if (isByNameRef(arg)) { // thunk does not need to be forced because it's a reference to a by-name arg passed to a by-name param
byNameArgs += arg
arg setType functionType(Nil, arg.tpe)
- }
- else {
+ } else {
log(s"Argument '$arg' at line ${arg.pos.line} is $formal from ${fun.fullName}")
- def canUseDirectly(recv: Tree) = (
- recv.tpe.typeSymbol.isSubClass(FunctionClass(0))
- && treeInfo.isExprSafeToInline(recv)
- )
+ def canUseDirectly(qual: Tree) = qual.tpe.typeSymbol.isSubClass(FunctionClass(0)) && treeInfo.isExprSafeToInline(qual)
arg match {
// don't add a thunk for by-name argument if argument already is an application of
// a Function0. We can then remove the application and use the existing Function0.
- case Apply(Select(recv, nme.apply), Nil) if canUseDirectly(recv) =>
- recv
- case _ =>
- newFunction0(arg)
+ case Apply(Select(qual, nme.apply), Nil) if canUseDirectly(qual) => qual
+ case body =>
+ val thunkFun = localTyper.typedPos(body.pos)(Function(Nil, body)).asInstanceOf[Function]
+ log(s"Change owner from $currentOwner to ${thunkFun.symbol} in ${thunkFun.body}")
+ thunkFun.body.changeOwner((currentOwner, thunkFun.symbol))
+ transformFunction(thunkFun)
}
}
}
@@ -422,9 +366,10 @@ abstract class UnCurry extends InfoTransform
val sym = tree.symbol
// true if the target is a lambda body that's been lifted into a method
- def isLiftedLambdaBody(target: Tree) = target.symbol.isLocalToBlock && target.symbol.isArtifact && target.symbol.name.containsName(nme.ANON_FUN_NAME)
+ def isLiftedLambdaMethod(funSym: Symbol) =
+ funSym.isArtifact && funSym.name.containsName(nme.ANON_FUN_NAME) && funSym.isLocalToBlock
- val result = (
+ val result =
if ((sym ne null) && sym.elisionLevel.exists(_ < settings.elidebelow.value))
replaceElidableTree(tree)
else translateSynchronized(tree) match {
@@ -477,7 +422,7 @@ abstract class UnCurry extends InfoTransform
case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) =>
withNeedLift(needLift = true) { super.transform(tree) }
- case ret @ Return(_) if (isNonLocalReturn(ret)) =>
+ case ret @ Return(_) if isNonLocalReturn(ret) =>
withNeedLift(needLift = true) { super.transform(ret) }
case Try(_, Nil, _) =>
@@ -496,7 +441,7 @@ abstract class UnCurry extends InfoTransform
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) =>
+ case fun @ Function(_, Apply(target, _)) if !forceExpandFunction && isLiftedLambdaMethod(target.symbol) =>
super.transform(fun)
case fun @ Function(_, _) =>
@@ -516,9 +461,8 @@ abstract class UnCurry extends InfoTransform
}
tree1
}
- )
- assert(result.tpe != null, result.shortClass + " tpe is null:\n" + result)
- result modifyType uncurry
+
+ result.setType(uncurry(result.tpe))
}
def postTransform(tree: Tree): Tree = exitingUncurry {
@@ -537,7 +481,7 @@ abstract class UnCurry extends InfoTransform
tree
}
- def isThrowable(pat: Tree): Boolean = pat match {
+ @tailrec def isThrowable(pat: Tree): Boolean = pat match {
case Typed(Ident(nme.WILDCARD), tpt) =>
tpt.tpe =:= ThrowableTpe
case Bind(_, pat) =>
@@ -609,7 +553,7 @@ abstract class UnCurry extends InfoTransform
case Select(_, _) | TypeApply(_, _) =>
applyUnary()
case ret @ Return(expr) if isNonLocalReturn(ret) =>
- log("non-local return from %s to %s".format(currentOwner.enclMethod, ret.symbol))
+ log(s"non-local return from ${currentOwner.enclMethod} to ${ret.symbol}")
atPos(ret.pos)(nonLocalReturnThrow(expr, ret.symbol))
case TypeTree() =>
tree
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 5a62c32d2e..d9d68f0773 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1055,9 +1055,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (hasUndets)
return instantiate(tree, mode, pt)
- // we know `!(tree.tpe <:< pt)`, try to remedy if there's a sam for pt
+ // we know `!(tree.tpe <:< pt)`; try to remedy if there's a sam for pt
val sam = if (tree.isInstanceOf[Function] && !isFunctionType(pt)) samOf(pt) else NoSymbol
- if (sam.exists && sameLength(sam.info.params, tree.asInstanceOf[Function].vparams)) {
+ if (sam.exists && samMatchesFunctionBasedOnArity(sam, tree.asInstanceOf[Function].vparams)) {
// Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body`
// to an instance of the corresponding anonymous subclass of `pt`.
val samTree = synthesizeSAMFunction(sam, tree.asInstanceOf[Function], pt, mode)
@@ -2800,10 +2800,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}")
- val samFunTp = {
- val samDefinedTp = ptFullyDefined memberInfo sam
- functionType(samDefinedTp.paramTypes, samDefinedTp.finalResultType)
- }
+ // what's the signature of the method that we should actually be overriding?
+ val samMethType = ptFullyDefined memberInfo sam
+ val samFunTp = functionType(samMethType.paramTypes, samMethType.finalResultType)
+
+ // TODO: should we preemptively check that ptFullyDefined is a valid class type?
+ // (to avoid running into type errors when type checking the instantiation below)
if (!(fun.tpe <:< samFunTp)) return EmptyTree
@@ -2833,9 +2835,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
TypeTree(fun.body.tpe) setPos sampos,
fun.body)
- // what's the signature of the method that we should actually be overriding?
- val samMethType = ptFullyDefined memberInfo sam
-
// drop symbol info, use type info from sam so we implement the right method
val samDefParams = map2(fun.vparams, samMethType.paramTypes) { (vd, tp) => ValDef(vd.mods, vd.name, TypeTree(tp), vd.rhs) }
@@ -2911,7 +2910,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* TODO: handle vararg sams?
*/
val ptNorm =
- if (sam.exists && sameLength(sam.info.params, fun.vparams)) samToFunctionType(pt, sam)
+ if (samMatchesFunctionBasedOnArity(sam, fun.vparams)) samToFunctionType(pt, sam)
else pt
val (argpts, respt) =
ptNorm baseType FunctionSymbol match {
@@ -4402,18 +4401,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
treeCopy.New(tree, tpt1).setType(tp)
}
- def functionTypeWildcard(tree: Tree, arity: Int): Type = {
- val tp = functionType(List.fill(arity)(WildcardType), WildcardType)
- if (tp == NoType) MaxFunctionArityError(tree)
- tp
+ def functionTypeWildcard(arity: Int): Type =
+ functionType(List.fill(arity)(WildcardType), WildcardType)
+
+ def checkArity(tree: Tree)(tp: Type): tp.type = tp match {
+ case NoType => MaxFunctionArityError(tree); tp
+ case _ => tp
}
def expectingFunctionMatchingFormals(formals: List[Symbol]) =
- isFunctionType(pt) || {
- val sam = samOf(pt)
- // TODO: handle vararg sam here and in typedFunction
- sam.exists && sameLength(sam.info.params, formals)
- }
+ isFunctionType(pt) || samMatchesFunctionBasedOnArity(samOf(pt), formals)
def typedEta(expr1: Tree): Tree = expr1.tpe match {
case TypeRef(_, ByNameParamClass, _) =>
@@ -4426,10 +4423,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
typed1(expr2, mode, pt)
case PolyType(_, MethodType(formals, _)) =>
if (expectingFunctionMatchingFormals(formals)) expr1
- else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length))
+ else adapt(expr1, mode, checkArity(expr1)(functionTypeWildcard(formals.length)))
case MethodType(formals, _) =>
if (expectingFunctionMatchingFormals(formals)) expr1
- else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length))
+ else adapt(expr1, mode, checkArity(expr1)(functionTypeWildcard(formals.length)))
case ErrorType =>
expr1
case _ =>
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 425502d25f..9a212fcbcb 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -686,6 +686,11 @@ trait Definitions extends api.StandardDefinitions {
}
}
+ // the SAM's parameters and the Function's formals must have the same length
+ // (varargs etc don't come into play, as we're comparing signatures, not checking an application)
+ def samMatchesFunctionBasedOnArity(sam: Symbol, formals: List[Any]): Boolean =
+ sam.exists && sameLength(sam.info.params, formals)
+
def isTupleType(tp: Type) = isTupleTypeDirect(tp.dealiasWiden)
def tupleComponents(tp: Type) = tp.dealiasWiden.typeArgs