Skip to content

Commit f181deb

Browse files
committed
Move the inlining of value class methods before Erasure
VCInline is split into two phases: - VCInlineMethods (before Erasure) replaces value class method calls by calls to extension methods - VCInlineEqAndField (after Erasure) handles == and the underlying field of a value class, as VCInline did before. This should not affect anything currently, but in the future we will have phases before Erasure that mangle names (like TypeSpecializer, see scala#630), being able to put these phases after VCInlineMethods means that VCInlineMethods does not need to know anything about how these phases mangle names, this reduces the coupling between phases. The trade-off is that VCInlineMethods needs to deal with type parameters and multiple parameter lists whereas VCInline didn't.
1 parent 07e61bd commit f181deb

File tree

4 files changed

+150
-25
lines changed

4 files changed

+150
-25
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ class Compiler {
5151
List(new PatternMatcher,
5252
new ExplicitOuter,
5353
new Splitter),
54-
List(new SeqLiterals,
54+
List(new VCInlineMethods),
55+
List(new SeqLiterals,
5556
new InterceptedMethods,
5657
new Literalize,
5758
new Getters,
@@ -61,7 +62,7 @@ class Compiler {
6162
new ResolveSuper),
6263
List(new Erasure),
6364
List(new ElimErasedValueType,
64-
new VCInline,
65+
new VCInlineEqAndField,
6566
new Mixin,
6667
new LazyVals,
6768
new Memoize,

src/dotty/tools/dotc/transform/VCInline.scala renamed to src/dotty/tools/dotc/transform/VCInlineEqAndField.scala

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,18 @@ import Contexts._, Trees._, StdNames._, Symbols._
77
import DenotTransformers._, TreeTransforms._, Phases.Phase
88
import ExtensionMethods._, TreeExtractors._, ValueClasses._
99

10-
/** This phase inlines calls to methods and fields of value classes.
10+
/** This phase inlines calls to == and to the field of a value class
1111
*
1212
* For a value class V defined as:
13-
* case class V(val underlying: U) extends AnyVal
14-
* We replace method calls by calls to the corresponding extension method:
15-
* v.foo(args) => V.foo$extension(v.underlying(), args)
16-
* And we avoid unnecessary allocations:
13+
* class V(val underlying: U) extends AnyVal
14+
* we avoid unnecessary allocations:
1715
* new V(u1) == new V(u2) => u1 == u2
1816
* (new V(u)).underlying() => u
1917
*/
20-
class VCInline extends MiniPhaseTransform with IdentityDenotTransformer {
18+
class VCInlineEqAndField extends MiniPhaseTransform with IdentityDenotTransformer {
2119
import tpd._
2220

23-
override def phaseName: String = "vcInline"
21+
override def phaseName: String = "vcInlineEqAndField"
2422

2523
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimErasedValueType])
2624

@@ -37,22 +35,6 @@ class VCInline extends MiniPhaseTransform with IdentityDenotTransformer {
3735
case ValueClassUnbox(NewWithArgs(_, List(u))) =>
3836
u
3937

40-
// (new V(u)).foo(args) => V.foo$extension(u, args)
41-
// v.foo(args) => V.foo$extension(v.underlying(), args)
42-
case Apply(sel @ Select(receiver, _), args) =>
43-
val classMeth = sel.symbol
44-
if (isMethodWithExtension(classMeth)) {
45-
val classSym = receiver.tpe.widenDealias.typeSymbol.asClass
46-
val unboxedReceiver = receiver match {
47-
case NewWithArgs(_, List(u)) =>
48-
u
49-
case _ =>
50-
receiver.select(valueClassUnbox(classSym)).appliedToNone
51-
}
52-
val extensionMeth = extensionMethod(classMeth)
53-
ref(extensionMeth).appliedToArgs(unboxedReceiver :: args)
54-
} else tree
55-
5638
case _ =>
5739
tree
5840
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import ast.{Trees, tpd}
5+
import core._, core.Decorators._
6+
import Contexts._, Trees._, Types._
7+
import DenotTransformers._, TreeTransforms._, Phases.Phase
8+
import ExtensionMethods._, ValueClasses._
9+
10+
import collection.mutable.ListBuffer
11+
12+
/** This phase inlines calls to methods of value classes.
13+
*
14+
* A value class V after [[ExtensionMethods]] will look like:
15+
* class V[A, B, ...](val underlying: U) extends AnyVal {
16+
* def foo[T, S, ...](arg1: A1, arg2: A2, ...) =
17+
* V.foo$extension[T, S, ..., A, B, ...](this)(arg1, arg2, ...)
18+
*
19+
* ...
20+
* }
21+
*
22+
* Let e have type V, if e is a stable prefix or if V does not have any class
23+
* type parameter, then we can rewrite:
24+
* e.foo[X, Y, ...](args)
25+
* as:
26+
* V.foo$extension[X, Y, ..., e.A, e.B, ...](e)(args)
27+
* Otherwise, we need to evaluate e first:
28+
* {
29+
* val ev = e
30+
* V.foo$extension[X, Y, ..., ev.A, ev.B, ...](ev)(args)
31+
* }
32+
*
33+
* This phase needs to be placed after phases which may introduce calls to
34+
* value class methods (like [[PatternMatcher]]). This phase uses name mangling
35+
* to find the correct extension method corresponding to a value class method
36+
* (see [[ExtensionMethods.extensionMethod]]), therefore we choose to place it
37+
* before phases which may perform their own name mangling on value class
38+
* methods (like [[TypeSpecializer]]), this way [[VCInlineMethods]] does not
39+
* need to have any knowledge of the name mangling done by other phases.
40+
*/
41+
class VCInlineMethods extends MiniPhaseTransform with IdentityDenotTransformer {
42+
import tpd._
43+
44+
override def phaseName: String = "vcInlineMethods"
45+
46+
override def runsAfter: Set[Class[_ <: Phase]] =
47+
Set(classOf[ExtensionMethods], classOf[PatternMatcher])
48+
49+
/** Replace a value class method call by a call to the corresponding extension method.
50+
*
51+
* @param tree The tree corresponding to the method call
52+
* @param mtArgs Type arguments for the method call not present in `tree`
53+
* @param mArgss Arguments for the method call not present in `tree`
54+
* @return A tree for the extension method call
55+
*/
56+
private def rewire(tree: Tree, mtArgs: List[Tree] = Nil, mArgss: List[List[Tree]] = Nil)
57+
(implicit ctx: Context): Tree =
58+
tree match {
59+
case Apply(qual, mArgs) =>
60+
rewire(qual, mtArgs, mArgs :: mArgss)
61+
case TypeApply(qual, mtArgs2) =>
62+
assert(mtArgs == Nil)
63+
rewire(qual, mtArgs2, mArgss)
64+
case sel @ Select(qual, _) =>
65+
val origMeth = sel.symbol
66+
val ctParams = origMeth.enclosingClass.typeParams
67+
val extensionMeth = extensionMethod(origMeth)
68+
69+
if (!ctParams.isEmpty) {
70+
evalOnce(qual) { ev =>
71+
val ctArgs = ctParams map (ev.select(_))
72+
ref(extensionMeth)
73+
.appliedToTypeTrees(mtArgs ++ ctArgs)
74+
.appliedTo(ev)
75+
.appliedToArgss(mArgss)
76+
}
77+
} else {
78+
ref(extensionMeth)
79+
.appliedToTypeTrees(mtArgs)
80+
.appliedTo(qual)
81+
.appliedToArgss(mArgss)
82+
}
83+
}
84+
85+
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree =
86+
tree.tpe.widen match {
87+
case tp: MethodOrPoly =>
88+
tree // The rewiring will be handled by a fully-applied parent node
89+
case _ =>
90+
if (isMethodWithExtension(tree.symbol))
91+
rewire(tree)
92+
else
93+
tree
94+
}
95+
96+
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
97+
tree.tpe.widen match {
98+
case tp: MethodType =>
99+
tree // The rewiring will be handled by a fully-applied parent node
100+
case _ =>
101+
if (isMethodWithExtension(tree.symbol))
102+
rewire(tree)
103+
else
104+
tree
105+
}
106+
107+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
108+
tree.tpe.widen match {
109+
case tp: MethodType =>
110+
tree // The rewiring will be handled by transformApply
111+
case _ =>
112+
if (isMethodWithExtension(tree.symbol))
113+
rewire(tree)
114+
else
115+
tree
116+
}
117+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package paramlists
2+
3+
class Meter[T](val x: T) extends AnyVal {
4+
def zero: T = x
5+
def zero2[M >: T]: M = x
6+
def one(): T = x
7+
def one2[M >: T](): M = x
8+
def one3(x: T): T = x
9+
def one4[M >: T](x: M): M = x
10+
def two(x: T)(y: T): T = y
11+
def two2[M >: T](x: T)(y: M): M = y
12+
}
13+
14+
object Test {
15+
def test: Unit = {
16+
val m1 = new Meter(1)
17+
m1.zero
18+
m1.zero2
19+
m1.one
20+
m1.one2
21+
m1.one3(10)
22+
m1.two(11)(12)
23+
m1.two2(11)(12)
24+
}
25+
}

0 commit comments

Comments
 (0)