diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-21 21:54:48 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2016-03-26 22:55:02 -0700 |
commit | 878e20a5243383300d3b4990146d260409bf5dfd (patch) | |
tree | d7ed82ebf6d317ae901cc5e2317ac6029970a5da /src/compiler/scala/tools/nsc/transform/Delambdafy.scala | |
parent | 391e2843f420bb4686b974b18ac361c9bb49465c (diff) | |
download | scala-878e20a5243383300d3b4990146d260409bf5dfd.tar.gz scala-878e20a5243383300d3b4990146d260409bf5dfd.tar.bz2 scala-878e20a5243383300d3b4990146d260409bf5dfd.zip |
Track Function's SAM symbol & target type using an attachment
We cannot use the expected type to track whether a Function node
targets a SAM type, as the expected type may be erased (see test
for an example).
Thus, the type checker attaches a SAMFunction attachment to a
Function node when SAM conversion is performed in adapt. Ideally,
we'd move to Dotty's Closure AST, but that will need a
deprecation cycle.
Thanks to Jason for catching my mistake, suggesting the fix and
providing the test.
Both the sam method symbol and sam target type must be tracked,
as their relationship can be complicated (due to inheritance).
For example, the sam method could be defined in a superclass (T)
of the Function's target type (U).
```
trait T { def foo(a: Any): Any }
trait U extends T { def apply = ??? }
(((x: Any) => x) : U).foo("")
```
This removes some of the duplication in deriving the sam method
from the expected type, but some grossness (see TODO) remains.
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/Delambdafy.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Delambdafy.scala | 20 |
1 files changed, 13 insertions, 7 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 0614b138a7..32ab52203e 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -29,7 +29,7 @@ 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) + final case class LambdaMetaFactoryCapable(target: Symbol, arity: Int, functionalInterface: Symbol, sam: Symbol) /** * Get the symbol of the target lifted lambda body method from a function. I.e. if @@ -60,7 +60,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre private[this] lazy val methodReferencesThis: Set[Symbol] = (new ThisReferringMethodsTraverser).methodReferencesThisIn(unit.body) - private def mkLambdaMetaFactoryCall(fun: Function, target: Symbol, functionalInterface: Symbol, isSpecialized: Boolean): Tree = { + private def mkLambdaMetaFactoryCall(fun: Function, target: Symbol, functionalInterface: Symbol, samUserDefined: Symbol, isSpecialized: Boolean): Tree = { val pos = fun.pos val allCapturedArgRefs = { // find which variables are free in the lambda because those are captures that need to be @@ -84,8 +84,14 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // We then apply this symbol to the captures. val apply = localTyper.typedPos(pos)(Apply(Ident(msym), allCapturedArgRefs)) + // TODO: this is a bit gross + val sam = samUserDefined orElse { + if (isSpecialized) functionalInterface.info.decls.find(_.isDeferred).get + else functionalInterface.info.member(nme.apply) + } + // no need for adaptation when the implemented sam is of a specialized built-in function type - val lambdaTarget = if (isSpecialized) target else createBoxingBridgeMethodIfNeeded(fun, target, functionalInterface) + val lambdaTarget = if (isSpecialized) target else createBoxingBridgeMethodIfNeeded(fun, target, functionalInterface, sam) // 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. @@ -93,7 +99,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // see https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html // instantiatedMethodType is derived from lambdaTarget's signature // samMethodType is derived from samOf(functionalInterface)'s signature - apply.updateAttachment(LambdaMetaFactoryCapable(lambdaTarget, fun.vparams.length, functionalInterface)) + apply.updateAttachment(LambdaMetaFactoryCapable(lambdaTarget, fun.vparams.length, functionalInterface, sam)) apply } @@ -113,7 +119,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre } // determine which lambda target to use with java's LMF -- create a new one if scala-specific boxing is required - def createBoxingBridgeMethodIfNeeded(fun: Function, target: Symbol, functionalInterface: Symbol): Symbol = { + def createBoxingBridgeMethodIfNeeded(fun: Function, target: Symbol, functionalInterface: Symbol, sam: Symbol): Symbol = { val oldClass = fun.symbol.enclClass val pos = fun.pos @@ -121,7 +127,6 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre val functionParamTypes = exitingErasure(target.info.paramTypes) val functionResultType = exitingErasure(target.info.resultType) - val sam = samOf(functionalInterface.tpe) orElse functionalInterface.info.member(nme.apply) val samParamTypes = exitingErasure(sam.info.paramTypes) val samResultType = exitingErasure(sam.info.resultType) @@ -241,7 +246,8 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre (functionalInterface, isSpecialized) } - mkLambdaMetaFactoryCall(originalFunction, target, functionalInterface, isSpecialized) + val sam = originalFunction.attachments.get[SAMFunction].map(_.sam).getOrElse(NoSymbol) + mkLambdaMetaFactoryCall(originalFunction, target, functionalInterface, sam, isSpecialized) } // here's the main entry point of the transform |