Skip to content

Commit

Permalink
Merge pull request scala#10831 from som-snytt/issue/13022-CCE-delambd…
Browse files Browse the repository at this point in the history
…a-result

Improve result of bridge in delambdafy
  • Loading branch information
lrytz authored Oct 30, 2024
2 parents c03e3c0 + 76d095f commit 854ef0a
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 9 deletions.
22 changes: 13 additions & 9 deletions src/compiler/scala/tools/nsc/transform/Delambdafy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,9 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre

final case class LambdaMetaFactoryCapable(lambdaTarget: Symbol, arity: Int, functionalInterface: Symbol, sam: Symbol, bridges: List[Symbol], isSerializable: Boolean)

/**
* 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
*/
/** 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 _ =>
Expand Down Expand Up @@ -177,9 +176,11 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val resTpOk = (
samResultType =:= UnitTpe
|| functionResultType =:= samResultType
|| (isReferenceType(samResultType) && isReferenceType(functionResultType))) // yes, this is what the spec says -- no further correspondence required
if (resTpOk && (samParamTypes corresponds functionParamTypes){ (samParamTp, funParamTp) =>
funParamTp =:= samParamTp || (isReferenceType(funParamTp) && isReferenceType(samParamTp) && funParamTp <:< samParamTp) }) target
|| (isReferenceType(samResultType) && isReferenceType(functionResultType))) // per spec, no further correspondence required
def paramTpsOk = samParamTypes.corresponds(functionParamTypes)((samParamTp, funParamTp) =>
funParamTp =:= samParamTp ||
(isReferenceType(funParamTp) && isReferenceType(samParamTp) && funParamTp <:< samParamTp))
if (resTpOk && paramTpsOk) target
else {
// We have to construct a new lambda target that bridges to the one created by uncurry.
// The bridge must satisfy the above invariants, while also minimizing adaptation on our end.
Expand All @@ -194,7 +195,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
// which means the function's parameter -- even if it expects a value class -- will need to be
// boxed on the generic call to the sam method.

val bridgeParamTypes = map2(samParamTypes, functionParamTypes){ (samParamTp, funParamTp) =>
val bridgeParamTypes = map2(samParamTypes, functionParamTypes) { (samParamTp, funParamTp) =>
if (isReferenceType(samParamTp) && funParamTp <:< samParamTp) funParamTp
else postErasure.elimErasedValueType(samParamTp)
}
Expand Down Expand Up @@ -243,9 +244,12 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre

gen.mkMethodCall(Select(gen.mkAttributedThis(oldClass), target), capturedArgRefs ::: functionArgRefs)
}
val forwarderResultType =
if (samResultType.isInstanceOf[ErasedValueType] && functionResultType.isInstanceOf[ErasedValueType]) bridgeResultType
else functionResultType

val bridge = postErasure.newTransformer(unit).transform(DefDef(methSym, List(bridgeParams.map(ValDef(_))),
adaptToType(forwarderCall setType functionResultType, bridgeResultType))).asInstanceOf[DefDef]
adaptToType(forwarderCall.setType(forwarderResultType), bridgeResultType))).asInstanceOf[DefDef]

boxingBridgeMethods += bridge
bridge.symbol
Expand Down
23 changes: 23 additions & 0 deletions test/files/run/t13022.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

case class StringValue(value: String) extends AnyVal

trait Foo[A] {
def singleMethod(arg: A): StringValue
}

class R {
val foo1: Foo[Int] = new Foo[Int] {
override def singleMethod(arg: Int): StringValue = new StringValue(arg.toString)
}
val foo2: Foo[Int] = (arg: Int) => new StringValue(arg.toString)
val foo3 = (arg: Int) => new StringValue(arg.toString)

def run() = {
assert(foo1.singleMethod(1).toString == "StringValue(1)")
assert(foo2.singleMethod(1).toString == "StringValue(1)") // throws ClassCastException
assert(foo3(1).toString == "StringValue(1)")
}
}
object Test extends App {
new R().run()
}

0 comments on commit 854ef0a

Please sign in to comment.