aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2014-04-02 17:43:07 +0200
committerDmitry Petrashko <dark@d-d.me>2014-04-02 17:43:07 +0200
commite52fa5086c413e6fceb10d8650fc7becdc5c04a9 (patch)
treef28f9d853f913b9dd8e9de68fe2c30ca41f8e58f
parent2033b5607a41b77590b8d23bf5c40c906a0b42e7 (diff)
parent959c8d0cd1d4003ab28ba88bf05854682d32d17d (diff)
downloaddotty-e52fa5086c413e6fceb10d8650fc7becdc5c04a9.tar.gz
dotty-e52fa5086c413e6fceb10d8650fc7becdc5c04a9.tar.bz2
dotty-e52fa5086c413e6fceb10d8650fc7becdc5c04a9.zip
Merge pull request #103 from DarkDimius/transform/erasure-transforms
TypeTestCasts fixes and InterceptedMethods transformer
-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.scala29
-rw-r--r--tests/pos/hashhash-overloads.scala (renamed from tests/untried/pos/hashhash-overloads.scala)2
6 files changed, 178 insertions, 20 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 54f72c20c..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
@@ -33,13 +34,13 @@ class TypeTestsCasts extends TreeTransform {
def isPrimitive(tp: Type) = tp.classSymbol.isPrimitiveValueClass
- def derivedTree(qual1: Tree, sym: Symbol) =
- cpy.TypeApply(tree, Select(qual1, sym) withPos qual.pos, tree.args)
+ def derivedTree(qual1: Tree, sym: Symbol, tp: Type) =
+ cpy.TypeApply(tree, Select(qual1, sym) withPos qual.pos, List(TypeTree(tp)))
def qualCls = qual.tpe.classSymbol
- def transformIsInstanceOf(argType: Type): Tree = {
- if (qual.tpe <:< argType)
+ def transformIsInstanceOf(expr:Tree, argType: Type): Tree = {
+ if (expr.tpe <:< argType)
Literal(Constant(true)) withPos tree.pos
else if (qualCls.isPrimitiveValueClass) {
val argCls = argType.classSymbol
@@ -49,11 +50,11 @@ class TypeTestsCasts extends TreeTransform {
else argType.dealias match {
case _: SingletonType =>
val cmpOp = if (argType derivesFrom defn.AnyValClass) defn.Any_equals else defn.Object_eq
- Apply(Select(qual, cmpOp), singleton(argType) :: Nil)
+ Apply(Select(expr, cmpOp), singleton(argType) :: Nil)
case AndType(tp1, tp2) =>
- evalOnce(fun) { fun =>
- val erased1 = transformIsInstanceOf(tp1)
- val erased2 = transformIsInstanceOf(tp2)
+ evalOnce(expr) { fun =>
+ val erased1 = transformIsInstanceOf(fun, tp1)
+ val erased2 = transformIsInstanceOf(fun, tp2)
erased1 match {
case Literal(Constant(true)) => erased2
case _ =>
@@ -68,10 +69,10 @@ class TypeTestsCasts extends TreeTransform {
runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil)
if (ndims == 1) isArrayTest(qual)
else evalOnce(qual) { qual1 =>
- mkAnd(derivedTree(qual1, defn.Object_isInstanceOf), isArrayTest(qual1))
+ mkAnd(derivedTree(qual1, defn.Object_isInstanceOf, qual1.tpe), isArrayTest(qual1))
}
case _ =>
- derivedTree(qual, defn.Object_isInstanceOf)
+ derivedTree(expr, defn.Object_isInstanceOf, argType)
}
}
@@ -81,14 +82,14 @@ class TypeTestsCasts extends TreeTransform {
else if (qualCls.isPrimitiveValueClass) {
val argCls = argType.classSymbol
if (argCls.isPrimitiveValueClass) primitiveConversion(qual, argCls)
- else derivedTree(box(qual), defn.Object_asInstanceOf)
+ else derivedTree(box(qual), defn.Object_asInstanceOf, argType)
}
else
- derivedTree(qual, defn.Object_asInstanceOf)
+ derivedTree(qual, defn.Object_asInstanceOf, argType)
}
if (sym eq defn.Any_isInstanceOf)
- transformIsInstanceOf(tree.args.head.tpe)
+ transformIsInstanceOf(qual, tree.args.head.tpe)
else if (defn.asInstanceOfMethods contains sym)
transformAsInstanceOf(tree.args.head.tpe)
else tree
diff --git a/tests/untried/pos/hashhash-overloads.scala b/tests/pos/hashhash-overloads.scala
index 40519bae0..a27cb1301 100644
--- a/tests/untried/pos/hashhash-overloads.scala
+++ b/tests/pos/hashhash-overloads.scala
@@ -3,4 +3,6 @@ object Test {
def g = 5f.##
def h = ({ 5 ; println("abc") }).##
def f2 = null.##
+ def l = 3L.##
+ def b(arg: Boolean) = arg.##
}