aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer/Namer.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/typer/Namer.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer/Namer.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Namer.scala1061
1 files changed, 1061 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala
new file mode 100644
index 000000000..148cf1da7
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala
@@ -0,0 +1,1061 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast._
+import Trees._, Constants._, StdNames._, Scopes._, Denotations._, Comments._
+import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._
+import ast.desugar, ast.desugar._
+import ProtoTypes._
+import util.Positions._
+import util.{Property, SourcePosition, DotClass}
+import collection.mutable
+import annotation.tailrec
+import ErrorReporting._
+import tpd.ListOfTreeDecorator
+import config.Config
+import config.Printers.{typr, completions, noPrinter}
+import Annotations._
+import Inferencing._
+import transform.ValueClasses._
+import TypeApplications._
+import language.implicitConversions
+import reporting.diagnostic.messages._
+
+trait NamerContextOps { this: Context =>
+
+ /** Enter symbol into current class, if current class is owner of current context,
+ * or into current scope, if not. Should always be called instead of scope.enter
+ * in order to make sure that updates to class members are reflected in
+ * finger prints.
+ */
+ def enter(sym: Symbol): Symbol = {
+ ctx.owner match {
+ case cls: ClassSymbol => cls.enter(sym)
+ case _ => this.scope.openForMutations.enter(sym)
+ }
+ sym
+ }
+
+ /** The denotation with the given name in current context */
+ def denotNamed(name: Name): Denotation =
+ if (owner.isClass)
+ if (outer.owner == owner) { // inner class scope; check whether we are referring to self
+ if (scope.size == 1) {
+ val elem = scope.lastEntry
+ if (elem.name == name) return elem.sym.denot // return self
+ }
+ assert(scope.size <= 1, scope)
+ owner.thisType.member(name)
+ }
+ else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
+ owner.findMember(name, owner.thisType, EmptyFlags)
+ else
+ scope.denotsNamed(name).toDenot(NoPrefix)
+
+ /** Either the current scope, or, if the current context owner is a class,
+ * the declarations of the current class.
+ */
+ def effectiveScope: Scope =
+ if (owner != null && owner.isClass) owner.asClass.unforcedDecls
+ else scope
+
+ /** The symbol (stored in some typer's symTree) of an enclosing context definition */
+ def symOfContextTree(tree: untpd.Tree) = {
+ def go(ctx: Context): Symbol = {
+ ctx.typeAssigner match {
+ case typer: Typer =>
+ tree.getAttachment(typer.SymOfTree) match {
+ case Some(sym) => sym
+ case None =>
+ var cx = ctx.outer
+ while (cx.typeAssigner eq typer) cx = cx.outer
+ go(cx)
+ }
+ case _ => NoSymbol
+ }
+ }
+ 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
+
+ /** The given type, unless `sym` is a constructor, in which case the
+ * type of the constructed instance is returned
+ */
+ def effectiveResultType(sym: Symbol, typeParams: List[Symbol], given: Type) =
+ if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams map (_.typeRef))
+ else given
+
+ /** if isConstructor, make sure it has one non-implicit parameter list */
+ def normalizeIfConstructor(paramSymss: List[List[Symbol]], isConstructor: Boolean) =
+ if (isConstructor &&
+ (paramSymss.isEmpty || paramSymss.head.nonEmpty && (paramSymss.head.head is Implicit)))
+ Nil :: paramSymss
+ else
+ paramSymss
+
+ /** The method type corresponding to given parameters and result type */
+ def methodType(typeParams: List[Symbol], valueParamss: List[List[Symbol]], resultType: Type, isJava: Boolean = false)(implicit ctx: Context): Type = {
+ val monotpe =
+ (valueParamss :\ resultType) { (params, resultType) =>
+ val make =
+ if (params.nonEmpty && (params.head is Implicit)) ImplicitMethodType
+ else if (isJava) JavaMethodType
+ else MethodType
+ if (isJava)
+ for (param <- params)
+ if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType
+ make.fromSymbols(params, resultType)
+ }
+ if (typeParams.nonEmpty) monotpe.LambdaAbstract(typeParams)
+ else if (valueParamss.isEmpty) ExprType(monotpe)
+ else monotpe
+ }
+
+ /** Find moduleClass/sourceModule in effective scope */
+ private def findModuleBuddy(name: Name)(implicit ctx: Context) = {
+ val scope = effectiveScope
+ val it = scope.lookupAll(name).filter(_ is Module)
+ assert(it.hasNext, s"no companion $name in $scope")
+ it.next
+ }
+
+ /** Add moduleClass or sourceModule functionality to completer
+ * for a module or module class
+ */
+ def adjustModuleCompleter(completer: LazyType, name: Name) =
+ if (name.isTermName)
+ completer withModuleClass (_ => findModuleBuddy(name.moduleClassName))
+ else
+ completer withSourceModule (_ => findModuleBuddy(name.sourceModuleName))
+}
+
+/** This class creates symbols from definitions and imports and gives them
+ * lazy types.
+ *
+ * Timeline:
+ *
+ * During enter, trees are expanded as necessary, populating the expandedTree map.
+ * Symbols are created, and the symOfTree map is set up.
+ *
+ * Symbol completion causes some trees to be already typechecked and typedTree
+ * entries are created to associate the typed trees with the untyped expanded originals.
+ *
+ * During typer, original trees are first expanded using expandedTree. For each
+ * expanded member definition or import we extract and remove the corresponding symbol
+ * from the symOfTree map and complete it. We then consult the typedTree map to see
+ * whether a typed tree exists already. If yes, the typed tree is returned as result.
+ * Otherwise, we proceed with regular type checking.
+ *
+ * The scheme is designed to allow sharing of nodes, as long as each duplicate appears
+ * in a different method.
+ */
+class Namer { typer: Typer =>
+
+ import untpd._
+
+ val TypedAhead = new Property.Key[tpd.Tree]
+ val ExpandedTree = new Property.Key[Tree]
+ val SymOfTree = new Property.Key[Symbol]
+
+ /** A partial map from unexpanded member and pattern defs and to their expansions.
+ * Populated during enterSyms, emptied during typer.
+ */
+ //lazy val expandedTree = new mutable.AnyRefMap[DefTree, Tree]
+ /*{
+ override def default(tree: DefTree) = tree // can't have defaults on AnyRefMaps :-(
+ }*/
+
+ /** A map from expanded MemberDef, PatDef or Import trees to their symbols.
+ * Populated during enterSyms, emptied at the point a typed tree
+ * with the same symbol is created (this can be when the symbol is completed
+ * or at the latest when the tree is typechecked.
+ */
+ //lazy val symOfTree = new mutable.AnyRefMap[Tree, Symbol]
+
+ /** A map from expanded trees to their typed versions.
+ * Populated when trees are typechecked during completion (using method typedAhead).
+ */
+ // lazy val typedTree = new mutable.AnyRefMap[Tree, tpd.Tree]
+
+ /** A map from method symbols to nested typers.
+ * Populated when methods are completed. Emptied when they are typechecked.
+ * The nested typer contains new versions of the four maps above including this
+ * one, so that trees that are shared between different DefDefs can be independently
+ * used as indices. It also contains a scope that contains nested parameters.
+ */
+ lazy val nestedTyper = new mutable.AnyRefMap[Symbol, Typer]
+
+ /** The scope of the typer.
+ * For nested typers this is a place parameters are entered during completion
+ * and where they survive until typechecking. A context with this typer also
+ * has this scope.
+ */
+ val scope = newScope
+
+ /** The symbol of the given expanded tree. */
+ def symbolOfTree(tree: Tree)(implicit ctx: Context): Symbol = {
+ val xtree = expanded(tree)
+ xtree.getAttachment(TypedAhead) match {
+ case Some(ttree) => ttree.symbol
+ case none => xtree.attachment(SymOfTree)
+ }
+ }
+
+ /** The enclosing class with given name; error if none exists */
+ def enclosingClassNamed(name: TypeName, pos: Position)(implicit ctx: Context): Symbol = {
+ if (name.isEmpty) NoSymbol
+ else {
+ val cls = ctx.owner.enclosingClassNamed(name)
+ if (!cls.exists) ctx.error(s"no enclosing class or object is named $name", pos)
+ cls
+ }
+ }
+
+ /** Record `sym` as the symbol defined by `tree` */
+ def recordSym(sym: Symbol, tree: Tree)(implicit ctx: Context): Symbol = {
+ val refs = tree.attachmentOrElse(References, Nil)
+ if (refs.nonEmpty) {
+ tree.removeAttachment(References)
+ refs foreach (_.pushAttachment(OriginalSymbol, sym))
+ }
+ tree.pushAttachment(SymOfTree, sym)
+ sym
+ }
+
+ /** If this tree is a member def or an import, create a symbol of it
+ * and store in symOfTree map.
+ */
+ def createSymbol(tree: Tree)(implicit ctx: Context): Symbol = {
+
+ def privateWithinClass(mods: Modifiers) =
+ enclosingClassNamed(mods.privateWithin, mods.pos)
+
+ def checkFlags(flags: FlagSet) =
+ if (flags.isEmpty) flags
+ else {
+ val (ok, adapted, kind) = tree match {
+ case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type")
+ case _ => (flags.isTermFlags, flags.toTermFlags, "value")
+ }
+ if (!ok)
+ ctx.error(i"modifier(s) `$flags' incompatible with $kind definition", tree.pos)
+ adapted
+ }
+
+ /** Add moduleClass/sourceModule to completer if it is for a module val or class */
+ def adjustIfModule(completer: LazyType, tree: MemberDef) =
+ if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name.encode)
+ else completer
+
+ typr.println(i"creating symbol for $tree in ${ctx.mode}")
+
+ def checkNoConflict(name: Name): Name = {
+ def errorName(msg: => String) = {
+ ctx.error(msg, tree.pos)
+ name.freshened
+ }
+ def preExisting = ctx.effectiveScope.lookup(name)
+ if (ctx.owner is PackageClass)
+ if (preExisting.isDefinedInCurrentRun)
+ errorName(s"${preExisting.showLocated} has already been compiled\nonce during this run")
+ else name
+ else
+ if ((!ctx.owner.isClass || name.isTypeName) && preExisting.exists)
+ errorName(i"$name is already defined as $preExisting")
+ else name
+ }
+
+ val inSuperCall = if (ctx.mode is Mode.InSuperCall) InSuperCall else EmptyFlags
+
+ tree match {
+ case tree: TypeDef if tree.isClassDef =>
+ val name = checkNoConflict(tree.name.encode).asTypeName
+ val flags = checkFlags(tree.mods.flags &~ Implicit)
+ val cls = recordSym(ctx.newClassSymbol(
+ ctx.owner, name, flags | inSuperCall,
+ cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree),
+ privateWithinClass(tree.mods), tree.namePos, ctx.source.file), tree)
+ cls.completer.asInstanceOf[ClassCompleter].init()
+ cls
+ case tree: MemberDef =>
+ val name = checkNoConflict(tree.name.encode)
+ val flags = checkFlags(tree.mods.flags)
+ val isDeferred = lacksDefinition(tree)
+ val deferred = if (isDeferred) Deferred else EmptyFlags
+ val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags
+ val inSuperCall1 = if (tree.mods is ParamOrAccessor) EmptyFlags else inSuperCall
+ // suppress inSuperCall for constructor parameters
+ val higherKinded = tree match {
+ case TypeDef(_, PolyTypeTree(_, _)) if isDeferred => HigherKinded
+ case _ => EmptyFlags
+ }
+
+ // to complete a constructor, move one context further out -- this
+ // is the context enclosing the class. Note that the context in which a
+ // constructor is recorded and the context in which it is completed are
+ // different: The former must have the class as owner (because the
+ // constructor is owned by the class), the latter must not (because
+ // constructor parameters are interpreted as if they are outside the class).
+ // Don't do this for Java constructors because they need to see the import
+ // of the companion object, and it is not necessary for them because they
+ // have no implementation.
+ val cctx = if (tree.name == nme.CONSTRUCTOR && !(tree.mods is JavaDefined)) ctx.outer else ctx
+
+ val completer = tree match {
+ case tree: TypeDef => new TypeDefCompleter(tree)(cctx)
+ case _ => new Completer(tree)(cctx)
+ }
+
+ recordSym(ctx.newSymbol(
+ ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1,
+ adjustIfModule(completer, tree),
+ privateWithinClass(tree.mods), tree.namePos), tree)
+ case tree: Import =>
+ recordSym(ctx.newSymbol(
+ ctx.owner, nme.IMPORT, Synthetic, new Completer(tree), NoSymbol, tree.pos), tree)
+ case _ =>
+ NoSymbol
+ }
+ }
+
+ /** If `sym` exists, enter it in effective scope. Check that
+ * package members are not entered twice in the same run.
+ */
+ def enterSymbol(sym: Symbol)(implicit ctx: Context) = {
+ if (sym.exists) {
+ typr.println(s"entered: $sym in ${ctx.owner} and ${ctx.effectiveScope}")
+ ctx.enter(sym)
+ }
+ sym
+ }
+
+ /** Create package if it does not yet exist. */
+ private def createPackageSymbol(pid: RefTree)(implicit ctx: Context): Symbol = {
+ val pkgOwner = pid match {
+ case Ident(_) => if (ctx.owner eq defn.EmptyPackageClass) defn.RootClass else ctx.owner
+ case Select(qual: RefTree, _) => createPackageSymbol(qual).moduleClass
+ }
+ val existing = pkgOwner.info.decls.lookup(pid.name)
+
+ if ((existing is Package) && (pkgOwner eq existing.owner)) existing
+ else {
+ /** If there's already an existing type, then the package is a dup of this type */
+ val existingType = pkgOwner.info.decls.lookup(pid.name.toTypeName)
+ if (existingType.exists) {
+ ctx.error(PkgDuplicateSymbol(existingType), pid.pos)
+ ctx.newCompletePackageSymbol(pkgOwner, (pid.name ++ "$_error_").toTermName).entered
+ }
+ else ctx.newCompletePackageSymbol(pkgOwner, pid.name.asTermName).entered
+ }
+ }
+
+ /** Expand tree and store in `expandedTree` */
+ def expand(tree: Tree)(implicit ctx: Context): Unit = tree match {
+ case mdef: DefTree =>
+ val expanded = desugar.defTree(mdef)
+ typr.println(i"Expansion: $mdef expands to $expanded")
+ if (expanded ne mdef) mdef.pushAttachment(ExpandedTree, expanded)
+ case _ =>
+ }
+
+ /** The expanded version of this tree, or tree itself if not expanded */
+ def expanded(tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case ddef: DefTree => ddef.attachmentOrElse(ExpandedTree, ddef)
+ case _ => tree
+ }
+
+ /** A new context that summarizes an import statement */
+ def importContext(sym: Symbol, selectors: List[Tree])(implicit ctx: Context) =
+ ctx.fresh.setImportInfo(new ImportInfo(sym, selectors))
+
+ /** A new context for the interior of a class */
+ def inClassContext(selfInfo: DotClass /* Should be Type | Symbol*/)(implicit ctx: Context): Context = {
+ val localCtx: Context = ctx.fresh.setNewScope
+ selfInfo match {
+ case sym: Symbol if sym.exists && sym.name != nme.WILDCARD =>
+ localCtx.scope.openForMutations.enter(sym)
+ case _ =>
+ }
+ localCtx
+ }
+
+ /** For all class definitions `stat` in `xstats`: If the companion class if
+ * not also defined in `xstats`, invalidate it by setting its info to
+ * NoType.
+ */
+ def invalidateCompanions(pkg: Symbol, xstats: List[untpd.Tree])(implicit ctx: Context): Unit = {
+ val definedNames = xstats collect { case stat: NameTree => stat.name }
+ def invalidate(name: TypeName) =
+ if (!(definedNames contains name)) {
+ val member = pkg.info.decl(name).asSymDenotation
+ if (member.isClass && !(member is Package)) member.info = NoType
+ }
+ xstats foreach {
+ case stat: TypeDef if stat.isClassDef =>
+ invalidate(stat.name.moduleClassName)
+ case _ =>
+ }
+ }
+
+ /** Expand tree and create top-level symbols for statement and enter them into symbol table */
+ def index(stat: Tree)(implicit ctx: Context): Context = {
+ expand(stat)
+ indexExpanded(stat)
+ }
+
+ /** Create top-level symbols for all statements in the expansion of this statement and
+ * enter them into symbol table
+ */
+ def indexExpanded(origStat: Tree)(implicit ctx: Context): Context = {
+ def recur(stat: Tree): Context = stat match {
+ case pcl: PackageDef =>
+ val pkg = createPackageSymbol(pcl.pid)
+ index(pcl.stats)(ctx.fresh.setOwner(pkg.moduleClass))
+ invalidateCompanions(pkg, Trees.flatten(pcl.stats map expanded))
+ setDocstring(pkg, stat)
+ ctx
+ case imp: Import =>
+ importContext(createSymbol(imp), imp.selectors)
+ case mdef: DefTree =>
+ val sym = enterSymbol(createSymbol(mdef))
+ setDocstring(sym, origStat)
+ addEnumConstants(mdef, sym)
+ ctx
+ case stats: Thicket =>
+ stats.toList.foreach(recur)
+ ctx
+ case _ =>
+ ctx
+ }
+ recur(expanded(origStat))
+ }
+
+ /** Determines whether this field holds an enum constant.
+ * To qualify, the following conditions must be met:
+ * - The field's class has the ENUM flag set
+ * - The field's class extends java.lang.Enum
+ * - The field has the ENUM flag set
+ * - The field is static
+ * - The field is stable
+ */
+ def isEnumConstant(vd: ValDef)(implicit ctx: Context) = {
+ // val ownerHasEnumFlag =
+ // Necessary to check because scalac puts Java's static members into the companion object
+ // while Scala's enum constants live directly in the class.
+ // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal
+ // cyclic reference error. See the commit message for details.
+ // if (ctx.compilationUnit.isJava) ctx.owner.companionClass.is(Enum) else ctx.owner.is(Enum)
+ vd.mods.is(allOf(Enum, Stable, JavaStatic, JavaDefined)) // && ownerHasEnumFlag
+ }
+
+ /** Add java enum constants */
+ def addEnumConstants(mdef: DefTree, sym: Symbol)(implicit ctx: Context): Unit = mdef match {
+ case vdef: ValDef if (isEnumConstant(vdef)) =>
+ val enumClass = sym.owner.linkedClass
+ if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed)
+ enumClass.addAnnotation(Annotation.makeChild(sym))
+ case _ =>
+ }
+
+
+ def setDocstring(sym: Symbol, tree: Tree)(implicit ctx: Context) = tree match {
+ case t: MemberDef if t.rawComment.isDefined =>
+ ctx.docCtx.foreach(_.addDocstring(sym, t.rawComment))
+ case _ => ()
+ }
+
+ /** Create top-level symbols for statements and enter them into symbol table */
+ def index(stats: List[Tree])(implicit ctx: Context): Context = {
+
+ val classDef = mutable.Map[TypeName, TypeDef]()
+ val moduleDef = mutable.Map[TypeName, TypeDef]()
+
+ /** Merge the definitions of a synthetic companion generated by a case class
+ * and the real companion, if both exist.
+ */
+ def mergeCompanionDefs() = {
+ for (cdef @ TypeDef(name, _) <- stats)
+ if (cdef.isClassDef) {
+ classDef(name) = cdef
+ cdef.attachmentOrElse(ExpandedTree, cdef) match {
+ case Thicket(cls :: mval :: (mcls @ TypeDef(_, _: Template)) :: crest) =>
+ moduleDef(name) = mcls
+ case _ =>
+ }
+ }
+ for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) {
+ val typName = name.toTypeName
+ val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree)
+ moduleDef(typName) = mcls
+ classDef get name.toTypeName match {
+ case Some(cdef) =>
+ cdef.attachmentOrElse(ExpandedTree, cdef) match {
+ case Thicket(cls :: mval :: TypeDef(_, compimpl: Template) :: crest) =>
+ val mcls1 = cpy.TypeDef(mcls)(
+ rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body))
+ mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil))
+ moduleDef(typName) = mcls1
+ cdef.putAttachment(ExpandedTree, Thicket(cls :: crest))
+ case _ =>
+ }
+ case none =>
+ }
+ }
+ }
+
+ def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
+ val claz = ctx.denotNamed(classTree.name.encode).symbol
+ val modl = ctx.denotNamed(moduleTree.name.encode).symbol
+ ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered
+ ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered
+ }
+
+ def createCompanionLinks(implicit ctx: Context): Unit = {
+ for (cdef @ TypeDef(name, _) <- classDef.values) {
+ moduleDef.getOrElse(name, EmptyTree) match {
+ case t: TypeDef =>
+ createLinks(cdef, t)
+ case EmptyTree =>
+ }
+ }
+ }
+
+ stats foreach expand
+ mergeCompanionDefs()
+ val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx))
+ createCompanionLinks(ctxWithStats)
+ ctxWithStats
+ }
+
+ /** The completer of a symbol defined by a member def or import (except ClassSymbols) */
+ class Completer(val original: Tree)(implicit ctx: Context) extends LazyType {
+
+ protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original)
+
+ protected def typeSig(sym: Symbol): Type = original match {
+ case original: ValDef =>
+ if (sym is Module) moduleValSig(sym)
+ else valOrDefDefSig(original, sym, Nil, Nil, identity)(localContext(sym).setNewScope)
+ case original: DefDef =>
+ val typer1 = ctx.typer.newLikeThis
+ nestedTyper(sym) = typer1
+ typer1.defDefSig(original, sym)(localContext(sym).setTyper(typer1))
+ case imp: Import =>
+ try {
+ val expr1 = typedAheadExpr(imp.expr, AnySelectionProto)
+ ImportType(expr1)
+ } catch {
+ case ex: CyclicReference =>
+ typr.println(s"error while completing ${imp.expr}")
+ throw ex
+ }
+ }
+
+ final override def complete(denot: SymDenotation)(implicit ctx: Context) = {
+ if (completions != noPrinter && ctx.typerState != this.ctx.typerState) {
+ completions.println(completions.getClass.toString)
+ def levels(c: Context): Int =
+ if (c.typerState eq this.ctx.typerState) 0
+ else if (c.typerState == null) -1
+ else if (c.outer.typerState == c.typerState) levels(c.outer)
+ else levels(c.outer) + 1
+ completions.println(s"!!!completing ${denot.symbol.showLocated} in buried typerState, gap = ${levels(ctx)}")
+ }
+ completeInCreationContext(denot)
+ }
+
+ protected def addAnnotations(denot: SymDenotation): Unit = original match {
+ case original: untpd.MemberDef =>
+ var hasInlineAnnot = false
+ for (annotTree <- untpd.modsDeco(original).mods.annotations) {
+ val cls = typedAheadAnnotation(annotTree)
+ val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
+ denot.addAnnotation(ann)
+ if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor))
+ denot.setFlag(Inline)
+ }
+ case _ =>
+ }
+
+ private def addInlineInfo(denot: SymDenotation) = original match {
+ case original: untpd.DefDef if denot.isInlineMethod =>
+ Inliner.registerInlineInfo(
+ denot,
+ implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
+ )(localContext(denot.symbol))
+ case _ =>
+ }
+
+ /** Intentionally left without `implicit ctx` parameter. We need
+ * to pick up the context at the point where the completer was created.
+ */
+ def completeInCreationContext(denot: SymDenotation): Unit = {
+ addAnnotations(denot)
+ addInlineInfo(denot)
+ denot.info = typeSig(denot.symbol)
+ Checking.checkWellFormed(denot.symbol)
+ }
+ }
+
+ class TypeDefCompleter(original: TypeDef)(ictx: Context) extends Completer(original)(ictx) with TypeParamsCompleter {
+ private var myTypeParams: List[TypeSymbol] = null
+ private var nestedCtx: Context = null
+ assert(!original.isClassDef)
+
+ def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = {
+ if (myTypeParams == null) {
+ //println(i"completing type params of $sym in ${sym.owner}")
+ nestedCtx = localContext(sym).setNewScope
+ myTypeParams = {
+ implicit val ctx: Context = nestedCtx
+ val tparams = original.rhs match {
+ case PolyTypeTree(tparams, _) => tparams
+ case _ => Nil
+ }
+ completeParams(tparams)
+ tparams.map(symbolOfTree(_).asType)
+ }
+ }
+ myTypeParams
+ }
+
+ override protected def typeSig(sym: Symbol): Type =
+ typeDefSig(original, sym, completerTypeParams(sym)(ictx))(nestedCtx)
+ }
+
+ class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context) extends Completer(original)(ictx) {
+ withDecls(newScope)
+
+ protected implicit val ctx: Context = localContext(cls).setMode(ictx.mode &~ Mode.InSuperCall)
+
+ val TypeDef(name, impl @ Template(constr, parents, self, _)) = original
+
+ val (params, rest) = impl.body span {
+ case td: TypeDef => td.mods is Param
+ case vd: ValDef => vd.mods is ParamAccessor
+ case _ => false
+ }
+
+ def init() = index(params)
+
+ /** The type signature of a ClassDef with given symbol */
+ override def completeInCreationContext(denot: SymDenotation): Unit = {
+
+ /* The type of a parent constructor. Types constructor arguments
+ * only if parent type contains uninstantiated type parameters.
+ */
+ def parentType(parent: untpd.Tree)(implicit ctx: Context): Type =
+ if (parent.isType) {
+ typedAheadType(parent, AnyTypeConstructorProto).tpe
+ } else {
+ val (core, targs) = stripApply(parent) match {
+ case TypeApply(core, targs) => (core, targs)
+ case core => (core, Nil)
+ }
+ val Select(New(tpt), nme.CONSTRUCTOR) = core
+ val targs1 = targs map (typedAheadType(_))
+ val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
+ if (ptype.typeParams.isEmpty) ptype
+ else typedAheadExpr(parent).tpe
+ }
+
+ /* Check parent type tree `parent` for the following well-formedness conditions:
+ * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix)
+ * (2) If may not derive from itself
+ * (3) Overriding type parameters must be correctly forwarded. (@see checkTypeParamOverride)
+ * (4) The class is not final
+ * (5) If the class is sealed, it is defined in the same compilation unit as the current class
+ */
+ def checkedParentType(parent: untpd.Tree, paramAccessors: List[Symbol]): Type = {
+ val ptype = parentType(parent)(ctx.superCallContext)
+ if (cls.isRefinementClass) ptype
+ else {
+ val pt = checkClassType(ptype, parent.pos,
+ traitReq = parent ne parents.head, stablePrefixReq = true)
+ if (pt.derivesFrom(cls)) {
+ val addendum = parent match {
+ case Select(qual: Super, _) if ctx.scala2Mode =>
+ "\n(Note that inheriting a class of the same name is no longer allowed)"
+ case _ => ""
+ }
+ ctx.error(i"cyclic inheritance: $cls extends itself$addendum", parent.pos)
+ defn.ObjectType
+ }
+ else if (!paramAccessors.forall(checkTypeParamOverride(pt, _)))
+ defn.ObjectType
+ else {
+ val pclazz = pt.typeSymbol
+ if (pclazz.is(Final))
+ ctx.error(em"cannot extend final $pclazz", cls.pos)
+ if (pclazz.is(Sealed) && pclazz.associatedFile != cls.associatedFile)
+ ctx.error(em"cannot extend sealed $pclazz in different compilation unit", cls.pos)
+ pt
+ }
+ }
+ }
+
+ /* Check that every parameter with the same name as a visible named parameter in the parent
+ * class satisfies the following two conditions:
+ * (1) The overriding parameter is also named (i.e. not local/name mangled).
+ * (2) The overriding parameter is passed on directly to the parent parameter, or the
+ * parent parameter is not fully defined.
+ * @return true if conditions are satisfied, false otherwise.
+ */
+ def checkTypeParamOverride(parent: Type, paramAccessor: Symbol): Boolean = {
+ var ok = true
+ val pname = paramAccessor.name
+
+ def illegal(how: String): Unit = {
+ ctx.error(em"Illegal override of public type parameter $pname in $parent$how", paramAccessor.pos)
+ ok = false
+ }
+
+ def checkAlias(tp: Type): Unit = tp match {
+ case tp: RefinedType =>
+ if (tp.refinedName == pname)
+ tp.refinedInfo match {
+ case TypeAlias(alias) =>
+ alias match {
+ case TypeRef(pre, name1) if name1 == pname && (pre =:= cls.thisType) =>
+ // OK, parameter is passed on directly
+ case _ =>
+ illegal(em".\nParameter is both redeclared and instantiated with $alias.")
+ }
+ case _ => // OK, argument is not fully defined
+ }
+ else checkAlias(tp.parent)
+ case _ =>
+ }
+ if (parent.nonPrivateMember(paramAccessor.name).symbol.is(Param))
+ if (paramAccessor is Private)
+ illegal("\nwith private parameter. Parameter definition needs to be prefixed with `type'.")
+ else
+ checkAlias(parent)
+ ok
+ }
+
+ addAnnotations(denot)
+
+ val selfInfo =
+ if (self.isEmpty) NoType
+ else if (cls.is(Module)) {
+ val moduleType = cls.owner.thisType select sourceModule
+ if (self.name == nme.WILDCARD) moduleType
+ else recordSym(
+ ctx.newSymbol(cls, self.name, self.mods.flags, moduleType, coord = self.pos),
+ self)
+ }
+ else createSymbol(self)
+
+ // pre-set info, so that parent types can refer to type params
+ val tempInfo = new TempClassInfo(cls.owner.thisType, cls, decls, selfInfo)
+ denot.info = tempInfo
+
+ // 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()
+
+ index(rest)(inClassContext(selfInfo))
+
+ val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList
+ val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors)))
+ val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls)
+ typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs")
+
+ tempInfo.finalize(denot, parentRefs)
+
+ Checking.checkWellFormed(cls)
+ if (isDerivedValueClass(cls)) cls.setFlag(Final)
+ cls.setApplicableFlags(
+ (NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
+ }
+ }
+
+ /** Typecheck tree during completion, and remember result in typedtree map */
+ private def typedAheadImpl(tree: Tree, pt: Type)(implicit ctx: Context): tpd.Tree = {
+ val xtree = expanded(tree)
+ xtree.getAttachment(TypedAhead) match {
+ case Some(ttree) => ttree
+ case none =>
+ val ttree = typer.typed(tree, pt)
+ xtree.pushAttachment(TypedAhead, ttree)
+ ttree
+ }
+ }
+
+ def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
+ typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type)
+
+ def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
+ typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType)
+
+ def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): Symbol = tree match {
+ case Apply(fn, _) => typedAheadAnnotation(fn)
+ case TypeApply(fn, _) => typedAheadAnnotation(fn)
+ case Select(qual, nme.CONSTRUCTOR) => typedAheadAnnotation(qual)
+ case New(tpt) => typedAheadType(tpt).tpe.classSymbol
+ }
+
+ /** Enter and typecheck parameter list */
+ def completeParams(params: List[MemberDef])(implicit ctx: Context) = {
+ index(params)
+ for (param <- params) typedAheadExpr(param)
+ }
+
+ /** The signature of a module valdef.
+ * This will compute the corresponding module class TypeRef immediately
+ * without going through the defined type of the ValDef. This is necessary
+ * to avoid cyclic references involving imports and module val defs.
+ */
+ def moduleValSig(sym: Symbol)(implicit ctx: Context): Type = {
+ val clsName = sym.name.moduleClassName
+ val cls = ctx.denotNamed(clsName) suchThat (_ is ModuleClass)
+ ctx.owner.thisType select (clsName, cls)
+ }
+
+ /** The type signature of a ValDef or DefDef
+ * @param mdef The definition
+ * @param sym Its symbol
+ * @param paramFn A wrapping function that produces the type of the
+ * defined symbol, given its final return type
+ */
+ def valOrDefDefSig(mdef: ValOrDefDef, sym: Symbol, typeParams: List[Symbol], paramss: List[List[Symbol]], paramFn: Type => Type)(implicit ctx: Context): Type = {
+
+ def inferredType = {
+ /** A 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
+ * of overridden methods.
+ * NoType if neither case holds.
+ */
+ val inherited =
+ if (sym.owner.isTerm) NoType
+ else {
+ // TODO: Look only at member of supertype instead?
+ lazy val schema = paramFn(WildcardType)
+ val site = sym.owner.thisType
+ ((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) =>
+ def instantiatedResType(info: Type, tparams: List[Symbol], paramss: List[List[Symbol]]): Type = info match {
+ case info: PolyType =>
+ if (info.paramNames.length == typeParams.length)
+ instantiatedResType(info.instantiate(tparams.map(_.typeRef)), Nil, paramss)
+ else NoType
+ case info: MethodType =>
+ paramss match {
+ case params :: paramss1 if info.paramNames.length == params.length =>
+ instantiatedResType(info.instantiate(params.map(_.termRef)), tparams, paramss1)
+ case _ =>
+ NoType
+ }
+ case _ =>
+ if (tparams.isEmpty && paramss.isEmpty) info.widenExpr
+ else NoType
+ }
+ val iRawInfo =
+ cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema).info
+ val iResType = instantiatedResType(iRawInfo, typeParams, paramss).asSeenFrom(site, cls)
+ if (iResType.exists)
+ typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType")
+ tp & iResType
+ }
+ }
+
+ /** The proto-type to be used when inferring the result type from
+ * the right hand side. This is `WildcardType` except if the definition
+ * is a default getter. In that case, the proto-type is the type of
+ * the corresponding parameter where bound parameters are replaced by
+ * Wildcards.
+ */
+ def rhsProto = {
+ val name = sym.asTerm.name
+ val idx = name.defaultGetterIndex
+ if (idx < 0) WildcardType
+ else {
+ val original = name.defaultGetterToMethod
+ val meth: Denotation =
+ if (original.isConstructorName && (sym.owner is ModuleClass))
+ sym.owner.companionClass.info.decl(nme.CONSTRUCTOR)
+ else
+ 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))
+ else paramProto(paramss1, idx - params.length)
+ case nil =>
+ WildcardType
+ }
+ val defaultAlts = meth.altsWith(_.hasDefaultParams)
+ if (defaultAlts.length == 1)
+ paramProto(defaultAlts.head.info.widen.paramTypess, idx)
+ else
+ WildcardType
+ }
+ }
+
+ // println(s"final inherited for $sym: ${inherited.toString}") !!!
+ // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}")
+ def isInline = sym.is(FinalOrInline, butNot = Method | Mutable)
+
+ // Widen rhs type and approximate `|' but keep ConstantTypes if
+ // definition is inline (i.e. final in Scala2).
+ def widenRhs(tp: Type): Type = tp.widenTermRefExpr match {
+ case tp: ConstantType if isInline => tp
+ case _ => ctx.harmonizeUnion(tp.widen)
+ }
+
+ // Replace aliases to Unit by Unit itself. If we leave the alias in
+ // it would be erased to BoxedUnit.
+ def dealiasIfUnit(tp: Type) = if (tp.isRef(defn.UnitClass)) defn.UnitType else tp
+
+ val rhsCtx = ctx.addMode(Mode.InferringReturnType)
+ def rhsType = typedAheadExpr(mdef.rhs, inherited orElse rhsProto)(rhsCtx).tpe
+ def cookedRhsType = ctx.deskolemize(dealiasIfUnit(widenRhs(rhsType)))
+ lazy val lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.pos)
+ //if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType")
+ if (inherited.exists)
+ if (sym.is(Final, butNot = Method) && lhsType.isInstanceOf[ConstantType])
+ lhsType // keep constant types that fill in for a non-constant (to be revised when inline has landed).
+ else inherited
+ else {
+ if (sym is Implicit) {
+ val resStr = if (mdef.isInstanceOf[DefDef]) "result " else ""
+ ctx.error(s"${resStr}type of implicit definition needs to be given explicitly", mdef.pos)
+ sym.resetFlag(Implicit)
+ }
+ lhsType orElse WildcardType
+ }
+ }
+
+ val tptProto = mdef.tpt match {
+ case _: untpd.DerivedTypeTree =>
+ WildcardType
+ case TypeTree() =>
+ inferredType
+ case TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) =>
+ val rhsType = typedAheadExpr(mdef.rhs, tpt.tpe).tpe
+ mdef match {
+ case mdef: DefDef if mdef.name == nme.ANON_FUN =>
+ val hygienicType = avoid(rhsType, paramss.flatten)
+ if (!(hygienicType <:< tpt.tpe))
+ ctx.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" +
+ i"it is not a supertype of the hygienic type $hygienicType", mdef.pos)
+ //println(i"lifting $rhsType over $paramss -> $hygienicType = ${tpt.tpe}")
+ //println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe })
+ case _ =>
+ }
+ WildcardType
+ case _ =>
+ WildcardType
+ }
+ paramFn(typedAheadType(mdef.tpt, tptProto).tpe)
+ }
+
+ /** The type signature of a DefDef with given symbol */
+ def defDefSig(ddef: DefDef, sym: Symbol)(implicit ctx: Context) = {
+ val DefDef(name, tparams, vparamss, _, _) = ddef
+ val isConstructor = name == nme.CONSTRUCTOR
+
+ // The following 3 lines replace what was previously just completeParams(tparams).
+ // But that can cause bad bounds being computed, as witnessed by
+ // tests/pos/paramcycle.scala. The problematic sequence is this:
+ // 0. Class constructor gets completed.
+ // 1. Type parameter CP of constructor gets completed
+ // 2. As a first step CP's bounds are set to Nothing..Any.
+ // 3. CP's real type bound demands the completion of corresponding type parameter DP
+ // of enclosing class.
+ // 4. Type parameter DP has a rhs a DerivedFromParam tree, as installed by
+ // desugar.classDef
+ // 5. The completion of DP then copies the current bounds of CP, which are still Nothing..Any.
+ // 6. The completion of CP finishes installing the real type bounds.
+ // Consequence: CP ends up with the wrong bounds!
+ // To avoid this we always complete type parameters of a class before the type parameters
+ // of the class constructor, but after having indexed the constructor parameters (because
+ // indexing is needed to provide a symbol to copy for DP's completion.
+ // With the patch, we get instead the following sequence:
+ // 0. Class constructor gets completed.
+ // 1. Class constructor parameter CP is indexed.
+ // 2. Class parameter DP starts completion.
+ // 3. Info of CP is computed (to be copied to DP).
+ // 4. CP is completed.
+ // 5. Info of CP is copied to DP and DP is completed.
+ index(tparams)
+ if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted())
+ for (tparam <- tparams) typedAheadExpr(tparam)
+
+ vparamss foreach completeParams
+ def typeParams = tparams map symbolOfTree
+ val paramSymss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor)
+ def wrapMethType(restpe: Type): Type = {
+ val restpe1 = // try to make anonymous functions non-dependent, so that they can be used in closures
+ if (name == nme.ANON_FUN) avoid(restpe, paramSymss.flatten)
+ else restpe
+ ctx.methodType(tparams map symbolOfTree, paramSymss, restpe1, isJava = ddef.mods is JavaDefined)
+ }
+ if (isConstructor) {
+ // set result type tree to unit, but take the current class as result type of the symbol
+ typedAheadType(ddef.tpt, defn.UnitType)
+ wrapMethType(ctx.effectiveResultType(sym, typeParams, NoType))
+ }
+ else valOrDefDefSig(ddef, sym, typeParams, paramSymss, wrapMethType)
+ }
+
+ def typeDefSig(tdef: TypeDef, sym: Symbol, tparamSyms: List[TypeSymbol])(implicit ctx: Context): Type = {
+ def abstracted(tp: Type): Type =
+ if (tparamSyms.nonEmpty) tp.LambdaAbstract(tparamSyms) else tp
+
+ val dummyInfo = abstracted(TypeBounds.empty)
+ sym.info = dummyInfo
+ // Temporarily set info of defined type T to ` >: Nothing <: Any.
+ // This is done to avoid cyclic reference errors for F-bounds.
+ // This is subtle: `sym` has now an empty TypeBounds, but is not automatically
+ // made an abstract type. If it had been made an abstract type, it would count as an
+ // abstract type of its enclosing class, which might make that class an invalid
+ // prefix. I verified this would lead to an error when compiling io.ClassPath.
+ // A distilled version is in pos/prefix.scala.
+ //
+ // The scheme critically relies on an implementation detail of isRef, which
+ // inspects a TypeRef's info, instead of simply dealiasing alias types.
+
+ val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree]
+ val rhs = tdef.rhs match {
+ case PolyTypeTree(_, body) => body
+ case rhs => rhs
+ }
+ val rhsBodyType = typedAheadType(rhs).tpe
+ val rhsType = if (isDerived) rhsBodyType else abstracted(rhsBodyType)
+ val unsafeInfo = rhsType match {
+ case bounds: TypeBounds => bounds
+ case alias => TypeAlias(alias, if (sym is Local) sym.variance else 0)
+ }
+ if (isDerived) sym.info = unsafeInfo
+ else {
+ sym.info = NoCompleter
+ sym.info = checkNonCyclic(sym, unsafeInfo, reportErrors = true)
+ }
+
+ // Here we pay the price for the cavalier setting info to TypeBounds.empty above.
+ // We need to compensate by invalidating caches in references that might
+ // still contain the TypeBounds.empty. If we do not do this, stdlib factories
+ // fail with a bounds error in PostTyper.
+ def ensureUpToDate(tp: Type, outdated: Type) = tp match {
+ case tref: TypeRef if tref.info == outdated && sym.info != outdated =>
+ tref.uncheckedSetSym(null)
+ case _ =>
+ }
+ ensureUpToDate(sym.typeRef, dummyInfo)
+ ensureUpToDate(sym.typeRef.appliedTo(tparamSyms.map(_.typeRef)), TypeBounds.empty)
+ sym.info
+ }
+}