aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/ast/Desugar.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty/tools/dotc/ast/Desugar.scala')
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala1089
1 files changed, 0 insertions, 1089 deletions
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
deleted file mode 100644
index 366a0e225..000000000
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ /dev/null
@@ -1,1089 +0,0 @@
-package dotty.tools
-package dotc
-package ast
-
-import core._
-import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
-import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
-import Decorators._
-import language.higherKinds
-import collection.mutable.ListBuffer
-import util.Property
-import reporting.diagnostic.messages._
-
-object desugar {
- import untpd._
-
- /** Tags a .withFilter call generated by desugaring a for expression.
- * Such calls can alternatively be rewritten to use filter.
- */
- val MaybeFilter = new Property.Key[Unit]
-
- /** Info of a variable in a pattern: The named tree and its type */
- private type VarInfo = (NameTree, Tree)
-
- /** Names of methods that are added unconditionally to case classes */
- def isDesugaredCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean =
- name == nme.isDefined ||
- name == nme.copy ||
- name == nme.productArity ||
- name.isSelectorName
-
-// ----- DerivedTypeTrees -----------------------------------
-
- class SetterParamTree extends DerivedTypeTree {
- def derivedType(sym: Symbol)(implicit ctx: Context) = sym.info.resultType
- }
-
- class TypeRefTree extends DerivedTypeTree {
- def derivedType(sym: Symbol)(implicit ctx: Context) = sym.typeRef
- }
-
- class DerivedFromParamTree extends DerivedTypeTree {
-
- /** Make sure that for all enclosing module classes their companion lasses
- * are completed. Reason: We need the constructor of such companion classes to
- * be completed so that OriginalSymbol attachments are pushed to DerivedTypeTrees
- * in apply/unapply methods.
- */
- override def ensureCompletions(implicit ctx: Context) =
- if (!(ctx.owner is Package))
- if (ctx.owner.isClass) {
- ctx.owner.ensureCompleted()
- if (ctx.owner is ModuleClass)
- ctx.owner.linkedClass.ensureCompleted()
- }
- else ensureCompletions(ctx.outer)
-
- /** Return info of original symbol, where all references to siblings of the
- * original symbol (i.e. sibling and original symbol have the same owner)
- * are rewired to same-named parameters or accessors in the scope enclosing
- * the current scope. The current scope is the scope owned by the defined symbol
- * itself, that's why we have to look one scope further out. If the resulting
- * type is an alias type, dealias it. This is necessary because the
- * accessor of a type parameter is a private type alias that cannot be accessed
- * from subclasses.
- */
- def derivedType(sym: Symbol)(implicit ctx: Context) = {
- val relocate = new TypeMap {
- val originalOwner = sym.owner
- def apply(tp: Type) = tp match {
- case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) =>
- val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next
- var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol
- if (local.exists) (defctx.owner.thisType select local).dealias
- else throw new java.lang.Error(
- s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}"
- )
- case _ =>
- mapOver(tp)
- }
- }
- relocate(sym.info)
- }
- }
-
- /** A type definition copied from `tdef` with a rhs typetree derived from it */
- def derivedTypeParam(tdef: TypeDef) =
- cpy.TypeDef(tdef)(
- rhs = new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef)
-
- /** A value definition copied from `vdef` with a tpt typetree derived from it */
- def derivedTermParam(vdef: ValDef) =
- cpy.ValDef(vdef)(
- tpt = new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef)
-
-// ----- Desugar methods -------------------------------------------------
-
- /** var x: Int = expr
- * ==>
- * def x: Int = expr
- * def x_=($1: <TypeTree()>): Unit = ()
- */
- def valDef(vdef: ValDef)(implicit ctx: Context): Tree = {
- val ValDef(name, tpt, rhs) = vdef
- val mods = vdef.mods
- def setterNeeded =
- (mods is Mutable) && ctx.owner.isClass && (!(mods is PrivateLocal) || (ctx.owner is Trait))
- if (setterNeeded) {
- // todo: copy of vdef as getter needed?
- // val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ?
- // right now vdef maps via expandedTree to a thicket which concerns itself.
- // I don't see a problem with that but if there is one we can avoid it by making a copy here.
- val setterParam = makeSyntheticParameter(tpt = (new SetterParamTree).watching(vdef))
- val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
- val setter = cpy.DefDef(vdef)(
- name = name.setterName,
- tparams = Nil,
- vparamss = (setterParam :: Nil) :: Nil,
- tpt = TypeTree(defn.UnitType),
- rhs = setterRhs
- ).withMods((mods | Accessor) &~ CaseAccessor) // rhs gets filled in later, when field is generated and getter has parameters
- Thicket(vdef, setter)
- }
- else vdef
- }
-
- /** Expand context bounds to evidence params. E.g.,
- *
- * def f[T >: L <: H : B](params)
- * ==>
- * def f[T >: L <: H](params)(implicit evidence$0: B[T])
- *
- * Expand default arguments to default getters. E.g,
- *
- * def f[T: B](x: Int = 1)(y: String = x + "m") = ...
- * ==>
- * def f[T](x: Int)(y: String)(implicit evidence$0: B[T]) = ...
- * def f$default$1[T] = 1
- * def f$default$2[T](x: Int) = x + "m"
- */
- def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
- val DefDef(name, tparams, vparamss, tpt, rhs) = meth
- val mods = meth.mods
- val epbuf = new ListBuffer[ValDef]
- val tparams1 = tparams mapConserve {
- case tparam @ TypeDef(_, ContextBounds(tbounds, cxbounds)) =>
- for (cxbound <- cxbounds) {
- val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param
- val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName
- epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit)
- }
- cpy.TypeDef(tparam)(rhs = tbounds)
- case tparam =>
- tparam
- }
-
- val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
-
- /** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
- def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
- case vparams :: vparamss1 =>
- val len = vparams.length
- if (n >= len) vparams :: takeUpTo(vparamss1, n - len) else Nil
- case _ =>
- Nil
- }
-
- def normalizedVparamss = meth1.vparamss map (_ map (vparam =>
- cpy.ValDef(vparam)(rhs = EmptyTree)))
-
- def dropContextBound(tparam: TypeDef) = tparam.rhs match {
- case ContextBounds(tbounds, _) => cpy.TypeDef(tparam)(rhs = tbounds)
- case _ => tparam
- }
-
- def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match {
- case (vparam :: vparams) :: vparamss1 =>
- def defaultGetter: DefDef =
- DefDef(
- name = meth.name.defaultGetterName(n),
- tparams = meth.tparams.map(tparam => dropContextBound(toDefParam(tparam))),
- vparamss = takeUpTo(normalizedVparamss, n),
- tpt = TypeTree(),
- rhs = vparam.rhs
- ).withMods(Modifiers(mods.flags & AccessFlags, mods.privateWithin))
- val rest = defaultGetters(vparams :: vparamss1, n + 1)
- if (vparam.rhs.isEmpty) rest else defaultGetter :: rest
- case Nil :: vparamss1 =>
- defaultGetters(vparamss1, n)
- case nil =>
- Nil
- }
-
- val defGetters = defaultGetters(vparamss, 0)
- if (defGetters.isEmpty) meth1
- else {
- val meth2 = cpy.DefDef(meth1)(vparamss = normalizedVparamss)
- .withMods(meth1.mods | DefaultParameterized)
- Thicket(meth2 :: defGetters)
- }
- }
-
- // Add all evidence parameters in `params` as implicit parameters to `meth` */
- private def addEvidenceParams(meth: DefDef, params: List[ValDef])(implicit ctx: Context): DefDef =
- params match {
- case Nil =>
- meth
- case evidenceParams =>
- val vparamss1 = meth.vparamss.reverse match {
- case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods is Implicit =>
- ((vparams ++ evidenceParams) :: rvparamss).reverse
- case _ =>
- meth.vparamss :+ evidenceParams
- }
- cpy.DefDef(meth)(vparamss = vparamss1)
- }
-
- /** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */
- private def evidenceParams(meth: DefDef)(implicit ctx: Context): List[ValDef] =
- meth.vparamss.reverse match {
- case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit =>
- vparams.dropWhile(!_.name.startsWith(nme.EVIDENCE_PARAM_PREFIX))
- case _ =>
- Nil
- }
-
- /** Fill in empty type bounds with Nothing/Any. Expand private local type parameters as follows:
- *
- * class C[v T]
- * ==>
- * class C { type v C$T; type v T = C$T }
- */
- def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = {
- if (tdef.mods is PrivateLocalParam) {
- val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner))
- .withMods(tdef.mods &~ PrivateLocal | ExpandedName)
- val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam))
- .withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic)
- Thicket(tparam, alias)
- }
- else tdef
- }
-
- @sharable private val synthetic = Modifiers(Synthetic)
-
- private def toDefParam(tparam: TypeDef): TypeDef =
- tparam.withMods(tparam.rawMods & EmptyFlags | Param)
- private def toDefParam(vparam: ValDef): ValDef =
- vparam.withMods(vparam.rawMods & Implicit | Param)
-
- /** The expansion of a class definition. See inline comments for what is involved */
- def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
- val className = checkNotReservedName(cdef).asTypeName
- val impl @ Template(constr0, parents, self, _) = cdef.rhs
- val mods = cdef.mods
- val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags)
-
- val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match {
- case meth: DefDef => (meth, Nil)
- case Thicket((meth: DefDef) :: defaults) => (meth, defaults)
- }
-
- // The original type and value parameters in the constructor already have the flags
- // needed to be type members (i.e. param, and possibly also private and local unless
- // prefixed by type or val). `tparams` and `vparamss` are the type parameters that
- // go in `constr`, the constructor after desugaring.
-
- /** Does `tree' look like a reference to AnyVal? Temporary test before we have inline classes */
- def isAnyVal(tree: Tree): Boolean = tree match {
- case Ident(tpnme.AnyVal) => true
- case Select(qual, tpnme.AnyVal) => isScala(qual)
- case _ => false
- }
- def isScala(tree: Tree): Boolean = tree match {
- case Ident(nme.scala_) => true
- case Select(Ident(nme.ROOTPKG), nme.scala_) => true
- case _ => false
- }
-
- val isCaseClass = mods.is(Case) && !mods.is(Module)
- val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
- // This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
-
- val constrTparams = constr1.tparams map toDefParam
- val constrVparamss =
- if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty
- if (isCaseClass)
- ctx.error(CaseClassMissingParamList(cdef), cdef.namePos)
- ListOfNil
- }
- else constr1.vparamss.nestedMap(toDefParam)
- val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)
-
- // Add constructor type parameters and evidence implicit parameters
- // to auxiliary constructors
- val normalizedBody = impl.body map {
- case ddef: DefDef if ddef.name.isConstructorName =>
- addEvidenceParams(
- cpy.DefDef(ddef)(tparams = constrTparams),
- evidenceParams(constr1).map(toDefParam))
- case stat =>
- stat
- }
-
- val derivedTparams = constrTparams map derivedTypeParam
- val derivedVparamss = constrVparamss nestedMap derivedTermParam
- val arity = constrVparamss.head.length
-
- var classTycon: Tree = EmptyTree
-
- // a reference to the class type, with all parameters given.
- val classTypeRef/*: Tree*/ = {
- // -language:keepUnions difference: classTypeRef needs type annotation, otherwise
- // infers Ident | AppliedTypeTree, which
- // renders the :\ in companions below untypable.
- classTycon = (new TypeRefTree) withPos cdef.pos.startPos // watching is set at end of method
- val tparams = impl.constr.tparams
- if (tparams.isEmpty) classTycon else AppliedTypeTree(classTycon, tparams map refOfDef)
- }
-
- // new C[Ts](paramss)
- lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
-
- // Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
- // def isDefined = true
- // def productArity = N
- // def _1 = this.p1
- // ...
- // def _N = this.pN
- // def copy(p1: T1 = p1: @uncheckedVariance, ...,
- // pN: TN = pN: @uncheckedVariance)(moreParams) =
- // new C[...](p1, ..., pN)(moreParams)
- //
- // Note: copy default parameters need @uncheckedVariance; see
- // neg/t1843-variances.scala for a test case. The test would give
- // two errors without @uncheckedVariance, one of them spurious.
- val caseClassMeths =
- if (isCaseClass) {
- def syntheticProperty(name: TermName, rhs: Tree) =
- DefDef(name, Nil, Nil, TypeTree(), rhs).withMods(synthetic)
- val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true)))
- val caseParams = constrVparamss.head.toArray
- val productElemMeths = for (i <- 0 until arity) yield
- syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name))
- def isRepeated(tree: Tree): Boolean = tree match {
- case PostfixOp(_, nme.raw.STAR) => true
- case ByNameTypeTree(tree1) => isRepeated(tree1)
- case _ => false
- }
- val hasRepeatedParam = constrVparamss.exists(_.exists {
- case ValDef(_, tpt, _) => isRepeated(tpt)
- case _ => false
- })
-
- val copyMeths =
- if (mods.is(Abstract) || hasRepeatedParam) Nil // cannot have default arguments for repeated parameters, hence copy method is not issued
- else {
- def copyDefault(vparam: ValDef) =
- makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam))
- val copyFirstParams = derivedVparamss.head.map(vparam =>
- cpy.ValDef(vparam)(rhs = copyDefault(vparam)))
- val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
- cpy.ValDef(vparam)(rhs = EmptyTree))
- DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr)
- .withMods(synthetic) :: Nil
- }
- copyMeths ::: isDefinedMeth :: productElemMeths.toList
- }
- else Nil
-
- def anyRef = ref(defn.AnyRefAlias.typeRef)
- def productConstr(n: Int) = {
- val tycon = scalaDot((tpnme.Product.toString + n).toTypeName)
- val targs = constrVparamss.head map (_.tpt)
- if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs)
- }
-
- // Case classes and case objects get a ProductN parent
- var parents1 = parents
- if (mods.is(Case) && arity <= Definitions.MaxTupleArity)
- parents1 = parents1 :+ productConstr(arity)
-
- // The thicket which is the desugared version of the companion object
- // synthetic object C extends parentTpt { defs }
- def companionDefs(parentTpt: Tree, defs: List[Tree]) =
- moduleDef(
- ModuleDef(
- className.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
- .withMods(companionMods | Synthetic))
- .withPos(cdef.pos).toList
-
- // The companion object definitions, if a companion is needed, Nil otherwise.
- // companion definitions include:
- // 1. If class is a case class case class C[Ts](p1: T1, ..., pN: TN)(moreParams):
- // def apply[Ts](p1: T1, ..., pN: TN)(moreParams) = new C[Ts](p1, ..., pN)(moreParams) (unless C is abstract)
- // def unapply[Ts]($1: C[Ts]) = $1
- // 2. The default getters of the constructor
- // The parent of the companion object of a non-parameterized case class
- // (T11, ..., T1N) => ... => (TM1, ..., TMN) => C
- // For all other classes, the parent is AnyRef.
- val companions =
- if (isCaseClass) {
- val parent =
- if (constrTparams.nonEmpty ||
- constrVparamss.length > 1 ||
- mods.is(Abstract) ||
- constr.mods.is(Private)) anyRef
- // todo: also use anyRef if constructor has a dependent method type (or rule that out)!
- else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe))
- val applyMeths =
- if (mods is Abstract) Nil
- else
- DefDef(nme.apply, derivedTparams, derivedVparamss, TypeTree(), creatorExpr)
- .withFlags(Synthetic | (constr1.mods.flags & DefaultParameterized)) :: Nil
- val unapplyMeth = {
- val unapplyParam = makeSyntheticParameter(tpt = classTypeRef)
- val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name)
- DefDef(nme.unapply, derivedTparams, (unapplyParam :: Nil) :: Nil, TypeTree(), unapplyRHS)
- .withMods(synthetic)
- }
- companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
- }
- else if (defaultGetters.nonEmpty)
- companionDefs(anyRef, defaultGetters)
- else if (isValueClass)
- companionDefs(anyRef, Nil)
- else Nil
-
-
- // For an implicit class C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, .., pMN: TMN), the method
- // synthetic implicit C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, ..., pMN: TMN): C[Ts] =
- // new C[Ts](p11, ..., p1N) ... (pM1, ..., pMN) =
- val implicitWrappers =
- if (!mods.is(Implicit))
- Nil
- else if (ctx.owner is Package) {
- ctx.error(TopLevelImplicitClass(cdef), cdef.pos)
- Nil
- }
- else if (isCaseClass) {
- ctx.error(ImplicitCaseClass(cdef), cdef.pos)
- Nil
- }
- else
- // implicit wrapper is typechecked in same scope as constructor, so
- // we can reuse the constructor parameters; no derived params are needed.
- DefDef(className.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
- .withMods(companionMods | Synthetic | Implicit)
- .withPos(cdef.pos) :: Nil
-
- val self1 = {
- val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt
- if (self.isEmpty) self
- else cpy.ValDef(self)(tpt = selfType).withMods(self.mods | SelfName)
- }
-
- val cdef1 = {
- val originalTparams = constr1.tparams.toIterator
- val originalVparams = constr1.vparamss.toIterator.flatten
- val tparamAccessors = derivedTparams.map(_.withMods(originalTparams.next.mods))
- val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags
- val vparamAccessors = derivedVparamss.flatten.map(_.withMods(originalVparams.next.mods | caseAccessor))
- cpy.TypeDef(cdef)(
- name = className,
- rhs = cpy.Template(impl)(constr, parents1, self1,
- tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths))
- }
-
- // install the watch on classTycon
- classTycon match {
- case tycon: DerivedTypeTree => tycon.watching(cdef1)
- case _ =>
- }
-
- flatTree(cdef1 :: companions ::: implicitWrappers)
- }
-
- val AccessOrSynthetic = AccessFlags | Synthetic
-
- /** Expand
- *
- * object name extends parents { self => body }
- *
- * to:
- * <module> val name: name$ = New(name$)
- * <module> final class name$ extends parents { self: name.type => body }
- */
- def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = {
- val moduleName = checkNotReservedName(mdef).asTermName
- val tmpl = mdef.impl
- val mods = mdef.mods
- if (mods is Package)
- PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, tmpl).withMods(mods &~ Package) :: Nil)
- else {
- val clsName = moduleName.moduleClassName
- val clsRef = Ident(clsName)
- val modul = ValDef(moduleName, clsRef, New(clsRef, Nil))
- .withMods(mods | ModuleCreationFlags | mods.flags & AccessFlags)
- .withPos(mdef.pos)
- val ValDef(selfName, selfTpt, _) = tmpl.self
- val selfMods = tmpl.self.mods
- if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos)
- val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), tmpl.self.rhs)
- .withMods(selfMods)
- .withPos(tmpl.self.pos orElse tmpl.pos.startPos)
- val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body)
- val cls = TypeDef(clsName, clsTmpl)
- .withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags)
- Thicket(modul, classDef(cls).withPos(mdef.pos))
- }
- }
-
- /** The name of `mdef`, after checking that it does not redefine a Scala core class.
- * If it does redefine, issue an error and return a mangled name instead of the original one.
- */
- def checkNotReservedName(mdef: MemberDef)(implicit ctx: Context): Name = {
- val name = mdef.name
- if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
- def kind = if (name.isTypeName) "class" else "object"
- ctx.error(em"illegal redefinition of standard $kind $name", mdef.pos)
- name.errorName
- }
- else name
- }
-
- /** val p1, ..., pN: T = E
- * ==>
- * makePatDef[[val p1: T1 = E]]; ...; makePatDef[[val pN: TN = E]]
- */
- def patDef(pdef: PatDef)(implicit ctx: Context): Tree = {
- val PatDef(mods, pats, tpt, rhs) = pdef
- val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
- flatTree(pats1 map (makePatDef(pdef, mods, _, rhs)))
- }
-
- /** If `pat` is a variable pattern,
- *
- * val/var/lazy val p = e
- *
- * Otherwise, in case there is exactly one variable x_1 in pattern
- * val/var/lazy val p = e ==> val/var/lazy val x_1 = (e: @unchecked) match (case p => (x_1))
- *
- * in case there are zero or more than one variables in pattern
- * val/var/lazy p = e ==> private synthetic [lazy] val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
- * val/var/def x_1 = t$._1
- * ...
- * val/var/def x_N = t$._N
- * If the original pattern variable carries a type annotation, so does the corresponding
- * ValDef or DefDef.
- */
- def makePatDef(original: Tree, mods: Modifiers, pat: Tree, rhs: Tree)(implicit ctx: Context): Tree = pat match {
- case VarPattern(named, tpt) =>
- derivedValDef(original, named, tpt, rhs, mods)
- case _ =>
- val rhsUnchecked = makeAnnotated(defn.UncheckedAnnot, rhs)
- val vars = getVariables(pat)
- val isMatchingTuple: Tree => Boolean = {
- case Tuple(es) => es.length == vars.length
- case _ => false
- }
- val ids = for ((named, _) <- vars) yield Ident(named.name)
- val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
- val matchExpr =
- if (forallResults(rhs, isMatchingTuple)) rhs
- else Match(rhsUnchecked, caseDef :: Nil)
- vars match {
- case Nil =>
- matchExpr
- case (named, tpt) :: Nil =>
- derivedValDef(original, named, tpt, matchExpr, mods)
- case _ =>
- val tmpName = ctx.freshName().toTermName
- val patMods = mods & (AccessFlags | Lazy) | Synthetic
- val firstDef =
- ValDef(tmpName, TypeTree(), matchExpr)
- .withPos(pat.pos.union(rhs.pos)).withMods(patMods)
- def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n))
- val restDefs =
- for (((named, tpt), n) <- vars.zipWithIndex)
- yield
- if (mods is Lazy) derivedDefDef(original, named, tpt, selector(n), mods &~ Lazy)
- else derivedValDef(original, named, tpt, selector(n), mods)
- flatTree(firstDef :: restDefs)
- }
- }
-
- /** Expand variable identifier x to x @ _ */
- def patternVar(tree: Tree)(implicit ctx: Context) = {
- val Ident(name) = tree
- Bind(name, Ident(nme.WILDCARD)).withPos(tree.pos)
- }
-
- def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
- case tree: ValDef => valDef(tree)
- case tree: TypeDef => if (tree.isClassDef) classDef(tree) else typeDef(tree)
- case tree: DefDef => defDef(tree)
- case tree: ModuleDef => moduleDef(tree)
- case tree: PatDef => patDef(tree)
- }
-
- /** { stats; <empty > }
- * ==>
- * { stats; () }
- */
- def block(tree: Block)(implicit ctx: Context): Block = tree.expr match {
- case EmptyTree =>
- cpy.Block(tree)(tree.stats,
- unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos))
- case _ =>
- tree
- }
-
- /** EmptyTree in lower bound ==> Nothing
- * EmptyTree in upper bounds ==> Any
- */
- def typeBoundsTree(tree: TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = {
- val TypeBoundsTree(lo, hi) = tree
- val lo1 = if (lo.isEmpty) untpd.TypeTree(defn.NothingType) else lo
- val hi1 = if (hi.isEmpty) untpd.TypeTree(defn.AnyType) else hi
- cpy.TypeBoundsTree(tree)(lo1, hi1)
- }
-
- /** Make closure corresponding to function.
- * params => body
- * ==>
- * def $anonfun(params) = body
- * Closure($anonfun)
- *
- * If `inlineable` is true, tag $anonfun with an @inline annotation.
- */
- def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = {
- var mods = synthetic
- if (inlineable) mods |= Inline
- Block(
- DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods),
- Closure(Nil, Ident(nme.ANON_FUN), EmptyTree))
- }
-
- /** If `nparams` == 1, expand partial function
- *
- * { cases }
- * ==>
- * x$1 => (x$1 @unchecked) match { cases }
- *
- * If `nparams` != 1, expand instead to
- *
- * (x$1, ..., x$n) => (x$0, ..., x${n-1} @unchecked) match { cases }
- */
- def makeCaseLambda(cases: List[CaseDef], nparams: Int = 1, unchecked: Boolean = true)(implicit ctx: Context) = {
- val params = (1 to nparams).toList.map(makeSyntheticParameter(_))
- val selector = makeTuple(params.map(p => Ident(p.name)))
-
- if (unchecked)
- Function(params, Match(Annotated(selector, New(ref(defn.UncheckedAnnotType))), cases))
- else
- Function(params, Match(selector, cases))
- }
-
- /** Map n-ary function `(p1, ..., pn) => body` where n != 1 to unary function as follows:
- *
- * x$1 => {
- * def p1 = x$1._1
- * ...
- * def pn = x$1._n
- * body
- * }
- */
- def makeTupledFunction(params: List[ValDef], body: Tree)(implicit ctx: Context): Tree = {
- val param = makeSyntheticParameter()
- def selector(n: Int) = Select(refOfDef(param), nme.selectorName(n))
- val vdefs =
- params.zipWithIndex.map{
- case (param, idx) =>
- DefDef(param.name, Nil, Nil, TypeTree(), selector(idx)).withPos(param.pos)
- }
- Function(param :: Nil, Block(vdefs, body))
- }
-
- /** Add annotation with class `cls` to tree:
- * tree @cls
- */
- def makeAnnotated(cls: Symbol, tree: Tree)(implicit ctx: Context) =
- Annotated(tree, untpd.New(untpd.TypeTree(cls.typeRef), Nil))
-
- private def derivedValDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(implicit ctx: Context) = {
- val vdef = ValDef(named.name.asTermName, tpt, rhs)
- .withMods(mods)
- .withPos(original.pos.withPoint(named.pos.start))
- val mayNeedSetter = valDef(vdef)
- mayNeedSetter
- }
-
- private def derivedDefDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers) =
- DefDef(named.name.asTermName, Nil, Nil, tpt, rhs)
- .withMods(mods)
- .withPos(original.pos.withPoint(named.pos.start))
-
- /** Main desugaring method */
- def apply(tree: Tree)(implicit ctx: Context): Tree = {
-
- /** { label def lname(): Unit = rhs; call }
- */
- def labelDefAndCall(lname: TermName, rhs: Tree, call: Tree) = {
- val ldef = DefDef(lname, Nil, ListOfNil, TypeTree(defn.UnitType), rhs).withFlags(Label)
- Block(ldef, call)
- }
-
- /** Translate infix operation expression left op right
- */
- def makeBinop(left: Tree, op: Name, right: Tree): Tree = {
- def assignToNamedArg(arg: Tree) = arg match {
- case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs)
- case _ => arg
- }
- if (isLeftAssoc(op)) {
- val args: List[Tree] = right match {
- case Parens(arg) => assignToNamedArg(arg) :: Nil
- case Tuple(args) => args mapConserve assignToNamedArg
- case _ => right :: Nil
- }
- Apply(Select(left, op), args)
- } else {
- val x = ctx.freshName().toTermName
- new InfixOpBlock(
- ValDef(x, TypeTree(), left).withMods(synthetic),
- Apply(Select(right, op), Ident(x)))
- }
- }
-
- /** Create tree for for-comprehension `<for (enums) do body>` or
- * `<for (enums) yield body>` where mapName and flatMapName are chosen
- * corresponding to whether this is a for-do or a for-yield.
- * The creation performs the following rewrite rules:
- *
- * 1.
- *
- * for (P <- G) E ==> G.foreach (P => E)
- *
- * Here and in the following (P => E) is interpreted as the function (P => E)
- * if P is a variable pattern and as the partial function { case P => E } otherwise.
- *
- * 2.
- *
- * for (P <- G) yield E ==> G.map (P => E)
- *
- * 3.
- *
- * for (P_1 <- G_1; P_2 <- G_2; ...) ...
- * ==>
- * G_1.flatMap (P_1 => for (P_2 <- G_2; ...) ...)
- *
- * 4.
- *
- * for (P <- G; E; ...) ...
- * =>
- * for (P <- G.filter (P => E); ...) ...
- *
- * 5. For any N:
- *
- * for (P_1 <- G; P_2 = E_2; val P_N = E_N; ...)
- * ==>
- * for (TupleN(P_1, P_2, ... P_N) <-
- * for (x_1 @ P_1 <- G) yield {
- * val x_2 @ P_2 = E_2
- * ...
- * val x_N & P_N = E_N
- * TupleN(x_1, ..., x_N)
- * } ...)
- *
- * If any of the P_i are variable patterns, the corresponding `x_i @ P_i` is not generated
- * and the variable constituting P_i is used instead of x_i
- *
- * @param mapName The name to be used for maps (either map or foreach)
- * @param flatMapName The name to be used for flatMaps (either flatMap or foreach)
- * @param enums The enumerators in the for expression
- * @param body The body of the for expression
- */
- def makeFor(mapName: TermName, flatMapName: TermName, enums: List[Tree], body: Tree): Tree = ctx.traceIndented(i"make for ${ForYield(enums, body)}", show = true) {
-
- /** Make a function value pat => body.
- * If pat is a var pattern id: T then this gives (id: T) => body
- * Otherwise this gives { case pat => body }
- */
- def makeLambda(pat: Tree, body: Tree): Tree = pat match {
- case VarPattern(named, tpt) =>
- Function(derivedValDef(pat, named, tpt, EmptyTree, Modifiers(Param)) :: Nil, body)
- case _ =>
- makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, unchecked = false)
- }
-
- /** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap
- * it in a Bind with a fresh name. Return the transformed pattern, and the identifier
- * that refers to the bound variable for the pattern.
- */
- def makeIdPat(pat: Tree): (Tree, Ident) = pat match {
- case Bind(name, _) => (pat, Ident(name))
- case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id)
- case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id)
- case _ =>
- val name = ctx.freshName().toTermName
- (Bind(name, pat), Ident(name))
- }
-
- /** Add MaybeFilter attachment */
- def orFilter(tree: Tree): tree.type = {
- tree.putAttachment(MaybeFilter, ())
- tree
- }
-
- /** Make a pattern filter:
- * rhs.withFilter { case pat => true case _ => false }
- *
- * On handling irrefutable patterns:
- * The idea is to wait until the pattern matcher sees a call
- *
- * xs withFilter { cases }
- *
- * where cases can be proven to be refutable i.e. cases would be
- * equivalent to { case _ => true }
- *
- * In that case, compile to
- *
- * xs withFilter alwaysTrue
- *
- * where `alwaysTrue` is a predefined function value:
- *
- * val alwaysTrue: Any => Boolean = true
- *
- * In the libraries operations can take advantage of alwaysTrue to shortcircuit the
- * withFilter call.
- *
- * def withFilter(f: Elem => Boolean) =
- * if (f eq alwaysTrue) this // or rather identity filter monadic applied to this
- * else real withFilter
- */
- def makePatFilter(rhs: Tree, pat: Tree): Tree = {
- val cases = List(
- CaseDef(pat, EmptyTree, Literal(Constant(true))),
- CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))))
- Apply(orFilter(Select(rhs, nme.withFilter)), makeCaseLambda(cases))
- }
-
- /** Is pattern `pat` irrefutable when matched against `rhs`?
- * We only can do a simple syntactic check here; a more refined check
- * is done later in the pattern matcher (see discussion in @makePatFilter).
- */
- def isIrrefutable(pat: Tree, rhs: Tree): Boolean = {
- def matchesTuple(pats: List[Tree], rhs: Tree): Boolean = rhs match {
- case Tuple(trees) => (pats corresponds trees)(isIrrefutable)
- case Parens(rhs1) => matchesTuple(pats, rhs1)
- case Block(_, rhs1) => matchesTuple(pats, rhs1)
- case If(_, thenp, elsep) => matchesTuple(pats, thenp) && matchesTuple(pats, elsep)
- case Match(_, cases) => cases forall (matchesTuple(pats, _))
- case CaseDef(_, _, rhs1) => matchesTuple(pats, rhs1)
- case Throw(_) => true
- case _ => false
- }
- pat match {
- case Bind(_, pat1) => isIrrefutable(pat1, rhs)
- case Parens(pat1) => isIrrefutable(pat1, rhs)
- case Tuple(pats) => matchesTuple(pats, rhs)
- case _ => isVarPattern(pat)
- }
- }
-
- def isIrrefutableGenFrom(gen: GenFrom): Boolean =
- gen.isInstanceOf[IrrefutableGenFrom] || isIrrefutable(gen.pat, gen.expr)
-
- /** rhs.name with a pattern filter on rhs unless `pat` is irrefutable when
- * matched against `rhs`.
- */
- def rhsSelect(gen: GenFrom, name: TermName) = {
- val rhs = if (isIrrefutableGenFrom(gen)) gen.expr else makePatFilter(gen.expr, gen.pat)
- Select(rhs, name)
- }
-
- enums match {
- case (gen: GenFrom) :: Nil =>
- Apply(rhsSelect(gen, mapName), makeLambda(gen.pat, body))
- case (gen: GenFrom) :: (rest @ (GenFrom(_, _) :: _)) =>
- val cont = makeFor(mapName, flatMapName, rest, body)
- Apply(rhsSelect(gen, flatMapName), makeLambda(gen.pat, cont))
- case (enum @ GenFrom(pat, rhs)) :: (rest @ GenAlias(_, _) :: _) =>
- val (valeqs, rest1) = rest.span(_.isInstanceOf[GenAlias])
- val pats = valeqs map { case GenAlias(pat, _) => pat }
- val rhss = valeqs map { case GenAlias(_, rhs) => rhs }
- val (defpat0, id0) = makeIdPat(pat)
- val (defpats, ids) = (pats map makeIdPat).unzip
- val pdefs = (valeqs, defpats, rhss).zipped.map(makePatDef(_, Modifiers(), _, _))
- val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, rhs) :: Nil, Block(pdefs, makeTuple(id0 :: ids)))
- val allpats = pat :: pats
- val vfrom1 = new IrrefutableGenFrom(makeTuple(allpats), rhs1)
- makeFor(mapName, flatMapName, vfrom1 :: rest1, body)
- case (gen: GenFrom) :: test :: rest =>
- val filtered = Apply(orFilter(rhsSelect(gen, nme.withFilter)), makeLambda(gen.pat, test))
- val genFrom =
- if (isIrrefutableGenFrom(gen)) new IrrefutableGenFrom(gen.pat, filtered)
- else GenFrom(gen.pat, filtered)
- makeFor(mapName, flatMapName, genFrom :: rest, body)
- case _ =>
- EmptyTree //may happen for erroneous input
- }
- }
-
- // begin desugar
- tree match {
- case SymbolLit(str) =>
- Apply(
- ref(defn.SymbolClass.companionModule.termRef),
- Literal(Constant(str)) :: Nil)
- case InterpolatedString(id, segments) =>
- val strs = segments map {
- case ts: Thicket => ts.trees.head
- case t => t
- }
- val elems = segments flatMap {
- case ts: Thicket => ts.trees.tail
- case t => Nil
- }
- Apply(Select(Apply(Ident(nme.StringContext), strs), id), elems)
- case InfixOp(l, op, r) =>
- if (ctx.mode is Mode.Type)
- if (op == tpnme.raw.AMP) AndTypeTree(l, r) // l & r
- else if (op == tpnme.raw.BAR) OrTypeTree(l, r) // l | r
- else AppliedTypeTree(Ident(op), l :: r :: Nil) // op[l, r]
- else if (ctx.mode is Mode.Pattern)
- Apply(Ident(op), l :: r :: Nil) // op(l, r)
- else // l.op(r), or val x = r; l.op(x), plus handle named args specially
- makeBinop(l, op, r)
- case PostfixOp(t, op) =>
- if ((ctx.mode is Mode.Type) && op == nme.raw.STAR) {
- val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType
- Annotated(
- AppliedTypeTree(ref(seqType), t),
- New(ref(defn.RepeatedAnnotType), Nil :: Nil))
- } else {
- assert(ctx.mode.isExpr || ctx.reporter.hasErrors, ctx.mode)
- Select(t, op)
- }
- case PrefixOp(op, t) =>
- Select(t, nme.UNARY_PREFIX ++ op)
- case Parens(t) =>
- t
- case Tuple(ts) =>
- val arity = ts.length
- def tupleTypeRef = defn.TupleType(arity)
- if (arity > Definitions.MaxTupleArity) {
- ctx.error(TupleTooLong(ts), tree.pos)
- unitLiteral
- } else if (arity == 1) ts.head
- else if (ctx.mode is Mode.Type) AppliedTypeTree(ref(tupleTypeRef), ts)
- else if (arity == 0) unitLiteral
- else Apply(ref(tupleTypeRef.classSymbol.companionModule.valRef), ts)
- case WhileDo(cond, body) =>
- // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() }
- val call = Apply(Ident(nme.WHILE_PREFIX), Nil)
- val rhs = If(cond, Block(body, call), unitLiteral)
- labelDefAndCall(nme.WHILE_PREFIX, rhs, call)
- case DoWhile(body, cond) =>
- // { label def doWhile$(): Unit = { body; if (cond) doWhile$() } ; doWhile$() }
- val call = Apply(Ident(nme.DO_WHILE_PREFIX), Nil)
- val rhs = Block(body, If(cond, call, unitLiteral))
- labelDefAndCall(nme.DO_WHILE_PREFIX, rhs, call)
- case ForDo(enums, body) =>
- makeFor(nme.foreach, nme.foreach, enums, body) orElse tree
- case ForYield(enums, body) =>
- makeFor(nme.map, nme.flatMap, enums, body) orElse tree
- case PatDef(mods, pats, tpt, rhs) =>
- val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
- flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
- case ParsedTry(body, handler, finalizer) =>
- handler match {
- case Match(EmptyTree, cases) => Try(body, cases, finalizer)
- case EmptyTree => Try(body, Nil, finalizer)
- case _ =>
- Try(body,
- List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))),
- finalizer)
- }
-
- }
- }.withPos(tree.pos)
-
- /** Create a class definition with the same info as the refined type given by `parent`
- * and `refinements`.
- *
- * parent { refinements }
- * ==>
- * trait <refinement> extends core { this: self => refinements }
- *
- * Here, `core` is the (possibly parameterized) class part of `parent`.
- * If `parent` is the same as `core`, self is empty. Otherwise `self` is `parent`.
- *
- * Example: Given
- *
- * class C
- * type T1 = C { type T <: A }
- *
- * the refined type
- *
- * T1 { type T <: B }
- *
- * is expanded to
- *
- * trait <refinement> extends C { this: T1 => type T <: A }
- *
- * The result of this method is used for validity checking, is thrown away afterwards.
- * @param parent The type of `parent`
- */
- def refinedTypeToClass(parent: tpd.Tree, refinements: List[Tree])(implicit ctx: Context): TypeDef = {
- def stripToCore(tp: Type): List[Type] = tp match {
- case tp: RefinedType if tp.argInfos.nonEmpty => tp :: Nil // parameterized class type
- case tp: TypeRef if tp.symbol.isClass => tp :: Nil // monomorphic class type
- case tp: TypeProxy => stripToCore(tp.underlying)
- case AndType(tp1, tp2) => stripToCore(tp1) ::: stripToCore(tp2)
- case _ => defn.AnyType :: Nil
- }
- val parentCores = stripToCore(parent.tpe)
- val untpdParent = TypedSplice(parent)
- val (classParents, self) =
- if (parentCores.length == 1 && (parent.tpe eq parentCores.head)) (untpdParent :: Nil, EmptyValDef)
- else (parentCores map TypeTree, ValDef(nme.WILDCARD, untpdParent, EmptyTree))
- val impl = Template(emptyConstructor, classParents, self, refinements)
- TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait)
- }
-
- /** If tree is a variable pattern, return its name and type, otherwise return None.
- */
- private object VarPattern {
- def unapply(tree: Tree)(implicit ctx: Context): Option[VarInfo] = tree match {
- case id: Ident => Some(id, TypeTree())
- case Typed(id: Ident, tpt) => Some((id, tpt))
- case _ => None
- }
- }
-
- /** Returns list of all pattern variables, possibly with their types,
- * without duplicates
- */
- private def getVariables(tree: Tree)(implicit ctx: Context): List[VarInfo] = {
- val buf = new ListBuffer[VarInfo]
- def seenName(name: Name) = buf exists (_._1.name == name)
- def add(named: NameTree, t: Tree): Unit =
- if (!seenName(named.name)) buf += ((named, t))
- def collect(tree: Tree): Unit = tree match {
- case Bind(nme.WILDCARD, tree1) =>
- collect(tree1)
- case tree @ Bind(_, Typed(tree1, tpt)) if !mayBeTypePat(tpt) =>
- add(tree, tpt)
- collect(tree1)
- case tree @ Bind(_, tree1) =>
- add(tree, TypeTree())
- collect(tree1)
- case Typed(id: Ident, t) if isVarPattern(id) && id.name != nme.WILDCARD && !isWildcardStarArg(tree) =>
- add(id, t)
- case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD =>
- add(id, TypeTree())
- case Apply(_, args) =>
- args foreach collect
- case Typed(expr, _) =>
- collect(expr)
- case NamedArg(_, arg) =>
- collect(arg)
- case SeqLiteral(elems, _) =>
- elems foreach collect
- case Alternative(trees) =>
- for (tree <- trees; (vble, _) <- getVariables(tree))
- ctx.error(IllegalVariableInPatternAlternative(), vble.pos)
- case Annotated(arg, _) =>
- collect(arg)
- case InterpolatedString(_, segments) =>
- segments foreach collect
- case InfixOp(left, _, right) =>
- collect(left)
- collect(right)
- case PrefixOp(_, od) =>
- collect(od)
- case Parens(tree) =>
- collect(tree)
- case Tuple(trees) =>
- trees foreach collect
- case _ =>
- }
- collect(tree)
- buf.toList
- }
-
- private class IrrefutableGenFrom(pat: Tree, expr: Tree) extends GenFrom(pat, expr)
-}