Skip to content

Add pattern matcher optimizations #2829

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jul 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c6076a0
Polishings
odersky Jun 25, 2017
886b2a1
Add new pattern matcher
odersky Jun 25, 2017
b2aa27e
Fixes to pattern matcher
odersky Jun 27, 2017
6fd3971
Make IsInstanceOfEvaluator more robust
odersky Jun 27, 2017
e8eb6d9
Fix isInstanceOfEvaluator
odersky Jun 28, 2017
f55959c
Test outer pointer in pattern matches
odersky Jun 28, 2017
e325601
Fixes to PatMat
odersky Jun 28, 2017
0717f18
Make TypeTestsCasts an Object
odersky Jun 29, 2017
4057fb2
Fix typo in TypeMap
odersky Jun 30, 2017
a43d8c1
Refine tpd.isInstance
odersky Jun 30, 2017
003301b
Add structural equality === for some trees
odersky Jun 30, 2017
2e7294c
Perform switch optimization in pattern matcher
odersky Jun 30, 2017
96dd7fd
Warn on failed switch optimization
odersky Jun 30, 2017
ff473ba
Update test
odersky Jun 30, 2017
499eed4
Refactorings
odersky Jun 30, 2017
fb03273
Replace previous pattern matcher
odersky Jun 30, 2017
40910d8
More docs and some cleanups
odersky Jun 30, 2017
bcd9836
Turn optimisations for tests on again.
odersky Jun 30, 2017
8c4323f
Turn off local optimizations again
odersky Jun 30, 2017
90226bd
Some small tweaks
odersky Jun 30, 2017
0e4e2bd
More extensive optimizations
odersky Jul 3, 2017
aaf9d7d
Split vars and labels
odersky Jul 3, 2017
eec5732
Update documentation
odersky Jul 4, 2017
661f392
Fix typo
odersky Jul 5, 2017
e61ef5d
Refactor optimizations
odersky Jul 5, 2017
b4377dc
More aggressive variable sharing
odersky Jul 5, 2017
7ce9e80
Fix docs
odersky Jul 5, 2017
8eacccf
Improve sanitize algorithm
odersky Jul 6, 2017
53d8fef
Refine condition when emitted code is a switch
odersky Jul 6, 2017
c97d783
Avoid test-pickling failure.
odersky Jul 6, 2017
e138568
Disable optimization
odersky Jul 6, 2017
79493df
Address reviewers comments
odersky Jul 10, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,37 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
case _ =>
false
}

/** Structural tree comparison (since == on trees is reference equality).
* For the moment, only Ident, Select, Literal, Apply and TypeApply are supported
*/
implicit class StructuralEqDeco(t1: Tree) {
def === (t2: Tree)(implicit ctx: Context): Boolean = (t1, t2) match {
case (t1: Ident, t2: Ident) =>
t1.symbol == t2.symbol
case (t1 @ Select(q1, _), t2 @ Select(q2, _)) =>
t1.symbol == t2.symbol && q1 === q2
case (Literal(c1), Literal(c2)) =>
c1 == c2
case (Apply(f1, as1), Apply(f2, as2)) =>
f1 === f2 && as1.corresponds(as2)(_ === _)
case (TypeApply(f1, ts1), TypeApply(f2, ts2)) =>
f1 === f2 && ts1.tpes.corresponds(ts2.tpes)(_ =:= _)
case _ =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of code in Dotty has a habbit of needlessly wrapping code in Block(Nil, expr).
It would be nice to add a case here that handles blocks to make it more robust.

false
}
def hash(implicit ctx: Context): Int =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead code?

t1.getClass.hashCode * 37 + {
t1 match {
case t1: Ident => t1.symbol.hashCode
case t1 @ Select(q1, _) => t1.symbol.hashCode * 41 + q1.hash
case Literal(c1) => c1.hashCode
case Apply(f1, as1) => (f1.hash /: as1)((h, arg) => h * 41 + arg.hash)
case TypeApply(f1, ts1) => (f1.hash /: ts1)((h, arg) => h * 41 + arg.tpe.hash)
case _ => t1.hashCode
}
}
}
}

object TreeInfo {
Expand Down
50 changes: 37 additions & 13 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Denotations._, Decorators._, DenotTransformers._
import collection.mutable
import util.{Property, SourceFile, NoSource}
import typer.ErrorReporting._
import NameKinds.TempResultName
import NameKinds.{TempResultName, OuterSelectName}

import scala.annotation.tailrec
import scala.io.Codec
Expand Down Expand Up @@ -171,6 +171,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def SyntheticValDef(name: TermName, rhs: Tree)(implicit ctx: Context): ValDef =
ValDef(ctx.newSymbol(ctx.owner, name, Synthetic, rhs.tpe.widen, coord = rhs.pos), rhs)

def DefDef(sym: TermSymbol, tparams: List[TypeSymbol], vparamss: List[List[TermSymbol]],
resultType: Type, rhs: Tree)(implicit ctx: Context): DefDef =
ta.assignType(
untpd.DefDef(sym.name, tparams map TypeDef, vparamss.nestedMap(ValDef(_)),
TypeTree(resultType), rhs),
sym)

def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef =
ta.assignType(DefDef(sym, Function.const(rhs) _), sym)

Expand Down Expand Up @@ -199,14 +206,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val (vparamss, rtp) = valueParamss(mtp)
val targs = tparams map (_.typeRef)
val argss = vparamss.nestedMap(vparam => Ident(vparam.termRef))
ta.assignType(
untpd.DefDef(
sym.name,
tparams map TypeDef,
vparamss.nestedMap(ValDef(_)),
TypeTree(rtp),
rhsFn(targs)(argss)),
sym)
DefDef(sym, tparams, vparamss, rtp, rhsFn(targs)(argss))
}

def TypeDef(sym: TypeSymbol)(implicit ctx: Context): TypeDef =
Expand Down Expand Up @@ -682,6 +682,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def select(name: Name)(implicit ctx: Context): Select =
Select(tree, name)

/** A select node with the given selector name such that the designated
* member satisfies predicate `p`. Useful for disambiguating overloaded members.
*/
def select(name: Name, p: Symbol => Boolean)(implicit ctx: Context): Select =
select(tree.tpe.member(name).suchThat(p).symbol)

/** A select node with the given type */
def select(tp: NamedType)(implicit ctx: Context): Select =
untpd.Select(tree, tp.name).withType(tp)
Expand Down Expand Up @@ -751,9 +757,20 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def ensureApplied(implicit ctx: Context): Tree =
if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone

/** `tree.isInstanceOf[tp]` */
def isInstance(tp: Type)(implicit ctx: Context): Tree =
tree.select(defn.Any_isInstanceOf).appliedToType(tp)
/** `tree == that` */
def equal(that: Tree)(implicit ctx: Context) =
applyOverloaded(tree, nme.EQ, that :: Nil, Nil, defn.BooleanType)

/** `tree.isInstanceOf[tp]`, with special treatment of singleton types */
def isInstance(tp: Type)(implicit ctx: Context): Tree = tp match {
case tp: SingletonType =>
if (tp.widen.derivesFrom(defn.ObjectClass))
tree.ensureConforms(defn.ObjectType).select(defn.Object_eq).appliedTo(singleton(tp))
else
singleton(tp).equal(tree)
case _ =>
tree.select(defn.Any_isInstanceOf).appliedToType(tp)
}

/** tree.asInstanceOf[`tp`] */
def asInstance(tp: Type)(implicit ctx: Context): Tree = {
Expand All @@ -771,7 +788,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
else Erasure.Boxing.adaptToType(tree, tp)

/** `tree ne null` (might need a cast to be type correct) */
def testNotNull(implicit ctx: Context): Tree =
def testNotNull(implicit ctx: Context): Tree =
tree.ensureConforms(defn.ObjectType)
.select(defn.Object_ne).appliedTo(Literal(Constant(null)))

Expand Down Expand Up @@ -805,6 +822,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
else Assign(tree, rhs)

/** A synthetic select with that will be turned into an outer path by ExplicitOuter.
* @param levels How many outer levels to select
* @param tp The type of the destination of the outer path.
*/
def outerSelect(levels: Int, tp: Type)(implicit ctx: Context): Tree =
untpd.Select(tree, OuterSelectName(EmptyTermName, levels)).withType(tp)

// --- Higher order traversal methods -------------------------------

/** Apply `f` to each subtree of this tree */
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ object Printers {
val pickling: Printer = noPrinter
val inlining: Printer = noPrinter
val exhaustivity: Printer = noPrinter
val patmatch: Printer = noPrinter
val simplify: Printer = noPrinter
}
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ class Definitions {
// generated by pattern matcher, eliminated by erasure

def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode,
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf)
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf, Any_typeTest)

lazy val ObjectClass: ClassSymbol = {
val cls = ctx.requiredClass("java.lang.Object")
Expand Down Expand Up @@ -373,6 +373,10 @@ class Definitions {
def Seq_apply(implicit ctx: Context) = Seq_applyR.symbol
lazy val Seq_headR = SeqClass.requiredMethodRef(nme.head)
def Seq_head(implicit ctx: Context) = Seq_headR.symbol
lazy val Seq_dropR = SeqClass.requiredMethodRef(nme.drop)
def Seq_drop(implicit ctx: Context) = Seq_dropR.symbol
lazy val Seq_lengthCompareR = SeqClass.requiredMethodRef(nme.lengthCompare)
def Seq_lengthCompare(implicit ctx: Context) = Seq_lengthCompareR.symbol

lazy val ArrayType: TypeRef = ctx.requiredClassRef("scala.Array")
def ArrayClass(implicit ctx: Context) = ArrayType.symbol.asClass
Expand Down
7 changes: 2 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,7 @@ object Types {
case _ => NoType
}

/** Is this type guaranteed not to have `null` as a value?
* For the moment this is only true for modules, but it could
* be refined later.
*/
/** Is this type guaranteed not to have `null` as a value? */
final def isNotNull(implicit ctx: Context): Boolean = this match {
case tp: ConstantType => tp.value.value != null
case tp: ClassInfo => !tp.cls.isNullableClass && tp.cls != defn.NothingClass
Expand Down Expand Up @@ -3753,7 +3750,7 @@ object Types {
if (underlying1 eq underlying) tp
else derivedAnnotatedType(tp, underlying1, mapOver(annot))

case tp @ WildcardType =>
case tp: WildcardType =>
derivedWildcardType(tp, mapOver(tp.optBounds))

case tp: JavaArrayType =>
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree : ${tree.tpe} / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase.prev}")
}

object Erasure extends TypeTestsCasts{

object Erasure {
import tpd._
import TypeTestsCasts._

object Boxing {

Expand Down
Loading