@@ -565,54 +565,85 @@ object Erasure {
565
565
super .typedDefDef(ddef1, sym)
566
566
}
567
567
568
- /** After erasure, we may have to replace the closure method by a bridge.
569
- * LambdaMetaFactory handles this automatically for most types, but we have
570
- * to deal with boxing and unboxing of value classes ourselves.
571
- */
572
568
override def typedClosure (tree : untpd.Closure , pt : Type )(implicit ctx : Context ) = {
573
569
val xxl = defn.isXXLFunctionClass(tree.typeOpt.typeSymbol)
574
570
var implClosure @ Closure (_, meth, _) = super .typedClosure(tree, pt)
575
571
if (xxl) implClosure = cpy.Closure (implClosure)(tpt = TypeTree (defn.FunctionXXLType ))
576
572
implClosure.tpe match {
577
573
case SAMType (sam) =>
578
- val implType = meth.tpe.widen
574
+ val implType = meth.tpe.widen. asInstanceOf [ MethodType ]
579
575
580
- val List ( implParamTypes) = implType.paramInfoss
576
+ val implParamTypes = implType.paramInfos
581
577
val List (samParamTypes) = sam.info.paramInfoss
582
578
val implResultType = implType.resultType
583
579
val samResultType = sam.info.resultType
584
580
585
- // Given a value class V with an underlying type U, the following code:
586
- // val f: Function1[V, V] = x => ...
587
- // results in the creation of a closure and a method:
588
- // def $anonfun(v1: V): V = ...
589
- // val f: Function1[V, V] = closure($anonfun)
590
- // After [[Erasure]] this method will look like:
591
- // def $anonfun(v1: ErasedValueType(V, U)): ErasedValueType(V, U) = ...
592
- // And after [[ElimErasedValueType]] it will look like:
593
- // def $anonfun(v1: U): U = ...
594
- // This method does not implement the SAM of Function1[V, V] anymore and
595
- // needs to be replaced by a bridge:
596
- // def $anonfun$2(v1: V): V = new V($anonfun(v1.underlying))
597
- // val f: Function1 = closure($anonfun$2)
598
- // In general, a bridge is needed when the signature of the closure method after
599
- // Erasure contains an ErasedValueType but the corresponding type in the functional
600
- // interface is not an ErasedValueType.
601
- val bridgeNeeded =
602
- (implResultType :: implParamTypes, samResultType :: samParamTypes).zipped.exists(
603
- (implType, samType) => implType.isErasedValueType && ! samType.isErasedValueType
604
- )
605
-
606
- if (bridgeNeeded) {
607
- val bridge = ctx.newSymbol(ctx.owner, nme.ANON_FUN , Flags .Synthetic | Flags .Method , sam.info)
608
- val bridgeCtx = ctx.withOwner(bridge)
609
- Closure (bridge, bridgeParamss => {
610
- implicit val ctx = bridgeCtx
611
-
612
- val List (bridgeParams) = bridgeParamss
613
- val rhs = Apply (meth, (bridgeParams, implParamTypes).zipped.map(adapt(_, _)))
614
- adapt(rhs, sam.info.resultType)
615
- })
581
+ // The following code:
582
+ //
583
+ // val f: Function1[Int, Any] = x => ...
584
+ //
585
+ // results in the creation of a closure and a method in the typer:
586
+ //
587
+ // def $anonfun(x: Int): Any = ...
588
+ // val f: Function1[Int, Any] = closure($anonfun)
589
+ //
590
+ // Notice that `$anonfun` takes a primitive as argument, but the single abstract method
591
+ // of `Function1` after erasure is:
592
+ //
593
+ // def apply(x: Object): Object
594
+ //
595
+ // which takes a reference as argument. Hence, some form of adaptation is required.
596
+ //
597
+ // If we do nothing, the LambdaMetaFactory bootstrap method will
598
+ // automatically do the adaptation. Unfortunately, the result does not
599
+ // implement the expected Scala semantics: null should be "unboxed" to
600
+ // the default value of the value class, but LMF will throw a
601
+ // NullPointerException instead. LMF is also not capable of doing
602
+ // adaptation for derived value classes.
603
+ //
604
+ // Thus, we need to replace the closure method by a bridge method that
605
+ // forwards to the original closure method with appropriate
606
+ // boxing/unboxing. For our example above, this would be:
607
+ //
608
+ // def $anonfun1(x: Object): Object = $anonfun(BoxesRunTime.unboxToInt(x))
609
+ // val f: Function1 = closure($anonfun1)
610
+ //
611
+ // In general, a bridge is needed when, after Erasure:
612
+ // - one of the parameter type of the closure method is a non-reference type,
613
+ // and the corresponding type in the SAM is a reference type
614
+ // - or the result type of the closure method is an erased value type
615
+ // and the result type in the SAM isn't
616
+ // However, the following exception exists: If the SAM is replaced by
617
+ // JFunction*mc* in [[FunctionalInterfaces]], no bridge is needed: the
618
+ // SAM contains default methods to handle adaptation
619
+ //
620
+ // See test cases lambda-*.scala and t8017/ for concrete examples.
621
+
622
+ def isReferenceType (tp : Type ) = ! tp.isPrimitiveValueType && ! tp.isErasedValueType
623
+
624
+ if (! defn.isSpecializableFunction(implClosure.tpe.widen.classSymbol.asClass, implParamTypes, implResultType)) {
625
+ val paramAdaptationNeeded =
626
+ (implParamTypes, samParamTypes).zipped.exists((implType, samType) =>
627
+ ! isReferenceType(implType) && isReferenceType(samType))
628
+ val resultAdaptationNeeded =
629
+ implResultType.isErasedValueType && ! samResultType.isErasedValueType
630
+
631
+ if (paramAdaptationNeeded || resultAdaptationNeeded) {
632
+ val bridgeType =
633
+ if (paramAdaptationNeeded) {
634
+ if (resultAdaptationNeeded) sam.info
635
+ else implType.derivedLambdaType(paramInfos = samParamTypes)
636
+ } else implType.derivedLambdaType(resType = samResultType)
637
+ val bridge = ctx.newSymbol(ctx.owner, nme.ANON_FUN , Flags .Synthetic | Flags .Method , bridgeType)
638
+ val bridgeCtx = ctx.withOwner(bridge)
639
+ Closure (bridge, bridgeParamss => {
640
+ implicit val ctx = bridgeCtx
641
+
642
+ val List (bridgeParams) = bridgeParamss
643
+ val rhs = Apply (meth, (bridgeParams, implParamTypes).zipped.map(adapt(_, _)))
644
+ adapt(rhs, bridgeType.resultType)
645
+ }, targetType = implClosure.tpt.tpe)
646
+ } else implClosure
616
647
} else implClosure
617
648
case _ =>
618
649
implClosure
0 commit comments