aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorDmitry Petrashko <dmitry.petrashko@gmail.com>2014-03-31 15:04:41 +0200
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2014-04-02 11:33:28 +0200
commit959c8d0cd1d4003ab28ba88bf05854682d32d17d (patch)
treef28f9d853f913b9dd8e9de68fe2c30ca41f8e58f /src/dotty/tools/dotc
parentea5acb56754e26585d2e17080c35a027988660e9 (diff)
downloaddotty-959c8d0cd1d4003ab28ba88bf05854682d32d17d.tar.gz
dotty-959c8d0cd1d4003ab28ba88bf05854682d32d17d.tar.bz2
dotty-959c8d0cd1d4003ab28ba88bf05854682d32d17d.zip
Intercepted methods transformer
Replace member references for: methods inside Any( == and !=) ## on primitives .getClass on primitives
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/Compiler.scala2
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala4
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala16
-rw-r--r--src/dotty/tools/dotc/transform/InterceptedMethods.scala145
-rw-r--r--src/dotty/tools/dotc/transform/TypeTestsCasts.scala3
5 files changed, 163 insertions, 7 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 1e8f13578..8bcd919c2 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -21,7 +21,7 @@ class Compiler {
List(
List(new FrontEnd),
List(new LazyValsCreateCompanionObjects, new PatternMatcher), //force separataion between lazyVals and LVCreateCO
- List(new LazyValTranformContext().transformer, new Splitter, new TypeTestsCasts),
+ List(new LazyValTranformContext().transformer, new Splitter, new TypeTestsCasts, new InterceptedMethods),
List(new Erasure),
List(new UncurryTreeTransform)
)
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index b0214a631..d6852fe13 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -205,7 +205,8 @@ object Contexts {
final def withPhase(phase: Phase): Context =
withPhase(phase.id)
- /** If -Ydebug is on, the top of the stack trace where this context
+
+ /** If -Ydebug is on, the top of the stack trace where this context
* was created, otherwise `null`.
*/
private var creationTrace: Array[StackTraceElement] = _
@@ -298,6 +299,7 @@ object Contexts {
setCreationTrace()
this
}
+
/** A fresh clone of this context. */
def fresh: FreshContext = clone.asInstanceOf[FreshContext].init(this)
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 46878d3ca..40fd33671 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -126,6 +126,7 @@ class Definitions {
lazy val AnyValClass: ClassSymbol = ctx.requiredClass("scala.AnyVal")
+ lazy val AnyVal_getClass = AnyValClass.requiredMethod(nme.getClass_)
lazy val Any_== = newMethod(AnyClass, nme.EQ, methOfAny(BooleanType), Final)
lazy val Any_!= = newMethod(AnyClass, nme.NE, methOfAny(BooleanType), Final)
lazy val Any_equals = newMethod(AnyClass, nme.equals_, methOfAny(BooleanType))
@@ -154,6 +155,7 @@ class Definitions {
ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef))
lazy val ScalaPredefModule = ctx.requiredModule("scala.Predef")
+ lazy val ScalaRuntimeModule = ctx.requiredModule("scala.runtime.ScalaRunTime")
lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef")
lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil")
@@ -170,7 +172,7 @@ class Definitions {
lazy val UnitClass = valueClassSymbol("scala.Unit", BoxedUnitClass, java.lang.Void.TYPE, UnitEnc)
lazy val BooleanClass = valueClassSymbol("scala.Boolean", BoxedBooleanClass, java.lang.Boolean.TYPE, BooleanEnc)
-
+ lazy val Boolean_! = BooleanClass.requiredMethod(nme.UNARY_!)
lazy val Boolean_and = BooleanClass.requiredMethod(nme.ZAND)
lazy val ByteClass = valueClassSymbol("scala.Byte", BoxedByteClass, java.lang.Byte.TYPE, ByteEnc)
@@ -178,6 +180,12 @@ class Definitions {
lazy val CharClass = valueClassSymbol("scala.Char", BoxedCharClass, java.lang.Character.TYPE, CharEnc)
lazy val IntClass = valueClassSymbol("scala.Int", BoxedIntClass, java.lang.Integer.TYPE, IntEnc)
lazy val LongClass = valueClassSymbol("scala.Long", BoxedLongClass, java.lang.Long.TYPE, LongEnc)
+ lazy val Long_XOR_Long = LongClass.info.member(nme.XOR).requiredSymbol(
+ x => (x is Method) && (x.info.firstParamTypes.head isRef defn.LongClass)
+ )
+ lazy val Long_LSR_Int = LongClass.info.member(nme.LSR).requiredSymbol(
+ x => (x is Method) && (x.info.firstParamTypes.head isRef defn.IntClass)
+ )
lazy val FloatClass = valueClassSymbol("scala.Float", BoxedFloatClass, java.lang.Float.TYPE, FloatEnc)
lazy val DoubleClass = valueClassSymbol("scala.Double", BoxedDoubleClass, java.lang.Double.TYPE, DoubleEnc)
@@ -421,9 +429,7 @@ class Definitions {
// ----- primitive value class machinery ------------------------------------------
- lazy val ScalaValueClasses: collection.Set[Symbol] = Set(
- UnitClass,
- BooleanClass,
+ lazy val ScalaNumericValueClasses: collection.Set[Symbol] = Set(
ByteClass,
ShortClass,
CharClass,
@@ -431,6 +437,8 @@ class Definitions {
LongClass,
FloatClass,
DoubleClass)
+
+ lazy val ScalaValueClasses: collection.Set[Symbol] = ScalaNumericValueClasses + UnitClass + BooleanClass
lazy val ScalaBoxedClasses = ScalaValueClasses map boxedClass
diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
new file mode 100644
index 000000000..b56985ffe
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
@@ -0,0 +1,145 @@
+package dotty.tools.dotc
+package transform
+
+import TreeTransforms._
+import core.DenotTransformers._
+import core.Denotations._
+import core.SymDenotations._
+import core.Contexts._
+import core.Types._
+import ast.Trees._
+import ast.tpd.{Apply, Tree, cpy}
+import dotty.tools.dotc.ast.tpd
+import scala.collection.mutable
+import dotty.tools.dotc._
+import core._
+import Contexts._
+import Symbols._
+import Decorators._
+import NameOps._
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, TreeTransform}
+import dotty.tools.dotc.ast.Trees._
+import dotty.tools.dotc.ast.{untpd, tpd}
+import dotty.tools.dotc.core.Constants.Constant
+import dotty.tools.dotc.core.Types.MethodType
+import dotty.tools.dotc.core.Names.Name
+import dotty.runtime.LazyVals
+import scala.collection.mutable.ListBuffer
+import dotty.tools.dotc.core.Denotations.SingleDenotation
+import dotty.tools.dotc.core.SymDenotations.SymDenotation
+import dotty.tools.dotc.core.DenotTransformers.DenotTransformer
+import StdNames._
+
+/** Replace member references as follows:
+ *
+ * - `x == y` for == in class Any becomes `x equals y` with equals in class Object.
+ * - `x != y` for != in class Any becomes `!(x equals y)` with equals in class Object.
+ * - `x.##` for ## in other classes becomes calls to ScalaRunTime.hash,
+ * using the most precise overload available
+ * - `x.getClass` for getClass in primitives becomes `x.getClass` with getClass in class Object.
+ */
+class InterceptedMethods extends TreeTransform {
+
+ import tpd._
+
+ override def name: String = "intercepted"
+
+ private var getClassMethods: Set[Symbol] = _
+ private var poundPoundMethods: Set[Symbol] = _
+ private var Any_comparisons: Set[Symbol] = _
+ private var interceptedMethods: Set[Symbol] = _
+ private var primitiveGetClassMethods: Set[Symbol] = _
+
+ /** perform context-dependant initialization */
+ override def init(implicit ctx: Context, info: TransformerInfo): Unit = {
+ getClassMethods = Set(defn.Any_getClass, defn.AnyVal_getClass)
+ poundPoundMethods = Set(defn.Any_##, defn.Object_##)
+ Any_comparisons = Set(defn.Any_==, defn.Any_!=)
+ interceptedMethods = getClassMethods ++ poundPoundMethods ++ Any_comparisons
+ primitiveGetClassMethods = Set[Symbol](defn.Any_getClass, defn.AnyVal_getClass) ++
+ defn.ScalaValueClasses.map(x => x.requiredMethod(nme.getClass_))
+ }
+
+ // this should be removed if we have guarantee that ## will get Apply node
+ override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ if (tree.symbol.isTerm && poundPoundMethods.contains(tree.symbol.asTerm)) {
+ val rewrite = PoundPoundValue(tree.qualifier)
+ ctx.log(s"$name rewrote $tree to $rewrite")
+ rewrite
+ }
+ else tree
+ }
+
+ private def PoundPoundValue(tree: Tree)(implicit ctx: Context) = {
+ val s = tree.tpe.widen.typeSymbol
+ if (s == defn.NullClass) Literal(Constant(0))
+ else {
+ // Since we are past typer, we need to avoid creating trees carrying
+ // overloaded types. This logic is custom (and technically incomplete,
+ // although serviceable) for def hash. What is really needed is for
+ // the overloading logic presently hidden away in a few different
+ // places to be properly exposed so we can just call "resolveOverload"
+ // after typer. Until then:
+
+ def alts = defn.ScalaRuntimeModule.info.member(nme.hash_)
+
+ // if tpe is a primitive value type, alt1 will match on the exact value,
+ // taking in account that null.asInstanceOf[Int] == 0
+ def alt1 = alts.suchThat(_.info.firstParamTypes.head =:= tree.tpe.widen)
+
+ // otherwise alt2 will match. alt2 also knows how to handle 'null' runtime value
+ def alt2 = defn.ScalaRuntimeModule.info.member(nme.hash_)
+ .suchThat(_.info.firstParamTypes.head.typeSymbol == defn.AnyClass)
+
+ if (defn.ScalaNumericValueClasses contains s) {
+ tpd.Apply(Ident(alt1.termRef), List(tree))
+ } else tpd.Apply(Ident(alt2.termRef), List(tree))
+ }
+ }
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ def unknown = {
+ assert(false, s"The symbol '${tree.fun.symbol}' was interecepted but didn't match any cases, " +
+ s"that means the intercepted methods set doesn't match the code")
+ tree
+ }
+ if (tree.fun.symbol.isTerm && tree.args.isEmpty &&
+ (interceptedMethods contains tree.fun.symbol.asTerm)) {
+ val rewrite: Tree = tree.fun match {
+ case Select(qual, name) =>
+ if (poundPoundMethods contains tree.fun.symbol.asTerm) {
+ PoundPoundValue(qual)
+ } else if (Any_comparisons contains tree.fun.symbol.asTerm) {
+ if (tree.fun.symbol eq defn.Any_==) {
+ Apply(Select(qual, defn.Object_equals.termRef), tree.args)
+ } else if (tree.fun.symbol eq defn.Any_!=) {
+ Select(Apply(Select(qual, defn.Object_equals.termRef), tree.args), defn.Boolean_!.termRef)
+ } else unknown
+ } /* else if (isPrimitiveValueClass(qual.tpe.typeSymbol)) {
+ // todo: this is needed to support value classes
+ // Rewrite 5.getClass to ScalaRunTime.anyValClass(5)
+ global.typer.typed(gen.mkRuntimeCall(nme.anyValClass,
+ List(qual, typer.resolveClassTag(tree.pos, qual.tpe.widen))))
+ }*/
+ else if (primitiveGetClassMethods.contains(tree.fun.symbol)) {
+ // if we got here then we're trying to send a primitive getClass method to either
+ // a) an Any, in which cage Object_getClass works because Any erases to object. Or
+ //
+ // b) a non-primitive, e.g. because the qualifier's type is a refinement type where one parent
+ // of the refinement is a primitive and another is AnyRef. In that case
+ // we get a primitive form of _getClass trying to target a boxed value
+ // so we need replace that method name with Object_getClass to get correct behavior.
+ // See SI-5568.
+ Apply(Select(qual, defn.Object_getClass.termRef), Nil)
+ } else {
+ unknown
+ }
+ case _ =>
+ unknown
+ }
+ ctx.log(s"$name rewrote $tree to $rewrite")
+ rewrite
+ }
+ else tree
+ }
+}
diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
index cbd521210..aba674d1c 100644
--- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
+++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
@@ -15,7 +15,8 @@ import typer.ErrorReporting._
import ast.Trees._
import Erasure.Boxing.box
-/** This transform normalizes type tests and type casts.
+/** This transform normalizes type tests and type casts,
+ * also replacing type tests with singleton argument type with refference equality check
* Any remaining type tests
* - use the object methods $isInstanceOf and $asInstanceOf
* - have a reference type as receiver