Skip to content

Commit 8014f64

Browse files
committed
New phase: VCInline which inlines value classes calls
This corresponds roughly to step 2 of SIP-15 and to the peephole optimizations of step 3. The extractors in TreeExtractors are copied or inspired from src/compiler/scala/tools/nsc/ast/TreeInfo.scala in scalac.
1 parent ac41e8a commit 8014f64

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class Compiler {
5858
new ResolveSuper),
5959
List(new Erasure),
6060
List(new ElimErasedValueType,
61+
new VCInline,
6162
new Mixin,
6263
new Memoize,
6364
new CapturedVars,

src/dotty/tools/dotc/transform/InterceptedMethods.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import scala.collection.mutable.ListBuffer
2727
import dotty.tools.dotc.core.Denotations.SingleDenotation
2828
import dotty.tools.dotc.core.SymDenotations.SymDenotation
2929
import StdNames._
30+
import Phases.Phase
3031

3132
/** Replace member references as follows:
3233
*
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import ast.{Trees, tpd}
5+
import core._, core.Decorators._
6+
import Contexts._, Flags._, Trees._, Types._, StdNames._, Symbols._
7+
import ValueClasses._
8+
9+
object TreeExtractors {
10+
import tpd._
11+
12+
/** Match arg1.op(arg2) and extract (arg1, op.symbol, arg2) */
13+
object BinaryOp {
14+
def unapply(t: Tree)(implicit ctx: Context): Option[(Tree, Symbol, Tree)] = t match {
15+
case Apply(sel @ Select(arg1, _), List(arg2)) =>
16+
Some((arg1, sel.symbol, arg2))
17+
case _ =>
18+
None
19+
}
20+
}
21+
22+
/** Match new C(args) and extract (C, args) */
23+
object NewWithArgs {
24+
def unapply(t: Tree)(implicit ctx: Context): Option[(Type, List[Tree])] = t match {
25+
case Apply(Select(New(_), nme.CONSTRUCTOR), args) =>
26+
Some((t.tpe, args))
27+
case _ =>
28+
None
29+
}
30+
}
31+
32+
/** For an instance v of a value class like:
33+
* class V(val underlying: X) extends AnyVal
34+
* Match v.underlying() and extract v
35+
*/
36+
object ValueClassUnbox {
37+
def unapply(t: Tree)(implicit ctx: Context): Option[Tree] = t match {
38+
case Apply(sel @ Select(ref, _), Nil) =>
39+
val d = ref.tpe.widenDealias.typeSymbol.denot
40+
if (isDerivedValueClass(d) && (sel.symbol eq valueClassUnbox(d.asClass))) {
41+
Some(ref)
42+
} else
43+
None
44+
case _ =>
45+
None
46+
}
47+
}
48+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import ast.{Trees, tpd}
5+
import core._, core.Decorators._
6+
import Contexts._, Trees._, StdNames._, Symbols._
7+
import DenotTransformers._, TreeTransforms._, Phases.Phase
8+
import ExtensionMethods._, TreeExtractors._, ValueClasses._
9+
10+
/** This phase inlines calls to methods and fields of value classes.
11+
*
12+
* 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:
17+
* new V(u1) == new V(u2) => u1 == u2
18+
* (new V(u)).underlying() => u
19+
*/
20+
class VCInline extends MiniPhaseTransform with IdentityDenotTransformer {
21+
import tpd._
22+
23+
override def phaseName: String = "vcInline"
24+
25+
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimErasedValueType])
26+
27+
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
28+
tree match {
29+
// new V(u1) == new V(u2) => u1 == u2
30+
// (We don't handle != because it has been eliminated by InterceptedMethods)
31+
case BinaryOp(NewWithArgs(tp1, List(u1)), op, NewWithArgs(tp2, List(u2)))
32+
if (tp1 eq tp2) && (op eq defn.Any_==) && isDerivedValueClass(tp1.typeSymbol) =>
33+
// == is overloaded in primitive classes
34+
applyOverloaded(u1, nme.EQ, List(u2), Nil, defn.BooleanType)
35+
36+
// (new V(u)).underlying() => u
37+
case ValueClassUnbox(NewWithArgs(_, List(u))) =>
38+
u
39+
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+
56+
case _ =>
57+
tree
58+
}
59+
}

0 commit comments

Comments
 (0)