Skip to content

Commit dffabb2

Browse files
committed
Handle classes which have multiple possible sam functions
1 parent d914da5 commit dffabb2

File tree

2 files changed

+58
-60
lines changed

2 files changed

+58
-60
lines changed

compiler/src/dotty/tools/dotc/config/JavaPlatform.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class JavaPlatform extends Platform {
5454
// Superaccessors already show up as abstract methods here, so no test necessary
5555
cls.typeRef.fields.isEmpty &&
5656
// Check if the SAM can be implemented via LambdaMetaFactory
57-
!TypeErasure.samNeedsExpansion(cls)
57+
TypeErasure.samDoesNotNeedExpansion(cls)
5858

5959
/** We could get away with excluding BoxedBooleanClass for the
6060
* purpose of equality testing since it need not compare equal

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 57 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ object TypeErasure:
587587
}
588588
erasure(functionType(applyInfo))
589589

590-
/** Check if LambdaMetaFactory cannot handle the SAM method's required signature adaptation.
590+
/** Check if LambdaMetaFactory can handle the SAM method's required signature adaptation.
591591
*
592592
* When a SAM method overrides other methods, the erased signatures must be compatible
593593
* for LambdaMetaFactory to work. This method returns true if any overridden method
@@ -597,67 +597,65 @@ object TypeErasure:
597597
* - For parameters: primitives and value classes cannot be auto-adapted by LMF
598598
* - For results: value classes and Unit cannot be auto-adapted by LMF
599599
*
600-
* When this returns true, the SAM class must be expanded rather than using LMF.
600+
* When this returns true, the SAM class does not need to be expanded.
601601
*
602602
* @param cls The SAM class to check
603-
* @return true if LMF cannot handle the required adaptation
603+
* @return true if LMF can handle the required adaptation
604604
*/
605-
def samNeedsExpansion(cls: ClassSymbol)(using Context): Boolean =
606-
val Seq(samMeth) = cls.typeRef.possibleSamMethods
607-
val samMethSym = samMeth.symbol
608-
val erasedSamInfo = transformInfo(samMethSym, samMeth.info)
609-
// println(i"Checking whether SAM ${cls} needs expansion, erased SAM info: $erasedSamInfo")
610-
611-
val (erasedSamParamTypes, erasedSamResultType) = erasedSamInfo match
612-
case mt: MethodType => (mt.paramInfos, mt.resultType)
613-
case _ => return false
614-
615-
def sameClass(tp1: Type, tp2: Type) = tp1.classSymbol == tp2.classSymbol
616-
617-
/** Can the implementation parameter type `tp` be auto-adapted to a different
618-
* parameter type in the SAM?
619-
*
620-
* For derived value classes, we always need to do the bridging manually.
621-
* For primitives, we cannot rely on auto-adaptation on the JVM because
622-
* the Scala spec requires null to be "unboxed" to the default value of
623-
* the value class, but the adaptation performed by LambdaMetaFactory
624-
* will throw a `NullPointerException` instead.
625-
*/
626-
def autoAdaptedParam(tp: Type) =
627-
!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(implResultType: Type) =
638-
!implResultType.isErasedValueType && !(implResultType.classSymbol eq defn.UnitClass)
639-
640-
samMethSym.allOverriddenSymbols.exists { overridden =>
641-
val erasedOverriddenInfo = transformInfo(overridden, overridden.info)
642-
// println(i" comparing to overridden method ${overridden} with erased info: $erasedOverriddenInfo")
643-
erasedOverriddenInfo match
644-
case mt: MethodType =>
645-
val overriddenParamTypes = mt.paramInfos
646-
val overriddenResultType = mt.resultType
647-
648-
val paramAdaptationNeeded =
649-
erasedSamParamTypes.lazyZip(overriddenParamTypes).exists((samType, overriddenType) =>
650-
!sameClass(samType, overriddenType) && (!autoAdaptedParam(samType)
651-
// LambdaMetaFactory cannot auto-adapt between Object and Array types
652-
|| overriddenType.isInstanceOf[JavaArrayType]))
653-
654-
val resultAdaptationNeeded =
655-
!sameClass(erasedSamResultType, overriddenResultType) && !autoAdaptedResult(erasedSamResultType)
656-
657-
paramAdaptationNeeded || resultAdaptationNeeded
658-
case _ => false
659-
}
660-
end samNeedsExpansion
605+
def samDoesNotNeedExpansion(cls: ClassSymbol)(using Context): Boolean = cls.typeRef.possibleSamMethods match
606+
case Seq(samMeth) =>
607+
val samMethSym = samMeth.symbol
608+
val erasedSamInfo = transformInfo(samMethSym, samMeth.info)
609+
610+
val (erasedSamParamTypes, erasedSamResultType) = erasedSamInfo match
611+
case mt: MethodType => (mt.paramInfos, mt.resultType)
612+
case _ => return false
613+
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+
638+
samMethSym.allOverriddenSymbols.forall { overridden =>
639+
val erasedOverriddenInfo = transformInfo(overridden, overridden.info)
640+
erasedOverriddenInfo match
641+
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)
655+
case _ => true
656+
}
657+
case _ => false
658+
end samDoesNotNeedExpansion
661659
end TypeErasure
662660

663661
import TypeErasure.*

0 commit comments

Comments
 (0)