aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/Splitter.scala
blob: d62be1a827e0ca53cdc18350e7e3298e2ada4c6b (plain) (tree)
1
2
3
4
5
6
7
8
9




                        

                                                                                             
 
                                                   
   
                                                            

                  
                                             
 







                                                                                                                     
   
 










                                                                                                    




















                                                                                              
                                                                                



                                                                                     





















                                                                       
                                                                                     








                                                                                  
                                                             



                                                       
                                             


















                                                                                                                
  
 
package dotty.tools.dotc
package transform

import TreeTransforms._
import ast.Trees._
import core._
import Contexts._, Types._, Decorators._, Denotations._, Symbols._, SymDenotations._, Names._

/** Distribute applications into Block and If nodes
 */
class Splitter extends MiniPhaseTransform { thisTransform =>
  import ast.tpd._

  override def phaseName: String = "splitter"

  /** Distribute arguments among splitted branches */
  def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context) = {
    def recur(fn: Tree): Tree = fn match {
      case Block(stats, expr) => Block(stats, recur(expr))
      case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep))
      case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos
    }
    recur(tree.fun)
  }

  override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) =
    distribute(tree, typeApply)

  override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
    distribute(tree, apply)

  private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx)
  private val apply     = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx)

/* The following is no longer necessary, since we select members on the join of an or type:
 *
  /** If we select a name, make sure the node has a symbol.
   *  If necessary, split the qualifier with type tests.
   *  Example: Assume:
   *
   *      class A { def f(x: S): T }
   *      class B { def f(x: S): T }
   *      def p(): A | B
   *
   *  Then   p().f(a)   translates to
   *
   *      val ev$1 = p()
   *      if (ev$1.isInstanceOf[A]) ev$1.asInstanceOf[A].f(a)
   *      else ev$1.asInstanceOf[B].f(a)
   */
  override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = {
    val Select(qual, name) = tree

    def memberDenot(tp: Type): SingleDenotation = {
      val mbr = tp.member(name)
      if (!mbr.isOverloaded) mbr.asSingleDenotation
      else tree.tpe match {
        case tref: TermRefWithSignature => mbr.atSignature(tref.sig).checkUnique
        case _ =>
          def alts = mbr.alternatives.map(alt => i"$alt: ${alt.info}").mkString(", ")
          ctx.error(s"cannot disambiguate overloaded members $alts", tree.pos)
          NoDenotation
      }
    }

    def candidates(tp: Type): List[Symbol] = {
      val mbr = memberDenot(tp)
      if (mbr.symbol.exists) mbr.symbol :: Nil
      else tp.widen match {
        case tref: TypeRef =>
          tref.info match {
            case TypeBounds(_, hi) => candidates(hi)
            case _ => Nil
          }
        case OrType(tp1, tp2) =>
          candidates(tp1) | candidates(tp2)
        case AndType(tp1, tp2) =>
          candidates(tp1) & candidates(tp2)
        case tpw =>
          Nil
      }
    }

    def isStructuralSelect(tp: Type): Boolean = tp.stripTypeVar match {
      case tp: RefinedType => tp.refinedName == name || isStructuralSelect(tp.parent)
      case tp: TypeProxy => isStructuralSelect(tp.underlying)
      case AndType(tp1, tp2) => isStructuralSelect(tp1) || isStructuralSelect(tp2)
      case _ => false
    }

    if (tree.symbol.exists) tree
    else {
      def choose(qual: Tree, syms: List[Symbol]): Tree = {
        def testOrCast(which: Symbol, mbr: Symbol) =
          qual.select(which).appliedToType(mbr.owner.typeRef)
        def select(sym: Symbol) = {
          val qual1 =
            if (qual.tpe derivesFrom sym.owner) qual
            else testOrCast(defn.Any_asInstanceOf, sym)
          qual1.select(sym).withPos(tree.pos)
        }
        syms match {
          case Nil =>
            def msg =
              if (isStructuralSelect(qual.tpe))
                s"cannot access member '$name' from structural type ${qual.tpe.widen.show}; use Dynamic instead"
              else
                s"no candidate symbols for ${tree.tpe.show} found in ${qual.tpe.show}"
            ctx.error(msg, tree.pos)
            tree
          case sym :: Nil =>
            select(sym)
          case sym :: syms1 =>
            If(testOrCast(defn.Any_isInstanceOf, sym), select(sym), choose(qual, syms1))
        }
      }
      evalOnce(qual)(qual => choose(qual, candidates(qual.tpe)))
    }
  }
*/
}