Skip to content

Commit 5fdbe42

Browse files
committed
Fix #3984: SAM closures returning Unit may need adaptation
If the abstract method in a SAM trait has a generic result type that is instantiated to Unit, we need to adapt closures implementing this trait to return BoxedUnit. This isn't necessary for Unit-returning closures without an explicit SAM type because in the backend they're implemented using the `JProcedure*` SAMs (see DottyBackendInterface#Closure) that already handle adapting to BoxedUnit.
1 parent d37dc51 commit 5fdbe42

File tree

2 files changed

+30
-12
lines changed

2 files changed

+30
-12
lines changed

compiler/src/dotty/tools/dotc/transform/Erasure.scala

+24-12
Original file line numberDiff line numberDiff line change
@@ -620,25 +620,37 @@ object Erasure {
620620
// def $anonfun1(x: Object): Object = $anonfun(BoxesRunTime.unboxToInt(x))
621621
// val f: Function1 = closure($anonfun1)
622622
//
623-
// In general, a bridge is needed when, after Erasure:
624-
// - one of the parameter type of the closure method is a non-reference type,
625-
// and the corresponding type in the SAM is a reference type
626-
// - or the result type of the closure method is an erased value type
627-
// and the result type in the SAM isn't
628-
// However, the following exception exists: If the SAM is replaced by
629-
// JFunction*mc* in [[FunctionalInterfaces]], no bridge is needed: the
630-
// SAM contains default methods to handle adaptation
623+
// In general a bridge is needed when, after Erasure, one of the
624+
// parameter type or the result type of the closure method has a
625+
// different type, and we cannot rely on auto-adaptation.
626+
//
627+
// Auto-adaptation works in the following cases:
628+
// - If the SAM is replaced by JFunction*mc* in
629+
// [[FunctionalInterfaces]], no bridge is needed: the SAM contains
630+
// default methods to handle adaptation.
631+
// - If a result type of the closure method is a primitive value type
632+
// different from Unit, we can rely on the auto-adaptation done by
633+
// LMF (because it only needs to box, not unbox, so no special
634+
// handling of null is required).
635+
// - If the SAM is replaced by JProcedure* in
636+
// [[DottyBackendInterface]] (this only happens when no explicit SAM
637+
// type is given), no bridge is needed to box a Unit result type:
638+
// the SAM contains a default method to handle that.
631639
//
632640
// See test cases lambda-*.scala and t8017/ for concrete examples.
633641

634-
def isReferenceType(tp: Type) = !tp.isPrimitiveValueType && !tp.isErasedValueType
635-
636642
if (!defn.isSpecializableFunction(implClosure.tpe.widen.classSymbol.asClass, implParamTypes, implResultType)) {
643+
def autoAdaptedParam(tp: Type) = !tp.isErasedValueType && !tp.isPrimitiveValueType
644+
val explicitSAMType = implClosure.tpt.tpe.exists
645+
def autoAdaptedResult(tp: Type) = !tp.isErasedValueType &&
646+
(!explicitSAMType || tp.typeSymbol != defn.UnitClass)
647+
def sameSymbol(tp1: Type, tp2: Type) = tp1.typeSymbol == tp2.typeSymbol
648+
637649
val paramAdaptationNeeded =
638650
(implParamTypes, samParamTypes).zipped.exists((implType, samType) =>
639-
!isReferenceType(implType) && isReferenceType(samType))
651+
!sameSymbol(implType, samType) && !autoAdaptedParam(implType))
640652
val resultAdaptationNeeded =
641-
implResultType.isErasedValueType && !samResultType.isErasedValueType
653+
!sameSymbol(implResultType, samResultType) && !autoAdaptedResult(implResultType)
642654

643655
if (paramAdaptationNeeded || resultAdaptationNeeded) {
644656
val bridgeType =

tests/run/lambda-unit.scala

+6
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@ trait SAMUnit {
22
def foo(a: Object): Unit
33
}
44

5+
trait GenericSAM[R] {
6+
def foo(a: Object): R
7+
}
8+
59
object Test {
610
val fun: Object => Unit = a => assert(a == "")
711
val sam: SAMUnit = a => assert(a == "")
12+
val genericSam: GenericSAM[Unit] = a => assert(a == "")
813

914
def main(args: Array[String]): Unit = {
1015
fun("")
1116
(fun: Object => Any)("")
1217
sam.foo("")
18+
genericSam.foo("")
1319
}
1420
}

0 commit comments

Comments
 (0)