diff options
Diffstat (limited to 'src/compiler')
7 files changed, 166 insertions, 117 deletions
diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 12b083b4f6..26b6a3cd14 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -921,7 +921,9 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val GetterTargetClass = getMetaAnnotation("getter") lazy val ParamTargetClass = getMetaAnnotation("param") lazy val SetterTargetClass = getMetaAnnotation("setter") - // TODO: module, moduleClass? package, packageObject? + lazy val ClassTargetClass = getMetaAnnotation("companionClass") + lazy val ObjectTargetClass = getMetaAnnotation("companionObject") + lazy val MethodTargetClass = getMetaAnnotation("companionMethod") // TODO: module, moduleClass? package, packageObject? private def getMetaAnnotation(name: String) = getRequiredClass("scala.annotation.meta." + name) def isMetaAnnotation(sym: Symbol): Boolean = metaAnnotations(sym) || ( @@ -1009,7 +1011,7 @@ trait Definitions extends reflect.api.StandardDefinitions { } def getDeclIfDefined(owner: Symbol, name: Name): Symbol = owner.info.nonPrivateDecl(name) - + def packageExists(packageName: String): Boolean = getModuleIfDefined(packageName).isPackage diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index b400743469..edc69be827 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -834,7 +834,7 @@ trait ContextErrors { implicit val context0 = context object SymValidateErrors extends Enumeration { - val ImplicitConstr, ImplicitNotTerm, ImplicitTopObject, + val ImplicitConstr, ImplicitNotTermOrClass, ImplicitAtToplevel, OverrideClass, SealedNonClass, AbstractNonClass, OverrideConstr, AbstractOverride, LazyAndEarlyInit, ByNameParameter, AbstractVar = Value @@ -913,10 +913,10 @@ trait ContextErrors { case ImplicitConstr => "`implicit' modifier not allowed for constructors" - case ImplicitNotTerm => - "`implicit' modifier can be used only for values, variables and methods" + case ImplicitNotTermOrClass => + "`implicit' modifier can be used only for values, variables, methods and classes" - case ImplicitTopObject => + case ImplicitAtToplevel => "`implicit' modifier cannot be used for top-level objects" case OverrideClass => diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 6382e5a847..e1c12adbcc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -67,7 +67,24 @@ trait MethodSynthesis { val params = owner newSyntheticValueParams formals MethodType(params, formals.last) } - } + + /** The annotations amongst those found on the original symbol which + * should be propagated to this kind of accessor. + */ + def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = { + initial filter { ann => + // There are no meta-annotation arguments attached to `ann` + if (ann.metaAnnotations.isEmpty) { + // A meta-annotation matching `annotKind` exists on `ann`'s definition. + (ann.defaultTargets contains category) || + // `ann`'s definition has no meta-annotations, and `keepClean` is true. + (ann.defaultTargets.isEmpty && keepClean) + } + // There are meta-annotation arguments, and one of them matches `annotKind` + else ann.metaAnnotations exists (_ matches category) + } + } + } import synthesisUtil._ class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) { @@ -157,28 +174,23 @@ trait MethodSynthesis { /** There are two key methods in here. * - * 1) enterGetterSetter is called from Namer with a ValDef which - * may need accessors. Some setup is performed. In general this - * creates symbols and enters them into the scope of the owner. + * 1) Enter methods such as enterGetterSetterare called + * from Namer with a tree which may generate further trees such as accessors or + * implicit wrappers. Some setup is performed. In general this creates symbols + * and enters them into the scope of the owner. * - * 2) finishGetterSetter is called from Typer when a Template is typed. + * 2) addDerivedTrees is called from Typer when a Template is typed. * It completes the job, returning a list of trees with their symbols - * set to those created in enterGetterSetter. Those trees then become + * set to those created in the enter methods. Those trees then become * part of the typed template. */ trait MethodSynth { self: Namer => import NamerErrorGen._ - - /** TODO - synthesize method. - */ - def enterImplicitClass(tree: ClassDef) { - /** e.g. - val ClassDef(mods, name, tparams, impl) = tree - val converter = ImplicitClassConverter(tree).createAndEnterSymbol() - ... - */ + + def enterImplicitWrapper(tree: ClassDef) { + ImplicitClassWrapper(tree).createAndEnterSymbol() } def enterGetterSetter(tree: ValDef) { @@ -205,7 +217,8 @@ trait MethodSynthesis { enterBeans(tree) } - def finishGetterSetter(typer: Typer, stat: Tree): List[Tree] = stat match { + + def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match { case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) && !vd.symbol.isLazy => // If we don't save the annotations, they seem to wander off. val annotations = stat.symbol.initialize.annotations @@ -213,9 +226,19 @@ trait MethodSynthesis { map (acc => atPos(vd.pos.focus)(acc derive annotations)) filterNot (_ eq EmptyTree) ) + case cd @ ClassDef(mods, _, _, _) if mods.isImplicit => + val annotations = stat.symbol.initialize.annotations + // TODO: need to shuffle annotations between wrapper and class. + val wrapper = ImplicitClassWrapper(cd) + val meth = wrapper.derivedSym + val mdef = context.unit.synthetics(meth) + context.unit.synthetics -= meth + meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, false) + cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, true) + List(cd, mdef) case _ => List(stat) - } + } def standardAccessors(vd: ValDef): List[DerivedFromValDef] = ( if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd)) @@ -236,35 +259,59 @@ trait MethodSynthesis { field ::: standardAccessors(vd) ::: beanAccessors(vd) } + /** This trait assembles what's needed for synthesizing derived methods. + * Important: Typically, instances of this trait are created TWICE for each derived + * symbol; once form Namers in an enter method, and once from Typers in addDerivedTrees. + * So it's important that creating an instance of Derived does not have a side effect, + * or if it has a side effect, control that it is done only once. + */ trait Derived { - /** The tree from which we are deriving a synthetic member. */ + + /** The tree from which we are deriving a synthetic member. Typically, that's + * given as an argument of the instance. */ def tree: Tree + + /** The name of the method */ def name: TermName + + /** The flags that are retained from the original symbol */ + def flagsMask: Long + + /** The flags that the derived symbol has in addition to those retained from + * the original symbol*/ def flagsExtra: Long - - /** The tree, symbol, and type completer for the synthetic member. */ + + /** type completer for the synthetic member. + */ def completer(sym: Symbol): Type + + /** The derived symbol. It is assumed that this symbol already exists and has been + * entered in the parent scope when derivedSym is called */ def derivedSym: Symbol + + /** The definition tree of the derived symbol. */ def derivedTree: Tree } - + trait DerivedFromMemberDef extends Derived { def tree: MemberDef - + def enclClass: Symbol + // Final methods to make the rest easier to reason about. final def mods = tree.mods final def basisSym = tree.symbol - final def enclClass = basisSym.enclClass final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra } - + trait DerivedFromClassDef extends DerivedFromMemberDef { def tree: ClassDef + final def enclClass = basisSym.owner.enclClass } trait DerivedFromValDef extends DerivedFromMemberDef { def tree: ValDef + final def enclClass = basisSym.enclClass /** Which meta-annotation is associated with this kind of entity. * Presently one of: field, getter, setter, beanGetter, beanSetter, param. @@ -288,22 +335,6 @@ trait MethodSynthesis { enterInScope(sym) sym setInfo completer(sym) } - /** The annotations amongst those found on the original symbol which - * should be propagated to this kind of accessor. - */ - private def deriveAnnotations(initial: List[AnnotationInfo]): List[AnnotationInfo] = { - initial filter { ann => - // There are no meta-annotation arguments attached to `ann` - if (ann.metaAnnotations.isEmpty) { - // A meta-annotation matching `annotKind` exists on `ann`'s definition. - (ann.defaultTargets contains category) || - // `ann`'s definition has no meta-annotations, and `keepClean` is true. - (ann.defaultTargets.isEmpty && keepClean) - } - // There are meta-annotation arguments, and one of them matches `annotKind` - else ann.metaAnnotations exists (_ matches category) - } - } private def logDerived(result: Tree): Tree = { debuglog("[+derived] " + ojoin(mods.flagString, basisSym.accurateKindString, basisSym.getterName.decode) + " (" + derivedSym + ")\n " + result) @@ -312,7 +343,7 @@ trait MethodSynthesis { } final def derive(initial: List[AnnotationInfo]): Tree = { validate() - derivedSym setAnnotations deriveAnnotations(initial) + derivedSym setAnnotations deriveAnnotations(initial, category, keepClean) logDerived(derivedTree) } } @@ -336,15 +367,21 @@ trait MethodSynthesis { /** A synthetic method which performs the implicit conversion implied by * the declaration of an implicit class. Yet to be written. */ - case class ImplicitClassConverter(tree: ClassDef) extends DerivedFromClassDef { - def completer(sym: Symbol): Type = ??? - def derivedSym: Symbol = ??? - def derivedTree: DefDef = ??? - def flagsExtra: Long = ??? - def flagsMask: Long = ??? - def name: TermName = ??? - } - + case class ImplicitClassWrapper(tree: ClassDef) extends DerivedFromClassDef { + def completer(sym: Symbol): Type = ??? // not needed + def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree) + def derivedSym: Symbol = { + val result = enclClass.info decl name + assert(result != NoSymbol, "not found: "+name+" in "+enclClass+" "+enclClass.info.decls) + result + } + def derivedTree: DefDef = + factoryMeth(mods & flagsMask | flagsExtra, name, tree, symbolic = false) + def flagsExtra: Long = METHOD | IMPLICIT + def flagsMask: Long = AccessFlags + def name: TermName = tree.name.toTermName + } + case class Getter(tree: ValDef) extends DerivedGetter { def name = tree.name def category = GetterTargetClass diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 696952fe6a..ffd00751e0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -99,7 +99,7 @@ trait Namers extends MethodSynthesis { owner.unsafeTypeParams foreach (paramContext.scope enter _) newNamer(paramContext) } - + def enclosingNamerWithScope(scope: Scope) = { var cx = context while (cx != NoContext && cx.scope != scope) cx = cx.outer @@ -653,10 +653,12 @@ trait Namers extends MethodSynthesis { "If possible, define " + tree.symbol + " in " + owner.skipPackageObject + " instead." ) } - + // Suggested location only. - if (mods.isImplicit) - enterImplicitClass(tree) + if (mods.isImplicit) { + log("enter implicit wrapper "+tree+", owner = "+owner) + enterImplicitWrapper(tree) + } } // this logic is needed in case typer was interrupted half @@ -671,7 +673,7 @@ trait Namers extends MethodSynthesis { val acc = sym.lazyAccessor if (acc != NoSymbol) enterIfNotThere(acc) } - defaultParametersOfMethod(sym) foreach enterIfNotThere + defaultParametersOfMethod(sym) foreach { symRef => enterIfNotThere(symRef()) } } this.context } @@ -1156,7 +1158,7 @@ trait Namers extends MethodSynthesis { // if compiling the same local block several times (which can happen in interactive mode) // we might otherwise not find the default symbol, because the second time it the // method symbol will be re-entered in the scope but the default parameter will not. - defaultParametersOfMethod(meth) += default + defaultParametersOfMethod(meth) += new WeakReference(default) } } else if (baseHasDefault) { // the parameter does not have a default itself, but the @@ -1399,10 +1401,10 @@ trait Namers extends MethodSynthesis { if (sym.isImplicit) { if (sym.isConstructor) fail(ImplicitConstr) - if (!sym.isTerm) - fail(ImplicitNotTerm) + if (!(sym.isTerm || (sym.isClass && !sym.isTrait))) + fail(ImplicitNotTermOrClass) if (sym.owner.isPackageClass) - fail(ImplicitTopObject) + fail(ImplicitAtToplevel) } if (sym.isClass) { if (sym.isAnyOverride && !sym.hasFlag(TRAIT)) diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index 2573678f8c..4d84bf4af2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -8,6 +8,7 @@ package typechecker import symtab.Flags._ import scala.collection.mutable +import scala.ref.WeakReference /** * @author Lukas Rytz @@ -20,7 +21,7 @@ trait NamesDefaults { self: Analyzer => import NamesDefaultsErrorsGen._ val defaultParametersOfMethod = - perRunCaches.newWeakMap[Symbol, Set[Symbol]]() withDefaultValue Set() + perRunCaches.newWeakMap[Symbol, Set[WeakReference[Symbol]]]() withDefaultValue Set() case class NamedApplyInfo( qual: Option[Tree], diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 5d9b8ae94a..76ea68442f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1655,7 +1655,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val body = if (isPastTyper || reporter.hasErrors) templ.body - else templ.body flatMap rewrappingWrapperTrees(namer.finishGetterSetter(Typer.this, _)) + else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) val body1 = typedStats(body, templ.symbol) @@ -2206,7 +2206,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { vparams map {p => if(p.tpt.tpe == null) typedType(p.tpt).tpe else p.tpt.tpe} } - def mkParams(methodSym: Symbol, formals: List[Type]/* = deriveFormals*/) = { + def mkParams(methodSym: Symbol, formals: List[Type] = deriveFormals) = { selOverride match { case None if targs.isEmpty => MissingParameterTypeAnonMatchError(tree, pt); (Nil, EmptyTree) case None => @@ -2232,7 +2232,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // rig the show so we can get started typing the method body -- later we'll correct the infos... anonClass setInfo ClassInfoType(List(ObjectClass.tpe, pt, SerializableClass.tpe), newScope, anonClass) val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym, deriveFormals) + val (paramSyms, selector) = mkParams(methodSym) if (selector eq EmptyTree) EmptyTree else { @@ -2296,7 +2296,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def isDefinedAtMethod = { val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos, FINAL) - val (paramSyms, selector) = mkParams(methodSym, deriveFormals) + val (paramSyms, selector) = mkParams(methodSym) if (selector eq EmptyTree) EmptyTree else { val methodBodyTyper = newTyper(context.makeNewScope(context.tree, methodSym)) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) @@ -2478,54 +2478,53 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { || (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate) ) - def checkNoDoubleDefsAndAddSynthetics(stats: List[Tree]): List[Tree] = { + def checkNoDoubleDefs(stats: List[Tree]): Unit = { val scope = if (inBlock) context.scope else context.owner.info.decls - var newStats = new ListBuffer[Tree] - var needsCheck = true - var moreToAdd = true - while (moreToAdd) { - val initSize = scope.size - var e = scope.elems - while ((e ne null) && e.owner == scope) { - - // check no double def - if (needsCheck) { - var e1 = scope.lookupNextEntry(e) - while ((e1 ne null) && e1.owner == scope) { - if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) && - (e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe))) - // default getters are defined twice when multiple overloads have defaults. an - // error for this is issued in RefChecks.checkDefaultsInOverloaded - if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefault && - !e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) { - log("Double definition detected:\n " + - ((e.sym.getClass, e.sym.info, e.sym.ownerChain)) + "\n " + - ((e1.sym.getClass, e1.sym.info, e1.sym.ownerChain))) - - DefDefinedTwiceError(e.sym, e1.sym) - scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779 - } - e1 = scope.lookupNextEntry(e1) + var e = scope.elems + while ((e ne null) && e.owner == scope) { + var e1 = scope.lookupNextEntry(e) + while ((e1 ne null) && e1.owner == scope) { + if (!accesses(e.sym, e1.sym) && !accesses(e1.sym, e.sym) && + (e.sym.isType || inBlock || (e.sym.tpe matches e1.sym.tpe))) + // default getters are defined twice when multiple overloads have defaults. an + // error for this is issued in RefChecks.checkDefaultsInOverloaded + if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag && + !e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) { + log("Double definition detected:\n " + + ((e.sym.getClass, e.sym.info, e.sym.ownerChain)) + "\n " + + ((e1.sym.getClass, e1.sym.info, e1.sym.ownerChain))) + + DefDefinedTwiceError(e.sym, e1.sym) + scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779 } - } - - // add synthetics - context.unit.synthetics get e.sym foreach { tree => - newStats += typedStat(tree) // might add even more synthetics to the scope - context.unit.synthetics -= e.sym + e1 = scope.lookupNextEntry(e1) } - e = e.next } - needsCheck = false - // the type completer of a synthetic might add more synthetics. example: if the - // factory method of a case class (i.e. the constructor) has a default. - moreToAdd = initSize != scope.size + } + + def addSynthetics(stats: List[Tree]): List[Tree] = { + val scope = if (inBlock) context.scope else context.owner.info.decls + var newStats = new ListBuffer[Tree] + var moreToAdd = true + while (moreToAdd) { + val initElems = scope.elems + for (sym <- scope) + for (tree <- context.unit.synthetics get sym) { + newStats += typedStat(tree) // might add even more synthetics to the scope + context.unit.synthetics -= sym + } + // the type completer of a synthetic might add more synthetics. example: if the + // factory method of a case class (i.e. the constructor) has a default. + moreToAdd = scope.elems ne initElems } if (newStats.isEmpty) stats else { // put default getters next to the method they belong to, // same for companion objects. fixes #2489 and #4036. + // [Martin] This is pretty ugly. I think we could avoid + // this code by associating defaults and companion objects + // with the original tree instead of the new symbol. def matches(stat: Tree, synt: Tree) = (stat, synt) match { case (DefDef(_, statName, _, _, _, _), DefDef(mods, syntName, _, _, _, _)) => mods.hasDefaultFlag && syntName.toString.startsWith(statName.toString) @@ -2555,7 +2554,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } context.updateBuffer(statsErrors) if (phase.erasedTypes) stats1 - else checkNoDoubleDefsAndAddSynthetics(stats1) + else { + checkNoDoubleDefs(stats1) + addSynthetics(stats1) + } } def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = { @@ -4655,12 +4657,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { // [Eugene] no more MaxArrayDims. ClassTags are flexible enough to allow creation of arrays of arbitrary dimensionality (w.r.t JVM restrictions) val Some((level, componentType)) = erasure.GenericArray.unapply(tpt.tpe) val tagType = List.iterate(componentType, level)(tpe => appliedType(ArrayClass.asType, List(tpe))).last - val newArrayApp = atPos(tree.pos) { + val newArrayApp = atPos(tree.pos) { val tag = resolveClassTag(tree, tagType) if (tag.isEmpty) MissingClassTagError(tree, tagType) else new ApplyToImplicitArgs(Select(tag, nme.newArray), args) - } - typed(newArrayApp, mode, pt) + } + typed(newArrayApp, mode, pt) case tree1 => tree1 } diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 4f5b6868ae..1f79d8212d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -112,8 +112,8 @@ trait Unapplies extends ast.TreeDSL private def toIdent(x: DefTree) = Ident(x.name) setPos x.pos.focus - private def classType(cdef: ClassDef, tparams: List[TypeDef]): Tree = { - val tycon = REF(cdef.symbol) + private def classType(cdef: ClassDef, tparams: List[TypeDef], symbolic: Boolean = true): Tree = { + val tycon = if (symbolic) REF(cdef.symbol) else Ident(cdef.name) if (tparams.isEmpty) tycon else AppliedTypeTree(tycon, tparams map toIdent) } @@ -166,15 +166,20 @@ trait Unapplies extends ast.TreeDSL /** The apply method corresponding to a case class */ - def caseModuleApplyMeth(cdef: ClassDef): DefDef = { + def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef, symbolic: Boolean): DefDef = { val tparams = cdef.tparams map copyUntypedInvariant val cparamss = constrParamss(cdef) + def classtpe = classType(cdef, tparams, symbolic) atPos(cdef.pos.focus)( - DefDef(caseMods, nme.apply, tparams, cparamss, classType(cdef, tparams), - New(classType(cdef, tparams), mmap(cparamss)(gen.paramToArg))) + DefDef(mods, name, tparams, cparamss, classtpe, + New(classtpe, mmap(cparamss)(gen.paramToArg))) ) } + /** The apply method corresponding to a case class + */ + def caseModuleApplyMeth(cdef: ClassDef): DefDef = factoryMeth(caseMods, nme.apply, cdef, symbolic = true) + /** The unapply method corresponding to a case class */ def caseModuleUnapplyMeth(cdef: ClassDef): DefDef = { |