alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (NamesDefaults.scala)

This example Scala source code file (NamesDefaults.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

apply, int, list, list, namedapplyinfo, nil, nil, none, option, select, select, some, tree, tree

The Scala NamesDefaults.scala source code

/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc
package typechecker

import symtab.Flags._

import scala.collection.mutable.{ListBuffer, WeakHashMap}
import scala.collection.immutable.Set

/**
 *  @author Lukas Rytz
 *  @version 1.0
 */
trait NamesDefaults { self: Analyzer =>

  import global._
  import definitions._

  val defaultParametersOfMethod = new WeakHashMap[Symbol, Set[Symbol]] {
    override def default(key: Symbol) = Set()
  }

  case class NamedApplyInfo(qual: Option[Tree], targs: List[Tree],
                            vargss: List[List[Tree]], blockTyper: Typer)
  val noApplyInfo = NamedApplyInfo(None, Nil, Nil, null)

  def nameOf(arg: Tree) = arg match {
    case AssignOrNamedArg(Ident(name), rhs) => Some(name)
    case _ => None
  }
  def isNamed(arg: Tree) = nameOf(arg).isDefined

  /** @param pos maps indicies from old to new */ 
  def reorderArgs[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = {
    val res = new Array[T](args.length)
    // (hopefully) faster than zipWithIndex
    (0 /: args) { case (index, arg) => res(pos(index)) = arg; index + 1 }
    res.toList
  }
  
  /** @param pos maps indicies from new to old (!) */
  def reorderArgsInv[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = {
    val argsArray = args.toArray
    val res = new ListBuffer[T]
    for (i <- 0 until argsArray.length)
      res += argsArray(pos(i))
    res.toList
  }

  /** returns `true' if every element is equal to its index */
  def isIdentity(a: Array[Int]) = (0 until a.length).forall(i => a(i) == i)

  /**
   * Transform a function application into a Block, and assigns typer.context
   * .namedApplyBlockInfo to the new block as side-effect. If tree has the form
   *    Apply(fun, args)
   * first the function "fun" (which might be an application itself!) is transformed into a
   * block of the form
   *   {
   *     val qual$1 = qualifier_of_fun
   *     val x$1 = arg_1_of_fun
   *     ...
   *     val x$n = arg_n_of_fun
   *     qual$1.fun[targs](x$1, ...)...(..., x$n)
   *   }
   * then for each argument in args, a value is created and entered into the block. finally
   * the application expression of the block is updated.
   *   {
   *     val qual$1 = ..
   *     ...
   *     val x$n = ...
   *  >  val qual$n+1 = arg(1)
   *  >  ...
   *  >  val qual$n+m = arg(m)
   *  >  qual$1.fun[targs](x$1, ...)...(..., x$n)(x$n+1, ..., x$n+m)
   *   }
   * 
   * @param typer the typer calling this method; this method calls
   *    typer.doTypedApply
   * @param mode the mode to use for calling typer.doTypedApply
   * @param pt the expected type for calling typer.doTypedApply
   * 
   * @param tree: the function application tree
   * @argPos: a function mapping arguments from their current position to the
   *   position specified by the method type. example:
   *    def foo(a: Int, b: String)
   *    foo(b = "1", a = 2)
   *  calls
   *    transformNamedApplication(Apply(foo, List("1", 2), { 0 => 1, 1 => 0 })
   * 
   *  @return the transformed application (a Block) together with the NamedApplyInfo.
   *     if isNamedApplyBlock(tree), returns the existing context.namedApplyBlockInfo
   */
  def transformNamedApplication(typer: Typer, mode: Int, pt: Type)
                               (tree: Tree, argPos: Int => Int): Tree = {
    import typer._
    import typer.infer._
    val context = typer.context
    import context.unit

    /**
     * Transform a function into a block, and passing context.namedApplyBlockInfo to
     * the new block as side-effect.
     *
     * `baseFun' is typed, the resulting block must be typed as well.
     *
     * Fun is transformed in the following way:
     *  - Ident(f)                                    ==>  Block(Nil, Ident(f))
     *  - Select(qual, f) if (qual is stable)         ==>  Block(Nil, Select(qual, f))
     *  - Select(qual, f) otherwise                   ==>  Block(ValDef(qual$1, qual), Select(qual$1, f))
     *  - TypeApply(fun, targs)                       ==>  Block(Nil or qual$1, TypeApply(fun, targs))
     *  - Select(New(TypeTree()), <init>)             ==>  Block(Nil, Select(New(TypeTree()), ))
     *  - Select(New(Select(qual, typeName)), <init>) ==>  Block(Nil, Select(...))     NOTE: qual must be stable in a `new'
     */
    def baseFunBlock(baseFun: Tree): Tree = {
      val isConstr = baseFun.symbol.isConstructor
      val blockTyper = newTyper(context.makeNewScope(tree, context.owner))

      // baseFun1: extract the function from a potential TypeApply
      // funTargs: type arguments on baseFun, used to reconstruct TypeApply in blockWith(Out)Qualifier
      // defaultTargs: type arguments to be used for calling defaultGetters. If the type arguments are given
      //   in the source code, re-use them for default getter. Otherwise infer the default getter's t-args.
      val (baseFun1, funTargs, defaultTargs) = baseFun match {
        case TypeApply(fun, targs) =>
          val targsInSource =
            if (targs.forall(a => context.undetparams contains a.symbol)) Nil
            else targs
          (fun, targs, targsInSource)

        case Select(New(tpt @ TypeTree()), _) if isConstr =>
          val targsInSource = tpt.tpe match {
            case TypeRef(pre, sym, args)
            if (!args.forall(a => context.undetparams contains a.typeSymbol)) =>
              args.map(TypeTree(_))
            case _ =>
              Nil
          }
          (baseFun, Nil, targsInSource)

        case Select(TypeApply(New(TypeTree()), targs), _) if isConstr =>
          val targsInSource =
            if (targs.forall(a => context.undetparams contains a.symbol)) Nil
            else targs
          (baseFun, Nil, targsInSource)

        case _ => (baseFun, Nil, Nil)
      }

      // never used for constructor calls, they always have a stable qualifier
      def blockWithQualifier(qual: Tree, selected: Name) = {
        val sym = blockTyper.context.owner.newValue(qual.pos, unit.freshTermName("qual$"))
                            .setInfo(qual.tpe)
        blockTyper.context.scope.enter(sym)
        val vd = atPos(sym.pos)(ValDef(sym, qual).setType(NoType))

        var baseFunTransformed = atPos(baseFun.pos.makeTransparent) {
          // don't use treeCopy: it would assign opaque position.
          val f = Select(gen.mkAttributedRef(sym), selected)
                   .setType(baseFun1.tpe).setSymbol(baseFun1.symbol)
          if (funTargs.isEmpty) f
          else TypeApply(f, funTargs).setType(baseFun.tpe)
        }

        val b = Block(List(vd), baseFunTransformed)
                  .setType(baseFunTransformed.tpe).setPos(baseFun.pos)

        val defaultQual = Some(atPos(qual.pos.focus)(gen.mkAttributedRef(sym)))
        context.namedApplyBlockInfo =
          Some((b, NamedApplyInfo(defaultQual, defaultTargs, Nil, blockTyper)))
        b
      }

      def blockWithoutQualifier(defaultQual: Option[Tree]) = {
        val b = atPos(baseFun.pos)(Block(Nil, baseFun).setType(baseFun.tpe))
        context.namedApplyBlockInfo =
          Some((b, NamedApplyInfo(defaultQual, defaultTargs, Nil, blockTyper)))
        b
      }

      def moduleQual(pos: Position, classType: Type) = {
        // prefix does 'normalize', which fixes #3384
        val pre = classType.prefix
        if (pre == NoType) {
          None
        } else {
          val module = companionModuleOf(baseFun.symbol.owner, context)
          if (module == NoSymbol) None
          else Some(atPos(pos.focus)(gen.mkAttributedRef(pre, module)))
        }
      }

      baseFun1 match {
        // constructor calls

        case Select(New(tp @ TypeTree()), _) if isConstr =>
          // 'moduleQual' fixes #3338. Same qualifier for selecting the companion object as for the class.
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
        case Select(TypeApply(New(tp @ TypeTree()), _), _) if isConstr =>
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))

        case Select(New(tp @ Ident(_)), _) if isConstr =>
          // 'moduleQual' fixes #3344
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
        case Select(TypeApply(New(tp @ Ident(_)), _), _) if isConstr =>
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))

        case Select(New(tp @ Select(qual, _)), _) if isConstr =>
          // in `new q.C()', q is always stable
          assert(treeInfo.isPureExpr(qual), qual)
          // 'moduleQual' fixes #2057
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))
        case Select(TypeApply(New(tp @ Select(qual, _)), _), _) if isConstr =>
          assert(treeInfo.isPureExpr(qual), qual)
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))

        // super constructor calls
        case Select(sp @ Super(_, _), _) if isConstr =>
          // 'moduleQual' fixes #3207. selection of the companion module of the
          // superclass needs to have the same prefix as the the superclass.
          blockWithoutQualifier(moduleQual(baseFun.pos, sp.symbol.tpe.parents.head))

        // self constructor calls (in secondary constructors)
        case Select(tp, name) if isConstr =>
          assert(treeInfo.isPureExpr(tp), tp)
          blockWithoutQualifier(moduleQual(tp.pos, tp.tpe))

        // other method calls

        case Ident(_) =>
          blockWithoutQualifier(None)

        case Select(qual, name) =>
          if (treeInfo.isPureExpr(qual))
            blockWithoutQualifier(Some(qual.duplicate))
          else
            blockWithQualifier(qual, name)
      }
    }

    /**
     * For each argument (arg: T), create a local value
     *  x$n: T = arg
     *
     * assumes "args" are typed. owner of the definitions in the block is the owner of
     * the block (see typedBlock), but the symbols have to be entered into the block's scope.
     *
     * For by-name parameters, create a value
     *  x$n: () => T = () => arg
     */
    def argValDefs(args: List[Tree], paramTypes: List[Type], blockTyper: Typer): List[ValDef] = {
      val context = blockTyper.context
      val symPs = (args, paramTypes).zipped map ((arg, tpe) => {
        val byName = isByNameParamType(tpe)
        val (argTpe, repeated) =
          if (isScalaRepeatedParamType(tpe)) arg match {
            case Typed(expr, Ident(tpnme.WILDCARD_STAR)) =>
              (expr.tpe, true)
            case _ =>
              (seqType(arg.tpe), true)
          } else (arg.tpe, false)
        val s = context.owner.newValue(arg.pos, unit.freshTermName("x$"))
        val valType = if (byName) functionType(List(), argTpe)
                      else if (repeated) argTpe
                      else argTpe
        s.setInfo(valType)
        (context.scope.enter(s), byName, repeated)
      })
      (symPs, args).zipped map {
        case ((sym, byName, repeated), arg) =>
          // resetAttrs required for #2290. given a block { val x = 1; x }, when wrapping into a function
          // () => { val x = 1; x }, the owner of symbol x must change (to the apply method of the function).
          val body = if (byName) blockTyper.typed(Function(List(), resetLocalAttrs(arg)))
                     else if (repeated) arg match {
                       case Typed(expr, Ident(tpnme.WILDCARD_STAR)) =>
                         expr
                       case _ =>
                         val factory = Select(gen.mkAttributedRef(SeqModule), nme.apply)
                         blockTyper.typed(Apply(factory, List(resetLocalAttrs(arg))))
                     } else arg
          atPos(body.pos)(ValDef(sym, body).setType(NoType))
      }
    }

    // begin transform
    if (isNamedApplyBlock(tree)) {
      context.namedApplyBlockInfo.get._1
    } else tree match {
      // `fun' is typed. `namelessArgs' might be typed or not, if they are types are kept.
      case Apply(fun, namelessArgs) =>
        val transformedFun = transformNamedApplication(typer, mode, pt)(fun, x => x)
        if (transformedFun.isErroneous) setError(tree)
        else {
          assert(isNamedApplyBlock(transformedFun), transformedFun)
          val NamedApplyInfo(qual, targs, vargss, blockTyper) =
            context.namedApplyBlockInfo.get._2
          val existingBlock @ Block(stats, funOnly) = transformedFun

          // type the application without names; put the arguments in definition-site order
          val typedApp = doTypedApply(tree, funOnly, reorderArgs(namelessArgs, argPos), mode, pt)

          if (typedApp.tpe.isError) setError(tree)
          else typedApp match {
            // Extract the typed arguments, restore the call-site evaluation order (using
            // ValDef's in the block), change the arguments to these local values.
            case Apply(expr, typedArgs) =>
              // typedArgs: definition-site order
              val formals = formalTypes(expr.tpe.paramTypes, typedArgs.length, false, false)
              // valDefs: call-site order
              val valDefs = argValDefs(reorderArgsInv(typedArgs, argPos),
                                       reorderArgsInv(formals, argPos),
                                       blockTyper)
              // refArgs: definition-site order again
              val refArgs = (reorderArgs(valDefs, argPos), formals).zipped map ((vDef, tpe) => {
                val ref = gen.mkAttributedRef(vDef.symbol)
                atPos(vDef.pos.focus) {
                  // for by-name parameters, the local value is a nullary function returning the argument
                  if (isByNameParamType(tpe)) Apply(ref, List())
                  else if (isScalaRepeatedParamType(tpe)) Typed(ref, Ident(tpnme.WILDCARD_STAR))
                  else ref
                }
              })
              // cannot call blockTyper.typedBlock here, because the method expr might be partially applied only
              val res = blockTyper.doTypedApply(tree, expr, refArgs, mode, pt)
              res.setPos(res.pos.makeTransparent)
              val block = Block(stats ::: valDefs, res).setType(res.tpe).setPos(tree.pos)
              context.namedApplyBlockInfo =
                Some((block, NamedApplyInfo(qual, targs, vargss ::: List(refArgs), blockTyper)))
              block
          }
        }

      case baseFun => // also treats "case TypeApply(fun, targs)" and "case Select(New(..), <init>)"
        baseFunBlock(baseFun)

    }
  }

  def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOf _): (List[Symbol], Boolean) = {
    val namedArgs = args.dropWhile(arg => {
      val n = argName(arg)
      n.isEmpty || params.forall(p => p.name != n.get)
    })
    val namedParams = params.drop(args.length - namedArgs.length)
    // missing: keep those with a name which doesn't exist in namedArgs
    val missingParams = namedParams.filter(p => namedArgs.forall(arg => {
      val n = argName(arg)
      n.isEmpty || n.get != p.name
    }))
    val allPositional = missingParams.length == namedParams.length
    (missingParams, allPositional)
  }

  /**
   * Extend the argument list `givenArgs' with default arguments. Defaults are added
   * as named arguments calling the corresponding default getter.
   *
   * Example: given
   *   def foo(x: Int = 2, y: String = "def")
   *   foo(y = "lt")
   * the argument list (y = "lt") is transformed to (y = "lt", x = foo$default$1())
   */
  def addDefaults(givenArgs: List[Tree], qual: Option[Tree], targs: List[Tree],
                  previousArgss: List[List[Tree]], params: List[Symbol],
                  pos: util.Position, context: Context): (List[Tree], List[Symbol]) = {
    if (givenArgs.length < params.length) {
      val (missing, positional) = missingParams(givenArgs, params)
      if (missing forall (_.hasDefaultFlag)) {
        val defaultArgs = missing flatMap (p => {
          val defGetter = defaultGetter(p, context)
          if (defGetter == NoSymbol) None // prevent crash in erroneous trees, #3649
          else {
            var default1 = qual match {
              case Some(q) => gen.mkAttributedSelect(q.duplicate, defGetter)
              case None    => gen.mkAttributedRef(defGetter)

            }
            default1 = if (targs.isEmpty) default1
                       else TypeApply(default1, targs.map(_.duplicate))
            val default2 = (default1 /: previousArgss)((tree, args) =>
              Apply(tree, args.map(_.duplicate)))
            Some(atPos(pos) {
              if (positional) default2
              else AssignOrNamedArg(Ident(p.name), default2)
            })
          }
        })
        (givenArgs ::: defaultArgs, Nil)
      } else (givenArgs, missing filterNot (_.hasDefaultFlag))
    } else (givenArgs, Nil)
  }

  /**
   * For a parameter with default argument, find the method symbol of
   * the default getter.
   */
  def defaultGetter(param: Symbol, context: Context): Symbol = {
    val i = param.owner.paramss.flatten.indexWhere(p => p.name == param.name) + 1
    if (i > 0) {
      val defGetterName = nme.defaultGetterName(param.owner.name, i)
      if (param.owner.isConstructor) {
        val mod = companionModuleOf(param.owner.owner, context)
        mod.info.member(defGetterName)
      }
      else {
        // isClass also works for methods in objects, owner is the ModuleClassSymbol
        if (param.owner.owner.isClass) {
          // .toInterface: otherwise we get the method symbol of the impl class
          param.owner.owner.toInterface.info.member(defGetterName)
        } else {
          // the owner of the method is another method. find the default
          // getter in the context.
          context.lookup(defGetterName, param.owner.owner)
        }
      }
    } else NoSymbol
  }

  /**
   * Removes name assignments from args. Additionally, returns an array mapping
   * argument indicies from call-site-order to definition-site-order.
   *
   * Verifies that names are not specified twice, positional args don't appear
   * after named ones.
   */
  def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = {
    import typer.infer.errorTree

    // maps indicies from (order written by user) to (order of definition)
    val argPos = (new Array[Int](args.length)) map (x => -1)
    var positionalAllowed = true
    val namelessArgs = for ((arg, index) <- (args.zipWithIndex)) yield arg match {
      case a @ AssignOrNamedArg(Ident(name), rhs) =>
        val (pos, newName) = paramPos(params, name)
        newName.foreach(n => {
          typer.context.unit.deprecationWarning(arg.pos, "the parameter name "+ name +" has been deprecated. Use "+ n +" instead.")
        })
        if (pos == -1) {
          if (positionalAllowed) {
            argPos(index) = index
            // prevent isNamed from being true when calling doTypedApply recursively,
            // treat the arg as an assignment of type Unit
            Assign(a.lhs, rhs).setPos(arg.pos)
          } else {
            errorTree(arg, "unknown parameter name: "+ name)
          }
        } else if (argPos contains pos) {
          errorTree(arg, "parameter specified twice: "+ name)
        } else {
          // for named arguments, check whether the assignment expression would
          // typecheck. if it does, report an ambiguous error.
          val param = params(pos)
          val paramtpe = params(pos).tpe.cloneInfo(param)
          // replace type parameters by wildcard. in the below example we need to
          // typecheck (x = 1) with wildcard (not T) so that it succeeds.
          //   def f[T](x: T) = x
          //   var x = 0
          //   f(x = 1)   <<  "x = 1" typechecks with expected type WildcardType
          val udp = typer.context.extractUndetparams()
          val subst = new SubstTypeMap(udp, udp map (_ => WildcardType)) {
            override def apply(tp: Type): Type = tp match {
              case TypeRef(_, ByNameParamClass, List(arg))  => super.apply(arg)
              case _ => super.apply(tp)
            }
          }
          val reportAmbiguousErrors = typer.context.reportAmbiguousErrors
          typer.context.reportAmbiguousErrors = false

          var variableNameClash = false
          val typedAssign = try {
            typer.silent(_.typed(arg, subst(paramtpe)))
          } catch {
            // `silent` only catches and returns TypeErrors which are not
            // CyclicReferences.  Fix for #3685
            case cr @ CyclicReference(sym, info) if sym.name == param.name =>
              if (sym.isVariable || sym.isGetter && sym.accessed.isVariable) {
                // named arg not allowed
                variableNameClash = true
                typer.context.error(sym.pos, 
                  "%s definition needs %s because '%s' is used as a named argument in its body.".format(
                    "variable",   // "method"
                    "type",       // "result type"
                    sym.name
                  )
                )
                typer.infer.setError(arg)
              }
              else cr
          }

          def applyNamedArg = {
            // if the named argument is on the original parameter
            // position, positional after named is allowed.
            if (index != pos)
              positionalAllowed = false
            argPos(index) = pos
            rhs
          }

          val res = typedAssign match {
            case _: TypeError => applyNamedArg

            case t: Tree =>
              if (t.isErroneous && !variableNameClash) {
                applyNamedArg
              } else if (t.isErroneous) {
                t // name clash with variable. error was already reported above.
              } else {
                // This throws an exception which is caught in `tryTypedApply` (as it
                // uses `silent`) - unfortunately, tryTypedApply recovers from the
                // exception if you use errorTree(arg, ...) and conforms is allowed as
                // a view (see tryImplicit in Implicits) because it tries to produce a
                // new qualifier (if the old one was P, the new one will be
                // conforms.apply(P)), and if that works, it pretends nothing happened.
                //
                // To make sure tryTypedApply fails, we would like to pass EmptyTree
                // instead of arg, but can't do that because eventually setType(ErrorType)
                // is called, and EmptyTree can only be typed NoType.  Thus we need to
                // disable conforms as a view...
                errorTree(arg, "reference to "+ name +" is ambiguous; it is both, a parameter\n"+
                               "name of the method and the name of a variable currently in scope.")
              }
          }

          typer.context.reportAmbiguousErrors = reportAmbiguousErrors
          //@M note that we don't get here when an ambiguity was detected (during the computation of res),
          // as errorTree throws an exception
          typer.context.undetparams = udp
          res
        }
      case _ =>
        argPos(index) = index
        if (positionalAllowed) arg
        else errorTree(arg, "positional after named argument.")
    }
    (namelessArgs, argPos)
  }

  /**
   * Returns
   *  - the position of the parameter named `name`
   *  - optionally, if `name` is @deprecatedName, the new name
   */
  def paramPos(params: List[Symbol], name: Name): (Int, Option[Name]) = {
    var i = 0
    var rest = params
    while (!rest.isEmpty) {
      val p = rest.head
      if (!p.isSynthetic) {
        if (p.name == name) return (i, None)
        if (deprecatedName(p) == Some(name)) return (i, Some(p.name))
      }
      i += 1
      rest = rest.tail
    }
    (-1, None)
  }

  def deprecatedName(sym: Symbol): Option[Name] =
    sym.getAnnotation(DeprecatedNameAttr).map(ann => (ann.args(0): @unchecked) match {
      case Apply(fun, Literal(str) :: Nil) if (fun.symbol == Symbol_apply) =>
        newTermName(str.stringValue)
    })
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala NamesDefaults.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.