diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 103a6275a2a6..2f3580170455 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -984,28 +984,39 @@ class Definitions { isNonDepFunctionType(tp.dropDependentRefinement) // Specialized type parameters defined for scala.Function{0,1,2}. - private lazy val Function1SpecializedParams: collection.Set[Type] = + lazy val Function1SpecializedParamTypes: collection.Set[TypeRef] = Set(IntType, LongType, FloatType, DoubleType) - private lazy val Function2SpecializedParams: collection.Set[Type] = + lazy val Function2SpecializedParamTypes: collection.Set[TypeRef] = Set(IntType, LongType, DoubleType) - private lazy val Function0SpecializedReturns: collection.Set[Type] = - ScalaNumericValueTypeList.toSet[Type] + UnitType + BooleanType - private lazy val Function1SpecializedReturns: collection.Set[Type] = + lazy val Function0SpecializedReturnTypes: collection.Set[TypeRef] = + ScalaNumericValueTypeList.toSet + UnitType + BooleanType + lazy val Function1SpecializedReturnTypes: collection.Set[TypeRef] = Set(UnitType, BooleanType, IntType, FloatType, LongType, DoubleType) - private lazy val Function2SpecializedReturns: collection.Set[Type] = - Function1SpecializedReturns + lazy val Function2SpecializedReturnTypes: collection.Set[TypeRef] = + Function1SpecializedReturnTypes + + lazy val Function1SpecializedParamClasses = + new PerRun[collection.Set[Symbol]](implicit ctx => Function1SpecializedParamTypes.map(_.symbol)) + lazy val Function2SpecializedParamClasses = + new PerRun[collection.Set[Symbol]](implicit ctx => Function2SpecializedParamTypes.map(_.symbol)) + lazy val Function0SpecializedReturnClasses = + new PerRun[collection.Set[Symbol]](implicit ctx => Function0SpecializedReturnTypes.map(_.symbol)) + lazy val Function1SpecializedReturnClasses = + new PerRun[collection.Set[Symbol]](implicit ctx => Function1SpecializedReturnTypes.map(_.symbol)) + lazy val Function2SpecializedReturnClasses = + new PerRun[collection.Set[Symbol]](implicit ctx => Function2SpecializedReturnTypes.map(_.symbol)) def isSpecializableFunction(cls: ClassSymbol, paramTypes: List[Type], retType: Type)(implicit ctx: Context) = - isFunctionClass(cls) && (paramTypes match { + paramTypes.length <= 2 && cls.derivesFrom(FunctionClass(paramTypes.length)) && (paramTypes match { case Nil => - Function0SpecializedReturns.contains(retType) + Function0SpecializedReturnClasses().contains(retType.typeSymbol) case List(paramType0) => - Function1SpecializedParams.contains(paramType0) && - Function1SpecializedReturns.contains(retType) + Function1SpecializedParamClasses().contains(paramType0.typeSymbol) && + Function1SpecializedReturnClasses().contains(retType.typeSymbol) case List(paramType0, paramType1) => - Function2SpecializedParams.contains(paramType0) && - Function2SpecializedParams.contains(paramType1) && - Function2SpecializedReturns.contains(retType) + Function2SpecializedParamClasses().contains(paramType0.typeSymbol) && + Function2SpecializedParamClasses().contains(paramType1.typeSymbol) && + Function2SpecializedReturnClasses().contains(retType.typeSymbol) case _ => false }) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 3ce42683d0c5..adb08d3e2e2a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -620,25 +620,37 @@ object Erasure { // def $anonfun1(x: Object): Object = $anonfun(BoxesRunTime.unboxToInt(x)) // val f: Function1 = closure($anonfun1) // - // In general, a bridge is needed when, after Erasure: - // - one of the parameter type of the closure method is a non-reference type, - // and the corresponding type in the SAM is a reference type - // - or the result type of the closure method is an erased value type - // and the result type in the SAM isn't - // However, the following exception exists: If the SAM is replaced by - // JFunction*mc* in [[FunctionalInterfaces]], no bridge is needed: the - // SAM contains default methods to handle adaptation + // In general a bridge is needed when, after Erasure, one of the + // parameter type or the result type of the closure method has a + // different type, and we cannot rely on auto-adaptation. + // + // Auto-adaptation works in the following cases: + // - If the SAM is replaced by JFunction*mc* in + // [[FunctionalInterfaces]], no bridge is needed: the SAM contains + // default methods to handle adaptation. + // - If a result type of the closure method is a primitive value type + // different from Unit, we can rely on the auto-adaptation done by + // LMF (because it only needs to box, not unbox, so no special + // handling of null is required). + // - If the SAM is replaced by JProcedure* in + // [[DottyBackendInterface]] (this only happens when no explicit SAM + // type is given), no bridge is needed to box a Unit result type: + // the SAM contains a default method to handle that. // // See test cases lambda-*.scala and t8017/ for concrete examples. - def isReferenceType(tp: Type) = !tp.isPrimitiveValueType && !tp.isErasedValueType - if (!defn.isSpecializableFunction(implClosure.tpe.widen.classSymbol.asClass, implParamTypes, implResultType)) { + def autoAdaptedParam(tp: Type) = !tp.isErasedValueType && !tp.isPrimitiveValueType + val explicitSAMType = implClosure.tpt.tpe.exists + def autoAdaptedResult(tp: Type) = !tp.isErasedValueType && + (!explicitSAMType || tp.typeSymbol != defn.UnitClass) + def sameSymbol(tp1: Type, tp2: Type) = tp1.typeSymbol == tp2.typeSymbol + val paramAdaptationNeeded = (implParamTypes, samParamTypes).zipped.exists((implType, samType) => - !isReferenceType(implType) && isReferenceType(samType)) + !sameSymbol(implType, samType) && !autoAdaptedParam(implType)) val resultAdaptationNeeded = - implResultType.isErasedValueType && !samResultType.isErasedValueType + !sameSymbol(implResultType, samResultType) && !autoAdaptedResult(implResultType) if (paramAdaptationNeeded || resultAdaptationNeeded) { val bridgeType = diff --git a/tests/run/lambda-unit.scala b/tests/run/lambda-unit.scala index 3f4cca3b7e3c..073f4ae1e3e2 100644 --- a/tests/run/lambda-unit.scala +++ b/tests/run/lambda-unit.scala @@ -2,13 +2,19 @@ trait SAMUnit { def foo(a: Object): Unit } +trait GenericSAM[R] { + def foo(a: Object): R +} + object Test { val fun: Object => Unit = a => assert(a == "") val sam: SAMUnit = a => assert(a == "") + val genericSam: GenericSAM[Unit] = a => assert(a == "") def main(args: Array[String]): Unit = { fun("") (fun: Object => Any)("") sam.foo("") + genericSam.foo("") } }