@@ -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,72 @@ 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
+ val applyRhs = applyDef.rhs
58
58
val applyFn = applyDef.symbol.asTerm
59
59
60
60
val MethodTpe (paramNames, paramTypes, _) = applyFn.info
61
61
val isDefinedAtFn = applyFn.copy(
62
62
name = nme.isDefinedAt,
63
63
flags = Synthetic | Method ,
64
64
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
+ }
85
94
}
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
+
86
118
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)
89
123
}
90
124
91
125
private def checkRefinements (tpe : Type , pos : Position )(implicit ctx : Context ): Type = tpe match {
0 commit comments