diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 35 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala | 136 |
2 files changed, 94 insertions, 77 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index d0f1722b1c..5073cd02ae 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -497,25 +497,30 @@ trait Namers { self: Analyzer => finish case vd @ ValDef(mods, name, tp, rhs) => - if ((!context.owner.isClass || - (mods.isPrivateLocal && !mods.isCaseAccessor) || - name.startsWith(nme.OUTER) || - context.unit.isJava) && - !mods.isLazy) { - val vsym = owner.newValue(tree.pos, name).setFlag(mods.flags); - if(context.unit.isJava) setPrivateWithin(tree, vsym, mods) // #3663 -- for Scala fields we assume private[this] + val needsNoAccessors = !mods.isLazy && ( + !context.owner.isClass + || (mods.isPrivateLocal && !mods.isCaseAccessor) + || name.startsWith(nme.OUTER) + || context.unit.isJava + ) + + if (needsNoAccessors) { + val vsym = owner.newValue(tree.pos, name).setFlag(mods.flags) + if (context.unit.isJava) setPrivateWithin(tree, vsym, mods) // #3663 -- for Scala fields we assume private[this] tree.symbol = enterInScope(vsym) finish - } else { - val mods1 = - if (mods.isPrivateLocal && !mods.isLazy) { - context.error(tree.pos, "private[this] not allowed for case class parameters") - mods &~ LOCAL - } else mods + } + else { + val mods1 = ( + if (mods.isPrivateLocal && !mods.isLazy) { + context.error(tree.pos, "private[this] not allowed for case class parameters") + mods &~ LOCAL + } + else mods + ) // add getter and possibly also setter if (nme.isSetterName(name)) context.error(tree.pos, "Names of vals or vars may not end in `_='") - // .isInstanceOf[..]: probably for (old) IDE hook. is this obsolete? val getter = enterAccessorMethod(tree, name, getterFlags(mods1.flags), mods1) setInfo(getter)(namerOf(getter).getterTypeCompleter(vd)) if (mods1.isMutable) { @@ -529,7 +534,7 @@ trait Namers { self: Analyzer => } else { val vsym = if (!context.owner.isClass) { - assert(mods1.isLazy) // if not a field, it has to be a lazy val + assert(mods1.isLazy, mods1) // if not a field, it has to be a lazy val owner.newValue(tree.pos, name + "$lzy" ).setFlag((mods1.flags | MUTABLE) & ~IMPLICIT) } else { val mFlag = if (mods1.isLazy) MUTABLE else 0 diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 89b6eb9759..95fb104f5f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -124,7 +124,6 @@ trait SyntheticMethods extends ast.TreeDSL { m setInfo infoFn(m) finishMethod(m, f) } - private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = { val m = original.cloneSymbol(clazz) setPos clazz.pos.focus m.name = name @@ -199,8 +198,15 @@ trait SyntheticMethods extends ast.TreeDSL { ) import synthesizer._ - def accessors = clazz.caseFieldAccessors - def arity = accessors.size + val originalAccessors = clazz.caseFieldAccessors + // private ones will have been renamed -- make sure they are entered + // in the original order. + def accessors = clazz.caseFieldAccessors sortBy { acc => + originalAccessors indexWhere { orig => + (acc.name == orig.name) || (acc.name startsWith (orig.name + "$").toTermName) + } + } + val arity = accessors.size // If this is ProductN[T1, T2, ...], accessorLub is the lub of T1, T2, ..., . // !!! Hidden behind -Xexperimental due to bummer type inference bugs. // Refining from Iterator[Any] leads to types like @@ -299,28 +305,6 @@ trait SyntheticMethods extends ast.TreeDSL { argsBody } - def newAccessorMethod(ddef: DefDef): Tree = { - deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc => - makeMethodPublic(newAcc) - newAcc resetFlag (ACCESSOR | PARAMACCESSOR) - ddef.rhs.duplicate - } - } - - // A buffer collecting additional methods for the template body - val ts = new ListBuffer[Tree] - - def makeAccessorsPublic() { - // If this case class has fields with less than public visibility, their getter at this - // point also has those permissions. In that case we create a new, public accessor method - // with a new name and remove the CASEACCESSOR flag from the existing getter. This complicates - // the retrieval of the case field accessors (see def caseFieldAccessors in Symbols.) - def needsService(s: Symbol) = s.isMethod && s.isCaseAccessor && !s.isPublic - for (ddef @ DefDef(_, _, _, _, _, _) <- templ.body; if needsService(ddef.symbol)) { - ts += newAccessorMethod(ddef) - ddef.symbol resetFlag CASEACCESSOR - } - } /** The _1, _2, etc. methods to implement ProductN. */ def productNMethods = { @@ -355,48 +339,76 @@ trait SyntheticMethods extends ast.TreeDSL { // Object_equals -> (() => createMethod(Object_equals)(m => This(clazz) ANY_EQ methodArg(m, 0))) ) - def addReadResolve() { - /** If you serialize a singleton and then deserialize it twice, - * you will have two instances of your singleton, unless you implement - * the readResolve() method (see http://www.javaworld.com/javaworld/ - * jw-04-2003/jw-0425-designpatterns_p.html) - */ - if (clazz.isSerializable && !hasConcreteImpl(nme.readResolve)) { - // Aha, I finally decoded the original comment. - // This method should be generated as private, but apparently if it is, then - // it is name mangled afterward. (Wonder why that is.) So it's only protected. - // For sure special methods like "readResolve" should not be mangled. - ts += createMethod(nme.readResolve, Nil, ObjectClass.tpe) { m => - m setFlag PRIVATE - REF(clazz.sourceModule) - } - } - } + /** If you serialize a singleton and then deserialize it twice, + * you will have two instances of your singleton, unless you implement + * the readResolve() method (see http://www.javaworld.com/javaworld/ + * jw-04-2003/jw-0425-designpatterns_p.html) + */ - def synthesize() = { - if (clazz.isCase) { - makeAccessorsPublic() - val methods = if (clazz.isModuleClass) caseObjectMethods else caseClassMethods + // Only nested objects inside objects should get readResolve automatically. + // Otherwise, after de-serialization we get null references for lazy accessors + // (nested object -> lazy val + class def) since the bitmap gets serialized but + // the moduleVar not. + def needsReadResolve = ( + clazz.isModuleClass + && clazz.owner.isModuleClass + && clazz.isSerializable + && !hasConcreteImpl(nme.readResolve) + ) - for ((m, impl) <- methods ; if !hasOverridingImplementation(m)) - ts += impl() - } - // Only nested objects inside objects should get readResolve automatically. - // Otherwise, after de-serialization we get null references for lazy accessors - // (nested object -> lazy val + class def) since the bitmap gets serialized but - // the moduleVar not. - if (clazz.isModuleClass && clazz.owner.isModuleClass) - addReadResolve() + def synthesize(): List[Tree] = { + val methods = ( + if (!clazz.isCase) Nil + else if (clazz.isModuleClass) caseObjectMethods + else caseClassMethods + ) + def impls = for ((m, impl) <- methods ; if !hasOverridingImplementation(m)) yield impl() + def extras = ( + if (needsReadResolve) { + // Aha, I finally decoded the original comment. + // This method should be generated as private, but apparently if it is, then + // it is name mangled afterward. (Wonder why that is.) So it's only protected. + // For sure special methods like "readResolve" should not be mangled. + List(createMethod(nme.readResolve, Nil, ObjectClass.tpe)(m => { m setFlag PRIVATE ; REF(clazz.sourceModule) })) + } + else Nil + ) + + try impls ++ extras + catch { case _: TypeError if reporter.hasErrors => Nil } } - try synthesize() - catch { case _: TypeError if reporter.hasErrors => () } + /** If this case class has any less than public accessors, + * adds new accessors at the correct locations to preserve ordering. + * Note that this must be done before the other method synthesis + * because synthesized methods need refer to the new symbols. + * Care must also be taken to preserve the case accessor order. + */ + def caseTemplateBody(): List[Tree] = { + val lb = ListBuffer[Tree]() + def isRewrite(sym: Symbol) = sym.isCaseAccessorMethod && !sym.isPublic + + for (ddef @ DefDef(_, _, _, _, _, _) <- templ.body ; if isRewrite(ddef.symbol)) { + val original = ddef.symbol + val newAcc = deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc => + makeMethodPublic(newAcc) + newAcc resetFlag (ACCESSOR | PARAMACCESSOR) + ddef.rhs.duplicate + } + ddef.symbol resetFlag CASEACCESSOR + lb += logResult("case accessor new")(newAcc) + } - if (phase.id <= currentRun.typerPhase.id) { - treeCopy.Template(templ, templ.parents, templ.self, - if (ts.isEmpty) templ.body else templ.body ++ ts // avoid copying templ.body if empty - ) + lb ++= templ.body ++= synthesize() toList } - else templ + + if (phase.id > currentRun.typerPhase.id) templ + else treeCopy.Template(templ, templ.parents, templ.self, + if (clazz.isCase) caseTemplateBody() + else synthesize() match { + case Nil => templ.body // avoiding unnecessary copy + case ms => templ.body ++ ms + } + ) } } |