@@ -13,7 +13,7 @@ import dotty.tools.dotc.util.Positions.Position
13
13
/** Expand SAM closures that cannot be represented by the JVM as lambdas to anonymous classes.
14
14
* These fall into five categories
15
15
*
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.
17
17
* 2. Closures implementing non-trait classes.
18
18
* 3. Closures implementing classes that inherit from a class other than Object
19
19
* (a lambda cannot not be a run-time subtype of such a class)
@@ -54,38 +54,70 @@ class ExpandSAMs extends MiniPhase {
54
54
val Block (
55
55
(applyDef @ DefDef (nme.ANON_FUN , Nil , List (List (param)), _, _)) :: Nil ,
56
56
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
58
76
val applyFn = applyDef.symbol.asTerm
59
77
60
78
val MethodTpe (paramNames, paramTypes, _) = applyFn.info
61
79
val isDefinedAtFn = applyFn.copy(
62
80
name = nme.isDefinedAt,
63
81
flags = Synthetic | Method ,
64
82
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
+ }
85
114
}
115
+
86
116
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)
89
121
}
90
122
91
123
private def checkRefinements (tpe : Type , pos : Position )(implicit ctx : Context ): Type = tpe.dealias match {
0 commit comments