diff options
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/dotc/ast/Desugar.scala | 172 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/untpd.scala | 44 | ||||
-rw-r--r-- | src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 34 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 19 |
6 files changed, 195 insertions, 77 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) } diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 4e55fe868..44f340932 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -6,12 +6,13 @@ import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ import Denotations._, SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ import Decorators._ +import util.Attachment import language.higherKinds import collection.mutable.ListBuffer object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { -// ----- Tree cases that exist in untyped form only ------------------ + // ----- Tree cases that exist in untyped form only ------------------ trait OpTree extends Tree { def op: Name @@ -61,6 +62,47 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this, mods, name.toTypeName, tparams, rhs) } + // ----- TypeTrees that refer to other tree's symbols ------------------- + + /** 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 extends TypeTree(EmptyTree) { + + private var myWatched: Tree = EmptyTree + + /** The watched tree; used only for printing */ + def watched: Tree = myWatched + + /** Install the derived type tree as a dependency on `original` */ + def watching(original: DefTree): this.type = { + myWatched = original + val existing = original.attachmentOrElse(References, Nil) + original.putAttachment(References, this :: existing) + this + } + + /** A hook to ensure that all necessary symbols are completed so that + * OriginalSymbol attachments are propagated to this tree + */ + def ensureCompletions(implicit ctx: Context): Unit = () + + /** The method that computes the type of this tree */ + def derivedType(originalSym: Symbol)(implicit ctx: Context): Type + } + + /** 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]] + + /** 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] + // ------ Creation methods for untyped only ----------------- def Ident(name: Name): Ident = new Ident(name) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 0d84a9e61..1a65b2978 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -241,6 +241,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case SeqLiteral(elems) => "[" ~ toTextGlobal(elems, ",") ~ "]" + case tpt: untpd.DerivedTypeTree => + "<derived typetree watching " ~ toText(tpt.watched) ~ ">" case TypeTree(orig) => if (tree.hasType) toText(tree.typeOpt) else toText(orig) case SingletonTypeTree(ref) => diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index ea396519a..36822cb85 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -84,6 +84,7 @@ trait Checking extends NoChecking { /** Check that (return) type of implicit definition is not empty */ override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match { + case tpt: untpd.DerivedTypeTree => case TypeTree(untpd.EmptyTree) => val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else "" ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos) diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 162e13bbc..7885e85ac 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -72,6 +72,13 @@ trait NamerContextOps { this: Context => } go(this) } + + /** Context where `sym` is defined, assuming we are in a nested context. */ + def defContext(sym: Symbol) = + outersIterator + .dropWhile(_.owner != sym) + .dropWhile(_.owner == sym) + .next } /** This class creates symbols from definitions and imports and gives them @@ -173,10 +180,10 @@ class Namer { typer: Typer => enclosingClassNamed(mods.privateWithin, mods.pos) def record(sym: Symbol): Symbol = { - val refs = tree.attachmentOrElse(desugar.References, Nil) + val refs = tree.attachmentOrElse(References, Nil) if (refs.nonEmpty) { - tree.removeAttachment(desugar.References) - refs foreach (_.pushAttachment(desugar.OriginalSymbol, sym)) + tree.removeAttachment(References) + refs foreach (_.pushAttachment(OriginalSymbol, sym)) } tree.pushAttachment(SymOfTree, sym) sym @@ -449,17 +456,25 @@ class Namer { typer: Typer => if (self.isEmpty) NoType else if (cls is Module) cls.owner.thisType select sourceModule else createSymbol(self) + // pre-set info, so that parent types can refer to type params denot.info = ClassInfo(cls.owner.thisType, cls, Nil, decls, selfInfo) + + // Ensure constructor is completed so that any parameter accessors + // which have type trees deriving from its parameters can be + // completed in turn. Note that parent types access such parameter + // accessors, that's why the constructor needs to be completed before + // the parent types are elaborated. + index(constr) + symbolOfTree(constr).ensureCompleted() + val parentTypes = ensureFirstIsClass(parents map checkedParentType) val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls) typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs") - index(constr) index(rest)(inClassContext(selfInfo)) denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo) // make sure constr parameters are all entered because we refer to them in desugarings: - symbolOfTree(constr).ensureCompleted() } } @@ -509,13 +524,6 @@ class Namer { typer: Typer => if (!mdef.tpt.isEmpty) WildcardType else { - /** Context where `sym` is defined */ - def defContext(sym: Symbol) = - ctx.outersIterator - .dropWhile(_.owner != sym) - .dropWhile(_.owner == sym) - .next - /** An type for this definition that might be inherited from elsewhere: * If this is a setter parameter, the corresponding getter type. * If this is a class member, the conjunction of all result types @@ -561,7 +569,7 @@ class Namer { typer: Typer => if (original.isConstructorName && (sym.owner is ModuleClass)) sym.owner.companionClass.info.decl(nme.CONSTRUCTOR) else - defContext(sym).denotNamed(original) + ctx.defContext(sym).denotNamed(original) def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match { case params :: paramss1 => if (idx < params.length) wildApprox(params(idx)) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index e9a79eeb1..67e5c5902 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -637,12 +637,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { if (tree.original.isEmpty) tree match { - case tree: desugar.DerivedTypeTree => - TypeTree(tree.derivedType(tree.attachment(desugar.OriginalSymbol))) withPos tree.pos - // btw, no need to remove the attachment. The typed - // tree is different from the untyped one, so the - // untyped tree is no longer accessed after all - // accesses with typedTypeTree are done. + case tree: untpd.DerivedTypeTree => + tree.ensureCompletions + try + TypeTree(tree.derivedType(tree.attachment(untpd.OriginalSymbol))) withPos tree.pos + // btw, no need to remove the attachment. The typed + // tree is different from the untyped one, so the + // untyped tree is no longer accessed after all + // accesses with typedTypeTree are done. + catch { + case ex: NoSuchElementException => + println(s"missing OriginalSymbol for ${ctx.owner.ownersIterator.toList}") + throw ex + } case _ => assert(isFullyDefined(pt, ForceDegree.none)) tree.withType(pt) |