aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/transform/ElimByName.scala
blob: 06c48902952d03a141de899fb4ee769abdfe98f0 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12



                        







                          
                 
                      


                        
                                                                                                
                                            
  
                                       
  
                                  
  
                                                                                            
                                   
  
                                                                             



                                                                                          

                                                                                               


                                                                                          





                                                                                                     





                                                                                                   
                                                                                     



                                                                                                 



                                                                                     
                                               
 
                                                         


                                                                                     






                                                                                                

                                                                            

                                                                  
                                
                                                  
                                                                               

                   
                                                                                        
                                     
                                                                                                     




                                                   
         
                                                                     



               
                                               
                                                                      


                                    

                                                                               

                                                                 



                                                                                           



                                                     


                                                                                                


                                                                                                  







                                                                                                                     



                                                                                                  
 
                                                                                    
                                                                             

                

                                                                                  
 
package dotty.tools.dotc
package transform

import TreeTransforms._
import core._
import DenotTransformers._
import Symbols._
import SymDenotations._
import Contexts._
import Types._
import Flags._
import Decorators._
import SymUtils._
import util.Attachment
import core.StdNames.nme
import ast.Trees._

/** This phase eliminates ExprTypes `=> T` as types of function parameters, and replaces them by
 *  nullary function types.  More precisely:
 *
 *  For the types of parameter symbols:
 *
 *      => T        ==>    () => T
 *
 *  Note that `=> T` types are not eliminated in MethodTypes. This is done later at erasure.
 *  Terms are rewritten as follows:
 *
 *      x           ==>    x.apply()   if x is a parameter that had type => T
 *
 *  Arguments to call-by-name parameters are translated as follows. First, the argument is
 *  rewritten by the rules
 *
 *      e.apply()   ==>    e           if e.apply() is an argument to a call-by-name parameter
 *      expr        ==>    () => expr  if other expr is an argument to a call-by-name parameter
 *
 *  This makes the argument compatible with a parameter type of () => T, which will be the
 *  formal parameter type at erasure. But to be -Ycheckable until then, any argument
 *  ARG rewritten by the rules above is again wrapped in an application DummyApply(ARG)
 *  where
 *
 *     DummyApply: [T](() => T): T
 *
 *  is a synthetic method defined in Definitions. Erasure will later strip these DummyApply wrappers.
 *
 *  Note: This scheme to have inconsistent types between method types (whose formal types are still
 *  ExprTypes and parameter valdefs (which are now FunctionTypes) is not pretty. There are two
 *  other options which have been abandoned or not yet pursued.
 *
 *  Option 1: Transform => T to () => T also in method and function types. The problem with this is
 *  that is that it requires to look at every type, and this forces too much, causing
 *  Cyclic Reference errors. Abandoned for this reason.
 *
 *  Option 2: Merge ElimByName with erasure, or have it run immediately before. This has not been
 *  tried yet.
 */
class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransformer =>
  import ast.tpd._

  override def phaseName: String = "elimByName"

  override def runsAfterGroupsOf = Set(classOf[Splitter])
    // assumes idents and selects have symbols; interferes with splitter distribution
    // that's why it's "after group".

  /** The info of the tree's symbol at phase Nullarify (i.e. before transformation) */
  private def originalDenotation(tree: Tree)(implicit ctx: Context) =
    tree.symbol.denot(ctx.withPhase(thisTransformer))

  override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
    ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) {

    def transformArg(arg: Tree, formal: Type): Tree = formal.dealias match {
      case formalExpr: ExprType =>
        var argType = arg.tpe.widenIfUnstable
        if (defn.isBottomType(argType)) argType = formal.widenExpr
        val argFun = arg match {
          case Apply(Select(qual, nme.apply), Nil)
          if qual.tpe.derivesFrom(defn.FunctionClass(0)) && isPureExpr(qual) =>
            qual
          case _ =>
            val inSuper = if (ctx.mode.is(Mode.InSuperCall)) InSuperCall else EmptyFlags
            val meth = ctx.newSymbol(
                ctx.owner, nme.ANON_FUN, Synthetic | Method | inSuper, MethodType(Nil, Nil, argType))
            Closure(meth, _ =>
              atGroupEnd { implicit ctx: Context =>
                arg.changeOwner(ctx.owner, meth)
              }
            )
        }
        ref(defn.dummyApply).appliedToType(argType).appliedTo(argFun)
      case _ =>
        arg
    }

    val mt @ MethodType(_) = tree.fun.tpe.widen
    val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg)
    cpy.Apply(tree)(tree.fun, args1)
  }

  /** If denotation had an ExprType before, it now gets a function type */
  private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) =
    (symd is Param) || (symd is (ParamAccessor, butNot = Method))

  /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */
  private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = {
    val origDenot = originalDenotation(ftree)
    if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType]))
      tree.select(defn.Function0_apply).appliedToNone
    else tree
  }

  override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
    applyIfFunction(tree, tree)

  override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree =
    applyIfFunction(tree, tree)

  override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
    case TypeApply(Select(_, nme.asInstanceOf_), arg :: Nil) =>
      // tree might be of form e.asInstanceOf[x.type] where x becomes a function.
      // See pos/t296.scala
      applyIfFunction(tree, arg)
    case _ => tree
  }

  override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree =
    if (exprBecomesFunction(tree.symbol))
      cpy.ValDef(tree)(tpt = tree.tpt.withType(tree.symbol.info))
    else tree

  def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match {
    case ExprType(rt) if exprBecomesFunction(sym) => defn.FunctionOf(Nil, rt)
    case _ => tp
  }

  override def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm
}