aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala131
1 files changed, 131 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala
new file mode 100644
index 000000000..7c60e8d72
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/transform/InterceptedMethods.scala
@@ -0,0 +1,131 @@
+package dotty.tools.dotc
+package transform
+
+import TreeTransforms._
+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 scala.collection.mutable.ListBuffer
+import dotty.tools.dotc.core.Denotations.SingleDenotation
+import dotty.tools.dotc.core.SymDenotations.SymDenotation
+import StdNames._
+import Phases.Phase
+
+/** Replace member references as follows:
+ *
+ * - `x != y` for != in class Any becomes `!(x == y)` with == in class Any.
+ * - `x.##` for ## in NullClass becomes `0`
+ * - `x.##` for ## in Any 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 MiniPhaseTransform {
+ thisTransform =>
+
+ import tpd._
+
+ override def phaseName: String = "intercepted"
+
+ private var primitiveGetClassMethods: Set[Symbol] = _
+
+ var Any_## : Symbol = _ // cached for performance reason
+
+ /** perform context-dependant initialization */
+ override def prepareForUnit(tree: Tree)(implicit ctx: Context) = {
+ this.Any_## = defn.Any_##
+ primitiveGetClassMethods = Set[Symbol]() ++ defn.ScalaValueClasses().map(x => x.requiredMethod(nme.getClass_))
+ this
+ }
+
+ // 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 && (Any_## eq tree.symbol.asTerm)) {
+ val rewrite = poundPoundValue(tree.qualifier)
+ ctx.log(s"$phaseName 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)
+
+ Ident((if (s.isNumericValueClass) alt1 else alt2).termRef)
+ .appliedTo(tree)
+ }
+ }
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ def unknown = {
+ assert(false, s"The symbol '${tree.fun.symbol.showLocated}' was intercepted but didn't match any cases, " +
+ s"that means the intercepted methods set doesn't match the code")
+ tree
+ }
+ lazy val Select(qual, _) = tree.fun
+ val Any_## = this.Any_##
+ val Any_!= = defn.Any_!=
+ val rewrite: Tree = tree.fun.symbol match {
+ case Any_## =>
+ poundPoundValue(qual)
+ case Any_!= =>
+ qual.select(defn.Any_==).appliedToArgs(tree.args).select(defn.Boolean_!)
+ /*
+ /* 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))))
+ }*/
+ */
+ case t if primitiveGetClassMethods.contains(t) =>
+ // 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.
+ qual.selectWithSig(defn.Any_getClass).appliedToNone
+ case _ =>
+ tree
+ }
+ ctx.log(s"$phaseName rewrote $tree to $rewrite")
+ rewrite
+ }
+}