summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala13
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala52
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala35
-rw-r--r--src/compiler/scala/tools/nsc/transform/TypingTransformers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala39
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala2
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala2
-rw-r--r--src/reflect/scala/reflect/internal/transform/Erasure.scala2
-rw-r--r--test/files/run/delambdafy_t6028.check12
-rw-r--r--test/files/run/t6028.check12
-rw-r--r--test/files/run/trait-defaults-modules.scala20
-rw-r--r--test/files/run/trait-defaults-modules2/T_1.scala4
-rw-r--r--test/files/run/trait-defaults-modules2/Test_2.scala5
-rw-r--r--test/files/run/trait-defaults-modules3.scala8
15 files changed, 141 insertions, 69 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 332acf4a26..3167f87383 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -242,11 +242,14 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree =
mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats)
- def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree =
- Block(mkSynchronized(
- attrThis,
- If(cond, Block(syncBody: _*), EmptyTree)) ::
- stats: _*)
+ def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = {
+ def blockOrStat(stats: List[Tree]): Tree = stats match {
+ case head :: Nil => head
+ case _ => Block(stats : _*)
+ }
+ val sync = mkSynchronized(attrThis, If(cond, blockOrStat(syncBody), EmptyTree))
+ blockOrStat(sync :: stats)
+ }
/** Creates a tree representing new Object { stats }.
* To make sure an anonymous subclass of Object is created,
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index b6695efb0b..73c15c1f53 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -54,14 +54,35 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0
import symtab.Flags._
+ private def flattenThickets(stats: List[Tree]): List[Tree] = stats.flatMap(_ match {
+ case b @ Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if b.tpe == null && n1.endsWith(nme.LAZY_SLOW_SUFFIX) =>
+ List(d1, d2)
+ case stat =>
+ List(stat)
+ })
/** Perform the following transformations:
* - for a lazy accessor inside a method, make it check the initialization bitmap
+ * - implement double checked locking of member modules for non-trait owners (trait just have the abstract accessor)
+ * ```
+ * // typer
+ * class C { object x }
+ * // refchecks
+ * class C { var x$module; def x() = { x$module = new x; x$module }
+ * // lazyvals
+ * class C {
+ * var x$module // module var
+ * def x() = { if (x$module == null) x$lzycompute() else x$module // fast path
+ * def x$lzycompute() = { synchronized { if (x$module == null) x$module = new x }; x$module } // slow path
+ * }
+ * ```
* - for all methods, add enough int vars to allow one flag per lazy local value
* - blocks in template bodies behave almost like methods. A single bitmaps section is
* added in the first block, for all lazy values defined in such blocks.
* - remove ACCESSOR flags: accessors in traits are not statically implemented,
* but moved to the host class. local lazy values should be statically implemented.
+ *
+ * The general pattern is
*/
override def transform(tree: Tree): Tree = {
val sym = tree.symbol
@@ -72,20 +93,14 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
case Block(_, _) =>
val block1 = super.transform(tree)
val Block(stats, expr) = block1
- val stats1 = stats.flatMap(_ match {
- case Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if (nme.newLazyValSlowComputeName(n2) == n1) =>
- List(d1, d2)
- case stat =>
- List(stat)
- })
- treeCopy.Block(block1, stats1, expr)
+ treeCopy.Block(block1, flattenThickets(stats), expr)
case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) {
val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) {
val enclosingClassOrDummyOrMethod = {
val enclMethod = sym.enclMethod
- if (enclMethod != NoSymbol ) {
+ if (enclMethod != NoSymbol) {
val enclClass = sym.enclClass
if (enclClass != NoSymbol && enclMethod == enclClass.enclMethod)
enclClass
@@ -100,11 +115,26 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym)
sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR)
(rhs1, sDef)
- } else
+ } else if (sym.hasAllFlags(MODULE | METHOD) && !sym.owner.isTrait) {
+ rhs match {
+ case b @ Block((assign @ Assign(moduleRef, _)) :: Nil, expr) =>
+ val cond = Apply(Select(moduleRef, Object_eq), List(Literal(Constant(null))))
+ val (fastPath, slowPath) = mkFastPathBody(sym.owner.enclClass, moduleRef.symbol, cond, transform(assign) :: Nil, Nil, transform(expr))
+ (localTyper.typedPos(tree.pos)(fastPath), localTyper.typedPos(tree.pos)(slowPath))
+ case rhs =>
+ global.reporter.error(tree.pos, "Unexpected tree on the RHS of a module accessor: " + rhs)
+ (rhs, EmptyTree)
+ }
+ } else {
(transform(rhs), EmptyTree)
+ }
val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res)
- if (slowPathDef != EmptyTree) Block(slowPathDef, ddef1) else ddef1
+ if (slowPathDef != EmptyTree) {
+ // The contents of this block are flattened into the enclosing statement sequence, see flattenThickets
+ // This is a poor man's version of dotty's Thicket: https://github.com/lampepfl/dotty/blob/d5280358d1/src/dotty/tools/dotc/ast/Trees.scala#L707
+ Block(slowPathDef, ddef1)
+ } else ddef1
}
case Template(_, _, body) => atOwner(currentOwner) {
@@ -135,7 +165,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
})
toAdd0
} else List()
- deriveTemplate(tree)(_ => innerClassBitmaps ++ stats)
+ deriveTemplate(tree)(_ => innerClassBitmaps ++ flattenThickets(stats))
}
case ValDef(_, _, _, _) if !sym.owner.isModule && !sym.owner.isClass =>
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 00a994fe87..d6e4e1a727 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -861,16 +861,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
typedPos(init.head.pos)(mkFastPathLazyBody(clazz, lzyVal, cond, syncBody, nulls, retVal))
}
- def mkInnerClassAccessorDoubleChecked(attrThis: Tree, rhs: Tree, moduleSym: Symbol, args: List[Tree]): Tree =
- rhs match {
- case Block(List(assign), returnTree) =>
- val Assign(moduleVarRef, _) = assign
- val cond = Apply(Select(moduleVarRef, Object_eq), List(NULL))
- mkFastPathBody(clazz, moduleSym, cond, List(assign), List(NULL), returnTree, attrThis, args)
- case _ =>
- abort(s"Invalid getter $rhs for module in $clazz")
- }
-
def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = {
val sym = fieldSym.getterIn(fieldSym.owner)
val bitmapSym = bitmapFor(clazz, offset, sym)
@@ -926,18 +916,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))), UNIT))
else stat
}
- else if (sym.isModule && (!clazz.isTrait || clazz.isImplClass) && !sym.isBridge) {
- deriveDefDef(stat)(rhs =>
- typedPos(stat.pos)(
- mkInnerClassAccessorDoubleChecked(
- // Martin to Hubert: I think this can be replaced by selfRef(tree.pos)
- // @PP: It does not seem so, it crashes for me trying to bootstrap.
- if (clazz.isImplClass) gen.mkAttributedIdent(stat.vparamss.head.head.symbol) else gen.mkAttributedThis(clazz),
- rhs, sym, stat.vparamss.head
- )
- )
- )
- }
else stat
}
stats map {
@@ -1082,18 +1060,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
})
}
else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) {
- // add modules
- val vsym = sym.owner.newModuleVarSymbol(sym)
- addDef(position(sym), ValDef(vsym))
-
- // !!! TODO - unravel the enormous duplication between this code and
- // eliminateModuleDefs in RefChecks.
- val rhs = gen.newModule(sym, vsym.tpe)
- val assignAndRet = gen.mkAssignAndReturn(vsym, rhs)
- val attrThis = gen.mkAttributedThis(clazz)
- val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet, sym, List())
-
- addDefDef(sym, rhs1)
+ // Moved to Refchecks
}
else if (!sym.isMethod) {
// add fields
diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
index dc3313e2e4..d5adfe12e9 100644
--- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
+++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala
@@ -26,7 +26,7 @@ trait TypingTransformers {
def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A = {
val savedLocalTyper = localTyper
- localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner)
+ localTyper = localTyper.atOwner(tree, if (owner.isModuleNotMethod) owner.moduleClass else owner)
val result = super.atOwner(owner)(trans)
localTyper = savedLocalTyper
result
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 7252cae0b5..915ffcc7d0 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1188,14 +1188,6 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// set NoType so it will be ignored.
val cdef = ClassDef(module.moduleClass, impl) setType NoType
- def newInnerObject() = {
- val moduleVar = site newModuleVarSymbol module
- val rhs = gen.newModule(module, moduleVar.tpe)
- val body = if (site.isTrait) rhs else gen.mkAssignAndReturn(moduleVar, rhs)
- val accessor = DefDef(module, body.changeOwner(moduleVar -> module))
-
- ValDef(moduleVar) :: accessor :: Nil
- }
def matchingInnerObject() = {
val newFlags = (module.flags | STABLE) & ~MODULE
val newInfo = NullaryMethodType(module.moduleClass.tpe)
@@ -1208,10 +1200,36 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O.
if (module.isOverridingSymbol) matchingInnerObject() else Nil
else
- newInnerObject()
+ newInnerObject(site, module)
)
transformTrees(newTrees map localTyper.typedPos(moduleDef.pos))
}
+ def newInnerObject(site: Symbol, module: Symbol): List[Tree] = {
+ if (site.isTrait)
+ DefDef(module, EmptyTree) :: Nil
+ else {
+ val moduleVar = site newModuleVarSymbol module
+ // used for the mixin case: need a new symbol owned by the subclass for the accessor, rather than repurposing the module symbol
+ def mkAccessorSymbol =
+ site.newMethod(module.name.toTermName, site.pos, STABLE | MODULE | MIXEDIN)
+ .setInfo(moduleVar.tpe)
+ .andAlso(self => if (module.isPrivate) self.expandName(module.owner))
+
+ val accessor = if (module.owner == site) module else mkAccessorSymbol
+ val accessorDef = DefDef(accessor, gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.tpe)).changeOwner(moduleVar -> accessor))
+
+ ValDef(moduleVar) :: accessorDef :: Nil
+ }
+ }
+
+ def mixinModuleDefs(clazz: Symbol): List[Tree] = {
+ val res = for {
+ mixinClass <- clazz.mixinClasses.iterator
+ module <- mixinClass.info.decls.iterator.filter(_.isModule)
+ newMember <- newInnerObject(clazz, module)
+ } yield transform(localTyper.typedPos(clazz.pos)(newMember))
+ res.toList
+ }
def transformStat(tree: Tree, index: Int): List[Tree] = tree match {
case t if treeInfo.isSelfConstrCall(t) =>
@@ -1643,11 +1661,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// SI-7870 default getters for constructors live in the companion module
checkOverloadedRestrictions(currentOwner, currentOwner.companionModule)
val bridges = addVarargBridges(currentOwner)
+ val moduleDesugared = if (currentOwner.isTrait) Nil else mixinModuleDefs(currentOwner)
checkAllOverrides(currentOwner)
checkAnyValSubclass(currentOwner)
if (currentOwner.isDerivedValueClass)
currentOwner.primaryConstructor makeNotPrivate NoSymbol // SI-6601, must be done *after* pickler!
- if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree
+ if (bridges.nonEmpty || moduleDesugared.nonEmpty) deriveTemplate(tree)(_ ::: bridges ::: moduleDesugared) else tree
case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc")
case tpt@TypeTree() =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index e0d96df062..187d2b1084 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -387,7 +387,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val savedValid = validCurrentOwner
if (owner.isClass) validCurrentOwner = true
val savedLocalTyper = localTyper
- localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner)
+ localTyper = localTyper.atOwner(tree, if (owner.isModuleNotMethod) owner.moduleClass else owner)
typers = typers updated (owner, localTyper)
val result = super.atOwner(tree, owner)(trans)
localTyper = savedLocalTyper
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index f1016e1b76..9a31634751 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -876,7 +876,7 @@ trait StdNames {
val toCharacter: NameType = "toCharacter"
val toInteger: NameType = "toInteger"
- def newLazyValSlowComputeName(lzyValName: Name) = lzyValName append LAZY_SLOW_SUFFIX
+ def newLazyValSlowComputeName(lzyValName: Name) = (lzyValName stripSuffix MODULE_VAR_SUFFIX append LAZY_SLOW_SUFFIX).toTermName
// ASCII names for operators
val ADD = encode("+")
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index c3b2a8da08..3d87035f88 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -323,7 +323,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def newModuleVarSymbol(accessor: Symbol): TermSymbol = {
val newName = nme.moduleVarName(accessor.name.toTermName)
val newFlags = MODULEVAR | ( if (this.isClass) PrivateLocal | SYNTHETIC else 0 )
- val newInfo = accessor.tpe.finalResultType
+ val newInfo = thisType.memberType(accessor).finalResultType
val mval = newVariable(newName, accessor.pos.focus, newFlags.toLong) addAnnotation VolatileAttr
if (this.isClass)
diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala
index 9853a0fa0c..f661978fcd 100644
--- a/src/reflect/scala/reflect/internal/transform/Erasure.scala
+++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala
@@ -123,7 +123,7 @@ trait Erasure {
case tref @ TypeRef(pre, sym, args) =>
if (sym == ArrayClass)
if (unboundedGenericArrayLevel(tp) == 1) ObjectTpe
- else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectTpe)
+ else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectTpe)
else typeRef(apply(pre), sym, args map applyInArray)
else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass) ObjectTpe
else if (sym == UnitClass) BoxedUnitTpe
diff --git a/test/files/run/delambdafy_t6028.check b/test/files/run/delambdafy_t6028.check
index 885fc3c9f3..c8c4b1cb4c 100644
--- a/test/files/run/delambdafy_t6028.check
+++ b/test/files/run/delambdafy_t6028.check
@@ -38,10 +38,18 @@ package <empty> {
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
};
- final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
- MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1);
+ final <stable> private[this] def MethodLocalObject$lzycompute$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
+ T.this.synchronized({
+ if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
+ MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1);
+ scala.runtime.BoxedUnit.UNIT
+ });
MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]()
};
+ final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
+ T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1)
+ else
+ MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]();
abstract trait MethodLocalTrait$1$class extends Object with T#MethodLocalTrait$1 {
def /*MethodLocalTrait$1$class*/$init$(barParam$1: String): Unit = {
()
diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check
index a768f32976..b3d20a9902 100644
--- a/test/files/run/t6028.check
+++ b/test/files/run/t6028.check
@@ -50,10 +50,18 @@ package <empty> {
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
};
- final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
- MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1);
+ final <stable> private[this] def MethodLocalObject$lzycompute$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
+ T.this.synchronized({
+ if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
+ MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1);
+ scala.runtime.BoxedUnit.UNIT
+ });
MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]()
};
+ final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
+ T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1)
+ else
+ MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]();
abstract trait MethodLocalTrait$1$class extends Object with T#MethodLocalTrait$1 {
def /*MethodLocalTrait$1$class*/$init$(barParam$1: Int): Unit = {
()
diff --git a/test/files/run/trait-defaults-modules.scala b/test/files/run/trait-defaults-modules.scala
new file mode 100644
index 0000000000..93fc74baff
--- /dev/null
+++ b/test/files/run/trait-defaults-modules.scala
@@ -0,0 +1,20 @@
+trait T1 { def a: Any }
+
+trait T2 extends T1 { object a; object b; private object c; def usec: Any = c}
+trait T3 extends T2
+
+class C1 extends T1 { object a; object b }
+class C2 extends C1
+class C3 extends T2
+class C4 extends T3
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ val (c1, c2, c3, c4) = (new C1, new C2, new C3, new C4)
+ c1.a; c1.b; (c1: T1).a
+ c2.a; c2.b; (c2: T1).a
+ c3.a; c3.b; (c3: T1).a; c3.usec
+ c4.a; c4.b; (c4: T1).a; c4.usec
+ }
+
+}
diff --git a/test/files/run/trait-defaults-modules2/T_1.scala b/test/files/run/trait-defaults-modules2/T_1.scala
new file mode 100644
index 0000000000..962acdade1
--- /dev/null
+++ b/test/files/run/trait-defaults-modules2/T_1.scala
@@ -0,0 +1,4 @@
+trait T {
+ private object O
+ def useO: Any = O
+}
diff --git a/test/files/run/trait-defaults-modules2/Test_2.scala b/test/files/run/trait-defaults-modules2/Test_2.scala
new file mode 100644
index 0000000000..a1c49f5ddd
--- /dev/null
+++ b/test/files/run/trait-defaults-modules2/Test_2.scala
@@ -0,0 +1,5 @@
+object Test extends T {
+ def main(args: Array[String]): Unit = {
+ useO
+ }
+}
diff --git a/test/files/run/trait-defaults-modules3.scala b/test/files/run/trait-defaults-modules3.scala
new file mode 100644
index 0000000000..8790a95f4c
--- /dev/null
+++ b/test/files/run/trait-defaults-modules3.scala
@@ -0,0 +1,8 @@
+object Test {
+ def main(args: Array[String]): Unit = {
+ object O
+ val x = O
+ val y = O
+ assert(x eq y)
+ }
+}