aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/transform/IsInstanceOfEvaluator.scala
blob: d4483bb18a34f73c87c9e6d8083ae37df64b530d (plain) (tree)
























































































                                                                                                                                                    
package dotty.tools.dotc
package transform

import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.util.Positions._
import dotty.tools.dotc.core.TypeErasure._
import TreeTransforms.{MiniPhaseTransform, TransformerInfo}

class IsInstanceOfEvaluator extends MiniPhaseTransform { thisTransformer =>

  import dotty.tools.dotc.ast.tpd._

  def phaseName = "reachabilityChecker"

  override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = {
    val defn = ctx.definitions

    def handleStaticallyKnown(tree: TypeApply, scrutinee: Type, selector: Type, inMatch: Boolean, pos: Position): Tree =
      if (!(scrutinee <:< selector) && inMatch) {
        ctx.error(s"this case is unreachable due to `${selector.show}` not being a subclass of `${scrutinee.show}`", pos)
        tree
      } else if (!(scrutinee <:< selector) && !inMatch) {
        ctx.warning(s"this will always yield false since `${scrutinee.show}` is not a subclass of `${selector.show}` (will be optimized away)", pos)
        rewrite(tree, to = false)
      } else if (scrutinee <:< selector && !inMatch) {
        ctx.warning(s"this will always yield true since `${scrutinee.show}` is a subclass of `${selector.show}` (will be optimized away)", pos)
        rewrite(tree, to = true)
      } else /* if (scrutinee <:< selector && inMatch) */ rewrite(tree, to = true)

    def rewrite(tree: TypeApply, to: Boolean): Tree = tree

    def evalTypeApply(tree: TypeApply): Tree =
      if (tree.symbol != defn.Any_isInstanceOf) tree
      else tree.fun match {
        case s: Select => {
          val scrutinee = erasure(s.qualifier.tpe.widen)
          val selector  = erasure(tree.args.head.tpe.widen)

          val scTrait = scrutinee.typeSymbol is Flags.Trait
          val scClass =
            scrutinee.typeSymbol.isClass &&
            !(scrutinee.typeSymbol is Flags.Trait) &&
            !(scrutinee.typeSymbol is Flags.Module)

          val scClassNonFinal = scClass && !scrutinee.typeSymbol.is(Flags.Final)
          val scFinalClass    = scClass && (scrutinee.typeSymbol is Flags.Final)

          val selTrait = selector.typeSymbol is Flags.Trait
          val selClass =
            selector.typeSymbol.isClass &&
            !(selector.typeSymbol is Flags.Trait) &&
            !(selector.typeSymbol is Flags.Module)

          val selClassNonFinal = scClass && !(selector.typeSymbol is Flags.Final)
          val selFinalClass    = scClass && (selector.typeSymbol is Flags.Final)

          // Cases ---------------------------------
          val knownStatically = scFinalClass

          val falseIfUnrelated =
            (scClassNonFinal && selClassNonFinal) ||
            (scClassNonFinal && selFinalClass)    ||
            (scTrait && selFinalClass)

          // Doesn't need to be calculated (since true if others are false)
          //val happens =
          //  (scClassNonFinal && selClassNonFinal) ||
          //  (scTrait && selClassNonFinal)         ||
          //  (scTrait && selTrait)

          val inMatch = s.qualifier.symbol is Flags.Case

          if (knownStatically)
            handleStaticallyKnown(tree, scrutinee, selector, inMatch, tree.pos)
          else if (falseIfUnrelated && !(selector <:< scrutinee))
            rewrite(tree, to = false)
          else /*if (happens)*/ tree
        }

        case _ => tree
      }

    evalTypeApply(tree)
  }
}