1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Martin Odersky
*/
package scala.tools.nsc
package typechecker
import scala.collection.mutable.ListBuffer
import symtab.Flags._
/** This trait ...
*
* @author Martin Odersky
* @version 1.0
*/
trait EtaExpansion { self: Analyzer =>
import global._
/** Expand partial method application `p.f(es_1)...(es_n)`.
*
* We expand this to the following block, which evaluates
* the target of the application and its supplied arguments if needed (they are not stable),
* and then wraps a Function that abstracts over the missing arguments.
*
* ```
* {
* private synthetic val eta$f = p.f // if p is not stable
* ...
* private synthetic val eta$e_i = e_i // if e_i is not stable
* ...
* (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m))
* }
* ```
*
* This is called from instantiateToMethodType after type checking `tree`,
* and we realize we have a method type, where a function type (builtin or SAM) is expected.
*
**/
def etaExpand(unit: CompilationUnit, tree: Tree, typer: Typer): Tree = {
val tpe = tree.tpe
var cnt = 0 // for NoPosition
def freshName() = {
cnt += 1
unit.freshTermName("eta$" + (cnt - 1) + "$")
}
val defs = new ListBuffer[Tree]
/* Append to `defs` value definitions for all non-stable
* subexpressions of the function application `tree`.
*/
def liftoutPrefix(tree: Tree): Tree = {
def liftout(tree: Tree, byName: Boolean): Tree =
if (treeInfo.isExprSafeToInline(tree)) tree
else {
val vname: Name = freshName()
// Problem with ticket #2351 here
defs += atPos(tree.pos) {
val rhs = if (byName) {
val res = typer.typed(Function(List(), tree))
new ChangeOwnerTraverser(typer.context.owner, res.symbol) traverse tree // SI-6274
res
} else tree
ValDef(Modifiers(SYNTHETIC), vname.toTermName, TypeTree(), rhs)
}
atPos(tree.pos.focus) {
if (byName) Apply(Ident(vname), List()) else Ident(vname)
}
}
val tree1 = tree match {
// a partial application using named arguments has the following form:
// { val qual$1 = qual
// val x$1 = arg1
// [...]
// val x$n = argn
// qual$1.fun(x$1, ..)..(.., x$n) }
// Eta-expansion has to be performed on `fun`
case Block(stats, fun) =>
defs ++= stats
liftoutPrefix(fun)
case Apply(fn, args) =>
val byName: Int => Option[Boolean] = fn.tpe.params.map(p => definitions.isByNameParamType(p.tpe)).lift
val newArgs = mapWithIndex(args) { (arg, i) =>
// with repeated params, there might be more or fewer args than params
liftout(arg, byName(i).getOrElse(false))
}
treeCopy.Apply(tree, liftoutPrefix(fn), newArgs).clearType()
case TypeApply(fn, args) =>
treeCopy.TypeApply(tree, liftoutPrefix(fn), args).clearType()
case Select(qual, name) =>
val name = tree.symbol.name // account for renamed imports, SI-7233
treeCopy.Select(tree, liftout(qual, byName = false), name).clearType() setSymbol NoSymbol
case Ident(name) =>
tree
}
if (tree1 ne tree) tree1 setPos tree1.pos.makeTransparent
tree1
}
/* Eta-expand lifted tree. */
def expand(tree: Tree, tpe: Type): Tree = tpe match {
case mt @ MethodType(paramSyms, restpe) if !mt.isImplicit =>
val params: List[(ValDef, Boolean)] = paramSyms.map {
sym =>
val origTpe = sym.tpe
val isRepeated = definitions.isRepeatedParamType(origTpe)
// SI-4176 Don't leak A* in eta-expanded function types. See t4176b.scala
val droppedStarTpe = if (settings.etaExpandKeepsStar) origTpe else dropIllegalStarTypes(origTpe)
val valDef = ValDef(Modifiers(SYNTHETIC | PARAM), sym.name.toTermName, TypeTree(droppedStarTpe), EmptyTree)
(valDef, isRepeated)
}
atPos(tree.pos.makeTransparent) {
val args = params.map {
case (valDef, isRepeated) => gen.paramToArg(Ident(valDef.name), isRepeated)
}
Function(params.map(_._1), expand(Apply(tree, args), restpe))
}
case _ =>
tree
}
val tree1 = liftoutPrefix(tree)
atPos(tree.pos)(Block(defs.toList, expand(tree1, tpe)))
}
}
|