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 NameKinds.DefaultGetterName 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 => import NamerContextOps._ /** 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.asInstanceOf[List[TermSymbol]], resultType) } if (typeParams.nonEmpty) PolyType.fromParams(typeParams.asInstanceOf[List[TypeSymbol]], monotpe) else if (valueParamss.isEmpty) ExprType(monotpe) else monotpe } /** Add moduleClass or sourceModule functionality to completer * for a module or module class */ def adjustModuleCompleter(completer: LazyType, name: Name) = { val scope = this.effectiveScope if (name.isTermName) completer withModuleClass (implicit ctx => findModuleBuddy(name.moduleClassName, scope)) else completer withSourceModule (implicit ctx => findModuleBuddy(name.sourceModuleName, scope)) } } object NamerContextOps { /** Find moduleClass/sourceModule in effective scope */ private def findModuleBuddy(name: Name, scope: Scope)(implicit ctx: Context) = { val it = scope.lookupAll(name).filter(_ is Module) assert(it.hasNext, s"no companion $name in $scope") it.next } } /** 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).toTypeName 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(_, LambdaTypeTree(_, _)) 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(imp: Import, sym: Symbol)(implicit ctx: Context) = { val impNameOpt = imp.expr match { case ref: RefTree => Some(ref.name.asTermName) case _ => None } ctx.fresh.setImportInfo(new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt)) } /** 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(imp, createSymbol(imp)) 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 = { // module name -> (stat, moduleCls | moduleVal) val moduleClsDef = mutable.Map[TypeName, (Tree, TypeDef)]() val moduleValDef = mutable.Map[TermName, (Tree, ValDef)]() /** Remove the subtree `tree` from the expanded tree of `mdef` */ def removeInExpanded(mdef: Tree, tree: Tree): Unit = { val Thicket(trees) = expanded(mdef) mdef.putAttachment(ExpandedTree, Thicket(trees.filter(_ != tree))) } /** Merge the module class `modCls` in the expanded tree of `mdef` with the given stats */ def mergeModuleClass(mdef: Tree, modCls: TypeDef, stats: List[Tree]): TypeDef = { var res: TypeDef = null val Thicket(trees) = expanded(mdef) val merged = trees.map { tree => if (tree == modCls) { val impl = modCls.rhs.asInstanceOf[Template] res = cpy.TypeDef(modCls)(rhs = cpy.Template(impl)(body = stats ++ impl.body)) res } else tree } mdef.putAttachment(ExpandedTree, Thicket(merged)) res } /** Merge `fromCls` of `fromStat` into `toCls` of `toStat` * if the former is synthetic and the latter not. * * Note: * 1. `fromStat` and `toStat` could be the same stat * 2. `fromCls` and `toCls` are necessarily different */ def mergeIfSynthetic(fromStat: Tree, fromCls: TypeDef, toStat: Tree, toCls: TypeDef): Unit = if (fromCls.mods.is(Synthetic) && !toCls.mods.is(Synthetic)) { removeInExpanded(fromStat, fromCls) val mcls = mergeModuleClass(toStat, toCls, fromCls.rhs.asInstanceOf[Template].body) moduleClsDef(fromCls.name) = (toStat, mcls) } /** Merge the definitions of a synthetic companion generated by a case class * and the real companion, if both exist. */ def mergeCompanionDefs() = { def valid(mdef: MemberDef): Boolean = mdef.mods.is(Module, butNot = Package) for (stat <- stats) expanded(stat) match { case Thicket(trees) => // companion object always expands to thickets trees.map { case mcls @ TypeDef(name, impl: Template) if valid(mcls) => (moduleClsDef.get(name): @unchecked) match { case Some((stat1, mcls1@TypeDef(_, impl1: Template))) => mergeIfSynthetic(stat, mcls, stat1, mcls1) mergeIfSynthetic(stat1, mcls1, stat, mcls) case None => moduleClsDef(name) = (stat, mcls) } case vdef @ ValDef(name, _, _) if valid(vdef) => moduleValDef.get(name) match { case Some((stat1, vdef1)) => if (vdef.mods.is(Synthetic) && !vdef1.mods.is(Synthetic)) removeInExpanded(stat, vdef) else if (!vdef.mods.is(Synthetic) && vdef1.mods.is(Synthetic)) { removeInExpanded(stat1, vdef1) moduleValDef(name) = (stat, vdef) } else { // double definition of objects or case classes, handled elsewhere } case None => moduleValDef(name) = (stat, vdef) } case _ => } case _ => } } /** Create links between companion object and companion class */ def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = { val claz = ctx.effectiveScope.lookup(classTree.name.encode) val modl = ctx.effectiveScope.lookup(moduleTree.name.encode) ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered } def createCompanionLinks(implicit ctx: Context): Unit = { val classDef = mutable.Map[TypeName, TypeDef]() val moduleDef = mutable.Map[TypeName, TypeDef]() def updateCache(cdef: TypeDef): Unit = { if (!cdef.isClassDef || cdef.mods.is(Package)) return if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef else classDef(cdef.name) = cdef } for (stat <- stats) expanded(stat) match { case cdef : TypeDef => updateCache(cdef) case Thicket(trees) => trees.map { case cdef: TypeDef => updateCache(cdef) case _ => } case _ => } for (cdef @ TypeDef(name, _) <- classDef.values) { moduleDef.getOrElse(name.moduleClassName, EmptyTree) match { case t: TypeDef => createLinks(cdef, t) case EmptyTree => } } // If a top-level object or class has no companion in the current run, we // enter a dummy companion (`denot.isAbsent` returns true) in scope. This // ensures that we never use a companion from a previous run or from the // classpath. See tests/pos/false-companion for an example where this // matters. if (ctx.owner.is(PackageClass)) { for (cdef @ TypeDef(moduleName, _) <- moduleDef.values) { val moduleSym = ctx.effectiveScope.lookup(moduleName.encode) if (moduleSym.isDefinedInCurrentRun) { val className = moduleName.stripModuleClassSuffix.toTypeName val classSym = ctx.effectiveScope.lookup(className.encode) if (!classSym.isDefinedInCurrentRun) { val absentClassSymbol = ctx.newClassSymbol(ctx.owner, className, EmptyFlags, _ => NoType) enterSymbol(absentClassSymbol) } } } for (cdef @ TypeDef(className, _) <- classDef.values) { val classSym = ctx.effectiveScope.lookup(className.encode) if (classSym.isDefinedInCurrentRun) { val moduleName = className.toTermName val moduleSym = ctx.effectiveScope.lookup(moduleName.encode) if (!moduleSym.isDefinedInCurrentRun) { val absentModuleSymbol = ctx.newModuleSymbol(ctx.owner, moduleName, EmptyFlags, EmptyFlags, (_, _) => NoType) enterSymbol(absentModuleSymbol) } } } } } stats.foreach(expand) mergeCompanionDefs() val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx)) createCompanionLinks(ctxWithStats) ctxWithStats } /** Add all annotations of definitions in `stats` to the defined symbols */ def annotate(stats: List[Tree])(implicit ctx: Context): Unit = { def recur(stat: Tree): Unit = stat match { case pcl: PackageDef => annotate(pcl.stats) case stat: untpd.MemberDef => stat.getAttachment(SymOfTree) match { case Some(sym) => sym.infoOrCompleter match { case info: Completer if !defn.isPredefClass(sym.owner) => // Annotate Predef methods only when they are completed; // This is necessary to break a cyclic dependence between `Predef` // and `deprecated` in test `compileStdLib`. addAnnotations(sym, stat)(info.creationContext) case _ => // Annotations were already added as part of the symbol's completion } case none => assert(stat.typeOpt.exists, i"no symbol for $stat") } case stat: untpd.Thicket => stat.trees.foreach(recur) case _ => } for (stat <- stats) recur(expanded(stat)) } /** Add annotations of `stat` to `sym`. * This method can be called twice on a symbol (e.g. once * during the `annotate` phase and then again during completion). * Therefore, care needs to be taken not to add annotations again * that are already added to the symbol. */ def addAnnotations(sym: Symbol, stat: MemberDef)(implicit ctx: Context) = { // (1) The context in which an annotation of a top-level class or module is evaluated // is the closest enclosing context which has the enclosing package as owner. // (2) The context in which an annotation for any other symbol is evaluated is the // closest enclosing context which has the owner of the class enclosing the symbol as owner. // E.g in // // package p // import a.b // class C { // import d.e // @ann m() ... // } // // `@ann` is evaluated in the context just outside `C`, where the `a.b` // import is visible but the `d.e` import is forgotten. This measure is necessary // in order to avoid cycles. lazy val annotCtx = { var target = sym.owner.lexicallyEnclosingClass if (!target.is(PackageClass)) target = target.owner var c = ctx while (c.owner != target) c = c.outer c } for (annotTree <- untpd.modsDeco(stat).mods.annotations) { val cls = typedAheadAnnotation(annotTree)(annotCtx) if (sym.unforcedAnnotation(cls).isEmpty) { val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) sym.addAnnotation(ann) if (cls == defn.InlineAnnot && sym.is(Method, butNot = Accessor)) sym.setFlag(Inline) } } } def indexAndAnnotate(stats: List[Tree])(implicit ctx: Context): Context = { val localCtx = index(stats) annotate(stats) localCtx } /** 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) /** The context with which this completer was created */ def creationContext = ctx 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) } 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 = { val sym = denot.symbol original match { case original: MemberDef => addAnnotations(sym, original) case _ => } addInlineInfo(denot) denot.info = typeSig(sym) Checking.checkWellFormed(sym) denot.info = avoidPrivateLeaks(sym, sym.pos) } } 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 = nestedCtx val tparams = original.rhs match { case LambdaTypeTree(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 fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.pos) } /* 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) The class is not final * (4) If the class is sealed, it is defined in the same compilation unit as the current class */ def checkedParentType(parent: untpd.Tree): 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 { 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 } } } addAnnotations(denot.symbol, original) 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) annotate(constr :: params) symbolOfTree(constr).ensureCompleted() indexAndAnnotate(rest)(inClassContext(selfInfo)) val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_))) 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.info = avoidPrivateLeaks(cls, cls.pos) } } /** Typecheck `tree` during completion using `typed`, and remember result in TypedAhead map */ def typedAheadImpl(tree: Tree, typed: untpd.Tree => tpd.Tree)(implicit ctx: Context): tpd.Tree = { val xtree = expanded(tree) xtree.getAttachment(TypedAhead) match { case Some(ttree) => ttree case none => val ttree = typed(tree) xtree.putAttachment(TypedAhead, ttree) ttree } } def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type)) def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = typedAheadImpl(tree, typer.typed(_, 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) = { indexAndAnnotate(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 = sym.asTerm.name collect { case DefaultGetterName(original, idx) => 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), null, Set.empty) else paramProto(paramss1, idx - params.length) case nil => WildcardType } val defaultAlts = meth.altsWith(_.hasDefaultParams) if (defaultAlts.length == 1) paramProto(defaultAlts.head.info.widen.paramInfoss, idx) else WildcardType } getOrElse 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) and keep module singleton types // instead of widening to the underlying module class types. def widenRhs(tp: Type): Type = tp.widenTermRefExpr match { case ctp: ConstantType if isInline => ctp case ref: TypeRef if ref.symbol.is(ModuleClass) => 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 { def missingType(modifier: String) = { ctx.error(s"${modifier}type of implicit definition needs to be given explicitly", mdef.pos) sym.resetFlag(Implicit) } if (sym is Implicit) mdef match { case _: DefDef => missingType("result") case _: ValDef if sym.owner.isType => missingType("") case _ => } 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. indexAndAnnotate(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 = HKTypeLambda.fromParams(tparamSyms, 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 LambdaTypeTree(_, 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 } }