@@ -13,7 +13,7 @@ import dotty.tools.dotc.util.Positions.Position
1313/** Expand SAM closures that cannot be represented by the JVM as lambdas to anonymous classes.
1414 * These fall into five categories
1515 *
16- * 1. Partial function closures, we need to generate a isDefinedAt method for these.
16+ * 1. Partial function closures, we need to generate isDefinedAt and applyOrElse methods for these.
1717 * 2. Closures implementing non-trait classes.
1818 * 3. Closures implementing classes that inherit from a class other than Object
1919 * (a lambda cannot not be a run-time subtype of such a class)
@@ -54,38 +54,72 @@ class ExpandSAMs extends MiniPhase {
5454 val Block (
5555 (applyDef @ DefDef (nme.ANON_FUN , Nil , List (List (param)), _, _)) :: Nil ,
5656 Closure (_, _, tpt)) = tree
57- val applyRhs : Tree = applyDef.rhs
57+ val applyRhs = applyDef.rhs
5858 val applyFn = applyDef.symbol.asTerm
5959
6060 val MethodTpe (paramNames, paramTypes, _) = applyFn.info
6161 val isDefinedAtFn = applyFn.copy(
6262 name = nme.isDefinedAt,
6363 flags = Synthetic | Method ,
6464 info = MethodType (paramNames, paramTypes, defn.BooleanType )).asTerm
65- val tru = Literal (Constant (true ))
66- def isDefinedAtRhs (paramRefss : List [List [Tree ]]) = applyRhs match {
67- case Match (selector, cases) =>
68- assert(selector.symbol == param.symbol)
69- val paramRef = paramRefss.head.head
70- // Again, the alternative
71- // val List(List(paramRef)) = paramRefs
72- // fails with a similar self instantiation error
73- def translateCase (cdef : CaseDef ): CaseDef =
74- cpy.CaseDef (cdef)(body = tru).changeOwner(applyFn, isDefinedAtFn)
75- val defaultSym = ctx.newSymbol(isDefinedAtFn, nme.WILDCARD , Synthetic , selector.tpe.widen)
76- val defaultCase =
77- CaseDef (
78- Bind (defaultSym, Underscore (selector.tpe.widen)),
79- EmptyTree ,
80- Literal (Constant (false )))
81- val annotated = Annotated (paramRef, New (ref(defn.UncheckedAnnotType )))
82- cpy.Match (applyRhs)(annotated, cases.map(translateCase) :+ defaultCase)
83- case _ =>
84- tru
65+
66+ val applyOrElseFn = applyFn.copy(
67+ name = nme.applyOrElse,
68+ flags = Synthetic | Method ,
69+ info = tpt.tpe.memberInfo(defn.PartialFunction_applyOrElse )).asTerm
70+
71+ def isDefinedAtRhs (paramRefss : List [List [Tree ]]) = {
72+ val tru = Literal (Constant (true ))
73+ applyRhs match {
74+ case Match (selector, cases) =>
75+ assert(selector.symbol == param.symbol)
76+ val paramRef = paramRefss.head.head
77+ def translateCase (cdef : CaseDef )=
78+ cpy.CaseDef (cdef)(body = tru).changeOwner(applyFn, isDefinedAtFn)
79+ val defaultSym = ctx.newSymbol(isDefinedAtFn, nme.WILDCARD , Synthetic , selector.tpe.widen)
80+ val defaultCase =
81+ CaseDef (
82+ Bind (defaultSym, Underscore (selector.tpe.widen)),
83+ EmptyTree ,
84+ Literal (Constant (false )))
85+ val annotated = Annotated (paramRef, New (ref(defn.UncheckedAnnotType )))
86+ cpy.Match (applyRhs)(annotated, cases.map(translateCase) :+ defaultCase)
87+ .subst(param.symbol :: Nil , paramRef.symbol :: Nil )
88+ // Needed because a partial function can be written as:
89+ // x => x match { case "foo" if foo(x) => x }
90+ // And we need to update all references to 'x'
91+ case _ =>
92+ tru
93+ }
8594 }
95+
96+ def applyOrElseRhs (paramRefss : List [List [Tree ]]) = {
97+ val List (paramRef, defaultRef) = paramRefss.head
98+ applyRhs match {
99+ case Match (selector, cases) =>
100+ assert(selector.symbol == param.symbol)
101+ def translateCase (cdef : CaseDef ) =
102+ cdef.changeOwner(applyFn, applyOrElseFn)
103+ val defaultSym = ctx.newSymbol(applyOrElseFn, nme.WILDCARD , Synthetic , selector.tpe.widen)
104+ val defaultCase =
105+ CaseDef (
106+ Bind (defaultSym, Underscore (selector.tpe.widen)),
107+ EmptyTree ,
108+ defaultRef.select(nme.apply).appliedTo(paramRef))
109+ val annotated = Annotated (paramRef, New (ref(defn.UncheckedAnnotType )))
110+ cpy.Match (applyRhs)(annotated, cases.map(translateCase) :+ defaultCase)
111+ .subst(param.symbol :: Nil , paramRef.symbol :: Nil )
112+ // Same as for isDefinedAtRhs. See comment above
113+ case _ =>
114+ ref(applyFn).appliedTo(paramRef)
115+ }
116+ }
117+
86118 val isDefinedAtDef = transformFollowingDeep(DefDef (isDefinedAtFn, isDefinedAtRhs(_)))
87- val anonCls = AnonClass (tpt.tpe :: Nil , List (applyFn, isDefinedAtFn), List (nme.apply, nme.isDefinedAt))
88- cpy.Block (tree)(List (applyDef, isDefinedAtDef), anonCls)
119+ val applyOrElseDef = transformFollowingDeep(DefDef (applyOrElseFn, applyOrElseRhs(_)))
120+
121+ val anonCls = AnonClass (tpt.tpe :: Nil , List (applyFn, isDefinedAtFn, applyOrElseFn), List (nme.apply, nme.isDefinedAt, nme.applyOrElse))
122+ cpy.Block (tree)(List (applyDef, isDefinedAtDef, applyOrElseDef), anonCls)
89123 }
90124
91125 private def checkRefinements (tpe : Type , pos : Position )(implicit ctx : Context ): Type = tpe match {
0 commit comments