diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala index 5cd3d48bcc00..3630a2d50c56 100644 --- a/src/compiler/scala/tools/nsc/transform/Fields.scala +++ b/src/compiler/scala/tools/nsc/transform/Fields.scala @@ -52,10 +52,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor val pre = site.thisType @tailrec def loop (bcs: List[Symbol]): Boolean = { // println(s"checking ${bcs.head} for member overriding $member (of ${member.owner})") - bcs.head != member.owner && (matchingAccessor(pre, member, bcs.head) != NoSymbol || loop(bcs.tail)) + bcs.nonEmpty && bcs.head != member.owner && (matchingAccessor(pre, member, bcs.head) != NoSymbol || loop(bcs.tail)) } - loop(site.info.baseClasses) + member.exists && loop(site.info.baseClasses) } class FieldMemoization(accessorOrField: Symbol, site: Symbol) { @@ -120,21 +120,24 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor // strict, memoized accessors will receive an implementation in first real class to extend this trait decls.foreach { case accessor if accessor hasFlag ACCESSOR => + // check flags before calling makeNotPrivate + val memoizedGetter = !(accessor hasFlag (DEFERRED | LAZY)) && fieldMemoizationIn(accessor, clazz).needsField + val finality = if (accessor hasFlag FINAL) FINAL_TRAIT_ACCESSOR else 0 + // only affects private symbols, with a destructive update of their name, also sets flags // required for private vals in traits accessor.makeNotPrivate(clazz) - if (!(accessor hasFlag (DEFERRED | LAZY)) && fieldMemoizationIn(accessor, clazz).needsField) { - // in a trait, a memoized accessor becomes deferred - // (it'll receive an implementation in the first real class to extend this trait) + // trait members cannot be final (but the synthesized ones should be) + // LOCAL no longer applies (already made not-private) + accessor resetFlag (FINAL | LOCAL) + // derive trait setter after calling makeNotPrivate (so that names are mangled consistently) + if (memoizedGetter) { + // a memoized accessor in a trait is made deferred now (mixins will deal with non-memoized getters like any other method) // can't mark getter as FINAL in trait, but remember for when we synthetisize the impl in the subclass to make it FINAL - val finality = if (accessor hasFlag FINAL) FINAL_TRAIT_ACCESSOR else 0 - accessor setFlag (finality | lateFINAL | DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS) - - // trait members cannot be final (but the synthesized ones should be) - // LOCAL no longer applies (already made not-private) - accessor resetFlag (FINAL | LOCAL) + // (it'll receive an implementation in the first real class to extend this trait) + accessor setFlag (finality | DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS) if ((accessor hasFlag STABLE) && accessor.isGetter) // TODO: isGetter is probably redundant? newSetters += newTraitSetter(accessor, clazz) @@ -151,6 +154,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor } else tp // mix in fields & accessors for all mixed in traits + case tp@ClassInfoType(parents, oldDecls, clazz) if !clazz.isPackageClass => val site = clazz.thisType // TODO (1): improve logic below, which is used to avoid mixing in anything that would result in an error in refchecks @@ -265,6 +269,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor mixedInFieldAndAccessors foreach newDecls.enter newDecls } +// println(s"new decls: $newDecls") if (newDecls eq oldDecls) tp else ClassInfoType(parents, newDecls, clazz) @@ -285,12 +290,13 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor def fieldsAndAccessors(templateSym: Symbol): List[ValOrDefDef] = { val clazz = templateSym.owner def fieldAccess(accessor: Symbol) = { - val field = accessor.accessed - assert(field.exists, s"No field for $accessor in $clazz") + val fieldName = accessor.localName + val field = clazz.info.decl(fieldName) + assert(field.exists, s"Field '$fieldName' not found in ${clazz.info.decls}") Select(This(clazz), field) } - val accessorsAndFieldsNeedingTrees = afterOwnPhase{ clazz.info }.decls.toList.filter(_ hasFlag NEEDS_TREES) + val accessorsAndFieldsNeedingTrees = clazz.info.decls.toList.filter(_ hasFlag NEEDS_TREES) accessorsAndFieldsNeedingTrees foreach (_ resetFlag NEEDS_TREES) // emitting the needed trees now // println(s"accessorsAndFieldsNeedingTrees: $accessorsAndFieldsNeedingTrees") diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index ba095c808ef1..f6dc5a5d6c81 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -120,7 +120,7 @@ trait Namers extends MethodSynthesis { def deriveAccessors(vd: ValDef) = vd.mods.isLazy || (owner.isClass && deriveAccessorsInClass(vd)) private def deriveAccessorsInClass(vd: ValDef) = - !vd.mods.isPrivateLocal && // note, private[this] lazy vals do get accessors -- see outer disjunction of deriveAccessors + (!vd.mods.isPrivateLocal || owner.isTrait) && // note, private[this] lazy vals do get accessors -- see outer disjunction of deriveAccessors !(vd.name startsWith nme.OUTER) && // outer accessors are added later, in explicitouter !isEnumConstant(vd) // enums can only occur in classes, so only check here diff --git a/test/files/trait-defaults/fields.scala b/test/files/trait-defaults/fields.scala index 4dca4fb1bb44..a5d362ec1f2a 100644 --- a/test/files/trait-defaults/fields.scala +++ b/test/files/trait-defaults/fields.scala @@ -1,3 +1,40 @@ +// test mixin of getters / setters, and implementing abstract +// methods using @BeanProperty +class C extends T with BeanF { + def foo() { + setF("doch!") + setG(true) + this.getF() + } +} + +trait T { + @scala.beans.BeanProperty var f = "nei" + @scala.beans.BooleanBeanProperty var g = false +} + +trait BeanF { + def getF(): String + def setF(n: String): Unit + + def isG(): Boolean + def setG(nb: Boolean): Unit +} + +/* + @scala.beans.BeanProperty private[this] var f: String = "nei"; + def f: String = T.this.f; + def f_=(x$1: String): Unit = T.this.f = x$1; + def setF(x$1: String): Unit = T.this.f = x$1; + @scala.beans.BooleanBeanProperty private[this] var g: Boolean = false; + def g: Boolean = T.this.g; + def g_=(x$1: Boolean): Unit = T.this.g = x$1; + def setG(x$1: Boolean): Unit = T.this.g = x$1; + def getF(): String = T.this.f; + def isG(): Boolean = T.this.g +*/ + + trait T { final val bla: Int = 123 } class C extends T // bla should be final in C @@ -135,29 +172,29 @@ class SUB extends IterableSplitter // } // } -// class Nest { val x = println(1)} +class Nest { val x = println(1)} -// package scala -// -// trait OneConcreteVal[T] { -// @deprecatedOverriding val x = 1 // : T = ??? -// @volatile var vy = "a" -// println(x) -// def foo = x -// } -// +package scala -// trait OneOtherConcreteVal[T] { -// var y: T = ??? -// } -// -// class C extends OneConcreteVal[Int] with OneOtherConcreteVal[String] +trait OneConcreteVal[T] { + @deprecatedOverriding val x = 1 // : T = ??? + @volatile var vy = "a" + println(x) + def foo = x +} -// object T extends App { -// val c = new C -// println(c.x) -// println(c.y) -// } + +trait OneOtherConcreteVal[T] { + var y: T = ??? +} + +class C extends OneConcreteVal[Int] with OneOtherConcreteVal[String] + +object T extends App { + val c = new C + println(c.x) + println(c.y) +} /* old decls for trait trait OneOtherConcreteVal: Scope{ def y(): Object; @@ -204,64 +241,64 @@ new decls for class C: Scope{ */ -// class Meh { -// final val x = 1 -// def foo = x -// } -// class CE extends Empty -// -// trait T { -// val abs: String -// protected val protabs: String -// val pub = "public" -// protected val prot = "protected" -// private val privvy = "private" -// private[this] val privateThis = "private[this]" -// // TODO: -// // final val const = "const" -// -// trait Nested { println(abs + privateThis) } -// -// object NO { -// println(abs) -// println(pub) -// println(prot) -// println(protabs) -// println(privvy) -// println(privateThis) -// } -// -// trait NT { -// println(abs) -// println(pub) -// println(prot) -// println(protabs) -// println(privvy) -// println(privateThis) -// } -// -// class NC { -// println(abs) -// println(pub) -// println(prot) -// println(protabs) -// println(privvy) -// println(privateThis) -// } -// } -// -// class C extends AnyRef with T { -// println("x") -// val abs = "abstract" -// println("y") -// val protabs = "abstract protected" -// final val const = "const" -// println("z") -// } -// -// object Test extends C { -// def main(args: Array[String]): Unit = { -// NO -// new NT{} -// new NC -// }} \ No newline at end of file +class Meh { + final val x = 1 + def foo = x +} +class CE extends Empty + +trait T { + val abs: String + protected val protabs: String + val pub = "public" + protected val prot = "protected" + private val privvy = "private" + private[this] val privateThis = "private[this]" + // TODO: + // final val const = "const" + + trait Nested { println(abs + privateThis) } + + object NO { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } + + trait NT { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } + + class NC { + println(abs) + println(pub) + println(prot) + println(protabs) + println(privvy) + println(privateThis) + } +} + +class C extends AnyRef with T { + println("x") + val abs = "abstract" + println("y") + val protabs = "abstract protected" + final val const = "const" + println("z") +} + +object Test extends C { + def main(args: Array[String]): Unit = { + NO + new NT{} + new NC +}} \ No newline at end of file diff --git a/test/files/trait-defaults/private_this.scala b/test/files/trait-defaults/private_this.scala new file mode 100644 index 000000000000..8065cc89e6a1 --- /dev/null +++ b/test/files/trait-defaults/private_this.scala @@ -0,0 +1,5 @@ +trait Chars { + private[this] val char2uescapeArray: String = ??? +} + +object Chars extends Chars \ No newline at end of file