summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2016-05-29 21:45:08 -0700
committerAdriaan Moors <adriaan@lightbend.com>2016-08-11 10:59:16 -0700
commit8f792280630721bdc1e6ee9199eb0cf8cb035fce (patch)
tree3034a2e1d7cb19e33a1ee62cbff299a53e39b1ce
parent6f0bb49c17ea1a46283777e39ed5ce016aa048a5 (diff)
downloadscala-8f792280630721bdc1e6ee9199eb0cf8cb035fce.tar.gz
scala-8f792280630721bdc1e6ee9199eb0cf8cb035fce.tar.bz2
scala-8f792280630721bdc1e6ee9199eb0cf8cb035fce.zip
Simplify erasure + mixin
Remove some old, obsolete & untested hacks from ExplicitOuter. Added a test for one of them to show this is now fine. There are a lot of `makeNotPrivate` invocations sprinkled around the codebase. Lets see if we can centralize the ones dealing with trait methods that need implementations in the phase that emits them. For example Fields (accessors for fields/modules) or SuperAccessors.
-rw-r--r--src/compiler/scala/tools/nsc/transform/AddInterfaces.scala94
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala59
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala105
-rw-r--r--src/compiler/scala/tools/nsc/transform/Fields.scala3
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala142
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala6
-rw-r--r--src/reflect/scala/reflect/internal/transform/Erasure.scala14
-rw-r--r--test/files/run/t2946/MyResponseCommon_2.scala7
-rw-r--r--test/files/run/t2946/ResponseCommon_1.scala13
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala2
10 files changed, 177 insertions, 268 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
index 406832c262..e69de29bb2 100644
--- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
+++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
@@ -1,94 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2013 LAMP/EPFL
- * @author Martin Odersky
- */
-
-package scala.tools.nsc
-package transform
-
-import symtab._
-import Flags._
-
-abstract class AddInterfaces extends InfoTransform { self: Erasure =>
- import global._ // the global environment
- import definitions._ // standard classes and methods
-
- def transformMixinInfo(tp: Type): Type = tp match {
- case ClassInfoType(parents, decls, clazz) if clazz.isPackageClass || !clazz.isJavaDefined =>
-
- val parents1 = parents match {
- case Nil => Nil
- case hd :: tl =>
- assert(!hd.typeSymbol.isTrait, clazz)
- if (clazz.isTrait) ObjectTpe :: tl
- else parents
- }
- if (clazz.isTrait) {
- decls foreach { sym =>
- if (!sym.isType) sym.info // initialize to set lateMETHOD flag if necessary
- }
- }
- if (parents1 eq parents) tp
- else ClassInfoType(parents1, decls, clazz)
- case _ =>
- tp
- }
-
-// Tree transformation --------------------------------------------------------------
- private class ChangeOwnerAndReturnTraverser(oldowner: Symbol, newowner: Symbol)
- extends ChangeOwnerTraverser(oldowner, newowner) {
- override def traverse(tree: Tree) {
- tree match {
- case _: Return => change(tree.symbol)
- case _ =>
- }
- super.traverse(tree)
- }
- }
-
- /** Add calls to supermixin constructors
- * `super[mix].$init$()`
- * to tree, which is assumed to be the body of a constructor of class clazz.
- */
- private def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = {
- def mixinConstructorCall(mc: Symbol): Tree = atPos(tree.pos) {
- Apply(SuperSelect(clazz, mc.primaryConstructor), Nil)
- }
- val mixinConstructorCalls: List[Tree] = {
- for (mc <- clazz.mixinClasses.reverse
- if mc.isTrait && mc.primaryConstructor != NoSymbol)
- yield mixinConstructorCall(mc)
- }
- tree match {
-
- case Block(Nil, expr) =>
- // AnyVal constructor - have to provide a real body so the
- // jvm doesn't throw a VerifyError. But we can't add the
- // body until now, because the typer knows that Any has no
- // constructor and won't accept a call to super.init.
- assert((clazz isSubClass AnyValClass) || clazz.info.parents.isEmpty, clazz)
- Block(List(Apply(gen.mkSuperInitCall, Nil)), expr)
-
- case Block(stats, expr) =>
- // needs `hasSymbolField` check because `supercall` could be a block (named / default args)
- val (presuper, supercall :: rest) = stats span (t => t.hasSymbolWhich(_ hasFlag PRESUPER))
- treeCopy.Block(tree, presuper ::: (supercall :: mixinConstructorCalls ::: rest), expr)
- }
- }
-
- protected val mixinTransformer = new Transformer {
- override def transform(tree: Tree): Tree = {
- val sym = tree.symbol
- val tree1 = tree match {
- case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass =>
- deriveDefDef(tree)(addMixinConstructorCalls(_, sym.owner)) // (3)
- case Template(parents, self, body) =>
- val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos)
- treeCopy.Template(tree, parents1, noSelfType, body)
- case _ =>
- tree
- }
- super.transform(tree1)
- }
- }
-}
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index f3fd7c5f67..d190802f66 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -12,7 +12,7 @@ import symtab._
import Flags._
import scala.reflect.internal.Mode._
-abstract class Erasure extends AddInterfaces
+abstract class Erasure extends InfoTransform
with scala.reflect.internal.transform.Erasure
with typechecker.Analyzer
with TypingTransformers
@@ -373,16 +373,53 @@ abstract class Erasure extends AddInterfaces
class UnknownSig extends Exception
- /** The symbol's erased info. This is the type's erasure, except for the following symbols:
- *
- * - For $asInstanceOf : [T]T
- * - For $isInstanceOf : [T]scala#Boolean
- * - For class Array : [T]C where C is the erased classinfo of the Array class.
- * - For Array[T].<init> : {scala#Int)Array[T]
- * - For a type parameter : A type bounds type consisting of the erasures of its bounds.
- */
- override def transformInfo(sym: Symbol, tp: Type): Type =
- transformMixinInfo(super.transformInfo(sym, tp))
+ // TODO: move to constructors?
+ object mixinTransformer extends Transformer {
+ /** Add calls to supermixin constructors
+ * `super[mix].$init$()`
+ * to tree, which is assumed to be the body of a constructor of class clazz.
+ */
+ private def addMixinConstructorCalls(tree: Tree, clazz: Symbol): Tree = {
+ def mixinConstructorCall(mc: Symbol): Tree = atPos(tree.pos) {
+ Apply(SuperSelect(clazz, mc.primaryConstructor), Nil)
+ }
+ val mixinConstructorCalls: List[Tree] = {
+ for (mc <- clazz.mixinClasses.reverse
+ if mc.isTrait && mc.primaryConstructor != NoSymbol)
+ yield mixinConstructorCall(mc)
+ }
+ tree match {
+
+ case Block(Nil, expr) =>
+ // AnyVal constructor - have to provide a real body so the
+ // jvm doesn't throw a VerifyError. But we can't add the
+ // body until now, because the typer knows that Any has no
+ // constructor and won't accept a call to super.init.
+ assert((clazz isSubClass AnyValClass) || clazz.info.parents.isEmpty, clazz)
+ Block(List(Apply(gen.mkSuperInitCall, Nil)), expr)
+
+ case Block(stats, expr) =>
+ // needs `hasSymbolField` check because `supercall` could be a block (named / default args)
+ val (presuper, supercall :: rest) = stats span (t => t.hasSymbolWhich(_ hasFlag PRESUPER))
+ treeCopy.Block(tree, presuper ::: (supercall :: mixinConstructorCalls ::: rest), expr)
+ }
+ }
+
+ override def transform(tree: Tree): Tree = {
+ val sym = tree.symbol
+ val tree1 = tree match {
+ case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass =>
+ deriveDefDef(tree)(addMixinConstructorCalls(_, sym.owner)) // (3)
+ case Template(parents, self, body) =>
+ val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos)
+ treeCopy.Template(tree, parents1, noSelfType, body)
+ case _ =>
+ tree
+ }
+ super.transform(tree1)
+ }
+ }
+
val deconstMap = new TypeMap {
// For some reason classOf[Foo] creates ConstantType(Constant(tpe)) with an actual Type for tpe,
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index 411ff6b9be..f3d5ceb0f0 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -158,13 +158,6 @@ abstract class ExplicitOuter extends InfoTransform
case MethodType(params, resTp) =>
val resTpTransformed = transformInfo(sym, resTp)
- // juggle flags (and mangle names) after transforming info
- if (sym.owner.isTrait) {
- // TODO: I don't believe any private accessors remain after the fields phase
- if ((sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.isModule) sym.makeNotPrivate(sym.owner) // 5
- if (sym.isProtected) sym setFlag notPROTECTED // 6
- }
-
val paramsWithOuter =
if (sym.isClassConstructor && isInner(sym.owner)) // 1
sym.newValueParameter(nme.OUTER_ARG, sym.pos).setInfo(sym.owner.outerClass.thisType) :: params
@@ -202,14 +195,6 @@ abstract class ExplicitOuter extends InfoTransform
if (restp eq restp1) tp else PolyType(tparams, restp1)
case _ =>
- // Local fields of traits need to be unconditionally unprivatized.
- // Reason: Those fields might need to be unprivatized if referenced by an inner class.
- // On the other hand, mixing in the trait into a separately compiled
- // class needs to have a common naming scheme, independently of whether
- // the field was accessed from an inner class or not. See #2946
- if (sym.owner.isTrait && sym.isLocalToThis &&
- (sym.getterIn(sym.owner) == NoSymbol))
- sym.makeNotPrivate(sym.owner)
tp
}
@@ -300,61 +285,41 @@ abstract class ExplicitOuter extends InfoTransform
}
}
- /** <p>
- * The phase performs the following transformations on terms:
- * </p>
- * <ol>
- * <li> <!-- 1 -->
- * <p>
- * An class which is not an interface and is not static gets an outer
- * accessor (@see outerDefs).
- * </p>
- * <p>
- * 1a. A class which is not a trait gets an outer field.
- * </p>
- * </li>
- * <li> <!-- 4 -->
- * A constructor of a non-trait inner class gets an outer parameter.
- * </li>
- * <li> <!-- 5 -->
- * A reference C.this where C refers to an
- * outer class is replaced by a selection
- * this.$outer$$C1 ... .$outer$$Cn (@see outerPath)
- * </li>
- * <li>
- * </li>
- * <li> <!-- 7 -->
- * A call to a constructor Q.<init>(args) or Q.$init$(args) where Q != this and
- * the constructor belongs to a non-static class is augmented by an outer argument.
- * E.g. Q.<init>(OUTER, args) where OUTER
- * is the qualifier corresponding to the singleton type Q.
- * </li>
- * <li>
- * A call to a constructor this.<init>(args) in a
- * secondary constructor is augmented to this.<init>(OUTER, args)
- * where OUTER is the last parameter of the secondary constructor.
- * </li>
- * <li> <!-- 9 -->
- * Remove private modifier from class members M
- * that are accessed from an inner class.
- * </li>
- * <li> <!-- 10 -->
- * Remove protected modifier from class members M
- * that are accessed without a super qualifier accessed from an inner
- * class or trait.
- * </li>
- * <li> <!-- 11 -->
- * Remove private and protected modifiers
- * from type symbols
- * </li>
- * <li> <!-- 12 -->
- * Remove private modifiers from members of traits
- * </li>
- * </ol>
- * <p>
- * Note: The whole transform is run in phase explicitOuter.next.
- * </p>
- */
+ /** The phase performs the following transformations (more or less...):
+ *
+ * (1) An class which is not an interface and is not static gets an outer accessor (@see outerDefs).
+ * (1a) A class which is not a trait gets an outer field.
+ *
+ * (4) A constructor of a non-trait inner class gets an outer parameter.
+ *
+ * (5) A reference C.this where C refers to an outer class is replaced by a selection
+ * `this.$outer$$C1 ... .$outer$$Cn` (@see outerPath)
+ *
+ * (7) A call to a constructor Q.(args) or Q.$init$(args) where Q != this and
+ * the constructor belongs to a non-static class is augmented by an outer argument.
+ * E.g. Q.(OUTER, args) where OUTER
+ * is the qualifier corresponding to the singleton type Q.
+ *
+ * (8) A call to a constructor this.(args) in a
+ * secondary constructor is augmented to this.(OUTER, args)
+ * where OUTER is the last parameter of the secondary constructor.
+ *
+ * (9) Remove private modifier from class members M that are accessed from an inner class.
+ *
+ * (10) Remove protected modifier from class members M that are accessed
+ * without a super qualifier accessed from an inner class or trait.
+ *
+ * (11) Remove private and protected modifiers from type symbols
+ *
+ * Note: The whole transform is run in phase explicitOuter.next.
+ *
+ * TODO: Make this doc reflect what's actually going on.
+ * Some of the deviations are motivated by separate compilation
+ * (name mangling based on usage is inherently unstable).
+ * Now that traits are compiled 1:1 to interfaces, they can have private members,
+ * so there's also less need to make trait members non-private
+ * (they still may need to be implemented in subclasses, though we could make those protected...).
+ */
class ExplicitOuterTransformer(unit: CompilationUnit) extends OuterPathTransformer(unit) {
transformer =>
diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala
index a9ed8e3aca..1900fcdc16 100644
--- a/src/compiler/scala/tools/nsc/transform/Fields.scala
+++ b/src/compiler/scala/tools/nsc/transform/Fields.scala
@@ -53,6 +53,8 @@ import symtab.Flags._
* An overridden val's side-effect is still performed.
* The only change due to overriding is that its value is never written to the field
* (the overridden val's value is, of course, stored in the field in addition to its side-effect being performed).
+ *
+ * TODO: check init support (or drop the -Xcheck-init flag??)
*/
abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransformers {
@@ -247,6 +249,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
val accessorUnderConsideration = !(member hasFlag (DEFERRED | LAZY))
// destructively mangle accessor's name (which may cause rehashing of decls), also sets flags
+ // TODO: technically, only necessary for stored fields
if (member hasFlag PRIVATE) member makeNotPrivate clazz
// Need to mark as notPROTECTED, so that it's carried over to the synthesized member in subclasses,
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 441ae625d0..a1441fe7b3 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -19,6 +19,22 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
/** The name of the phase: */
val phaseName: String = "mixin"
+ /** Some trait methods need to be implemented in subclasses, so they cannot be private.
+ *
+ * They may be protected, now that traits are compiled 1:1 to interfaces.
+ *
+ * TODO: interfaces can also have private members, so there's also less need to make trait members non-private
+ * can we leave more methods private?
+ * (they still may need to be implemented in subclasses, though we could make those protected...).
+ */
+ def publicizeTraitMethod(sym: Symbol): Unit = {
+ if ((sym hasFlag PRIVATE) &&
+ ( (sym hasFlag SUPERACCESSOR) // super accessors by definition must be implemented in a subclass, so can't have the private (TODO: why are they ever private in a trait to begin with!?!?)
+ || (sym hasFlag ACCESSOR | MODULE))) // an accessor / module *may* need to be implemented in a subclass, and thus cannot be private
+ sym.makeNotPrivate(sym.owner)
+
+ if (sym hasFlag PROTECTED) sym setFlag notPROTECTED
+ }
/** This map contains a binding (class -> info) if
* the class with this info at phase mixinPhase has been treated for mixin composition
@@ -43,7 +59,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* methods in the impl class (because they can have arbitrary initializers)
*/
private def isImplementedStatically(sym: Symbol) = (
- sym.isMethod
+ (sym.isMethod || ((sym hasFlag MODULE) && !sym.isStatic))
&& notDeferred(sym)
&& sym.owner.isTrait
&& (!sym.isModule || sym.hasFlag(PRIVATE | LIFTED))
@@ -221,6 +237,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}
clazz.info.decls.unlink(member)
}
+ else if (member.isMethod) publicizeTraitMethod(member)
}
debuglog("new defs of " + clazz + " = " + clazz.info.decls)
}
@@ -318,10 +335,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
superAccessor.asInstanceOf[TermSymbol] setAlias alias1
}
}
- else if (mixinMember.hasAllFlags(METHOD | MODULE) && mixinMember.hasNoFlags(LIFTED | BRIDGE)) {
- // mixin objects: todo what happens with abstract objects?
- // addMember(clazz, mixinMember.cloneSymbol(clazz, mixinMember.flags & ~DEFERRED) setPos clazz.pos)
- }
else if (mixinMember.hasFlag(ACCESSOR) && notDeferred(mixinMember)
&& (mixinMember hasFlag (LAZY | PARAMACCESSOR))
&& !isOverriddenAccessor(mixinMember, clazz.info.baseClasses)) {
@@ -866,109 +879,70 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
def getterBody(getter: Symbol) = {
assert(getter.isGetter)
- val readValue = getter.tpe match {
- // A field "final val f = const" in a trait generates a getter with a ConstantType.
- case MethodType(Nil, ConstantType(c)) =>
- Literal(c)
- case _ =>
- // if it is a mixed-in lazy value, complete the accessor
- if (getter.isLazy) {
- val isUnit = isUnitGetter(getter)
- val initCall = Apply(SuperSelect(clazz, initializer(getter)), Nil)
- val selection = fieldAccess(getter)
- val init = if (isUnit) initCall else atPos(getter.pos)(Assign(selection, initCall))
- val returns = if (isUnit) UNIT else selection
- mkLazyDef(clazz, getter, List(init), returns, fieldOffset(getter))
+ val readValue =
+ if (getter.isLazy) {
+ getter.tpe.resultType match {
+ case ConstantType(c) => Literal(c)
+ case _ =>
+ val initCall = Apply(SuperSelect(clazz, initializer(getter)), Nil)
+ val offset = fieldOffset(getter)
+ if (isUnitGetter(getter)) mkLazyDef(clazz, getter, List(initCall), UNIT, offset)
+ else mkLazyDef(clazz, getter, List(atPos(getter.pos)(Assign(fieldAccess(getter), initCall))), fieldAccess(getter), offset)
}
- // For a field of type Unit in a trait, no actual field is generated when being mixed in.
- else if (isUnitGetter(getter)) UNIT
- else fieldAccess(getter)
- }
+ } else {
+ assert(getter.hasFlag(PARAMACCESSOR))
+ fieldAccess(getter)
+ }
+
if (!needsInitFlag(getter)) readValue
else mkCheckedAccessor(clazz, readValue, fieldOffset(getter), getter.pos, getter)
}
def setterBody(setter: Symbol) = {
val getter = setter.getterIn(clazz)
-
- // A trait with a field of type Unit creates a trait setter (invoked by the
- // implementation class constructor), like for any other trait field.
- // However, no actual field is created in the class that mixes in the trait.
- // Therefore the setter does nothing (except setting the -Xcheckinit flag).
+ assert(getter.hasFlag(PARAMACCESSOR), s"missing implementation for non-paramaccessor $setter in $clazz")
val setInitFlag =
if (!needsInitFlag(getter)) Nil
else List(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))
- val fieldInitializer =
- if (isUnitGetter(getter)) Nil
- else List(Assign(fieldAccess(setter), Ident(setter.firstParam)))
-
- (fieldInitializer ::: setInitFlag) match {
- case Nil => UNIT
- // If there's only one statement, the Block factory does not actually create a Block.
- case stats => Block(stats: _*)
- }
+ Block(Assign(fieldAccess(setter), Ident(setter.firstParam)) :: setInitFlag : _*)
}
def fieldAccess(accessor: Symbol) = Select(This(clazz), accessor.accessed)
- def isOverriddenSetter(sym: Symbol) =
- nme.isTraitSetterName(sym.name) && {
- val other = sym.nextOverriddenSymbol
- isOverriddenAccessor(other.getterIn(other.owner), clazz.info.baseClasses)
- }
- // for all symbols `sym` in the class definition, which are mixed in:
+ // for all symbols `sym` in the class definition, which are mixed in by mixinTraitMembers
for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) {
// if current class is a trait, add an abstract method for accessor `sym`
- if (clazz.isTrait) {
- addDefDef(sym)
- } else {
- // if class is not a trait add accessor definitions
- if (sym.hasFlag(ACCESSOR) && !sym.hasFlag(DEFERRED)) {
- assert(sym hasFlag (LAZY | PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?")
-
- // add accessor definitions
- addDefDef(sym, {
- if (sym.isSetter) {
- // If this is a setter of a mixed-in field which is overridden by another mixin,
- // the trait setter of the overridden one does not need to do anything - the
- // trait setter of the overriding field will initialize the field.
- if (isOverriddenSetter(sym)) UNIT
- else setterBody(sym)
- }
- else getterBody(sym)
- })
- }
- else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) {
- // Moved to Refchecks
- }
- else if (!sym.isMethod) {
- // add fields
- addValDef(sym)
- }
- else if (sym.isSuperAccessor) {
- // add superaccessors
- addDefDef(sym)
- }
- else {
- // add forwarders
- assert(sym.alias != NoSymbol, (sym, sym.debugFlagString, clazz))
- // debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString)
- if (!sym.isMacro) addDefDef(sym, Apply(SuperSelect(clazz, sym.alias), sym.paramss.head.map(Ident(_))))
- }
+ // ditto for a super accessor (will get an RHS in completeSuperAccessor)
+ if (clazz.isTrait || sym.isSuperAccessor) addDefDef(sym)
+ // implement methods mixed in from a supertrait (the symbols were created by mixinTraitMembers)
+ else if (sym.hasFlag(ACCESSOR) && !sym.hasFlag(DEFERRED)) {
+ assert(sym hasFlag (LAZY | PARAMACCESSOR), s"mixed in $sym from $clazz is not lazy/param?!?")
+
+ // add accessor definitions
+ addDefDef(sym, if (sym.isSetter) setterBody(sym) else getterBody(sym))
+ }
+ else if (!sym.isMethod) addValDef(sym) // field
+ else if (!sym.isMacro) { // forwarder
+ assert(sym.alias != NoSymbol, (sym, sym.debugFlagString, clazz))
+ // debuglog("New forwarder: " + sym.defString + " => " + sym.alias.defString)
+ addDefDef(sym, Apply(SuperSelect(clazz, sym.alias), sym.paramss.head.map(Ident(_))))
}
}
+
stats1 = add(stats1, newDefs.toList)
- if (clazz.isTrait) stats1 =
- stats1.filter {
+
+ if (clazz.isTrait) stats1 = stats1.filter {
case vd: ValDef =>
- // TODO do we get here?
+ assert(vd.symbol.hasFlag(PRESUPER | PARAMACCESSOR | LAZY), s"unexpected valdef $vd in trait $clazz")
false
case _ => true
}
+
if (!clazz.isTrait) stats1 = stats1 map completeSuperAccessor
+
stats1
}
@@ -989,8 +963,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
/** The transform that gets applied to a tree after it has been completely
* traversed and possible modified by a preTransform.
* This step will
- * - change every node type that refers to an implementation class to its
- * corresponding interface, unless the node's symbol is an implementation class.
* - change parents of templates to conform to parents in the symbol info
* - add all new definitions to a class or interface
* - remove widening casts
@@ -998,8 +970,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* to static calls of methods in implementation modules (@see staticCall)
* - change super calls to methods in implementation classes to static calls
* (@see staticCall)
- * - change `this` in implementation modules to references to the self parameter
- * - refer to fields in some implementation class via an abstract method in the interface.
*/
private def postTransform(tree: Tree): Tree = {
val sym = tree.symbol
@@ -1020,6 +990,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
treeCopy.Template(tree, parents1, self, statsWithNewDefs)
case Select(qual, name) if sym.owner.isTrait && !sym.isMethod =>
+ assert(sym.hasFlag(PARAMACCESSOR | PRESUPER), s"!!! Unexpected reference to field $sym in trait $currentOwner")
+
// refer to fields in some trait an abstract getter in the interface.
val ifaceGetter = sym getterIn sym.owner
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 35ec80901e..eca1bbea5a 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -1040,11 +1040,7 @@ trait Definitions extends api.StandardDefinitions {
}
}
- /** Remove references to class Object (other than the head) in a list of parents */
- def removeLaterObjects(tps: List[Type]): List[Type] = tps match {
- case Nil => Nil
- case x :: xs => x :: xs.filterNot(_.typeSymbol == ObjectClass)
- }
+
/** Remove all but one reference to class Object from a list of parents. */
def removeRedundantObjects(tps: List[Type]): List[Type] = tps match {
case Nil => Nil
diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala
index 412c49f571..62ca50d035 100644
--- a/src/reflect/scala/reflect/internal/transform/Erasure.scala
+++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala
@@ -148,9 +148,19 @@ trait Erasure {
apply(atp)
case ClassInfoType(parents, decls, clazz) =>
ClassInfoType(
- if (clazz == ObjectClass || isPrimitiveValueClass(clazz)) Nil
+ if (clazz == ObjectClass || isPrimitiveValueClass(clazz) || parents.isEmpty) Nil
else if (clazz == ArrayClass) ObjectTpe :: Nil
- else removeLaterObjects(parents map this),
+ else {
+ val erasedParents = parents map this
+
+ // drop first parent for traits -- it has been normalized to a class by now,
+ // but we should drop that in bytecode
+ val firstParent =
+ if (clazz.hasFlag(Flags.TRAIT) && !clazz.hasFlag(Flags.JAVA)) ObjectTpe
+ else erasedParents.head
+
+ firstParent :: erasedParents.tail.filter(_.typeSymbol != ObjectClass)
+ },
decls, clazz)
case _ =>
mapOver(tp)
diff --git a/test/files/run/t2946/MyResponseCommon_2.scala b/test/files/run/t2946/MyResponseCommon_2.scala
new file mode 100644
index 0000000000..4f8f924f2c
--- /dev/null
+++ b/test/files/run/t2946/MyResponseCommon_2.scala
@@ -0,0 +1,7 @@
+class MyResponseCommon extends Parser with ResponseCommon
+
+object Test {
+ def main(args: Array[String]) {
+ new MyResponseCommon
+ }
+}
diff --git a/test/files/run/t2946/ResponseCommon_1.scala b/test/files/run/t2946/ResponseCommon_1.scala
new file mode 100644
index 0000000000..bb921e7027
--- /dev/null
+++ b/test/files/run/t2946/ResponseCommon_1.scala
@@ -0,0 +1,13 @@
+class Parser {
+ def parse(t: Any): Unit = {}
+}
+
+trait ResponseCommon extends Parser {
+ private[this] var paramsParser: Parser = null
+ def withParamsParser(parser: Parser) = {paramsParser = parser; this}
+
+ override abstract def parse(t: Any): Unit = t match {
+ case ("params", value: List[_]) => value.foreach {paramsParser.parse(_)}
+ case _ => super.parse(t)
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
index 9217183c74..e03b703dc9 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
@@ -106,7 +106,7 @@ class ScalaInlineInfoTest extends BytecodeTesting {
("x5()I", MethodInlineInfo(true, false,false)),
("x5$(LT;)I", MethodInlineInfo(true ,false,false)),
("L$lzycompute$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)),
- ("L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true ,false,false)),
+ ("T$$L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true ,false,false)),
("nest$1()I", MethodInlineInfo(true, false,false)),
("$init$(LT;)V", MethodInlineInfo(true,false,false))),
None // warning