From ee24807f8706bb91b9d854eaba39f0ddd9c2a054 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 28 Jan 2013 08:42:27 +0100 Subject: Move a test from pos to run to highlight bytecode deficiencies. We'll address them in subsequent commits. --- test/files/pos/t6259.scala | 47 -------------------------------------- test/files/run/t6259.scala | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 47 deletions(-) delete mode 100644 test/files/pos/t6259.scala create mode 100644 test/files/run/t6259.scala diff --git a/test/files/pos/t6259.scala b/test/files/pos/t6259.scala deleted file mode 100644 index 43361c417e..0000000000 --- a/test/files/pos/t6259.scala +++ /dev/null @@ -1,47 +0,0 @@ -package t6259 - -import scala.reflect.runtime.universe._ - -class A[X](implicit val tt: TypeTag[X]) {} -object B extends A[String] - -object C { - object D extends A[String] -} - -trait E { - object F extends A[String] -} - -class G { - object H extends A[String] -} - -object Test { - val x = { - object InVal extends A[String] - 5 - } - -} - -// Note: Both of these fail right now. - -trait NeedsEarly { - val x: AnyRef -} - -object Early extends { - // Drops to this.getClass and is not ok... - val x = { object EarlyOk extends A[String]; EarlyOk } -} with NeedsEarly - - -class DoubleTrouble[X](x: AnyRef)(implicit override val tt: TypeTag[X]) extends A[X] - -object DoubleOk extends DoubleTrouble[String]({ - // Drops to this.getClass and is an issue - object InnerTrouble extends A[String]; - InnerTrouble -}) - diff --git a/test/files/run/t6259.scala b/test/files/run/t6259.scala new file mode 100644 index 0000000000..a5a7bf9043 --- /dev/null +++ b/test/files/run/t6259.scala @@ -0,0 +1,56 @@ +import scala.reflect.runtime.universe._ + +class A[X](implicit val tt: TypeTag[X]) {} +object B extends A[String] + +object C { + object D extends A[String] +} + +trait E { + object F extends A[String] +} + +class G { + object H extends A[String] +} + +object HasX { + val x = { + object InVal extends A[String] + InVal + 5 + } + +} + +trait NeedsEarly { + val x: AnyRef +} + +object Early extends { + // Drops to this.getClass and is not ok... + val x = { object EarlyOk extends A[String]; EarlyOk } +} with NeedsEarly + + +class DoubleTrouble[X](x: AnyRef)(implicit override val tt: TypeTag[X]) extends A[X] + +object DoubleOk extends DoubleTrouble[String]({ + // Drops to this.getClass and is an issue + object InnerTrouble extends A[String]; + InnerTrouble +}) + +object Test extends App { + B + C.D + val e = new E {}; e.F + val g = new G; g.H + + //locally(HasX.x) TODO sort out VerifyError in HasX$InVal$2$. by accounting for nesting in Namer#inConstructorFlag + // locally(Early.x) TODO sort out VerifyError in Early$. + // DoubleOk TODO sort out VerifyError in DoubleOk$. +} + + -- cgit v1.2.3 From fd6125428af90b02cb8969a53586f3551e275b0f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 28 Jan 2013 09:10:37 +0100 Subject: SI-6666 Account for nesting in setting INCONSTRUCTOR This flag is calcualed in Namers, and assigned to class and module class symbols that are defined in self/super-calls, and in early definitions. For example, class D is INCONSTRUCTOR in each case below: class C extends Super({class D; ()}) class C(a: Any) { def this(a: Any) = this({class D; ()}) } new { val x = { class D; () } with Super(()) But, the calculation of this flag failed to account for nesting, so it was not set in cases like: class C(a: Any) { def this(a: Any) = this({val x = {class D; ()}; x}) } This patch searches the enclosing context chain, rather than just the immediate context. The search is terminated at the first non term-owned context. In the following example, this avoids marking `E` as INCONSTRUCTOR; only `D` should be. class C extends Super({class D { class E }; ()}) This closes SI-6259 and SI-6506, and fixes one problem in the recently reopened SI-6957. --- src/compiler/scala/tools/nsc/typechecker/Namers.scala | 9 ++++++--- test/files/run/t6259.scala | 2 +- test/files/run/t6506.scala | 8 ++++++++ test/files/run/t6666a.scala | 16 ++++++++++++++++ test/files/run/t6957.scala | 8 ++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 test/files/run/t6506.scala create mode 100644 test/files/run/t6666a.scala create mode 100644 test/files/run/t6957.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 98b6264051..967a3214f2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -123,9 +123,12 @@ trait Namers extends MethodSynthesis { def setPrivateWithin(tree: MemberDef, sym: Symbol): Symbol = setPrivateWithin(tree, sym, tree.mods) - def inConstructorFlag: Long = - if (owner.isConstructor && !context.inConstructorSuffix || owner.isEarlyInitialized) INCONSTRUCTOR - else 0l + def inConstructorFlag: Long = { + val termOwnedContexts: List[Context] = context.enclosingContextChain.takeWhile(_.owner.isTerm) + val constructorNonSuffix = termOwnedContexts exists (c => c.owner.isConstructor && !c.inConstructorSuffix) + val earlyInit = termOwnedContexts exists (_.owner.isEarlyInitialized) + if (constructorNonSuffix || earlyInit) INCONSTRUCTOR else 0L + } def moduleClassFlags(moduleFlags: Long) = (moduleFlags & ModuleToClassFlags) | inConstructorFlag diff --git a/test/files/run/t6259.scala b/test/files/run/t6259.scala index a5a7bf9043..294c95e96b 100644 --- a/test/files/run/t6259.scala +++ b/test/files/run/t6259.scala @@ -48,7 +48,7 @@ object Test extends App { val e = new E {}; e.F val g = new G; g.H - //locally(HasX.x) TODO sort out VerifyError in HasX$InVal$2$. by accounting for nesting in Namer#inConstructorFlag + locally(HasX.x) // locally(Early.x) TODO sort out VerifyError in Early$. // DoubleOk TODO sort out VerifyError in DoubleOk$. } diff --git a/test/files/run/t6506.scala b/test/files/run/t6506.scala new file mode 100644 index 0000000000..04d77c3c16 --- /dev/null +++ b/test/files/run/t6506.scala @@ -0,0 +1,8 @@ +object Test { + def main(args: Array[String]) { + new WL(new {} #:: S) with T + } + object S { def #::(a: Any): Any = () } + trait T + class WL(a: Any) +} diff --git a/test/files/run/t6666a.scala b/test/files/run/t6666a.scala new file mode 100644 index 0000000000..1d208a32e7 --- /dev/null +++ b/test/files/run/t6666a.scala @@ -0,0 +1,16 @@ +class A(a: Any) + +object Test { + def main(args: Array[String]): Unit = { + } + + val x: Unit = { + object InVal extends A({ + new {} // okay + val o = {new {}} // nesting triggers a VerifyError. + null + }); + InVal; + () + }; +} diff --git a/test/files/run/t6957.scala b/test/files/run/t6957.scala new file mode 100644 index 0000000000..d0bf8e7b5e --- /dev/null +++ b/test/files/run/t6957.scala @@ -0,0 +1,8 @@ +object Test { + def main(args: Array[String]) { + class Foo + class Parent(f:Foo) + class Child extends Parent({val x=new Foo{}; x}) + new Child + } +} -- cgit v1.2.3 From 4c34280e5bb3e48db35d97d890e4f5f1c5fb3a26 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 28 Jan 2013 09:26:31 +0100 Subject: Add a test case from the comments of SI-6666. This one lands in the new implementation restriction which beats the VerifyError. --- test/files/neg/t6666b.check | 4 ++++ test/files/neg/t6666b.scala | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 test/files/neg/t6666b.check create mode 100644 test/files/neg/t6666b.scala diff --git a/test/files/neg/t6666b.check b/test/files/neg/t6666b.check new file mode 100644 index 0000000000..090bee800d --- /dev/null +++ b/test/files/neg/t6666b.check @@ -0,0 +1,4 @@ +t6666b.scala:6: error: Implementation restriction: access of object TreeOrd$1 from object TreeOrd$2, would require illegal premature access to the unconstructed `this` of class Test + implicit object TreeOrd extends Ordering[K](){ + ^ +one error found diff --git a/test/files/neg/t6666b.scala b/test/files/neg/t6666b.scala new file mode 100644 index 0000000000..0b8f1aa1a3 --- /dev/null +++ b/test/files/neg/t6666b.scala @@ -0,0 +1,17 @@ +import scala.collection.immutable.TreeMap +import scala.math.Ordering + +class Test[K](param:TreeMap[K,Int]){ + def this() = this({ + implicit object TreeOrd extends Ordering[K](){ + def compare(a: K, b: K) = { + -1 + } + } + new TreeMap[K, Int]() + }) +} + +object Test extends App { + new Test() +} -- cgit v1.2.3 From 66fa1f22ac058e87350304388eca17aedc1e4b64 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 24 Jan 2013 00:35:19 +0100 Subject: Broader checks for poisonous this references. Replaces more VerifyErrors with implementation restrictions. --- .../scala/tools/nsc/transform/ExplicitOuter.scala | 64 ++++++++++++++++------ .../scala/tools/nsc/transform/LambdaLift.scala | 25 +++------ test/files/neg/t6666.check | 18 ++---- test/files/neg/t6666.scala | 19 ------- test/files/neg/t6666b.check | 11 ++-- test/files/neg/t6666b.scala | 38 ++++++++----- test/files/neg/t6666c.check | 10 ++++ test/files/neg/t6666c.scala | 8 +++ test/files/neg/t6666d.check | 4 ++ test/files/neg/t6666d.scala | 18 ++++++ test/files/neg/t6666e.check | 4 ++ test/files/neg/t6666e.scala | 9 +++ 12 files changed, 146 insertions(+), 82 deletions(-) create mode 100644 test/files/neg/t6666c.check create mode 100644 test/files/neg/t6666c.scala create mode 100644 test/files/neg/t6666d.check create mode 100644 test/files/neg/t6666d.scala create mode 100644 test/files/neg/t6666e.check create mode 100644 test/files/neg/t6666e.scala diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 1003d417f6..a86d8aa2d6 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -8,6 +8,7 @@ package transform import symtab._ import Flags.{ CASE => _, _ } +import scala.collection.mutable import scala.collection.mutable.ListBuffer import matching.{ Patterns, ParallelMatching } @@ -209,6 +210,8 @@ abstract class ExplicitOuter extends InfoTransform /** The first outer selection from currently transformed tree. * The result is typed but not positioned. + * + * Will return `EmptyTree` if there is no outer accessor because of a premature self reference. */ protected def outerValue: Tree = if (outerParam != NoSymbol) ID(outerParam) @@ -218,25 +221,34 @@ abstract class ExplicitOuter extends InfoTransform * The result is typed but not positioned. * If the outer access is from current class and current class is final * take outer field instead of accessor + * + * Will return `EmptyTree` if there is no outer accessor because of a premature self reference. */ private def outerSelect(base: Tree): Tree = { - val outerAcc = outerAccessor(base.tpe.typeSymbol.toInterface) - val currentClass = this.currentClass //todo: !!! if this line is removed, we get a build failure that protected$currentClass need an override modifier - // outerFld is the $outer field of the current class, if the reference can - // use it (i.e. reference is allowed to be of the form this.$outer), - // otherwise it is NoSymbol - val outerFld = - if (outerAcc.owner == currentClass && + val baseSym = base.tpe.typeSymbol.toInterface + val outerAcc = outerAccessor(baseSym) + if (outerAcc == NoSymbol && baseSym.ownersIterator.exists(isUnderConstruction)) { + // e.g neg/t6666.scala + // The caller will report the error with more information. + EmptyTree + } else { + val currentClass = this.currentClass //todo: !!! if this line is removed, we get a build failure that protected$currentClass need an override modifier + // outerFld is the $outer field of the current class, if the reference can + // use it (i.e. reference is allowed to be of the form this.$outer), + // otherwise it is NoSymbol + val outerFld = + if (outerAcc.owner == currentClass && base.tpe =:= currentClass.thisType && outerAcc.owner.isEffectivelyFinal) - outerField(currentClass) suchThat (_.owner == currentClass) - else - NoSymbol - val path = - if (outerFld != NoSymbol) Select(base, outerFld) - else Apply(Select(base, outerAcc), Nil) - - localTyper typed path + outerField(currentClass) suchThat (_.owner == currentClass) + else + NoSymbol + val path = + if (outerFld != NoSymbol) Select(base, outerFld) + else Apply(Select(base, outerAcc), Nil) + + localTyper typed path + } } /** The path @@ -256,6 +268,17 @@ abstract class ExplicitOuter extends InfoTransform else outerPath(outerSelect(base), from.outerClass, to) } + + /** The stack of constructor symbols in which a call to this() or to the super + * constructor is active. + */ + protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls exists (_.owner == clazz) + protected val selfOrSuperCalls = mutable.Stack[Symbol]() + @inline protected def inSelfOrSuperCall[A](sym: Symbol)(a: => A) = { + selfOrSuperCalls push sym + try a finally selfOrSuperCalls.pop() + } + override def transform(tree: Tree): Tree = { val savedOuterParam = outerParam try { @@ -269,7 +292,10 @@ abstract class ExplicitOuter extends InfoTransform } case _ => } - super.transform(tree) + if (treeInfo isSelfOrSuperConstrCall tree) // TODO also handle (and test) (treeInfo isEarlyDef tree) + inSelfOrSuperCall(currentOwner)(super.transform(tree)) + else + super.transform(tree) } finally outerParam = savedOuterParam } @@ -335,7 +361,8 @@ abstract class ExplicitOuter extends InfoTransform /** The definition tree of the outer accessor of current class */ - def outerFieldDef: Tree = VAL(outerField(currentClass)) === EmptyTree + def outerFieldDef: Tree = + VAL(outerField(currentClass)) === EmptyTree /** The definition tree of the outer accessor of current class */ @@ -483,6 +510,9 @@ abstract class ExplicitOuter extends InfoTransform val clazz = sym.owner val vparamss1 = if (isInner(clazz)) { // (4) + if (isUnderConstruction(clazz.outerClass)) { + reporter.error(tree.pos, s"Implementation restriction: ${clazz.fullLocationString} requires premature access to ${clazz.outerClass}.") + } val outerParam = sym.newValueParameter(nme.OUTER, sym.pos) setInfo clazz.outerClass.thisType ((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index a4f75f424f..d912b76f68 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -317,7 +317,7 @@ abstract class LambdaLift extends InfoTransform { else searchIn(currentOwner) } - private def memberRef(sym: Symbol) = { + private def memberRef(sym: Symbol): Tree = { val clazz = sym.owner.enclClass //Console.println("memberRef from "+currentClass+" to "+sym+" in "+clazz) def prematureSelfReference() { @@ -331,12 +331,17 @@ abstract class LambdaLift extends InfoTransform { if (clazz == currentClass) gen.mkAttributedThis(clazz) else { sym resetFlag (LOCAL | PRIVATE) - if (selfOrSuperCalls exists (_.owner == clazz)) { + if (isUnderConstruction(clazz)) { prematureSelfReference() EmptyTree } else if (clazz.isStaticOwner) gen.mkAttributedQualifier(clazz.thisType) - else outerPath(outerValue, currentClass.outerClass, clazz) + else { + outerValue match { + case EmptyTree => prematureSelfReference(); return EmptyTree + case o => outerPath(o, currentClass.outerClass, clazz) + } + } } Select(qual, sym) setType sym.tpe } @@ -507,25 +512,13 @@ abstract class LambdaLift extends InfoTransform { private def preTransform(tree: Tree) = super.transform(tree) setType lifted(tree.tpe) - /** The stack of constructor symbols in which a call to this() or to the super - * constructor is active. - */ - private val selfOrSuperCalls = mutable.Stack[Symbol]() - @inline private def inSelfOrSuperCall[A](sym: Symbol)(a: => A) = try { - selfOrSuperCalls push sym - a - } finally selfOrSuperCalls.pop() - override def transform(tree: Tree): Tree = tree match { case Select(ReferenceToBoxed(idt), elem) if elem == nme.elem => postTransform(preTransform(idt), isBoxedRef = false) case ReferenceToBoxed(idt) => postTransform(preTransform(idt), isBoxedRef = true) case _ => - def transformTree = postTransform(preTransform(tree)) - if (treeInfo isSelfOrSuperConstrCall tree) - inSelfOrSuperCall(currentOwner)(transformTree) - else transformTree + postTransform(preTransform(tree)) } /** Transform statements and add lifted definitions to them. */ diff --git a/test/files/neg/t6666.check b/test/files/neg/t6666.check index d0378173ea..63145f6ed7 100644 --- a/test/files/neg/t6666.check +++ b/test/files/neg/t6666.check @@ -16,25 +16,19 @@ t6666.scala:54: error: Implementation restriction: access of value x$7 in class t6666.scala:58: error: Implementation restriction: access of method x$8 in class C3 from anonymous class 9, would require illegal premature access to the unconstructed `this` of class C3 F.hof(() => x) ^ -t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$5, would require illegal premature access to the unconstructed `this` of class C4 +t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$3, would require illegal premature access to the unconstructed `this` of class C4 object Nested { def xx = x} ^ -t6666.scala:68: error: Implementation restriction: access of method x$10 in class C5 from object Nested$6, would require illegal premature access to the unconstructed `this` of class C5 - object Nested { def xx = x} - ^ -t6666.scala:83: error: Implementation restriction: access of method x$12 in class C11 from anonymous class 12, would require illegal premature access to the unconstructed `this` of class C11 +t6666.scala:76: error: Implementation restriction: access of method x$11 in class C11 from anonymous class 12, would require illegal premature access to the unconstructed `this` of class C11 F.byname(x) ^ -t6666.scala:102: error: Implementation restriction: access of method x$13 in class C13 from anonymous class 13, would require illegal premature access to the unconstructed `this` of class C13 +t6666.scala:95: error: Implementation restriction: access of method x$12 in class C13 from anonymous class 13, would require illegal premature access to the unconstructed `this` of class C13 F.hof(() => x) ^ -t6666.scala:111: error: Implementation restriction: access of method x$14 in class C14 from object Nested$7, would require illegal premature access to the unconstructed `this` of class C14 +t6666.scala:104: error: Implementation restriction: access of method x$13 in class C14 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C14 object Nested { def xx = x} ^ -t6666.scala:122: error: Implementation restriction: access of method x$15 in class C15 from object Nested$8, would require illegal premature access to the unconstructed `this` of class C15 - object Nested { def xx = x} - ^ -t6666.scala:131: error: Implementation restriction: access of method foo$1 in class COuter from class CInner$1, would require illegal premature access to the unconstructed `this` of class COuter +t6666.scala:112: error: Implementation restriction: access of method foo$1 in class COuter from class CInner$1, would require illegal premature access to the unconstructed `this` of class COuter class CInner extends C({foo}) ^ -13 errors found +11 errors found diff --git a/test/files/neg/t6666.scala b/test/files/neg/t6666.scala index d37ffaf141..19073884bd 100644 --- a/test/files/neg/t6666.scala +++ b/test/files/neg/t6666.scala @@ -62,13 +62,6 @@ class C4 extends C({ object Nested { def xx = x} Nested.xx }) -class C5 extends C({ - def x = "".toString - val y = { - object Nested { def xx = x} - Nested.xx - } -}) // okay, for same reason as O6 class C6 extends C({ @@ -114,18 +107,6 @@ class C14(a: Any) { } } -class C15(a: Any) { - def this() = { - this({ - def x = "".toString - val y = { - object Nested { def xx = x} - Nested.xx - } - }) - } -} - class COuter extends C({ def foo = 0 class CInner extends C({foo}) diff --git a/test/files/neg/t6666b.check b/test/files/neg/t6666b.check index 090bee800d..c3ffc7cfa9 100644 --- a/test/files/neg/t6666b.check +++ b/test/files/neg/t6666b.check @@ -1,4 +1,7 @@ -t6666b.scala:6: error: Implementation restriction: access of object TreeOrd$1 from object TreeOrd$2, would require illegal premature access to the unconstructed `this` of class Test - implicit object TreeOrd extends Ordering[K](){ - ^ -one error found +t6666b.scala:11: error: Implementation restriction: access of method x$1 in class C5 from object Nested$3, would require illegal premature access to the unconstructed `this` of class C5 + object Nested { def xx = x} + ^ +t6666b.scala:22: error: Implementation restriction: access of method x$2 in class C15 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C15 + object Nested { def xx = x} + ^ +two errors found diff --git a/test/files/neg/t6666b.scala b/test/files/neg/t6666b.scala index 0b8f1aa1a3..205ded76e5 100644 --- a/test/files/neg/t6666b.scala +++ b/test/files/neg/t6666b.scala @@ -1,17 +1,27 @@ -import scala.collection.immutable.TreeMap -import scala.math.Ordering - -class Test[K](param:TreeMap[K,Int]){ - def this() = this({ - implicit object TreeOrd extends Ordering[K](){ - def compare(a: K, b: K) = { - -1 - } - } - new TreeMap[K, Int]() - }) +class C(a: Any) +object F { + def byname(a: => Any) = println(a) + def hof(a: () => Any) = println(a()) } -object Test extends App { - new Test() + +class C5 extends C({ + def x = "".toString + val y = { + object Nested { def xx = x} + Nested.xx + } +}) + + +class C15(a: Any) { + def this() = { + this({ + def x = "".toString + val y = { + object Nested { def xx = x} + Nested.xx + } + }) + } } diff --git a/test/files/neg/t6666c.check b/test/files/neg/t6666c.check new file mode 100644 index 0000000000..8fb9f4ba14 --- /dev/null +++ b/test/files/neg/t6666c.check @@ -0,0 +1,10 @@ +t6666c.scala:2: error: Implementation restriction: access of method x$1 in class D from object X$4, would require illegal premature access to the unconstructed `this` of class D +class D extends C({def x = 0; object X { x }}) + ^ +t6666c.scala:5: error: Implementation restriction: access of method x$2 in class D1 from object X$5, would require illegal premature access to the unconstructed `this` of class D1 +class D1 extends C1({def x = 0; () => {object X { x }}}) + ^ +t6666c.scala:8: error: Implementation restriction: access of method x$3 from object X$6, would require illegal premature access to the unconstructed `this` of anonymous class 2 +class D2 extends C2({def x = 0; object X { x }}) + ^ +three errors found diff --git a/test/files/neg/t6666c.scala b/test/files/neg/t6666c.scala new file mode 100644 index 0000000000..76cc358307 --- /dev/null +++ b/test/files/neg/t6666c.scala @@ -0,0 +1,8 @@ +class C(a: Any) +class D extends C({def x = 0; object X { x }}) + +class C1(a: () => Any) +class D1 extends C1({def x = 0; () => {object X { x }}}) + +class C2(a: => Any) +class D2 extends C2({def x = 0; object X { x }}) diff --git a/test/files/neg/t6666d.check b/test/files/neg/t6666d.check new file mode 100644 index 0000000000..b4785f0129 --- /dev/null +++ b/test/files/neg/t6666d.check @@ -0,0 +1,4 @@ +t6666d.scala:7: error: Implementation restriction: access of object TreeOrd$1 from object TreeOrd$2, would require illegal premature access to the unconstructed `this` of class Test + implicit object TreeOrd extends Ordering[K](){ + ^ +one error found diff --git a/test/files/neg/t6666d.scala b/test/files/neg/t6666d.scala new file mode 100644 index 0000000000..49a688f91b --- /dev/null +++ b/test/files/neg/t6666d.scala @@ -0,0 +1,18 @@ + +import scala.collection.immutable.TreeMap +import scala.math.Ordering + +class Test[K](param:TreeMap[K,Int]){ + def this() = this({ + implicit object TreeOrd extends Ordering[K](){ + def compare(a: K, b: K) = { + -1 + } + } + new TreeMap[K, Int]() + }) +} + +object Test extends App { + new Test() +} diff --git a/test/files/neg/t6666e.check b/test/files/neg/t6666e.check new file mode 100644 index 0000000000..9fcc3ab718 --- /dev/null +++ b/test/files/neg/t6666e.check @@ -0,0 +1,4 @@ +t6666e.scala:8: error: Implementation restriction: anonymous class $anonfun requires premature access to class Crash. + this(Nil.collect{case x =>}) + ^ +one error found diff --git a/test/files/neg/t6666e.scala b/test/files/neg/t6666e.scala new file mode 100644 index 0000000000..120a5878b2 --- /dev/null +++ b/test/files/neg/t6666e.scala @@ -0,0 +1,9 @@ + +import scala.collection.immutable.TreeMap +import scala.math.Ordering + + +class Crash(a: Any) { + def this() = + this(Nil.collect{case x =>}) +} -- cgit v1.2.3 From 275b341545a3c4e633bd735cf45ccc1956a4233e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 29 Jan 2013 13:02:01 +0100 Subject: SI-6666 Catch VerifyErrors in the making in early defs. As we did for self/super calls, add a backstop into explicitouter and lambdalift to check when we try to get an outer pointer to an under-construction instance. --- src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala | 10 +++++----- test/files/neg/t6666.check | 9 ++++++--- test/files/neg/t6666.scala | 10 +++++++++- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index a86d8aa2d6..0575254c26 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -269,10 +269,10 @@ abstract class ExplicitOuter extends InfoTransform } - /** The stack of constructor symbols in which a call to this() or to the super - * constructor is active. + /** The stack of class symbols in which a call to this() or to the super + * constructor, or early definition is active */ - protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls exists (_.owner == clazz) + protected def isUnderConstruction(clazz: Symbol) = selfOrSuperCalls contains clazz protected val selfOrSuperCalls = mutable.Stack[Symbol]() @inline protected def inSelfOrSuperCall[A](sym: Symbol)(a: => A) = { selfOrSuperCalls push sym @@ -292,8 +292,8 @@ abstract class ExplicitOuter extends InfoTransform } case _ => } - if (treeInfo isSelfOrSuperConstrCall tree) // TODO also handle (and test) (treeInfo isEarlyDef tree) - inSelfOrSuperCall(currentOwner)(super.transform(tree)) + if ((treeInfo isSelfOrSuperConstrCall tree) || (treeInfo isEarlyDef tree)) + inSelfOrSuperCall(currentOwner.owner)(super.transform(tree)) else super.transform(tree) } diff --git a/test/files/neg/t6666.check b/test/files/neg/t6666.check index 63145f6ed7..6337d4c7d9 100644 --- a/test/files/neg/t6666.check +++ b/test/files/neg/t6666.check @@ -16,7 +16,7 @@ t6666.scala:54: error: Implementation restriction: access of value x$7 in class t6666.scala:58: error: Implementation restriction: access of method x$8 in class C3 from anonymous class 9, would require illegal premature access to the unconstructed `this` of class C3 F.hof(() => x) ^ -t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$3, would require illegal premature access to the unconstructed `this` of class C4 +t6666.scala:62: error: Implementation restriction: access of method x$9 in class C4 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C4 object Nested { def xx = x} ^ t6666.scala:76: error: Implementation restriction: access of method x$11 in class C11 from anonymous class 12, would require illegal premature access to the unconstructed `this` of class C11 @@ -25,10 +25,13 @@ t6666.scala:76: error: Implementation restriction: access of method x$11 in clas t6666.scala:95: error: Implementation restriction: access of method x$12 in class C13 from anonymous class 13, would require illegal premature access to the unconstructed `this` of class C13 F.hof(() => x) ^ -t6666.scala:104: error: Implementation restriction: access of method x$13 in class C14 from object Nested$4, would require illegal premature access to the unconstructed `this` of class C14 +t6666.scala:104: error: Implementation restriction: access of method x$13 in class C14 from object Nested$5, would require illegal premature access to the unconstructed `this` of class C14 object Nested { def xx = x} ^ t6666.scala:112: error: Implementation restriction: access of method foo$1 in class COuter from class CInner$1, would require illegal premature access to the unconstructed `this` of class COuter class CInner extends C({foo}) ^ -11 errors found +t6666.scala:118: error: Implementation restriction: access of method x$14 in class CEarly from object Nested$6, would require illegal premature access to the unconstructed `this` of class CEarly + object Nested { def xx = x} + ^ +12 errors found diff --git a/test/files/neg/t6666.scala b/test/files/neg/t6666.scala index 19073884bd..1919ea3ca9 100644 --- a/test/files/neg/t6666.scala +++ b/test/files/neg/t6666.scala @@ -110,4 +110,12 @@ class C14(a: Any) { class COuter extends C({ def foo = 0 class CInner extends C({foo}) -}) \ No newline at end of file +}) + + +class CEarly(a: Any) extends { + val early = {def x = "".toString + object Nested { def xx = x} + Nested.xx + } +} with AnyRef \ No newline at end of file -- cgit v1.2.3 From 81fa8316092e295c1a893b6fcf65568c11fffb58 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 2 Feb 2013 13:18:00 +0100 Subject: Class symbols can't be contravariant. During development of the fix for SI-6666, I encountered: % test/files/pos/t4842.scala test/files/pos/t4842.scala:10: error: contravariant class Bar occurs in covariant position in type ()this.Bar of constructor Bar this(new { class Bar { println(Bar.this); new { println(Bar.this) } }; new Bar } ) // okay I had incorrectly set the INCONSTRUCTOR flag on the class symbol `Bar`. (It isn't directly in the self constructor call, as it is nested an intervening anonymous class.) But, this flag shares a slot with CONTRAVARIANT, and the variance validation intepreted it as such. ClassSymbol already has this code to resolve the ambiguous flags for display purposes: override def resolveOverloadedFlag(flag: Long) = flag match { case INCONSTRUCTOR => "" // INCONSTRUCTOR / CONTRAVARIANT / LABEL case EXISTENTIAL => "" // EXISTENTIAL / MIXEDIN case IMPLCLASS => "" // IMPLCLASS / PRESUPER case _ => super.resolveOverloadedFlag(flag) } This commit overrides `isContravariant` to reflect the same logic. --- .../scala/tools/nsc/interpreter/IMain.scala | 4 ++- src/reflect/scala/reflect/internal/Symbols.scala | 1 + test/files/run/class-symbol-contravariant.check | 36 ++++++++++++++++++++++ test/files/run/class-symbol-contravariant.scala | 15 +++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 test/files/run/class-symbol-contravariant.check create mode 100644 test/files/run/class-symbol-contravariant.scala diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index b46d28dec3..4b6466c079 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -262,7 +262,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = { settings.outputDirs setSingleOutput virtualDirectory settings.exposeEmptyPackage.value = true - new Global(settings, reporter) with ReplGlobal + new Global(settings, reporter) with ReplGlobal { + override def toString: String = "" + } } /** Parent classloader. Overridable. */ diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index a4287fb181..923dac7498 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2867,6 +2867,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => final override def isNonClassType = false final override def isAbstractType = false final override def isAliasType = false + final override def isContravariant = false override def isAbstractClass = this hasFlag ABSTRACT override def isCaseClass = this hasFlag CASE diff --git a/test/files/run/class-symbol-contravariant.check b/test/files/run/class-symbol-contravariant.check new file mode 100644 index 0000000000..987f215bca --- /dev/null +++ b/test/files/run/class-symbol-contravariant.check @@ -0,0 +1,36 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> :power +** Power User mode enabled - BEEP WHIR GYVE ** +** :phase has been set to 'typer'. ** +** scala.tools.nsc._ has been imported ** +** global._, definitions._ also imported ** +** Try :help, :vals, power. ** + +scala> val u = rootMirror.universe +u: $r.intp.global.type = + +scala> import u._, scala.reflect.internal.Flags +import u._ +import scala.reflect.internal.Flags + +scala> class C +defined class C + +scala> val sym = u.typeOf[C].typeSymbol +sym: u.Symbol = class C + +scala> sym.isContravariant +res0: Boolean = false + +scala> sym setFlag Flags.INCONSTRUCTOR +res1: sym.type = class C + +scala> sym.isClassLocalToConstructor +res2: Boolean = true + +scala> sym.isContravariant // was true +res3: Boolean = false + +scala> diff --git a/test/files/run/class-symbol-contravariant.scala b/test/files/run/class-symbol-contravariant.scala new file mode 100644 index 0000000000..6a84944e3b --- /dev/null +++ b/test/files/run/class-symbol-contravariant.scala @@ -0,0 +1,15 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + override def code = """ + |:power + |val u = rootMirror.universe + |import u._, scala.reflect.internal.Flags + |class C + |val sym = u.typeOf[C].typeSymbol + |sym.isContravariant + |sym setFlag Flags.INCONSTRUCTOR + |sym.isClassLocalToConstructor + |sym.isContravariant // was true + |""".stripMargin.trim +} \ No newline at end of file -- cgit v1.2.3