@@ -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,70 @@ 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+
58+ def translateMatch (tree : Match , selector : Tree , cases : List [CaseDef ], defaultValue : Tree ) = {
59+ assert(tree.selector.symbol == param.symbol)
60+ val selectorTpe = selector.tpe.widen
61+ val defaultSym = ctx.newSymbol(selector.symbol.owner, nme.WILDCARD , Synthetic , selectorTpe)
62+ val defaultCase =
63+ CaseDef (
64+ Bind (defaultSym, Underscore (selectorTpe)),
65+ EmptyTree ,
66+ defaultValue)
67+ val unchecked = Annotated (selector, New (ref(defn.UncheckedAnnotType )))
68+ cpy.Match (tree)(unchecked, cases :+ defaultCase)
69+ .subst(param.symbol :: Nil , selector.symbol :: Nil )
70+ // Needed because a partial function can be written as:
71+ // param => param match { case "foo" if foo(param) => param }
72+ // And we need to update all references to 'param'
73+ }
74+
75+ val applyRhs = applyDef.rhs
5876 val applyFn = applyDef.symbol.asTerm
5977
6078 val MethodTpe (paramNames, paramTypes, _) = applyFn.info
6179 val isDefinedAtFn = applyFn.copy(
6280 name = nme.isDefinedAt,
6381 flags = Synthetic | Method ,
6482 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
83+
84+ val applyOrElseFn = applyFn.copy(
85+ name = nme.applyOrElse,
86+ flags = Synthetic | Method ,
87+ info = tpt.tpe.memberInfo(defn.PartialFunction_applyOrElse )).asTerm
88+
89+ def isDefinedAtRhs (paramRefss : List [List [Tree ]]) = {
90+ val tru = Literal (Constant (true ))
91+ applyRhs match {
92+ case tree @ Match (_, cases) =>
93+ def translateCase (cdef : CaseDef )=
94+ cpy.CaseDef (cdef)(body = tru).changeOwner(applyFn, isDefinedAtFn)
95+ val paramRef = paramRefss.head.head
96+ val defaultValue = Literal (Constant (false ))
97+ translateMatch(tree, paramRef, cases.map(translateCase), defaultValue)
98+ case _ =>
99+ tru
100+ }
101+ }
102+
103+ def applyOrElseRhs (paramRefss : List [List [Tree ]]) = {
104+ val List (paramRef, defaultRef) = paramRefss.head
105+ applyRhs match {
106+ case tree @ Match (_, cases) =>
107+ def translateCase (cdef : CaseDef ) =
108+ cdef.changeOwner(applyFn, applyOrElseFn)
109+ val defaultValue = defaultRef.select(nme.apply).appliedTo(paramRef)
110+ translateMatch(tree, paramRef, cases.map(translateCase), defaultValue)
111+ case _ =>
112+ ref(applyFn).appliedTo(paramRef)
113+ }
85114 }
115+
86116 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)
117+ val applyOrElseDef = transformFollowingDeep(DefDef (applyOrElseFn, applyOrElseRhs(_)))
118+
119+ val anonCls = AnonClass (tpt.tpe :: Nil , List (applyFn, isDefinedAtFn, applyOrElseFn), List (nme.apply, nme.isDefinedAt, nme.applyOrElse))
120+ cpy.Block (tree)(List (applyDef, isDefinedAtDef, applyOrElseDef), anonCls)
89121 }
90122
91123 private def checkRefinements (tpe : Type , pos : Position )(implicit ctx : Context ): Type = tpe match {
0 commit comments