@@ -3783,8 +3783,8 @@ object Types {
37833783 * and PolyType not allowed!)
37843784 * - can be instantiated without arguments or with just () as argument.
37853785 *
3786- * The pattern `SAMType(denot )` matches a SAM type, where `denot ` is the
3787- * denotation of the single abstract method as a member of the type .
3786+ * The pattern `SAMType(sam )` matches a SAM type, where `sam ` is the
3787+ * type of the single abstract method.
37883788 */
37893789 object SAMType {
37903790 def zeroParamClass (tp : Type )(implicit ctx : Context ): Type = tp match {
@@ -3811,20 +3811,55 @@ object Types {
38113811 }
38123812 def isInstantiatable (tp : Type )(implicit ctx : Context ): Boolean = zeroParamClass(tp) match {
38133813 case cinfo : ClassInfo =>
3814- val tref = tp.narrow
3815- val selfType = cinfo.selfType.asSeenFrom(tref, cinfo.cls)
3816- tref <:< selfType
3814+ val selfType = cinfo.selfType.asSeenFrom(tp, cinfo.cls)
3815+ tp <:< selfType
38173816 case _ =>
38183817 false
38193818 }
3820- def unapply (tp : Type )(implicit ctx : Context ): Option [SingleDenotation ] =
3819+ def unapply (tp : Type )(implicit ctx : Context ): Option [MethodType ] =
38213820 if (isInstantiatable(tp)) {
38223821 val absMems = tp.abstractTermMembers
38233822 // println(s"absMems: ${absMems map (_.show) mkString ", "}")
38243823 if (absMems.size == 1 )
38253824 absMems.head.info match {
3826- case mt : MethodType if ! mt.isParamDependent => Some (absMems.head)
3827- case _ => None
3825+ case mt : MethodType if ! mt.isParamDependent =>
3826+ val cls = tp.classSymbol
3827+
3828+ // Given a SAM type such as:
3829+ //
3830+ // import java.util.function.Function
3831+ // Function[_ >: String, _ <: Int]
3832+ //
3833+ // the single abstract method will have type:
3834+ //
3835+ // (x: Function[_ >: String, _ <: Int]#T): Function[_ >: String, _ <: Int]#R
3836+ //
3837+ // which is not implementable outside of the scope of Function.
3838+ //
3839+ // To avoid this kind of issue, we approximate references to
3840+ // parameters of the SAM type by their bounds, this way in the
3841+ // above example we get:
3842+ //
3843+ // (x: String): Int
3844+ val approxParams = new ApproximatingTypeMap {
3845+ def apply (tp : Type ): Type = tp match {
3846+ case tp : TypeRef if tp.symbol.is(ClassTypeParam ) && tp.symbol.owner == cls =>
3847+ tp.info match {
3848+ case TypeAlias (alias) =>
3849+ mapOver(alias)
3850+ case TypeBounds (lo, hi) =>
3851+ range(atVariance(- variance)(apply(lo)), apply(hi))
3852+ case _ =>
3853+ range(defn.NothingType , defn.AnyType ) // should happen only in error cases
3854+ }
3855+ case _ =>
3856+ mapOver(tp)
3857+ }
3858+ }
3859+ val approx = approxParams(mt).asInstanceOf [MethodType ]
3860+ Some (approx)
3861+ case _ =>
3862+ None
38283863 }
38293864 else if (tp isRef defn.PartialFunctionClass )
38303865 // To maintain compatibility with 2.x, we treat PartialFunction specially,
@@ -3833,7 +3868,7 @@ object Types {
38333868 // def isDefinedAt(x: T) = true
38343869 // and overwrite that method whenever the function body is a sequence of
38353870 // case clauses.
3836- absMems.find(_.symbol.name == nme.apply)
3871+ absMems.find(_.symbol.name == nme.apply).map(_.info. asInstanceOf [ MethodType ])
38373872 else None
38383873 }
38393874 else None
0 commit comments