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
|
package dotty.tools.dotc
package transform
import ast.{Trees, tpd}
import core._, core.Decorators._
import Contexts._, Trees._, Types._
import DenotTransformers._, TreeTransforms._, Phases.Phase
import ExtensionMethods._, ValueClasses._
import collection.mutable.ListBuffer
/** This phase inlines calls to methods of value classes.
*
* A value class V after [[ExtensionMethods]] will look like:
* class V[A, B, ...](val underlying: U) extends AnyVal {
* def foo[T, S, ...](arg1: A1, arg2: A2, ...) =
* V.foo$extension[T, S, ..., A, B, ...](this)(arg1, arg2, ...)
*
* ...
* }
*
* Let e have type V, if e is a stable prefix or if V does not have any class
* type parameter, then we can rewrite:
* e.foo[X, Y, ...](args)
* as:
* V.foo$extension[X, Y, ..., e.A, e.B, ...](e)(args)
* Otherwise, we need to evaluate e first:
* {
* val ev = e
* V.foo$extension[X, Y, ..., ev.A, ev.B, ...](ev)(args)
* }
*
* This phase needs to be placed after phases which may introduce calls to
* value class methods (like [[PatternMatcher]]). This phase uses name mangling
* to find the correct extension method corresponding to a value class method
* (see [[ExtensionMethods.extensionMethod]]), therefore we choose to place it
* before phases which may perform their own name mangling on value class
* methods (like [[TypeSpecializer]]), this way [[VCInlineMethods]] does not
* need to have any knowledge of the name mangling done by other phases.
*/
class VCInlineMethods extends MiniPhaseTransform with IdentityDenotTransformer {
import tpd._
override def phaseName: String = "vcInlineMethods"
override def runsAfter: Set[Class[_ <: Phase]] =
Set(classOf[ExtensionMethods], classOf[PatternMatcher])
/** Replace a value class method call by a call to the corresponding extension method.
*
* @param tree The tree corresponding to the method call
* @param mtArgs Type arguments for the method call not present in `tree`
* @param mArgss Arguments for the method call not present in `tree`
* @return A tree for the extension method call
*/
private def rewire(tree: Tree, mtArgs: List[Tree] = Nil, mArgss: List[List[Tree]] = Nil)
(implicit ctx: Context): Tree =
tree match {
case Apply(qual, mArgs) =>
rewire(qual, mtArgs, mArgs :: mArgss)
case TypeApply(qual, mtArgs2) =>
assert(mtArgs == Nil)
rewire(qual, mtArgs2, mArgss)
case sel @ Select(qual, _) =>
val origMeth = sel.symbol
val ctParams = origMeth.enclosingClass.typeParams
val extensionMeth = extensionMethod(origMeth)
if (!ctParams.isEmpty) {
evalOnce(qual) { ev =>
val ctArgs = ctParams map (ev.select(_))
ref(extensionMeth)
.appliedToTypeTrees(mtArgs ++ ctArgs)
.appliedTo(ev)
.appliedToArgss(mArgss)
}
} else {
ref(extensionMeth)
.appliedToTypeTrees(mtArgs)
.appliedTo(qual)
.appliedToArgss(mArgss)
}
}
/** If this tree corresponds to a fully-applied value class method call, replace it
* by a call to the corresponding extension method, otherwise return it as is.
*/
private def rewireIfNeeded(tree: Tree)(implicit ctx: Context) = tree.tpe.widen match {
case tp: MethodOrPoly =>
tree // The rewiring will be handled by a fully-applied parent node
case _ =>
if (isMethodWithExtension(tree.symbol))
rewire(tree).ensureConforms(tree.tpe)
else
tree
}
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree =
rewireIfNeeded(tree)
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
rewireIfNeeded(tree)
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
rewireIfNeeded(tree)
}
|