package dotty.tools.dotc package transform import core.Contexts._ import core.Symbols._ import core.Types._ import core.Constants._ import core.StdNames._ import core.TypeErasure.isUnboundedGeneric import ast.Trees._ import Erasure.Boxing._ import core.TypeErasure._ import ValueClasses._ /** This transform normalizes type tests and type casts, * also replacing type tests with singleton argument type with reference equality check * Any remaining type tests * - use the object methods $isInstanceOf and $asInstanceOf * - have a reference type as receiver * - can be translated directly to machine instructions * * * Unfortunately this phase ended up being not Y-checkable unless types are erased. A cast to an ConstantType(3) or x.type * cannot be rewritten before erasure. */ trait TypeTestsCasts { import ast.tpd._ // override def phaseName: String = "typeTestsCasts" def interceptTypeApply(tree: TypeApply)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show}", show = true) { tree.fun match { case fun @ Select(qual, selector) => val sym = tree.symbol def isPrimitive(tp: Type) = tp.classSymbol.isPrimitiveValueClass def derivedTree(qual1: Tree, sym: Symbol, tp: Type) = cpy.TypeApply(tree)(qual1.select(sym).withPos(qual.pos), List(TypeTree(tp))) def qualCls = qual.tpe.widen.classSymbol def transformIsInstanceOf(expr:Tree, argType: Type): Tree = { def argCls = argType.classSymbol if ((expr.tpe <:< argType) && isPureExpr(expr)) Literal(Constant(true)) withPos tree.pos else if (argCls.isPrimitiveValueClass) if (qualCls.isPrimitiveValueClass) Literal(Constant(qualCls == argCls)) withPos tree.pos else transformIsInstanceOf(expr, defn.boxedType(argCls.typeRef)) else argType.dealias match { case _: SingletonType => val cmpOp = if (argType derivesFrom defn.AnyValClass) defn.Any_equals else defn.Object_eq expr.select(cmpOp).appliedTo(singleton(argType)) case AndType(tp1, tp2) => evalOnce(expr) { fun => val erased1 = transformIsInstanceOf(fun, tp1) val erased2 = transformIsInstanceOf(fun, tp2) erased1 match { case Literal(Constant(true)) => erased2 case _ => erased2 match { case Literal(Constant(true)) => erased1 case _ => erased1 and erased2 } } } case defn.MultiArrayOf(elem, ndims) if isUnboundedGeneric(elem) => def isArrayTest(arg: Tree) = ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal(Constant(ndims))) if (ndims == 1) isArrayTest(qual) else evalOnce(qual) { qual1 => derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1) } case _ => derivedTree(expr, defn.Any_isInstanceOf, argType) } } def transformAsInstanceOf(argType: Type): Tree = { def argCls = argType.widen.classSymbol if (qual.tpe <:< argType) Typed(qual, tree.args.head) else if (qualCls.isPrimitiveValueClass) { if (argCls.isPrimitiveValueClass) primitiveConversion(qual, argCls) else derivedTree(box(qual), defn.Any_asInstanceOf, argType) } else if (argCls.isPrimitiveValueClass) unbox(qual.ensureConforms(defn.ObjectType), argType) else if (isDerivedValueClass(argCls)) { qual // adaptToType in Erasure will do the necessary type adaptation } else derivedTree(qual, defn.Any_asInstanceOf, argType) } /** Transform isInstanceOf OrType * * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B] * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B] * * The transform happens before erasure of `argType`, thus cannot be merged * with `transformIsInstanceOf`, which depends on erased type of `argType`. */ def transformTypeTest(qual: Tree, argType: Type): Tree = argType.dealias match { case OrType(tp1, tp2) => evalOnce(qual) { fun => transformTypeTest(fun, tp1) .select(defn.Boolean_||) .appliedTo(transformTypeTest(fun, tp2)) } case AndType(tp1, tp2) => evalOnce(qual) { fun => transformTypeTest(fun, tp1) .select(defn.Boolean_&&) .appliedTo(transformTypeTest(fun, tp2)) } case _ => transformIsInstanceOf(qual, erasure(argType)) } if (sym eq defn.Any_isInstanceOf) transformTypeTest(qual, tree.args.head.tpe) else if (sym eq defn.Any_asInstanceOf) transformAsInstanceOf(erasure(tree.args.head.tpe)) else tree case _ => tree } } }