@@ -16,16 +16,18 @@ private[async] trait AnfTransform {
16
16
import c .internal ._
17
17
import decorators ._
18
18
19
- def anfTransform (tree : Tree ): Block = {
19
+ def anfTransform (tree : Tree , owner : Symbol ): Block = {
20
20
// Must prepend the () for issue #31.
21
- val block = c.typecheck(atPos(tree.pos)(Block (List (Literal (Constant (()))), tree))).setType(tree.tpe)
21
+ val block = c.typecheck(atPos(tree.pos)(newBlock (List (Literal (Constant (()))), tree))).setType(tree.tpe)
22
22
23
23
sealed abstract class AnfMode
24
24
case object Anf extends AnfMode
25
25
case object Linearizing extends AnfMode
26
26
27
+ val tree1 = adjustTypeOfTranslatedPatternMatches(block, owner)
28
+
27
29
var mode : AnfMode = Anf
28
- typingTransform(block )((tree, api) => {
30
+ typingTransform(tree1, owner )((tree, api) => {
29
31
def blockToList (tree : Tree ): List [Tree ] = tree match {
30
32
case Block (stats, expr) => stats :+ expr
31
33
case t => t :: Nil
@@ -34,7 +36,7 @@ private[async] trait AnfTransform {
34
36
def listToBlock (trees : List [Tree ]): Block = trees match {
35
37
case trees @ (init :+ last) =>
36
38
val pos = trees.map(_.pos).reduceLeft(_ union _)
37
- Block (init, last).setType(last.tpe).setPos(pos)
39
+ newBlock (init, last).setType(last.tpe).setPos(pos)
38
40
}
39
41
40
42
object linearize {
@@ -66,6 +68,17 @@ private[async] trait AnfTransform {
66
68
stats :+ valDef :+ atPos(tree.pos)(ref1)
67
69
68
70
case If (cond, thenp, elsep) =>
71
+ // If we run the ANF transform post patmat, deal with trees like `(if (cond) jump1(){String} else jump2(){String}){String}`
72
+ // as though it was typed with `Unit`.
73
+ def isPatMatGeneratedJump (t : Tree ): Boolean = t match {
74
+ case Block (_, expr) => isPatMatGeneratedJump(expr)
75
+ case If (_, thenp, elsep) => isPatMatGeneratedJump(thenp) && isPatMatGeneratedJump(elsep)
76
+ case _ : Apply if isLabel(t.symbol) => true
77
+ case _ => false
78
+ }
79
+ if (isPatMatGeneratedJump(expr)) {
80
+ internal.setType(expr, definitions.UnitTpe )
81
+ }
69
82
// if type of if-else is Unit don't introduce assignment,
70
83
// but add Unit value to bring it into form expected by async transform
71
84
if (expr.tpe =:= definitions.UnitTpe ) {
@@ -77,7 +90,7 @@ private[async] trait AnfTransform {
77
90
def branchWithAssign (orig : Tree ) = api.typecheck(atPos(orig.pos) {
78
91
def cast (t : Tree ) = mkAttributedCastPreservingAnnotations(t, tpe(varDef.symbol))
79
92
orig match {
80
- case Block (thenStats, thenExpr) => Block (thenStats, Assign (Ident (varDef.symbol), cast(thenExpr)))
93
+ case Block (thenStats, thenExpr) => newBlock (thenStats, Assign (Ident (varDef.symbol), cast(thenExpr)))
81
94
case _ => Assign (Ident (varDef.symbol), cast(orig))
82
95
}
83
96
})
@@ -115,7 +128,7 @@ private[async] trait AnfTransform {
115
128
}
116
129
}
117
130
118
- private def defineVar (prefix : String , tp : Type , pos : Position ): ValDef = {
131
+ def defineVar (prefix : String , tp : Type , pos : Position ): ValDef = {
119
132
val sym = api.currentOwner.newTermSymbol(name.fresh(prefix), pos, MUTABLE | SYNTHETIC ).setInfo(uncheckedBounds(tp))
120
133
valDef(sym, mkZero(uncheckedBounds(tp))).setType(NoType ).setPos(pos)
121
134
}
@@ -152,8 +165,7 @@ private[async] trait AnfTransform {
152
165
}
153
166
154
167
def _transformToList (tree : Tree ): List [Tree ] = trace(tree) {
155
- val containsAwait = tree exists isAwait
156
- if (! containsAwait) {
168
+ if (! containsAwait(tree)) {
157
169
tree match {
158
170
case Block (stats, expr) =>
159
171
// avoids nested block in `while(await(false)) ...`.
@@ -207,10 +219,11 @@ private[async] trait AnfTransform {
207
219
funStats ++ argStatss.flatten.flatten :+ typedNewApply
208
220
209
221
case Block (stats, expr) =>
210
- (stats :+ expr).flatMap(linearize.transformToList)
222
+ val trees = stats.flatMap(linearize.transformToList).filterNot(isLiteralUnit) ::: linearize.transformToList(expr)
223
+ eliminateMatchEndLabelParameter(trees)
211
224
212
225
case ValDef (mods, name, tpt, rhs) =>
213
- if (rhs exists isAwait ) {
226
+ if (containsAwait( rhs) ) {
214
227
val stats :+ expr = api.atOwner(api.currentOwner.owner)(linearize.transformToList(rhs))
215
228
stats.foreach(_.changeOwner(api.currentOwner, api.currentOwner.owner))
216
229
stats :+ treeCopy.ValDef (tree, mods, name, tpt, expr)
@@ -247,7 +260,7 @@ private[async] trait AnfTransform {
247
260
scrutStats :+ treeCopy.Match (tree, scrutExpr, caseDefs)
248
261
249
262
case LabelDef (name, params, rhs) =>
250
- List (LabelDef (name, params, Block (linearize.transformToList(rhs), Literal (Constant (())))).setSymbol(tree.symbol))
263
+ List (LabelDef (name, params, newBlock (linearize.transformToList(rhs), Literal (Constant (())))).setSymbol(tree.symbol))
251
264
252
265
case TypeApply (fun, targs) =>
253
266
val funStats :+ simpleFun = linearize.transformToList(fun)
@@ -259,6 +272,52 @@ private[async] trait AnfTransform {
259
272
}
260
273
}
261
274
275
+ // Replace the label parameters on `matchEnd` with use of a `matchRes` temporary variable
276
+ //
277
+ // CaseDefs are translated to labels without parmeters. A terminal label, `matchEnd`, accepts
278
+ // a parameter which is the result of the match (this is regular, so even Unit-typed matches have this).
279
+ //
280
+ // For our purposes, it is easier to:
281
+ // - extract a `matchRes` variable
282
+ // - rewrite the terminal label def to take no parameters, and instead read this temp variable
283
+ // - change jumps to the terminal label to an assignment and a no-arg label application
284
+ def eliminateMatchEndLabelParameter (statsExpr : List [Tree ]): List [Tree ] = {
285
+ import internal .{methodType , setInfo }
286
+ val caseDefToMatchResult = collection.mutable.Map [Symbol , Symbol ]()
287
+
288
+ val matchResults = collection.mutable.Buffer [Tree ]()
289
+ val statsExpr0 = statsExpr.reverseMap {
290
+ case ld @ LabelDef (_, param :: Nil , body) =>
291
+ val matchResult = linearize.defineVar(name.matchRes, param.tpe, ld.pos)
292
+ matchResults += matchResult
293
+ caseDefToMatchResult(ld.symbol) = matchResult.symbol
294
+ val ld2 = treeCopy.LabelDef (ld, ld.name, Nil , body.substituteSymbols(param.symbol :: Nil , matchResult.symbol :: Nil ))
295
+ setInfo(ld.symbol, methodType(Nil , ld.symbol.info.resultType))
296
+ ld2
297
+ case t =>
298
+ if (caseDefToMatchResult.isEmpty) t
299
+ else typingTransform(t)((tree, api) =>
300
+ tree match {
301
+ case Apply (fun, arg :: Nil ) if isLabel(fun.symbol) && caseDefToMatchResult.contains(fun.symbol) =>
302
+ api.typecheck(atPos(tree.pos)(newBlock(Assign (Ident (caseDefToMatchResult(fun.symbol)), api.recur(arg)) :: Nil , treeCopy.Apply (tree, fun, Nil ))))
303
+ case Block (stats, expr) =>
304
+ api.default(tree) match {
305
+ case Block (stats, Block (stats1, expr)) =>
306
+ treeCopy.Block (tree, stats ::: stats1, expr)
307
+ case t => t
308
+ }
309
+ case _ =>
310
+ api.default(tree)
311
+ }
312
+ )
313
+ }
314
+ matchResults.toList match {
315
+ case Nil => statsExpr
316
+ case r1 :: Nil => (r1 +: statsExpr0.reverse) :+ atPos(tree.pos)(gen.mkAttributedIdent(r1.symbol))
317
+ case _ => c.error(macroPos, " Internal error: unexpected tree encountered during ANF transform " + statsExpr); statsExpr
318
+ }
319
+ }
320
+
262
321
def anfLinearize (tree : Tree ): Block = {
263
322
val trees : List [Tree ] = mode match {
264
323
case Anf => anf._transformToList(tree)
0 commit comments