diff options
author | Lukas Rytz <lukas.rytz@epfl.ch> | 2013-02-03 23:37:48 -0800 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@epfl.ch> | 2013-02-03 23:37:48 -0800 |
commit | 5aa225f3a57238ed90f8f60cf139043a23bd9f4f (patch) | |
tree | b61731c335ca09f1ab23b057a4ec0c87b3275ee2 /src/compiler | |
parent | 786a686b78e7bffb452c6508a83b69ad83849819 (diff) | |
parent | f3cdf146709e0dd98533ee77e8ca2566380cb932 (diff) | |
download | scala-5aa225f3a57238ed90f8f60cf139043a23bd9f4f.tar.gz scala-5aa225f3a57238ed90f8f60cf139043a23bd9f4f.tar.bz2 scala-5aa225f3a57238ed90f8f60cf139043a23bd9f4f.zip |
Merge pull request #2022 from lrytz/analyzerPlugins210
Analyzer Plugins
Diffstat (limited to 'src/compiler')
9 files changed, 613 insertions, 241 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index f338e390bb..965063a724 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -231,7 +231,17 @@ abstract class UnCurry extends InfoTransform * If `settings.XoldPatmat.value`, also synthesized AbstractPartialFunction subclasses (see synthPartialFunction). * */ - def transformFunction(fun: Function): Tree = + def transformFunction(fun: Function): Tree = { + fun.tpe match { + // can happen when analyzer plugins assign refined types to functions, e.g. + // (() => Int) { def apply(): Int @typeConstraint } + case RefinedType(List(funTp), decls) => + debuglog(s"eliminate refinement from function type ${fun.tpe}") + fun.tpe = funTp + case _ => + () + } + deEta(fun) match { // nullary or parameterless case fun1 if fun1 ne fun => fun1 @@ -239,10 +249,7 @@ abstract class UnCurry extends InfoTransform // only get here when running under -Xoldpatmat synthPartialFunction(fun) case _ => - val parents = ( - if (isFunctionType(fun.tpe)) addSerializable(abstractFunctionForFunctionType(fun.tpe)) - else addSerializable(ObjectClass.tpe, fun.tpe) - ) + val parents = addSerializable(abstractFunctionForFunctionType(fun.tpe)) val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation anonClass setInfo ClassInfoType(parents, newScope, anonClass) @@ -275,6 +282,7 @@ abstract class UnCurry extends InfoTransform } } + } /** Transform a function node (x => body) of type PartialFunction[T, R] where * body = expr match { case P_i if G_i => E_i }_i=1..n diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 78175f393a..b50486306d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -25,6 +25,7 @@ trait Analyzer extends AnyRef with TypeDiagnostics with ContextErrors with StdAttachments + with AnalyzerPlugins { val global : Global import global._ diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala new file mode 100644 index 0000000000..28f620dbb5 --- /dev/null +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -0,0 +1,225 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package typechecker + +/** + * @author Lukas Rytz + * @version 1.0 + */ +trait AnalyzerPlugins { self: Analyzer => + import global._ + + + trait AnalyzerPlugin { + /** + * Selectively activate this analyzer plugin, e.g. according to the compiler phase. + * + * Note that the current phase can differ from the global compiler phase (look for `enteringPhase` + * invocations in the compiler). For instance, lazy types created by the UnPickler are completed + * at the phase in which their symbol is created. Observations show that this can even be the + * parser phase. Since symbol completion can trigger subtyping, typing etc, your plugin might + * need to be active also in phases other than namer and typer. + * + * Typically, this method can be implemented as + * + * global.phase.id < global.currentRun.picklerPhase.id + */ + def isActive(): Boolean = true + + /** + * Let analyzer plugins change the expected type before type checking a tree. + */ + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = pt + + /** + * Let analyzer plugins modify the type that has been computed for a tree. + * + * @param tpe The type inferred by the type checker, initially (for first plugin) `tree.tpe` + * @param typer The yper that type checked `tree` + * @param tree The type-checked tree + * @param mode Mode that was used for typing `tree` + * @param pt Expected type that was used for typing `tree` + */ + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = tpe + + /** + * Let analyzer plugins change the types assigned to definitions. For definitions that have + * an annotated type, the assigned type is obtained by typing that type tree. Otherwise, the + * type is inferred by typing the definition's righthand side. + * + * In order to know if the type was inferred, you can query the `wasEmpty` field in the `tpt` + * TypeTree of the definition (for DefDef and ValDef). + * + * (*) If the type of a method or value is inferred, the type-checked tree is stored in the + * `analyzer.transformed` hash map, indexed by the definition's rhs tree. + * + * NOTE: Invoking the type checker can lead to cyclic reference errors. For instance, if this + * method is called from the type completer of a recursive method, type checking the mehtod + * rhs will invoke the same completer again. It might be possible to avoid this situation by + * assigning `tpe` to `defTree.symbol` (untested) - the final type computed by this method + * will then be assigned to the definition's symbol by monoTypeCompleter (in Namers). + * + * The hooks into `typeSig` allow analyzer plugins to add annotations to (or change the types + * of) definition symbols. This cannot not be achieved by using `pluginsTyped`: this method + * is only called during type checking, so changing the type of a symbol at this point is too + * late: references to the symbol might already be typed and therefore obtain the the original + * type assigned during naming. + * + * @param defTree is the definition for which the type was computed. The different cases are + * outlined below. Note that this type is untyped (for methods and values with inferred type, + * the typed rhs trees are available in analyzer.transformed). + * + * Case defTree: Template + * - tpe : A ClassInfoType for the template + * - typer: The typer for template members, i.e. expressions and definitions of defTree.body + * - pt : WildcardType + * - the class symbol is accessible through typer.context.owner + * + * Case defTree: ClassDef + * - tpe : A ClassInfoType, or a PolyType(params, ClassInfoType) for polymorphic classes. + * The class type is the one computed by templateSig, i.e. through the above case + * - typer: The typer for the class. Note that this typer has a different context than the + * typer for the template. + * - pt : WildcardType + * + * Case defTree: ModuleDef + * - tpe : A ClassInfoType computed by templateSig + * - typer: The typer for the module. context.owner of this typer is the module class symbol + * - pt : WildcardType + * + * Case defTree: DefDef + * - tpe : The type of the method (MethodType, PolyType or NullaryMethodType). (*) + * - typer: The typer the rhs of this method + * - pt : If tpt.isEmpty, either the result type from the overridden method, or WildcardType. + * Otherwise the type obtained from typing tpt. + * - Note that for constructors, pt is the class type which the constructor creates. To type + * check the rhs of the constructor however, the expected type has to be WildcardType (see + * Typers.typedDefDef) + * + * Case defTree: ValDef + * - tpe : The type of this value. (*) + * - typer: The typer for the rhs of this value + * - pt : If tpt.isEmpty, WildcardType. Otherwise the type obtained from typing tpt. + * - Note that pluginsTypeSig might be called multiple times for the same ValDef since it is + * used to compute the types of the accessor methods (see `pluginsTypeSigAccessor`) + * + * Case defTree: TypeDef + * - tpe : The type obtained from typing rhs (PolyType if the TypeDef defines a polymorphic type) + * - typer: The typer for the rhs of this type + * - pt : WildcardType + */ + def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = tpe + + /** + * Modify the types of field accessors. The namer phase creates method types for getters and + * setters based on the type of the corresponding field. + * + * Note: in order to compute the method type of an accessor, the namer calls `typeSig` on the + * `ValDef` tree of the corresponding field. This implies that the `pluginsTypeSig` method + * is potentially called multiple times for the same ValDef tree. + * + * @param tpe The method type created by the namer for the accessor + * @param typer The typer for the ValDef (not for the rhs) + * @param tree The ValDef corresponding to the accessor + * @param sym The accessor method symbol (getter, setter, beanGetter or beanSetter) + */ + def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = tpe + + /** + * Decide whether this analyzer plugin can adapt a tree that has an annotated type to the + * given type tp, taking into account the given mode (see method adapt in trait Typers). + */ + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = false + + /** + * Adapt a tree that has an annotated type to the given type tp, taking into account the given + * mode (see method adapt in trait Typers). + * + * An implementation cannot rely on canAdaptAnnotations being called before. If the implementing + * class cannot do the adapting, it should return the tree unchanged. + */ + def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = tree + + /** + * Modify the type of a return expression. By default, return expressions have type + * NothingClass.tpe. + * + * @param tpe The type of the return expression + * @param typer The typer that was used for typing the return tree + * @param tree The typed return expression tree + * @param pt The return type of the enclosing method + */ + def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = tpe + } + + + + /** A list of registered analyzer plugins */ + private var analyzerPlugins: List[AnalyzerPlugin] = Nil + + /** Registers a new analyzer plugin */ + def addAnalyzerPlugin(plugin: AnalyzerPlugin) { + if (!analyzerPlugins.contains(plugin)) + analyzerPlugins = plugin :: analyzerPlugins + } + + + /** @see AnalyzerPlugin.pluginsPt */ + def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = + if (analyzerPlugins.isEmpty) pt + else analyzerPlugins.foldLeft(pt)((pt, plugin) => + if (!plugin.isActive()) pt else plugin.pluginsPt(pt, typer, tree, mode)) + + /** @see AnalyzerPlugin.pluginsTyped */ + def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = { + // support deprecated methods in annotation checkers + val annotCheckersTpe = addAnnotations(tree, tpe) + if (analyzerPlugins.isEmpty) annotCheckersTpe + else analyzerPlugins.foldLeft(annotCheckersTpe)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTyped(tpe, typer, tree, mode, pt)) + } + + /** @see AnalyzerPlugin.pluginsTypeSig */ + def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = + if (analyzerPlugins.isEmpty) tpe + else analyzerPlugins.foldLeft(tpe)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTypeSig(tpe, typer, defTree, pt)) + + /** @see AnalyzerPlugin.pluginsTypeSigAccessor */ + def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = + if (analyzerPlugins.isEmpty) tpe + else analyzerPlugins.foldLeft(tpe)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTypeSigAccessor(tpe, typer, tree, sym)) + + /** @see AnalyzerPlugin.canAdaptAnnotations */ + def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = { + // support deprecated methods in annotation checkers + val annotCheckersExists = global.canAdaptAnnotations(tree, mode, pt) + annotCheckersExists || { + if (analyzerPlugins.isEmpty) false + else analyzerPlugins.exists(plugin => + plugin.isActive() && plugin.canAdaptAnnotations(tree, typer, mode, pt)) + } + } + + /** @see AnalyzerPlugin.adaptAnnotations */ + def adaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Tree = { + // support deprecated methods in annotation checkers + val annotCheckersTree = global.adaptAnnotations(tree, mode, pt) + if (analyzerPlugins.isEmpty) annotCheckersTree + else analyzerPlugins.foldLeft(annotCheckersTree)((tree, plugin) => + if (!plugin.isActive()) tree else plugin.adaptAnnotations(tree, typer, mode, pt)) + } + + /** @see AnalyzerPlugin.pluginsTypedReturn */ + def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = { + val annotCheckersType = adaptTypeOfReturn(tree.expr, pt, tpe) + if (analyzerPlugins.isEmpty) annotCheckersType + else analyzerPlugins.foldLeft(annotCheckersType)((tpe, plugin) => + if (!plugin.isActive()) tpe else plugin.pluginsTypedReturn(tpe, typer, tree, pt)) + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index af2aeefecd..620665126e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -35,7 +35,7 @@ trait Contexts { self: Analyzer => val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil } - private val startContext = { + private lazy val startContext = { NoContext.make( Template(List(), emptyValDef, List()) setSymbol global.NoSymbol setType global.NoType, rootMirror.RootClass, @@ -342,6 +342,16 @@ trait Contexts { self: Analyzer => c } + /** + * A context for typing constructor parameter ValDefs, super or self invocation arguments and default getters + * of constructors. These expressions need to be type checked in a scope outside the class, cf. spec 5.3.1. + * + * This method is called by namer / typer where `this` is the context for the constructor DefDef. The + * owner of the resulting (new) context is the outer context for the Template, i.e. the context for the + * ClassDef. This means that class type parameters will be in scope. The value parameters of the current + * constructor are also entered into the new constructor scope. Members of the class however will not be + * accessible. + */ def makeConstructorContext = { var baseContext = enclClass.outer while (baseContext.tree.isInstanceOf[Template]) @@ -361,6 +371,8 @@ trait Contexts { self: Analyzer => enterLocalElems(c.scope.elems) } } + // Enter the scope elements of this (the scope for the constructor DefDef) into the new constructor scope. + // Concretely, this will enter the value parameters of constructor. enterElems(this) argContext } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 7dddd320ef..74078a4ed3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -257,8 +257,8 @@ trait Infer extends Checkable { tp1 // @MAT aliases already handled by subtyping } - private val stdErrorClass = rootMirror.RootClass.newErrorClass(tpnme.ERROR) - private val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) + private lazy val stdErrorClass = rootMirror.RootClass.newErrorClass(tpnme.ERROR) + private lazy val stdErrorValue = stdErrorClass.newErrorValue(nme.ERROR) /** The context-dependent inferencer part */ class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index acc4f7ff67..95de2ae4c4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -235,8 +235,8 @@ trait MethodSynthesis { context.unit.synthetics get meth match { case Some(mdef) => context.unit.synthetics -= meth - meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, false) - cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, true) + meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, keepClean = false) + cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, keepClean = true) List(cd, mdef) case _ => // Shouldn't happen, but let's give ourselves a reasonable error when it does @@ -329,6 +329,7 @@ trait MethodSynthesis { */ def category: Symbol + /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter) final def fieldSelection = Select(This(enclClass), basisSym) final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 98b6264051..c728185d4e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -65,7 +65,18 @@ trait Namers extends MethodSynthesis { case ModuleDef(_, _, _) => tree.symbol.moduleClass case _ => tree.symbol } - newNamer(context.makeNewScope(tree, sym)) + def isConstrParam(vd: ValDef) = { + (sym hasFlag PARAM | PRESUPER) && + !vd.mods.isJavaDefined && + sym.owner.isConstructor + } + val ownerCtx = tree match { + case vd: ValDef if isConstrParam(vd) => + context.makeConstructorContext + case _ => + context + } + newNamer(ownerCtx.makeNewScope(tree, sym)) } def createInnerNamer() = { newNamer(context.make(context.tree, owner, newScope)) @@ -423,6 +434,7 @@ trait Namers extends MethodSynthesis { def enterSyms(trees: List[Tree]): Namer = { trees.foldLeft(this: Namer) { (namer, t) => val ctx = namer enterSym t + // for Import trees, enterSym returns a changed context, so we need a new namer if (ctx eq namer.context) namer else newNamer(ctx) } @@ -521,20 +533,19 @@ trait Namers extends MethodSynthesis { noDuplicates(selectors map (_.rename), AppearsTwice) } - def enterCopyMethod(copyDefDef: Tree, tparams: List[TypeDef]): Symbol = { - val sym = copyDefDef.symbol - val lazyType = completerOf(copyDefDef, tparams) + def enterCopyMethod(copyDef: DefDef): Symbol = { + val sym = copyDef.symbol + val lazyType = completerOf(copyDef) /** Assign the types of the class parameters to the parameters of the * copy method. See comment in `Unapplies.caseClassCopyMeth` */ def assignParamTypes() { val clazz = sym.owner val constructorType = clazz.primaryConstructor.tpe - val subst = new SubstSymMap(clazz.typeParams, tparams map (_.symbol)) + val subst = new SubstSymMap(clazz.typeParams, copyDef.tparams map (_.symbol)) val classParamss = constructorType.paramss - val DefDef(_, _, _, copyParamss, _, _) = copyDefDef - map2(copyParamss, classParamss)((copyParams, classParams) => + map2(copyDef.vparamss, classParamss)((copyParams, classParams) => map2(copyParams, classParams)((copyP, classP) => copyP.tpt setType subst(classP.tpe) ) @@ -542,24 +553,28 @@ trait Namers extends MethodSynthesis { } sym setInfo { - mkTypeCompleter(copyDefDef) { sym => + mkTypeCompleter(copyDef) { sym => assignParamTypes() lazyType complete sym } } } - def completerOf(tree: Tree): TypeCompleter = completerOf(tree, treeInfo.typeParameters(tree)) - def completerOf(tree: Tree, tparams: List[TypeDef]): TypeCompleter = { + + def completerOf(tree: Tree): TypeCompleter = { val mono = namerOf(tree.symbol) monoTypeCompleter tree + val tparams = treeInfo.typeParameters(tree) if (tparams.isEmpty) mono else { - //@M! TypeDef's type params are handled differently - //@M e.g., in [A[x <: B], B], A and B are entered first as both are in scope in the definition of x - //@M x is only in scope in `A[x <: B]' + /* @M! TypeDef's type params are handled differently, e.g., in `type T[A[x <: B], B]`, A and B are entered + * first as both are in scope in the definition of x. x is only in scope in `A[x <: B]`. + * No symbols are created for the abstract type's params at this point, i.e. the following assertion holds: + * !tree.symbol.isAbstractType || { tparams.forall(_.symbol == NoSymbol) + * (tested with the above example, `trait C { type T[A[X <: B], B] }`). See also comment in PolyTypeCompleter. + */ if (!tree.symbol.isAbstractType) //@M TODO: change to isTypeMember ? createNamer(tree) enterSyms tparams - new PolyTypeCompleter(tparams, mono, tree, context) //@M + new PolyTypeCompleter(tparams, mono, context) //@M } } @@ -621,9 +636,9 @@ trait Namers extends MethodSynthesis { val sym = assignAndEnterSymbol(tree) setFlag bridgeFlag if (name == nme.copy && sym.isSynthetic) - enterCopyMethod(tree, tparams) + enterCopyMethod(tree) else - sym setInfo completerOf(tree, tparams) + sym setInfo completerOf(tree) } def enterClassDef(tree: ClassDef) { @@ -736,13 +751,13 @@ trait Namers extends MethodSynthesis { } } - def accessorTypeCompleter(tree: ValDef, isSetter: Boolean = false) = mkTypeCompleter(tree) { sym => + /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ + def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym => logAndValidate(sym) { sym setInfo { - if (isSetter) - MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitClass.tpe) - else - NullaryMethodType(typeSig(tree)) + val tp = if (isSetter) MethodType(List(sym.newSyntheticValueParam(typeSig(tree))), UnitClass.tpe) + else NullaryMethodType(typeSig(tree)) + pluginsTypeSigAccessor(tp, typer, tree, sym) } } } @@ -805,17 +820,12 @@ trait Namers extends MethodSynthesis { * assigns the type to the tpt's node. Returns the type. */ private def assignTypeToTree(tree: ValOrDefDef, defnTyper: Typer, pt: Type): Type = { - // compute result type from rhs - val typedBody = + val rhsTpe = if (tree.symbol.isTermMacro) defnTyper.computeMacroDefType(tree, pt) else defnTyper.computeType(tree.rhs, pt) - val typedDefn = widenIfNecessary(tree.symbol, typedBody, pt) - assignTypeToTree(tree, typedDefn) - } - - private def assignTypeToTree(tree: ValOrDefDef, tpe: Type): Type = { - tree.tpt defineType tpe setPos tree.pos.focus + val defnTpe = widenIfNecessary(tree.symbol, rhsTpe, pt) + tree.tpt defineType defnTpe setPos tree.pos.focus tree.tpt.tpe } @@ -892,163 +902,257 @@ trait Namers extends MethodSynthesis { for (cda <- module.attachments.get[ConstructorDefaultsAttachment]) { cda.companionModuleClassNamer = templateNamer } - ClassInfoType(parents, decls, clazz) + val classTp = ClassInfoType(parents, decls, clazz) + pluginsTypeSig(classTp, templateNamer.typer, templ, WildcardType) } - private def classSig(tparams: List[TypeDef], impl: Template): Type = { + private def classSig(cdef: ClassDef): Type = { + val clazz = cdef.symbol + val ClassDef(_, _, tparams, impl) = cdef val tparams0 = typer.reenterTypeParams(tparams) val resultType = templateSig(impl) - GenPolyType(tparams0, resultType) + val res = GenPolyType(tparams0, resultType) + val pluginsTp = pluginsTypeSig(res, typer, cdef, WildcardType) + + // Already assign the type to the class symbol (monoTypeCompleter will do it again). + // Allows isDerivedValueClass to look at the info. + clazz setInfo pluginsTp + if (clazz.isDerivedValueClass) { + log("Ensuring companion for derived value class " + cdef.name + " at " + cdef.pos.show) + clazz setFlag FINAL + // Don't force the owner's info lest we create cycles as in SI-6357. + enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef) + } + pluginsTp } - private def methodSig(ddef: DefDef, mods: Modifiers, tparams: List[TypeDef], - vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): Type = { - val meth = owner - val clazz = meth.owner - // enters the skolemized version into scope, returns the deSkolemized symbols - val tparamSyms = typer.reenterTypeParams(tparams) - // since the skolemized tparams are in scope, the TypeRefs in vparamSymss refer to skolemized tparams - var vparamSymss = enterValueParams(vparamss) + private def moduleSig(mdef: ModuleDef): Type = { + val moduleSym = mdef.symbol + // The info of both the module and the moduleClass symbols need to be assigned. monoTypeCompleter assigns + // the result of typeSig to the module symbol. The module class info is assigned here as a side-effect. + val result = templateSig(mdef.impl) + val pluginsTp = pluginsTypeSig(result, typer, mdef, WildcardType) + // Assign the moduleClass info (templateSig returns a ClassInfoType) + val clazz = moduleSym.moduleClass + clazz setInfo pluginsTp + // clazz.tpe returns a `ModuleTypeRef(clazz)`, a typeRef that links to the module class `clazz` + // (clazz.info would the ClassInfoType, which is not what should be assigned to the module symbol) + clazz.tpe + } + + /** + * The method type for `ddef`. + * + * If a PolyType(tparams, restp) is returned, `tparams` are the external symbols (not type skolems), + * i.e. instances of AbstractTypeSymbol. All references in `restp` to the type parameters are TypeRefs + * to these non-skolems. + * + * For type-checking the rhs (in case the result type is inferred), the type skolems of the type parameters + * are entered in scope. Equally, the parameter symbols entered into scope have types which refer to those + * skolems: when type-checking the rhs, references to parameters need to have types that refer to the skolems. + * In summary, typing an rhs happens with respect to the skolems. + * + * This means that the method's result type computed by the typer refers to skolems. In order to put it + * into the method type (the result of methodSig), typeRefs to skolems have to be replaced by references + * to the non-skolems. + */ + private def methodSig(ddef: DefDef): Type = { // DEPMETTODO: do we need to skolemize value parameter symbols? - if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { - tpt defineType context.enclClass.owner.tpe - tpt setPos meth.pos.focus - } - var resultPt = if (tpt.isEmpty) WildcardType else typer.typedType(tpt).tpe - val site = clazz.thisType - /** Called for all value parameter lists, right to left - * @param vparams the symbols of one parameter list - * @param restpe the result type (possibly a MethodType) + val DefDef(_, _, tparams, vparamss, tpt, _) = ddef + + val meth = owner + val methOwner = meth.owner + val site = methOwner.thisType + + /* tparams already have symbols (created in enterDefDef/completerOf), namely the skolemized ones (created + * by the PolyTypeCompleter constructor, and assigned to tparams). reenterTypeParams enters the type skolems + * into scope and returns the non-skolems. */ - def makeMethodType(vparams: List[Symbol], restpe: Type) = { - // TODODEPMET: check that we actually don't need to do anything here - // new dependent method types: probably OK already, since 'enterValueParams' above - // enters them in scope, and all have a lazy type. so they may depend on other params. but: need to - // check that params only depend on ones in earlier sections, not the same. (done by checkDependencies, - // so re-use / adapt that) - if (owner.isJavaDefined) - // TODODEPMET necessary?? new dependent types: replace symbols in restpe with the ones in vparams - JavaMethodType(vparams map (p => p setInfo objToAny(p.tpe)), restpe) - else - MethodType(vparams, restpe) - } + val tparamSyms = typer.reenterTypeParams(tparams) + + val tparamSkolems = tparams.map(_.symbol) + + /* since the skolemized tparams are in scope, the TypeRefs in types of vparamSymss refer to the type skolems + * note that for parameters with missing types, `methodSig` reassigns types of these symbols (the parameter + * types from the overridden method). + */ + var vparamSymss = enterValueParams(vparamss) + + /** + * Creates a method type using tparamSyms and vparamsSymss as argument symbols and `respte` as result type. + * All typeRefs to type skolems are replaced by references to the corresponding non-skolem type parameter, + * so the resulting type is a valid external method type, it does not contain (references to) skolems. + */ def thisMethodType(restpe: Type) = { val checkDependencies = new DependentTypeChecker(context)(this) checkDependencies check vparamSymss // DEPMETTODO: check not needed when they become on by default checkDependencies(restpe) - GenPolyType( + val makeMethodType = (vparams: List[Symbol], restpe: Type) => { + // TODODEPMET: check that we actually don't need to do anything here + // new dependent method types: probably OK already, since 'enterValueParams' above + // enters them in scope, and all have a lazy type. so they may depend on other params. but: need to + // check that params only depend on ones in earlier sections, not the same. (done by checkDependencies, + // so re-use / adapt that) + if (meth.isJavaDefined) + // TODODEPMET necessary?? new dependent types: replace symbols in restpe with the ones in vparams + JavaMethodType(vparams map (p => p setInfo objToAny(p.tpe)), restpe) + else + MethodType(vparams, restpe) + } + + + val res = GenPolyType( tparamSyms, // deSkolemized symbols -- TODO: check that their infos don't refer to method args? if (vparamSymss.isEmpty) NullaryMethodType(restpe) // vparamss refer (if they do) to skolemized tparams else (vparamSymss :\ restpe) (makeMethodType) ) + res.substSym(tparamSkolems, tparamSyms) } - def transformedResult = - thisMethodType(resultPt).substSym(tparams map (_.symbol), tparamSyms) + /** + * Creates a schematic method type which has WildcardTypes for non specified + * return or parameter types. For instance, in `def f[T](a: T, b) = ...`, the + * type schema is + * + * PolyType(T, MethodType(List(a: T, b: WildcardType), WildcardType)) + * + * where T are non-skolems. + */ + def methodTypeSchema(resTp: Type) = { + // for all params without type set WildcaradType + mforeach(vparamss)(v => if (v.tpt.isEmpty) v.symbol setInfo WildcardType) + thisMethodType(resTp) + } - // luc: added .substSym from skolemized to deSkolemized - // site.memberType(sym): PolyType(tparams, MethodType(..., ...)) - // ==> all references to tparams are deSkolemized - // thisMethodType: tparams in PolyType are deSkolemized, the references in the MethodTypes are skolemized. - // ==> the two didn't match - // - // for instance, B.foo would not override A.foo, and the default on parameter b would not be inherited - // class A { def foo[T](a: T)(b: T = a) = a } - // class B extends A { override def foo[U](a: U)(b: U) = b } - def overriddenSymbol = - intersectionType(clazz.info.parents).nonPrivateMember(meth.name).filter { sym => - sym != NoSymbol && (site.memberType(sym) matches transformedResult) + def overriddenSymbol(resTp: Type) = { + intersectionType(methOwner.info.parents).nonPrivateMember(meth.name).filter { sym => + sym != NoSymbol && (site.memberType(sym) matches methodTypeSchema(resTp)) } - // TODO: see whether this or something similar would work instead. - // + } + // TODO: see whether this or something similar would work instead: // def overriddenSymbol = meth.nextOverriddenSymbol - // fill in result type and parameter types from overridden symbol if there is a unique one. - if (clazz.isClass && (tpt.isEmpty || mexists(vparamss)(_.tpt.isEmpty))) { - // try to complete from matching definition in base type - mforeach(vparamss)(v => if (v.tpt.isEmpty) v.symbol setInfo WildcardType) - val overridden = overriddenSymbol - if (overridden != NoSymbol && !overridden.isOverloaded) { - overridden.cookJavaRawInfo() // #3404 xform java rawtypes into existentials - resultPt = site.memberType(overridden) match { - case PolyType(tparams, rt) => rt.substSym(tparams, tparamSyms) - case mt => mt - } + /** + * If `meth` doesn't have an explicit return type, extracts the return type from the method + * overridden by `meth` (if there's an unique one). This type is lateron used as the expected + * type for computing the type of the rhs. The resulting type references type skolems for + * type parameters (consistent with the result of `typer.typedType(tpt).tpe`). + * + * As a first side effect, this method assigns a MethodType constructed using this + * return type to `meth`. This allows omitting the result type for recursive methods. + * + * As another side effect, this method also assigns paramter types from the overridden + * method to parameters of `meth` that have missing types (the parser accepts missing + * parameter types under -Yinfer-argument-types). + */ + def typesFromOverridden(methResTp: Type): Type = { + val overridden = overriddenSymbol(methResTp) + if (overridden == NoSymbol || overridden.isOverloaded) { + methResTp + } else { + overridden.cookJavaRawInfo() // #3404 xform java rawtypes into existentials + var overriddenTp = site.memberType(overridden) match { + case PolyType(tparams, rt) => rt.substSym(tparams, tparamSkolems) + case mt => mt + } for (vparams <- vparamss) { - var pps = resultPt.params + var overriddenParams = overriddenTp.params for (vparam <- vparams) { if (vparam.tpt.isEmpty) { - val paramtpe = pps.head.tpe - vparam.symbol setInfo paramtpe - vparam.tpt defineType paramtpe setPos vparam.pos.focus + val overriddenParamTp = overriddenParams.head.tpe + // references to type parameteres in overriddenParamTp link to the type skolems, so the + // assigned type is consistent with the other / existing parameter types in vparamSymss. + vparam.symbol setInfo overriddenParamTp + vparam.tpt defineType overriddenParamTp setPos vparam.pos.focus } - pps = pps.tail + overriddenParams = overriddenParams.tail } - resultPt = resultPt.resultType + overriddenTp = overriddenTp.resultType } - resultPt match { - case NullaryMethodType(rtpe) => resultPt = rtpe - case MethodType(List(), rtpe) => resultPt = rtpe + + overriddenTp match { + case NullaryMethodType(rtpe) => overriddenTp = rtpe + case MethodType(List(), rtpe) => overriddenTp = rtpe case _ => } + if (tpt.isEmpty) { // provisionally assign `meth` a method type with inherited result type // that way, we can leave out the result type even if method is recursive. - meth setInfo thisMethodType(resultPt) + meth setInfo thisMethodType(overriddenTp) + overriddenTp + } else { + methResTp } } } - // Add a () parameter section if this overrides some method with () parameters. - if (clazz.isClass && vparamss.isEmpty && overriddenSymbol.alternatives.exists( - _.info.isInstanceOf[MethodType])) { + + if (tpt.isEmpty && meth.name == nme.CONSTRUCTOR) { + tpt defineType context.enclClass.owner.tpe + tpt setPos meth.pos.focus + } + + val methResTp = if (tpt.isEmpty) WildcardType else typer.typedType(tpt).tpe + val resTpFromOverride = if (methOwner.isClass && (tpt.isEmpty || mexists(vparamss)(_.tpt.isEmpty))) { + typesFromOverridden(methResTp) + } else { + methResTp + } + + // Add a () parameter section if this overrides some method with () parameters + if (methOwner.isClass && vparamss.isEmpty && + overriddenSymbol(methResTp).alternatives.exists(_.info.isInstanceOf[MethodType])) { vparamSymss = ListOfNil } + + // issue an error for missing parameter types mforeach(vparamss) { vparam => if (vparam.tpt.isEmpty) { MissingParameterOrValTypeError(vparam) vparam.tpt defineType ErrorType } } - addDefaultGetters(meth, vparamss, tparams, overriddenSymbol) + + addDefaultGetters(meth, vparamss, tparams, overriddenSymbol(methResTp)) // fast track macros, i.e. macros defined inside the compiler, are hardcoded // hence we make use of that and let them have whatever right-hand side they need // (either "macro ???" as they used to or just "???" to maximally simplify their compilation) - if (fastTrack contains ddef.symbol) ddef.symbol setFlag MACRO + if (fastTrack contains meth) meth setFlag MACRO // macro defs need to be typechecked in advance // because @macroImpl annotation only gets assigned during typechecking // otherwise macro defs wouldn't be able to robustly coexist with their clients // because a client could be typechecked before a macro def that it uses - if (ddef.symbol.isTermMacro) { - val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) - typer.computeMacroDefType(ddef, pt) + if (meth.isTermMacro) { + typer.computeMacroDefType(ddef, resTpFromOverride) } - thisMethodType({ + val res = thisMethodType({ val rt = ( if (!tpt.isEmpty) { - typer.typedType(tpt).tpe + methResTp } else { - // replace deSkolemized symbols with skolemized ones - // (for resultPt computed by looking at overridden symbol, right?) - val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) - assignTypeToTree(ddef, typer, pt) - } - ) + // return type is inferred, we don't just use resTpFromOverride. Here, C.f has type String: + // trait T { def f: Object }; class C <: T { def f = "" } + // using resTpFromOverride as expected type allows for the following (C.f has type A): + // trait T { def f: A }; class C <: T { implicit def b2a(t: B): A = ???; def f = new B } + assignTypeToTree(ddef, typer, resTpFromOverride) + }) // #2382: return type of default getters are always @uncheckedVariance if (meth.hasDefault) rt.withAnnotation(AnnotationInfo(uncheckedVarianceClass.tpe, List(), List())) else rt }) + pluginsTypeSig(res, typer, ddef, methResTp) } /** @@ -1060,9 +1164,9 @@ trait Namers extends MethodSynthesis { * flag. */ private def addDefaultGetters(meth: Symbol, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) { - val clazz = meth.owner + val methOwner = meth.owner val isConstr = meth.isConstructor - val overridden = if (isConstr || !clazz.isClass) NoSymbol else overriddenSymbol + val overridden = if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol val overrides = overridden != NoSymbol && !overridden.isOverloaded // value parameters of the base class (whose defaults might be overridden) var baseParamss = (vparamss, overridden.tpe.paramss) match { @@ -1112,7 +1216,7 @@ trait Namers extends MethodSynthesis { val parentNamer = if (isConstr) { val (cdef, nmr) = moduleNamer.getOrElse { - val module = companionSymbolOf(clazz, context) + val module = companionSymbolOf(methOwner, context) module.initialize // call type completer (typedTemplate), adds the // module's templateNamer to classAndNamerOfModule module.attachments.get[ConstructorDefaultsAttachment] match { @@ -1158,7 +1262,7 @@ trait Namers extends MethodSynthesis { name, deftParams, defvParamss, defTpt, defRhs) } if (!isConstr) - clazz.resetFlag(INTERFACE) // there's a concrete member now + methOwner.resetFlag(INTERFACE) // there's a concrete member now val default = parentNamer.enterSyntheticSym(defaultTree) if (forInteractive && default.owner.isTerm) { // save the default getters as attachments in the method symbol. if compiling the @@ -1183,15 +1287,31 @@ trait Namers extends MethodSynthesis { } } + private def valDefSig(vdef: ValDef) = { + val ValDef(_, _, tpt, rhs) = vdef + val result = if (tpt.isEmpty) { + if (rhs.isEmpty) { + MissingParameterOrValTypeError(tpt) + ErrorType + } + else assignTypeToTree(vdef, typer, WildcardType) + } else { + typer.typedType(tpt).tpe + } + pluginsTypeSig(result, typer, vdef, if (tpt.isEmpty) WildcardType else result) + + } + //@M! an abstract type definition (abstract type member/type parameter) // may take type parameters, which are in scope in its bounds - private def typeDefSig(tpsym: Symbol, tparams: List[TypeDef], rhs: Tree) = { + private def typeDefSig(tdef: TypeDef) = { + val TypeDef(_, _, tparams, rhs) = tdef // log("typeDefSig(" + tpsym + ", " + tparams + ")") val tparamSyms = typer.reenterTypeParams(tparams) //@M make tparams available in scope (just for this abstypedef) val tp = typer.typedType(rhs).tpe match { case TypeBounds(lt, rt) if (lt.isError || rt.isError) => TypeBounds.empty - case tp @ TypeBounds(lt, rt) if (tpsym hasFlag JAVA) => + case tp @ TypeBounds(lt, rt) if (tdef.symbol hasFlag JAVA) => TypeBounds(lt, objToAny(rt)) case tp => tp @@ -1213,9 +1333,32 @@ trait Namers extends MethodSynthesis { // However, separate compilation requires the symbol info to be // loaded to do this check, but loading the info will probably // lead to spurious cyclic errors. So omit the check. - GenPolyType(tparamSyms, tp) + val res = GenPolyType(tparamSyms, tp) + pluginsTypeSig(res, typer, tdef, WildcardType) } + private def importSig(imp: Import) = { + val Import(expr, selectors) = imp + val expr1 = typer.typedQualifier(expr) + typer checkStable expr1 + if (expr1.symbol != null && expr1.symbol.isRootPackage) + RootImportError(imp) + + if (expr1.isErrorTyped) + ErrorType + else { + val newImport = treeCopy.Import(imp, expr1, selectors).asInstanceOf[Import] + checkSelectors(newImport) + transformed(imp) = newImport + // copy symbol and type attributes back into old expression + // so that the structure builder will find it. + expr.symbol = expr1.symbol + expr.tpe = expr1.tpe + ImportType(expr1) + } + } + + /** Given a case class * case class C[Ts] (ps: Us) * Add the following methods to toScope: @@ -1239,6 +1382,11 @@ trait Namers extends MethodSynthesis { caseClassCopyMeth(cdef) foreach namer.enterSyntheticSym } + /** + * TypeSig is invoked by monoTypeCompleters. It returns the type of a definition which + * is then assigned to the corresponding symbol (typeSig itself does not need to assign + * the type to the symbol, but it can if necessary). + */ def typeSig(tree: Tree): Type = { // log("typeSig " + tree) /** For definitions, transform Annotation trees to AnnotationInfos, assign @@ -1271,84 +1419,33 @@ trait Namers extends MethodSynthesis { } val sym: Symbol = tree.symbol - // @Lukas: I am not sure this is the right way to do things. - // We used to only decorate the module class with annotations, which is - // clearly wrong. Now we decorate both the class and the object. - // But maybe some annotations are only meant for one of these but not for the other? - // - // TODO: meta-annotations to indicate class vs. object. + + // TODO: meta-annotations to indicate where module annotations should go (module vs moduleClass) annotate(sym) if (sym.isModule) annotate(sym.moduleClass) def getSig = tree match { - case cdef @ ClassDef(_, name, tparams, impl) => - val clazz = tree.symbol - val result = createNamer(tree).classSig(tparams, impl) - clazz setInfo result - if (clazz.isDerivedValueClass) { - log("Ensuring companion for derived value class " + name + " at " + cdef.pos.show) - clazz setFlag FINAL - // Don't force the owner's info lest we create cycles as in SI-6357. - enclosingNamerWithScope(clazz.owner.rawInfo.decls).ensureCompanionObject(cdef) - } - result - - case ModuleDef(_, _, impl) => - val clazz = sym.moduleClass - clazz setInfo createNamer(tree).templateSig(impl) - clazz.tpe - - case ddef @ DefDef(mods, _, tparams, vparamss, tpt, rhs) => - // TODO: cleanup parameter list - createNamer(tree).methodSig(ddef, mods, tparams, vparamss, tpt, rhs) - - case vdef @ ValDef(mods, name, tpt, rhs) => - val isBeforeSupercall = ( - (sym hasFlag PARAM | PRESUPER) - && !mods.isJavaDefined - && sym.owner.isConstructor - ) - val typer1 = typer.constrTyperIf(isBeforeSupercall) - if (tpt.isEmpty) { - if (rhs.isEmpty) { - MissingParameterOrValTypeError(tpt) - ErrorType - } - else assignTypeToTree(vdef, newTyper(typer1.context.make(vdef, sym)), WildcardType) - } - else typer1.typedType(tpt).tpe - - case TypeDef(_, _, tparams, rhs) => - createNamer(tree).typeDefSig(sym, tparams, rhs) //@M! - - case Import(expr, selectors) => - val expr1 = typer.typedQualifier(expr) - typer checkStable expr1 - if (expr1.symbol != null && expr1.symbol.isRootPackage) - RootImportError(tree) - - if (expr1.isErrorTyped) - ErrorType - else { - val newImport = treeCopy.Import(tree, expr1, selectors).asInstanceOf[Import] - checkSelectors(newImport) - transformed(tree) = newImport - // copy symbol and type attributes back into old expression - // so that the structure builder will find it. - expr.symbol = expr1.symbol - expr.tpe = expr1.tpe - ImportType(expr1) - } - } + case cdef: ClassDef => + createNamer(tree).classSig(cdef) + + case mdef: ModuleDef => + createNamer(tree).moduleSig(mdef) + + case ddef: DefDef => + createNamer(tree).methodSig(ddef) - val result = - try getSig - catch typeErrorHandler(tree, ErrorType) + case vdef: ValDef => + createNamer(tree).valDefSig(vdef) - result match { - case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => deskolemizeTypeParams(tparams)(result) - case _ => result + case tdef: TypeDef => + createNamer(tree).typeDefSig(tdef) //@M! + + case imp: Import => + importSig(imp) } + + try getSig + catch typeErrorHandler(tree, ErrorType) } def includeParent(tpe: Type, parent: Symbol): Type = tpe match { @@ -1508,14 +1605,25 @@ trait Namers extends MethodSynthesis { } } - /** A class representing a lazy type with known type parameters. + /** + * A class representing a lazy type with known type parameters. `ctx` is the namer context in which the + * `owner` is defined. + * + * Constructing a PolyTypeCompleter for a DefDef creates type skolems for the type parameters and + * assigns them to the `tparams` trees. */ - class PolyTypeCompleter(tparams: List[TypeDef], restp: TypeCompleter, owner: Tree, ctx: Context) extends LockingTypeCompleter with FlagAgnosticCompleter { - private val ownerSym = owner.symbol - override val typeParams = tparams map (_.symbol) //@M - override val tree = restp.tree + class PolyTypeCompleter(tparams: List[TypeDef], restp: TypeCompleter, ctx: Context) extends LockingTypeCompleter with FlagAgnosticCompleter { + // @M. If `owner` is an abstract type member, `typeParams` are all NoSymbol (see comment in `completerOf`), + // otherwise, the non-skolemized (external) type parameter symbols + override val typeParams = tparams map (_.symbol) + + /* The definition tree (poly ClassDef, poly DefDef or HK TypeDef) */ + override val tree = restp.tree + + private val defnSym = tree.symbol - if (ownerSym.isTerm) { + if (defnSym.isTerm) { + // for polymorphic DefDefs, create type skolems and assign them to the tparam trees. val skolems = deriveFreshSkolems(tparams map (_.symbol)) map2(tparams, skolems)(_ setSymbol _) } @@ -1523,8 +1631,8 @@ trait Namers extends MethodSynthesis { def completeImpl(sym: Symbol) = { // @M an abstract type's type parameters are entered. // TODO: change to isTypeMember ? - if (ownerSym.isAbstractType) - newNamerFor(ctx, owner) enterSyms tparams //@M + if (defnSym.isAbstractType) + newNamerFor(ctx, tree) enterSyms tparams //@M restp complete sym } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index b1e6b8b00a..026c130a87 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -453,12 +453,12 @@ trait Typers extends Modes with Adaptations with Tags { def reenterValueParams(vparamss: List[List[ValDef]]) { for (vparams <- vparamss) for (vparam <- vparams) - vparam.symbol = context.scope enter vparam.symbol + context.scope enter vparam.symbol } def reenterTypeParams(tparams: List[TypeDef]): List[Symbol] = for (tparam <- tparams) yield { - tparam.symbol = context.scope enter tparam.symbol + context.scope enter tparam.symbol tparam.symbol.deSkolemize } @@ -872,7 +872,9 @@ trait Typers extends Modes with Adaptations with Tags { case _ => debuglog("fallback on implicits: " + tree + "/" + resetAllAttrs(original)) val tree1 = typed(resetAllAttrs(original), mode, WildcardType) - tree1.tpe = addAnnotations(tree1, tree1.tpe) + // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that + // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin. + tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, pt) if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, EmptyTree) } else @@ -1077,8 +1079,8 @@ trait Typers extends Modes with Adaptations with Tags { // begin adapt tree.tpe match { - case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (-1) - adaptAnnotations(tree, mode, pt) + case atp @ AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1) + adaptAnnotations(tree, this, mode, pt) case ct @ ConstantType(value) if inNoModes(mode, TYPEmode | FUNmode) && (ct <:< pt) && !forScaladoc && !forInteractive => // (0) val sym = tree.symbol if (sym != null && sym.isDeprecated) { @@ -1182,8 +1184,8 @@ trait Typers extends Modes with Adaptations with Tags { Select(tree, "to" + sym.name) } } - case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, mode, pt) => // (13) - return typed(adaptAnnotations(tree, mode, pt), mode, pt) + case AnnotatedType(_, _, _) if canAdaptAnnotations(tree, this, mode, pt) => // (13) + return typed(adaptAnnotations(tree, this, mode, pt), mode, pt) case _ => } if (!context.undetparams.isEmpty) { @@ -2064,13 +2066,23 @@ trait Typers extends Modes with Adaptations with Tags { * @return ... */ def typedValDef(vdef: ValDef): ValDef = { -// attributes(vdef) + val sym = vdef.symbol + val valDefTyper = { + val maybeConstrCtx = + if ((sym.isParameter || sym.isEarlyInitialized) && sym.owner.isConstructor) context.makeConstructorContext + else context + newTyper(maybeConstrCtx.makeNewScope(vdef, sym)) + } + valDefTyper.typedValDefImpl(vdef) + } + + // use typedValDef instead. this version is called after creating a new context for the ValDef + private def typedValDefImpl(vdef: ValDef) = { val sym = vdef.symbol.initialize - val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor) val typedMods = typedModifiers(vdef.mods) sym.annotations.map(_.completeInfo) - var tpt1 = checkNoEscaping.privates(sym, typer1.typedType(vdef.tpt)) + val tpt1 = checkNoEscaping.privates(sym, typedType(vdef.tpt)) checkNonCyclic(vdef, tpt1) if (sym.hasAnnotation(definitions.VolatileAttr) && !sym.isMutable) @@ -2098,7 +2110,7 @@ trait Typers extends Modes with Adaptations with Tags { else subst(tpt1.tpe.typeArgs(0)) else subst(tpt1.tpe) } else tpt1.tpe - newTyper(typer1.context.make(vdef, sym)).transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) + transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2) } treeCopy.ValDef(vdef, typedMods, vdef.name, tpt1, checkDead(rhs1)) setType NoType } @@ -2397,13 +2409,12 @@ trait Typers extends Modes with Adaptations with Tags { } def typedTypeDef(tdef: TypeDef): TypeDef = - typerWithCondLocalContext(context.makeNewScope(tdef, tdef.symbol))(tdef.tparams.nonEmpty){ - _.typedTypeDef0(tdef) + typerWithCondLocalContext(context.makeNewScope(tdef, tdef.symbol))(tdef.tparams.nonEmpty) { + _.typedTypeDefImpl(tdef) } - // call typedTypeDef instead - // a TypeDef with type parameters must always be type checked in a new scope - private def typedTypeDef0(tdef: TypeDef): TypeDef = { + // use typedTypeDef instead. this version is called after creating a new context for the TypeDef + private def typedTypeDefImpl(tdef: TypeDef): TypeDef = { tdef.symbol.initialize reenterTypeParams(tdef.tparams) val tparams1 = tdef.tparams mapConserve typedTypeDef @@ -4458,8 +4469,9 @@ trait Typers extends Modes with Adaptations with Tags { if (typed(expr).tpe.typeSymbol != UnitClass) unit.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded") } - treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner) - .setType(adaptTypeOfReturn(expr1, restpt.tpe, NothingClass.tpe)) + val res = treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner) + val tp = pluginsTypedReturn(NothingClass.tpe, this, res, restpt.tpe) + res.setType(tp) } } } @@ -5363,10 +5375,14 @@ trait Typers extends Modes with Adaptations with Tags { typed(docdef.definition, mode, pt) } + /** + * The typer with the correct context for a method definition. If the method is a default getter for + * a constructor default, the resulting typer has a constructor context (fixes SI-5543). + */ def defDefTyper(ddef: DefDef) = { - val flag = ddef.mods.hasDefaultFlag && sym.owner.isModuleClass && + val isConstrDefaultGetter = ddef.mods.hasDefaultFlag && sym.owner.isModuleClass && nme.defaultGetterToMethod(sym.name) == nme.CONSTRUCTOR - newTyper(context.makeNewScope(ddef, sym)).constrTyperIf(flag) + newTyper(context.makeNewScope(ddef, sym)).constrTyperIf(isConstrDefaultGetter) } def typedAlternative(alt: Alternative) = { @@ -5653,20 +5669,21 @@ trait Typers extends Modes with Adaptations with Tags { lastTreeToTyper = tree indentTyping() - var alreadyTyped = false + val ptPlugins = pluginsPt(pt, this, tree, mode) + val startByType = if (Statistics.canEnable) Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null if (Statistics.canEnable) Statistics.incCounter(visitsByType, tree.getClass) try { if (context.retyping && - (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) { + (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))) { tree.tpe = null if (tree.hasSymbol) tree.symbol = NoSymbol } - alreadyTyped = tree.tpe ne null + val alreadyTyped = tree.tpe ne null var tree1: Tree = if (alreadyTyped) tree else { printTyping( - ptLine("typing %s: pt = %s".format(ptTree(tree), pt), + ptLine("typing %s: pt = %s".format(ptTree(tree), ptPlugins), "undetparams" -> context.undetparams, "implicitsEnabled" -> context.implicitsEnabled, "enrichmentEnabled" -> context.enrichmentEnabled, @@ -5675,7 +5692,7 @@ trait Typers extends Modes with Adaptations with Tags { "context.owner" -> context.owner ) ) - typed1(tree, mode, dropExistential(pt)) + typed1(tree, mode, dropExistential(ptPlugins)) } // Can happen during erroneous compilation - error(s) have been // reported, but we need to avoid causing an NPE with this tree @@ -5689,12 +5706,12 @@ trait Typers extends Modes with Adaptations with Tags { ) } - tree1.tpe = addAnnotations(tree1, tree1.tpe) - val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt, tree) + tree1.tpe = pluginsTyped(tree1.tpe, this, tree1, mode, ptPlugins) + val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, ptPlugins, tree) if (!alreadyTyped) { printTyping("adapted %s: %s to %s, %s".format( - tree1, tree1.tpe.widen, pt, context.undetparamsString) + tree1, tree1.tpe.widen, ptPlugins, context.undetparamsString) ) //DEBUG } if (!isPastTyper) signalDone(context.asInstanceOf[analyzer.Context], tree, result) @@ -5709,7 +5726,7 @@ trait Typers extends Modes with Adaptations with Tags { setError(tree) case ex: Exception => if (settings.debug.value) // @M causes cyclic reference error - Console.println("exception when typing "+tree+", pt = "+pt) + Console.println("exception when typing "+tree+", pt = "+ptPlugins) if (context != null && context.unit.exists && tree != null) logError("AT: " + (tree.pos).dbgString, ex) throw ex diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 5782d7bbca..8f607e9f67 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -112,7 +112,7 @@ trait Unapplies extends ast.TreeDSL def createFun = gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) def parents = if (inheritFromFun) List(createFun) else Nil def toString = DefDef( - Modifiers(OVERRIDE | FINAL), + Modifiers(OVERRIDE | FINAL | SYNTHETIC), nme.toString_, Nil, ListOfNil, |