Skip to content

Commit 726dcc0

Browse files
authored
Merge pull request #3988 from dotty-staging/fix-closure-unit
Fix #3984: SAM closures returning Unit may need adaptation
2 parents afab822 + fdafdc1 commit 726dcc0

File tree

3 files changed

+55
-26
lines changed

3 files changed

+55
-26
lines changed

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

+25-14
Original file line numberDiff line numberDiff line change
@@ -984,28 +984,39 @@ class Definitions {
984984
isNonDepFunctionType(tp.dropDependentRefinement)
985985

986986
// Specialized type parameters defined for scala.Function{0,1,2}.
987-
private lazy val Function1SpecializedParams: collection.Set[Type] =
987+
lazy val Function1SpecializedParamTypes: collection.Set[TypeRef] =
988988
Set(IntType, LongType, FloatType, DoubleType)
989-
private lazy val Function2SpecializedParams: collection.Set[Type] =
989+
lazy val Function2SpecializedParamTypes: collection.Set[TypeRef] =
990990
Set(IntType, LongType, DoubleType)
991-
private lazy val Function0SpecializedReturns: collection.Set[Type] =
992-
ScalaNumericValueTypeList.toSet[Type] + UnitType + BooleanType
993-
private lazy val Function1SpecializedReturns: collection.Set[Type] =
991+
lazy val Function0SpecializedReturnTypes: collection.Set[TypeRef] =
992+
ScalaNumericValueTypeList.toSet + UnitType + BooleanType
993+
lazy val Function1SpecializedReturnTypes: collection.Set[TypeRef] =
994994
Set(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType)
995-
private lazy val Function2SpecializedReturns: collection.Set[Type] =
996-
Function1SpecializedReturns
995+
lazy val Function2SpecializedReturnTypes: collection.Set[TypeRef] =
996+
Function1SpecializedReturnTypes
997+
998+
lazy val Function1SpecializedParamClasses =
999+
new PerRun[collection.Set[Symbol]](implicit ctx => Function1SpecializedParamTypes.map(_.symbol))
1000+
lazy val Function2SpecializedParamClasses =
1001+
new PerRun[collection.Set[Symbol]](implicit ctx => Function2SpecializedParamTypes.map(_.symbol))
1002+
lazy val Function0SpecializedReturnClasses =
1003+
new PerRun[collection.Set[Symbol]](implicit ctx => Function0SpecializedReturnTypes.map(_.symbol))
1004+
lazy val Function1SpecializedReturnClasses =
1005+
new PerRun[collection.Set[Symbol]](implicit ctx => Function1SpecializedReturnTypes.map(_.symbol))
1006+
lazy val Function2SpecializedReturnClasses =
1007+
new PerRun[collection.Set[Symbol]](implicit ctx => Function2SpecializedReturnTypes.map(_.symbol))
9971008

9981009
def isSpecializableFunction(cls: ClassSymbol, paramTypes: List[Type], retType: Type)(implicit ctx: Context) =
999-
isFunctionClass(cls) && (paramTypes match {
1010+
paramTypes.length <= 2 && cls.derivesFrom(FunctionClass(paramTypes.length)) && (paramTypes match {
10001011
case Nil =>
1001-
Function0SpecializedReturns.contains(retType)
1012+
Function0SpecializedReturnClasses().contains(retType.typeSymbol)
10021013
case List(paramType0) =>
1003-
Function1SpecializedParams.contains(paramType0) &&
1004-
Function1SpecializedReturns.contains(retType)
1014+
Function1SpecializedParamClasses().contains(paramType0.typeSymbol) &&
1015+
Function1SpecializedReturnClasses().contains(retType.typeSymbol)
10051016
case List(paramType0, paramType1) =>
1006-
Function2SpecializedParams.contains(paramType0) &&
1007-
Function2SpecializedParams.contains(paramType1) &&
1008-
Function2SpecializedReturns.contains(retType)
1017+
Function2SpecializedParamClasses().contains(paramType0.typeSymbol) &&
1018+
Function2SpecializedParamClasses().contains(paramType1.typeSymbol) &&
1019+
Function2SpecializedReturnClasses().contains(retType.typeSymbol)
10091020
case _ =>
10101021
false
10111022
})

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)