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.scala172
1 files changed, 115 insertions, 57 deletions
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 4a635d259..ea9e7566a 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -6,9 +6,9 @@ import core._
import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import Decorators._
-import util.Attachment
import language.higherKinds
import collection.mutable.ListBuffer
+import config.Printers._
import typer.ErrorReporting.InfoString
import typer.Mode
@@ -22,34 +22,64 @@ object desugar {
/** Info of a variable in a pattern: The named tree and its type */
private type VarInfo = (NameTree, Tree)
-// ----- TypeTrees that refer to other tree's symbols -------------------
+// ----- DerivedTypeTrees -----------------------------------
- /** Attachment key containing TypeTrees whose type is computed
- * from the symbol in this type. These type trees have marker trees
- * TypeRefOfSym or InfoOfSym as their originals.
- */
- val References = new Attachment.Key[List[Tree]]
+ class SetterParamTree extends DerivedTypeTree {
+ def derivedType(sym: Symbol)(implicit ctx: Context) = sym.info.resultType
+ }
- /** Attachment key for TypeTrees marked with TypeRefOfSym or InfoOfSym
- * which contains the symbol of the original tree from which this
- * TypeTree is derived.
- */
- val OriginalSymbol = new Attachment.Key[Symbol]
+ class TypeRefTree extends DerivedTypeTree {
+ def derivedType(sym: Symbol)(implicit ctx: Context) = sym.typeRef
+ }
- /** A type tree that gets its type from some other tree's symbol. Enters the
- * type tree in the References attachment of the `from` tree as a side effect.
- */
- abstract class DerivedTypeTree(from: Tree) extends TypeTree(EmptyTree) {
- val existing = from.attachmentOrElse(References, Nil)
- from.putAttachment(References, this :: existing)
+ class DerivedFromParamTree extends DerivedTypeTree {
- /** The method that computes the type of this tree */
- def derivedType(originalSym: Symbol)(implicit ctx: Context): Type
+ /** 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 appy/unapply methods.
+ */
+ override def ensureCompletions(implicit ctx: Context) =
+ if (!(ctx.owner is Package))
+ 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.owner eq originalOwner =>
+ val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next
+ var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol
+ typr.println(s"rewiring ${tp.symbol} from ${originalOwner.showLocated} to ${local.showLocated}, current owner = ${ctx.owner.showLocated}")
+ if (local.exists) (defctx.owner.thisType select local).dealias
+ else throw new Error(s"no matching symbol for ${sym.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}")
+ case _ =>
+ mapOver(tp)
+ }
+ }
+ relocate(sym.info)
+ }
}
- class SetterParam(vdef: ValDef) extends DerivedTypeTree(vdef) {
- def derivedType(vsym: Symbol)(implicit ctx: Context) = vsym.info.resultType
- }
+ /** A type definition copied from `tdef` with a rhs typetree derived from it */
+ def derivedTypeParam(tdef: TypeDef) =
+ cpy.TypeDef(tdef, tdef.mods, tdef.name,
+ new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef, tdef.tparams) // todo: copy type params
+
+ /** A value definition copied from `vdef` with a tpt typetree derived from it */
+ def derivedTermParam(vdef: ValDef) =
+ cpy.ValDef(vdef, vdef.mods, vdef.name,
+ new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef, vdef.rhs)
// ----- Desugar methods -------------------------------------------------
@@ -67,7 +97,7 @@ object desugar {
// 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 SetterParam(vdef))
+ val setterParam = makeSyntheticParameter(tpt = (new SetterParamTree).watching(vdef))
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
val setter = cpy.DefDef(vdef,
mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil,
@@ -183,6 +213,9 @@ object desugar {
private def toDefParam(tparam: TypeDef) =
cpy.TypeDef(tparam, Modifiers(Param), tparam.name, tparam.rhs, tparam.tparams)
+ private def toDefParam(vparam: ValDef) =
+ cpy.ValDef(vparam, Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs)
+
/** The expansion of a class definition. See inline comments for what is involved */
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
val TypeDef(
@@ -198,31 +231,35 @@ object desugar {
// prefixed by type or val). `tparams` and `vparamss` are the type parameters that
// go in `constr`, the constructor after desugaring.
- val tparams = constr1.tparams map toDefParam
- val vparamss =
+ val constrTparams = constr1.tparams map toDefParam
+ val constrVparamss =
if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty
if (mods is Case)
ctx.error("case class needs to have at least one parameter list", cdef.pos)
ListOfNil
- } else
- constr1.vparamss.nestedMap(vparam => cpy.ValDef(vparam,
- Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs))
-
+ }
+ else constr1.vparamss.nestedMap(toDefParam)
val constr = cpy.DefDef(constr1,
- constr1.mods, constr1.name, tparams, vparamss, constr1.tpt, constr1.rhs)
+ constr1.mods, constr1.name, constrTparams, constrVparamss, constr1.tpt, constr1.rhs)
+
+ 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.
- val tycon = Ident(cdef.name) withPos cdef.pos.startPos
+ classTycon = (new TypeRefTree) withPos cdef.pos.startPos // watching is set at end of method
val tparams = impl.constr.tparams
- if (tparams.isEmpty) tycon else AppliedTypeTree(tycon, tparams map refOfDef)
+ if (tparams.isEmpty) classTycon else AppliedTypeTree(classTycon, tparams map refOfDef)
}
// new C[Ts](paramss)
- lazy val creatorExpr = New(classTypeRef, vparamss nestedMap refOfDef)
+ lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef)
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
// def isDefined = true
@@ -233,23 +270,23 @@ object desugar {
// def copy(p1: T1 = p1, ..., pN: TN = pN)(moreParams) = new C[...](p1, ..., pN)(moreParams)
val caseClassMeths =
if (mods is Case) {
- val caseParams = vparamss.head.toArray
def syntheticProperty(name: TermName, rhs: Tree) =
DefDef(synthetic, name, Nil, Nil, TypeTree(), rhs)
val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true)))
- val productArityMeth = syntheticProperty(nme.productArity, Literal(Constant(caseParams.length)))
+ val productArityMeth = syntheticProperty(nme.productArity, Literal(Constant(arity)))
def selectorName(n: Int) =
- if (caseParams.length == 1) nme.get else nme.selectorName(n)
- val productElemMeths = for (i <- 0 until caseParams.length) yield
+ if (arity == 1) nme.get else nme.selectorName(n)
+ val caseParams = constrVparamss.head.toArray
+ val productElemMeths = for (i <- 0 until arity) yield
syntheticProperty(selectorName(i), Select(This(EmptyTypeName), caseParams(i).name))
val copyMeths =
if (mods is Abstract) Nil
else {
- val copyFirstParams = vparamss.head.map(vparam =>
+ val copyFirstParams = derivedVparamss.head.map(vparam =>
cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, refOfDef(vparam)))
- val copyRestParamss = vparamss.tail.nestedMap(vparam =>
+ val copyRestParamss = derivedVparamss.tail.nestedMap(vparam =>
cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, EmptyTree))
- DefDef(synthetic, nme.copy, tparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) :: Nil
+ DefDef(synthetic, nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) :: Nil
}
copyMeths ::: isDefinedMeth :: productArityMeth :: productElemMeths.toList
}
@@ -258,15 +295,14 @@ object desugar {
def anyRef = ref(defn.AnyRefAlias.typeRef)
def productConstr(n: Int) = {
val tycon = ref(defn.ProductNClass(n).typeRef)
- val targs = vparamss.head map (_.tpt)
+ val targs = constrVparamss.head map (_.tpt)
AppliedTypeTree(tycon, targs)
}
// Case classes get a ProductN parent
var parents1 = parents
- val n = vparamss.head.length
- if ((mods is Case) && 2 <= n && n <= Definitions.MaxTupleArity)
- parents1 = parents1 :+ productConstr(n)
+ if ((mods is Case) && 2 <= arity && arity <= Definitions.MaxTupleArity)
+ parents1 = parents1 :+ productConstr(arity)
// The thicket which is the desugared version of the companion object
// synthetic object C extends parentTpt { defs }
@@ -288,17 +324,18 @@ object desugar {
val companions =
if (mods is Case) {
val parent =
- if (tparams.nonEmpty) anyRef
- else (vparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe))
+ if (constrTparams.nonEmpty) 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(
+ else
+ DefDef(
synthetic | (constr1.mods.flags & DefaultParameterized), nme.apply,
- tparams, vparamss, TypeTree(), creatorExpr) :: Nil
+ derivedTparams, derivedVparamss, TypeTree(), creatorExpr) :: Nil
val unapplyMeth = {
val unapplyParam = makeSyntheticParameter(tpt = classTypeRef)
- val unapplyRHS = if (n == 0) Literal(Constant(true)) else Ident(unapplyParam.name)
- DefDef(synthetic, nme.unapply, tparams, (unapplyParam :: Nil) :: Nil, TypeTree(), unapplyRHS)
+ val unapplyRHS = if (arity == 0) Literal(Constant(true)) else Ident(unapplyParam.name)
+ DefDef(synthetic, nme.unapply, derivedTparams, (unapplyParam :: Nil) :: Nil, TypeTree(), unapplyRHS)
}
companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
}
@@ -315,19 +352,40 @@ object desugar {
ctx.error("implicit classes may not be toplevel", cdef.pos)
if (mods is Case)
ctx.error("implicit classes may not case classes", cdef.pos)
+
+ // implicit wrapper is typechecked in same scope as constructor, so
+ // we can reuse the constructor parameters; no derived params are needed.
DefDef(Modifiers(Synthetic | Implicit), name.toTermName,
- tparams, vparamss, classTypeRef, creatorExpr) :: Nil
+ constrTparams, constrVparamss, classTypeRef, creatorExpr) :: Nil
}
else Nil
- val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt
- val self1 =
+ val self1 = {
+ val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt
if (self.isEmpty) self
else cpy.ValDef(self, self.mods | SelfName, self.name, selfType, self.rhs)
+ }
+
+ val cdef1 = {
+ val originalTparams = constr1.tparams.toIterator
+ val originalVparams = constr1.vparamss.toIterator.flatten
+ val tparamAccessors = derivedTparams map { tdef =>
+ cpy.TypeDef(tdef, originalTparams.next.mods, tdef.name, tdef.rhs, tdef.tparams)
+ }
+ val vparamAccessors = derivedVparamss.flatten map { vdef =>
+ cpy.ValDef(vdef, originalVparams.next.mods, vdef.name, vdef.tpt, vdef.rhs)
+ }
+ cpy.TypeDef(cdef, mods, name,
+ cpy.Template(impl, constr, parents1, self1,
+ tparamAccessors ::: vparamAccessors ::: body ::: caseClassMeths))
+ }
+
+ // install the watch on classTycon
+ classTycon match {
+ case tycon: DerivedTypeTree => tycon.watching(cdef1)
+ case _ =>
+ }
- val cdef1 = cpy.TypeDef(cdef, mods, name,
- cpy.Template(impl, constr, parents1, self1,
- constr1.tparams ::: constr1.vparamss.flatten ::: body ::: caseClassMeths))
flatTree(cdef1 :: companions ::: implicitWrappers)
}