@@ -587,22 +587,81 @@ object TypeErasure:
587587 }
588588 erasure(functionType(applyInfo))
589589
590- /** Check if LambdaMetaFactory can handle the SAM method's required signature adaptation .
590+ /** Check if LambdaMetaFactory can handle signature adaptation between two method types .
591591 *
592- * When a SAM method overrides other methods, the erased signatures must be compatible
593- * for LambdaMetaFactory to work. This method returns true if any overridden method
594- * has an incompatible erased signature that LMF cannot auto-adapt.
592+ * LMF has limitations on what type adaptations it can perform automatically.
593+ * This method checks whether manual bridging is needed for params and/or result.
595594 *
596- * The adaptation rules mirror those in `Erasure.Boxing.adaptClosure` :
595+ * The adaptation rules are :
597596 * - For parameters: primitives and value classes cannot be auto-adapted by LMF
598- * - For results: value classes and Unit cannot be auto-adapted by LMF
597+ * because the Scala spec requires null to be "unboxed" to the default value,
598+ * but LMF throws `NullPointerException` instead.
599+ * - For results: value classes and Unit cannot be auto-adapted by LMF.
600+ * Non-Unit primitives can be auto-adapted since LMF only needs to box (not unbox).
601+ * - LMF cannot auto-adapt between Object and Array types.
602+ *
603+ * @param implParamTypes Parameter types of the implementation method
604+ * @param implResultType Result type of the implementation method
605+ * @param samParamTypes Parameter types of the SAM method
606+ * @param samResultType Result type of the SAM method
607+ *
608+ * @return (paramNeeded, resultNeeded) indicating what needs bridging
609+ */
610+ def additionalAdaptationNeeded (
611+ implParamTypes : List [Type ],
612+ implResultType : Type ,
613+ samParamTypes : List [Type ],
614+ samResultType : Type
615+ )(using Context ): (paramNeeded : Boolean , resultNeeded : Boolean ) =
616+ def sameClass (tp1 : Type , tp2 : Type ) = tp1.classSymbol == tp2.classSymbol
617+
618+ /** Can the implementation parameter type `tp` be auto-adapted to a different
619+ * parameter type in the SAM?
620+ *
621+ * For derived value classes, we always need to do the bridging manually.
622+ * For primitives, we cannot rely on auto-adaptation on the JVM because
623+ * the Scala spec requires null to be "unboxed" to the default value of
624+ * the value class, but the adaptation performed by LambdaMetaFactory
625+ * will throw a `NullPointerException` instead.
626+ */
627+ def autoAdaptedParam (tp : Type ) = ! tp.isErasedValueType && ! tp.isPrimitiveValueType
628+
629+ /** Can the implementation result type be auto-adapted to a different result
630+ * type in the SAM?
631+ *
632+ * For derived value classes, it's the same story as for parameters.
633+ * For non-Unit primitives, we can actually rely on the `LambdaMetaFactory`
634+ * adaptation, because it only needs to box, not unbox, so no special
635+ * handling of null is required.
636+ */
637+ def autoAdaptedResult (tp : Type ) =
638+ ! tp.isErasedValueType && ! (tp.classSymbol eq defn.UnitClass )
639+
640+ val paramAdaptationNeeded =
641+ implParamTypes.lazyZip(samParamTypes).exists((implType, samType) =>
642+ ! sameClass(implType, samType) && (! autoAdaptedParam(implType)
643+ // LambdaMetaFactory cannot auto-adapt between Object and Array types
644+ || samType.isInstanceOf [JavaArrayType ]))
645+
646+ val resultAdaptationNeeded =
647+ ! sameClass(implResultType, samResultType) && ! autoAdaptedResult(implResultType)
648+
649+ (paramAdaptationNeeded, resultAdaptationNeeded)
650+ end additionalAdaptationNeeded
651+
652+ /** Check if LambdaMetaFactory can handle the SAM method's required signature adaptation.
653+ *
654+ * When a SAM method overrides other methods, the erased signatures must be compatible
655+ * to be qualifies as a valid functional interface on JVM.
656+ * This method returns true if all overridden methods have compatible erased signatures
657+ * that LMF can auto-adapt (or don't need adaptation).
599658 *
600659 * When this returns true, the SAM class does not need to be expanded.
601660 *
602661 * @param cls The SAM class to check
603662 * @return true if LMF can handle the required adaptation
604663 */
605- def samDoesNotNeedExpansion (cls : ClassSymbol )(using Context ): Boolean = cls.typeRef.possibleSamMethods match
664+ def samNotNeededExpansion (cls : ClassSymbol )(using Context ): Boolean = cls.typeRef.possibleSamMethods match
606665 case Seq (samMeth) =>
607666 val samMethSym = samMeth.symbol
608667 val erasedSamInfo = transformInfo(samMethSym, samMeth.info)
@@ -611,51 +670,17 @@ object TypeErasure:
611670 case mt : MethodType => (mt.paramInfos, mt.resultType)
612671 case _ => return false
613672
614- def sameClass (tp1 : Type , tp2 : Type ) = tp1.classSymbol == tp2.classSymbol
615-
616- /** Can the implementation parameter type `tp` be auto-adapted to a different
617- * parameter type in the SAM?
618- *
619- * For derived value classes, we always need to do the bridging manually.
620- * For primitives, we cannot rely on auto-adaptation on the JVM because
621- * the Scala spec requires null to be "unboxed" to the default value of
622- * the value class, but the adaptation performed by LambdaMetaFactory
623- * will throw a `NullPointerException` instead.
624- */
625- def autoAdaptedParam (tp : Type ) = ! tp.isErasedValueType && ! tp.isPrimitiveValueType
626-
627- /** Can the implementation result type be auto-adapted to a different result
628- * type in the SAM?
629- *
630- * For derived value classes, it's the same story as for parameters.
631- * For non-Unit primitives, we can actually rely on the `LambdaMetaFactory`
632- * adaptation, because it only needs to box, not unbox, so no special
633- * handling of null is required.
634- */
635- def autoAdaptedResult (implResultType : Type ) =
636- ! implResultType.isErasedValueType && ! (implResultType.classSymbol eq defn.UnitClass )
637-
638673 samMethSym.allOverriddenSymbols.forall { overridden =>
639674 val erasedOverriddenInfo = transformInfo(overridden, overridden.info)
640675 erasedOverriddenInfo match
641676 case mt : MethodType =>
642- val overriddenParamTypes = mt.paramInfos
643- val overriddenResultType = mt.resultType
644-
645- val paramAdaptationNeeded =
646- erasedSamParamTypes.lazyZip(overriddenParamTypes).exists((samType, overriddenType) =>
647- ! sameClass(samType, overriddenType) && (! autoAdaptedParam(samType)
648- // LambdaMetaFactory cannot auto-adapt between Object and Array types
649- || overriddenType.isInstanceOf [JavaArrayType ]))
650-
651- val resultAdaptationNeeded =
652- ! sameClass(erasedSamResultType, overriddenResultType) && ! autoAdaptedResult(erasedSamResultType)
653-
654- ! (paramAdaptationNeeded || resultAdaptationNeeded)
677+ val (paramNeeded, resultNeeded) =
678+ additionalAdaptationNeeded(erasedSamParamTypes, erasedSamResultType, mt.paramInfos, mt.resultType)
679+ ! (paramNeeded || resultNeeded)
655680 case _ => true
656681 }
657682 case _ => false
658- end samDoesNotNeedExpansion
683+ end samNotNeededExpansion
659684end TypeErasure
660685
661686import TypeErasure .*
0 commit comments