From d71f59ebda35bd58338b13eb55a6913a38d6d90e Mon Sep 17 00:00:00 2001 From: Kato Kazuyoshi Date: Wed, 16 Jan 2013 23:46:03 +0900 Subject: SI-4976 Scaladoc: Add a source link to package objects --- test/scaladoc/run/package-object.check | 1 + test/scaladoc/run/package-object.scala | 1 + 2 files changed, 2 insertions(+) (limited to 'test') diff --git a/test/scaladoc/run/package-object.check b/test/scaladoc/run/package-object.check index 01dbcc682f..7da897a4f2 100644 --- a/test/scaladoc/run/package-object.check +++ b/test/scaladoc/run/package-object.check @@ -1,3 +1,4 @@ List(test.B, test.A, scala.AnyRef, scala.Any) List(B, A, AnyRef, Any) +Some((newSource,10)) Done. diff --git a/test/scaladoc/run/package-object.scala b/test/scaladoc/run/package-object.scala index 5fb5a4ddf1..f5c79b1332 100644 --- a/test/scaladoc/run/package-object.scala +++ b/test/scaladoc/run/package-object.scala @@ -11,6 +11,7 @@ object Test extends ScaladocModelTest { val p = root._package("test") println(p.linearizationTemplates) println(p.linearizationTypes) + println(p.inSource) } } -- cgit v1.2.3 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 (limited to 'test') 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 eab288442931f01b5bad2dcfa244a6183db0f4b6 Mon Sep 17 00:00:00 2001 From: James Iry Date: Wed, 23 Jan 2013 16:01:59 -0800 Subject: SI-5313 Do not eliminate stores that potentially wipe referenes Storing to local variables of reference or array type is indirectly observable because it potentially allows gc to collect an object. So this commit makes DeadCodeElimination mark a store necessary if it assigns to a local that potentially stored by a previous necessary store. --- .../nsc/backend/opt/DeadCodeElimination.scala | 84 ++++++++++++++++++++-- test/files/run/t5313.check | 8 +++ test/files/run/t5313.scala | 42 +++++++++++ 3 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 test/files/run/t5313.check create mode 100644 test/files/run/t5313.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index fee683ce3a..90d5686bd0 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -65,6 +65,9 @@ abstract class DeadCodeElimination extends SubComponent { /** what local variables have been accessed at least once? */ var accessedLocals: List[Local] = Nil + + /** Map from a local and a basic block to the instructions that store to that local in that basic block */ + val localStores = mutable.Map[(Local, BasicBlock), mutable.BitSet]() /** the current method. */ var method: IMethod = _ @@ -76,6 +79,7 @@ abstract class DeadCodeElimination extends SubComponent { if (m.hasCode) { debuglog("dead code elimination on " + m); dropOf.clear() + localStores.clear() m.code.blocks.clear() accessedLocals = m.params.reverse m.code.blocks ++= linearizer.linearize(m) @@ -104,10 +108,10 @@ abstract class DeadCodeElimination extends SubComponent { for (Pair(i, idx) <- bb.toList.zipWithIndex) { i match { - case LOAD_LOCAL(l) => + case LOAD_LOCAL(_) => defs = defs + Pair(((bb, idx)), rd.vars) - case STORE_LOCAL(_) => + case STORE_LOCAL(l) => /* SI-4935 Check whether a module is stack top, if so mark the instruction that loaded it * (otherwise any side-effects of the module's constructor go lost). * (a) The other two cases where a module's value is stored (STORE_FIELD and STORE_ARRAY_ITEM) @@ -125,6 +129,16 @@ abstract class DeadCodeElimination extends SubComponent { } } if (necessary) worklist += ((bb, idx)) + // add it to the localStores map + val key = (l, bb) + val set = if(localStores contains key) + localStores(key) + else { + val set = new mutable.BitSet + localStores.put(key, set) + set + } + set += idx case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | @@ -162,11 +176,18 @@ abstract class DeadCodeElimination extends SubComponent { def mark() { // log("Starting with worklist: " + worklist) while (!worklist.isEmpty) { - val (bb, idx) = worklist.iterator.next + val (bb, idx) = worklist.head worklist -= ((bb, idx)) debuglog("Marking instr: \tBB_" + bb + ": " + idx + " " + bb(idx)) val instr = bb(idx) + // adds the instrutions that define the stack values about to be consumed to the work list to + // be marked useful + def addDefs() = for ((bb1, idx1) <- rdef.findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) { + debuglog(s"\t${bb1(idx1)} is consumed by $instr") + worklist += ((bb1, idx1)) + } + if (!useful(bb)(idx)) { useful(bb) += idx dropOf.get(bb, idx) foreach { @@ -180,6 +201,58 @@ abstract class DeadCodeElimination extends SubComponent { worklist += ((bb1, idx1)) } + // Storing to local variables of reference or array type may be indirectly + // observable because may remove a reference to an object which may allow the object to be + // gc'd. See SI-5313. In this code I call the LOCAL_STORE(s) that immediately follow a + // LOCAL_STORE and that store to the same local "clobbers." If a LOCAL_STORE is marked + // useful then its clobbers must also go into the worklist to be marked useful. + case STORE_LOCAL(l1) if l1.kind.isRefOrArrayType => + addDefs() + // previously visited blocks tracked to prevent searching forever in a cycle + val inspected = mutable.Set[BasicBlock]() + // our worklist of blocks that still need to be checked + val blocksToBeInspected = mutable.Set[BasicBlock]() + + // Tries to find the next clobber of l1 starting at idx1 in bb1. + // if it finds any it adds them clobber to the worklist. + // If not it adds both bb's exception blocks and direct + // successor blocks to the uninspectedBlocks + def findClobber(idx1: Int, bb1: BasicBlock) { + val key = ((l1, bb1)) + val foundClobber = (localStores contains key) && { + def minIdx(s : mutable.BitSet) = if(s.isEmpty) -1 else s.min + + // find the smallest index greater than or equal to idx1 + val clobberIdx = minIdx(localStores(key) dropWhile (_ < idx1)) + if (clobberIdx == -1) + false + else { + debuglog(s"\t${bb1(clobberIdx)} is a clobber of $instr") + if (!useful(bb1)(clobberIdx)) worklist += ((bb1, clobberIdx)) + true + } + } + + // if we found a clobber in this block then we don't need to add the successors + // because they'll be picked up when the worklist comes around to work on that clobber + if (!foundClobber) { + blocksToBeInspected ++= (bb1.exceptionSuccessors filterNot inspected) + blocksToBeInspected ++= (bb1.directSuccessors filterNot inspected) + } + } + + // first search starting at the current index + // note we don't put bb in the inspected list yet because a loop may later force + // us back around to search from the beginning of bb + findClobber(idx + 1, bb) + // then loop until we've exhausted the set of uninspected blocks + while(!blocksToBeInspected.isEmpty) { + val bb1 = blocksToBeInspected.head + blocksToBeInspected -= bb1 + inspected += bb1 + findClobber(0, bb1) + } + case nw @ NEW(REFERENCE(sym)) => assert(nw.init ne null, "null new.init at: " + bb + ": " + idx + "(" + instr + ")") worklist += findInstruction(bb, nw.init) @@ -199,10 +272,7 @@ abstract class DeadCodeElimination extends SubComponent { () case _ => - for ((bb1, idx1) <- rdef.findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) { - debuglog("\tAdding " + bb1(idx1)) - worklist += ((bb1, idx1)) - } + addDefs() } } } diff --git a/test/files/run/t5313.check b/test/files/run/t5313.check new file mode 100644 index 0000000000..dd8791f109 --- /dev/null +++ b/test/files/run/t5313.check @@ -0,0 +1,8 @@ +STORE_LOCAL(variable kept1) +STORE_LOCAL(value result) +STORE_LOCAL(variable kept1) +STORE_LOCAL(variable kept2) +STORE_LOCAL(value kept3) +STORE_LOCAL(variable kept2) +STORE_LOCAL(variable kept4) +STORE_LOCAL(variable kept4) diff --git a/test/files/run/t5313.scala b/test/files/run/t5313.scala new file mode 100644 index 0000000000..b65311622a --- /dev/null +++ b/test/files/run/t5313.scala @@ -0,0 +1,42 @@ +import scala.tools.partest.IcodeTest + +object Test extends IcodeTest { + override def printIcodeAfterPhase = "dce" + + override def extraSettings: String = super.extraSettings + " -optimize" + + override def code = + """class Foo { + def foo = true + def bar = { + var kept1 = new Object + val result = new java.lang.ref.WeakReference(kept1) + kept1 = null // we can't eliminate this assigment because result can observe + // when the object has no more references. See SI-5313 + var erased2 = null // we can eliminate this store because it's never used + val erased3 = erased2 // and this + var erased4 = erased2 // and this + val erased5 = erased4 // and this + var kept2: Object = new Object // ultimately can't be eliminated + while(foo) { + val kept3 = kept2 + kept2 = null // this can't, because it clobbers x, which is ultimately used + erased4 = null // safe to eliminate + println(kept3) + } + var kept4 = new Object // have to keep, it's used + try + println(kept4) + catch { + case _ : Throwable => kept4 = null // have to keep, it clobbers kept4 which is used + } + result + } + }""".stripMargin + + override def show() { + val storeLocal = "STORE_LOCAL" + val lines1 = collectIcode("") filter (_ contains storeLocal) map (x => x.drop(x.indexOf(storeLocal))) + println(lines1 mkString "\n") + } +} -- cgit v1.2.3 From 9b4fa8382fe0ed6fef3ff91e2c153a1840c954b9 Mon Sep 17 00:00:00 2001 From: James Iry Date: Tue, 29 Jan 2013 09:13:30 -0800 Subject: SI-5313 Eliminate more stores by replacing clobbers with null stores When an unused store clobbers a previous store, replace it with storing a null. Don't mark clobbers as "used" so that the original clobber and all following clobbers can still be eliminated. --- .../nsc/backend/opt/DeadCodeElimination.scala | 128 +++++++++++++-------- test/files/run/t5313.check | 2 + test/files/run/t5313.scala | 8 +- 3 files changed, 87 insertions(+), 51 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 90d5686bd0..53270c0651 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -68,6 +68,9 @@ abstract class DeadCodeElimination extends SubComponent { /** Map from a local and a basic block to the instructions that store to that local in that basic block */ val localStores = mutable.Map[(Local, BasicBlock), mutable.BitSet]() + + /** Stores that clobber previous stores to array or ref locals. See SI-5313 */ + val clobbers = mutable.Set[(BasicBlock, Int)]() /** the current method. */ var method: IMethod = _ @@ -80,6 +83,7 @@ abstract class DeadCodeElimination extends SubComponent { debuglog("dead code elimination on " + m); dropOf.clear() localStores.clear() + clobbers.clear() m.code.blocks.clear() accessedLocals = m.params.reverse m.code.blocks ++= linearizer.linearize(m) @@ -201,58 +205,15 @@ abstract class DeadCodeElimination extends SubComponent { worklist += ((bb1, idx1)) } - // Storing to local variables of reference or array type may be indirectly - // observable because may remove a reference to an object which may allow the object to be - // gc'd. See SI-5313. In this code I call the LOCAL_STORE(s) that immediately follow a - // LOCAL_STORE and that store to the same local "clobbers." If a LOCAL_STORE is marked - // useful then its clobbers must also go into the worklist to be marked useful. case STORE_LOCAL(l1) if l1.kind.isRefOrArrayType => addDefs() - // previously visited blocks tracked to prevent searching forever in a cycle - val inspected = mutable.Set[BasicBlock]() - // our worklist of blocks that still need to be checked - val blocksToBeInspected = mutable.Set[BasicBlock]() - - // Tries to find the next clobber of l1 starting at idx1 in bb1. - // if it finds any it adds them clobber to the worklist. - // If not it adds both bb's exception blocks and direct - // successor blocks to the uninspectedBlocks - def findClobber(idx1: Int, bb1: BasicBlock) { - val key = ((l1, bb1)) - val foundClobber = (localStores contains key) && { - def minIdx(s : mutable.BitSet) = if(s.isEmpty) -1 else s.min - - // find the smallest index greater than or equal to idx1 - val clobberIdx = minIdx(localStores(key) dropWhile (_ < idx1)) - if (clobberIdx == -1) - false - else { - debuglog(s"\t${bb1(clobberIdx)} is a clobber of $instr") - if (!useful(bb1)(clobberIdx)) worklist += ((bb1, clobberIdx)) - true - } - } - - // if we found a clobber in this block then we don't need to add the successors - // because they'll be picked up when the worklist comes around to work on that clobber - if (!foundClobber) { - blocksToBeInspected ++= (bb1.exceptionSuccessors filterNot inspected) - blocksToBeInspected ++= (bb1.directSuccessors filterNot inspected) - } - } - - // first search starting at the current index - // note we don't put bb in the inspected list yet because a loop may later force - // us back around to search from the beginning of bb - findClobber(idx + 1, bb) - // then loop until we've exhausted the set of uninspected blocks - while(!blocksToBeInspected.isEmpty) { - val bb1 = blocksToBeInspected.head - blocksToBeInspected -= bb1 - inspected += bb1 - findClobber(0, bb1) - } - + // see SI-5313 + // search for clobbers of this store if we aren't doing l1 = null + // this doesn't catch the second store in x=null;l1=x; but in practice this catches + // a lot of null stores very cheaply + if (idx == 0 || bb(idx - 1) != CONSTANT(Constant(null))) + findClobbers(l1, bb, idx + 1) + case nw @ NEW(REFERENCE(sym)) => assert(nw.init ne null, "null new.init at: " + bb + ": " + idx + "(" + instr + ")") worklist += findInstruction(bb, nw.init) @@ -277,6 +238,67 @@ abstract class DeadCodeElimination extends SubComponent { } } } + + /** + * Finds and marks all clobbers of the given local starting in the given + * basic block at the given index + * + * Storing to local variables of reference or array type may be indirectly + * observable because it may remove a reference to an object which may allow the object + * to be gc'd. See SI-5313. In this code I call the LOCAL_STORE(s) that immediately follow a + * LOCAL_STORE and that store to the same local "clobbers." If a LOCAL_STORE is marked + * useful then its clobbers must go into the set of clobbers, which will be + * compensated for later + */ + def findClobbers(l: Local, bb: BasicBlock, idx: Int) { + // previously visited blocks tracked to prevent searching forever in a cycle + val inspected = mutable.Set[BasicBlock]() + // our worklist of blocks that still need to be checked + val blocksToBeInspected = mutable.Set[BasicBlock]() + + // Tries to find the next clobber of l1 in bb1 starting at idx1. + // if it finds one it adds the clobber to clobbers set for later + // handling. If not it adds the direct successor blocks to + // the uninspectedBlocks to try to find clobbers there. Either way + // it adds the exception successor blocks for further search + def findClobberInBlock(idx1: Int, bb1: BasicBlock) { + val key = ((l, bb1)) + val foundClobber = (localStores contains key) && { + def minIdx(s : mutable.BitSet) = if(s.isEmpty) -1 else s.min + + // find the smallest index greater than or equal to idx1 + val clobberIdx = minIdx(localStores(key) dropWhile (_ < idx1)) + if (clobberIdx == -1) + false + else { + debuglog(s"\t${bb1(clobberIdx)} is a clobber of ${bb(idx)}") + clobbers += ((bb1, clobberIdx)) + true + } + } + + // always need to look into the exception successors for additional clobbers + // because we don't know when flow might enter an exception handler + blocksToBeInspected ++= (bb1.exceptionSuccessors filterNot inspected) + // If we didn't find a clobber here then we need to look at successor blocks. + // if we found a clobber then we don't need to search in the direct successors + if (!foundClobber) { + blocksToBeInspected ++= (bb1.directSuccessors filterNot inspected) + } + } + + // first search starting at the current index + // note we don't put bb in the inspected list yet because a loop may later force + // us back around to search from the beginning of bb + findClobberInBlock(idx, bb) + // then loop until we've exhausted the set of uninspected blocks + while(!blocksToBeInspected.isEmpty) { + val bb1 = blocksToBeInspected.head + blocksToBeInspected -= bb1 + inspected += bb1 + findClobberInBlock(0, bb1) + } + } def sweep(m: IMethod) { val compensations = computeCompensations(m) @@ -306,6 +328,12 @@ abstract class DeadCodeElimination extends SubComponent { i match { case NEW(REFERENCE(sym)) => log(s"Eliminated instantation of $sym inside $m") + case STORE_LOCAL(l) if clobbers contains ((bb, idx)) => + // if an unused instruction was a clobber of a used store to a reference or array type + // then we'll replace it with the store of a null to make sure the reference is + // eliminated. See SI-5313 + bb emit CONSTANT(Constant(null)) + bb emit STORE_LOCAL(l) case _ => () } debuglog("Skipped: bb_" + bb + ": " + idx + "( " + i + ")") diff --git a/test/files/run/t5313.check b/test/files/run/t5313.check index dd8791f109..aa30c0efa5 100644 --- a/test/files/run/t5313.check +++ b/test/files/run/t5313.check @@ -6,3 +6,5 @@ STORE_LOCAL(value kept3) STORE_LOCAL(variable kept2) STORE_LOCAL(variable kept4) STORE_LOCAL(variable kept4) +STORE_LOCAL(variable kept5) +STORE_LOCAL(variable kept5) diff --git a/test/files/run/t5313.scala b/test/files/run/t5313.scala index b65311622a..64009e29af 100644 --- a/test/files/run/t5313.scala +++ b/test/files/run/t5313.scala @@ -13,6 +13,7 @@ object Test extends IcodeTest { val result = new java.lang.ref.WeakReference(kept1) kept1 = null // we can't eliminate this assigment because result can observe // when the object has no more references. See SI-5313 + kept1 = new Object // but we can eliminate this one because kept1 has already been clobbered var erased2 = null // we can eliminate this store because it's never used val erased3 = erased2 // and this var erased4 = erased2 // and this @@ -20,7 +21,7 @@ object Test extends IcodeTest { var kept2: Object = new Object // ultimately can't be eliminated while(foo) { val kept3 = kept2 - kept2 = null // this can't, because it clobbers x, which is ultimately used + kept2 = null // this can't, because it clobbers kept2, which is used erased4 = null // safe to eliminate println(kept3) } @@ -30,6 +31,11 @@ object Test extends IcodeTest { catch { case _ : Throwable => kept4 = null // have to keep, it clobbers kept4 which is used } + var kept5 = new Object + print(kept5) + kept5 = null // can't eliminate it's a clobber and it's used + print(kept5) + kept5 = null // can eliminate because we don't care about clobbers of nulls result } }""".stripMargin -- cgit v1.2.3 From 29892586382ef846b0ad46271c2fba9970943faf Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 30 Jan 2013 17:21:38 +0100 Subject: SI-6539 moves @compileTimeOnly away from scala-reflect The move is done to provide forward compatibility with 2.10.0. The annotation isn't replaced with one of the macro-based solutions right away (see comments for more information about those), because we lack necessary tech in 2.10.x. --- src/reflect/scala/reflect/api/Exprs.scala | 2 ++ .../scala/reflect/internal/Definitions.scala | 2 +- .../internal/annotations/compileTimeOnly.scala | 31 ++++++++++++++++++++++ .../scala/reflect/macros/compileTimeOnly.scala | 16 ----------- test/files/neg/t6539/Macro_1.scala | 2 +- test/files/neg/t6539/Test_2.scala | 6 +++++ 6 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala delete mode 100644 src/reflect/scala/reflect/macros/compileTimeOnly.scala (limited to 'test') diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala index 562b1da8e3..2ba18a8207 100644 --- a/src/reflect/scala/reflect/api/Exprs.scala +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -90,6 +90,7 @@ trait Exprs { self: Universe => * }}} * because expr of type Expr[T] itself does not have a method foo. */ + // @compileTimeOnly("Cannot use splice outside reify") def splice: T /** @@ -106,6 +107,7 @@ trait Exprs { self: Universe => * object Impls { def foo_impl(c: Context)(x: c.Expr[X]): c.Expr[x.value.T] = ... } * }}} */ + // @compileTimeOnly("Cannot use value except for signatures of macro implementations") val value: T override def canEqual(x: Any) = x.isInstanceOf[Expr[_]] diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 4269b65297..3806f555ac 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -949,7 +949,7 @@ trait Definitions extends api.StandardDefinitions { lazy val BeanPropertyAttr = requiredClass[scala.beans.BeanProperty] lazy val BooleanBeanPropertyAttr = requiredClass[scala.beans.BooleanBeanProperty] lazy val CloneableAttr = requiredClass[scala.annotation.cloneable] - lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.reflect.macros.compileTimeOnly") + lazy val CompileTimeOnlyAttr = getClassIfDefined("scala.reflect.internal.annotations.compileTimeOnly") lazy val DeprecatedAttr = requiredClass[scala.deprecated] lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName] lazy val DeprecatedInheritanceAttr = requiredClass[scala.deprecatedInheritance] diff --git a/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala b/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala new file mode 100644 index 0000000000..058ff61fbf --- /dev/null +++ b/src/reflect/scala/reflect/internal/annotations/compileTimeOnly.scala @@ -0,0 +1,31 @@ +package scala.reflect +package internal +package annotations + +import scala.annotation.meta._ + +/** + * An annotation that designates a member should not be referred to after + * type checking (which includes macro expansion); it must only be used in + * the arguments of some other macro that will eliminate it from the AST. + * + * Later on, this annotation should be removed and implemented with domain-specific macros. + * If a certain method `inner` mustn't be called outside the context of a given macro `outer`, + * then it should itself be declared as a macro. + * + * Approach #1. Expansion of `inner` checks whether its enclosures contain `outer` and + * report an error if `outer` is not detected. In principle, we could use this approach right now, + * but currently enclosures are broken, because contexts aren't exactly famous for keeping precise + * track of the stack of the trees being typechecked. + * + * Approach #2. Default implementation of `inner` is just an invocation of `c.abort`. + * `outer` is an untyped macro, which expands into a block, which contains a redefinition of `inner` + * and a call to itself. The redefined `inner` could either be a stub like `Expr.splice` or carry out + * domain-specific logic. + * + * @param message the error message to print during compilation if a reference remains + * after type checking + * @since 2.10.1 + */ +@getter @setter @beanGetter @beanSetter +final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/src/reflect/scala/reflect/macros/compileTimeOnly.scala b/src/reflect/scala/reflect/macros/compileTimeOnly.scala deleted file mode 100644 index 5a3a352a53..0000000000 --- a/src/reflect/scala/reflect/macros/compileTimeOnly.scala +++ /dev/null @@ -1,16 +0,0 @@ -package scala.reflect -package macros - -import scala.annotation.meta._ - -/** - * An annotation that designates a member should not be referred to after - * type checking (which includes macro expansion); it must only be used in - * the arguments of some other macro that will eliminate it from the AST. - * - * @param message the error message to print during compilation if a reference remains - * after type checking - * @since 2.10.1 - */ -@getter @setter @beanGetter @beanSetter -final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation diff --git a/test/files/neg/t6539/Macro_1.scala b/test/files/neg/t6539/Macro_1.scala index ed52776d95..4f7d289e2e 100644 --- a/test/files/neg/t6539/Macro_1.scala +++ b/test/files/neg/t6539/Macro_1.scala @@ -5,6 +5,6 @@ object M { def m(a: Any, b: Any): Any = macro mImpl def mImpl(c: Context)(a: c.Expr[Any], b: c.Expr[Any]) = a - @reflect.macros.compileTimeOnly("cto may only be used as an argument to " + "m") + @reflect.internal.annotations.compileTimeOnly("cto may only be used as an argument to " + "m") def cto = 0 } diff --git a/test/files/neg/t6539/Test_2.scala b/test/files/neg/t6539/Test_2.scala index 5a602879ec..26f4504222 100644 --- a/test/files/neg/t6539/Test_2.scala +++ b/test/files/neg/t6539/Test_2.scala @@ -3,4 +3,10 @@ object Test { M.m(M.cto, ()) // error M.m((), M.cto) // okay M.cto // error + + locally { + val expr = scala.reflect.runtime.universe.reify(2) + val splice = expr.splice + val value = expr.value + } } -- cgit v1.2.3 From 2403d1ddcaa1bd76c1f376a32ec03a36d4dab48b Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 31 Jan 2013 19:46:51 +0100 Subject: SI-7046 reflection now auto-initializes knownDirectSubclasses knownDirectSubclasses joins the happy family of flags, annotations and privateWithin, which automatically trigger initialization, when used within runtime reflection. --- src/reflect/scala/reflect/internal/Symbols.scala | 6 +++++- test/files/run/t7046.check | 2 ++ test/files/run/t7046.scala | 13 +++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t7046.check create mode 100644 test/files/run/t7046.scala (limited to 'test') diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index c1b868f3cb..f4d70a991c 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -86,7 +86,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => case n: TypeName => if (isClass) newClassSymbol(n, pos, newFlags) else newNonClassSymbol(n, pos, newFlags) } - def knownDirectSubclasses = children + def knownDirectSubclasses = { + if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize + children + } + def baseClasses = info.baseClasses def module = sourceModule def thisPrefix: Type = thisType diff --git a/test/files/run/t7046.check b/test/files/run/t7046.check new file mode 100644 index 0000000000..427f1ce610 --- /dev/null +++ b/test/files/run/t7046.check @@ -0,0 +1,2 @@ +Set(class D, class E) +Set(class D, class E) diff --git a/test/files/run/t7046.scala b/test/files/run/t7046.scala new file mode 100644 index 0000000000..647a15cd18 --- /dev/null +++ b/test/files/run/t7046.scala @@ -0,0 +1,13 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +sealed class C +class D extends C +class E extends C + +object Test extends App { + val c = cm.staticClass("C") + println(c.knownDirectSubclasses) + c.typeSignature + println(c.knownDirectSubclasses) +} \ No newline at end of file -- cgit v1.2.3 From 71ea3e8278aad030cbe8c9093fe49790a4e419cb Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 25 Jan 2013 14:16:27 -0800 Subject: no null check for type-tested unapply arg pattern matching on case classes where pattern is not known to be a subclass of the unapply's argument type used to result in code like: ``` if (x1.isInstanceOf[Foo]) { val x2 = x1.asInstanceOf[Foo] if (x2 != null) { // redundant ... } } ``` this wastes byte code on the redundant null check with this patch, when previous type tests imply the variable cannot be null, there's no null check --- .../tools/nsc/typechecker/PatternMatching.scala | 35 +++- test/files/jvm/patmat_opt_no_nullcheck.check | 1 + test/files/jvm/patmat_opt_no_nullcheck.flags | 1 + .../jvm/patmat_opt_no_nullcheck/Analyzed_1.scala | 25 +++ test/files/jvm/patmat_opt_no_nullcheck/test.scala | 8 + test/files/run/inline-ex-handlers.check | 206 ++++++++++----------- 6 files changed, 167 insertions(+), 109 deletions(-) create mode 100644 test/files/jvm/patmat_opt_no_nullcheck.check create mode 100644 test/files/jvm/patmat_opt_no_nullcheck.flags create mode 100644 test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala create mode 100644 test/files/jvm/patmat_opt_no_nullcheck/test.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 9b2898741e..f32ca4bd8e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -409,6 +409,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // example check: List[Int] <:< ::[Int] // TODO: extractor.paramType may contain unbound type params (run/t2800, run/t3530) + // `patBinderOrCasted` is assigned the result of casting `patBinder` to `extractor.paramType` val (typeTestTreeMaker, patBinderOrCasted, binderKnownNonNull) = if (needsTypeTest(patBinder.info.widen, extractor.paramType)) { // chain a type-testing extractor before the actual extractor call @@ -416,7 +417,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC] // (the prefix of the argument passed to the unapply must equal the prefix of the type of the binder) val treeMaker = TypeTestTreeMaker(patBinder, patBinder, extractor.paramType, extractor.paramType)(pos, extractorArgTypeTest = true) - (List(treeMaker), treeMaker.nextBinder, false) + + // check whether typetest implies patBinder is not null, + // even though the eventual null check will be on patBinderOrCasted + // it'll be equal to patBinder casted to extractor.paramType anyway (and the type test is on patBinder) + (List(treeMaker), treeMaker.nextBinder, treeMaker.impliesBinderNonNull(patBinder)) } else { // no type test needed, but the tree maker relies on `patBinderOrCasted` having type `extractor.paramType` (and not just some type compatible with it) // SI-6624 shows this is necessary because apparently patBinder may have an unfortunate type (.decls don't have the case field accessors) @@ -773,11 +778,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def resultType = tpe.finalResultType def isSeq = extractorCall.symbol.name == nme.unapplySeq - /** Create the TreeMaker that embodies this extractor call - * - * `binder` has been casted to `paramType` if necessary - * `binderKnownNonNull` is not used in this subclass - */ + /** Create the TreeMaker that embodies this extractor call + * + * `binder` has been casted to `paramType` if necessary + * `binderKnownNonNull` is not used in this subclass + * + * TODO: implement review feedback by @retronym: + * Passing the pair of values around suggests: + * case class Binder(sym: Symbol, knownNotNull: Boolean). + * Perhaps it hasn't reached critical mass, but it would already clean things up a touch. + */ def treeMaker(patBinderOrCasted: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker = { // the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern) val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted)) @@ -1177,6 +1187,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def eqTest(pat: Tree, testedBinder: Symbol): Result = false def and(a: Result, b: Result): Result = false // we don't and type tests, so the conjunction must include at least one false } + + def nonNullImpliedByTestChecker(binder: Symbol) = new TypeTestCondStrategy { + type Result = Boolean + + def typeTest(testedBinder: Symbol, expectedTp: Type): Result = testedBinder eq binder + def outerTest(testedBinder: Symbol, expectedTp: Type): Result = false + def nonNullTest(testedBinder: Symbol): Result = testedBinder eq binder + def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null + def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null + def and(a: Result, b: Result): Result = a || b + } } /** implements the run-time aspects of (ยง8.2) (typedPattern has already done the necessary type transformations) @@ -1260,6 +1281,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // is this purely a type test, e.g. no outer check, no equality tests (used in switch emission) def isPureTypeTest = renderCondition(pureTypeTestChecker) + def impliesBinderNonNull(binder: Symbol) = renderCondition(nonNullImpliedByTestChecker(binder)) + override def toString = "TT"+(expectedTp, testedBinder.name, nextBinderTp) } diff --git a/test/files/jvm/patmat_opt_no_nullcheck.check b/test/files/jvm/patmat_opt_no_nullcheck.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/patmat_opt_no_nullcheck.flags b/test/files/jvm/patmat_opt_no_nullcheck.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala new file mode 100644 index 0000000000..1594eb523c --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala @@ -0,0 +1,25 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +case class Foo(x: Any) + +class SameBytecode { + def a = + (Foo(1): Any) match { + case Foo(_: String) => + } + + // there's no null check + def b: Unit = { + val x1: Any = Foo(1) + if (x1.isInstanceOf[Foo]) { + val x3 = x1.asInstanceOf[Foo] + if (x3.x.isInstanceOf[String]) { + val x4 = x3.x.asInstanceOf[String] + val x = () + return + } + } + + throw new MatchError(x1) + } +} \ No newline at end of file diff --git a/test/files/jvm/patmat_opt_no_nullcheck/test.scala b/test/files/jvm/patmat_opt_no_nullcheck/test.scala new file mode 100644 index 0000000000..2927e763d5 --- /dev/null +++ b/test/files/jvm/patmat_opt_no_nullcheck/test.scala @@ -0,0 +1,8 @@ +import scala.tools.partest.BytecodeTest + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index 282542a732..905dfc3ee7 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -26,54 +26,55 @@ --- > locals: value args, variable result, value ex6, value x4, value x5, value x 397c393 -< blocks: [1,2,3,4,5,8,11,13,14,16] +< blocks: [1,2,3,4,5,8,10,11,13] --- -> blocks: [1,2,3,5,8,11,13,14,16,17] +> blocks: [1,2,3,5,8,10,11,13,14] 421c417,426 < 103 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 17 +> ? JUMP 14 > -> 17: +> 14: > 101 LOAD_LOCAL(value ex6) > 101 STORE_LOCAL(value x4) > 101 SCOPE_ENTER value x4 > 106 LOAD_LOCAL(value x4) > 106 IS_INSTANCE REF(class MyException) -> 106 CZJUMP (BOOL)NE ? 5 : 11 +> 106 CZJUMP (BOOL)NE ? 5 : 8 434,436d438 < 101 JUMP 4 < < 4: -450,453d451 +446,449d447 < 106 LOAD_LOCAL(value x5) < 106 CALL_METHOD MyException.message (dynamic) < 106 STORE_LOCAL(value message) < 106 SCOPE_ENTER value message -455c453,454 +451c449,450 < 106 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 106 CALL_METHOD MyException.message (dynamic) -527c526 +523c522 < blocks: [1,2,3,4,6,7,8,9,10] --- > blocks: [1,2,3,4,6,7,8,9,10,11,12,13] -556c555,560 +552c551 < 306 THROW(MyException) --- > ? JUMP 11 -> +553a553,557 > 11: > ? LOAD_LOCAL(variable monitor4) > 305 MONITOR_EXIT > ? JUMP 12 -562c566 +> +558c562 < ? THROW(Throwable) --- > ? JUMP 12 -568c572,579 +564c568,575 < ? THROW(Throwable) --- > ? STORE_LOCAL(value t) @@ -84,7 +85,7 @@ > 304 MONITOR_EXIT > ? STORE_LOCAL(value t) > ? JUMP 13 -583a595,606 +579a591,602 > 13: > 310 LOAD_MODULE object Predef > 310 CALL_PRIMITIVE(StartConcat) @@ -97,35 +98,35 @@ > 310 CALL_METHOD scala.Predef.println (dynamic) > 310 JUMP 2 > -592c615 +588c611 < catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6 --- > catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6 -595c618 +591c614 < catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3 --- > catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3 -627c650 +623c646 < blocks: [1,2,3,4,5,6,7,9,10] --- > blocks: [1,2,3,4,5,6,7,9,10,11,12] -651c674,675 +647c670,671 < 78 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 11 -652a677,681 +648a673,677 > 11: > 81 LOAD_LOCAL(value e) > ? STORE_LOCAL(variable exc1) > ? JUMP 12 > -680c709,710 +676c705,706 < 81 THROW(Exception) --- > ? STORE_LOCAL(variable exc1) > ? JUMP 12 -696a727,739 +692a723,735 > 12: > 83 LOAD_MODULE object Predef > 83 CONSTANT("finally") @@ -139,88 +140,88 @@ > 84 LOAD_LOCAL(variable exc1) > 84 THROW(Throwable) > -702c745 +698c741 < catch () in ArrayBuffer(4, 6, 7, 9) starting at: 3 --- > catch () in ArrayBuffer(4, 6, 7, 9, 11) starting at: 3 -726c769 +722c765 < locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x -728c771 -< blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31] +724c767 +< blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25] --- -> blocks: [1,2,3,4,5,6,9,12,14,17,18,19,22,25,27,28,30,31,32,33,34] -752c795,802 +> blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25,26,27,28] +748c791,798 < 172 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 32 +> ? JUMP 26 > -> 32: +> 26: > 170 LOAD_LOCAL(value ex6) > 170 STORE_LOCAL(value x4) > 170 SCOPE_ENTER value x4 -> 170 JUMP 18 -799,802d848 +> 170 JUMP 15 +791,794d840 < 175 LOAD_LOCAL(value x5) < 175 CALL_METHOD MyException.message (dynamic) < 175 STORE_LOCAL(value message) < 175 SCOPE_ENTER value message -804c850,851 +796c842,843 < 176 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 176 CALL_METHOD MyException.message (dynamic) -808c855,856 +800c847,848 < 177 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 177 CALL_METHOD MyException.message (dynamic) -810c858,859 +802c850,851 < 177 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 33 -814c863,864 +> ? JUMP 27 +806c855,856 < 170 THROW(Throwable) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 33 -823a874,879 -> 33: +> ? JUMP 27 +815a866,871 +> 27: > 169 LOAD_LOCAL(value ex6) > 169 STORE_LOCAL(value x4) > 169 SCOPE_ENTER value x4 > 169 JUMP 5 > -838,841d893 +826,829d881 < 180 LOAD_LOCAL(value x5) < 180 CALL_METHOD MyException.message (dynamic) < 180 STORE_LOCAL(value message) < 180 SCOPE_ENTER value message -843c895,896 +831c883,884 < 181 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 181 CALL_METHOD MyException.message (dynamic) -847c900,901 +835c888,889 < 182 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 182 CALL_METHOD MyException.message (dynamic) -849c903,904 +837c891,892 < 182 THROW(MyException) --- > ? STORE_LOCAL(variable exc2) -> ? JUMP 34 -853c908,909 +> ? JUMP 28 +841c896,897 < 169 THROW(Throwable) --- > ? STORE_LOCAL(variable exc2) -> ? JUMP 34 -869a926,938 -> 34: +> ? JUMP 28 +857a914,926 +> 28: > 184 LOAD_MODULE object Predef > 184 CONSTANT("finally") > 184 CALL_METHOD scala.Predef.println (dynamic) @@ -233,159 +234,158 @@ > 185 LOAD_LOCAL(variable exc2) > 185 THROW(Throwable) > -875c944 -< catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30) starting at: 4 +863c932 +< catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24) starting at: 4 --- -> catch (Throwable) in ArrayBuffer(17, 18, 19, 22, 25, 27, 28, 30, 32) starting at: 4 -878c947 -< catch () in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30) starting at: 3 +> catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24, 26) starting at: 4 +866c935 +< catch () in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24) starting at: 3 --- -> catch () in ArrayBuffer(4, 5, 6, 9, 12, 17, 18, 19, 22, 25, 27, 28, 30, 32, 33) starting at: 3 -902c971 +> catch () in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24, 26, 27) starting at: 3 +890c959 < locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value e, value ex6, value x4, value x5, value x -904c973 -< blocks: [1,2,3,6,7,8,11,14,16,17,19] +892c961 +< blocks: [1,2,3,6,7,8,11,13,14,16] --- -> blocks: [1,2,3,6,7,8,11,14,16,17,19,20] -928c997,1004 +> blocks: [1,2,3,6,7,8,11,13,14,16,17] +916c985,992 < 124 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 +> ? JUMP 17 > -> 20: +> 17: > 122 LOAD_LOCAL(value ex6) > 122 STORE_LOCAL(value x4) > 122 SCOPE_ENTER value x4 > 122 JUMP 7 -957,960d1032 +941,944d1016 < 127 LOAD_LOCAL(value x5) < 127 CALL_METHOD MyException.message (dynamic) < 127 STORE_LOCAL(value message) < 127 SCOPE_ENTER value message -962c1034,1035 +946c1018,1019 < 127 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 127 CALL_METHOD MyException.message (dynamic) -991c1064 -< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19) starting at: 3 +975c1048 +< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16) starting at: 3 --- -> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 14, 16, 17, 19, 20) starting at: 3 -1015c1088 +> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3 +999c1072 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e --- > locals: value args, variable result, value ex6, value x4, value x5, value x, value e -1017c1090 -< blocks: [1,2,3,4,5,8,11,15,16,17,19] +1001c1074 +< blocks: [1,2,3,4,5,8,12,13,14,16] --- -> blocks: [1,2,3,5,8,11,15,16,17,19,20] -1041c1114,1123 +> blocks: [1,2,3,5,8,12,13,14,16,17] +1025c1098,1107 < 148 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 +> ? JUMP 17 > -> 20: +> 17: > 145 LOAD_LOCAL(value ex6) > 145 STORE_LOCAL(value x4) > 145 SCOPE_ENTER value x4 > 154 LOAD_LOCAL(value x4) > 154 IS_INSTANCE REF(class MyException) -> 154 CZJUMP (BOOL)NE ? 5 : 11 -1062,1064d1143 +> 154 CZJUMP (BOOL)NE ? 5 : 8 +1046,1048d1127 < 145 JUMP 4 < < 4: -1078,1081d1156 +1058,1061d1136 < 154 LOAD_LOCAL(value x5) < 154 CALL_METHOD MyException.message (dynamic) < 154 STORE_LOCAL(value message) < 154 SCOPE_ENTER value message -1083c1158,1159 +1063c1138,1139 < 154 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 154 CALL_METHOD MyException.message (dynamic) -1300c1376 +1280c1356 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1324c1400,1401 +1304c1380,1387 < 38 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 8 -1325a1403,1408 +> > 8: > 42 LOAD_MODULE object Predef > 42 CONSTANT("IllegalArgumentException") > 42 CALL_METHOD scala.Predef.println (dynamic) > 42 JUMP 2 -> -1371c1454 +1351c1434 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, value x4, value x5, value x -1373c1456 -< blocks: [1,2,3,4,5,8,11,13,14,16,17,19] +1353c1436 +< blocks: [1,2,3,4,5,8,10,11,13,14,16] --- -> blocks: [1,2,3,5,8,11,13,14,16,17,19,20] -1397c1480,1481 +> blocks: [1,2,3,5,8,10,11,13,14,16,17] +1377c1460,1461 < 203 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 -1417c1501,1510 +> ? JUMP 17 +1397c1481,1490 < 209 THROW(MyException) --- > ? STORE_LOCAL(value ex6) -> ? JUMP 20 +> ? JUMP 17 > -> 20: +> 17: > 200 LOAD_LOCAL(value ex6) > 200 STORE_LOCAL(value x4) > 200 SCOPE_ENTER value x4 > 212 LOAD_LOCAL(value x4) > 212 IS_INSTANCE REF(class MyException) -> 212 CZJUMP (BOOL)NE ? 5 : 11 -1430,1432d1522 +> 212 CZJUMP (BOOL)NE ? 5 : 8 +1410,1412d1502 < 200 JUMP 4 < < 4: -1446,1449d1535 +1422,1425d1511 < 212 LOAD_LOCAL(value x5) < 212 CALL_METHOD MyException.message (dynamic) < 212 STORE_LOCAL(value message) < 212 SCOPE_ENTER value message -1451c1537,1538 +1427c1513,1514 < 213 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 213 CALL_METHOD MyException.message (dynamic) -1495c1582 +1471c1558 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1519c1606,1607 +1495c1582,1583 < 58 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 8 -1520a1609,1614 +1496a1585,1590 > 8: > 62 LOAD_MODULE object Predef > 62 CONSTANT("RuntimeException") > 62 CALL_METHOD scala.Predef.println (dynamic) > 62 JUMP 2 > -1568c1662 +1544c1638 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1588c1682,1687 +1564c1658,1663 < 229 THROW(MyException) --- > ? JUMP 5 @@ -394,19 +394,19 @@ > ? LOAD_LOCAL(variable monitor1) > 228 MONITOR_EXIT > 228 THROW(Throwable) -1594c1693 +1570c1669 < ? THROW(Throwable) --- > 228 THROW(Throwable) -1622c1721 +1598c1697 < locals: value args, variable result, variable monitor2, variable monitorResult1 --- > locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 -1624c1723 +1600c1699 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1647c1746,1754 +1623c1722,1730 < 245 THROW(MyException) --- > ? STORE_LOCAL(value exception$1) @@ -418,7 +418,7 @@ > ? LOAD_LOCAL(variable monitor2) > 244 MONITOR_EXIT > 244 THROW(Throwable) -1653c1760 +1629c1736 < ? THROW(Throwable) --- > 244 THROW(Throwable) -- cgit v1.2.3 From 494ba94518c9b40b7bf600ec7b70561842375597 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 29 Jan 2013 00:13:14 -0800 Subject: don't store subpats bound to underscore also, tweak fix in place for SI-5158 to appease SI-6941 don't store mutable fields from scala.* as we can assume these classes are well-behaved and do not mutate their case class fields --- .../tools/nsc/typechecker/PatternMatching.scala | 64 ++++++++++++++++++---- test/files/jvm/patmat_opt_ignore_underscore.check | 1 + test/files/jvm/patmat_opt_ignore_underscore.flags | 1 + .../patmat_opt_ignore_underscore/Analyzed_1.scala | 30 ++++++++++ .../jvm/patmat_opt_ignore_underscore/test.scala | 15 +++++ test/files/run/t6288.check | 10 +--- 6 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 test/files/jvm/patmat_opt_ignore_underscore.check create mode 100644 test/files/jvm/patmat_opt_ignore_underscore.flags create mode 100644 test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala create mode 100644 test/files/jvm/patmat_opt_ignore_underscore/test.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index f32ca4bd8e..8ae2e0a3b8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -647,6 +647,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case bp => bp } + // never store these in local variables (for PreserveSubPatBinders) + lazy val ignoredSubPatBinders = (subPatBinders zip args).collect{ + case (b, PatternBoundToUnderscore()) => b + }.toSet + def subPatTypes: List[Type] = if(isSeq) { val TypeRef(pre, SeqClass, args) = seqTp @@ -750,13 +755,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position): TreeMaker = { val paramAccessors = binder.constrParamAccessors // binders corresponding to mutable fields should be stored (SI-5158, SI-6070) + // make an exception for classes under the scala package as they should be well-behaved, + // to optimize matching on List val mutableBinders = - if (paramAccessors exists (_.isMutable)) + if (!binder.info.typeSymbol.hasTransOwner(ScalaPackageClass) && + (paramAccessors exists (_.isMutable))) subPatBinders.zipWithIndex.collect{ case (binder, idx) if paramAccessors(idx).isMutable => binder } else Nil // checks binder ne null before chaining to the next extractor - ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder), mutableBinders, binderKnownNonNull) + ProductExtractorTreeMaker(binder, lengthGuard(binder))(subPatBinders, subPatRefs(binder), mutableBinders, binderKnownNonNull, ignoredSubPatBinders) } // reference the (i-1)th case accessor if it exists, otherwise the (i-1)th tuple component @@ -792,7 +800,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // the extractor call (applied to the binder bound by the flatMap corresponding to the previous (i.e., enclosing/outer) pattern) val extractorApply = atPos(pos)(spliceApply(patBinderOrCasted)) val binder = freshSym(pos, pureType(resultInMonad)) // can't simplify this when subPatBinders.isEmpty, since UnitClass.tpe is definitely wrong when isSeq, and resultInMonad should always be correct since it comes directly from the extractor's result type - ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(subPatBinders, subPatRefs(binder), resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted) + ExtractorTreeMaker(extractorApply, lengthGuard(binder), binder)(subPatBinders, subPatRefs(binder), resultType.typeSymbol == BooleanClass, checkedLength, patBinderOrCasted, ignoredSubPatBinders) } override protected def seqTree(binder: Symbol): Tree = @@ -849,6 +857,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } + object PatternBoundToUnderscore { + def unapply(pat: Tree): Boolean = pat match { + case Bind(nme.WILDCARD, _) => true // don't skip when binding an interesting symbol! + case Ident(nme.WILDCARD) => true + case Alternative(ps) => ps forall (PatternBoundToUnderscore.unapply(_)) + case Typed(PatternBoundToUnderscore(), _) => true + case _ => false + } + } + object Bound { def unapply(t: Tree): Option[(Symbol, Tree)] = t match { case t@Bind(n, p) if (t.symbol ne null) && (t.symbol ne NoSymbol) => // pos/t2429 does not satisfy these conditions @@ -1016,10 +1034,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL trait PreserveSubPatBinders extends TreeMaker { val subPatBinders: List[Symbol] val subPatRefs: List[Tree] + val ignoredSubPatBinders: Set[Symbol] // unless `debugInfoEmitVars`, this set should contain the bare minimum for correctness // mutable case class fields need to be stored regardless (SI-5158, SI-6070) -- see override in ProductExtractorTreeMaker - def storedBinders: Set[Symbol] = if (debugInfoEmitVars) subPatBinders.toSet else Set.empty + // sub patterns bound to wildcard (_) are never stored as they can't be referenced + // dirty debuggers will have to get dirty to see the wildcards + lazy val storedBinders: Set[Symbol] = + (if (debugInfoEmitVars) subPatBinders.toSet else Set.empty) ++ extraStoredBinders -- ignoredSubPatBinders + + // e.g., mutable fields of a case class in ProductExtractorTreeMaker + def extraStoredBinders: Set[Symbol] def emitVars = storedBinders.nonEmpty @@ -1040,10 +1065,22 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL Substitution(subPatBinders, subPatRefs) >> super.subPatternsAsSubstitution import CODE._ - def bindSubPats(in: Tree): Tree = if (!emitVars) in + def bindSubPats(in: Tree): Tree = + if (!emitVars) in else { - val (subPatBindersStored, subPatRefsStored) = stored.unzip - Block(map2(subPatBindersStored.toList, subPatRefsStored.toList)(VAL(_) === _), in) + // binders in `subPatBindersStored` that are referenced by tree `in` + val usedBinders = new collection.mutable.HashSet[Symbol]() + // all potentially stored subpat binders + val potentiallyStoredBinders = stored.unzip._1.toSet + // compute intersection of all symbols in the tree `in` and all potentially stored subpat binders + in.foreach(t => if (potentiallyStoredBinders(t.symbol)) usedBinders += t.symbol) + + if (usedBinders.isEmpty) in + else { + // only store binders actually used + val (subPatBindersStored, subPatRefsStored) = stored.filter{case (b, _) => usedBinders(b)}.unzip + Block(map2(subPatBindersStored.toList, subPatRefsStored.toList)(VAL(_) === _), in) + } } } @@ -1063,7 +1100,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val subPatRefs: List[Tree], extractorReturnsBoolean: Boolean, val checkedLength: Option[Int], - val prevBinder: Symbol) extends FunTreeMaker with PreserveSubPatBinders { + val prevBinder: Symbol, + val ignoredSubPatBinders: Set[Symbol] + ) extends FunTreeMaker with PreserveSubPatBinders { + + def extraStoredBinders: Set[Symbol] = Set() def chainBefore(next: Tree)(casegen: Casegen): Tree = { val condAndNext = extraCond match { @@ -1107,14 +1148,15 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val subPatBinders: List[Symbol], val subPatRefs: List[Tree], val mutableBinders: List[Symbol], - binderKnownNonNull: Boolean) extends FunTreeMaker with PreserveSubPatBinders { + binderKnownNonNull: Boolean, + val ignoredSubPatBinders: Set[Symbol] + ) extends FunTreeMaker with PreserveSubPatBinders { import CODE._ val nextBinder = prevBinder // just passing through // mutable binders must be stored to avoid unsoundness or seeing mutation of fields after matching (SI-5158, SI-6070) - // (the implementation could be optimized by duplicating code from `super.storedBinders`, but this seems more elegant) - override def storedBinders: Set[Symbol] = super.storedBinders ++ mutableBinders.toSet + def extraStoredBinders: Set[Symbol] = mutableBinders.toSet def chainBefore(next: Tree)(casegen: Casegen): Tree = { val nullCheck = REF(prevBinder) OBJ_NE NULL diff --git a/test/files/jvm/patmat_opt_ignore_underscore.check b/test/files/jvm/patmat_opt_ignore_underscore.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/patmat_opt_ignore_underscore.flags b/test/files/jvm/patmat_opt_ignore_underscore.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore.flags @@ -0,0 +1 @@ +-optimize \ No newline at end of file diff --git a/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala b/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala new file mode 100644 index 0000000000..ac59e41ba7 --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala @@ -0,0 +1,30 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +// this is not the best test for shielding against regressing on this particular issue, +// but it sets the stage for checking the bytecode emitted by the pattern matcher and +// comparing it to manually tuned code using if/then/else etc. +class SameBytecode { + case class Foo(x: Any, y: String) + + def a = + Foo(1, "a") match { + case Foo(_: String, y) => y + } + + // this method's body holds the tree that should be generated by the pattern matcher for method a (-Xprint:patmat) + // the test checks that bytecode for a and b is identical (modulo line numbers) + // we can't diff trees as they are quite different (patmat uses jumps to labels that cannot be expressed in source, for example) + // note that the actual tree is quite bad: we do an unnecessary null check, isInstanceOf and local val (x3) + // some of these will be fixed soon (the initial null check is for the scrutinee, which is harder to fix in patmat) + def b: String = { + val x1 = Foo(1, "a") + if (x1.ne(null)) { + if (x1.x.isInstanceOf[String]) { + val x3 = x1.x.asInstanceOf[String] + return x1.y + } + } + + throw new MatchError(x1) + } +} \ No newline at end of file diff --git a/test/files/jvm/patmat_opt_ignore_underscore/test.scala b/test/files/jvm/patmat_opt_ignore_underscore/test.scala new file mode 100644 index 0000000000..6179101a7e --- /dev/null +++ b/test/files/jvm/patmat_opt_ignore_underscore/test.scala @@ -0,0 +1,15 @@ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} diff --git a/test/files/run/t6288.check b/test/files/run/t6288.check index af6bd5d269..4895c2c007 100644 --- a/test/files/run/t6288.check +++ b/test/files/run/t6288.check @@ -11,10 +11,7 @@ [64]case5()[84]{ [84] val o7: [84]Option[Int] = [84][84]Case3.unapply([84]x1); [84]if ([84]o7.isEmpty.unary_!) - [84]{ - [90]val nr: [90]Int = [90]o7.get; - [97][97]matchEnd4([97]()) - } + [97][97]matchEnd4([97]()) else [84][84]case6() }; @@ -38,10 +35,7 @@ [195] val o7: [195]Option[List[Int]] = [195][195]Case4.unapplySeq([195]x1); [195]if ([195]o7.isEmpty.unary_!) [195]if ([195][195][195][195]o7.get.!=([195]null).&&([195][195][195][195]o7.get.lengthCompare([195]1).==([195]0))) - [195]{ - [201]val nr: [201]Int = [201][201]o7.get.apply([201]0); - [208][208]matchEnd4([208]()) - } + [208][208]matchEnd4([208]()) else [195][195]case6() else -- cgit v1.2.3 From b47bb0fe1a0eb2f60f280ef0fa62a6f0be65580b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 29 Jan 2013 00:21:43 -0800 Subject: no type test if static type <:< primitive value class --- .../tools/nsc/typechecker/PatternMatching.scala | 44 +++++++++++++--------- test/files/jvm/patmat_opt_primitive_typetest.check | 1 + test/files/jvm/patmat_opt_primitive_typetest.flags | 1 + .../patmat_opt_primitive_typetest/Analyzed_1.scala | 25 ++++++++++++ .../jvm/patmat_opt_primitive_typetest/test.scala | 8 ++++ 5 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 test/files/jvm/patmat_opt_primitive_typetest.check create mode 100644 test/files/jvm/patmat_opt_primitive_typetest.flags create mode 100644 test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala create mode 100644 test/files/jvm/patmat_opt_primitive_typetest/test.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 8ae2e0a3b8..c6f80c9b20 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -411,7 +411,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // TODO: extractor.paramType may contain unbound type params (run/t2800, run/t3530) // `patBinderOrCasted` is assigned the result of casting `patBinder` to `extractor.paramType` val (typeTestTreeMaker, patBinderOrCasted, binderKnownNonNull) = - if (needsTypeTest(patBinder.info.widen, extractor.paramType)) { + if (patBinder.info.widen <:< extractor.paramType) { + // no type test needed, but the tree maker relies on `patBinderOrCasted` having type `extractor.paramType` (and not just some type compatible with it) + // SI-6624 shows this is necessary because apparently patBinder may have an unfortunate type (.decls don't have the case field accessors) + // TODO: get to the bottom of this -- I assume it happens when type checking infers a weird type for an unapply call + // by going back to the parameterType for the extractor call we get a saner type, so let's just do that for now + /* TODO: uncomment when `settings.developer` and `devWarning` become available + if (settings.developer.value && !(patBinder.info =:= extractor.paramType)) + devWarning(s"resetting info of $patBinder: ${patBinder.info} to ${extractor.paramType}") + */ + (Nil, patBinder setInfo extractor.paramType, false) + } else { // chain a type-testing extractor before the actual extractor call // it tests the type, checks the outer pointer and casts to the expected type // TODO: the outer check is mandated by the spec for case classes, but we do it for user-defined unapplies as well [SPEC] @@ -422,16 +432,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // even though the eventual null check will be on patBinderOrCasted // it'll be equal to patBinder casted to extractor.paramType anyway (and the type test is on patBinder) (List(treeMaker), treeMaker.nextBinder, treeMaker.impliesBinderNonNull(patBinder)) - } else { - // no type test needed, but the tree maker relies on `patBinderOrCasted` having type `extractor.paramType` (and not just some type compatible with it) - // SI-6624 shows this is necessary because apparently patBinder may have an unfortunate type (.decls don't have the case field accessors) - // TODO: get to the bottom of this -- I assume it happens when type checking infers a weird type for an unapply call - // by going back to the parameterType for the extractor call we get a saner type, so let's just do that for now - /* TODO: uncomment when `settings.developer` and `devWarning` become available - if (settings.developer.value && !(patBinder.info =:= extractor.paramType)) - devWarning(s"resetting info of $patBinder: ${patBinder.info} to ${extractor.paramType}") - */ - (Nil, patBinder setInfo extractor.paramType, false) } withSubPats(typeTestTreeMaker :+ extractor.treeMaker(patBinderOrCasted, binderKnownNonNull, pos), extractor.subBindersAndPatterns: _*) @@ -1176,9 +1176,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL override def toString = "P"+(prevBinder.name, extraCond getOrElse "", localSubstitution) } - // typetag-based tests are inserted by the type checker - def needsTypeTest(tp: Type, pt: Type): Boolean = !(tp <:< pt) - object TypeTestTreeMaker { // factored out so that we can consistently generate other representations of the tree that implements the test // (e.g. propositions for exhaustivity and friends, boolean for isPureTypeTest) @@ -1192,12 +1189,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def equalsTest(pat: Tree, testedBinder: Symbol): Result def eqTest(pat: Tree, testedBinder: Symbol): Result def and(a: Result, b: Result): Result + def tru: Result } object treeCondStrategy extends TypeTestCondStrategy { import CODE._ type Result = Tree def and(a: Result, b: Result): Result = a AND b + def tru = TRUE_typed def typeTest(testedBinder: Symbol, expectedTp: Type) = codegen._isInstanceOf(testedBinder, expectedTp) def nonNullTest(testedBinder: Symbol) = REF(testedBinder) OBJ_NE NULL def equalsTest(pat: Tree, testedBinder: Symbol) = codegen._equals(pat, testedBinder) @@ -1228,6 +1227,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def equalsTest(pat: Tree, testedBinder: Symbol): Result = false def eqTest(pat: Tree, testedBinder: Symbol): Result = false def and(a: Result, b: Result): Result = false // we don't and type tests, so the conjunction must include at least one false + def tru = true } def nonNullImpliedByTestChecker(binder: Symbol) = new TypeTestCondStrategy { @@ -1239,6 +1239,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def equalsTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null def eqTest(pat: Tree, testedBinder: Symbol): Result = false // could in principle analyse pat and see if it's statically known to be non-null def and(a: Result, b: Result): Result = a || b + def tru = false } } @@ -1308,10 +1309,16 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // I think it's okay: // - the isInstanceOf test includes a test for the element type // - Scala's arrays are invariant (so we don't drop type tests unsoundly) - case _ if (expectedTp <:< AnyRefClass.tpe) && !needsTypeTest(testedBinder.info.widen, expectedTp) => - // do non-null check first to ensure we won't select outer on null - if (outerTestNeeded) and(nonNullTest(testedBinder), outerTest(testedBinder, expectedTp)) - else nonNullTest(testedBinder) + case _ if testedBinder.info.widen <:< expectedTp => + // if the expected type is a primitive value type, it cannot be null and it cannot have an outer pointer + // since the types conform, no further checking is required + if (expectedTp.typeSymbol.isPrimitiveValueClass) tru + // have to test outer and non-null only when it's a reference type + else if (expectedTp <:< AnyRefClass.tpe) { + // do non-null check first to ensure we won't select outer on null + if (outerTestNeeded) and(nonNullTest(testedBinder), outerTest(testedBinder, expectedTp)) + else nonNullTest(testedBinder) + } else default case _ => default } @@ -1823,6 +1830,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def nonNullTest(testedBinder: Symbol) = NonNullCond(binderToUniqueTree(testedBinder)) def equalsTest(pat: Tree, testedBinder: Symbol) = EqualityCond(binderToUniqueTree(testedBinder), unique(pat)) def eqTest(pat: Tree, testedBinder: Symbol) = EqualityCond(binderToUniqueTree(testedBinder), unique(pat)) // TODO: eq, not == + def tru = TrueCond } ttm.renderCondition(condStrategy) case EqualityTestTreeMaker(prevBinder, patTree, _) => EqualityCond(binderToUniqueTree(prevBinder), unique(patTree)) diff --git a/test/files/jvm/patmat_opt_primitive_typetest.check b/test/files/jvm/patmat_opt_primitive_typetest.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/patmat_opt_primitive_typetest.flags b/test/files/jvm/patmat_opt_primitive_typetest.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala b/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala new file mode 100644 index 0000000000..c5b8811449 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala @@ -0,0 +1,25 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +class SameBytecode { + case class Foo(x: Int, y: String) + + def a = + Foo(1, "a") match { + case Foo(_: Int, y) => y + } + + // this method's body holds the tree that should be generated by the pattern matcher for method a (-Xprint:patmat) + // the test checks that bytecode for a and b is identical (modulo line numbers) + // we can't diff trees as they are quite different (patmat uses jumps to labels that cannot be expressed in source, for example) + // note that the actual tree is quite bad: we do an unnecessary null check, and local val (x3) + // some of these will be fixed soon (the initial null check is for the scrutinee, which is harder to fix in patmat) + def b: String = { + val x1 = Foo(1, "a") + if (x1.ne(null)) { + val x3 = x1.x + return x1.y + } + + throw new MatchError(x1) + } +} \ No newline at end of file diff --git a/test/files/jvm/patmat_opt_primitive_typetest/test.scala b/test/files/jvm/patmat_opt_primitive_typetest/test.scala new file mode 100644 index 0000000000..2927e763d5 --- /dev/null +++ b/test/files/jvm/patmat_opt_primitive_typetest/test.scala @@ -0,0 +1,8 @@ +import scala.tools.partest.BytecodeTest + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + sameBytecode(getMethod(classNode, "a"), getMethod(classNode, "b")) + } +} -- cgit v1.2.3 From b92396b57912f040d8f536b8a60b844ff586ff0d Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 29 Jan 2013 19:16:37 -0800 Subject: SI-6686 drop valdef unused in flatMapCond's block --- .../tools/nsc/typechecker/PatternMatching.scala | 16 ++- .../patmat_opt_ignore_underscore/Analyzed_1.scala | 1 - .../jvm/patmat_opt_no_nullcheck/Analyzed_1.scala | 1 - .../patmat_opt_primitive_typetest/Analyzed_1.scala | 1 - test/files/run/inline-ex-handlers.check | 136 ++++++++++----------- test/files/run/t6288b-jump-position.check | 6 +- 6 files changed, 80 insertions(+), 81 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index c6f80c9b20..4b53802d95 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -3792,11 +3792,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // nextBinder: T // next == MatchMonad[U] // returns MatchMonad[U] - def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = - ifThenElseZero(cond, BLOCK( - VAL(nextBinder) === res, - next - )) + def flatMapCond(cond: Tree, res: Tree, nextBinder: Symbol, next: Tree): Tree = { + val rest = + // only emit a local val for `nextBinder` if it's actually referenced in `next` + if (next.exists(_.symbol eq nextBinder)) + BLOCK( + VAL(nextBinder) === res, + next + ) + else next + ifThenElseZero(cond, rest) + } // guardTree: Boolean // next: MatchMonad[T] diff --git a/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala b/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala index ac59e41ba7..fa3639380d 100644 --- a/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala +++ b/test/files/jvm/patmat_opt_ignore_underscore/Analyzed_1.scala @@ -20,7 +20,6 @@ class SameBytecode { val x1 = Foo(1, "a") if (x1.ne(null)) { if (x1.x.isInstanceOf[String]) { - val x3 = x1.x.asInstanceOf[String] return x1.y } } diff --git a/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala index 1594eb523c..3a594c401e 100644 --- a/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala +++ b/test/files/jvm/patmat_opt_no_nullcheck/Analyzed_1.scala @@ -14,7 +14,6 @@ class SameBytecode { if (x1.isInstanceOf[Foo]) { val x3 = x1.asInstanceOf[Foo] if (x3.x.isInstanceOf[String]) { - val x4 = x3.x.asInstanceOf[String] val x = () return } diff --git a/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala b/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala index c5b8811449..e5db6c4dd0 100644 --- a/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala +++ b/test/files/jvm/patmat_opt_primitive_typetest/Analyzed_1.scala @@ -16,7 +16,6 @@ class SameBytecode { def b: String = { val x1 = Foo(1, "a") if (x1.ne(null)) { - val x3 = x1.x return x1.y } diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index 905dfc3ee7..f2f0b60687 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -21,15 +21,15 @@ < 92 JUMP 7 < < 7: -395c391 +391c387 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, value x4, value x5, value x -397c393 +393c389 < blocks: [1,2,3,4,5,8,10,11,13] --- > blocks: [1,2,3,5,8,10,11,13,14] -421c417,426 +417c413,422 < 103 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -42,39 +42,39 @@ > 106 LOAD_LOCAL(value x4) > 106 IS_INSTANCE REF(class MyException) > 106 CZJUMP (BOOL)NE ? 5 : 8 -434,436d438 +430,432d434 < 101 JUMP 4 < < 4: -446,449d447 +442,445d443 < 106 LOAD_LOCAL(value x5) < 106 CALL_METHOD MyException.message (dynamic) < 106 STORE_LOCAL(value message) < 106 SCOPE_ENTER value message -451c449,450 +447c445,446 < 106 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 106 CALL_METHOD MyException.message (dynamic) -523c522 +519c518 < blocks: [1,2,3,4,6,7,8,9,10] --- > blocks: [1,2,3,4,6,7,8,9,10,11,12,13] -552c551 +548c547 < 306 THROW(MyException) --- > ? JUMP 11 -553a553,557 +549a549,553 > 11: > ? LOAD_LOCAL(variable monitor4) > 305 MONITOR_EXIT > ? JUMP 12 > -558c562 +554c558 < ? THROW(Throwable) --- > ? JUMP 12 -564c568,575 +560c564,571 < ? THROW(Throwable) --- > ? STORE_LOCAL(value t) @@ -85,7 +85,7 @@ > 304 MONITOR_EXIT > ? STORE_LOCAL(value t) > ? JUMP 13 -579a591,602 +575a587,598 > 13: > 310 LOAD_MODULE object Predef > 310 CALL_PRIMITIVE(StartConcat) @@ -98,35 +98,35 @@ > 310 CALL_METHOD scala.Predef.println (dynamic) > 310 JUMP 2 > -588c611 +584c607 < catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6 --- > catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6 -591c614 +587c610 < catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3 --- > catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3 -623c646 +619c642 < blocks: [1,2,3,4,5,6,7,9,10] --- > blocks: [1,2,3,4,5,6,7,9,10,11,12] -647c670,671 +643c666,667 < 78 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 11 -648a673,677 +644a669,673 > 11: > 81 LOAD_LOCAL(value e) > ? STORE_LOCAL(variable exc1) > ? JUMP 12 > -676c705,706 +672c701,702 < 81 THROW(Exception) --- > ? STORE_LOCAL(variable exc1) > ? JUMP 12 -692a723,735 +688a719,731 > 12: > 83 LOAD_MODULE object Predef > 83 CONSTANT("finally") @@ -140,19 +140,19 @@ > 84 LOAD_LOCAL(variable exc1) > 84 THROW(Throwable) > -698c741 +694c737 < catch () in ArrayBuffer(4, 6, 7, 9) starting at: 3 --- > catch () in ArrayBuffer(4, 6, 7, 9, 11) starting at: 3 -722c765 +718c761 < locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x -724c767 +720c763 < blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25] --- > blocks: [1,2,3,4,5,6,9,11,14,15,16,19,21,22,24,25,26,27,28] -748c791,798 +744c787,794 < 172 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -163,64 +163,64 @@ > 170 STORE_LOCAL(value x4) > 170 SCOPE_ENTER value x4 > 170 JUMP 15 -791,794d840 +787,790d836 < 175 LOAD_LOCAL(value x5) < 175 CALL_METHOD MyException.message (dynamic) < 175 STORE_LOCAL(value message) < 175 SCOPE_ENTER value message -796c842,843 +792c838,839 < 176 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 176 CALL_METHOD MyException.message (dynamic) -800c847,848 +796c843,844 < 177 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 177 CALL_METHOD MyException.message (dynamic) -802c850,851 +798c846,847 < 177 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 27 -806c855,856 +802c851,852 < 170 THROW(Throwable) --- > ? STORE_LOCAL(value ex6) > ? JUMP 27 -815a866,871 +811a862,867 > 27: > 169 LOAD_LOCAL(value ex6) > 169 STORE_LOCAL(value x4) > 169 SCOPE_ENTER value x4 > 169 JUMP 5 > -826,829d881 +822,825d877 < 180 LOAD_LOCAL(value x5) < 180 CALL_METHOD MyException.message (dynamic) < 180 STORE_LOCAL(value message) < 180 SCOPE_ENTER value message -831c883,884 +827c879,880 < 181 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 181 CALL_METHOD MyException.message (dynamic) -835c888,889 +831c884,885 < 182 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 182 CALL_METHOD MyException.message (dynamic) -837c891,892 +833c887,888 < 182 THROW(MyException) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 28 -841c896,897 +837c892,893 < 169 THROW(Throwable) --- > ? STORE_LOCAL(variable exc2) > ? JUMP 28 -857a914,926 +853a910,922 > 28: > 184 LOAD_MODULE object Predef > 184 CONSTANT("finally") @@ -234,23 +234,23 @@ > 185 LOAD_LOCAL(variable exc2) > 185 THROW(Throwable) > -863c932 +859c928 < catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24) starting at: 4 --- > catch (Throwable) in ArrayBuffer(14, 15, 16, 19, 21, 22, 24, 26) starting at: 4 -866c935 +862c931 < catch () in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24) starting at: 3 --- > catch () in ArrayBuffer(4, 5, 6, 9, 14, 15, 16, 19, 21, 22, 24, 26, 27) starting at: 3 -890c959 +886c955 < locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value e, value ex6, value x4, value x5, value x -892c961 +888c957 < blocks: [1,2,3,6,7,8,11,13,14,16] --- > blocks: [1,2,3,6,7,8,11,13,14,16,17] -916c985,992 +912c981,988 < 124 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -261,29 +261,29 @@ > 122 STORE_LOCAL(value x4) > 122 SCOPE_ENTER value x4 > 122 JUMP 7 -941,944d1016 +937,940d1012 < 127 LOAD_LOCAL(value x5) < 127 CALL_METHOD MyException.message (dynamic) < 127 STORE_LOCAL(value message) < 127 SCOPE_ENTER value message -946c1018,1019 +942c1014,1015 < 127 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 127 CALL_METHOD MyException.message (dynamic) -975c1048 +971c1044 < catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16) starting at: 3 --- > catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3 -999c1072 +995c1068 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e --- > locals: value args, variable result, value ex6, value x4, value x5, value x, value e -1001c1074 +997c1070 < blocks: [1,2,3,4,5,8,12,13,14,16] --- > blocks: [1,2,3,5,8,12,13,14,16,17] -1025c1098,1107 +1021c1094,1103 < 148 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -296,25 +296,25 @@ > 154 LOAD_LOCAL(value x4) > 154 IS_INSTANCE REF(class MyException) > 154 CZJUMP (BOOL)NE ? 5 : 8 -1046,1048d1127 +1042,1044d1123 < 145 JUMP 4 < < 4: -1058,1061d1136 +1054,1057d1132 < 154 LOAD_LOCAL(value x5) < 154 CALL_METHOD MyException.message (dynamic) < 154 STORE_LOCAL(value message) < 154 SCOPE_ENTER value message -1063c1138,1139 +1059c1134,1135 < 154 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 154 CALL_METHOD MyException.message (dynamic) -1280c1356 +1276c1352 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1304c1380,1387 +1300c1376,1383 < 38 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) @@ -325,20 +325,20 @@ > 42 CONSTANT("IllegalArgumentException") > 42 CALL_METHOD scala.Predef.println (dynamic) > 42 JUMP 2 -1351c1434 +1347c1430 < locals: value args, variable result, value ex6, value x4, value x5, value message, value x --- > locals: value args, variable result, value ex6, value x4, value x5, value x -1353c1436 +1349c1432 < blocks: [1,2,3,4,5,8,10,11,13,14,16] --- > blocks: [1,2,3,5,8,10,11,13,14,16,17] -1377c1460,1461 +1373c1456,1457 < 203 THROW(MyException) --- > ? STORE_LOCAL(value ex6) > ? JUMP 17 -1397c1481,1490 +1393c1477,1486 < 209 THROW(MyException) --- > ? STORE_LOCAL(value ex6) @@ -351,41 +351,41 @@ > 212 LOAD_LOCAL(value x4) > 212 IS_INSTANCE REF(class MyException) > 212 CZJUMP (BOOL)NE ? 5 : 8 -1410,1412d1502 +1406,1408d1498 < 200 JUMP 4 < < 4: -1422,1425d1511 +1418,1421d1507 < 212 LOAD_LOCAL(value x5) < 212 CALL_METHOD MyException.message (dynamic) < 212 STORE_LOCAL(value message) < 212 SCOPE_ENTER value message -1427c1513,1514 +1423c1509,1510 < 213 LOAD_LOCAL(value message) --- > ? LOAD_LOCAL(value x5) > 213 CALL_METHOD MyException.message (dynamic) -1471c1558 +1467c1554 < blocks: [1,2,3,4,5,7] --- > blocks: [1,2,3,4,5,7,8] -1495c1582,1583 +1491c1578,1579 < 58 THROW(IllegalArgumentException) --- > ? STORE_LOCAL(value e) > ? JUMP 8 -1496a1585,1590 +1492a1581,1586 > 8: > 62 LOAD_MODULE object Predef > 62 CONSTANT("RuntimeException") > 62 CALL_METHOD scala.Predef.println (dynamic) > 62 JUMP 2 > -1544c1638 +1540c1634 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1564c1658,1663 +1560c1654,1659 < 229 THROW(MyException) --- > ? JUMP 5 @@ -394,19 +394,19 @@ > ? LOAD_LOCAL(variable monitor1) > 228 MONITOR_EXIT > 228 THROW(Throwable) -1570c1669 +1566c1665 < ? THROW(Throwable) --- > 228 THROW(Throwable) -1598c1697 +1594c1693 < locals: value args, variable result, variable monitor2, variable monitorResult1 --- > locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 -1600c1699 +1596c1695 < blocks: [1,2,3,4] --- > blocks: [1,2,3,4,5] -1623c1722,1730 +1619c1718,1726 < 245 THROW(MyException) --- > ? STORE_LOCAL(value exception$1) @@ -418,7 +418,7 @@ > ? LOAD_LOCAL(variable monitor2) > 244 MONITOR_EXIT > 244 THROW(Throwable) -1629c1736 +1625c1732 < ? THROW(Throwable) --- > 244 THROW(Throwable) diff --git a/test/files/run/t6288b-jump-position.check b/test/files/run/t6288b-jump-position.check index 45ec31c308..ece88b18f0 100644 --- a/test/files/run/t6288b-jump-position.check +++ b/test/files/run/t6288b-jump-position.check @@ -19,7 +19,7 @@ object Case3 extends Object { Exception handlers: def main(args: Array[String] (ARRAY[REF(class String)])): Unit { - locals: value args, value x1, value x2, value x + locals: value args, value x1, value x startBlock: 1 blocks: [1,2,3,6,7] @@ -35,10 +35,6 @@ object Case3 extends Object { 5 CZJUMP (BOOL)NE ? 3 : 6 3: - 5 LOAD_LOCAL(value x1) - 5 CHECK_CAST REF(class String) - 5 STORE_LOCAL(value x2) - 5 SCOPE_ENTER value x2 6 LOAD_MODULE object Predef 6 CONSTANT("case 0") 6 CALL_METHOD scala.Predef.println (dynamic) -- cgit v1.2.3 From b2117cf6f4080eff9028b2aa33a013329022efd6 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Thu, 31 Jan 2013 10:18:26 -0800 Subject: SI-6941 tests tests that the methods' bytecodes are similar as variable load/stores are reordered, it ignores which variables are modified when checking for bytecode equality the assert is: `similarBytecode(methNodeA, methNodeB, equalsModuloVar)` --- test/files/jvm/t6941.check | 1 + test/files/jvm/t6941.flags | 1 + test/files/jvm/t6941/Analyzed_1.scala | 11 +++++++++++ test/files/jvm/t6941/test.scala | 15 +++++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 test/files/jvm/t6941.check create mode 100644 test/files/jvm/t6941.flags create mode 100644 test/files/jvm/t6941/Analyzed_1.scala create mode 100644 test/files/jvm/t6941/test.scala (limited to 'test') diff --git a/test/files/jvm/t6941.check b/test/files/jvm/t6941.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/t6941.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/t6941.flags b/test/files/jvm/t6941.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/jvm/t6941.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/jvm/t6941/Analyzed_1.scala b/test/files/jvm/t6941/Analyzed_1.scala new file mode 100644 index 0000000000..549abd5e64 --- /dev/null +++ b/test/files/jvm/t6941/Analyzed_1.scala @@ -0,0 +1,11 @@ +// this class's bytecode, compiled under -optimize is analyzed by the test +// method a's bytecode should be identical to method b's bytecode +class SameBytecode { + def a(xs: List[Int]) = xs match { + case x :: _ => x + } + + def b(xs: List[Int]) = xs match { + case xs: ::[Int] => xs.hd$1 + } +} \ No newline at end of file diff --git a/test/files/jvm/t6941/test.scala b/test/files/jvm/t6941/test.scala new file mode 100644 index 0000000000..248617f71f --- /dev/null +++ b/test/files/jvm/t6941/test.scala @@ -0,0 +1,15 @@ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + def show: Unit = { + val classNode = loadClassNode("SameBytecode") + similarBytecode(getMethod(classNode, "a"), getMethod(classNode, "b"), equalsModuloVar) + } +} -- cgit v1.2.3 From 3f78bee128bd6a478bef6a66c5574f77a2d6dd74 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 30 Jan 2013 17:08:42 +0100 Subject: SI-7029 - Makes sure that uncaught exceptions are propagated to the UEH for the global ExecutionContext --- .../concurrent/impl/ExecutionContextImpl.scala | 34 +++++++++++++++++----- src/library/scala/concurrent/impl/Promise.scala | 2 +- test/files/jvm/future-spec/FutureTests.scala | 24 +++++++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 215f90b17e..77625e381c 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -25,11 +25,15 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: case some => some } + private val uncaughtExceptionHandler: Thread.UncaughtExceptionHandler = new Thread.UncaughtExceptionHandler { + def uncaughtException(thread: Thread, cause: Throwable): Unit = reporter(cause) + } + // Implement BlockContext on FJP threads class DefaultThreadFactory(daemonic: Boolean) extends ThreadFactory with ForkJoinPool.ForkJoinWorkerThreadFactory { def wire[T <: Thread](thread: T): T = { thread.setDaemon(daemonic) - //Potentially set things like uncaught exception handler, name etc + thread.setUncaughtExceptionHandler(uncaughtExceptionHandler) thread } @@ -73,7 +77,7 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: new ForkJoinPool( desiredParallelism, threadFactory, - null, //FIXME we should have an UncaughtExceptionHandler, see what Akka does + uncaughtExceptionHandler, true) // Async all the way baby } catch { case NonFatal(t) => @@ -94,13 +98,13 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: def execute(runnable: Runnable): Unit = executor match { case fj: ForkJoinPool => + val fjt = runnable match { + case t: ForkJoinTask[_] => t + case r => new ExecutionContextImpl.AdaptedForkJoinTask(r) + } Thread.currentThread match { - case fjw: ForkJoinWorkerThread if fjw.getPool eq fj => - (runnable match { - case fjt: ForkJoinTask[_] => fjt - case _ => ForkJoinTask.adapt(runnable) - }).fork - case _ => fj.execute(runnable) + case fjw: ForkJoinWorkerThread if fjw.getPool eq fj => fjt.fork() + case _ => fj execute fjt } case generic => generic execute runnable } @@ -111,6 +115,20 @@ private[scala] class ExecutionContextImpl private[impl] (es: Executor, reporter: private[concurrent] object ExecutionContextImpl { + final class AdaptedForkJoinTask(runnable: Runnable) extends ForkJoinTask[Unit] { + final override def setRawResult(u: Unit): Unit = () + final override def getRawResult(): Unit = () + final override def exec(): Boolean = try { runnable.run(); true } catch { + case anything: Throwable โ‡’ + val t = Thread.currentThread + t.getUncaughtExceptionHandler match { + case null โ‡’ + case some โ‡’ some.uncaughtException(t, anything) + } + throw anything + } + } + def fromExecutor(e: Executor, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextImpl = new ExecutionContextImpl(e, reporter) def fromExecutorService(es: ExecutorService, reporter: Throwable => Unit = ExecutionContext.defaultReporter): ExecutionContextImpl with ExecutionContextExecutorService = new ExecutionContextImpl(es, reporter) with ExecutionContextExecutorService { diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index e9da45a079..52f1075137 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -34,7 +34,7 @@ private class CallbackRunnable[T](val executor: ExecutionContext, val onComplete value = v // Note that we cannot prepare the ExecutionContext at this point, since we might // already be running on a different thread! - executor.execute(this) + try executor.execute(this) catch { case NonFatal(t) => executor reportFailure t } } } diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index 8674be168c..ae2e72bf96 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -74,6 +74,30 @@ object FutureTests extends MinimalScalaTest { "A future with global ExecutionContext" should { import ExecutionContext.Implicits._ + "output uncaught exceptions" in { + import java.io.{ ByteArrayOutputStream, PrintStream } + + val baos = new ByteArrayOutputStream(1 << 16) { def isEmpty: Boolean = count == 0 } + val tmpErr = new PrintStream(baos) + + def assertPrintedToErr(t: Throwable): Unit = { + t.printStackTrace(tmpErr) + tmpErr.flush() + val expected = baos.toByteArray.toIndexedSeq + baos.reset() + val f = Future(throw t) + val d = Deadline.now + 5.seconds + while(d.hasTimeLeft && baos.isEmpty) Thread.sleep(10) + baos.toByteArray.toIndexedSeq mustBe (expected) + } + + val oldErr = System.err + System.setErr(tmpErr) + try { + assertPrintedToErr(new NotImplementedError("foo")) + } finally System.setErr(oldErr) + } + "compose with for-comprehensions" in { def async(x: Int) = future { (x * 2).toString } val future0 = future[Any] { -- cgit v1.2.3 From 5275baee6c563418f53abd2486764be08916c8c5 Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Fri, 1 Feb 2013 03:32:18 +0100 Subject: SI-7029 - Make test more robust --- test/files/jvm/future-spec/FutureTests.scala | 36 ++++++++++------------------ 1 file changed, 12 insertions(+), 24 deletions(-) (limited to 'test') diff --git a/test/files/jvm/future-spec/FutureTests.scala b/test/files/jvm/future-spec/FutureTests.scala index ae2e72bf96..0efa83fbd9 100644 --- a/test/files/jvm/future-spec/FutureTests.scala +++ b/test/files/jvm/future-spec/FutureTests.scala @@ -70,33 +70,21 @@ object FutureTests extends MinimalScalaTest { //FIXME should check } } - - "A future with global ExecutionContext" should { - import ExecutionContext.Implicits._ - "output uncaught exceptions" in { - import java.io.{ ByteArrayOutputStream, PrintStream } - - val baos = new ByteArrayOutputStream(1 << 16) { def isEmpty: Boolean = count == 0 } - val tmpErr = new PrintStream(baos) - - def assertPrintedToErr(t: Throwable): Unit = { - t.printStackTrace(tmpErr) - tmpErr.flush() - val expected = baos.toByteArray.toIndexedSeq - baos.reset() - val f = Future(throw t) - val d = Deadline.now + 5.seconds - while(d.hasTimeLeft && baos.isEmpty) Thread.sleep(10) - baos.toByteArray.toIndexedSeq mustBe (expected) - } + "The default ExecutionContext" should { + "report uncaught exceptions" in { + val p = Promise[Throwable]() + val logThrowable: Throwable => Unit = p.trySuccess(_) + val ec: ExecutionContext = ExecutionContext.fromExecutor(null, logThrowable) - val oldErr = System.err - System.setErr(tmpErr) - try { - assertPrintedToErr(new NotImplementedError("foo")) - } finally System.setErr(oldErr) + val t = new NotImplementedError("foo") + val f = Future(throw t)(ec) + Await.result(p.future, 2.seconds) mustBe t } + } + + "A future with global ExecutionContext" should { + import ExecutionContext.Implicits._ "compose with for-comprehensions" in { def async(x: Int) = future { (x * 2).toString } -- cgit v1.2.3 From f1701f704a2485fcc2eb6d5d8b5d0228beddd9b3 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 31 Jan 2013 20:40:16 +0100 Subject: SI-7008 @throws annotations are now populated in reflect Runtime reflection in JavaMirrors previously forgot to fill in @throws when importing Java reflection artifacts. Now this is fixed. Note that generic exception types used in `throws` specifications will be garbled (i.e. erased), because we don't use `getGenericExceptionTypes` in favor of just `getExceptionTypes` to stay compatible with the behavior of ClassfileParser. That's a bug, but a separate one and should be fixed separately. Also note that this commit updated javac-artifacts.jar, because we need to test how reflection works with javac-produced classfiles. The sources that were used to produce those classfiles can be found in the jar next to the classfiles. --- .../tools/nsc/symtab/classfile/ClassfileParser.scala | 8 +------- .../scala/reflect/internal/AnnotationInfos.scala | 17 ++++++++++++++--- src/reflect/scala/reflect/runtime/JavaMirrors.scala | 8 ++++++++ test/files/lib/javac-artifacts.jar.desired.sha1 | 2 +- test/files/run/t7008.check | 9 +++++++++ test/files/run/t7008/Impls_Macros_1.scala | 12 ++++++++++++ test/files/run/t7008/Test_2.scala | 9 +++++++++ 7 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 test/files/run/t7008.check create mode 100644 test/files/run/t7008/Impls_Macros_1.scala create mode 100644 test/files/run/t7008/Test_2.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 4b1d3c34f3..7ac7b4d637 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1043,13 +1043,7 @@ abstract class ClassfileParser { val nClasses = in.nextChar for (n <- 0 until nClasses) { val cls = pool.getClassSymbol(in.nextChar.toInt) - val tp = if (cls.isMonomorphicType) cls.tpe else { - debuglog(s"Encountered polymorphic exception `${cls.fullName}` while parsing class file.") - // in case we encounter polymorphic exception the best we can do is to convert that type to - // monomorphic one by introducing existientals, see SI-7009 for details - typer.packSymbols(cls.typeParams, cls.tpe) - } - sym.addAnnotation(appliedType(definitions.ThrowsClass, tp), Literal(Constant(tp))) + sym.addThrowsAnnotation(cls) } } diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 6a5a742cc7..29879f9806 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -33,6 +33,17 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => case ThrownException(exc) => exc } + def addThrowsAnnotation(excSym: Symbol): Self = { + val excTpe = if (excSym.isMonomorphicType) excSym.tpe else { + debuglog(s"Encountered polymorphic exception `${excSym.fullName}` while parsing class file.") + // in case we encounter polymorphic exception the best we can do is to convert that type to + // monomorphic one by introducing existentials, see SI-7009 for details + existentialAbstraction(excSym.typeParams, excSym.tpe) + } + val throwsAnn = AnnotationInfo(appliedType(definitions.ThrowsClass, excTpe), List(Literal(Constant(excTpe))), Nil) + withAnnotations(List(throwsAnn)) + } + /** Tests for, get, or remove an annotation */ def hasAnnotation(cls: Symbol): Boolean = //OPT inlined from exists to save on #closures; was: annotations exists (_ matches cls) @@ -330,14 +341,14 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => implicit val AnnotationTag = ClassTag[AnnotationInfo](classOf[AnnotationInfo]) object UnmappableAnnotation extends CompleteAnnotationInfo(NoType, Nil, Nil) - + /** Extracts symbol of thrown exception from AnnotationInfo. - * + * * Supports both โ€œold-styleโ€ `@throws(classOf[Exception])` * as well as โ€œnew-styeโ€ `@throws[Exception]("cause")` annotations. */ object ThrownException { - def unapply(ann: AnnotationInfo): Option[Symbol] = + def unapply(ann: AnnotationInfo): Option[Symbol] = ann match { case AnnotationInfo(tpe, _, _) if tpe.typeSymbol != ThrowsClass => None diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 01e0634902..698b60b929 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -610,11 +610,19 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni /** * Copy all annotations of Java annotated element `jann` over to Scala symbol `sym`. + * Also creates `@throws` annotations if necessary. * Pre: `sym` is already initialized with a concrete type. * Note: If `sym` is a method or constructor, its parameter annotations are copied as well. */ private def copyAnnotations(sym: Symbol, jann: AnnotatedElement) { sym setAnnotations (jann.getAnnotations map JavaAnnotationProxy).toList + // FIXME: we're not using getGenericExceptionTypes here to be consistent with ClassfileParser + val jexTpes = jann match { + case jm: jMethod => jm.getExceptionTypes.toList + case jconstr: jConstructor[_] => jconstr.getExceptionTypes.toList + case _ => Nil + } + jexTpes foreach (jexTpe => sym.addThrowsAnnotation(classSymbol(jexTpe))) } /** diff --git a/test/files/lib/javac-artifacts.jar.desired.sha1 b/test/files/lib/javac-artifacts.jar.desired.sha1 index a49c986386..508141b771 100644 --- a/test/files/lib/javac-artifacts.jar.desired.sha1 +++ b/test/files/lib/javac-artifacts.jar.desired.sha1 @@ -1 +1 @@ -61931a51bb1a2d308d214b96d06e9a8808515dcf ?javac-artifacts.jar +a05ca69ac68fa7006b1b8ed98597046e105b4047 ?javac-artifacts.jar diff --git a/test/files/run/t7008.check b/test/files/run/t7008.check new file mode 100644 index 0000000000..ee077f90ff --- /dev/null +++ b/test/files/run/t7008.check @@ -0,0 +1,9 @@ +: List(throws[NullPointerException](classOf[java.lang.NullPointerException])) +bar: List(throws[Exception](classOf[java.lang.Exception])) +baz: List(throws[IllegalStateException](classOf[java.lang.IllegalStateException])) +foo: List(throws[Exception](classOf[java.lang.Exception])) +============= +: List(throws[java.lang.NullPointerException](classOf[java.lang.NullPointerException])) +bar: List(throws[java.lang.Exception](classOf[java.lang.Exception])) +baz: List(throws[java.lang.IllegalStateException](classOf[java.lang.IllegalStateException])) +foo: List(throws[java.lang.Exception](classOf[java.lang.Exception])) diff --git a/test/files/run/t7008/Impls_Macros_1.scala b/test/files/run/t7008/Impls_Macros_1.scala new file mode 100644 index 0000000000..f2eb7425f5 --- /dev/null +++ b/test/files/run/t7008/Impls_Macros_1.scala @@ -0,0 +1,12 @@ +import language.experimental.macros +import scala.reflect.macros.Context + +object Macros { + def impl(c: Context) = { + val decls = c.typeOf[JavaClassWithCheckedExceptions[_]].declarations.toList + val s = decls.sortBy(_.name.toString).map(decl => (s"${decl.name}: ${decl.annotations}")).mkString(scala.compat.Platform.EOL) + c.universe.reify(println(c.literal(s).splice)) + } + + def foo = macro impl +} \ No newline at end of file diff --git a/test/files/run/t7008/Test_2.scala b/test/files/run/t7008/Test_2.scala new file mode 100644 index 0000000000..b67faa327f --- /dev/null +++ b/test/files/run/t7008/Test_2.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + Macros.foo + println("=============") + + val decls = typeOf[JavaClassWithCheckedExceptions[_]].declarations.toList + decls sortBy (_.name.toString) foreach (decl => println(s"${decl.name}: ${decl.annotations}")) +} \ No newline at end of file -- cgit v1.2.3 From adf50a3ac0c6861a77a781abc0814c5d17927175 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 1 Feb 2013 19:23:00 +0100 Subject: evicts javac-artifacts.jar Apparently, the usual _1, _2, _3... naming scheme also works for java files, which need to be compiled together with partests. This allows us to get rid of javac-artifacts.jar. --- test/files/lib/javac-artifacts.jar.desired.sha1 | 1 - test/files/run/reflection-java-annotations.check | 2 +- test/files/run/reflection-java-annotations.scala | 7 ---- .../JavaAnnottee_1.java | 47 ++++++++++++++++++++++ .../JavaComplexAnnotation_1.java | 34 ++++++++++++++++ .../JavaSimpleAnnotation_1.java | 21 ++++++++++ .../JavaSimpleEnumeration_1.java | 4 ++ .../run/reflection-java-annotations/Test_2.scala | 7 ++++ test/files/run/reflection-java-crtp.scala | 8 ---- .../JavaSimpleEnumeration_1.java | 4 ++ test/files/run/reflection-java-crtp/Main_2.scala | 8 ++++ test/files/run/t6548.check | 2 +- test/files/run/t6548.scala | 12 ------ .../run/t6548/JavaAnnotationWithNestedEnum_1.java | 17 ++++++++ test/files/run/t6548/Test_2.scala | 12 ++++++ test/files/run/t7008/Impls_Macros_1.scala | 12 ------ test/files/run/t7008/Impls_Macros_2.scala | 12 ++++++ .../t7008/JavaClassWithCheckedExceptions_1.java | 7 ++++ test/files/run/t7008/Test_2.scala | 9 ----- test/files/run/t7008/Test_3.scala | 9 +++++ 20 files changed, 184 insertions(+), 51 deletions(-) delete mode 100644 test/files/lib/javac-artifacts.jar.desired.sha1 delete mode 100644 test/files/run/reflection-java-annotations.scala create mode 100644 test/files/run/reflection-java-annotations/JavaAnnottee_1.java create mode 100644 test/files/run/reflection-java-annotations/JavaComplexAnnotation_1.java create mode 100644 test/files/run/reflection-java-annotations/JavaSimpleAnnotation_1.java create mode 100644 test/files/run/reflection-java-annotations/JavaSimpleEnumeration_1.java create mode 100644 test/files/run/reflection-java-annotations/Test_2.scala delete mode 100644 test/files/run/reflection-java-crtp.scala create mode 100644 test/files/run/reflection-java-crtp/JavaSimpleEnumeration_1.java create mode 100644 test/files/run/reflection-java-crtp/Main_2.scala delete mode 100644 test/files/run/t6548.scala create mode 100644 test/files/run/t6548/JavaAnnotationWithNestedEnum_1.java create mode 100644 test/files/run/t6548/Test_2.scala delete mode 100644 test/files/run/t7008/Impls_Macros_1.scala create mode 100644 test/files/run/t7008/Impls_Macros_2.scala create mode 100644 test/files/run/t7008/JavaClassWithCheckedExceptions_1.java delete mode 100644 test/files/run/t7008/Test_2.scala create mode 100644 test/files/run/t7008/Test_3.scala (limited to 'test') diff --git a/test/files/lib/javac-artifacts.jar.desired.sha1 b/test/files/lib/javac-artifacts.jar.desired.sha1 deleted file mode 100644 index 508141b771..0000000000 --- a/test/files/lib/javac-artifacts.jar.desired.sha1 +++ /dev/null @@ -1 +0,0 @@ -a05ca69ac68fa7006b1b8ed98597046e105b4047 ?javac-artifacts.jar diff --git a/test/files/run/reflection-java-annotations.check b/test/files/run/reflection-java-annotations.check index 53c53cfbcc..2d37fff1f4 100644 --- a/test/files/run/reflection-java-annotations.check +++ b/test/files/run/reflection-java-annotations.check @@ -1 +1 @@ -List(JavaComplexAnnotation(v1 = 1, v10 = "hello", v101 = [101, 101], v102 = [102, 102], v103 = ['g', 'g'], v104 = [104, 104], v105 = [105L, 105L], v106 = [106.0, 106.0], v107 = [107.0, 107.0], v108 = [false, true], v11 = classOf[JavaAnnottee], v110 = ["hello", "world"], v111 = [classOf[JavaSimpleAnnotation], classOf[JavaComplexAnnotation]], v112 = [FOO, BAR], v113 = [JavaSimpleAnnotation(v1 = 21, v10 = "world2", v11 = classOf[JavaComplexAnnotation], v12 = BAR, v2 = 22, v3 = '\027', v4 = 24, v5 = 25L, v6 = 26.0, v7 = 27.0, v8 = false)], v12 = FOO, v13 = JavaSimpleAnnotation(v1 = 11, v10 = "world1", v11 = classOf[JavaSimpleAnnotation], v12 = FOO, v2 = 12, v3 = '\r', v4 = 14, v5 = 15L, v6 = 16.0, v7 = 17.0, v8 = false), v2 = 2, v3 = '\03', v4 = 4, v5 = 5L, v6 = 6.0, v7 = 7.0, v8 = false)) +List(JavaComplexAnnotation_1(v1 = 1, v10 = "hello", v101 = [101, 101], v102 = [102, 102], v103 = ['g', 'g'], v104 = [104, 104], v105 = [105L, 105L], v106 = [106.0, 106.0], v107 = [107.0, 107.0], v108 = [false, true], v11 = classOf[JavaAnnottee_1], v110 = ["hello", "world"], v111 = [classOf[JavaSimpleAnnotation_1], classOf[JavaComplexAnnotation_1]], v112 = [FOO, BAR], v113 = [JavaSimpleAnnotation_1(v1 = 21, v10 = "world2", v11 = classOf[JavaComplexAnnotation_1], v12 = BAR, v2 = 22, v3 = '\027', v4 = 24, v5 = 25L, v6 = 26.0, v7 = 27.0, v8 = false)], v12 = FOO, v13 = JavaSimpleAnnotation_1(v1 = 11, v10 = "world1", v11 = classOf[JavaSimpleAnnotation_1], v12 = FOO, v2 = 12, v3 = '\r', v4 = 14, v5 = 15L, v6 = 16.0, v7 = 17.0, v8 = false), v2 = 2, v3 = '\03', v4 = 4, v5 = 5L, v6 = 6.0, v7 = 7.0, v8 = false)) diff --git a/test/files/run/reflection-java-annotations.scala b/test/files/run/reflection-java-annotations.scala deleted file mode 100644 index 2e3fed48ce..0000000000 --- a/test/files/run/reflection-java-annotations.scala +++ /dev/null @@ -1,7 +0,0 @@ -object Test extends App { - import scala.reflect.runtime.universe._ - val sym = typeOf[JavaAnnottee].typeSymbol - sym.typeSignature - sym.annotations foreach (_.javaArgs) - println(sym.annotations) -} \ No newline at end of file diff --git a/test/files/run/reflection-java-annotations/JavaAnnottee_1.java b/test/files/run/reflection-java-annotations/JavaAnnottee_1.java new file mode 100644 index 0000000000..b241f5d25e --- /dev/null +++ b/test/files/run/reflection-java-annotations/JavaAnnottee_1.java @@ -0,0 +1,47 @@ +@JavaComplexAnnotation_1( + v1 = (byte)1, + v2 = (short)2, + v3 = (char)3, + v4 = (int)4, + v5 = (long)5, + v6 = (float)6, + v7 = (double)7, + v10 = "hello", + v11 = JavaAnnottee_1.class, + v12 = JavaSimpleEnumeration_1.FOO, + v13 = @JavaSimpleAnnotation_1( + v1 = (byte)11, + v2 = (short)12, + v3 = (char)13, + v4 = (int)14, + v5 = (long)15, + v6 = (float)16, + v7 = (double)17, + v10 = "world1", + v11 = JavaSimpleAnnotation_1.class, + v12 = JavaSimpleEnumeration_1.FOO + ), + v101 = {(byte)101, (byte)101}, + v102 = {(short)102, (short)102}, + v103 = {(char)103, (char)103}, + v104 = {(int)104, (int)104}, + v105 = {(long)105, (long)105}, + v106 = {(float)106, (float)106}, + v107 = {(double)107, (double)107}, + v108 = {false, true}, + v110 = {"hello", "world"}, + v111 = {JavaSimpleAnnotation_1.class, JavaComplexAnnotation_1.class}, + v112 = {JavaSimpleEnumeration_1.FOO, JavaSimpleEnumeration_1.BAR}, + v113 = {@JavaSimpleAnnotation_1( + v1 = (byte)21, + v2 = (short)22, + v3 = (char)23, + v4 = (int)24, + v5 = (long)25, + v6 = (float)26, + v7 = (double)27, + v10 = "world2", + v11 = JavaComplexAnnotation_1.class, + v12 = JavaSimpleEnumeration_1.BAR + )}) +public class JavaAnnottee_1 {} \ No newline at end of file diff --git a/test/files/run/reflection-java-annotations/JavaComplexAnnotation_1.java b/test/files/run/reflection-java-annotations/JavaComplexAnnotation_1.java new file mode 100644 index 0000000000..645eeb9399 --- /dev/null +++ b/test/files/run/reflection-java-annotations/JavaComplexAnnotation_1.java @@ -0,0 +1,34 @@ +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +public @interface JavaComplexAnnotation_1 { + byte v1(); + short v2(); + char v3(); + int v4(); + long v5(); + float v6(); + double v7(); + boolean v8() default false; + // void v9(); + String v10(); + Class v11(); + JavaSimpleEnumeration_1 v12(); + JavaSimpleAnnotation_1 v13(); + byte[] v101(); + short[] v102(); + char[] v103(); + int[] v104(); + long[] v105(); + float[] v106(); + double[] v107(); + boolean[] v108(); + String[] v110(); + Class[] v111(); + JavaSimpleEnumeration_1[] v112(); + JavaSimpleAnnotation_1[] v113(); +} \ No newline at end of file diff --git a/test/files/run/reflection-java-annotations/JavaSimpleAnnotation_1.java b/test/files/run/reflection-java-annotations/JavaSimpleAnnotation_1.java new file mode 100644 index 0000000000..c0f92fad2c --- /dev/null +++ b/test/files/run/reflection-java-annotations/JavaSimpleAnnotation_1.java @@ -0,0 +1,21 @@ +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) +public @interface JavaSimpleAnnotation_1 { + byte v1(); + short v2(); + char v3(); + int v4(); + long v5(); + float v6(); + double v7(); + boolean v8() default false; + // void v9(); + String v10(); + Class v11(); + JavaSimpleEnumeration_1 v12(); +} \ No newline at end of file diff --git a/test/files/run/reflection-java-annotations/JavaSimpleEnumeration_1.java b/test/files/run/reflection-java-annotations/JavaSimpleEnumeration_1.java new file mode 100644 index 0000000000..39246141cc --- /dev/null +++ b/test/files/run/reflection-java-annotations/JavaSimpleEnumeration_1.java @@ -0,0 +1,4 @@ +enum JavaSimpleEnumeration_1 { + FOO, + BAR +} \ No newline at end of file diff --git a/test/files/run/reflection-java-annotations/Test_2.scala b/test/files/run/reflection-java-annotations/Test_2.scala new file mode 100644 index 0000000000..d2c3157071 --- /dev/null +++ b/test/files/run/reflection-java-annotations/Test_2.scala @@ -0,0 +1,7 @@ +object Test extends App { + import scala.reflect.runtime.universe._ + val sym = typeOf[JavaAnnottee_1].typeSymbol + sym.typeSignature + sym.annotations foreach (_.javaArgs) + println(sym.annotations) +} \ No newline at end of file diff --git a/test/files/run/reflection-java-crtp.scala b/test/files/run/reflection-java-crtp.scala deleted file mode 100644 index 260d3540dc..0000000000 --- a/test/files/run/reflection-java-crtp.scala +++ /dev/null @@ -1,8 +0,0 @@ -object Test extends App { - import scala.reflect.runtime.universe._ - val enum = typeOf[JavaSimpleEnumeration].baseClasses(1).asClass - // make sure that the E's in Enum> are represented by the same symbol - val e1 = enum.typeParams(0).asType - val TypeBounds(_, TypeRef(_, _, List(TypeRef(_, e2: TypeSymbol, _)))) = e1.typeSignature - println(e1, e2, e1 eq e2) -} \ No newline at end of file diff --git a/test/files/run/reflection-java-crtp/JavaSimpleEnumeration_1.java b/test/files/run/reflection-java-crtp/JavaSimpleEnumeration_1.java new file mode 100644 index 0000000000..39246141cc --- /dev/null +++ b/test/files/run/reflection-java-crtp/JavaSimpleEnumeration_1.java @@ -0,0 +1,4 @@ +enum JavaSimpleEnumeration_1 { + FOO, + BAR +} \ No newline at end of file diff --git a/test/files/run/reflection-java-crtp/Main_2.scala b/test/files/run/reflection-java-crtp/Main_2.scala new file mode 100644 index 0000000000..fb5668f323 --- /dev/null +++ b/test/files/run/reflection-java-crtp/Main_2.scala @@ -0,0 +1,8 @@ +object Test extends App { + import scala.reflect.runtime.universe._ + val enum = typeOf[JavaSimpleEnumeration_1].baseClasses(1).asClass + // make sure that the E's in Enum> are represented by the same symbol + val e1 = enum.typeParams(0).asType + val TypeBounds(_, TypeRef(_, _, List(TypeRef(_, e2: TypeSymbol, _)))) = e1.typeSignature + println(e1, e2, e1 eq e2) +} \ No newline at end of file diff --git a/test/files/run/t6548.check b/test/files/run/t6548.check index e82cae110a..5dfcb12e02 100644 --- a/test/files/run/t6548.check +++ b/test/files/run/t6548.check @@ -1,2 +1,2 @@ false -List(JavaAnnotationWithNestedEnum(value = VALUE)) +List(JavaAnnotationWithNestedEnum_1(value = VALUE)) diff --git a/test/files/run/t6548.scala b/test/files/run/t6548.scala deleted file mode 100644 index be3eb5b932..0000000000 --- a/test/files/run/t6548.scala +++ /dev/null @@ -1,12 +0,0 @@ -import scala.reflect.runtime.universe._ -import scala.reflect.runtime.{currentMirror => cm} - -class Bean { - @JavaAnnotationWithNestedEnum(JavaAnnotationWithNestedEnum.Value.VALUE) - def value = 1 -} - -object Test extends App { - println(cm.staticClass("Bean").isCaseClass) - println(typeOf[Bean].declaration(newTermName("value")).annotations) -} diff --git a/test/files/run/t6548/JavaAnnotationWithNestedEnum_1.java b/test/files/run/t6548/JavaAnnotationWithNestedEnum_1.java new file mode 100644 index 0000000000..32004de537 --- /dev/null +++ b/test/files/run/t6548/JavaAnnotationWithNestedEnum_1.java @@ -0,0 +1,17 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, + ElementType.TYPE, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface JavaAnnotationWithNestedEnum_1 +{ + public Value value() default Value.VALUE; + + public enum Value + { + VALUE; + } +} \ No newline at end of file diff --git a/test/files/run/t6548/Test_2.scala b/test/files/run/t6548/Test_2.scala new file mode 100644 index 0000000000..6e4f6ba92a --- /dev/null +++ b/test/files/run/t6548/Test_2.scala @@ -0,0 +1,12 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +class Bean { + @JavaAnnotationWithNestedEnum_1(JavaAnnotationWithNestedEnum_1.Value.VALUE) + def value = 1 +} + +object Test extends App { + println(cm.staticClass("Bean").isCaseClass) + println(typeOf[Bean].declaration(newTermName("value")).annotations) +} diff --git a/test/files/run/t7008/Impls_Macros_1.scala b/test/files/run/t7008/Impls_Macros_1.scala deleted file mode 100644 index f2eb7425f5..0000000000 --- a/test/files/run/t7008/Impls_Macros_1.scala +++ /dev/null @@ -1,12 +0,0 @@ -import language.experimental.macros -import scala.reflect.macros.Context - -object Macros { - def impl(c: Context) = { - val decls = c.typeOf[JavaClassWithCheckedExceptions[_]].declarations.toList - val s = decls.sortBy(_.name.toString).map(decl => (s"${decl.name}: ${decl.annotations}")).mkString(scala.compat.Platform.EOL) - c.universe.reify(println(c.literal(s).splice)) - } - - def foo = macro impl -} \ No newline at end of file diff --git a/test/files/run/t7008/Impls_Macros_2.scala b/test/files/run/t7008/Impls_Macros_2.scala new file mode 100644 index 0000000000..7a17314085 --- /dev/null +++ b/test/files/run/t7008/Impls_Macros_2.scala @@ -0,0 +1,12 @@ +import language.experimental.macros +import scala.reflect.macros.Context + +object Macros { + def impl(c: Context) = { + val decls = c.typeOf[JavaClassWithCheckedExceptions_1[_]].declarations.toList + val s = decls.sortBy(_.name.toString).map(decl => (s"${decl.name}: ${decl.annotations}")).mkString(scala.compat.Platform.EOL) + c.universe.reify(println(c.literal(s).splice)) + } + + def foo = macro impl +} \ No newline at end of file diff --git a/test/files/run/t7008/JavaClassWithCheckedExceptions_1.java b/test/files/run/t7008/JavaClassWithCheckedExceptions_1.java new file mode 100644 index 0000000000..dda2128302 --- /dev/null +++ b/test/files/run/t7008/JavaClassWithCheckedExceptions_1.java @@ -0,0 +1,7 @@ +class JavaClassWithCheckedExceptions_1 { + public JavaClassWithCheckedExceptions_1() throws NullPointerException {} + + public void bar() throws E1 {} + public void baz(int x) throws IllegalStateException {} + public void foo() throws E2 {} +} \ No newline at end of file diff --git a/test/files/run/t7008/Test_2.scala b/test/files/run/t7008/Test_2.scala deleted file mode 100644 index b67faa327f..0000000000 --- a/test/files/run/t7008/Test_2.scala +++ /dev/null @@ -1,9 +0,0 @@ -import scala.reflect.runtime.universe._ - -object Test extends App { - Macros.foo - println("=============") - - val decls = typeOf[JavaClassWithCheckedExceptions[_]].declarations.toList - decls sortBy (_.name.toString) foreach (decl => println(s"${decl.name}: ${decl.annotations}")) -} \ No newline at end of file diff --git a/test/files/run/t7008/Test_3.scala b/test/files/run/t7008/Test_3.scala new file mode 100644 index 0000000000..b2961a829e --- /dev/null +++ b/test/files/run/t7008/Test_3.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + Macros.foo + println("=============") + + val decls = typeOf[JavaClassWithCheckedExceptions_1[_]].declarations.toList + decls sortBy (_.name.toString) foreach (decl => println(s"${decl.name}: ${decl.annotations}")) +} \ No newline at end of file -- cgit v1.2.3 From 8ae0e2a37764e0193dccd5658fd35f56edcbfd69 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 1 Feb 2013 11:55:01 -0800 Subject: SI-7039 unapplySeq result type independent of subpattern count Fixes a bug in the implementation of the `unapplySeq` part of the spec below. An `unapply` method with result type `R` in an object `x` matches the pattern `x(p_1, ..., p_n)` if it takes exactly one argument and, either: - `n = 0` and `R =:= Boolean`, or - `n = 1` and `R <:< Option[T]`, for some type `T`. The argument pattern `p1` is typed in turn with expected type `T`. - Or, `n > 1` and `R <:< Option[Product_n[T_1, ..., T_n]]`, for some types `T_1, ..., T_n`. The argument patterns `p_1, ..., p_n` are typed with expected types `T_1, ..., T_n`. An `unapplySeq` method in an object `x` matches the pattern `x(p_1, ..., p_n)` if it takes exactly one argument and its result type is of the form `Option[S]`, where either: - `S` is a subtype of `Seq[U]` for some element type `U`, (set `m = 0`) - or `S` is a `ProductX[T_1, ..., T_m]` and `T_m <: Seq[U]` (`m <= n`). The argument patterns `p_1, ..., p_n` are typed with expected types `T_1, ..., T_m, U, ..., U`. Here, `U` is repeated `n-m` times. --- .../scala/tools/nsc/typechecker/Infer.scala | 70 +++++++++++++--------- .../tools/nsc/typechecker/PatternMatching.scala | 2 +- test/files/run/t7039.check | 1 + test/files/run/t7039.scala | 11 ++++ 4 files changed, 55 insertions(+), 29 deletions(-) create mode 100644 test/files/run/t7039.check create mode 100644 test/files/run/t7039.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 581f9f3bfa..d724bc430e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -58,20 +58,31 @@ trait Infer extends Checkable { * @throws TypeError when the unapply[Seq] definition is ill-typed * @returns (null, null) when the expected number of sub-patterns cannot be satisfied by the given extractor * - * From the spec: + * This is the spec currently implemented -- TODO: update it. + * * 8.1.8 ExtractorPatterns * * An extractor pattern x(p1, ..., pn) where n โ‰ฅ 0 is of the same syntactic form as a constructor pattern. * However, instead of a case class, the stable identifier x denotes an object which has a member method named unapply or unapplySeq that matches the pattern. - * An unapply method in an object x matches the pattern x(p1, ..., pn) if it takes exactly one argument and one of the following applies: * - * n = 0 and unapplyโ€™s result type is Boolean. + * An `unapply` method with result type `R` in an object `x` matches the + * pattern `x(p_1, ..., p_n)` if it takes exactly one argument and, either: + * - `n = 0` and `R =:= Boolean`, or + * - `n = 1` and `R <:< Option[T]`, for some type `T`. + * The argument pattern `p1` is typed in turn with expected type `T`. + * - Or, `n > 1` and `R <:< Option[Product_n[T_1, ..., T_n]]`, for some + * types `T_1, ..., T_n`. The argument patterns `p_1, ..., p_n` are + * typed with expected types `T_1, ..., T_n`. + * + * An `unapplySeq` method in an object `x` matches the pattern `x(p_1, ..., p_n)` + * if it takes exactly one argument and its result type is of the form `Option[S]`, + * where either: + * - `S` is a subtype of `Seq[U]` for some element type `U`, (set `m = 0`) + * - or `S` is a `ProductX[T_1, ..., T_m]` and `T_m <: Seq[U]` (`m <= n`). * - * n = 1 and unapplyโ€™s result type is Option[T], for some type T. - * the (only) argument pattern p1 is typed in turn with expected type T + * The argument patterns `p_1, ..., p_n` are typed with expected types + * `T_1, ..., T_m, U, ..., U`. Here, `U` is repeated `n-m` times. * - * n > 1 and unapplyโ€™s result type is Option[(T1, ..., Tn)], for some types T1, ..., Tn. - * the argument patterns p1, ..., pn are typed in turn with expected types T1, ..., Tn */ def extractorFormalTypes(pos: Position, resTp: Type, nbSubPats: Int, unappSym: Symbol): (List[Type], List[Type]) = { val isUnapplySeq = unappSym.name == nme.unapplySeq @@ -83,31 +94,34 @@ trait Infer extends Checkable { else toRepeated } + // empty list --> error, otherwise length == 1 + lazy val optionArgs = resTp.baseType(OptionClass).typeArgs + // empty list --> not a ProductN, otherwise product element types + def productArgs = getProductArgs(optionArgs.head) + val formals = - if (nbSubPats == 0 && booleanExtractor && !isUnapplySeq) Nil - else resTp.baseType(OptionClass).typeArgs match { - case optionTArg :: Nil => - def productArgs = getProductArgs(optionTArg) + // convert Seq[T] to the special repeated argument type + // so below we can use formalTypes to expand formals to correspond to the number of actuals + if (isUnapplySeq) { + if (optionArgs.nonEmpty) + productArgs match { + case Nil => List(seqToRepeatedChecked(optionArgs.head)) + case normalTps :+ seqTp => normalTps :+ seqToRepeatedChecked(seqTp) + } + else throw new TypeError(s"result type $resTp of unapplySeq defined in ${unappSym.fullLocationString} does not conform to Option[_]") + } else { + if (booleanExtractor && nbSubPats == 0) Nil + else if (optionArgs.nonEmpty) if (nbSubPats == 1) { - if (isUnapplySeq) List(seqToRepeatedChecked(optionTArg)) - else { - val productArity = productArgs.size - if (productArity > 1 && settings.lint.value) - global.currentUnit.warning(pos, s"extractor pattern binds a single value to a Product${productArity} of type ${optionTArg}") - List(optionTArg) - } + val productArity = productArgs.size + if (productArity > 1 && settings.lint.value) + global.currentUnit.warning(pos, s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") + optionArgs } // TODO: update spec to reflect we allow any ProductN, not just TupleN - else productArgs match { - case Nil if isUnapplySeq => List(seqToRepeatedChecked(optionTArg)) - case tps if isUnapplySeq => tps.init :+ seqToRepeatedChecked(tps.last) - case tps => tps - } - case _ => - if (isUnapplySeq) - throw new TypeError(s"result type $resTp of unapplySeq defined in ${unappSym.owner+unappSym.owner.locationString} not in {Option[_], Some[_]}") - else - throw new TypeError(s"result type $resTp of unapply defined in ${unappSym.owner+unappSym.owner.locationString} not in {Boolean, Option[_], Some[_]}") + else productArgs + else + throw new TypeError(s"result type $resTp of unapply defined in ${unappSym.fullLocationString} does not conform to Option[_] or Boolean") } // for unapplySeq, replace last vararg by as many instances as required by nbSubPats diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index b0745b4c09..59ced09451 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -800,7 +800,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL protected lazy val rawSubPatTypes = if (resultInMonad.typeSymbol eq UnitClass) Nil - else if(nbSubPats == 1) List(resultInMonad) + else if(!isSeq && nbSubPats == 1) List(resultInMonad) else getProductArgs(resultInMonad) match { case Nil => List(resultInMonad) case x => x diff --git a/test/files/run/t7039.check b/test/files/run/t7039.check new file mode 100644 index 0000000000..954906040f --- /dev/null +++ b/test/files/run/t7039.check @@ -0,0 +1 @@ +Matched! diff --git a/test/files/run/t7039.scala b/test/files/run/t7039.scala new file mode 100644 index 0000000000..475c4ae267 --- /dev/null +++ b/test/files/run/t7039.scala @@ -0,0 +1,11 @@ +object UnapplySeqTest { + def unapplySeq(any: Any): Option[(Int, Seq[Int])] = Some((5, List(1))) +} + +object Test extends App { + null match { + case UnapplySeqTest(5) => println("uh-oh") + case UnapplySeqTest(5, 1) => println("Matched!") // compiles + case UnapplySeqTest(5, xs @ _*) => println("toooo long: "+ (xs: Seq[Int])) + } +} \ No newline at end of file -- cgit v1.2.3 From 4ed88363f6867e5b4ebedfde10b88c0167f24f23 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 26 Jan 2013 10:37:12 +0100 Subject: [backport] SI-6482, lost bounds in extension methods. Squashed commit of the following: commit 5c156185306ba797c0443d9dccae0ae7ce462a1f Author: Paul Phillips Date: Sat Oct 6 15:42:50 2012 -0700 A little more housecleaning in ExtensionMethods. The only real contribution is readability. (cherry picked from commit 61f12faacaaccf366f9211ba6493fb042a91f1d2) Conflicts: src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala commit 79f443edf584745d614e24fb9ca6644c6b18d439 Author: Paul Phillips Date: Sat Oct 6 14:22:19 2012 -0700 Incorporated pull request feedback. (cherry picked from commit 153ccb4757718cceb219988f30381f73362e6075) commit 707f580b0cdcb01e27ca4c76991dea427945b5bd Author: Paul Phillips Date: Sat Oct 6 10:20:45 2012 -0700 Fix for SI-6482, lost bounds in extension methods. That was a good one. How to create a new method with type parameters from multiple sources, herein. (cherry picked from commit ff9f60f420c090b6716c927ab0359b082f2299de) commit 8889c7a13f74bc175e48aa2209549089a974c2af Author: Paul Phillips Date: Fri Oct 5 22:19:52 2012 -0700 Responded to comment about how many isCoercibles there are. I make the case that there is only one. (cherry picked from commit 883f1ac88dd7cec5882d42d6b48d7f267d1f6e00) --- .../tools/nsc/transform/ExtensionMethods.scala | 189 +++++++++++++-------- .../scala/tools/nsc/typechecker/Infer.scala | 11 ++ .../scala/reflect/internal/Definitions.scala | 11 ++ test/files/pos/t6482.scala | 11 ++ 4 files changed, 150 insertions(+), 72 deletions(-) create mode 100644 test/files/pos/t6482.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index c7ca239fa9..bc54054028 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -91,39 +91,42 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { matching.head } + /** Recognize a MethodType which represents an extension method. + * + * It may have a curried parameter list with the `$this` alone in the first + * parameter list, in which case that parameter list is dropped. Or, since + * the curried lists disappear during uncurry, it may have a single parameter + * list with `$this` as the first parameter, in which case that parameter is + * removed from the list. + */ + object ExtensionMethodType { + def unapply(tp: Type) = tp match { + case MethodType(thiz :: rest, restpe) if thiz.name == nme.SELF => + Some((thiz, if (rest.isEmpty) restpe else MethodType(rest, restpe) )) + case _ => + None + } + } + /** This method removes the `$this` argument from the parameter list a method. * * A method may be a `PolyType`, in which case we tear out the `$this` and the class - * type params from its nested `MethodType`. - * It may be a `MethodType`, either with a curried parameter list in which the first argument - * is a `$this` - we just return the rest of the list. - * This means that the corresponding symbol was generated during `extmethods`. - * - * It may also be a `MethodType` in which the `$this` does not appear in a curried parameter list. - * The curried lists disappear during `uncurry`, and the methods may be duplicated afterwards, - * for instance, during `specialize`. - * In this case, the first argument is `$this` and we just get rid of it. + * type params from its nested `MethodType`. Or it may be a MethodType, as + * described at the ExtensionMethodType extractor. */ private def normalize(stpe: Type, clazz: Symbol): Type = stpe match { case PolyType(tparams, restpe) => - // Split the type parameters of the extension method into two groups, - // corresponding the to class and method type parameters. - val numClassParams = clazz.typeParams.length - val methTParams = tparams dropRight numClassParams - val classTParams = tparams takeRight numClassParams - - GenPolyType(methTParams, - normalize(restpe.substSym(classTParams, clazz.typeParams), clazz)) - case MethodType(List(thiz), restpe) if thiz.name == nme.SELF => - restpe.substituteTypes(thiz :: Nil, clazz.thisType :: Nil) - case MethodType(thiz :: params, restpe) => - MethodType(params, restpe) + // method type parameters, class type parameters + val (mtparams, ctparams) = tparams splitAt (tparams.length - clazz.typeParams.length) + GenPolyType(mtparams, + normalize(restpe.substSym(ctparams, clazz.typeParams), clazz)) + case ExtensionMethodType(thiz, etpe) => + etpe.substituteTypes(thiz :: Nil, clazz.thisType :: Nil) case _ => stpe } class Extender(unit: CompilationUnit) extends TypingTransformer(unit) { - private val extensionDefs = mutable.Map[Symbol, mutable.ListBuffer[Tree]]() def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: Symbol): Unit = @@ -134,31 +137,54 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { if (unboxed.isDerivedValueClass) checkNonCyclic(pos, seen + clazz, unboxed) } + /** We will need to clone the info of the original method (which obtains clones + * of the method type parameters), clone the type parameters of the value class, + * and create a new polymethod with the union of all those type parameters, with + * their infos adjusted to be consistent with their new home. Example: + * + * class Foo[+A <: AnyRef](val xs: List[A]) extends AnyVal { + * def baz[B >: A](x: B): List[B] = x :: xs + * // baz has to be transformed into this extension method, where + * // A is cloned from class Foo and B is cloned from method baz: + * // def extension$baz[B >: A <: Any, A >: Nothing <: AnyRef]($this: Foo[A])(x: B): List[B] + * } + * + * TODO: factor out the logic for consolidating type parameters from a class + * and a method for re-use elsewhere, because nobody will get this right without + * some higher level facilities. + */ def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = { - // No variance for method type parameters - var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) map (_ resetFlag COVARIANT | CONTRAVARIANT) - val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpeHK)) + val GenPolyType(tparamsFromMethod, methodResult) = origInfo cloneInfo extensionMeth + // Start with the class type parameters - clones will be method type parameters + // so must drop their variance. + val tparamsFromClass = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) map (_ resetFlag COVARIANT | CONTRAVARIANT) + + val thisParamType = appliedType(clazz, tparamsFromClass map (_.tpeHK): _*) val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType - def transform(clonedType: Type): Type = clonedType match { - case MethodType(params, restpe) => - // I assume it was a bug that this was dropping params... [Martin]: No, it wasn't; it's curried. - MethodType(List(thisParam), clonedType) - case NullaryMethodType(restpe) => - MethodType(List(thisParam), restpe) - } - val GenPolyType(tparams, restpe) = origInfo cloneInfo extensionMeth - val selfParamSingletonType = singleType(currentOwner.companionModule.thisType, thisParam) - GenPolyType( - tparams ::: newTypeParams, - transform(restpe) substThisAndSym (clazz, selfParamSingletonType, clazz.typeParams, newTypeParams) - ) - } + val resultType = MethodType(List(thisParam), dropNullaryMethod(methodResult)) + val selfParamType = singleType(currentOwner.companionModule.thisType, thisParam) - private def allParams(tpe: Type): List[Symbol] = tpe match { - case MethodType(params, res) => params ::: allParams(res) - case _ => List() - } + def fixres(tp: Type) = tp substThisAndSym (clazz, selfParamType, clazz.typeParams, tparamsFromClass) + def fixtparam(tp: Type) = tp substSym (clazz.typeParams, tparamsFromClass) + + // We can't substitute symbols on the entire polytype because we + // need to modify the bounds of the cloned type parameters, but we + // don't want to substitute for the cloned type parameters themselves. + val tparams = tparamsFromMethod ::: tparamsFromClass + GenPolyType(tparams map (_ modifyInfo fixtparam), fixres(resultType)) + // For reference, calling fix on the GenPolyType plays out like this: + // error: scala.reflect.internal.Types$TypeError: type arguments [B#7344,A#6966] + // do not conform to method extension$baz#16148's type parameter bounds + // + // And the difference is visible here. See how B is bounded from below by A#16149 + // in both cases, but in the failing case, the other type parameter has turned into + // a different A. (What is that A? It is a clone of the original A created in + // SubstMap during the call to substSym, but I am not clear on all the particulars.) + // + // bad: [B#16154 >: A#16149, A#16155 <: AnyRef#2189]($this#16156: Foo#6965[A#16155])(x#16157: B#16154)List#2457[B#16154] + // good: [B#16151 >: A#16149, A#16149 <: AnyRef#2189]($this#16150: Foo#6965[A#16149])(x#16153: B#16151)List#2457[B#16151] + } override def transform(tree: Tree): Tree = { tree match { case Template(_, _, _) => @@ -173,37 +199,56 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { super.transform(tree) } else tree case DefDef(_, _, tparams, vparamss, _, rhs) if tree.symbol.isMethodWithExtension => - val companion = currentOwner.companionModule - val origMeth = tree.symbol - val extensionName = extensionNames(origMeth).head - val extensionMeth = companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) - .setAnnotations(origMeth.annotations) - companion.info.decls.enter(extensionMeth) - val newInfo = extensionMethInfo(extensionMeth, origMeth.info, currentOwner) + val origMeth = tree.symbol + val origThis = currentOwner + val origTpeParams = tparams.map(_.symbol) ::: origThis.typeParams // method type params ++ class type params + val origParams = vparamss.flatten map (_.symbol) + val companion = origThis.companionModule + + def makeExtensionMethodSymbol = { + val extensionName = extensionNames(origMeth).head + val extensionMeth = ( + companion.moduleClass.newMethod(extensionName, origMeth.pos, origMeth.flags & ~OVERRIDE & ~PROTECTED | FINAL) + setAnnotations origMeth.annotations + ) + companion.info.decls.enter(extensionMeth) + } + + val extensionMeth = makeExtensionMethodSymbol + val newInfo = extensionMethInfo(extensionMeth, origMeth.info, origThis) extensionMeth setInfo newInfo - log("Value class %s spawns extension method.\n Old: %s\n New: %s".format( - currentOwner, - origMeth.defString, - extensionMeth.defString)) // extensionMeth.defStringSeenAs(origInfo - - def thisParamRef = gen.mkAttributedStableRef(extensionMeth.info.params.head setPos extensionMeth.pos) - val GenPolyType(extensionTpeParams, extensionMono) = extensionMeth.info - val origTpeParams = (tparams map (_.symbol)) ::: currentOwner.typeParams - val extensionBody = rhs + + log(s"Value class $origThis spawns extension method.\n Old: ${origMeth.defString}\n New: ${extensionMeth.defString}") + + val GenPolyType(extensionTpeParams, MethodType(thiz :: Nil, extensionMono)) = newInfo + val extensionParams = allParameters(extensionMono) + val extensionThis = gen.mkAttributedStableRef(thiz setPos extensionMeth.pos) + + val extensionBody = ( + rhs .substituteSymbols(origTpeParams, extensionTpeParams) - .substituteSymbols(vparamss.flatten map (_.symbol), allParams(extensionMono).tail) - .substituteThis(currentOwner, thisParamRef) - .changeOwner((origMeth, extensionMeth)) - extensionDefs(companion) += atPos(tree.pos) { DefDef(extensionMeth, extensionBody) } - val extensionCallPrefix = Apply( - gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpeHK)), - List(This(currentOwner))) - val extensionCall = atOwner(origMeth) { - localTyper.typedPos(rhs.pos) { - gen.mkForwarder(extensionCallPrefix, mmap(vparamss)(_.symbol)) - } - } - deriveDefDef(tree)(_ => extensionCall) + .substituteSymbols(origParams, extensionParams) + .substituteThis(origThis, extensionThis) + .changeOwner(origMeth -> extensionMeth) + ) + + // Record the extension method ( FIXME: because... ? ) + extensionDefs(companion) += atPos(tree.pos)(DefDef(extensionMeth, extensionBody)) + + // These three lines are assembling Foo.bar$extension[T1, T2, ...]($this) + // which leaves the actual argument application for extensionCall. + val sel = Select(gen.mkAttributedRef(companion), extensionMeth) + val targs = origTpeParams map (_.tpeHK) + val callPrefix = gen.mkMethodCall(sel, targs, This(origThis) :: Nil) + + // Apply all the argument lists. + deriveDefDef(tree)(_ => + atOwner(origMeth)( + localTyper.typedPos(rhs.pos)( + gen.mkForwarder(callPrefix, mmap(vparamss)(_.symbol)) + ) + ) + ) case _ => super.transform(tree) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 581f9f3bfa..7dddd320ef 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -411,8 +411,19 @@ trait Infer extends Checkable { /** Like weakly compatible but don't apply any implicit conversions yet. * Used when comparing the result type of a method with its prototype. + * * [Martin] I think Infer is also created by Erasure, with the default * implementation of isCoercible + * [Paulp] (Assuming the above must refer to my comment on isCoercible) + * Nope, I examined every occurrence of Inferencer in trunk. It + * appears twice as a self-type, once at its definition, and once + * where it is instantiated in Typers. There are no others. + * + % ack -A0 -B0 --no-filename '\bInferencer\b' src + self: Inferencer => + self: Inferencer => + class Inferencer(context: Context) extends InferencerContextErrors with InferCheckable { + val infer = new Inferencer(context0) { */ def isConservativelyCompatible(tp: Type, pt: Type): Boolean = context.withImplicitsDisabled(isWeaklyCompatible(tp, pt)) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 4269b65297..8f451c207a 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -671,6 +671,11 @@ trait Definitions extends api.StandardDefinitions { case _ => Nil } + def dropNullaryMethod(tp: Type) = tp match { + case NullaryMethodType(restpe) => restpe + case _ => tp + } + def unapplyUnwrap(tpe:Type) = tpe.finalResultType.normalize match { case RefinedType(p :: _, _) => p.normalize case tp => tp @@ -864,6 +869,12 @@ trait Definitions extends api.StandardDefinitions { removeRedundantObjects(parents) } + /** Flatten curried parameter lists of a method type. */ + def allParameters(tpe: Type): List[Symbol] = tpe match { + case MethodType(params, res) => params ::: allParameters(res) + case _ => Nil + } + def typeStringNoPackage(tp: Type) = "" + tp stripPrefix tp.typeSymbol.enclosingPackage.fullName + "." diff --git a/test/files/pos/t6482.scala b/test/files/pos/t6482.scala new file mode 100644 index 0000000000..24ea38e519 --- /dev/null +++ b/test/files/pos/t6482.scala @@ -0,0 +1,11 @@ +final class TraversableOnceOps[+A](val collection: TraversableOnce[A]) extends AnyVal { + def reduceLeftOption[B >: A](op: (B, A) => B): Option[B] = + if (collection.isEmpty) None else Some(collection.reduceLeft[B](op)) +} +// error: type arguments [B] do not conform to method reduceLeft's type parameter bounds [B >: A] +// if (collection.isEmpty) None else Some(collection.reduceLeft[B](op)) +// ^ + +class Foo[+A <: AnyRef](val xs: List[A]) extends AnyVal { + def baz[B >: A](x: B): List[B] = x :: xs +} -- cgit v1.2.3 From 374c912a1440193b06fc6fd74b39063949b2c086 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 26 Jan 2013 10:43:48 +0100 Subject: SI-7022 Additional test case for value class w. bounds As reported against 2.10.0, and as fixed by SI-6482, which was backported in the previous commit. --- test/files/pos/t7022.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 test/files/pos/t7022.scala (limited to 'test') diff --git a/test/files/pos/t7022.scala b/test/files/pos/t7022.scala new file mode 100644 index 0000000000..0609e2d250 --- /dev/null +++ b/test/files/pos/t7022.scala @@ -0,0 +1,9 @@ +class Catch[+T] { + def either[U >: T](body: => U): Either[Throwable, U] = ??? +} + +object Test { + implicit class RichCatch[T](val c: Catch[T]) extends AnyVal { + def validation[U >: T](u: => U): Either[Throwable, U] = c.either(u) + } +} -- cgit v1.2.3 From 3af838c55674c0d03e8849d8c756d6ad56c537db Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 28 Jan 2013 14:10:48 +0100 Subject: SI-7033 Be symful when creating factory methods. Implicit class factory methods were synthesizing the reference to the class as `Ident(classDef.name)`, which was unhygienic in case of `implicit class X[X]`. To use symbols without causing a cycle, I switched from `REF(symbol)` to `Ident(symbol)`. The former calls into: at scala.reflect.internal.TreeGen.mkAttributedSelect(TreeGen.scala:184) at scala.reflect.internal.TreeGen.mkAttributedRef(TreeGen.scala:124) at scala.reflect.internal.TreeGen.mkAttributedRef(TreeGen.scala:130) at scala.tools.nsc.ast.TreeDSL$CODE$.REF(TreeDSL.scala:307) which forces the info of enclosing module and forms a cycle. --- .../scala/tools/nsc/typechecker/MethodSynthesis.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/Unapplies.scala | 11 ++++++----- test/files/pos/t7033.scala | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 test/files/pos/t7033.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index acc4f7ff67..b1cf93a879 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -389,7 +389,7 @@ trait MethodSynthesis { result } def derivedTree: DefDef = - factoryMeth(mods & flagsMask | flagsExtra, name, tree, symbolic = false) + factoryMeth(mods & flagsMask | flagsExtra, name, tree) def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC def flagsMask: Long = AccessFlags def name: TermName = tree.name.toTermName diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 5782d7bbca..3e4e0f49d7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -79,8 +79,9 @@ trait Unapplies extends ast.TreeDSL private def toIdent(x: DefTree) = Ident(x.name) setPos x.pos.focus - private def classType(cdef: ClassDef, tparams: List[TypeDef], symbolic: Boolean = true): Tree = { - val tycon = if (symbolic) REF(cdef.symbol) else Ident(cdef.name) + private def classType(cdef: ClassDef, tparams: List[TypeDef]): Tree = { + // SI-7033 Unattributed to avoid forcing `cdef.symbol.info`. + val tycon = Ident(cdef.symbol) if (tparams.isEmpty) tycon else AppliedTypeTree(tycon, tparams map toIdent) } @@ -133,10 +134,10 @@ trait Unapplies extends ast.TreeDSL /** The apply method corresponding to a case class */ - def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef, symbolic: Boolean): DefDef = { + def factoryMeth(mods: Modifiers, name: TermName, cdef: ClassDef): DefDef = { val tparams = cdef.tparams map copyUntypedInvariant val cparamss = constrParamss(cdef) - def classtpe = classType(cdef, tparams, symbolic) + def classtpe = classType(cdef, tparams) atPos(cdef.pos.focus)( DefDef(mods, name, tparams, cparamss, classtpe, New(classtpe, mmap(cparamss)(gen.paramToArg))) @@ -145,7 +146,7 @@ trait Unapplies extends ast.TreeDSL /** The apply method corresponding to a case class */ - def caseModuleApplyMeth(cdef: ClassDef): DefDef = factoryMeth(caseMods, nme.apply, cdef, symbolic = true) + def caseModuleApplyMeth(cdef: ClassDef): DefDef = factoryMeth(caseMods, nme.apply, cdef) /** The unapply method corresponding to a case class */ diff --git a/test/files/pos/t7033.scala b/test/files/pos/t7033.scala new file mode 100644 index 0000000000..a4d256673b --- /dev/null +++ b/test/files/pos/t7033.scala @@ -0,0 +1,15 @@ +import language.higherKinds +object Wrap { + implicit class X[X](val a: X) + + X[Int](0) +} + +class Wrap { + implicit class Y[Y](val a: Y) + Y[Int](0) + implicit class Z[Z[_]](val a: Z[Wrap.this.Z[Z]]) + Z[List](List(new Z[List](null))) +} + +case class X[X](val a: X) -- 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 (limited to 'test') 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 (limited to 'test') 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 (limited to 'test') 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(-) (limited to 'test') 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 c7d489e21f234bf1a2ea04c6b68990c53b5b387d Mon Sep 17 00:00:00 2001 From: James Iry Date: Sat, 2 Feb 2013 09:27:21 -0800 Subject: SI-5313 Test clobbers on the back edge of a loop I realized I was missing a test case for a local store early in a loop that was unused but turned out to be a clobber of a store later in the loop. --- test/files/run/t5313.check | 2 ++ test/files/run/t5313.scala | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/test/files/run/t5313.check b/test/files/run/t5313.check index aa30c0efa5..7a48b2b711 100644 --- a/test/files/run/t5313.check +++ b/test/files/run/t5313.check @@ -8,3 +8,5 @@ STORE_LOCAL(variable kept4) STORE_LOCAL(variable kept4) STORE_LOCAL(variable kept5) STORE_LOCAL(variable kept5) +STORE_LOCAL(variable kept6) +STORE_LOCAL(variable kept6) diff --git a/test/files/run/t5313.scala b/test/files/run/t5313.scala index 64009e29af..7da8726a1f 100644 --- a/test/files/run/t5313.scala +++ b/test/files/run/t5313.scala @@ -7,7 +7,7 @@ object Test extends IcodeTest { override def code = """class Foo { - def foo = true + def randomBoolean = util.Random.nextInt % 2 == 0 def bar = { var kept1 = new Object val result = new java.lang.ref.WeakReference(kept1) @@ -19,7 +19,7 @@ object Test extends IcodeTest { var erased4 = erased2 // and this val erased5 = erased4 // and this var kept2: Object = new Object // ultimately can't be eliminated - while(foo) { + while(randomBoolean) { val kept3 = kept2 kept2 = null // this can't, because it clobbers kept2, which is used erased4 = null // safe to eliminate @@ -36,6 +36,12 @@ object Test extends IcodeTest { kept5 = null // can't eliminate it's a clobber and it's used print(kept5) kept5 = null // can eliminate because we don't care about clobbers of nulls + while(randomBoolean) { + var kept6: AnyRef = null // not used, but have to keep because it clobbers the next used store + // on the back edge of the loop + kept6 = new Object // used + println(kept6) + } result } }""".stripMargin -- cgit v1.2.3 From 3f0bce95fffcea60b3cbf8bc9e0c9a410f732cde Mon Sep 17 00:00:00 2001 From: Kato Kazuyoshi Date: Tue, 8 Jan 2013 01:22:34 +0900 Subject: SI-6017 Generate Scaladoc's index links in Scala side There is no reason to do it in JavaScript. --- .../scala/tools/nsc/doc/html/page/Index.scala | 14 +++++++++++++- .../scala/tools/nsc/doc/html/resource/lib/index.js | 19 +------------------ test/scaladoc/run/SI-6017.scala | 3 +++ 3 files changed, 17 insertions(+), 19 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala index 86407fb9a3..2f83741d4e 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala @@ -48,10 +48,22 @@ class Index(universe: doc.Universe, val index: doc.Index) extends HtmlPage { + def letters: NodeSeq = { + val xs = index.firstLetterIndex.keys.toSeq + xs.sorted map { + c => { + if (c == '_') '#' else c.toUpper + } + } + } + def browser =
-
+
+
+
{ letters }
+
{ def packageElem(pack: model.Package): NodeSeq = { diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js index 1323a06c01..70073b272a 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.js @@ -335,8 +335,7 @@ function keyboardScrolldownLeftPane() { /* Configures the text filter */ function configureTextFilter() { scheduler.add("init", function() { - $("#filter").append("
"); - printAlphabet(); + $("#textfilter").append(""); var input = $("#textfilter input"); resizeFilterBlock(); input.bind('keyup', function(event) { @@ -532,19 +531,3 @@ function kindFilterSync() { function resizeFilterBlock() { $("#tpl").css("top", $("#filter").outerHeight(true)); } - -function printAlphabet() { - var html = '#'; - var c; - for (c = 'a'; c <= 'z'; c = String.fromCharCode(c.charCodeAt(0) + 1)) { - html += [ - '', - c.toUpperCase(), - '' - ].join(''); - } - $("#filter").append('
' + html + '
'); -} - diff --git a/test/scaladoc/run/SI-6017.scala b/test/scaladoc/run/SI-6017.scala index a4950e48d8..e729dca67d 100644 --- a/test/scaladoc/run/SI-6017.scala +++ b/test/scaladoc/run/SI-6017.scala @@ -16,6 +16,9 @@ object Test extends ScaladocModelTest { val index = IndexModelFactory.makeIndex(universe) // Because "STAR" and "Star" are different assert(index.firstLetterIndex('s').keys.toSeq.length == 2) + + val indexPage = new Index(universe, index) + assert(indexPage.letters.length == 1) } case _ => assert(false) } -- cgit v1.2.3 From 0e8d8c735e9a4590d3f74b549b8453f46335709a Mon Sep 17 00:00:00 2001 From: Kato Kazuyoshi Date: Fri, 1 Feb 2013 22:53:45 +0900 Subject: SI-6017 Scaladoc: Show all letters without dangling links Use instead of if there is no page on the letter. --- .../scala/tools/nsc/doc/html/page/Index.scala | 20 +++++++++++++------- .../scala/tools/nsc/doc/html/resource/lib/index.css | 6 +++++- test/scaladoc/run/SI-6017.scala | 4 +++- 3 files changed, 21 insertions(+), 9 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala index 2f83741d4e..c76bdc58d9 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala @@ -48,14 +48,20 @@ class Index(universe: doc.Universe, val index: doc.Index) extends HtmlPage {
- def letters: NodeSeq = { - val xs = index.firstLetterIndex.keys.toSeq - xs.sorted map { - c => { - if (c == '_') '#' else c.toUpper - } + def letters: NodeSeq = + '_' +: ('a' to 'z') map { + char => { + val label = if (char == '_') '#' else char.toUpper + + index.firstLetterIndex.get(char) match { + case Some(_) => + { + label + } + case None => { label } + } + } } - } def browser =
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css index 2a8f9b570a..55fb370a41 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/index.css @@ -206,7 +206,7 @@ h1 { border-right:0; } -#letters > a { +#letters > a, #letters > span { /* font-family: monospace;*/ color: #858484; font-weight: bold; @@ -214,6 +214,10 @@ h1 { text-shadow: #ffffff 0 1px 0; padding-right: 2px; } + +#letters > span { + color: #bbb; +} #tpl { display: block; diff --git a/test/scaladoc/run/SI-6017.scala b/test/scaladoc/run/SI-6017.scala index e729dca67d..9951534c6d 100644 --- a/test/scaladoc/run/SI-6017.scala +++ b/test/scaladoc/run/SI-6017.scala @@ -18,7 +18,9 @@ object Test extends ScaladocModelTest { assert(index.firstLetterIndex('s').keys.toSeq.length == 2) val indexPage = new Index(universe, index) - assert(indexPage.letters.length == 1) + val letters = indexPage.letters + assert(letters.length > 1) + assert(letters(0).toString == "#") } case _ => assert(false) } -- 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 (limited to 'test') 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 From b67f8e57f4381024c0fba109ebe11d4006472d83 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Feb 2013 19:53:32 +0100 Subject: [nomerge] SI-6667 Demote a new ambiguity error to a lint warning. In the interests of not breaking source compability. A few projects are relying on this bug. Should not be merged to master. --- src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 7 ++++++- test/files/neg/t6667.check | 3 ++- test/files/neg/t6667.flags | 1 + test/files/neg/t6667b.check | 3 ++- test/files/neg/t6667b.flags | 1 + 5 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 test/files/neg/t6667.flags create mode 100644 test/files/neg/t6667b.flags (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index ec3a0a0ef7..d1cf9b1904 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1319,12 +1319,17 @@ trait Implicits { // `materializeImplicit` does some preprocessing for `pt` // is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`? - if (result.isFailure && !wasAmbigious) result = searchImplicit(implicitsOfExpectedType, false) + if (result.isFailure) result = searchImplicit(implicitsOfExpectedType, false) if (result.isFailure) { context.updateBuffer(previousErrs) if (Statistics.canEnable) Statistics.stopTimer(oftypeFailNanos, failstart) } else { + if (wasAmbigious && settings.lint.value) + reporter.warning(tree.pos, + "Search of in-scope implicits was ambiguous, and the implicit scope was searched. In Scala 2.11.0, this code will not compile. See SI-6667. \n" + + previousErrs.map(_.errMsg).mkString("\n")) + if (Statistics.canEnable) Statistics.stopTimer(oftypeSucceedNanos, succstart) if (Statistics.canEnable) Statistics.incCounter(oftypeImplicitHits) } diff --git a/test/files/neg/t6667.check b/test/files/neg/t6667.check index 43313fa4fe..b04251d7c1 100644 --- a/test/files/neg/t6667.check +++ b/test/files/neg/t6667.check @@ -1,4 +1,5 @@ -t6667.scala:8: error: ambiguous implicit values: +t6667.scala:8: error: Search of in-scope implicits was ambiguous, and the implicit scope was searched. In Scala 2.11.0, this code will not compile. See SI-6667. +ambiguous implicit values: both value inScope1 in object Test of type => C and value inScope2 in object Test of type => C match expected type C diff --git a/test/files/neg/t6667.flags b/test/files/neg/t6667.flags new file mode 100644 index 0000000000..6c1dd108ae --- /dev/null +++ b/test/files/neg/t6667.flags @@ -0,0 +1 @@ +-Xfatal-warnings -Xlint \ No newline at end of file diff --git a/test/files/neg/t6667b.check b/test/files/neg/t6667b.check index 99cea9a47c..5d56e776c3 100644 --- a/test/files/neg/t6667b.check +++ b/test/files/neg/t6667b.check @@ -4,7 +4,8 @@ t6667b.scala:16: error: ambiguous implicit values: match expected type Test.Box new Test() ^ -t6667b.scala:19: error: ambiguous implicit values: +t6667b.scala:19: error: Search of in-scope implicits was ambiguous, and the implicit scope was searched. In Scala 2.11.0, this code will not compile. See SI-6667. +ambiguous implicit values: both value a in object Test of type => Test.Box and value b of type Test.Box match expected type Test.Box diff --git a/test/files/neg/t6667b.flags b/test/files/neg/t6667b.flags new file mode 100644 index 0000000000..6c1dd108ae --- /dev/null +++ b/test/files/neg/t6667b.flags @@ -0,0 +1 @@ +-Xfatal-warnings -Xlint \ No newline at end of file -- cgit v1.2.3 From 02dd4c974f33d137ea353a72e27efb70928fb378 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 4 Feb 2013 22:23:54 +0100 Subject: reflecting @throws defined in Scala code As per Jason's comment: How are Scala classes containing @throws annots treated? I can't figure out whether we pickle the annotation in addition to adding the exception to the signature. If we do, might we end up with duplicate annotations in runtime reflection? This warrants a test. See the context of the discussion here: https://github.com/scala/scala/pull/2040/files#r2874769. No, we won't end up with duplicates, because classes defined in Scala are loaded in a different completer. But I'll add a test - you can never have too many of those. --- test/files/run/t7008-scala-defined.check | 7 +++++++ test/files/run/t7008-scala-defined/Impls_Macros_2.scala | 12 ++++++++++++ .../ScalaClassWithCheckedExceptions_1.scala | 6 ++++++ test/files/run/t7008-scala-defined/Test_3.scala | 9 +++++++++ 4 files changed, 34 insertions(+) create mode 100644 test/files/run/t7008-scala-defined.check create mode 100644 test/files/run/t7008-scala-defined/Impls_Macros_2.scala create mode 100644 test/files/run/t7008-scala-defined/ScalaClassWithCheckedExceptions_1.scala create mode 100644 test/files/run/t7008-scala-defined/Test_3.scala (limited to 'test') diff --git a/test/files/run/t7008-scala-defined.check b/test/files/run/t7008-scala-defined.check new file mode 100644 index 0000000000..84ed62619e --- /dev/null +++ b/test/files/run/t7008-scala-defined.check @@ -0,0 +1,7 @@ +: List(throws[NullPointerException]("")) +bar: List(throws[E1]("")) +baz: List(throws[IllegalStateException]("")) +============= +: List(throws[NullPointerException]("")) +bar: List(throws[E1]("")) +baz: List(throws[IllegalStateException]("")) diff --git a/test/files/run/t7008-scala-defined/Impls_Macros_2.scala b/test/files/run/t7008-scala-defined/Impls_Macros_2.scala new file mode 100644 index 0000000000..94fd99018e --- /dev/null +++ b/test/files/run/t7008-scala-defined/Impls_Macros_2.scala @@ -0,0 +1,12 @@ +import language.experimental.macros +import scala.reflect.macros.Context + +object Macros { + def impl(c: Context) = { + val decls = c.typeOf[ScalaClassWithCheckedExceptions_1[_]].declarations.toList + val s = decls.sortBy(_.name.toString).map(decl => (s"${decl.name}: ${decl.annotations}")).mkString(scala.compat.Platform.EOL) + c.universe.reify(println(c.literal(s).splice)) + } + + def foo = macro impl +} \ No newline at end of file diff --git a/test/files/run/t7008-scala-defined/ScalaClassWithCheckedExceptions_1.scala b/test/files/run/t7008-scala-defined/ScalaClassWithCheckedExceptions_1.scala new file mode 100644 index 0000000000..7783c873ec --- /dev/null +++ b/test/files/run/t7008-scala-defined/ScalaClassWithCheckedExceptions_1.scala @@ -0,0 +1,6 @@ +class ScalaClassWithCheckedExceptions_1[E1 <: Exception] @throws[NullPointerException]("") () { + @throws[E1]("") def bar() {} + @throws[IllegalStateException]("") def baz(x: Int) {} + // FIXME: SI-7066 + // @throws[E2]("") def foo[E2 <: Exception] {} +} \ No newline at end of file diff --git a/test/files/run/t7008-scala-defined/Test_3.scala b/test/files/run/t7008-scala-defined/Test_3.scala new file mode 100644 index 0000000000..03bb79d311 --- /dev/null +++ b/test/files/run/t7008-scala-defined/Test_3.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + Macros.foo + println("=============") + + val decls = typeOf[ScalaClassWithCheckedExceptions_1[_]].declarations.toList + decls sortBy (_.name.toString) foreach (decl => println(s"${decl.name}: ${decl.annotations}")) +} \ No newline at end of file -- cgit v1.2.3 From 02ed5fb3bc8d792872223ceecfd8fd0ae089d923 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 31 Jan 2013 11:49:19 +0100 Subject: SI-6989 privateWithin is now populated in reflect Runtime reflection in JavaMirrors previously forgot to fill in privateWithin when importing Java reflection artifacts. Now this is fixed. --- .../nsc/symtab/classfile/ClassfileParser.scala | 21 ++++--------------- .../scala/reflect/internal/PrivateWithin.scala | 23 +++++++++++++++++++++ .../scala/reflect/internal/SymbolTable.scala | 1 + .../scala/reflect/runtime/JavaMirrors.scala | 5 +++++ test/files/run/t6989.check | 24 ++++++++++++++++++++++ test/files/run/t6989/JavaClass_1.java | 7 +++++++ test/files/run/t6989/Test_2.scala | 23 +++++++++++++++++++++ 7 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 src/reflect/scala/reflect/internal/PrivateWithin.scala create mode 100644 test/files/run/t6989.check create mode 100644 test/files/run/t6989/JavaClass_1.java create mode 100644 test/files/run/t6989/Test_2.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index a708a262e7..19b32dd5fb 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -548,8 +548,8 @@ abstract class ClassfileParser { skipMembers() // methods if (!isScala) { clazz setFlag sflags - setPrivateWithin(clazz, jflags) - setPrivateWithin(staticModule, jflags) + importPrivateWithinFromJavaFlags(clazz, jflags) + importPrivateWithinFromJavaFlags(staticModule, jflags) clazz.setInfo(classInfo) moduleClass setInfo staticInfo staticModule.setInfo(moduleClass.tpe) @@ -612,7 +612,7 @@ abstract class ClassfileParser { if (isEnum) ConstantType(Constant(sym)) else info } - setPrivateWithin(sym, jflags) + importPrivateWithinFromJavaFlags(sym, jflags) parseAttributes(sym, info) getScope(jflags).enter(sym) @@ -663,7 +663,7 @@ abstract class ClassfileParser { info = MethodType(newParams, clazz.tpe) } sym.setInfo(info) - setPrivateWithin(sym, jflags) + importPrivateWithinFromJavaFlags(sym, jflags) parseAttributes(sym, info) if ((jflags & JAVA_ACC_VARARGS) != 0) { sym.setInfo(arrayToRepeated(sym.info)) @@ -1263,19 +1263,6 @@ abstract class ClassfileParser { protected def getScope(flags: Int): Scope = if (isStatic(flags)) staticScope else instanceScope - private def setPrivateWithin(sym: Symbol, jflags: Int) { - if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0) - // See ticket #1687 for an example of when topLevelClass is NoSymbol: it - // apparently occurs when processing v45.3 bytecode. - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - - // protected in java means package protected. #3946 - if ((jflags & JAVA_ACC_PROTECTED) != 0) - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - } - private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0 private def isStatic(flags: Int) = (flags & JAVA_ACC_STATIC) != 0 private def hasAnnotation(flags: Int) = (flags & JAVA_ACC_ANNOTATION) != 0 diff --git a/src/reflect/scala/reflect/internal/PrivateWithin.scala b/src/reflect/scala/reflect/internal/PrivateWithin.scala new file mode 100644 index 0000000000..9b99b94b41 --- /dev/null +++ b/src/reflect/scala/reflect/internal/PrivateWithin.scala @@ -0,0 +1,23 @@ +package scala.reflect +package internal + +import ClassfileConstants._ + +trait PrivateWithin { + self: SymbolTable => + + def importPrivateWithinFromJavaFlags(sym: Symbol, jflags: Int): Symbol = { + if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0) + // See ticket #1687 for an example of when topLevelClass is NoSymbol: it + // apparently occurs when processing v45.3 bytecode. + if (sym.enclosingTopLevelClass != NoSymbol) + sym.privateWithin = sym.enclosingTopLevelClass.owner + + // protected in java means package protected. #3946 + if ((jflags & JAVA_ACC_PROTECTED) != 0) + if (sym.enclosingTopLevelClass != NoSymbol) + sym.privateWithin = sym.enclosingTopLevelClass.owner + + sym + } +} \ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 02ac59a461..f75855f1ec 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -38,6 +38,7 @@ abstract class SymbolTable extends macros.Universe with StdAttachments with StdCreators with BuildUtils + with PrivateWithin { val gen = new TreeGen { val global: SymbolTable.this.type = SymbolTable.this } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 01e0634902..70d506ad95 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -631,6 +631,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni /** used to avoid cycles while initializing classes */ private var parentsLevel = 0 private var pendingLoadActions: List[() => Unit] = Nil + private val relatedSymbols = clazz +: (if (module != NoSymbol) List(module, module.moduleClass) else Nil) override def load(sym: Symbol): Unit = { debugInfo("completing from Java " + sym + "/" + clazz.fullName)//debug @@ -642,6 +643,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni module.moduleClass setFlag (flags & PRIVATE | JAVA) } + relatedSymbols foreach (importPrivateWithinFromJavaFlags(_, jclazz.getModifiers)) copyAnnotations(clazz, jclazz) // to do: annotations to set also for module? @@ -1060,6 +1062,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni .newValue(newTermName(jfield.getName), NoPosition, toScalaFieldFlags(jfield.getModifiers)) .setInfo(typeToScala(jfield.getGenericType)) fieldCache enter (jfield, field) + importPrivateWithinFromJavaFlags(field, jfield.getModifiers) copyAnnotations(field, jfield) field } @@ -1085,6 +1088,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni val paramtpes = jmeth.getGenericParameterTypes.toList map typeToScala val resulttpe = typeToScala(jmeth.getGenericReturnType) setMethType(meth, tparams, paramtpes, resulttpe) + importPrivateWithinFromJavaFlags(meth, jmeth.getModifiers) copyAnnotations(meth, jmeth) if ((jmeth.getModifiers & JAVA_ACC_VARARGS) != 0) meth.setInfo(arrayToRepeated(meth.info)) meth @@ -1108,6 +1112,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala setMethType(constr, tparams, paramtpes, clazz.tpe) constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe)) + importPrivateWithinFromJavaFlags(constr, jconstr.getModifiers) copyAnnotations(constr, jconstr) constr } diff --git a/test/files/run/t6989.check b/test/files/run/t6989.check new file mode 100644 index 0000000000..06bc3b7200 --- /dev/null +++ b/test/files/run/t6989.check @@ -0,0 +1,24 @@ +============ +class JavaClass_1 +isPrivate = false +isProtected = false +isPublic = false +privateWithin = package foo +============ +variable x +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +variable y +isPrivate = false +isProtected = true +isPublic = false +privateWithin = package foo +============ +variable z +isPrivate = false +isProtected = false +isPublic = true +privateWithin = diff --git a/test/files/run/t6989/JavaClass_1.java b/test/files/run/t6989/JavaClass_1.java new file mode 100644 index 0000000000..61253dfe6f --- /dev/null +++ b/test/files/run/t6989/JavaClass_1.java @@ -0,0 +1,7 @@ +package foo; + +class JavaClass_1 { + private int x = 0; + protected String y = ""; + public float z = 0.0f; +} \ No newline at end of file diff --git a/test/files/run/t6989/Test_2.scala b/test/files/run/t6989/Test_2.scala new file mode 100644 index 0000000000..ba88e5cce3 --- /dev/null +++ b/test/files/run/t6989/Test_2.scala @@ -0,0 +1,23 @@ +import scala.reflect.runtime.universe._ + +package object foo { + def test(sym: Symbol) = { + println("============") + println(sym) + println(s"isPrivate = ${sym.isPrivate}") + println(s"isProtected = ${sym.isProtected}") + println(s"isPublic = ${sym.isPublic}") + println(s"privateWithin = ${sym.privateWithin}") + } + + def testAll() = { + test(typeOf[foo.JavaClass_1].typeSymbol) + test(typeOf[foo.JavaClass_1].declaration(newTermName("x"))) + test(typeOf[foo.JavaClass_1].declaration(newTermName("y"))) + test(typeOf[foo.JavaClass_1].declaration(newTermName("z"))) + } +} + +object Test extends App { + foo.testAll() +} \ No newline at end of file -- cgit v1.2.3 From b579a42ee3c079e9acf53b74085ae01ac6c35f0f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 5 Feb 2013 11:31:35 +0100 Subject: SI-6888 Loosen criteria for $outer search. In order to cater for nested classes with names that end with '$', which lead to ambiguity when unmangling expanded names. In: class X { object $ } We end up with: orginalName(X$$$$$outer) = $$$outer This change modifies `outerSource` to consider that to be and outer accessor name. It is a piecemeal fix, and no doubt there are other nasty surprises in store for those inclined to flash their $$$ in identifier names, but the method changed is not used widely and this solves the reported problem. SI-2806 remains open to address the deeper problem. --- src/reflect/scala/reflect/internal/Symbols.scala | 4 +++- test/files/run/t6888.check | 3 +++ test/files/run/t6888.scala | 19 +++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t6888.check create mode 100644 test/files/run/t6888.scala (limited to 'test') diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 72ad84edec..4ffd198dc4 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2515,7 +2515,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => } override def outerSource: Symbol = - if (originalName == nme.OUTER) initialize.referenced + // SI-6888 Approximate the name to workaround the deficiencies in `nme.originalName` + // in the face of clases named '$'. SI-2806 remains open to address the deeper problem. + if (originalName endsWith (nme.OUTER)) initialize.referenced else NoSymbol def setModuleClass(clazz: Symbol): TermSymbol = { diff --git a/test/files/run/t6888.check b/test/files/run/t6888.check new file mode 100644 index 0000000000..4e8a2de2db --- /dev/null +++ b/test/files/run/t6888.check @@ -0,0 +1,3 @@ +2 +3 +3 diff --git a/test/files/run/t6888.scala b/test/files/run/t6888.scala new file mode 100644 index 0000000000..0c64cbe5b6 --- /dev/null +++ b/test/files/run/t6888.scala @@ -0,0 +1,19 @@ +class C { + val x = 1 + object $ { + val y = x + x + class abc$ { + def xy = x + y + } + object abc$ { + def xy = x + y + } + } +} + +object Test extends App { + val c = new C() + println(c.$.y) + println(c.$.abc$.xy) + println(new c.$.abc$().xy) +} -- cgit v1.2.3 From e5c0e59373897d9f9dc6c49f8458666e88419586 Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Sun, 3 Feb 2013 23:47:44 +0100 Subject: SI-7060 More conservative dead code elim marking In dead code elimination, a DROP instruction that gets marked as useful and can be reached via several paths needs to also mark all the reaching definitions as useful, else we'll get unbalanced stacks on the two paths. A simplistic example: ``` BB1: CALL X // useful, leaves a LONG on the stack JUMP BB3 BB2: LOAD_FIELD Y // not useful JUMP BB3 BB3: DROP LONG // useful because "CALL X" is useful // but unless we mark "LOAD_FIELD Y" as useful too // we'll get unbalanced stacks when reaching BB3 ``` This patch addresses the unbalanced stack problem by adding all the reaching definitions of a useful DROP as useful instructions too. --- .../nsc/backend/opt/DeadCodeElimination.scala | 98 +++++++++++++++------- test/files/pos/SI-7060.flags | 1 + test/files/pos/SI-7060.scala | 11 +++ 3 files changed, 79 insertions(+), 31 deletions(-) create mode 100644 test/files/pos/SI-7060.flags create mode 100644 test/files/pos/SI-7060.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index d4a6d18c60..1beed3f420 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -20,7 +20,7 @@ abstract class DeadCodeElimination extends SubComponent { /** The block and index where an instruction is located */ type InstrLoc = (BasicBlock, Int) - + val phaseName = "dce" /** Create a new phase */ @@ -68,10 +68,10 @@ abstract class DeadCodeElimination extends SubComponent { /** what local variables have been accessed at least once? */ var accessedLocals: List[Local] = Nil - + /** Map from a local and a basic block to the instructions that store to that local in that basic block */ val localStores = mutable.Map[(Local, BasicBlock), mutable.BitSet]() withDefault {_ => mutable.BitSet()} - + /** Stores that clobber previous stores to array or ref locals. See SI-5313 */ val clobbers = mutable.Set[InstrLoc]() @@ -91,7 +91,7 @@ abstract class DeadCodeElimination extends SubComponent { accessedLocals = m.params.reverse m.code.blocks ++= linearizer.linearize(m) collectRDef(m) - mark + mark() sweep(m) accessedLocals = accessedLocals.distinct val diff = m.locals diff accessedLocals @@ -113,10 +113,25 @@ abstract class DeadCodeElimination extends SubComponent { useful(bb) = new mutable.BitSet(bb.size) var rd = rdef.in(bb); for (Pair(i, idx) <- bb.toList.zipWithIndex) { + + // utility for adding to worklist + def moveToWorkList() = moveToWorkListIf(true) + + // utility for (conditionally) adding to worklist + def moveToWorkListIf(cond: Boolean) = + if (cond) { + debuglog("in worklist: " + i) + worklist += ((bb, idx)) + } else { + debuglog("not in worklist: " + i) + } + + // instruction-specific logic i match { case LOAD_LOCAL(_) => defs = defs + Pair(((bb, idx)), rd.vars) + moveToWorkListIf(false) case STORE_LOCAL(l) => /* SI-4935 Check whether a module is stack top, if so mark the instruction that loaded it @@ -135,7 +150,8 @@ abstract class DeadCodeElimination extends SubComponent { case _ => false } } - if (necessary) worklist += ((bb, idx)) + moveToWorkListIf(necessary) + // add it to the localStores map val key = (l, bb) val set = localStores(key) @@ -144,10 +160,15 @@ abstract class DeadCodeElimination extends SubComponent { case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | - LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx)) - case CALL_METHOD(m1, _) if isSideEffecting(m1) => worklist += ((bb, idx)); debuglog("marking " + m1) + LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => + moveToWorkList() + + case CALL_METHOD(m1, _) if isSideEffecting(m1) => + moveToWorkList() + case CALL_METHOD(m1, SuperCall(_)) => - worklist += ((bb, idx)) // super calls to constructor + moveToWorkList() // super calls to constructor + case DROP(_) => val necessary = rdef.findDefs(bb, idx, 1) exists { p => val (bb1, idx1) = p @@ -156,12 +177,13 @@ abstract class DeadCodeElimination extends SubComponent { case LOAD_EXCEPTION(_) | DUP(_) | LOAD_MODULE(_) => true case _ => dropOf((bb1, idx1)) = (bb,idx) :: dropOf.getOrElse((bb1, idx1), Nil) -// println("DROP is innessential: " + i + " because of: " + bb1(idx1) + " at " + bb1 + ":" + idx1) + debuglog("DROP is innessential: " + i + " because of: " + bb1(idx1) + " at " + bb1 + ":" + idx1) false } } - if (necessary) worklist += ((bb, idx)) + moveToWorkListIf(necessary) case _ => () + moveToWorkListIf(false) } rd = rdef.interpret(bb, idx, rd) } @@ -183,19 +205,30 @@ abstract class DeadCodeElimination extends SubComponent { debuglog("Marking instr: \tBB_" + bb + ": " + idx + " " + bb(idx)) val instr = bb(idx) - // adds the instrutions that define the stack values about to be consumed to the work list to + // adds the instrutions that define the stack values about to be consumed to the work list to // be marked useful def addDefs() = for ((bb1, idx1) <- rdef.findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) { debuglog(s"\t${bb1(idx1)} is consumed by $instr") worklist += ((bb1, idx1)) } + // DROP logic -- if an instruction is useful, its drops are also useful + // and we don't mark the DROPs as useful directly but add them to the + // worklist so we also mark their reaching defs as useful - see SI-7060 if (!useful(bb)(idx)) { useful(bb) += idx dropOf.get(bb, idx) foreach { - for ((bb1, idx1) <- _) - useful(bb1) += idx1 + for ((bb1, idx1) <- _) { + /* + * SI-7060: A drop that we now mark as useful can be reached via several paths, + * so we should follow by marking all its reaching definition as useful too: + */ + debuglog("\tAdding: " + bb1(idx1) + " to the worklist, as a useful DROP.") + worklist += ((bb1, idx1)) + } } + + // per-instruction logic instr match { case LOAD_LOCAL(l1) => for ((l2, bb1, idx1) <- defs((bb, idx)) if l1 == l2; if !useful(bb1)(idx1)) { @@ -203,7 +236,7 @@ abstract class DeadCodeElimination extends SubComponent { worklist += ((bb1, idx1)) } - case STORE_LOCAL(l1) if l1.kind.isRefOrArrayType => + case STORE_LOCAL(l1) if l1.kind.isRefOrArrayType => addDefs() // see SI-5313 // search for clobbers of this store if we aren't doing l1 = null @@ -211,7 +244,7 @@ abstract class DeadCodeElimination extends SubComponent { // a lot of null stores very cheaply if (idx == 0 || bb(idx - 1) != CONSTANT(Constant(null))) findClobbers(l1, bb, idx + 1) - + case nw @ NEW(REFERENCE(sym)) => assert(nw.init ne null, "null new.init at: " + bb + ": " + idx + "(" + instr + ")") worklist += findInstruction(bb, nw.init) @@ -236,15 +269,15 @@ abstract class DeadCodeElimination extends SubComponent { } } } - + /** * Finds and marks all clobbers of the given local starting in the given * basic block at the given index - * + * * Storing to local variables of reference or array type may be indirectly - * observable because it may remove a reference to an object which may allow the object - * to be gc'd. See SI-5313. In this code I call the LOCAL_STORE(s) that immediately follow a - * LOCAL_STORE and that store to the same local "clobbers." If a LOCAL_STORE is marked + * observable because it may remove a reference to an object which may allow the object + * to be gc'd. See SI-5313. In this code I call the LOCAL_STORE(s) that immediately follow a + * LOCAL_STORE and that store to the same local "clobbers." If a LOCAL_STORE is marked * useful then its clobbers must go into the set of clobbers, which will be * compensated for later */ @@ -253,20 +286,20 @@ abstract class DeadCodeElimination extends SubComponent { val inspected = mutable.Set[BasicBlock]() // our worklist of blocks that still need to be checked val blocksToBeInspected = mutable.Set[BasicBlock]() - - // Tries to find the next clobber of l1 in bb1 starting at idx1. + + // Tries to find the next clobber of l1 in bb1 starting at idx1. // if it finds one it adds the clobber to clobbers set for later - // handling. If not it adds the direct successor blocks to + // handling. If not it adds the direct successor blocks to // the uninspectedBlocks to try to find clobbers there. Either way // it adds the exception successor blocks for further search - def findClobberInBlock(idx1: Int, bb1: BasicBlock) { + def findClobberInBlock(idx1: Int, bb1: BasicBlock) { val key = ((l, bb1)) val foundClobber = (localStores contains key) && { def minIdx(s : mutable.BitSet) = if(s.isEmpty) -1 else s.min // find the smallest index greater than or equal to idx1 val clobberIdx = minIdx(localStores(key) dropWhile (_ < idx1)) - if (clobberIdx == -1) + if (clobberIdx == -1) false else { debuglog(s"\t${bb1(clobberIdx)} is a clobber of ${bb(idx)}") @@ -275,7 +308,7 @@ abstract class DeadCodeElimination extends SubComponent { } } - // always need to look into the exception successors for additional clobbers + // always need to look into the exception successors for additional clobbers // because we don't know when flow might enter an exception handler blocksToBeInspected ++= (bb1.exceptionSuccessors filterNot inspected) // If we didn't find a clobber here then we need to look at successor blocks. @@ -284,7 +317,7 @@ abstract class DeadCodeElimination extends SubComponent { blocksToBeInspected ++= (bb1.directSuccessors filterNot inspected) } } - + // first search starting at the current index // note we don't put bb in the inspected list yet because a loop may later force // us back around to search from the beginning of bb @@ -301,14 +334,16 @@ abstract class DeadCodeElimination extends SubComponent { def sweep(m: IMethod) { val compensations = computeCompensations(m) + debuglog("Sweeping: " + m) + m foreachBlock { bb => -// Console.println("** Sweeping block " + bb + " **") + debuglog(bb + ":") val oldInstr = bb.toList bb.open bb.clear for (Pair(i, idx) <- oldInstr.zipWithIndex) { if (useful(bb)(idx)) { -// log(" " + i + " is useful") + debuglog(" * " + i + " is useful") bb.emit(i, i.pos) compensations.get(bb, idx) match { case Some(is) => is foreach bb.emit @@ -328,13 +363,13 @@ abstract class DeadCodeElimination extends SubComponent { log(s"Eliminated instantation of $sym inside $m") case STORE_LOCAL(l) if clobbers contains ((bb, idx)) => // if an unused instruction was a clobber of a used store to a reference or array type - // then we'll replace it with the store of a null to make sure the reference is + // then we'll replace it with the store of a null to make sure the reference is // eliminated. See SI-5313 bb emit CONSTANT(Constant(null)) bb emit STORE_LOCAL(l) case _ => () } - debuglog("Skipped: bb_" + bb + ": " + idx + "( " + i + ")") + debuglog(" " + i + " [swept]") } } @@ -355,6 +390,7 @@ abstract class DeadCodeElimination extends SubComponent { val defs = rdef.findDefs(bb, idx, 1, depth) for (d <- defs) { val (bb, idx) = d + debuglog("rdef: "+ bb(idx)) bb(idx) match { case DUP(_) if idx > 0 => bb(idx - 1) match { diff --git a/test/files/pos/SI-7060.flags b/test/files/pos/SI-7060.flags new file mode 100644 index 0000000000..c926ad6493 --- /dev/null +++ b/test/files/pos/SI-7060.flags @@ -0,0 +1 @@ +-Yinline -Ydead-code diff --git a/test/files/pos/SI-7060.scala b/test/files/pos/SI-7060.scala new file mode 100644 index 0000000000..c87620e020 --- /dev/null +++ b/test/files/pos/SI-7060.scala @@ -0,0 +1,11 @@ +object Test { + + @inline final def mbarray_apply_minibox(array: Any, tag: Byte): Long = + if (tag == 0) { + array.asInstanceOf[Array[Long]](0) + } else + array.asInstanceOf[Array[Byte]](0).toLong + + def crash_method(): Unit = + mbarray_apply_minibox(null, 0) +} -- cgit v1.2.3 From 570f4a46f663a8f55ce045bfde2d834bd4902f9c Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 5 Feb 2013 11:51:24 +0100 Subject: [nomaster] Revert "introduces global.pendingSuperCall" This reverts commit 0ebf72b9498108e67c2133c6522c436af50a18e8. Conflicts: src/compiler/scala/tools/nsc/typechecker/Typers.scala src/reflect/scala/reflect/internal/Trees.scala --- .../scala/reflect/reify/codegen/GenTrees.scala | 4 +- .../scala/reflect/reify/codegen/GenUtils.scala | 3 + src/compiler/scala/tools/nsc/ast/Positions.scala | 2 +- src/compiler/scala/tools/nsc/ast/Trees.scala | 23 +++---- .../tools/nsc/typechecker/StdAttachments.scala | 12 ++-- .../scala/tools/nsc/typechecker/Typers.scala | 73 ++++++++++++++-------- src/reflect/scala/reflect/api/Trees.scala | 9 --- src/reflect/scala/reflect/internal/Importers.scala | 2 - src/reflect/scala/reflect/internal/Positions.scala | 2 +- src/reflect/scala/reflect/internal/Printers.scala | 4 +- src/reflect/scala/reflect/internal/StdNames.scala | 1 - src/reflect/scala/reflect/internal/Trees.scala | 26 +++----- test/files/neg/anyval-anyref-parent.check | 2 +- test/files/neg/names-defaults-neg.check | 2 +- test/files/neg/t5529.check | 5 +- test/files/run/t5064.check | 6 +- test/files/run/t5603.check | 4 +- test/files/run/t6288.check | 12 ++-- test/files/run/t6288b-jump-position.check | 6 +- 19 files changed, 98 insertions(+), 100 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 31974b5b76..949f7f1799 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -45,9 +45,7 @@ trait GenTrees { case global.EmptyTree => reifyMirrorObject(EmptyTree) case global.emptyValDef => - mirrorSelect(nme.emptyValDef) - case global.pendingSuperCall => - mirrorSelect(nme.pendingSuperCall) + mirrorBuildSelect(nme.emptyValDef) case FreeDef(_, _, _, _, _) => reifyNestedFreeDef(tree) case FreeRef(_, _) => diff --git a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala index 21db93d8f5..49877b4286 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenUtils.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenUtils.scala @@ -34,6 +34,9 @@ trait GenUtils { def mirrorSelect(name: String): Tree = termPath(nme.UNIVERSE_PREFIX + name) + def mirrorBuildSelect(name: String): Tree = + termPath(nme.UNIVERSE_BUILD_PREFIX + name) + def mirrorMirrorSelect(name: String): Tree = termPath(nme.MIRROR_PREFIX + name) diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala index 0503c5fb10..d8fb632f73 100644 --- a/src/compiler/scala/tools/nsc/ast/Positions.scala +++ b/src/compiler/scala/tools/nsc/ast/Positions.scala @@ -20,7 +20,7 @@ trait Positions extends scala.reflect.internal.Positions { // When we prune due to encountering a position, traverse the // pruned children so we can warn about those lacking positions. t.children foreach { c => - if (c.isDummy) () + if ((c eq EmptyTree) || (c eq emptyValDef)) () else if (c.pos == NoPosition) { reporter.warning(t.pos, " Positioned tree has unpositioned child in phase " + globalPhase) inform("parent: " + treeSymStatus(t)) diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 4c725fd661..2872b83d88 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -65,13 +65,6 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => // --- factory methods ---------------------------------------------------------- - /** Factory method for a primary constructor super call `super.(args_1)...(args_n)` - */ - def PrimarySuperCall(argss: List[List[Tree]]): Tree = argss match { - case Nil => Apply(gen.mkSuperSelect, Nil) - case xs :: rest => rest.foldLeft(Apply(gen.mkSuperSelect, xs): Tree)(Apply.apply) - } - /** Generates a template with constructor corresponding to * * constrmods (vparams1_) ... (vparams_n) preSuper { presupers } @@ -124,12 +117,12 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) vparamss1 = List() :: vparamss1; val superRef: Tree = atPos(superPos)(gen.mkSuperSelect) - val superCall = pendingSuperCall // we can't know in advance which of the parents will end up as a superclass - // this requires knowing which of the parents is a type macro and which is not - // and that's something that cannot be found out before typer - // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) - // this means that we don't know what will be the arguments of the super call - // therefore here we emit a dummy which gets populated when the template is named and typechecked + val superCall = Apply(superRef, Nil) // we can't know in advance which of the parents will end up as a superclass + // this requires knowing which of the parents is a type macro and which is not + // and that's something that cannot be found out before typer + // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) + // this means that we don't know what will be the arguments of the super call + // therefore here we emit a dummy which gets populated when the template is named and typechecked List( // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)` // is it going to be a problem that we can no longer include the `argss`? @@ -337,8 +330,6 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => else super.transform { tree match { - case tree if tree.isDummy => - tree case tpt: TypeTree => if (tpt.original != null) transform(tpt.original) @@ -352,6 +343,8 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => transform(fn) case This(_) if tree.symbol != null && tree.symbol.isPackageClass => tree + case EmptyTree => + tree case _ => val dupl = tree.duplicate if (tree.hasSymbol && (!localOnly || (locals contains tree.symbol)) && !(keepLabels && tree.symbol.isLabel)) diff --git a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala index 0a1d3bfa7a..fa2913bee3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala +++ b/src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala @@ -21,14 +21,12 @@ trait StdAttachments { */ case class SuperCallArgsAttachment(argss: List[List[Tree]]) - /** Convenience method for `SuperCallArgsAttachment`. + /** Extractor for `SuperCallArgsAttachment`. * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, * so it really benefits from a dedicated extractor. */ - def superCallArgs(tree: Tree): Option[List[List[Tree]]] = - tree.attachments.get[SuperCallArgsAttachment] collect { case SuperCallArgsAttachment(argss) => argss } - - /** Determines whether the given tree has an associated SuperCallArgsAttachment. - */ - def hasSuperArgs(tree: Tree): Boolean = superCallArgs(tree).nonEmpty + object CarriesSuperCallArgs { + def unapply(tree: Tree): Option[List[List[Tree]]] = + tree.attachments.get[SuperCallArgsAttachment] collect { case SuperCallArgsAttachment(argss) => argss } + } } \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 5ec4530287..45b151348b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -53,10 +53,8 @@ trait Typers extends Modes with Adaptations with Tags { object UnTyper extends Traverser { override def traverse(tree: Tree) = { - if (!tree.isDummy) { - tree.tpe = null - if (tree.hasSymbol) tree.symbol = NoSymbol - } + if (tree != EmptyTree) tree.tpe = null + if (tree.hasSymbol) tree.symbol = NoSymbol super.traverse(tree) } } @@ -1584,7 +1582,7 @@ trait Typers extends Modes with Adaptations with Tags { var supertpt = typedTypeConstructor(decodedtpt) val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else Nil if (supertparams.nonEmpty) { - typedPrimaryConstrBody(templ) { + typedPrimaryConstrBody(templ) { superRef => val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK))) val supercall = New(supertpe, mmap(argss)(_.duplicate)) val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall @@ -1603,8 +1601,8 @@ trait Typers extends Modes with Adaptations with Tags { } /** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template. - * Before commencing the typecheck, replaces the `pendingSuperCall` dummy with the result of `actualSuperCall`. - * `actualSuperCall` can return `EmptyTree`, in which case the dummy is replaced with a literal unit. + * Before commencing the typecheck applies `superCallTransform` to a super call (if the latter exists). + * The transform can return `EmptyTree`, in which case the super call is replaced with a literal unit. * * ***Return value and side effects*** * @@ -1629,14 +1627,34 @@ trait Typers extends Modes with Adaptations with Tags { * Block(List( * ValDef(NoMods, x, TypeTree(), 2) * ValDef(NoMods, y, TypeTree(), 4) - * global.pendingSuperCall, + * Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), List()), * Literal(Constant(()))) * - * Note the `pendingSuperCall` part. This is the representation of a fill-me-in-later supercall dummy, - * which encodes the fact that supercall argss are unknown during parsing and need to be transplanted - * from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`. + * Note the Select(Super(_, _), nme.CONSTRUCTOR) part. This is the representation of + * a fill-me-in-later supercall dummy. The argss are Nil, which encodes the fact + * that supercall argss are unknown during parsing and need to be transplanted from one of the parent types. + * Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`. + * + * The entire Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), List()) is a dummy, + * and it's the one and only possible representation that can be emitted by parser. + * + * Despite of being unwieldy, this tree is quite convenient because: + * * It works as is for the case when no processing is required (empty ctor args for the superclass) + * * Stripping off the Apply produces a core that only needs rewrapping with applications of actual argss. + * + * For some time I was thinking of using just Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), + * but that one required wrapping even if the superclass doesn't take any argss. + * + * Another option would be to introduce a singleton tree akin to `emptyValDef` and use it as a dummy. + * Unfortunately this won't work out of the box, because the Super part is supposed to get attributed + * during `typedPrimaryConstrBody`. + * + * We could introduce another attachment for that or change SuperCallArgsAttachment + * to accommodate for the attributed Super, and then using the attached info to adjust the primary constructor + * during typedTemplate. However, given the scope of necessary changes (beyond a few lines) and the fact that, + * according to Martin, the whole thing is to be rewritten soon, I'd say we don't do the follow-up refactoring. */ - private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree = + private def typedPrimaryConstrBody(templ: Template)(superCallTransform: Tree => Tree): Tree = treeInfo.firstConstructor(templ.body) match { case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => val (preSuperStats, superCall) = { @@ -1644,7 +1662,7 @@ trait Typers extends Modes with Adaptations with Tags { (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) } val superCall1 = (superCall match { - case global.pendingSuperCall => actualSuperCall + case Apply(superRef @ Select(Super(_, _), nme.CONSTRUCTOR), Nil) => superCallTransform(superRef) case EmptyTree => EmptyTree }) orElse cunit val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1) @@ -1724,7 +1742,7 @@ trait Typers extends Modes with Adaptations with Tags { // and therefore early fields have their type trees not assigned // here we detect this situation and take preventive measures if (treeInfo.hasUntypedPreSuperFields(templ.body)) - typedPrimaryConstrBody(templ)(EmptyTree) + typedPrimaryConstrBody(templ)(superRef => EmptyTree) supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) } catch { @@ -2007,8 +2025,6 @@ trait Typers extends Modes with Adaptations with Tags { validateParentClasses(parents1, selfType) if (clazz.isCase) validateNoCaseAncestor(clazz) - if (clazz.isTrait && hasSuperArgs(parents1.head)) - ConstrArgsInParentOfTraitError(parents1.head, clazz) if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.owner.isPackageClass) unit.error(clazz.pos, "inner classes cannot be classfile annotations") @@ -2020,16 +2036,23 @@ trait Typers extends Modes with Adaptations with Tags { val body = if (isPastTyper || reporter.hasErrors) templ.body else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) - val primaryCtor = treeInfo.firstConstructor(body) - val primaryCtor1 = primaryCtor match { - case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) => - val argss = superCallArgs(parents1.head) getOrElse Nil - val pos = wrappingPos(parents1.head.pos, argss.flatten) - val superCall = atPos(pos)(PrimarySuperCall(argss)) - deriveDefDef(primaryCtor)(block => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos - case _ => primaryCtor + parents1.head match { + case CarriesSuperCallArgs(argss) => + if (clazz.isTrait) { + ConstrArgsInParentOfTraitError(parents1.head, clazz) + body + } else { + val pos = wrappingPos(parents1.head.pos, argss.flatten) + val primaryCtor = treeInfo.firstConstructor(templ.body) + val primaryCtor1 = (deriveDefDef(primaryCtor) { + case block @ Block(earlyVals :+ Apply(superRef, Nil), unit) => + val superCall = atPos(pos)((superRef /: argss)(Apply.apply)) + Block(earlyVals :+ superCall, unit) setPos pos + }) setPos pos + body map { case `primaryCtor` => primaryCtor1; case stat => stat } + } + case _ => body } - body mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat } } val body1 = typedStats(body, templ.symbol) diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index 869b92a104..0937a93738 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -2405,15 +2405,6 @@ trait Trees { self: Universe => */ val emptyValDef: ValDef - /** An empty superclass constructor call corresponding to: - * super.() - * This is used as a placeholder in the primary constructor body in class templates - * to denote the insertion point of a call to superclass constructor after the typechecker - * figures out the superclass of a given template. - * @group Trees - */ - val pendingSuperCall: Apply - // ---------------------- factories ---------------------------------------------- /** A factory method for `ClassDef` nodes. diff --git a/src/reflect/scala/reflect/internal/Importers.scala b/src/reflect/scala/reflect/internal/Importers.scala index 2f2b02975c..43902c1930 100644 --- a/src/reflect/scala/reflect/internal/Importers.scala +++ b/src/reflect/scala/reflect/internal/Importers.scala @@ -334,8 +334,6 @@ trait Importers extends api.Importers { self: SymbolTable => new ModuleDef(importModifiers(mods), importName(name).toTermName, importTemplate(impl)) case from.emptyValDef => emptyValDef - case from.pendingSuperCall => - pendingSuperCall case from.ValDef(mods, name, tpt, rhs) => new ValDef(importModifiers(mods), importName(name).toTermName, importTree(tpt), importTree(rhs)) case from.DefDef(mods, name, tparams, vparamss, tpt, rhs) => diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index b65df0e70b..faa161d6b1 100644 --- a/src/reflect/scala/reflect/internal/Positions.scala +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -38,7 +38,7 @@ trait Positions extends api.Positions { self: SymbolTable => protected class DefaultPosAssigner extends PosAssigner { var pos: Position = _ override def traverse(t: Tree) { - if (t.isDummy) () + if (t eq EmptyTree) () else if (t.pos == NoPosition) { t.setPos(pos) super.traverse(t) // TODO: bug? shouldn't the traverse be outside of the if? diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 6f834803c4..80d247c0ea 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -542,10 +542,8 @@ trait Printers extends api.Printers { self: SymbolTable => print(")") case EmptyTree => print("EmptyTree") - case self.emptyValDef => + case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => print("emptyValDef") - case self.pendingSuperCall => - print("pendingSuperCall") case tree: Tree => val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index c870d8972d..5e7f5777b2 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -730,7 +730,6 @@ trait StdNames { val null_ : NameType = "null" val ofDim: NameType = "ofDim" val origin: NameType = "origin" - val pendingSuperCall: NameType = "pendingSuperCall" val prefix : NameType = "prefix" val productArity: NameType = "productArity" val productElement: NameType = "productElement" diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index e586ab4cad..693b75fbbc 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -36,7 +36,6 @@ trait Trees extends api.Trees { self: SymbolTable => def isDef = false def isEmpty = false - def isDummy = false /** The canonical way to test if a Tree represents a term. */ @@ -229,6 +228,14 @@ trait Trees extends api.Trees { self: SymbolTable => override def isDef = true } + case object EmptyTree extends TermTree { + val asList = List(this) + super.tpe_=(NoType) + override def tpe_=(t: Type) = + if (t != NoType) throw new UnsupportedOperationException("tpe_=("+t+") inapplicable for ") + override def isEmpty = true + } + abstract class MemberDef extends DefTree with MemberDefApi { def mods: Modifiers def keyword: String = this match { @@ -592,7 +599,6 @@ trait Trees extends api.Trees { self: SymbolTable => case _: ApplyToImplicitArgs => new ApplyToImplicitArgs(fun, args) case _: ApplyImplicitView => new ApplyImplicitView(fun, args) // TODO: ApplyConstructor ??? - case self.pendingSuperCall => self.pendingSuperCall case _ => new Apply(fun, args) }).copyAttrs(tree) def ApplyDynamic(tree: Tree, qual: Tree, args: List[Tree]) = @@ -955,24 +961,12 @@ trait Trees extends api.Trees { self: SymbolTable => def ValDef(sym: Symbol): ValDef = ValDef(sym, EmptyTree) - trait DummyTree extends Tree { + object emptyValDef extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) { override def isEmpty = true - override def isDummy = true - - private def unsupported(what: String, args: Any*) = - throw new UnsupportedOperationException(s"$what($args) inapplicable for "+self.toString) - super.setPos(NoPosition) - override def setPos(pos: Position) = unsupported("setPos", pos) - - super.setType(NoType) - override def tpe_=(t: Type) = if (t != NoType) unsupported("tpe_=", t) + override def setPos(pos: Position) = { assert(false); this } } - case object EmptyTree extends TermTree with DummyTree { val asList = List(this) } - object emptyValDef extends ValDef(Modifiers(PRIVATE), nme.WILDCARD, TypeTree(NoType), EmptyTree) with DummyTree - object pendingSuperCall extends Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List()) with DummyTree - def DefDef(sym: Symbol, mods: Modifiers, vparamss: List[List[ValDef]], rhs: Tree): DefDef = atPos(sym.pos) { assert(sym != NoSymbol) diff --git a/test/files/neg/anyval-anyref-parent.check b/test/files/neg/anyval-anyref-parent.check index 8c2aa36583..fe20e5de81 100644 --- a/test/files/neg/anyval-anyref-parent.check +++ b/test/files/neg/anyval-anyref-parent.check @@ -3,7 +3,7 @@ trait Foo2 extends AnyVal // fail ^ anyval-anyref-parent.scala:5: error: Any does not have a constructor class Bar1 extends Any // fail - ^ + ^ anyval-anyref-parent.scala:6: error: value class needs to have exactly one public val parameter class Bar2(x: Int) extends AnyVal // fail ^ diff --git a/test/files/neg/names-defaults-neg.check b/test/files/neg/names-defaults-neg.check index f6bd703e1f..ea7c323b74 100644 --- a/test/files/neg/names-defaults-neg.check +++ b/test/files/neg/names-defaults-neg.check @@ -100,7 +100,7 @@ Error occurred in an application involving default arguments. ^ names-defaults-neg.scala:86: error: module extending its companion class cannot use default constructor arguments object C extends C() - ^ + ^ names-defaults-neg.scala:90: error: deprecated parameter name x has to be distinct from any other parameter name (deprecated or not). def deprNam1(x: Int, @deprecatedName('x) y: String) = 0 ^ diff --git a/test/files/neg/t5529.check b/test/files/neg/t5529.check index da3f84e1ec..5d2175fa79 100644 --- a/test/files/neg/t5529.check +++ b/test/files/neg/t5529.check @@ -4,4 +4,7 @@ t5529.scala:12: error: File is already defined as class File t5529.scala:10: error: class type required but test.Test.File found sealed class Dir extends File { } ^ -two errors found +t5529.scala:10: error: test.Test.File does not have a constructor + sealed class Dir extends File { } + ^ +three errors found diff --git a/test/files/run/t5064.check b/test/files/run/t5064.check index 61ccfd16e7..077006abd9 100644 --- a/test/files/run/t5064.check +++ b/test/files/run/t5064.check @@ -1,6 +1,6 @@ -[53] T5064.super.() -[53] T5064.super. -[53] this +[12] T5064.super.() +[12] T5064.super. +[12] this [16:23] immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1})) [16:20] immutable.this.List.apply <16:20> immutable.this.List diff --git a/test/files/run/t5603.check b/test/files/run/t5603.check index 3b2eb55313..5127d3c1c7 100644 --- a/test/files/run/t5603.check +++ b/test/files/run/t5603.check @@ -12,7 +12,7 @@ [95:101] private[this] val i: [98:101]Int = _; <119:139>def ([95]i: [98]Int) = <119:139>{ <119:139>val nameElse = <134:139>"Bob"; - [NoPosition][NoPosition][NoPosition]super.(); + [94][94][94]super.(); [94]() }; [168:184]val name = [179:184]"avc"; @@ -20,7 +20,7 @@ }; [215:241]object Test extends [227:241][235:238]App { [227]def () = [227]{ - [NoPosition][NoPosition][NoPosition]super.(); + [227][227][227]super.(); [227]() }; [NoPosition] diff --git a/test/files/run/t6288.check b/test/files/run/t6288.check index 4895c2c007..e6467edc95 100644 --- a/test/files/run/t6288.check +++ b/test/files/run/t6288.check @@ -1,8 +1,8 @@ [[syntax trees at end of patmat]] // newSource1 [7]package [7] { [7]object Case3 extends [13][106]scala.AnyRef { - [106]def (): [13]Case3.type = [106]{ - [106][106][106]Case3.super.(); + [13]def (): [13]Case3.type = [13]{ + [13][13][13]Case3.super.(); [13]() }; [21]def unapply([29]z: [32]): [21]Option[Int] = [56][52][52]scala.Some.apply[[52]Int]([58]-1); @@ -24,8 +24,8 @@ } }; [113]object Case4 extends [119][217]scala.AnyRef { - [217]def (): [119]Case4.type = [217]{ - [217][217][217]Case4.super.(); + [119]def (): [119]Case4.type = [119]{ + [119][119][119]Case4.super.(); [119]() }; [127]def unapplySeq([138]z: [141]): [127]Option[List[Int]] = [167]scala.None; @@ -50,8 +50,8 @@ } }; [224]object Case5 extends [230][312]scala.AnyRef { - [312]def (): [230]Case5.type = [312]{ - [312][312][312]Case5.super.(); + [230]def (): [230]Case5.type = [230]{ + [230][230][230]Case5.super.(); [230]() }; [238]def unapply([246]z: [249]): [238]Boolean = [265]true; diff --git a/test/files/run/t6288b-jump-position.check b/test/files/run/t6288b-jump-position.check index ece88b18f0..83ba810958 100644 --- a/test/files/run/t6288b-jump-position.check +++ b/test/files/run/t6288b-jump-position.check @@ -65,9 +65,9 @@ object Case3 extends Object { blocks: [1] 1: - 12 THIS(Case3) - 12 CALL_METHOD java.lang.Object. (super()) - 12 RETURN(UNIT) + 1 THIS(Case3) + 1 CALL_METHOD java.lang.Object. (super()) + 1 RETURN(UNIT) } Exception handlers: -- cgit v1.2.3 From c9a0e36224b6eb2807bad0df2d5aa11bb05c8a32 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 5 Feb 2013 11:53:43 +0100 Subject: [nomaster] Revert "refactors handling of parent types" This reverts commit 40063b0009d55ed527bf1625d99a168a8faa4124. Conflicts: src/compiler/scala/tools/nsc/ast/parser/Parsers.scala src/compiler/scala/tools/nsc/typechecker/Typers.scala --- src/compiler/scala/tools/nsc/ast/Trees.scala | 18 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 62 ++-- .../scala/tools/nsc/ast/parser/TreeBuilder.scala | 20 +- .../scala/tools/nsc/transform/Constructors.scala | 1 + .../scala/tools/nsc/transform/UnCurry.scala | 4 +- .../tools/nsc/typechecker/ContextErrors.scala | 13 +- .../scala/tools/nsc/typechecker/Namers.scala | 9 +- .../tools/nsc/typechecker/StdAttachments.scala | 22 -- .../scala/tools/nsc/typechecker/Typers.scala | 396 +++++++-------------- .../scala/tools/nsc/typechecker/Unapplies.scala | 2 +- .../scala/tools/reflect/ToolBoxFactory.scala | 1 + src/reflect/scala/reflect/internal/TreeInfo.scala | 7 - src/reflect/scala/reflect/internal/Trees.scala | 3 - test/files/neg/cyclics-import.check | 11 +- test/files/neg/protected-constructors.check | 5 +- test/files/neg/t2148.check | 2 +- test/files/neg/t409.check | 4 +- test/files/neg/t5696.check | 2 +- test/files/neg/t667.check | 4 +- test/files/neg/t877.check | 4 +- test/files/run/analyzerPlugins.check | 35 +- 21 files changed, 225 insertions(+), 400 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 2872b83d88..2ad762fd55 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -82,7 +82,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * body * } */ - def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): Template = { + def Template(parents: List[Tree], self: ValDef, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): Template = { /* Add constructor to template */ // create parameters for as synthetic trees. @@ -117,16 +117,9 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => if (vparamss1.isEmpty || !vparamss1.head.isEmpty && vparamss1.head.head.mods.isImplicit) vparamss1 = List() :: vparamss1; val superRef: Tree = atPos(superPos)(gen.mkSuperSelect) - val superCall = Apply(superRef, Nil) // we can't know in advance which of the parents will end up as a superclass - // this requires knowing which of the parents is a type macro and which is not - // and that's something that cannot be found out before typer - // (the type macros aren't in the trunk yet, but there is a plan for them to land there soon) - // this means that we don't know what will be the arguments of the super call - // therefore here we emit a dummy which gets populated when the template is named and typechecked + val superCall = (superRef /: argss) (Apply.apply) List( - // TODO: previously this was `wrappingPos(superPos, lvdefs ::: argss.flatten)` - // is it going to be a problem that we can no longer include the `argss`? - atPos(wrappingPos(superPos, lvdefs)) ( + atPos(wrappingPos(superPos, lvdefs ::: argss.flatten)) ( DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant()))))) } } @@ -144,10 +137,11 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => * @param constrMods the modifiers for the class constructor, i.e. as in `class C private (...)` * @param vparamss the value parameters -- if they have symbols they * should be owned by `sym` + * @param argss the supercall arguments * @param body the template statements without primary constructor * and value parameter fields. */ - def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], body: List[Tree], superPos: Position): ClassDef = { + def ClassDef(sym: Symbol, constrMods: Modifiers, vparamss: List[List[ValDef]], argss: List[List[Tree]], body: List[Tree], superPos: Position): ClassDef = { // "if they have symbols they should be owned by `sym`" assert( mforall(vparamss)(p => (p.symbol eq NoSymbol) || (p.symbol.owner == sym)), @@ -157,7 +151,7 @@ trait Trees extends scala.reflect.internal.Trees { self: Global => ClassDef(sym, Template(sym.info.parents map TypeTree, if (sym.thisSym == sym || phase.erasedTypes) emptyValDef else ValDef(sym.thisSym), - constrMods, vparamss, body, superPos)) + constrMods, vparamss, argss, body, superPos)) } // --- subcomponents -------------------------------------------------- diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 915a8bc49b..6f79f639b9 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1559,9 +1559,9 @@ self => val nstart = in.skipToken() val npos = r2p(nstart, nstart, in.lastOffset) val tstart = in.offset - val (parents, self, stats) = template() + val (parents, argss, self, stats) = template(isTrait = false) val cpos = r2p(tstart, tstart, in.lastOffset max tstart) - makeNew(parents, self, stats, npos, cpos) + makeNew(parents, self, stats, argss, npos, cpos) case _ => syntaxErrorOrIncomplete("illegal start of simple expression", true) errorTermTree @@ -2739,17 +2739,20 @@ self => * TraitParents ::= AnnotType {with AnnotType} * }}} */ - def templateParents(): List[Tree] = { - val parents = new ListBuffer[Tree] - def readAppliedParent() = { - val start = in.offset - val parent = startAnnotType() - val argss = if (in.token == LPAREN) multipleArgumentExprs() else Nil - parents += atPos(start)((parent /: argss)(Apply.apply)) + def templateParents(isTrait: Boolean): (List[Tree], List[List[Tree]]) = { + val parents = new ListBuffer[Tree] += startAnnotType() + val argss = ( + // TODO: the insertion of ListOfNil here is where "new Foo" becomes + // indistinguishable from "new Foo()". + if (in.token == LPAREN && !isTrait) multipleArgumentExprs() + else ListOfNil + ) + + while (in.token == WITH) { + in.nextToken() + parents += startAnnotType() } - readAppliedParent() - while (in.token == WITH) { in.nextToken(); readAppliedParent() } - parents.toList + (parents.toList, argss) } /** {{{ @@ -2759,7 +2762,7 @@ self => * EarlyDef ::= Annotations Modifiers PatDef * }}} */ - def template(): (List[Tree], ValDef, List[Tree]) = { + def template(isTrait: Boolean): (List[Tree], List[List[Tree]], ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { // @S: pre template body cannot stub like post body can! @@ -2776,16 +2779,16 @@ self => case _ => List() } in.nextToken() - val parents = templateParents() - val (self1, body1) = templateBodyOpt(parenMeansSyntaxError = false) - (parents, self1, earlyDefs ::: body1) + val (parents, argss) = templateParents(isTrait = isTrait) + val (self1, body1) = templateBodyOpt(traitParentSeen = isTrait) + (parents, argss, self1, earlyDefs ::: body1) } else { - (List(), self, body) + (List(), ListOfNil, self, body) } } else { - val parents = templateParents() - val (self, body) = templateBodyOpt(parenMeansSyntaxError = false) - (parents, self, body) + val (parents, argss) = templateParents(isTrait = isTrait) + val (self, body) = templateBodyOpt(traitParentSeen = isTrait) + (parents, argss, self, body) } } @@ -2799,15 +2802,15 @@ self => * }}} */ def templateOpt(mods: Modifiers, name: Name, constrMods: Modifiers, vparamss: List[List[ValDef]], tstart: Int): Template = { - val (parents0, self, body) = ( + val (parents0, argss, self, body) = ( if (in.token == EXTENDS || in.token == SUBTYPE && mods.isTrait) { in.nextToken() - template() + template(isTrait = mods.isTrait) } else { newLineOptWhenFollowedBy(LBRACE) - val (self, body) = templateBodyOpt(parenMeansSyntaxError = mods.isTrait || name.isTermName) - (List(), self, body) + val (self, body) = templateBodyOpt(traitParentSeen = false) + (List(), ListOfNil, self, body) } ) def anyrefParents() = { @@ -2829,7 +2832,7 @@ self => if (inScalaRootPackage && ScalaValueClassNames.contains(name)) Template(parents0, self, anyvalConstructor :: body) else - Template(anyrefParents, self, constrMods, vparamss, body, o2p(tstart)) + Template(anyrefParents, self, constrMods, vparamss, argss, body, o2p(tstart)) } } @@ -2844,15 +2847,14 @@ self => case (self, Nil) => (self, EmptyTree.asList) case result => result } - def templateBodyOpt(parenMeansSyntaxError: Boolean): (ValDef, List[Tree]) = { + def templateBodyOpt(traitParentSeen: Boolean): (ValDef, List[Tree]) = { newLineOptWhenFollowedBy(LBRACE) if (in.token == LBRACE) { templateBody(isPre = false) } else { - if (in.token == LPAREN) { - if (parenMeansSyntaxError) syntaxError(s"traits or objects may not have parameters", true) - else abort("unexpected opening parenthesis") - } + if (in.token == LPAREN) + syntaxError((if (traitParentSeen) "parents of traits" else "traits or objects")+ + " may not have parameters", true) (emptyValDef, List()) } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index e7dc0a47be..7969bb9c20 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -205,26 +205,20 @@ abstract class TreeBuilder { */ def makeAnonymousNew(stats: List[Tree]): Tree = { val stats1 = if (stats.isEmpty) List(Literal(Constant(()))) else stats - makeNew(Nil, emptyValDef, stats1, NoPosition, NoPosition) + makeNew(Nil, emptyValDef, stats1, ListOfNil, NoPosition, NoPosition) } /** Create positioned tree representing an object creation - import global._ - - /** Carries information necessary to expand the host tree. - * At times we need to store this info, because macro expansion can be delayed until its targs are inferred. - * After a macro application has been successfully expanded, this attachment is destroyed. - */ type UnaffiliatedMacroContext = scala.reflect.macros.runtime.Context type MacroContext = UnaffiliatedMacroContext { val universe: self.global.type } case class MacroRuntimeAttachment(delayed: Boolean, typerContext: Context, macroContext: Option[MacroContext]) - - /** After being synthesized by the parser, primary constructors aren't fully baked yet. - * A call to super in such constructors is just a fill-me-in-later dummy resolved later - * by `parentTypes`. This attachment coordinates `parentTypes` and `typedTemplate` and - * allows them to complete the synthesis. - */ - case class SuperCallArgsAttachment(argss: List[List[Tree]]) - - /** Extractor for `SuperCallArgsAttachment`. - * Compared with `MacroRuntimeAttachment` this attachment has different a usage pattern, - * so it really benefits from a dedicated extractor. - */ - object CarriesSuperCallArgs { - def unapply(tree: Tree): Option[List[List[Tree]]] = - tree.attachments.get[SuperCallArgsAttachment] collect { case SuperCallArgsAttachment(argss) => argss } - } } \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 45b151348b..f518712701 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1399,6 +1399,13 @@ trait Typers extends Modes with Adaptations with Tags { if (member(qual, name) != NoSymbol) qual else adaptToMember(qual, HasMember(name)) + private def typePrimaryConstrBody(clazz : Symbol, cbody: Tree, tparams: List[Symbol], enclTparams: List[Symbol], vparamss: List[List[ValDef]]): Tree = { + // XXX: see about using the class's symbol.... + enclTparams foreach (sym => context.scope.enter(sym)) + namer.enterValueParams(vparamss) + typed(cbody) + } + private def validateNoCaseAncestor(clazz: Symbol) = { if (!phase.erasedTypes) { for (ancestor <- clazz.ancestors find (_.isCase)) { @@ -1500,263 +1507,126 @@ trait Typers extends Modes with Adaptations with Tags { unit.error(tparam.pos, "type parameter of value class may not be specialized") } - /** Typechecks a parent type reference. - * - * This typecheck is harder than it might look, because it should honor early - * definitions and also perform type argument inference with the help of super call - * arguments provided in `encodedtpt`. - * - * The method is called in batches (batch = 1 time per each parent type referenced), - * two batches per definition: once from namer, when entering a ClassDef or a ModuleDef - * and once from typer, when typechecking the definition. - * - * ***Arguments*** - * - * `encodedtpt` represents the parent type reference wrapped in an `Apply` node - * which indicates value arguments (i.e. type macro arguments or super constructor call arguments) - * If no value arguments are provided by the user, the `Apply` node is still - * there, but its `args` will be set to `Nil`. - * This argument is synthesized by `tools.nsc.ast.Parsers.templateParents`. - * - * `templ` is an enclosing template, which contains a primary constructor synthesized by the parser. - * Such a constructor is a DefDef which contains early initializers and maybe a super constructor call - * (I wrote "maybe" because trait constructors don't call super constructors). - * This argument is synthesized by `tools.nsc.ast.Trees.Template`. - * - * `inMixinPosition` indicates whether the reference is not the first in the - * list of parents (and therefore cannot be a class) or the opposite. - * - * ***Return value and side effects*** - * - * Returns a `TypeTree` representing a resolved parent type. - * If the typechecked parent reference implies non-nullary and non-empty argument list, - * this argument list is attached to the returned value in SuperCallArgsAttachment. - * The attachment is necessary for the subsequent typecheck to fixup a super constructor call - * in the body of the primary constructor (see `typedTemplate` for details). - * - * This method might invoke `typedPrimaryConstrBody`, hence it might cause the side effects - * described in the docs of that method. It might also attribute the Super(_, _) reference - * (if present) inside the primary constructor of `templ`. - * - * ***Example*** - * - * For the following definition: - * - * class D extends { - * val x = 2 - * val y = 4 - * } with B(x)(3) with C(y) with T - * - * this method will be called six times: - * - * (3 times from the namer) - * typedParentType(Apply(Apply(Ident(B), List(Ident(x))), List(3)), templ, inMixinPosition = false) - * typedParentType(Apply(Ident(C), List(Ident(y))), templ, inMixinPosition = true) - * typedParentType(Apply(Ident(T), List()), templ, inMixinPosition = true) - * - * (3 times from the typer) - * - */ - private def typedParentType(encodedtpt: Tree, templ: Template, inMixinPosition: Boolean): Tree = { - val app = treeInfo.dissectApplied(encodedtpt) - val (treeInfo.Applied(core, targs, argss), decodedtpt) = (app, app.callee) - val argssAreTrivial = argss == Nil || argss == ListOfNil - - // we cannot avoid cyclic references with `initialize` here, because when type macros arrive, - // we'll have to check the probe for isTypeMacro anyways. - // therefore I think it's reasonable to trade a more specific "inherits itself" error - // for a generic, yet understandable "cyclic reference" error - var probe = typedTypeConstructor(core.duplicate).tpe.typeSymbol - if (probe == null) probe = NoSymbol - probe.initialize - - if (probe.isTrait || inMixinPosition) { - if (!argssAreTrivial) { - if (probe.isTrait) ConstrArgsInParentWhichIsTraitError(encodedtpt, probe) - else () // a class in a mixin position - this warrants an error in `validateParentClasses` - // therefore here we do nothing, e.g. don't check that the # of ctor arguments - // matches the # of ctor parameters or stuff like that - } - typedType(decodedtpt) - } else { - var supertpt = typedTypeConstructor(decodedtpt) - val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else Nil - if (supertparams.nonEmpty) { - typedPrimaryConstrBody(templ) { superRef => - val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK))) - val supercall = New(supertpe, mmap(argss)(_.duplicate)) - val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall - ctor setType supertpe // this is an essential hack, otherwise it will occasionally fail to typecheck - atPos(supertpt.pos.focus)(supercall) - } match { - case EmptyTree => MissingTypeArgumentsParentTpeError(supertpt) - case tpt => supertpt = TypeTree(tpt.tpe) setPos supertpt.pos.focus + def parentTypes(templ: Template): List[Tree] = + if (templ.parents.isEmpty) List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) + else try { + val clazz = context.owner + // Normalize supertype and mixins so that supertype is always a class, not a trait. + var supertpt = typedTypeConstructor(templ.parents.head) + val firstParent = supertpt.tpe.typeSymbol + var mixins = templ.parents.tail map typedType + // If first parent is a trait, make it first mixin and add its superclass as first parent + while ((supertpt.tpe.typeSymbol ne null) && supertpt.tpe.typeSymbol.initialize.isTrait) { + val supertpt1 = typedType(supertpt) + if (!supertpt1.isErrorTyped) { + mixins = supertpt1 :: mixins + supertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus } } - // this is the place where we tell the typer what argss should be used for the super call - // if argss are nullary or empty, then (see the docs for `typedPrimaryConstrBody`) - // the super call dummy is already good enough, so we don't need to do anything - if (argssAreTrivial) supertpt else supertpt updateAttachment SuperCallArgsAttachment(argss) - } - } - - /** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template. - * Before commencing the typecheck applies `superCallTransform` to a super call (if the latter exists). - * The transform can return `EmptyTree`, in which case the super call is replaced with a literal unit. - * - * ***Return value and side effects*** - * - * If a super call is present in the primary constructor and is not erased by the transform, returns it typechecked. - * Otherwise (e.g. if the primary constructor is missing or the super call isn't there) returns `EmptyTree`. - * - * As a side effect, this method attributes the underlying fields of early vals. - * Early vals aren't typechecked anywhere else, so it's essential to call `typedPrimaryConstrBody` - * at least once per definition. It'd be great to disentangle this logic at some point. - * - * ***Example*** - * - * For the following definition: - * - * class D extends { - * val x = 2 - * val y = 4 - * } with B(x)(3) with C(y) with T - * - * the primary constructor of `templ` will be: - * - * Block(List( - * ValDef(NoMods, x, TypeTree(), 2) - * ValDef(NoMods, y, TypeTree(), 4) - * Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), List()), - * Literal(Constant(()))) - * - * Note the Select(Super(_, _), nme.CONSTRUCTOR) part. This is the representation of - * a fill-me-in-later supercall dummy. The argss are Nil, which encodes the fact - * that supercall argss are unknown during parsing and need to be transplanted from one of the parent types. - * Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`. - * - * The entire Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), List()) is a dummy, - * and it's the one and only possible representation that can be emitted by parser. - * - * Despite of being unwieldy, this tree is quite convenient because: - * * It works as is for the case when no processing is required (empty ctor args for the superclass) - * * Stripping off the Apply produces a core that only needs rewrapping with applications of actual argss. - * - * For some time I was thinking of using just Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)), - * but that one required wrapping even if the superclass doesn't take any argss. - * - * Another option would be to introduce a singleton tree akin to `emptyValDef` and use it as a dummy. - * Unfortunately this won't work out of the box, because the Super part is supposed to get attributed - * during `typedPrimaryConstrBody`. - * - * We could introduce another attachment for that or change SuperCallArgsAttachment - * to accommodate for the attributed Super, and then using the attached info to adjust the primary constructor - * during typedTemplate. However, given the scope of necessary changes (beyond a few lines) and the fact that, - * according to Martin, the whole thing is to be rewritten soon, I'd say we don't do the follow-up refactoring. - */ - private def typedPrimaryConstrBody(templ: Template)(superCallTransform: Tree => Tree): Tree = - treeInfo.firstConstructor(templ.body) match { - case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => - val (preSuperStats, superCall) = { - val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) - (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) - } - val superCall1 = (superCall match { - case Apply(superRef @ Select(Super(_, _), nme.CONSTRUCTOR), Nil) => superCallTransform(superRef) - case EmptyTree => EmptyTree - }) orElse cunit - val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1) - - val clazz = context.owner - assert(clazz != NoSymbol, templ) - val cscope = context.outer.makeNewScope(ctor, context.outer.owner) - val cbody2 = { // called both during completion AND typing. - val typer1 = newTyper(cscope) - // XXX: see about using the class's symbol.... - clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym)) - typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate))) - typer1.typed(cbody1) - } + if (supertpt.tpe.typeSymbol == AnyClass && firstParent.isTrait) + supertpt.tpe = AnyRefClass.tpe + + // Determine + // - supertparams: Missing type parameters from supertype + // - supertpe: Given supertype, polymorphic in supertparams + val supertparams = if (supertpt.hasSymbol) supertpt.symbol.typeParams else List() + var supertpe = supertpt.tpe + if (!supertparams.isEmpty) + supertpe = PolyType(supertparams, appliedType(supertpe, supertparams map (_.tpeHK))) + + // A method to replace a super reference by a New in a supercall + def transformSuperCall(scall: Tree): Tree = (scall: @unchecked) match { + case Apply(fn, args) => + treeCopy.Apply(scall, transformSuperCall(fn), args map (_.duplicate)) + case Select(Super(_, _), nme.CONSTRUCTOR) => + treeCopy.Select( + scall, + atPos(supertpt.pos.focus)(New(TypeTree(supertpe)) setType supertpe), + nme.CONSTRUCTOR) + } - val preSuperVals = treeInfo.preSuperFields(templ.body) - if (preSuperVals.isEmpty && preSuperStats.nonEmpty) - debugwarn("Wanted to zip empty presuper val list with " + preSuperStats) - else - map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) + treeInfo.firstConstructor(templ.body) match { + case constr @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) => + // Convert constructor body to block in environment and typecheck it + val (preSuperStats, superCall) = { + val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x)) + (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate) + } + val cstats1 = if (superCall == EmptyTree) preSuperStats else preSuperStats :+ superCall + val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall match { + case Apply(_, _) if supertparams.nonEmpty => transformSuperCall(superCall) + case _ => cunit.duplicate + }) + val outercontext = context.outer + + assert(clazz != NoSymbol, templ) + val cscope = outercontext.makeNewScope(constr, outercontext.owner) + val cbody2 = newTyper(cscope) // called both during completion AND typing. + .typePrimaryConstrBody(clazz, + cbody1, supertparams, clazz.unsafeTypeParams, vparamss map (_.map(_.duplicate))) + + superCall match { + case Apply(_, _) => + val treeInfo.Applied(_, _, argss) = superCall + val sarg = argss.flatten.headOption.getOrElse(EmptyTree) + if (sarg != EmptyTree && supertpe.typeSymbol != firstParent) + ConstrArgsInTraitParentTpeError(sarg, firstParent) + if (!supertparams.isEmpty) + supertpt = TypeTree(cbody2.tpe) setPos supertpt.pos.focus + case _ => + if (!supertparams.isEmpty) + MissingTypeArgumentsParentTpeError(supertpt) + } - if (superCall1 == cunit) EmptyTree else cbody2 - case _ => - EmptyTree - } + val preSuperVals = treeInfo.preSuperFields(templ.body) + if (preSuperVals.isEmpty && preSuperStats.nonEmpty) + debugwarn("Wanted to zip empty presuper val list with " + preSuperStats) + else + map2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt.tpe = ldef.symbol.tpe) - /** Makes sure that the first type tree in the list of parent types is always a class. - * If the first parent is a trait, prepend its supertype to the list until it's a class. - */ - private def normalizeFirstParent(parents: List[Tree]): List[Tree] = parents match { - case first :: rest if treeInfo.isTraitRef(first) => - def explode(supertpt: Tree, acc: List[Tree]): List[Tree] = { - if (treeInfo.isTraitRef(supertpt)) { - val supertpt1 = typedType(supertpt) - if (!supertpt1.isErrorTyped) { - val supersupertpt = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus - return explode(supersupertpt, supertpt1 :: acc) - } - } - if (supertpt.tpe.typeSymbol == AnyClass) supertpt.tpe = AnyRefClass.tpe - supertpt :: acc + case _ => + if (!supertparams.isEmpty) + MissingTypeArgumentsParentTpeError(supertpt) } - explode(first, Nil) ++ rest - case _ => parents - } +/* experimental: early types as type arguments + val hasEarlyTypes = templ.body exists (treeInfo.isEarlyTypeDef) + val earlyMap = new EarlyMap(clazz) + List.mapConserve(supertpt :: mixins){ tpt => + val tpt1 = checkNoEscaping.privates(clazz, tpt) + if (hasEarlyTypes) tpt1 else tpt1 setType earlyMap(tpt1.tpe) + } +*/ - /** Certain parents are added in the parser before it is known whether - * that class also declared them as parents. For instance, this is an - * error unless we take corrective action here: - * - * case class Foo() extends Serializable - * - * So we strip the duplicates before typer. - */ - private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match { - case Nil => Nil - case x :: xs => - val sym = x.symbol - x :: fixDuplicateSyntheticParents( - if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) - else xs - ) - } + //Console.println("parents("+clazz") = "+supertpt :: mixins);//DEBUG - def parentTypes(templ: Template): List[Tree] = templ.parents match { - case Nil => List(atPos(templ.pos)(TypeTree(AnyRefClass.tpe))) - case first :: rest => - try { - val supertpts = fixDuplicateSyntheticParents(normalizeFirstParent( - typedParentType(first, templ, inMixinPosition = false) +: - (rest map (typedParentType(_, templ, inMixinPosition = true))))) - - // if that is required to infer the targs of a super call - // typedParentType calls typedPrimaryConstrBody to do the inferring typecheck - // as a side effect, that typecheck also assigns types to the fields underlying early vals - // however if inference is not required, the typecheck doesn't happen - // and therefore early fields have their type trees not assigned - // here we detect this situation and take preventive measures - if (treeInfo.hasUntypedPreSuperFields(templ.body)) - typedPrimaryConstrBody(templ)(superRef => EmptyTree) - - supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt)) - } catch { - case ex: TypeError => - // fallback in case of cyclic errors - // @H none of the tests enter here but I couldn't rule it out - // upd. @E when a definitions inherits itself, we end up here - // because `typedParentType` triggers `initialize` for parent types symbols - log("Type error calculating parents in template " + templ) - log("Error: " + ex) - ParentTypesError(templ, ex) - List(TypeTree(AnyRefClass.tpe)) + // Certain parents are added in the parser before it is known whether + // that class also declared them as parents. For instance, this is an + // error unless we take corrective action here: + // + // case class Foo() extends Serializable + // + // So we strip the duplicates before typer. + def fixDuplicates(remaining: List[Tree]): List[Tree] = remaining match { + case Nil => Nil + case x :: xs => + val sym = x.symbol + x :: fixDuplicates( + if (isPossibleSyntheticParent(sym)) xs filterNot (_.symbol == sym) + else xs + ) } - } + + fixDuplicates(supertpt :: mixins) mapConserve (tpt => checkNoEscaping.privates(clazz, tpt)) + } + catch { + case ex: TypeError => + // fallback in case of cyclic errors + // @H none of the tests enter here but I couldn't rule it out + log("Type error calculating parents in template " + templ) + log("Error: " + ex) + ParentTypesError(templ, ex) + List(TypeTree(AnyRefClass.tpe)) + } /**

Check that

*
    @@ -2021,8 +1891,7 @@ trait Typers extends Modes with Adaptations with Tags { // the following is necessary for templates generated later assert(clazz.info.decls != EmptyScope, clazz) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body) - if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore - validateParentClasses(parents1, selfType) + validateParentClasses(parents1, selfType) if (clazz.isCase) validateNoCaseAncestor(clazz) @@ -2032,28 +1901,9 @@ trait Typers extends Modes with Adaptations with Tags { if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) - val body = { - val body = - if (isPastTyper || reporter.hasErrors) templ.body - else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) - parents1.head match { - case CarriesSuperCallArgs(argss) => - if (clazz.isTrait) { - ConstrArgsInParentOfTraitError(parents1.head, clazz) - body - } else { - val pos = wrappingPos(parents1.head.pos, argss.flatten) - val primaryCtor = treeInfo.firstConstructor(templ.body) - val primaryCtor1 = (deriveDefDef(primaryCtor) { - case block @ Block(earlyVals :+ Apply(superRef, Nil), unit) => - val superCall = atPos(pos)((superRef /: argss)(Apply.apply)) - Block(earlyVals :+ superCall, unit) setPos pos - }) setPos pos - body map { case `primaryCtor` => primaryCtor1; case stat => stat } - } - case _ => body - } - } + val body = + if (isPastTyper || reporter.hasErrors) templ.body + else templ.body flatMap rewrappingWrapperTrees(namer.addDerivedTrees(Typer.this, _)) val body1 = typedStats(body, templ.symbol) @@ -2843,7 +2693,7 @@ trait Typers extends Modes with Adaptations with Tags { members foreach (m => anonClass.info.decls enter m.symbol) val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) + Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) } if (typedBlock.isErrorTyped) typedBlock diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 8f607e9f67..fa4bb7188c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -126,7 +126,7 @@ trait Unapplies extends ast.TreeDSL ModuleDef( Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin), cdef.name.toTermName, - Template(parents, emptyValDef, NoMods, Nil, body, cdef.impl.pos.focus)) + Template(parents, emptyValDef, NoMods, Nil, ListOfNil, body, cdef.impl.pos.focus)) } private val caseMods = Modifiers(SYNTHETIC | CASE) diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 0125f1b189..95135b84e0 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -230,6 +230,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => emptyValDef, NoMods, List(), + List(List()), List(methdef), NoPosition)) trace("wrapped: ")(showAttributed(moduledef, true, true, settings.Yshowsymkinds.value)) diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 98c1afb323..2cc848d458 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -342,9 +342,6 @@ abstract class TreeInfo { def preSuperFields(stats: List[Tree]): List[ValDef] = stats collect { case vd: ValDef if isEarlyValDef(vd) => vd } - def hasUntypedPreSuperFields(stats: List[Tree]): Boolean = - preSuperFields(stats) exists (_.tpt.isEmpty) - def isEarlyDef(tree: Tree) = tree match { case TypeDef(mods, _, _, _) => mods hasFlag PRESUPER case ValDef(mods, _, _, _) => mods hasFlag PRESUPER @@ -509,10 +506,6 @@ abstract class TreeInfo { def isSynthCaseSymbol(sym: Symbol) = sym hasAllFlags SYNTH_CASE_FLAGS def hasSynthCaseSymbol(t: Tree) = t.symbol != null && isSynthCaseSymbol(t.symbol) - def isTraitRef(tree: Tree): Boolean = { - val sym = if (tree.tpe != null) tree.tpe.typeSymbol else null - ((sym ne null) && sym.initialize.isTrait) - } /** Applications in Scala can have one of the following shapes: * diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 693b75fbbc..5522c47cfa 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -1034,9 +1034,6 @@ trait Trees extends api.Trees { self: SymbolTable => def New(tpe: Type, args: Tree*): Tree = ApplyConstructor(TypeTree(tpe), args.toList) - def New(tpe: Type, argss: List[List[Tree]]): Tree = - New(TypeTree(tpe), argss) - def New(sym: Symbol, args: Tree*): Tree = New(sym.tpe, args: _*) diff --git a/test/files/neg/cyclics-import.check b/test/files/neg/cyclics-import.check index be09fca374..ef355fab0a 100644 --- a/test/files/neg/cyclics-import.check +++ b/test/files/neg/cyclics-import.check @@ -3,4 +3,13 @@ Note: this is often due in part to a class depending on a definition nested with If applicable, you may wish to try moving some members into another object. import User.UserStatus._ ^ -one error found +cyclics-import.scala:12: error: not found: type Value + type UserStatus = Value + ^ +cyclics-import.scala:14: error: not found: value Value + val Active = Value("1") + ^ +cyclics-import.scala:15: error: not found: value Value + val Disabled = Value("2") + ^ +four errors found diff --git a/test/files/neg/protected-constructors.check b/test/files/neg/protected-constructors.check index e295917050..f137158ed6 100644 --- a/test/files/neg/protected-constructors.check +++ b/test/files/neg/protected-constructors.check @@ -19,4 +19,7 @@ protected-constructors.scala:15: error: class Foo3 in object Ding cannot be acce object Ding in package dingus where target is defined class Bar3 extends Ding.Foo3("abc") ^ -four errors found +protected-constructors.scala:15: error: too many arguments for constructor Object: ()Object + class Bar3 extends Ding.Foo3("abc") + ^ +5 errors found diff --git a/test/files/neg/t2148.check b/test/files/neg/t2148.check index 27b5dce507..5113b48e51 100644 --- a/test/files/neg/t2148.check +++ b/test/files/neg/t2148.check @@ -1,4 +1,4 @@ -t2148.scala:9: error: A is not a legal prefix for a constructor +t2148.scala:9: error: type A is not a stable prefix val b = new A with A#A1 ^ one error found diff --git a/test/files/neg/t409.check b/test/files/neg/t409.check index 0edc0d03cd..433d64d25d 100644 --- a/test/files/neg/t409.check +++ b/test/files/neg/t409.check @@ -1,4 +1,4 @@ -t409.scala:6: error: class Case1 needs to be a trait to be mixed in +t409.scala:6: error: traits or objects may not have parameters class Toto extends Expr with Case1(12); - ^ + ^ one error found diff --git a/test/files/neg/t5696.check b/test/files/neg/t5696.check index e0fb61b839..72b7781fc4 100644 --- a/test/files/neg/t5696.check +++ b/test/files/neg/t5696.check @@ -15,5 +15,5 @@ t5696.scala:38: error: too many argument lists for constructor invocation ^ t5696.scala:46: error: too many argument lists for constructor invocation object x extends G(1)(2) {} - ^ + ^ 6 errors found diff --git a/test/files/neg/t667.check b/test/files/neg/t667.check index e68c6dea00..d4367bc87b 100644 --- a/test/files/neg/t667.check +++ b/test/files/neg/t667.check @@ -1,4 +1,4 @@ -t667.scala:8: error: illegal cyclic reference involving class Ni +t667.scala:8: error: class Ni inherits itself class Ni extends super.Ni with Ni; - ^ + ^ one error found diff --git a/test/files/neg/t877.check b/test/files/neg/t877.check index c3d4ab6584..5f25bd439c 100644 --- a/test/files/neg/t877.check +++ b/test/files/neg/t877.check @@ -1,7 +1,7 @@ t877.scala:3: error: Invalid literal number trait Foo extends A(22A, Bug!) {} ^ -t877.scala:3: error: ')' expected but eof found. +t877.scala:3: error: parents of traits may not have parameters trait Foo extends A(22A, Bug!) {} - ^ + ^ two errors found diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index 8856fef5b3..7d8d181306 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -22,12 +22,12 @@ pluginsPt(?, Trees$Annotated) [7] pluginsPt(?, Trees$Apply) [8] pluginsPt(?, Trees$ApplyImplicitView) [2] pluginsPt(?, Trees$Assign) [7] -pluginsPt(?, Trees$Block) [4] +pluginsPt(?, Trees$Block) [7] pluginsPt(?, Trees$ClassDef) [2] pluginsPt(?, Trees$DefDef) [14] -pluginsPt(?, Trees$Ident) [51] +pluginsPt(?, Trees$Ident) [49] pluginsPt(?, Trees$If) [2] -pluginsPt(?, Trees$Literal) [16] +pluginsPt(?, Trees$Literal) [20] pluginsPt(?, Trees$New) [5] pluginsPt(?, Trees$PackageDef) [1] pluginsPt(?, Trees$Return) [1] @@ -37,9 +37,9 @@ pluginsPt(?, Trees$This) [20] pluginsPt(?, Trees$TypeApply) [3] pluginsPt(?, Trees$TypeBoundsTree) [2] pluginsPt(?, Trees$TypeDef) [1] -pluginsPt(?, Trees$TypeTree) [38] +pluginsPt(?, Trees$TypeTree) [37] pluginsPt(?, Trees$Typed) [1] -pluginsPt(?, Trees$ValDef) [21] +pluginsPt(?, Trees$ValDef) [23] pluginsPt(Any, Trees$Literal) [2] pluginsPt(Any, Trees$Typed) [1] pluginsPt(Array[Any], Trees$ArrayValue) [1] @@ -53,7 +53,7 @@ pluginsPt(Int @testAnn, Trees$Literal) [1] pluginsPt(Int, Trees$Apply) [1] pluginsPt(Int, Trees$Ident) [2] pluginsPt(Int, Trees$If) [1] -pluginsPt(Int, Trees$Literal) [5] +pluginsPt(Int, Trees$Literal) [6] pluginsPt(Int, Trees$Select) [3] pluginsPt(List, Trees$Apply) [1] pluginsPt(List[Any], Trees$Select) [1] @@ -82,8 +82,8 @@ pluginsTypeSig(value lub1, Trees$ValDef) [2] pluginsTypeSig(value lub2, Trees$ValDef) [2] pluginsTypeSig(value param, Trees$ValDef) [2] pluginsTypeSig(value str, Trees$ValDef) [1] -pluginsTypeSig(value x, Trees$ValDef) [4] -pluginsTypeSig(value y, Trees$ValDef) [4] +pluginsTypeSig(value x, Trees$ValDef) [5] +pluginsTypeSig(value y, Trees$ValDef) [5] pluginsTypeSig(variable count, Trees$ValDef) [3] pluginsTypeSigAccessor(value annotField) [1] pluginsTypeSigAccessor(value inferField) [1] @@ -110,7 +110,7 @@ pluginsTyped(, Trees$ClassDef) [2] pluginsTyped(, Trees$DefDef) [14] pluginsTyped(, Trees$PackageDef) [1] pluginsTyped(, Trees$TypeDef) [1] -pluginsTyped(, Trees$ValDef) [21] +pluginsTyped(, Trees$ValDef) [23] pluginsTyped(, Trees$Ident) [1] pluginsTyped(=> Boolean @testAnn, Trees$Select) [1] pluginsTyped(=> Double, Trees$Select) [4] @@ -124,7 +124,7 @@ pluginsTyped(A, Trees$TypeTree) [4] pluginsTyped(A.super.type, Trees$Super) [1] pluginsTyped(A.this.type, Trees$This) [11] pluginsTyped(Any, Trees$TypeTree) [1] -pluginsTyped(AnyRef, Trees$Select) [4] +pluginsTyped(AnyRef, Trees$Select) [2] pluginsTyped(Array[Any], Trees$ArrayValue) [1] pluginsTyped(Boolean @testAnn, Trees$Select) [1] pluginsTyped(Boolean @testAnn, Trees$TypeTree) [4] @@ -137,12 +137,12 @@ pluginsTyped(Int @testAnn, Trees$TypeTree) [2] pluginsTyped(Int @testAnn, Trees$Typed) [2] pluginsTyped(Int(0), Trees$Literal) [3] pluginsTyped(Int(1) @testAnn, Trees$Typed) [1] -pluginsTyped(Int(1), Trees$Literal) [8] +pluginsTyped(Int(1), Trees$Literal) [9] pluginsTyped(Int(2), Trees$Literal) [1] pluginsTyped(Int, Trees$Apply) [1] pluginsTyped(Int, Trees$Ident) [2] pluginsTyped(Int, Trees$If) [2] -pluginsTyped(Int, Trees$Select) [15] +pluginsTyped(Int, Trees$Select) [17] pluginsTyped(Int, Trees$TypeTree) [13] pluginsTyped(List, Trees$Apply) [1] pluginsTyped(List, Trees$Select) [1] @@ -160,27 +160,26 @@ pluginsTyped(String("huhu"), Trees$Literal) [1] pluginsTyped(String("str") @testAnn, Trees$Typed) [1] pluginsTyped(String("str"), Trees$Literal) [1] pluginsTyped(String("str"), Trees$Typed) [1] -pluginsTyped(String("two"), Trees$Literal) [2] +pluginsTyped(String("two"), Trees$Literal) [3] pluginsTyped(String, Trees$Apply) [2] pluginsTyped(String, Trees$Block) [2] pluginsTyped(String, Trees$Ident) [1] pluginsTyped(String, Trees$Select) [9] -pluginsTyped(String, Trees$TypeTree) [7] +pluginsTyped(String, Trees$TypeTree) [8] pluginsTyped(Unit, Trees$Apply) [2] pluginsTyped(Unit, Trees$Assign) [8] -pluginsTyped(Unit, Trees$Block) [4] +pluginsTyped(Unit, Trees$Block) [7] pluginsTyped(Unit, Trees$If) [1] -pluginsTyped(Unit, Trees$Literal) [5] +pluginsTyped(Unit, Trees$Literal) [8] pluginsTyped(Unit, Trees$TypeTree) [1] pluginsTyped([A](xs: A*)List[A], Trees$Select) [1] pluginsTyped([T <: Int]=> Int, Trees$Select) [1] pluginsTyped([T0 >: ? <: ?]()T0, Trees$Select) [1] pluginsTyped([T](xs: Array[T])scala.collection.mutable.WrappedArray[T], Trees$Select) [1] -pluginsTyped(annotation.type, Trees$Select) [4] +pluginsTyped(annotation.type, Trees$Select) [2] pluginsTyped(math.type, Trees$Select) [9] pluginsTyped(scala.annotation.Annotation, Trees$Apply) [1] pluginsTyped(scala.annotation.TypeConstraint, Trees$Select) [4] -pluginsTyped(scala.annotation.TypeConstraint, Trees$TypeTree) [2] pluginsTyped(scala.collection.immutable.List.type, Trees$Select) [2] pluginsTyped(scala.collection.immutable.StringOps, Trees$ApplyImplicitView) [2] pluginsTyped(scala.collection.mutable.WrappedArray[Any], Trees$Apply) [1] -- cgit v1.2.3 From 1f838edb02f6e5ced6641504a96cca3ec4c1fa7a Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 5 Feb 2013 12:09:06 +0100 Subject: [nomaster] verifies compat with 2.10.0 Makes sure that the compiler understands the 2.10.0 format of super constructor calls after the recent revert from the typemacro-compatible format. --- test/files/run/t7064-old-style-supercalls.check | 1 + test/files/run/t7064-old-style-supercalls.scala | 48 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 test/files/run/t7064-old-style-supercalls.check create mode 100644 test/files/run/t7064-old-style-supercalls.scala (limited to 'test') diff --git a/test/files/run/t7064-old-style-supercalls.check b/test/files/run/t7064-old-style-supercalls.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t7064-old-style-supercalls.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t7064-old-style-supercalls.scala b/test/files/run/t7064-old-style-supercalls.scala new file mode 100644 index 0000000000..cffa7b1888 --- /dev/null +++ b/test/files/run/t7064-old-style-supercalls.scala @@ -0,0 +1,48 @@ +import scala.reflect.runtime.universe._ +import Flag._ +import definitions._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.ToolBox +import scala.tools.reflect.Eval + +object Test extends App { + val PARAMACCESSOR = (1L << 29).asInstanceOf[FlagSet] + + // these trees can be acquired by running the following incantation: + // echo 'class C(val x: Int); class D extends C(2)' > foo.scala + // ./scalac -Xprint:parser -Yshow-trees-stringified -Yshow-trees-compact foo.scala + + val c = ClassDef( + Modifiers(), newTypeName("C"), List(), + Template( + List(Select(Ident(ScalaPackage), newTypeName("AnyRef"))), + emptyValDef, + List( + ValDef(Modifiers(PARAMACCESSOR), newTermName("x"), Ident(newTypeName("Int")), EmptyTree), + DefDef( + Modifiers(), + nme.CONSTRUCTOR, + List(), + List(List(ValDef(Modifiers(PARAM | PARAMACCESSOR), newTermName("x"), Ident(newTypeName("Int")), EmptyTree))), + TypeTree(), + Block( + List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), + Literal(Constant(()))))))) + val d = ClassDef( + Modifiers(), newTypeName("D"), List(), + Template( + List(Ident(newTypeName("C"))), + emptyValDef, + List( + DefDef( + Modifiers(), + nme.CONSTRUCTOR, + List(), + List(List()), + TypeTree(), + Block( + List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List(Literal(Constant(2))))), + Literal(Constant(()))))))) + val result = Select(Apply(Select(New(Ident(newTypeName("D"))), nme.CONSTRUCTOR), List()), newTermName("x")) + println(cm.mkToolBox().eval(Block(List(c, d), result))) +} \ No newline at end of file -- cgit v1.2.3 From b43ae58f312e4769de5a6942b4cb820cd609f31b Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 5 Feb 2013 00:19:05 +0100 Subject: introduces an exhaustive java-to-scala test Originally composed to accommodate pull request feedback, this test has uncovered a handful of bugs in FromJavaClassCompleter, namely: * SI-7071 non-public ctors get lost * SI-7072 inner classes are read incorrectly I'm leaving the incorrect results of FromJavaClassCompleters in the check file, so that we get notified when something changes there. --- test/files/run/t6989.check | 200 +++++++++++++++++++++++++++++++++- test/files/run/t6989/JavaClass_1.java | 42 ++++++- test/files/run/t6989/Test_2.scala | 37 +++++-- 3 files changed, 262 insertions(+), 17 deletions(-) (limited to 'test') diff --git a/test/files/run/t6989.check b/test/files/run/t6989.check index 06bc3b7200..8943792115 100644 --- a/test/files/run/t6989.check +++ b/test/files/run/t6989.check @@ -1,24 +1,216 @@ ============ -class JavaClass_1 +sym = class PackagePrivateJavaClass, signature = ClassInfoType(...), owner = package foo isPrivate = false isProtected = false isPublic = false privateWithin = package foo ============ -variable x +sym = constructor PackagePrivateJavaClass, signature = (x$1: Int, x$2: Int)foo.PackagePrivateJavaClass, owner = class PackagePrivateJavaClass +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = variable privateField, signature = Int, owner = class PackagePrivateJavaClass +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = method privateMethod, signature = ()Unit, owner = class PackagePrivateJavaClass +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = variable protectedField, signature = Int, owner = class PackagePrivateJavaClass +isPrivate = false +isProtected = true +isPublic = false +privateWithin = package foo +============ +sym = method protectedMethod, signature = ()Unit, owner = class PackagePrivateJavaClass +isPrivate = false +isProtected = true +isPublic = false +privateWithin = package foo +============ +sym = variable publicField, signature = Int, owner = class PackagePrivateJavaClass +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = method publicMethod, signature = ()Unit, owner = class PackagePrivateJavaClass +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = object PackagePrivateJavaClass, signature = foo.PackagePrivateJavaClass.type, owner = package foo +isPrivate = false +isProtected = false +isPublic = false +privateWithin = package foo +============ +sym = variable privateStaticField, signature = Int, owner = object PackagePrivateJavaClass +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = method privateStaticMethod, signature = ()Unit, owner = object PackagePrivateJavaClass +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = variable protectedStaticField, signature = Int, owner = object PackagePrivateJavaClass +isPrivate = false +isProtected = true +isPublic = false +privateWithin = package foo +============ +sym = method protectedStaticMethod, signature = ()Unit, owner = object PackagePrivateJavaClass +isPrivate = false +isProtected = true +isPublic = false +privateWithin = package foo +============ +sym = variable publicStaticField, signature = Int, owner = object PackagePrivateJavaClass +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = method publicStaticMethod, signature = ()Unit, owner = object PackagePrivateJavaClass +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = class JavaClass_1, signature = ClassInfoType(...), owner = package foo +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = class $PrivateJavaClass, signature = ClassInfoType(...), owner = class JavaClass_1 +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = value this$0, signature = foo.JavaClass_1, owner = class $PrivateJavaClass +isPrivate = false +isProtected = false +isPublic = false +privateWithin = package foo +============ +sym = object $PrivateJavaClass, signature = JavaClass_1.this.$PrivateJavaClass.type, owner = class JavaClass_1 isPrivate = true isProtected = false isPublic = false privateWithin = ============ -variable y +sym = class $ProtectedJavaClass, signature = ClassInfoType(...), owner = class JavaClass_1 isPrivate = false isProtected = true isPublic = false privateWithin = package foo ============ -variable z +sym = value this$0, signature = foo.JavaClass_1, owner = class $ProtectedJavaClass +isPrivate = false +isProtected = false +isPublic = false +privateWithin = package foo +============ +sym = object $ProtectedJavaClass, signature = JavaClass_1.this.$ProtectedJavaClass.type, owner = class JavaClass_1 +isPrivate = false +isProtected = false +isPublic = false +privateWithin = package foo +============ +sym = class $PublicJavaClass, signature = ClassInfoType(...), owner = class JavaClass_1 +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = constructor $PublicJavaClass, signature = (x$1: foo.JavaClass_1)JavaClass_1.this.$PublicJavaClass, owner = class $PublicJavaClass +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = value this$0, signature = foo.JavaClass_1, owner = class $PublicJavaClass +isPrivate = false +isProtected = false +isPublic = false +privateWithin = package foo +============ +sym = object $PublicJavaClass, signature = JavaClass_1.this.$PublicJavaClass.type, owner = class JavaClass_1 +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = constructor JavaClass_1, signature = ()foo.JavaClass_1, owner = class JavaClass_1 +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = object JavaClass_1, signature = foo.JavaClass_1.type, owner = package foo isPrivate = false isProtected = false isPublic = true privateWithin = +============ +sym = class PrivateStaticJavaClass, signature = ClassInfoType(...), owner = object JavaClass_1 +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = object PrivateStaticJavaClass, signature = foo.JavaClass_1.PrivateStaticJavaClass.type, owner = object JavaClass_1 +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = class ProtectedStaticJavaClass, signature = ClassInfoType(...), owner = object JavaClass_1 +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = object ProtectedStaticJavaClass, signature = foo.JavaClass_1.ProtectedStaticJavaClass.type, owner = object JavaClass_1 +isPrivate = true +isProtected = false +isPublic = false +privateWithin = +============ +sym = class PublicStaticJavaClass, signature = ClassInfoType(...), owner = object JavaClass_1 +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = constructor PublicStaticJavaClass, signature = ()foo.JavaClass_1.PublicStaticJavaClass, owner = class PublicStaticJavaClass +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = object PublicStaticJavaClass, signature = foo.JavaClass_1.PublicStaticJavaClass.type, owner = object JavaClass_1 +isPrivate = false +isProtected = false +isPublic = true +privateWithin = +============ +sym = variable staticField, signature = Int, owner = object JavaClass_1 +isPrivate = true +isProtected = false +isPublic = false +privateWithin = diff --git a/test/files/run/t6989/JavaClass_1.java b/test/files/run/t6989/JavaClass_1.java index 61253dfe6f..eb26a08700 100644 --- a/test/files/run/t6989/JavaClass_1.java +++ b/test/files/run/t6989/JavaClass_1.java @@ -1,7 +1,41 @@ package foo; -class JavaClass_1 { - private int x = 0; - protected String y = ""; - public float z = 0.0f; +// Originally composed to accommodate pull request feedback, this test has +// uncovered a handful of bugs in FromJavaClassCompleter, namely: +// * SI-7071 non-public ctors get lost +// * SI-7072 inner classes are read incorrectly + +// I'm leaving the incorrect results of FromJavaClassCompleters in the check +// file, so that we get notified when something changes there. + +class PackagePrivateJavaClass { + private int privateField = 0; + protected int protectedField = 1; + public int publicField = 2; + + private static int privateStaticField = 3; + protected static int protectedStaticField = 4; + public static int publicStaticField = 5; + + private void privateMethod() {} + protected void protectedMethod() {} + public void publicMethod() {} + + private static void privateStaticMethod() {} + protected static void protectedStaticMethod() {} + public static void publicStaticMethod() {} + + private PackagePrivateJavaClass() {} + protected PackagePrivateJavaClass(int x) {} + public PackagePrivateJavaClass(int x, int y) {} +} + +public class JavaClass_1 { + private class PrivateJavaClass {} + private static class PrivateStaticJavaClass {} + protected class ProtectedJavaClass {} + private static class ProtectedStaticJavaClass {} + public class PublicJavaClass {} + public static class PublicStaticJavaClass {} + private static int staticField = 0; } \ No newline at end of file diff --git a/test/files/run/t6989/Test_2.scala b/test/files/run/t6989/Test_2.scala index ba88e5cce3..e48e82422d 100644 --- a/test/files/run/t6989/Test_2.scala +++ b/test/files/run/t6989/Test_2.scala @@ -1,21 +1,40 @@ import scala.reflect.runtime.universe._ +// Originally composed to accommodate pull request feedback, this test has +// uncovered a handful of bugs in FromJavaClassCompleter, namely: +// * SI-7071 non-public ctors get lost +// * SI-7072 inner classes are read incorrectly + +// I'm leaving the incorrect results of FromJavaClassCompleters in the check +// file, so that we get notified when something changes there. + package object foo { - def test(sym: Symbol) = { + def testAll(): Unit = { + test(typeOf[foo.PackagePrivateJavaClass].typeSymbol) + test(typeOf[foo.PackagePrivateJavaClass].typeSymbol.companionSymbol) + test(typeOf[foo.JavaClass_1].typeSymbol) + test(typeOf[foo.JavaClass_1].typeSymbol.companionSymbol) + } + + def test(sym: Symbol): Unit = { + printSymbolDetails(sym) + if (sym.isClass || sym.isModule) { + sym.typeSignature.declarations.toList.sortBy(_.name.toString) foreach test + } + } + + def printSymbolDetails(sym: Symbol): Unit = { + def stableSignature(sym: Symbol) = sym.typeSignature match { + case ClassInfoType(_, _, _) => "ClassInfoType(...)" + case tpe => tpe.toString + } println("============") - println(sym) + println(s"sym = $sym, signature = ${stableSignature(sym)}, owner = ${sym.owner}") println(s"isPrivate = ${sym.isPrivate}") println(s"isProtected = ${sym.isProtected}") println(s"isPublic = ${sym.isPublic}") println(s"privateWithin = ${sym.privateWithin}") } - - def testAll() = { - test(typeOf[foo.JavaClass_1].typeSymbol) - test(typeOf[foo.JavaClass_1].declaration(newTermName("x"))) - test(typeOf[foo.JavaClass_1].declaration(newTermName("y"))) - test(typeOf[foo.JavaClass_1].declaration(newTermName("z"))) - } } object Test extends App { -- cgit v1.2.3 From f784fbfbce7c1426fe90706f11096ea1b826e88c Mon Sep 17 00:00:00 2001 From: Eugene Vigdorchik Date: Wed, 6 Feb 2013 11:02:42 +0400 Subject: Add a request to presentation compiler to fetch doc comment information. Refactor scaladoc base functionality to allow it to be mixed in with Global in the IDE. --- .../tools/nsc/doc/base/CommentFactoryBase.scala | 46 +++---- .../tools/nsc/interactive/CompilerControl.scala | 22 +++ src/compiler/scala/tools/nsc/interactive/Doc.scala | 59 -------- .../scala/tools/nsc/interactive/Global.scala | 150 ++++++++++++++------- .../scala/tools/nsc/interactive/Picklers.scala | 7 +- .../tests/core/PresentationCompilerInstance.scala | 3 +- test/files/presentation/doc.check | 1 + test/files/presentation/doc.scala | 71 ---------- test/files/presentation/doc/doc.scala | 91 +++++++++++++ 9 files changed, 246 insertions(+), 204 deletions(-) delete mode 100755 src/compiler/scala/tools/nsc/interactive/Doc.scala mode change 100755 => 100644 test/files/presentation/doc.check delete mode 100755 test/files/presentation/doc.scala create mode 100755 test/files/presentation/doc/doc.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala index f60d56d9bb..f509c63ba0 100755 --- a/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/compiler/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -100,26 +100,26 @@ trait CommentFactoryBase { this: MemberLookupBase => } - protected val endOfText = '\u0003' - protected val endOfLine = '\u000A' + private val endOfText = '\u0003' + private val endOfLine = '\u000A' /** Something that should not have happened, happened, and Scaladoc should exit. */ - protected def oops(msg: String): Nothing = + private def oops(msg: String): Nothing = throw FatalError("program logic: " + msg) /** The body of a line, dropping the (optional) start star-marker, * one leading whitespace and all trailing whitespace. */ - protected val CleanCommentLine = + private val CleanCommentLine = new Regex("""(?:\s*\*\s?)?(.*)""") /** Dangerous HTML tags that should be replaced by something safer, * such as wiki syntax, or that should be dropped. */ - protected val DangerousTags = + private val DangerousTags = new Regex("""<(/?(div|ol|ul|li|h[1-6]|p))( [^>]*)?/?>|""") /** Maps a dangerous HTML tag to a safe wiki replacement, or an empty string * if it cannot be salvaged. */ - protected def htmlReplacement(mtch: Regex.Match): String = mtch.group(1) match { + private def htmlReplacement(mtch: Regex.Match): String = mtch.group(1) match { case "p" | "div" => "\n\n" case "h1" => "\n= " case "/h1" => " =\n" @@ -135,11 +135,11 @@ trait CommentFactoryBase { this: MemberLookupBase => /** Javadoc tags that should be replaced by something useful, such as wiki * syntax, or that should be dropped. */ - protected val JavadocTags = + private val JavadocTags = new Regex("""\{\@(code|docRoot|inheritDoc|link|linkplain|literal|value)([^}]*)\}""") /** Maps a javadoc tag to a useful wiki replacement, or an empty string if it cannot be salvaged. */ - protected def javadocReplacement(mtch: Regex.Match): String = mtch.group(1) match { + private def javadocReplacement(mtch: Regex.Match): String = mtch.group(1) match { case "code" => "`" + mtch.group(2) + "`" case "docRoot" => "" case "inheritDoc" => "" @@ -151,41 +151,41 @@ trait CommentFactoryBase { this: MemberLookupBase => } /** Safe HTML tags that can be kept. */ - protected val SafeTags = + private val SafeTags = new Regex("""((&\w+;)|(&#\d+;)|(]*)?/?>))""") - protected val safeTagMarker = '\u000E' + private val safeTagMarker = '\u000E' /** A Scaladoc tag not linked to a symbol and not followed by text */ - protected val SingleTag = + private val SingleTagRegex = new Regex("""\s*@(\S+)\s*""") /** A Scaladoc tag not linked to a symbol. Returns the name of the tag, and the rest of the line. */ - protected val SimpleTag = + private val SimpleTagRegex = new Regex("""\s*@(\S+)\s+(.*)""") /** A Scaladoc tag linked to a symbol. Returns the name of the tag, the name * of the symbol, and the rest of the line. */ - protected val SymbolTag = + private val SymbolTagRegex = new Regex("""\s*@(param|tparam|throws|groupdesc|groupname|groupprio)\s+(\S*)\s*(.*)""") /** The start of a scaladoc code block */ - protected val CodeBlockStart = + private val CodeBlockStartRegex = new Regex("""(.*?)((?:\{\{\{)|(?:\u000E]*)?>\u000E))(.*)""") /** The end of a scaladoc code block */ - protected val CodeBlockEnd = + private val CodeBlockEndRegex = new Regex("""(.*?)((?:\}\}\})|(?:\u000E\u000E))(.*)""") /** A key used for a tag map. The key is built from the name of the tag and * from the linked symbol if the tag has one. * Equality on tag keys is structural. */ - protected sealed abstract class TagKey { + private sealed abstract class TagKey { def name: String } - protected final case class SimpleTagKey(name: String) extends TagKey - protected final case class SymbolTagKey(name: String, symbol: String) extends TagKey + private final case class SimpleTagKey(name: String) extends TagKey + private final case class SymbolTagKey(name: String, symbol: String) extends TagKey /** Parses a raw comment string into a `Comment` object. * @param comment The expanded comment string (including start and end markers) to be parsed. @@ -231,7 +231,7 @@ trait CommentFactoryBase { this: MemberLookupBase => inCodeBlock: Boolean ): Comment = remaining match { - case CodeBlockStart(before, marker, after) :: ls if (!inCodeBlock) => + case CodeBlockStartRegex(before, marker, after) :: ls if (!inCodeBlock) => if (!before.trim.isEmpty && !after.trim.isEmpty) parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, false) else if (!before.trim.isEmpty) @@ -250,7 +250,7 @@ trait CommentFactoryBase { this: MemberLookupBase => parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, true) } - case CodeBlockEnd(before, marker, after) :: ls => + case CodeBlockEndRegex(before, marker, after) :: ls => if (!before.trim.isEmpty && !after.trim.isEmpty) parse0(docBody, tags, lastTagKey, before :: marker :: after :: ls, true) if (!before.trim.isEmpty) @@ -269,17 +269,17 @@ trait CommentFactoryBase { this: MemberLookupBase => parse0(docBody append endOfLine append marker, tags, lastTagKey, ls, false) } - case SymbolTag(name, sym, body) :: ls if (!inCodeBlock) => + case SymbolTagRegex(name, sym, body) :: ls if (!inCodeBlock) => val key = SymbolTagKey(name, sym) val value = body :: tags.getOrElse(key, Nil) parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case SimpleTag(name, body) :: ls if (!inCodeBlock) => + case SimpleTagRegex(name, body) :: ls if (!inCodeBlock) => val key = SimpleTagKey(name) val value = body :: tags.getOrElse(key, Nil) parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case SingleTag(name) :: ls if (!inCodeBlock) => + case SingleTagRegex(name) :: ls if (!inCodeBlock) => val key = SimpleTagKey(name) val value = "" :: tags.getOrElse(key, Nil) parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index b4af8f00d6..9c64e8e31e 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -157,6 +157,20 @@ trait CompilerControl { self: Global => def askLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) = postWorkItem(new AskLinkPosItem(sym, source, response)) + /** Sets sync var `response` to doc comment information for a given symbol. + * + * @param sym The symbol whose doc comment should be retrieved (might come from a classfile) + * @param site The place where sym is observed. + * @param source The source file that's supposed to contain the definition + * @param response A response that will be set to the following: + * If `source` contains a definition of a given symbol that has a doc comment, + * the (expanded, raw, position) triplet for a comment, otherwise ("", "", NoPosition). + * Note: This operation does not automatically load `source`. If `source` + * is unloaded, it stays that way. + */ + def askDocComment(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]) = + postWorkItem(new AskDocCommentItem(sym, site, source, response)) + /** Sets sync var `response` to list of members that are visible * as members of the tree enclosing `pos`, possibly reachable by an implicit. * @pre source is loaded @@ -374,6 +388,14 @@ trait CompilerControl { self: Global => response raise new MissingResponse } + case class AskDocCommentItem(val sym: Symbol, val site: Symbol, val source: SourceFile, response: Response[(String, String, Position)]) extends WorkItem { + def apply() = self.getDocComment(sym, site, source, response) + override def toString = "doc comment "+sym+" in "+source + + def raiseMissing() = + response raise new MissingResponse + } + case class AskLoadedTypedItem(val source: SourceFile, response: Response[Tree]) extends WorkItem { def apply() = self.waitLoadedTyped(source, response, this.onCompilerThread) override def toString = "wait loaded & typed "+source diff --git a/src/compiler/scala/tools/nsc/interactive/Doc.scala b/src/compiler/scala/tools/nsc/interactive/Doc.scala deleted file mode 100755 index ad28a28105..0000000000 --- a/src/compiler/scala/tools/nsc/interactive/Doc.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2007-2012 LAMP/EPFL - * @author Eugene Vigdorchik - */ - -package scala.tools.nsc -package interactive - -import doc.base._ -import comment._ -import scala.xml.NodeSeq - -sealed trait DocResult -final case class UrlResult(url: String) extends DocResult -final case class HtmlResult(comment: Comment) extends DocResult - -abstract class Doc(val settings: doc.Settings) extends MemberLookupBase with CommentFactoryBase { - - override val global: interactive.Global - import global._ - - def chooseLink(links: List[LinkTo]): LinkTo - - override def internalLink(sym: Symbol, site: Symbol): Option[LinkTo] = - ask { () => - if (sym.isClass || sym.isModule) - Some(LinkToTpl(sym)) - else - if ((site.isClass || site.isModule) && site.info.members.toList.contains(sym)) - Some(LinkToMember(sym, site)) - else - None - } - - override def toString(link: LinkTo) = ask { () => - link match { - case LinkToMember(mbr: Symbol, site: Symbol) => - mbr.signatureString + " in " + site.toString - case LinkToTpl(sym: Symbol) => sym.toString - case _ => link.toString - } - } - - def retrieve(sym: Symbol, site: Symbol): Option[DocResult] = { - val sig = ask { () => externalSignature(sym) } - findExternalLink(sym, sig) map { link => UrlResult(link.url) } orElse { - val resp = new Response[Tree] - // Ensure docComment tree is type-checked. - val pos = ask { () => docCommentPos(sym) } - askTypeAt(pos, resp) - resp.get.left.toOption flatMap { _ => - ask { () => - val comment = parseAtSymbol(expandedDocComment(sym), rawDocComment(sym), pos, Some(site)) - Some(HtmlResult(comment)) - } - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 4ab7b98b3d..c93f258997 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -462,6 +462,9 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") compileRunner } + private def ensureUpToDate(unit: RichCompilationUnit) = + if (!unit.isUpToDate && unit.status != JustParsed) reset(unit) // reparse previously typechecked units. + /** Compile all loaded source files in the order given by `allSources`. */ private[interactive] final def backgroundCompile() { @@ -474,7 +477,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") // ensure all loaded units are parsed for (s <- allSources; unit <- getUnit(s)) { // checkForMoreWork(NoPosition) // disabled, as any work done here would be in an inconsistent state - if (!unit.isUpToDate && unit.status != JustParsed) reset(unit) // reparse previously typechecked units. + ensureUpToDate(unit) parseAndEnter(unit) serviceParsedEntered() } @@ -727,7 +730,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") try { debugLog("starting targeted type check") typeCheck(unit) - println("tree not found at "+pos) +// println("tree not found at "+pos) EmptyTree } catch { case ex: TyperResult => new Locator(pos) locateIn ex.tree @@ -758,64 +761,69 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") respond(response)(typedTree(source, forceReload)) } - /** Implements CompilerControl.askLinkPos */ - private[interactive] def getLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) { + private def withTempUnit[T](source: SourceFile)(f: RichCompilationUnit => T): T = + getUnit(source) match { + case None => + reloadSources(List(source)) + try f(getUnit(source).get) + finally afterRunRemoveUnitOf(source) + case Some(unit) => + f(unit) + } - /** Find position of symbol `sym` in unit `unit`. Pre: `unit is loaded. */ - def findLinkPos(unit: RichCompilationUnit): Position = { - val originalTypeParams = sym.owner.typeParams - parseAndEnter(unit) - val pre = adaptToNewRunMap(ThisType(sym.owner)) - val rawsym = pre.typeSymbol.info.decl(sym.name) - val newsym = rawsym filter { alt => - sym.isType || { - try { - val tp1 = pre.memberType(alt) onTypeError NoType - val tp2 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, sym.owner.typeParams) - matchesType(tp1, tp2, false) || { - debugLog(s"getLinkPos matchesType($tp1, $tp2) failed") - val tp3 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, alt.owner.typeParams) - matchesType(tp1, tp3, false) || { - debugLog(s"getLinkPos fallback matchesType($tp1, $tp3) failed") - false - } - } - } - catch { - case ex: ControlThrowable => throw ex - case ex: Throwable => - println("error in hyperlinking: " + ex) - ex.printStackTrace() + /** Find a 'mirror' of symbol `sym` in unit `unit`. Pre: `unit is loaded. */ + private def findMirrorSymbol(sym: Symbol, unit: RichCompilationUnit): Symbol = { + val originalTypeParams = sym.owner.typeParams + ensureUpToDate(unit) + parseAndEnter(unit) + val pre = adaptToNewRunMap(ThisType(sym.owner)) + val rawsym = pre.typeSymbol.info.decl(sym.name) + val newsym = rawsym filter { alt => + sym.isType || { + try { + val tp1 = pre.memberType(alt) onTypeError NoType + val tp2 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, sym.owner.typeParams) + matchesType(tp1, tp2, false) || { + debugLog(s"findMirrorSymbol matchesType($tp1, $tp2) failed") + val tp3 = adaptToNewRunMap(sym.tpe) substSym (originalTypeParams, alt.owner.typeParams) + matchesType(tp1, tp3, false) || { + debugLog(s"findMirrorSymbol fallback matchesType($tp1, $tp3) failed") false + } } } - } - if (newsym == NoSymbol) { - if (rawsym.exists && !rawsym.isOverloaded) rawsym.pos - else { - debugLog("link not found " + sym + " " + source + " " + pre) - NoPosition + catch { + case ex: ControlThrowable => throw ex + case ex: Throwable => + debugLog("error in findMirrorSymbol: " + ex) + ex.printStackTrace() + false } - } else if (newsym.isOverloaded) { - settings.uniqid.value = true - debugLog("link ambiguous " + sym + " " + source + " " + pre + " " + newsym.alternatives) - NoPosition - } else { - debugLog("link found for " + newsym + ": " + newsym.pos) - newsym.pos } } + if (newsym == NoSymbol) { + if (rawsym.exists && !rawsym.isOverloaded) rawsym + else { + debugLog("mirror not found " + sym + " " + unit.source + " " + pre) + NoSymbol + } + } else if (newsym.isOverloaded) { + settings.uniqid.value = true + debugLog("mirror ambiguous " + sym + " " + unit.source + " " + pre + " " + newsym.alternatives) + NoSymbol + } else { + debugLog("mirror found for " + newsym + ": " + newsym.pos) + newsym + } + } + /** Implements CompilerControl.askLinkPos */ + private[interactive] def getLinkPos(sym: Symbol, source: SourceFile, response: Response[Position]) { informIDE("getLinkPos "+sym+" "+source) respond(response) { if (sym.owner.isClass) { - getUnit(source) match { - case None => - reloadSources(List(source)) - try findLinkPos(getUnit(source).get) - finally afterRunRemoveUnitOf(source) - case Some(unit) => - findLinkPos(unit) + withTempUnit(source){ u => + findMirrorSymbol(sym, u).pos } } else { debugLog("link not in class "+sym+" "+source+" "+sym.owner) @@ -824,6 +832,50 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } } + /** Implements CompilerControl.askDocComment */ + private[interactive] def getDocComment(sym: Symbol, site: Symbol, source: SourceFile, response: Response[(String, String, Position)]) { + informIDE("getDocComment "+sym+" "+source) + respond(response) { + withTempUnit(source){ u => + val mirror = findMirrorSymbol(sym, u) + if (mirror eq NoSymbol) + ("", "", NoPosition) + else { + forceDocComment(mirror, u) + (expandedDocComment(mirror), rawDocComment(mirror), docCommentPos(mirror)) + } + } + } + } + + private def forceDocComment(sym: Symbol, unit: RichCompilationUnit) { + // Either typer has been run and we don't find DocDef, + // or we force the targeted typecheck here. + // In both cases doc comment maps should be filled for the subject symbol. + val docTree = + unit.body find { + case DocDef(_, defn) if defn.symbol eq sym => true + case _ => false + } + + for (t <- docTree) { + debugLog("Found DocDef tree for "+sym) + // Cannot get a typed tree at position since DocDef range is transparent. + val prevPos = unit.targetPos + val prevInterruptsEnabled = interruptsEnabled + try { + unit.targetPos = t.pos + interruptsEnabled = true + typeCheck(unit) + } catch { + case _: TyperResult => // ignore since we are after the side effect. + } finally { + unit.targetPos = prevPos + interruptsEnabled = prevInterruptsEnabled + } + } + } + def stabilizedType(tree: Tree): Type = tree match { case Ident(_) if tree.symbol.isStable => singleType(NoPrefix, tree.symbol) diff --git a/src/compiler/scala/tools/nsc/interactive/Picklers.scala b/src/compiler/scala/tools/nsc/interactive/Picklers.scala index ffad19fbaa..84cb03c140 100644 --- a/src/compiler/scala/tools/nsc/interactive/Picklers.scala +++ b/src/compiler/scala/tools/nsc/interactive/Picklers.scala @@ -165,6 +165,11 @@ trait Picklers { self: Global => .wrapped { case sym ~ source => new AskLinkPosItem(sym, source, new Response) } { item => item.sym ~ item.source } .asClass (classOf[AskLinkPosItem]) + implicit def askDocCommentItem: CondPickler[AskDocCommentItem] = + (pkl[Symbol] ~ pkl[Symbol] ~ pkl[SourceFile]) + .wrapped { case sym ~ site ~ source => new AskDocCommentItem(sym, site, source, new Response) } { item => item.sym ~ item.site ~ item.source } + .asClass (classOf[AskDocCommentItem]) + implicit def askLoadedTypedItem: CondPickler[AskLoadedTypedItem] = pkl[SourceFile] .wrapped { source => new AskLoadedTypedItem(source, new Response) } { _.source } @@ -182,5 +187,5 @@ trait Picklers { self: Global => implicit def action: Pickler[() => Unit] = reloadItem | askTypeAtItem | askTypeItem | askTypeCompletionItem | askScopeCompletionItem | - askToDoFirstItem | askLinkPosItem | askLoadedTypedItem | askParsedEnteredItem | emptyAction + askToDoFirstItem | askLinkPosItem | askDocCommentItem | askLoadedTypedItem | askParsedEnteredItem | emptyAction } diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala index ea2333a65b..7bb2b7b7c8 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/PresentationCompilerInstance.scala @@ -8,7 +8,8 @@ import scala.reflect.internal.util.Position /** Trait encapsulating the creation of a presentation compiler's instance.*/ private[tests] trait PresentationCompilerInstance extends TestSettings { protected val settings = new Settings - protected def docSettings: doc.Settings = new doc.Settings(_ => ()) + protected val docSettings = new doc.Settings(_ => ()) + protected val compilerReporter: CompilerReporter = new InteractiveReporter { override def compiler = PresentationCompilerInstance.this.compiler } diff --git a/test/files/presentation/doc.check b/test/files/presentation/doc.check old mode 100755 new mode 100644 index 62b3bfc2e3..e33756773d --- a/test/files/presentation/doc.check +++ b/test/files/presentation/doc.check @@ -1,3 +1,4 @@ +reload: Test.scala body:Body(List(Paragraph(Chain(List(Summary(Chain(List(Text(This is a test comment), Text(.)))), Text( )))))) @example:Body(List(Paragraph(Chain(List(Summary(Monospace(Text("abb".permutations = Iterator(abb, bab, bba))))))))) diff --git a/test/files/presentation/doc.scala b/test/files/presentation/doc.scala deleted file mode 100755 index 4b0d6baa1f..0000000000 --- a/test/files/presentation/doc.scala +++ /dev/null @@ -1,71 +0,0 @@ -import scala.tools.nsc.doc -import scala.tools.nsc.doc.base.LinkTo -import scala.tools.nsc.doc.base.comment._ -import scala.tools.nsc.interactive._ -import scala.tools.nsc.interactive.tests._ -import scala.tools.nsc.util._ -import scala.tools.nsc.io._ - -object Test extends InteractiveTest { - override val settings: doc.Settings = docSettings - - val tags = Seq( - "@example `\"abb\".permutations = Iterator(abb, bab, bba)`", - "@version 1.0, 09/07/2012", - "@since 2.10", - "@todo this method is unsafe", - "@note Don't inherit!", - "@see some other method" - ) - - val comment = "This is a test comment." - val caret = "" - - def text(nTags: Int) = - """|/** %s - | - | * %s */ - |trait Commented {} - |class User(c: %sCommented)""".stripMargin.format(comment, tags take nTags mkString "\n", caret) - - override def main(args: Array[String]) { - val documenter = new Doc(settings) { - val global: compiler.type = compiler - - def chooseLink(links: List[LinkTo]): LinkTo = links.head - } - for (i <- 1 to tags.length) { - val markedText = text(i) - val idx = markedText.indexOf(caret) - val fileText = markedText.substring(0, idx) + markedText.substring(idx + caret.length) - val source = sourceFiles(0) - val batch = new BatchSourceFile(source.file, fileText.toCharArray) - val reloadResponse = new Response[Unit] - compiler.askReload(List(batch), reloadResponse) - reloadResponse.get.left.toOption match { - case None => - reporter.println("Couldn't reload") - case Some(_) => - val treeResponse = new compiler.Response[compiler.Tree] - val pos = compiler.rangePos(batch, idx, idx, idx) - compiler.askTypeAt(pos, treeResponse) - treeResponse.get.left.toOption match { - case Some(tree) => - val sym = tree.tpe.typeSymbol - documenter.retrieve(sym, sym.owner) match { - case Some(HtmlResult(comment)) => - import comment._ - val tags: List[(String, Iterable[Body])] = - List(("@example", example), ("@version", version), ("@since", since.toList), ("@todo", todo), ("@note", note), ("@see", see)) - val str = ("body:" + body + "\n") + - tags.map{ case (name, bodies) => name + ":" + bodies.mkString("\n") }.mkString("\n") - reporter.println(str) - case Some(_) => reporter.println("Got unexpected result") - case None => reporter.println("Got no result") - } - case None => reporter.println("Couldn't find a typedTree") - } - } - } - } -} diff --git a/test/files/presentation/doc/doc.scala b/test/files/presentation/doc/doc.scala new file mode 100755 index 0000000000..475d92b861 --- /dev/null +++ b/test/files/presentation/doc/doc.scala @@ -0,0 +1,91 @@ +import scala.tools.nsc.doc +import scala.tools.nsc.doc.base._ +import scala.tools.nsc.doc.base.comment._ +import scala.tools.nsc.interactive._ +import scala.tools.nsc.interactive.tests._ +import scala.tools.nsc.util._ +import scala.tools.nsc.io._ + +object Test extends InteractiveTest { + override val settings: doc.Settings = docSettings + + val tags = Seq( + "@example `\"abb\".permutations = Iterator(abb, bab, bba)`", + "@version 1.0, 09/07/2012", + "@since 2.10", + "@todo this method is unsafe", + "@note Don't inherit!", + "@see some other method" + ) + + val comment = "This is a test comment." + val caret = "" + + def text(nTags: Int) = + """|/** %s + | + | * %s */ + |trait Commented {} + |class User(c: %sCommented)""".stripMargin.format(comment, tags take nTags mkString "\n", caret) + + override lazy val compiler = { + new { + override val settings = { + prepareSettings(Test.this.settings) + Test.this.settings + } + } with Global(settings, compilerReporter) with MemberLookupBase with CommentFactoryBase { + val global: this.type = this + def chooseLink(links: List[LinkTo]): LinkTo = links.head + def internalLink(sym: Symbol, site: Symbol) = None + def toString(link: LinkTo) = link.toString + + def getComment(sym: Symbol, source: SourceFile) = { + val docResponse = new Response[(String, String, Position)] + askDocComment(sym, sym.owner, source, docResponse) + docResponse.get.left.toOption flatMap { + case (expanded, raw, pos) => + if (expanded.isEmpty) + None + else + Some(ask { () => parseAtSymbol(expanded, raw, pos, Some(sym.owner)) }) + } + } + } + } + + override def runDefaultTests() { + for (i <- 1 to tags.length) { + val markedText = text(i) + val idx = markedText.indexOf(caret) + val fileText = markedText.substring(0, idx) + markedText.substring(idx + caret.length) + val source = sourceFiles(0) + val batch = new BatchSourceFile(source.file, fileText.toCharArray) + val reloadResponse = new Response[Unit] + compiler.askReload(List(batch), reloadResponse) + reloadResponse.get.left.toOption match { + case None => + reporter.println("Couldn't reload") + case Some(_) => + val treeResponse = new compiler.Response[compiler.Tree] + val pos = compiler.rangePos(batch, idx, idx, idx) + compiler.askTypeAt(pos, treeResponse) + treeResponse.get.left.toOption match { + case Some(tree) => + val sym = tree.tpe.typeSymbol + compiler.getComment(sym, batch) match { + case None => println("Got no doc comment") + case Some(comment) => + import comment._ + val tags: List[(String, Iterable[Body])] = + List(("@example", example), ("@version", version), ("@since", since.toList), ("@todo", todo), ("@note", note), ("@see", see)) + val str = ("body:" + body + "\n") + + tags.map{ case (name, bodies) => name + ":" + bodies.mkString("\n") }.mkString("\n") + println(str) + } + case None => println("Couldn't find a typedTree") + } + } + } + } +} -- cgit v1.2.3 From 1426fec358342e0b052c5a96ee2b7e60d4d4066b Mon Sep 17 00:00:00 2001 From: James Iry Date: Mon, 4 Feb 2013 14:48:23 -0800 Subject: SI-7070 Turn restriction on companions in pkg objs into warning The implementation restriction created from SI-5954 in 3ef487ecb6733bfe3c13d89780ebcfc81f9a5ea0 has two problems. 1) The problematic code works fine if compile with sbt. That means the restriction is breaking some people needlessly. 2) It's not binary compatible. To fix all that this commit changes the error into a warning and removes the setting used to get around the restriction. --- .../scala/tools/nsc/settings/ScalaSettings.scala | 1 - src/compiler/scala/tools/nsc/typechecker/Typers.scala | 16 ++++++++-------- .../reflect/internal/settings/MutableSettings.scala | 2 -- src/reflect/scala/reflect/runtime/Settings.scala | 1 - test/files/neg/t5954.check | 10 +++++----- test/files/neg/t5954.flags | 1 + test/files/neg/t5954.scala | 2 +- test/files/pos/package-case.flags | 1 - test/files/pos/t2130-1.flags | 1 - test/files/pos/t2130-2.flags | 1 - test/files/pos/t3999b.flags | 1 - test/files/pos/t4052.flags | 1 - 12 files changed, 15 insertions(+), 23 deletions(-) create mode 100644 test/files/neg/t5954.flags delete mode 100644 test/files/pos/package-case.flags delete mode 100644 test/files/pos/t2130-1.flags delete mode 100644 test/files/pos/t2130-2.flags delete mode 100644 test/files/pos/t3999b.flags delete mode 100644 test/files/pos/t4052.flags (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 0a98d45cac..3df6334ec1 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -175,7 +175,6 @@ trait ScalaSettings extends AbsScalaSettings val etaExpandKeepsStar = BooleanSetting ("-Yeta-expand-keeps-star", "Eta-expand varargs methods to T* rather than Seq[T]. This is a temporary option to ease transition.") val Yinvalidate = StringSetting ("-Yinvalidate", "classpath-entry", "Invalidate classpath entry before run", "") val noSelfCheck = BooleanSetting ("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.") - val companionsInPkgObjs = BooleanSetting("-Ycompanions-in-pkg-objs", "Allow companion objects and case classes in package objects. See issue SI-5954.") val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes") val exposeEmptyPackage = BooleanSetting("-Yexpose-empty-package", "Internal only: expose the empty package.").internalOnly() diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 026c130a87..795ad7e5c7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1914,29 +1914,29 @@ trait Typers extends Modes with Adaptations with Tags { // SI-5954. On second compile of a companion class contained in a package object we end up // with some confusion of names which leads to having two symbols with the same name in the - // same owner. Until that can be straightened out we can't allow companion objects in package + // same owner. Until that can be straightened out we will warn on companion objects in package // objects. But this code also tries to be friendly by distinguishing between case classes and // user written companion pairs - def restrictPackageObjectMembers(mdef : ModuleDef) = for (m <- mdef.symbol.info.members) { + def warnPackageObjectMembers(mdef : ModuleDef) = for (m <- mdef.symbol.info.members) { // ignore synthetic objects, because the "companion" object to a case class is synthetic and // we only want one error per case class if (!m.isSynthetic) { // can't handle case classes in package objects - if (m.isCaseClass) pkgObjectRestriction(m, mdef, "case") + if (m.isCaseClass) pkgObjectWarning(m, mdef, "case") // can't handle companion class/object pairs in package objects else if ((m.isClass && m.companionModule != NoSymbol && !m.companionModule.isSynthetic) || (m.isModule && m.companionClass != NoSymbol && !m.companionClass.isSynthetic)) - pkgObjectRestriction(m, mdef, "companion") + pkgObjectWarning(m, mdef, "companion") } - def pkgObjectRestriction(m : Symbol, mdef : ModuleDef, restricted : String) = { + def pkgObjectWarning(m : Symbol, mdef : ModuleDef, restricted : String) = { val pkgName = mdef.symbol.ownerChain find (_.isPackage) map (_.decodedName) getOrElse mdef.symbol.toString - context.error(if (m.pos.isDefined) m.pos else mdef.pos, s"implementation restriction: package object ${pkgName} cannot contain ${restricted} ${m}. Instead, ${m} should be placed directly in package ${pkgName}.") + context.warning(if (m.pos.isDefined) m.pos else mdef.pos, s"${m} should be placed directly in package ${pkgName} instead of package object ${pkgName}. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954.") } } - if (!settings.companionsInPkgObjs.value && mdef.symbol.isPackageObject) - restrictPackageObjectMembers(mdef) + if (mdef.symbol.isPackageObject) + warnPackageObjectMembers(mdef) treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType } diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index ec3501d5bc..81368df7a6 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -47,6 +47,4 @@ abstract class MutableSettings extends AbsSettings { def XoldPatmat: BooleanSetting def XnoPatmatAnalysis: BooleanSetting def XfullLubs: BooleanSetting - def companionsInPkgObjs: BooleanSetting - } diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 2d5b76f094..0e0cf3fc40 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -43,7 +43,6 @@ private[reflect] class Settings extends MutableSettings { val printtypes = new BooleanSetting(false) val uniqid = new BooleanSetting(false) val verbose = new BooleanSetting(false) - val companionsInPkgObjs = new BooleanSetting(false) val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) diff --git a/test/files/neg/t5954.check b/test/files/neg/t5954.check index 3ca47cd430..ed10658b24 100644 --- a/test/files/neg/t5954.check +++ b/test/files/neg/t5954.check @@ -1,16 +1,16 @@ -t5954.scala:36: error: implementation restriction: package object A cannot contain case class D. Instead, class D should be placed directly in package A. +t5954.scala:36: error: class D should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. case class D() ^ -t5954.scala:35: error: implementation restriction: package object A cannot contain companion object C. Instead, object C should be placed directly in package A. +t5954.scala:35: error: object C should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. object C ^ -t5954.scala:34: error: implementation restriction: package object A cannot contain companion trait C. Instead, trait C should be placed directly in package A. +t5954.scala:34: error: trait C should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. trait C ^ -t5954.scala:33: error: implementation restriction: package object A cannot contain companion object B. Instead, object B should be placed directly in package A. +t5954.scala:33: error: object B should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. object B ^ -t5954.scala:32: error: implementation restriction: package object A cannot contain companion class B. Instead, class B should be placed directly in package A. +t5954.scala:32: error: class B should be placed directly in package A instead of package object A. Under some circumstances companion objects and case classes in package objects can fail to recompile. See https://issues.scala-lang.org/browse/SI-5954. class B ^ 5 errors found diff --git a/test/files/neg/t5954.flags b/test/files/neg/t5954.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/t5954.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/t5954.scala b/test/files/neg/t5954.scala index 9e6f5392c7..3ccb5ed3ff 100644 --- a/test/files/neg/t5954.scala +++ b/test/files/neg/t5954.scala @@ -1,4 +1,4 @@ -// if you ever think you've fixed the underlying reason for the implementation restrictions +// if you ever think you've fixed the underlying reason for the warning // imposed by SI-5954, then here's a test that should pass with two "succes"es // //import scala.tools.partest._ diff --git a/test/files/pos/package-case.flags b/test/files/pos/package-case.flags deleted file mode 100644 index 2f174c4732..0000000000 --- a/test/files/pos/package-case.flags +++ /dev/null @@ -1 +0,0 @@ --Ycompanions-in-pkg-objs diff --git a/test/files/pos/t2130-1.flags b/test/files/pos/t2130-1.flags deleted file mode 100644 index 2f174c4732..0000000000 --- a/test/files/pos/t2130-1.flags +++ /dev/null @@ -1 +0,0 @@ --Ycompanions-in-pkg-objs diff --git a/test/files/pos/t2130-2.flags b/test/files/pos/t2130-2.flags deleted file mode 100644 index 2f174c4732..0000000000 --- a/test/files/pos/t2130-2.flags +++ /dev/null @@ -1 +0,0 @@ --Ycompanions-in-pkg-objs diff --git a/test/files/pos/t3999b.flags b/test/files/pos/t3999b.flags deleted file mode 100644 index 2f174c4732..0000000000 --- a/test/files/pos/t3999b.flags +++ /dev/null @@ -1 +0,0 @@ --Ycompanions-in-pkg-objs diff --git a/test/files/pos/t4052.flags b/test/files/pos/t4052.flags deleted file mode 100644 index 2f174c4732..0000000000 --- a/test/files/pos/t4052.flags +++ /dev/null @@ -1 +0,0 @@ --Ycompanions-in-pkg-objs -- cgit v1.2.3 From 015ff514e18bdcd016f7175202286ac4c0d42831 Mon Sep 17 00:00:00 2001 From: James Iry Date: Wed, 6 Feb 2013 13:33:11 -0800 Subject: [nomaster] Revert "SI-5017 Poor performance of :+ operator on Arrays" This reverts commit 02b2da63409af6a28824cbb74d00d0ec04518c8d. --- src/library/scala/collection/mutable/ArrayOps.scala | 14 -------------- test/files/run/array-addition.check | 4 ---- test/files/run/array-addition.scala | 11 ----------- 3 files changed, 29 deletions(-) delete mode 100644 test/files/run/array-addition.check delete mode 100644 test/files/run/array-addition.scala (limited to 'test') diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala index 6b778b26f5..bb938a7aeb 100644 --- a/src/library/scala/collection/mutable/ArrayOps.scala +++ b/src/library/scala/collection/mutable/ArrayOps.scala @@ -52,20 +52,6 @@ trait ArrayOps[T] extends Any with ArrayLike[T, Array[T]] with CustomParalleliza super.toArray[U] } - def :+[B >: T: scala.reflect.ClassTag](elem: B): Array[B] = { - val result = Array.ofDim[B](repr.length + 1) - Array.copy(repr, 0, result, 0, repr.length) - result(repr.length) = elem - result - } - - def +:[B >: T: scala.reflect.ClassTag](elem: B): Array[B] = { - val result = Array.ofDim[B](repr.length + 1) - result(0) = elem - Array.copy(repr, 0, result, 1, repr.length) - result - } - override def par = ParArray.handoff(repr) /** Flattens a two-dimensional array by concatenating all its rows diff --git a/test/files/run/array-addition.check b/test/files/run/array-addition.check deleted file mode 100644 index 7bfbd9c711..0000000000 --- a/test/files/run/array-addition.check +++ /dev/null @@ -1,4 +0,0 @@ -Array(1, 2, 3, 4) -Array(1, 2, 3, 4) -Array(1) -Array(1) diff --git a/test/files/run/array-addition.scala b/test/files/run/array-addition.scala deleted file mode 100644 index 8def48e85c..0000000000 --- a/test/files/run/array-addition.scala +++ /dev/null @@ -1,11 +0,0 @@ -object Test { - def prettyPrintArray(x: Array[_]) = println("Array(" + x.mkString(", ") + ")") - - def main(args: Array[String]): Unit = { - prettyPrintArray(Array(1,2,3) :+ 4) - prettyPrintArray(1 +: Array(2,3,4)) - prettyPrintArray(Array() :+ 1) - prettyPrintArray(1 +: Array()) - } -} - -- cgit v1.2.3 From 79e774f584d5afed72f62b0a7f93f63765d0cb99 Mon Sep 17 00:00:00 2001 From: Mads Hartmann Jensen Date: Sat, 26 Jan 2013 20:01:36 +0100 Subject: SI-7026: parseTree should never return a typed one This commit fixes ticket SI-7026. This makes it safe to use parseTree outside of the presentation compiler thread. Solved it with an implementation that just parses the source every time without trying to memorize anything. Added tests that checks that 1. You get a new parse tree every time you ask for one. 2. You always get a parse tree, never a typed tree. 3. A parse tree should never contain any symbols or types [1]. 4. If you ask for a parse tree and then ask for a typed tree it shouldn't change the parse tree you originally asked for, i.e. property 3 still holds. Additionally the parser is now only interruptible when running on the presentation compiler thread. [1] There is an exception to this though. Some of the nodes that the compiler generates will actually contain symbols. I've chosen to just ignore these special cases for now. --- .../tools/nsc/interactive/CompilerControl.scala | 13 ++-- .../scala/tools/nsc/interactive/Global.scala | 7 +- test/files/presentation/ide-t1001326.check | 4 + test/files/presentation/ide-t1001326/Test.scala | 91 ++++++++++++++++++++++ test/files/presentation/ide-t1001326/src/a/A.scala | 5 ++ 5 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 test/files/presentation/ide-t1001326.check create mode 100644 test/files/presentation/ide-t1001326/Test.scala create mode 100644 test/files/presentation/ide-t1001326/src/a/A.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala index b4af8f00d6..73414ea6ed 100644 --- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -240,15 +240,12 @@ trait CompilerControl { self: Global => } /** Returns parse tree for source `source`. No symbols are entered. Syntax errors are reported. - * Can be called asynchronously from presentation compiler. + * + * This method is thread-safe and as such can safely run outside of the presentation + * compiler thread. */ - def parseTree(source: SourceFile): Tree = ask { () => - getUnit(source) match { - case Some(unit) if unit.status >= JustParsed => - unit.body - case _ => - new UnitParser(new CompilationUnit(source)).parse() - } + def parseTree(source: SourceFile): Tree = { + new UnitParser(new CompilationUnit(source)).parse() } /** Asks for a computation to be done quickly on the presentation compiler thread */ diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 4ab7b98b3d..b804d45b31 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -225,7 +225,10 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") /** Called from parser, which signals hereby that a method definition has been parsed. */ override def signalParseProgress(pos: Position) { - checkForMoreWork(pos) + // We only want to be interruptible when running on the PC thread. + if(onCompilerThread) { + checkForMoreWork(pos) + } } /** Called from typechecker, which signals hereby that a node has been completely typechecked. @@ -447,7 +450,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") */ @elidable(elidable.WARNING) override def assertCorrectThread() { - assert(initializing || (Thread.currentThread() eq compileRunner), + assert(initializing || onCompilerThread, "Race condition detected: You are running a presentation compiler method outside the PC thread.[phase: %s]".format(globalPhase) + " Please file a ticket with the current stack trace at https://www.assembla.com/spaces/scala-ide/support/tickets") } diff --git a/test/files/presentation/ide-t1001326.check b/test/files/presentation/ide-t1001326.check new file mode 100644 index 0000000000..0ac15faed4 --- /dev/null +++ b/test/files/presentation/ide-t1001326.check @@ -0,0 +1,4 @@ +Unique OK +Unattributed OK +NeverModify OK +AlwaysParseTree OK \ No newline at end of file diff --git a/test/files/presentation/ide-t1001326/Test.scala b/test/files/presentation/ide-t1001326/Test.scala new file mode 100644 index 0000000000..3091da4b40 --- /dev/null +++ b/test/files/presentation/ide-t1001326/Test.scala @@ -0,0 +1,91 @@ +import scala.tools.nsc.interactive.tests.InteractiveTest +import scala.reflect.internal.util.SourceFile +import scala.tools.nsc.interactive.Response + +object Test extends InteractiveTest { + + override def execute(): Unit = { + val sf = sourceFiles.find(_.file.name == "A.scala").head + uniqueParseTree_t1001326(sf) + unattributedParseTree_t1001326(sf) + neverModifyParseTree_t1001326(sf) + shouldAlwaysReturnParseTree_t1001326(sf) + } + + /** + * Asking twice for a parseTree on the same source should always return a new tree + */ + private def uniqueParseTree_t1001326(sf: SourceFile) { + val parseTree1 = compiler.parseTree(sf) + val parseTree2 = compiler.parseTree(sf) + if (parseTree1 != parseTree2) { + reporter.println("Unique OK") + } else { + reporter.println("Unique FAILED") + } + } + + /** + * A parseTree should never contain any symbols or types + */ + private def unattributedParseTree_t1001326(sf: SourceFile) { + if (noSymbolsOrTypes(compiler.parseTree(sf))) { + reporter.println("Unattributed OK") + } else { + reporter.println("Unattributed FAILED") + } + } + + /** + * Once you have obtained a parseTree it should never change + */ + private def neverModifyParseTree_t1001326(sf: SourceFile) { + val parsedTree = compiler.parseTree(sf) + loadSourceAndWaitUntilTypechecked(sf) + if (noSymbolsOrTypes(parsedTree)) { + reporter.println("NeverModify OK") + } else { + reporter.println("NeverModify FAILED") + } + } + + /** + * Should always return a parse tree + */ + private def shouldAlwaysReturnParseTree_t1001326(sf: SourceFile) { + loadSourceAndWaitUntilTypechecked(sf) + if (noSymbolsOrTypes(compiler.parseTree(sf))) { + reporter.println("AlwaysParseTree OK") + } else { + reporter.println("AlwaysParseTree FAILED") + } + } + + /** + * Load a source and block while it is type-checking. + */ + private def loadSourceAndWaitUntilTypechecked(sf: SourceFile): Unit = { + compiler.askToDoFirst(sf) + val res = new Response[Unit] + compiler.askReload(List(sf), res) + res.get + askLoadedTyped(sf).get + } + + /** + * Traverses a tree and makes sure that there are no types or symbols present in the tree with + * the exception of the symbol for the package 'scala'. This is because that symbol will be + * present in some of the nodes that the compiler generates. + */ + private def noSymbolsOrTypes(tree: compiler.Tree): Boolean = { + tree.forAll { t => + (t.symbol == null || + t.symbol == compiler.NoSymbol || + t.symbol == compiler.definitions.ScalaPackage // ignore the symbol for the scala package for now + ) && ( + t.tpe == null || + t.tpe == compiler.NoType) + } + } + +} \ No newline at end of file diff --git a/test/files/presentation/ide-t1001326/src/a/A.scala b/test/files/presentation/ide-t1001326/src/a/A.scala new file mode 100644 index 0000000000..c82ca02231 --- /dev/null +++ b/test/files/presentation/ide-t1001326/src/a/A.scala @@ -0,0 +1,5 @@ +package a + +class A { + def foo(s: String) = s + s +} \ No newline at end of file -- cgit v1.2.3 From 96b0eff51e18a1abd9761451e08e63c9a3eb9ea6 Mon Sep 17 00:00:00 2001 From: Evgeny Kotelnikov Date: Sat, 19 Jan 2013 17:42:12 +0400 Subject: SI-5824 Fix crashes in reify with _* Reification crashes if "foo: _*" construct is used. This happens besause type tree is represented either with TypeTree, or with Ident (present case), and `toPreTyperTypedOrAnnotated' only matches of the former. The fix is to cover the latter too. A test is included. --- src/compiler/scala/reflect/reify/codegen/GenTrees.scala | 2 +- src/compiler/scala/reflect/reify/phases/Reshape.scala | 12 ++++++++---- src/reflect/scala/reflect/internal/TreeInfo.scala | 7 +++++++ test/files/run/t5824.check | 1 + test/files/run/t5824.scala | 8 ++++++++ 5 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 test/files/run/t5824.check create mode 100644 test/files/run/t5824.scala (limited to 'test') diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 31974b5b76..45865268cd 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -177,7 +177,7 @@ trait GenTrees { // then we can reify the scrutinee as a symless AST and that will definitely be hygienic // why? because then typechecking of a scrutinee doesn't depend on the environment external to the quasiquote // otherwise we need to reify the corresponding type - if (sym.isLocalToReifee || tpe.isLocalToReifee) + if (sym.isLocalToReifee || tpe.isLocalToReifee || treeInfo.isWildcardStarType(tree)) reifyProduct(tree) else { if (reifyDebug) println("reifying bound type %s (underlying type is %s)".format(sym, tpe)) diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index 1b7509fdbe..7406f5d02d 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -188,8 +188,12 @@ trait Reshape { } private def toPreTyperTypedOrAnnotated(tree: Tree): Tree = tree match { - case ty @ Typed(expr1, tt @ TypeTree()) => + case ty @ Typed(expr1, tpt) => if (reifyDebug) println("reify typed: " + tree) + val original = tpt match { + case tt @ TypeTree() => tt.original + case tpt => tpt + } val annotatedArg = { def loop(tree: Tree): Tree = tree match { case annotated1 @ Annotated(ann, annotated2 @ Annotated(_, _)) => loop(annotated2) @@ -197,15 +201,15 @@ trait Reshape { case _ => EmptyTree } - loop(tt.original) + loop(original) } if (annotatedArg != EmptyTree) { if (annotatedArg.isType) { if (reifyDebug) println("verdict: was an annotated type, reify as usual") ty } else { - if (reifyDebug) println("verdict: was an annotated value, equivalent is " + tt.original) - toPreTyperTypedOrAnnotated(tt.original) + if (reifyDebug) println("verdict: was an annotated value, equivalent is " + original) + toPreTyperTypedOrAnnotated(original) } } else { if (reifyDebug) println("verdict: wasn't annotated, reify as usual") diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 98c1afb323..44dd92e62b 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -431,6 +431,13 @@ abstract class TreeInfo { case _ => false } + /** Is the argument a wildcard star type of the form `_*`? + */ + def isWildcardStarType(tree: Tree): Boolean = tree match { + case Ident(tpnme.WILDCARD_STAR) => true + case _ => false + } + /** Is this pattern node a catch-all (wildcard or variable) pattern? */ def isDefaultCase(cdef: CaseDef) = cdef match { case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat) diff --git a/test/files/run/t5824.check b/test/files/run/t5824.check new file mode 100644 index 0000000000..3774da60e5 --- /dev/null +++ b/test/files/run/t5824.check @@ -0,0 +1 @@ +a b c diff --git a/test/files/run/t5824.scala b/test/files/run/t5824.scala new file mode 100644 index 0000000000..2ad169e2d1 --- /dev/null +++ b/test/files/run/t5824.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.Eval + +object Test extends App { + reify { + println("%s %s %s".format(List("a", "b", "c"): _*)) + }.eval +} -- cgit v1.2.3 From 0d68a874e2158d9739abd3977ae0d9edd4a76e59 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Thu, 7 Feb 2013 18:45:34 +0100 Subject: SI-6113 typeOf now works for type lambdas It's not a problem to have an abstract type symbol in a ground type tag if that symbol is defined in the type being reified. This mechanics was already built in for existentials, now we extend it to include type params of poly types. Credit goes to @katefree --- src/compiler/scala/reflect/reify/codegen/GenTypes.scala | 3 +-- src/compiler/scala/reflect/reify/phases/Reify.scala | 5 ++++- test/files/run/t6113.check | 1 + test/files/run/t6113.scala | 6 ++++++ 4 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t6113.check create mode 100644 test/files/run/t6113.scala (limited to 'test') diff --git a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala index 7aa87dc2f8..bb7e1f9b56 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTypes.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTypes.scala @@ -69,8 +69,7 @@ trait GenTypes { def reificationIsConcrete: Boolean = state.reificationIsConcrete def spliceType(tpe: Type): Tree = { - val quantified = currentQuantified - if (tpe.isSpliceable && !(quantified contains tpe.typeSymbol)) { + if (tpe.isSpliceable && !(boundSymbolsInCallstack contains tpe.typeSymbol)) { if (reifyDebug) println("splicing " + tpe) val tagFlavor = if (concrete) tpnme.TypeTag.toString else tpnme.WeakTypeTag.toString diff --git a/src/compiler/scala/reflect/reify/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index dc0028be38..8e13a45cdb 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -28,7 +28,10 @@ trait Reify extends GenSymbols finally currents = currents.tail } } - def currentQuantified = flatCollect(reifyStack.currents)({ case ExistentialType(quantified, _) => quantified }) + def boundSymbolsInCallstack = flatCollect(reifyStack.currents) { + case ExistentialType(quantified, _) => quantified + case PolyType(typeParams, _) => typeParams + } def current = reifyStack.currents.head def currents = reifyStack.currents diff --git a/test/files/run/t6113.check b/test/files/run/t6113.check new file mode 100644 index 0000000000..65fb3cd090 --- /dev/null +++ b/test/files/run/t6113.check @@ -0,0 +1 @@ +Foo[[X](Int, X)] diff --git a/test/files/run/t6113.scala b/test/files/run/t6113.scala new file mode 100644 index 0000000000..321cae86a3 --- /dev/null +++ b/test/files/run/t6113.scala @@ -0,0 +1,6 @@ +trait Foo[C[_]] + +object Test extends App { + import scala.reflect.runtime.universe._ + println(typeOf[Foo[({type l[X] = (Int, X)})#l]]) +} \ No newline at end of file -- cgit v1.2.3 From a0ee6e996e602bf5729687d7301f60b340d57061 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 28 Jan 2013 16:36:27 +0100 Subject: SI-5082 Cycle avoidance between case companions We can synthesize the case companion unapply without forcing the info of the case class, by looking at the parameters in the `ClassDef` tree, rather than at `sym.caseFieldAccessors`. Access to non-public case class fields routed through the already-renamed case accessor methods. The renamings are conveyed via a back-channel (a per-run map, `renamedCaseAccessors`), rather than via the types to give us enough slack to avoid the cycle. Some special treatment of private[this] parameters is needed to avoid a misleading error message. Fortunately, we can determine this without forcing the info of the case class, by inspecting the parameter accessor trees. This change may allow us to resurrect the case class ProductN parentage, which was trialled but abandoned in the lead up to 2.10.0. --- .../tools/nsc/typechecker/SyntheticMethods.scala | 6 ++++ .../scala/tools/nsc/typechecker/Unapplies.scala | 33 ++++++++++++++++++---- test/files/pos/t5082.scala | 14 +++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 test/files/pos/t5082.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 39f6f764e7..242eb9c9fe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -50,6 +50,10 @@ trait SyntheticMethods extends ast.TreeDSL { else if (clazz.isDerivedValueClass) valueSymbols else Nil } + private lazy val renamedCaseAccessors = perRunCaches.newMap[Symbol, mutable.Map[TermName, TermName]]() + /** Does not force the info of `caseclazz` */ + final def caseAccessorName(caseclazz: Symbol, paramName: TermName) = + (renamedCaseAccessors get caseclazz).fold(paramName)(_(paramName)) /** Add the synthetic methods to case classes. */ @@ -384,6 +388,8 @@ trait SyntheticMethods extends ast.TreeDSL { // TODO: shouldn't the next line be: `original resetFlag CASEACCESSOR`? ddef.symbol resetFlag CASEACCESSOR lb += logResult("case accessor new")(newAcc) + val renamedInClassMap = renamedCaseAccessors.getOrElseUpdate(clazz, mutable.Map() withDefault(x => x)) + renamedInClassMap(original.name.toTermName) = newAcc.symbol.name.toTermName } (lb ++= templ.body ++= synthesize()).toList diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 8f607e9f67..e143015bbb 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -93,12 +93,33 @@ trait Unapplies extends ast.TreeDSL * @param param The name of the parameter of the unapply method, assumed to be of type C[Ts] * @param caseclazz The case class C[Ts] */ - private def caseClassUnapplyReturnValue(param: Name, caseclazz: Symbol) = { - def caseFieldAccessorValue(selector: Symbol): Tree = Ident(param) DOT selector + private def caseClassUnapplyReturnValue(param: Name, caseclazz: ClassDef) = { + def caseFieldAccessorValue(selector: ValDef): Tree = { + val accessorName = selector.name + val privateLocalParamAccessor = caseclazz.impl.body.collectFirst { + case dd: ValOrDefDef if dd.name == accessorName && dd.mods.isPrivateLocal => dd.symbol + } + privateLocalParamAccessor match { + case None => + // Selecting by name seems to be the most straight forward way here to + // avoid forcing the symbol of the case class in order to list the accessors. + val maybeRenamedAccessorName = caseAccessorName(caseclazz.symbol, accessorName) + Ident(param) DOT maybeRenamedAccessorName + case Some(sym) => + // But, that gives a misleading error message in neg/t1422.scala, where a case + // class has an illegal private[this] parameter. We can detect this by checking + // the modifiers on the param accessors. + // + // We just generate a call to that param accessor here, which gives us an inaccessible + // symbol error, as before. + Ident(param) DOT sym + } + } - caseclazz.caseFieldAccessors match { - case Nil => TRUE - case xs => SOME(xs map caseFieldAccessorValue: _*) + // Working with trees, rather than symbols, to avoid cycles like SI-5082 + constrParamss(caseclazz).take(1).flatten match { + case Nil => TRUE + case xs => SOME(xs map caseFieldAccessorValue: _*) } } @@ -157,7 +178,7 @@ trait Unapplies extends ast.TreeDSL } val cparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), unapplyParamName, classType(cdef, tparams), EmptyTree)) val ifNull = if (constrParamss(cdef).head.isEmpty) FALSE else REF(NoneModule) - val body = nullSafe({ case Ident(x) => caseClassUnapplyReturnValue(x, cdef.symbol) }, ifNull)(Ident(unapplyParamName)) + val body = nullSafe({ case Ident(x) => caseClassUnapplyReturnValue(x, cdef) }, ifNull)(Ident(unapplyParamName)) atPos(cdef.pos.focus)( DefDef(caseMods, method, tparams, List(cparams), TypeTree(), body) diff --git a/test/files/pos/t5082.scala b/test/files/pos/t5082.scala new file mode 100644 index 0000000000..63eeda38ba --- /dev/null +++ b/test/files/pos/t5082.scala @@ -0,0 +1,14 @@ +trait Something[T] +object Test { class A } +case class Test() extends Something[Test.A] + +object User { + val Test() = Test() +} + +object Wrap { + trait Something[T] + object Test { class A } + case class Test(a: Int, b: Int)(c: String) extends Something[Test.A] + val Test(x, y) = Test(1, 2)(""); (x + y).toString +} -- cgit v1.2.3 From 55c9b9c280ac9bc36bbac09397c7646f8dcf4583 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 31 Jan 2013 00:33:19 +0100 Subject: SI-6146 More accurate prefixes for sealed subtypes. When analysing exhaustivity/reachability of type tests and equality tests, the pattern matcher must construct a set of sealed subtypes based on the prefix of the static type of and the set of sealed descendent symbols of that type. Previously, it was using `memberType` for this purpose. In simple cases, this is sufficient: scala> class C { class I1; object O { class I2 } }; object D extends C defined class C defined module D scala> typeOf[D.type] memberType typeOf[C#I1].typeSymbol res0: u.Type = D.I1 But, as reported in this bug, it fails when there is an additional level of nesting: scala> typeOf[D.type] memberType typeOf[c.O.I2 forSome { val c: C }].typeSymbol res5: u.Type = C.O.I2 This commit introduces `nestedMemberType`, which uses `memberType` recursively up the prefix chain prefix chain. scala> nestedMemberType(typeOf[c.O.I2 forSome { val c: C }].typeSymbol, typeOf[D.type], typeOf[C].typeSymbol) res6: u.Type = D.O.Id --- .../scala/tools/nsc/interpreter/IMain.scala | 4 +- .../tools/nsc/typechecker/PatternMatching.scala | 4 +- src/reflect/scala/reflect/internal/Types.scala | 51 ++++++++++++++++-- test/files/pos/t6146.flags | 1 + test/files/pos/t6146.scala | 60 ++++++++++++++++++++++ test/files/run/t6146b.check | 52 +++++++++++++++++++ test/files/run/t6146b.scala | 39 ++++++++++++++ 7 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 test/files/pos/t6146.flags create mode 100644 test/files/pos/t6146.scala create mode 100644 test/files/run/t6146b.check create mode 100644 test/files/run/t6146b.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index a55f0af116..bed8570bd0 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/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 452957745a..4a765ea22e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -2818,7 +2818,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // compare to the fully known type `tp` (modulo abstract types), // so that we can rule out stuff like: sealed trait X[T]; class XInt extends X[Int] --> XInt not valid when enumerating X[String] // however, must approximate abstract types in - val subTp = appliedType(pre.memberType(sym), sym.typeParams.map(_ => WildcardType)) + + val memberType = nestedMemberType(sym, pre, tpApprox.typeSymbol.owner) + val subTp = appliedType(memberType, sym.typeParams.map(_ => WildcardType)) val subTpApprox = typer.infer.approximateAbstracts(subTp) // TODO: needed? // patmatDebug("subtp"+(subTpApprox <:< tpApprox, subTpApprox, tpApprox)) if (subTpApprox <:< tpApprox) Some(checkableType(subTp)) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 0dd98fb6ae..b708ca0fd6 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -586,9 +586,9 @@ trait Types extends api.Types { self: SymbolTable => * Expands type aliases and converts higher-kinded TypeRefs to PolyTypes. * Functions on types are also implemented as PolyTypes. * - * Example: (in the below, is the type constructor of List) - * TypeRef(pre, , List()) is replaced by - * PolyType(X, TypeRef(pre, , List(X))) + * Example: (in the below, `` is the type constructor of List) + * TypeRef(pre, ``, List()) is replaced by + * PolyType(X, TypeRef(pre, ``, List(X))) */ def normalize = this // @MAT @@ -4974,6 +4974,51 @@ trait Types extends api.Types { self: SymbolTable => } } + /** + * A more persistent version of `Type#memberType` which does not require + * that the symbol is a direct member of the prefix. + * + * For instance: + * + * {{{ + * class C[T] { + * sealed trait F[A] + * object X { + * object S1 extends F[T] + * } + * class S2 extends F[T] + * } + * object O extends C[Int] { + * def foo(f: F[Int]) = f match {...} // need to enumerate sealed subtypes of the scrutinee here. + * } + * class S3 extends O.F[String] + * + * nestedMemberType(, , ) = O.X.S1.type + * nestedMemberType(, , ) = O.S2.type + * nestedMemberType(, , ) = S3.type + * }}} + * + * @param sym The symbol of the subtype + * @param pre The prefix from which the symbol is seen + * @param owner + */ + def nestedMemberType(sym: Symbol, pre: Type, owner: Symbol): Type = { + def loop(tp: Type): Type = + if (tp.isTrivial) tp + else if (tp.prefix.typeSymbol isNonBottomSubClass owner) { + val widened = tp match { + case _: ConstantType => tp // Java enum constants: don't widen to the enum type! + case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect. + } + widened asSeenFrom (pre, tp.typeSymbol.owner) + } + else loop(tp.prefix) memberType tp.typeSymbol + + val result = loop(sym.tpeHK) + assert(sym.isTerm || result.typeSymbol == sym, s"($result).typeSymbol = ${result.typeSymbol}; expected ${sym}") + result + } + /** The most deeply nested owner that contains all the symbols * of thistype or prefixless typerefs/singletype occurrences in given type. */ diff --git a/test/files/pos/t6146.flags b/test/files/pos/t6146.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t6146.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/pos/t6146.scala b/test/files/pos/t6146.scala new file mode 100644 index 0000000000..b5bde826b1 --- /dev/null +++ b/test/files/pos/t6146.scala @@ -0,0 +1,60 @@ +// No unreachable or exhaustiveness warnings, please. + +// +// The reported bug +// + +trait AxisCompanion { + sealed trait Format + object Format { + case object Decimal extends Format + case object Integer extends Format + // Gives an unrelated warning: The outer reference in this type test cannot be checked at run time. + //final case class Time( hours: Boolean = false, millis: Boolean = true ) extends Format + } +} +object Axis extends AxisCompanion +class Axis { + import Axis._ + def test( f: Format ) = f match { + case Format.Integer => "Int" + // case Format.Time( hours, millis ) => "Time" + case Format.Decimal => "Dec" + } +} + + +// +// Some tricksier variations +// + +trait T1[X] { + trait T2[Y] { + sealed trait Format + object Format { + case object Decimal extends Format + case object Integer extends Format + } + } +} + +object O1 extends T1[Any] { + object O2 extends T2[Any] { + + } +} + +case object Shorty extends O1.O2.Format + +class Test1 { + import O1.O2._ + val FI: Format.Integer.type = Format.Integer + def test( f: Format ) = { + val ff: f.type = f + ff match { + case FI => "Int" + case Format.Decimal => "Dec" + case Shorty => "Sho" + } + } +} diff --git a/test/files/run/t6146b.check b/test/files/run/t6146b.check new file mode 100644 index 0000000000..b664d1152a --- /dev/null +++ b/test/files/run/t6146b.check @@ -0,0 +1,52 @@ +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; import u._, language._ +u: $r.intp.global.type = +import u._ +import language._ + +scala> val S1 = typeOf[c.X.S1.type forSome { val c: C[_] }].typeSymbol.tpeHK +S1: u.Type = C.X.S1.type + +scala> val S2 = typeOf[O.S2].typeSymbol.tpeHK +S2: u.Type = C.this.S2 + +scala> val S3 = typeOf[O.S3].typeSymbol.tpeHK +S3: u.Type = O.S3 + +scala> val S4 = typeOf[S4].typeSymbol.tpeHK +S4: u.Type = S4 + +scala> val F = typeOf[c.F[_] forSome { val c: C[_] }].typeSymbol.tpeHK +F: u.Type = C.this.F + +scala> val fTpe = typeOf[O.type].decl(newTermName("foo")).paramss.head.head.tpe +fTpe: u.Type = O.F[Int] + +scala> def memType(sub: Type, scrut: Type): Type = + nestedMemberType(sub.typeSymbol, scrut.prefix, scrut.typeSymbol.owner) +memType: (sub: u.Type, scrut: u.Type)u.Type + +scala> + +scala> memType(S1, fTpe) +res0: u.Type = O.X.S1.type + +scala> memType(S2, fTpe) +res1: u.Type = O.S2 + +scala> memType(S3, fTpe) +res2: u.Type = O.S3 + +scala> memType(S4, fTpe) +res3: u.Type = S4 + +scala> diff --git a/test/files/run/t6146b.scala b/test/files/run/t6146b.scala new file mode 100644 index 0000000000..adcd40d2ee --- /dev/null +++ b/test/files/run/t6146b.scala @@ -0,0 +1,39 @@ +import scala.tools.partest.ReplTest + +class A { + sealed trait F[A] +} + +class C[T] extends A { + sealed trait F[A] + object X { + object S1 extends F[T] + } + class S2 extends F[T] +} +object O extends C[Int] { + def foo(f: F[Int]) = f match { case X.S1 => } + + class S3 extends F[Int] +} +class S4 extends O.F[String] + +object Test extends ReplTest { + override def code = """ +:power +val u = rootMirror.universe; import u._, language._ +val S1 = typeOf[c.X.S1.type forSome { val c: C[_] }].typeSymbol.tpeHK +val S2 = typeOf[O.S2].typeSymbol.tpeHK +val S3 = typeOf[O.S3].typeSymbol.tpeHK +val S4 = typeOf[S4].typeSymbol.tpeHK +val F = typeOf[c.F[_] forSome { val c: C[_] }].typeSymbol.tpeHK +val fTpe = typeOf[O.type].decl(newTermName("foo")).paramss.head.head.tpe +def memType(sub: Type, scrut: Type): Type = + nestedMemberType(sub.typeSymbol, scrut.prefix, scrut.typeSymbol.owner) + +memType(S1, fTpe) +memType(S2, fTpe) +memType(S3, fTpe) +memType(S4, fTpe) + """.stripMargin.trim +} \ No newline at end of file -- cgit v1.2.3 From a53e1508660f849e86e34808e5d4230b078f7a3e Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Thu, 7 Feb 2013 23:33:07 +0100 Subject: SI-7100 Fixed infinite recursion in duplicators --- src/compiler/scala/tools/nsc/typechecker/Duplicators.scala | 2 +- test/files/pos/SI-7100.scala | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/SI-7100.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 0b46582cbf..f6142a81be 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -388,7 +388,7 @@ abstract class Duplicators extends Analyzer { cases } - super.typedPos(tree.pos, mode, pt)(Match(scrut, cases1)) + super.typed(atPos(tree.pos)(Match(scrut, cases1)), mode, pt) case EmptyTree => // no need to do anything, in particular, don't set the type to null, EmptyTree.tpe_= asserts diff --git a/test/files/pos/SI-7100.scala b/test/files/pos/SI-7100.scala new file mode 100644 index 0000000000..7cb6356ec8 --- /dev/null +++ b/test/files/pos/SI-7100.scala @@ -0,0 +1,6 @@ +class Buffer { + def f[@specialized(Int) T](): T = 0 match { + case 0 => 0.asInstanceOf[T] + case 1 => 0.asInstanceOf[T] + } +} -- cgit v1.2.3 From 6052e19292c95270ac0a4bf30b6df47a257f88d4 Mon Sep 17 00:00:00 2001 From: James Roper Date: Fri, 5 Oct 2012 12:22:24 +1000 Subject: [backport] SI-6478 Fixing JavaTokenParser ident Backport of 256934160007079f473131469af2df4d023c2cfc from PR https://github.com/scala/scala/pull/1466 --- .../util/parsing/combinator/JavaTokenParsers.scala | 7 +++--- test/files/run/parserJavaIdent.check | 26 ++++++++++++++++++++++ test/files/run/parserJavaIdent.scala | 26 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/files/run/parserJavaIdent.check create mode 100644 test/files/run/parserJavaIdent.scala (limited to 'test') diff --git a/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala b/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala index 78817cfb67..89832d3fb2 100644 --- a/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala +++ b/src/library/scala/util/parsing/combinator/JavaTokenParsers.scala @@ -21,11 +21,12 @@ import scala.annotation.migration * - `floatingPointNumber` */ trait JavaTokenParsers extends RegexParsers { - /** Anything starting with an ASCII alphabetic character or underscore, - * followed by zero or more repetitions of regex's `\w`. + /** Anything that is a valid Java identifier, according to + * The Java Language Spec. + * Generally, this means a letter, followed by zero or more letters or numbers. */ def ident: Parser[String] = - """[a-zA-Z_]\w*""".r + """\p{javaJavaIdentifierStart}\p{javaJavaIdentifierPart}*""".r /** An integer, without sign or with a negative sign. */ def wholeNumber: Parser[String] = """-?\d+""".r diff --git a/test/files/run/parserJavaIdent.check b/test/files/run/parserJavaIdent.check new file mode 100644 index 0000000000..597ddbee47 --- /dev/null +++ b/test/files/run/parserJavaIdent.check @@ -0,0 +1,26 @@ +[1.7] parsed: simple +[1.8] parsed: with123 +[1.6] parsed: with$ +[1.10] parsed: withรธรŸรถรจรฆ +[1.6] parsed: with_ +[1.6] parsed: _with +[1.1] failure: java identifier expected + +3start +^ +[1.1] failure: java identifier expected + +-start +^ +[1.5] failure: java identifier expected + +with-s + ^ +[1.3] failure: java identifier expected + +weโ™ฅscala + ^ +[1.6] failure: java identifier expected + +with space + ^ diff --git a/test/files/run/parserJavaIdent.scala b/test/files/run/parserJavaIdent.scala new file mode 100644 index 0000000000..c068075e4e --- /dev/null +++ b/test/files/run/parserJavaIdent.scala @@ -0,0 +1,26 @@ +object Test extends scala.util.parsing.combinator.JavaTokenParsers { + + def test[A](s: String) { + val res = parseAll(ident, s) match { + case Failure(_, in) => Failure("java identifier expected", in) + case o => o + } + println(res) + } + + def main(args: Array[String]) { + // Happy tests + test("simple") + test("with123") + test("with$") + test("withรธรŸรถรจรฆ") + test("with_") + test("_with") + // Sad tests + test("3start") + test("-start") + test("with-s") + test("weโ™ฅscala") + test("with space") + } +} -- cgit v1.2.3 From dfbaaa179fddd3d37abf66a08080f646b8557b6f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 27 Jan 2013 13:31:06 +0100 Subject: SI-6187 Make partial functions re-typable - `New(tpe)` doesn't survive a `resetAttrs` / typecheck; use a name instead. - Abandon the tree attachment that passed the default case from `typer` to `patmat`; this tree eluded the attribute reset performed in the macro. Instead, add it to the match. Apart from making the tree re-typable, it also exposes the true code structure to macros, which is important if they need to perform other code transformations. - Install original trees on the declared types of the parameters of the `applyOrElse` method to ensure that references to them within the method pick up the correct type parameter skolems upon retypechecking. - Propagate `TypeTree#original` through `copyAttrs`, which is called during tree duplication / `TreeCopiers`. Without this, the original trees that we installed were not visible anymore during `ResetAttrs`. We are not able to reify partial functions yet -- the particular sticking point is reification of the parentage which is only available in the `ClassInfoType`. --- .../tools/nsc/typechecker/PatternMatching.scala | 23 +++--- .../scala/tools/nsc/typechecker/Typers.scala | 85 ++++++++++++++++++---- src/reflect/scala/reflect/internal/StdNames.scala | 1 + src/reflect/scala/reflect/internal/TreeInfo.scala | 7 ++ src/reflect/scala/reflect/internal/Trees.scala | 18 ++++- test/files/run/idempotency-partial-functions.scala | 25 ------- test/files/run/t6187.check | 32 ++++++++ test/files/run/t6187.scala | 18 +++++ test/files/run/t6187b.scala | 5 ++ .../run/idempotency-partial-functions.scala | 28 +++++++ 10 files changed, 192 insertions(+), 50 deletions(-) delete mode 100644 test/files/run/idempotency-partial-functions.scala create mode 100644 test/files/run/t6187.check create mode 100644 test/files/run/t6187.scala create mode 100644 test/files/run/t6187b.scala create mode 100644 test/pending/run/idempotency-partial-functions.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 452957745a..80d628d7ac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -109,9 +109,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL import definitions._ import analyzer._ //Typer - - case class DefaultOverrideMatchAttachment(default: Tree) - object vpmName { val one = newTermName("one") val drop = newTermName("drop") @@ -222,11 +219,11 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // However this is a pain (at least the way I'm going about it) // and I have to think these detailed errors are primarily useful // for beginners, not people writing nested pattern matches. - def checkMatchVariablePatterns(m: Match) { + def checkMatchVariablePatterns(cases: List[CaseDef]) { // A string describing the first variable pattern var vpat: String = null // Using an iterator so we can recognize the last case - val it = m.cases.iterator + val it = cases.iterator def addendum(pat: Tree) = { matchingSymbolInScope(pat) match { @@ -269,7 +266,15 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL */ def translateMatch(match_ : Match): Tree = { val Match(selector, cases) = match_ - checkMatchVariablePatterns(match_) + + val (nonSyntheticCases, defaultOverride) = cases match { + case init :+ last if treeInfo isSyntheticDefaultCase last => + (init, Some(((scrut: Tree) => last.body))) + case _ => + (cases, None) + } + + checkMatchVariablePatterns(nonSyntheticCases) // we don't transform after uncurry // (that would require more sophistication when generating trees, @@ -296,14 +301,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // val packedPt = repeatedToSeq(typer.packedType(match_, context.owner)) - // the alternative to attaching the default case override would be to simply - // append the default to the list of cases and suppress the unreachable case error that may arise (once we detect that...) - val matchFailGenOverride = match_.attachments.get[DefaultOverrideMatchAttachment].map{case DefaultOverrideMatchAttachment(default) => ((scrut: Tree) => default)} - val selectorSym = freshSym(selector.pos, pureType(selectorTp)) setFlag treeInfo.SYNTH_CASE_FLAGS // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental - val combined = combineCases(selector, selectorSym, cases map translateCase(selectorSym, pt), pt, matchOwner, matchFailGenOverride) + val combined = combineCases(selector, selectorSym, nonSyntheticCases map translateCase(selectorSym, pt), pt, matchOwner, defaultOverride) if (Statistics.canEnable) Statistics.stopTimer(patmatNanos, start) combined diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f518712701..5add9b4991 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -30,7 +30,6 @@ trait Typers extends Modes with Adaptations with Tags { import global._ import definitions._ import TypersStats._ - import patmat.DefaultOverrideMatchAttachment final def forArgMode(fun: Tree, mode: Int) = if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode @@ -2604,8 +2603,13 @@ trait Typers extends Modes with Adaptations with Tags { def mkParam(methodSym: Symbol, tp: Type = argTp) = methodSym.newValueParameter(paramName, paramPos.focus, SYNTHETIC) setInfo tp + def mkDefaultCase(body: Tree) = + atPos(tree.pos.makeTransparent) { + CaseDef(Bind(nme.DEFAULT_CASE, Ident(nme.WILDCARD)), body) + } + // `def applyOrElse[A1 <: $argTp, B1 >: $matchResTp](x: A1, default: A1 => B1): B1 = - // ${`$selector match { $cases }` updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x)))}` + // ${`$selector match { $cases; case default$ => default(x) }` def applyOrElseMethodDef = { val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE) @@ -2614,7 +2618,7 @@ trait Typers extends Modes with Adaptations with Tags { val x = mkParam(methodSym, A1.tpe) // applyOrElse's default parameter: - val B1 = methodSym newTypeParameter (newTypeName("B1")) setInfo TypeBounds.empty //lower(resTp) + val B1 = methodSym newTypeParameter (newTypeName("B1")) setInfo TypeBounds.empty val default = methodSym newValueParameter (newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe) val paramSyms = List(x, default) @@ -2624,19 +2628,72 @@ trait Typers extends Modes with Adaptations with Tags { // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it) paramSyms foreach (methodBodyTyper.context.scope enter _) - val match_ = methodBodyTyper.typedMatch(selector, cases, mode, resTp) + // First, type without the default case; only the cases provided + // by the user are typed. The LUB of these becomes `B`, the lower + // bound of `B1`, which in turn is the result type of the default + // case + val match0 = methodBodyTyper.typedMatch(selector, cases, mode, resTp) + val matchResTp = match0.tpe - val matchResTp = match_.tpe B1 setInfo TypeBounds.lower(matchResTp) // patch info + // the default uses applyOrElse's first parameter since the scrut's type has been widened + val match_ = { + val defaultCase = methodBodyTyper.typedCase( + mkDefaultCase(methodBodyTyper.typed1(REF(default) APPLY (REF(x)), mode, B1.tpe).setType(B1.tpe)), argTp, B1.tpe) + treeCopy.Match(match0, match0.selector, match0.cases :+ defaultCase) + } match_ setType B1.tpe - // the default uses applyOrElse's first parameter since the scrut's type has been widened - val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(REF(default) APPLY (REF(x))) - (DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, B1.tpe)), matchResTp) + // SI-6187 Do you really want to know? Okay, here's what's going on here. + // + // Well behaved trees satisfy the property: + // + // typed(tree) == typed(resetLocalAttrs(typed(tree)) + // + // Trees constructed without low-level symbol manipulation get this for free; + // references to local symbols are cleared by `ResetAttrs`, but bind to the + // corresponding symbol in the re-typechecked tree. But PartialFunction synthesis + // doesn't play by these rules. + // + // During typechecking of method bodies, references to method type parameter from + // the declared types of the value parameters should bind to a fresh set of skolems, + // which have been entered into scope by `Namer#methodSig`. A comment therein: + // + // "since the skolemized tparams are in scope, the TypeRefs in vparamSymss refer to skolemized tparams" + // + // But, if we retypecheck the reset `applyOrElse`, the TypeTree of the `default` + // parameter contains no type. Somehow (where?!) it recovers a type that is _almost_ okay: + // `A1 => B1`. But it should really be `A1&0 => B1&0`. In the test, run/t6187.scala, this + // difference results in a type error, as `default.apply(x)` types as `B1`, which doesn't + // conform to the required `B1&0` + // + // I see three courses of action. + // + // 1) synthesize a `asInstanceOf[B1]` below (I tried this first. But... ewwww.) + // 2) install an 'original' TypeTree that will used after ResetAttrs (the solution below) + // 3) Figure out how the almost-correct type is recovered on re-typechecking, and + // substitute in the skolems. + // + // For 2.11, we'll probably shift this transformation back a phase or two, so macros + // won't be affected. But in any case, we should satisfy retypecheckability. + // + val originals: Map[Symbol, Tree] = { + def typedIdent(sym: Symbol) = methodBodyTyper.typedType(Ident(sym), mode) + val A1Tpt = typedIdent(A1) + val B1Tpt = typedIdent(B1) + Map( + x -> A1Tpt, + default -> gen.scalaFunctionConstr(List(A1Tpt), B1Tpt) + ) + } + val rhs = methodBodyTyper.virtualizedMatch(match_, mode, B1.tpe) + val defdef = DefDef(methodSym, Modifiers(methodSym.flags), originals, rhs) + + (defdef, matchResTp) } - // `def isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue ` updateAttachment DefaultOverrideMatchAttachment(FALSE_typed)}` + // `def isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue; case default$ => false } }` def isDefinedAtMethod = { val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos.makeTransparent, FINAL) val paramSym = mkParam(methodSym) @@ -2645,10 +2702,10 @@ trait Typers extends Modes with Adaptations with Tags { methodBodyTyper.context.scope enter paramSym methodSym setInfo MethodType(List(paramSym), BooleanClass.tpe) - val match_ = methodBodyTyper.typedMatch(selector, casesTrue, mode, BooleanClass.tpe) + val defaultCase = mkDefaultCase(FALSE_typed) + val match_ = methodBodyTyper.typedMatch(selector, casesTrue :+ defaultCase, mode, BooleanClass.tpe) - val matchWithDefault = match_ updateAttachment DefaultOverrideMatchAttachment(FALSE_typed) - DefDef(methodSym, methodBodyTyper.virtualizedMatch(matchWithDefault, mode, BooleanClass.tpe)) + DefDef(methodSym, methodBodyTyper.virtualizedMatch(match_, mode, BooleanClass.tpe)) } // only used for @cps annotated partial functions @@ -2693,7 +2750,9 @@ trait Typers extends Modes with Adaptations with Tags { members foreach (m => anonClass.info.decls enter m.symbol) val typedBlock = typedPos(tree.pos, mode, pt) { - Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(New(anonClass.tpe))) + Block(ClassDef(anonClass, NoMods, ListOfNil, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)( + Apply(Select(New(Ident(anonClass.name).setSymbol(anonClass)), nme.CONSTRUCTOR), List()) + )) } if (typedBlock.isErrorTyped) typedBlock diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index 5e7f5777b2..ddc5d94e70 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -297,6 +297,7 @@ trait StdNames { // Compiler internal names val ANYname: NameType = "" val CONSTRUCTOR: NameType = "" + val DEFAULT_CASE: NameType = "defaultCase$" val EQEQ_LOCAL_VAR: NameType = "eqEqTemp$" val FAKE_LOCAL_THIS: NameType = "this$" val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 2cc848d458..bccad69638 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -434,6 +434,13 @@ abstract class TreeInfo { case _ => false } + /** Is this pattern node a synthetic catch-all case, added during PartialFuction synthesis before we know + * whether the user provided cases are exhaustive. */ + def isSyntheticDefaultCase(cdef: CaseDef) = cdef match { + case CaseDef(Bind(nme.DEFAULT_CASE, _), EmptyTree, _) => true + case _ => false + } + /** Does this CaseDef catch Throwable? */ def catchesThrowable(cdef: CaseDef) = catchesAllOf(cdef, ThrowableClass.tpe) diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 5522c47cfa..754adcb80d 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -523,7 +523,11 @@ trait Trees extends api.Trees { self: SymbolTable => override private[scala] def copyAttrs(tree: Tree) = { super.copyAttrs(tree) tree match { - case other: TypeTree => wasEmpty = other.wasEmpty // SI-6648 Critical for correct operation of `resetAttrs`. + case other: TypeTree => + // SI-6648 Critical for correct operation of `resetAttrs`. + wasEmpty = other.wasEmpty + if (other.orig != null) + orig = other.orig.duplicate case _ => } this @@ -984,6 +988,18 @@ trait Trees extends api.Trees { self: SymbolTable => def DefDef(sym: Symbol, mods: Modifiers, rhs: Tree): DefDef = DefDef(sym, mods, mapParamss(sym)(ValDef), rhs) + /** A DefDef with original trees attached to the TypeTree of each parameter */ + def DefDef(sym: Symbol, mods: Modifiers, originalParamTpts: Symbol => Tree, rhs: Tree): DefDef = { + val paramms = mapParamss(sym){ sym => + val vd = ValDef(sym, EmptyTree) + (vd.tpt : @unchecked) match { + case tt: TypeTree => tt setOriginal (originalParamTpts(sym) setPos sym.pos.focus) + } + vd + } + DefDef(sym, mods, paramms, rhs) + } + def DefDef(sym: Symbol, rhs: Tree): DefDef = DefDef(sym, Modifiers(sym.flags), rhs) diff --git a/test/files/run/idempotency-partial-functions.scala b/test/files/run/idempotency-partial-functions.scala deleted file mode 100644 index dd5f1167f1..0000000000 --- a/test/files/run/idempotency-partial-functions.scala +++ /dev/null @@ -1,25 +0,0 @@ -import scala.reflect.runtime.universe._ -import scala.reflect.runtime.{currentMirror => cm} -import scala.tools.reflect.{ToolBox, ToolBoxError} -import scala.tools.reflect.Eval - -object Test extends App { - val partials = reify { - List((false,true)) collect { case (x,true) => x } - } - try { - println(partials.eval) - } catch { - case _: ToolBoxError => println("error!!") - } - try { - val tb = cm.mkToolBox() - val tpartials = tb.typeCheck(partials.tree) - println(tpartials) - val rtpartials = tb.resetAllAttrs(tpartials) - println(tb.eval(rtpartials)) - } catch { - // this is the current behaviour, rather than the desired behavior; see SI-6187 - case _: ToolBoxError => println("error!") - } -} \ No newline at end of file diff --git a/test/files/run/t6187.check b/test/files/run/t6187.check new file mode 100644 index 0000000000..c0ca02923b --- /dev/null +++ b/test/files/run/t6187.check @@ -0,0 +1,32 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> import language.experimental.macros, reflect.macros.Context +import language.experimental.macros +import reflect.macros.Context + +scala> def macroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[List[T]] = { + val r = c.universe.reify { List(t.splice) } + c.Expr[List[T]]( c.resetLocalAttrs(r.tree) ) +} +macroImpl: [T](c: scala.reflect.macros.Context)(t: c.Expr[T])(implicit evidence$1: c.WeakTypeTag[T])c.Expr[List[T]] + +scala> def demo[T](t: T): List[T] = macro macroImpl[T] +demo: [T](t: T)List[T] + +scala> def m[T](t: T): List[List[T]] = + demo( List((t,true)) collect { case (x,true) => x } ) +m: [T](t: T)List[List[T]] + +scala> m(List(1)) +res0: List[List[List[Int]]] = List(List(List(1))) + +scala> // Showing we haven't added unreachable warnings + +scala> List(1) collect { case x => x } +res1: List[Int] = List(1) + +scala> List("") collect { case x => x } +res2: List[String] = List("") + +scala> diff --git a/test/files/run/t6187.scala b/test/files/run/t6187.scala new file mode 100644 index 0000000000..ae642917e7 --- /dev/null +++ b/test/files/run/t6187.scala @@ -0,0 +1,18 @@ +import scala.tools.partest.ReplTest + +object Test extends ReplTest { + override def code = """ +import language.experimental.macros, reflect.macros.Context +def macroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[List[T]] = { + val r = c.universe.reify { List(t.splice) } + c.Expr[List[T]]( c.resetLocalAttrs(r.tree) ) +} +def demo[T](t: T): List[T] = macro macroImpl[T] +def m[T](t: T): List[List[T]] = + demo( List((t,true)) collect { case (x,true) => x } ) +m(List(1)) +// Showing we haven't added unreachable warnings +List(1) collect { case x => x } +List("") collect { case x => x } + """.trim +} diff --git a/test/files/run/t6187b.scala b/test/files/run/t6187b.scala new file mode 100644 index 0000000000..d2d3e9797d --- /dev/null +++ b/test/files/run/t6187b.scala @@ -0,0 +1,5 @@ +object Test extends App { + val x: PartialFunction[Int, Int] = { case 1 => 1 } + val o: Any = "" + assert(x.applyOrElse(0, (_: Int) => o) == "") +} diff --git a/test/pending/run/idempotency-partial-functions.scala b/test/pending/run/idempotency-partial-functions.scala new file mode 100644 index 0000000000..bc0ca706dd --- /dev/null +++ b/test/pending/run/idempotency-partial-functions.scala @@ -0,0 +1,28 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} +import scala.tools.reflect.{ToolBox, ToolBoxError} +import scala.tools.reflect.Eval + +// Related to SI-6187 +// +// Moved to pending as we are currently blocked by the inability +// to reify the parent types of the anoymous function class, +// which are not part of the tree, but rather only part of the +// ClassInfoType. +object Test extends App { + val partials = reify { + List((false,true)) collect { case (x,true) => x } + } + println(Seq(show(partials), showRaw(partials)).mkString("\n\n")) + try { + println(partials.eval) + } catch { + case e: ToolBoxError => println(e) + } + val tb = cm.mkToolBox() + val tpartials = tb.typeCheck(partials.tree) + println(tpartials) + val rtpartials = tb.resetAllAttrs(tpartials) + println(tb.eval(rtpartials)) +} +Test.main(null) \ No newline at end of file -- cgit v1.2.3 From fa3b8040ebfedc721a538476be28a6217f1dec56 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 7 Feb 2013 15:52:40 -0800 Subject: SI-6961 no structural sharing in list serialization Revert list serialization back to what it was in 2.9.x and before. Partial revert of a6fcd70b60 e234978dfd, which fixed SI-5374. The ListBuffer part of the fix remains in place. --- src/library/scala/collection/immutable/List.scala | 33 +++------- test/files/run/t5374.check | 6 -- test/files/run/t5374.scala | 76 ----------------------- 3 files changed, 9 insertions(+), 106 deletions(-) delete mode 100644 test/files/run/t5374.check delete mode 100644 test/files/run/t5374.scala (limited to 'test') diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 55ac3995e9..9765e7c52f 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -55,6 +55,12 @@ import java.io._ * val shorter = mainList.tail // costs nothing as it uses the same 2::1::Nil instances as mainList * }}} * + * @note The functional list is characterized by persistence and structural sharing, thus offering considerable + * performance and space consumption benefits in some scenarios if used correctly. + * However, note that objects having multiple references into the same functional list (that is, + * objects that rely on structural sharing), will be serialized and deserialized with multiple lists, one for + * each reference to it. I.e. structural sharing is lost after serialization/deserialization. + * * @author Martin Odersky and others * @version 2.8 * @since 1.0 @@ -352,25 +358,8 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend override def tail : List[B] = tl override def isEmpty: Boolean = false - private def writeObject(out: ObjectOutputStream) { - out.writeObject(ListSerializeStart) // needed to differentiate with the legacy `::` serialization - out.writeObject(this.hd) - out.writeObject(this.tl) - } - private def readObject(in: ObjectInputStream) { - val obj = in.readObject() - if (obj == ListSerializeStart) { - this.hd = in.readObject().asInstanceOf[B] - this.tl = in.readObject().asInstanceOf[List[B]] - } else oldReadObject(in, obj) - } - - /* The oldReadObject method exists here for compatibility reasons. - * :: objects used to be serialized by serializing all the elements to - * the output stream directly, but this was broken (see SI-5374). - */ - private def oldReadObject(in: ObjectInputStream, firstObject: AnyRef) { + val firstObject = in.readObject() hd = firstObject.asInstanceOf[B] assert(hd != ListSerializeEnd) var current: ::[B] = this @@ -378,14 +367,14 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend case ListSerializeEnd => current.tl = Nil return - case a : Any => + case a => val list : ::[B] = new ::(a.asInstanceOf[B], Nil) current.tl = list current = list } } - private def oldWriteObject(out: ObjectOutputStream) { + private def writeObject(out: ObjectOutputStream) { var xs: List[B] = this while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail } out.writeObject(ListSerializeEnd) @@ -653,10 +642,6 @@ object List extends SeqFactory[List] { } } -/** Only used for list serialization */ -@SerialVersionUID(0L - 8287891243975527522L) -private[scala] case object ListSerializeStart - /** Only used for list serialization */ @SerialVersionUID(0L - 8476791151975527571L) private[scala] case object ListSerializeEnd diff --git a/test/files/run/t5374.check b/test/files/run/t5374.check deleted file mode 100644 index 6be88d77ec..0000000000 --- a/test/files/run/t5374.check +++ /dev/null @@ -1,6 +0,0 @@ -ListBuffer(1, 2, 3, 1) -ListBuffer(1, 2, 3, 1) -ListBuffer() -List(1, 2, 3, 4, 5) -List(1, 2, 3) -ok \ No newline at end of file diff --git a/test/files/run/t5374.scala b/test/files/run/t5374.scala deleted file mode 100644 index 9b1671e795..0000000000 --- a/test/files/run/t5374.scala +++ /dev/null @@ -1,76 +0,0 @@ - - - -import collection.mutable.ListBuffer -import java.io._ - - - -object Test { - - def main(args: Array[String]) { - ticketExample() - emptyListBuffer() - list() - legacyList() - objectWithMultipleLists() - } - - def inAndOut[T <: AnyRef](obj: T): T = { - val baos = new ByteArrayOutputStream - val oos = new ObjectOutputStream(baos) - oos.writeObject( obj ) - val bais = new ByteArrayInputStream( baos.toByteArray ) - val ois = new ObjectInputStream(bais) - ois.readObject.asInstanceOf[T] - } - - def ticketExample() { - val lb = inAndOut(ListBuffer(1, 2, 3)) - val lb2 = ListBuffer[Int]() ++= lb - - lb2 ++= List(1) - lb ++= List(1) - println(lb) - println(lb2) - } - - def emptyListBuffer() { - val lb = inAndOut(ListBuffer[Int]()) - - println(lb) - } - - def list() { - val l = inAndOut(List(1, 2, 3, 4, 5)) - - println(l) - } - - // this byte array corresponds to what List(1, 2, 3) used to be serialized to prior to this fix - val listBytes = Array[Byte](-84, -19, 0, 5, 115, 114, 0, 39, 115, 99, 97, 108, 97, 46, 99, 111, 108, 108, 101, 99, 116, 105, 111, 110, 46, 105, 109, 109, 117, 116, 97, 98, 108, 101, 46, 36, 99, 111, 108, 111, 110, 36, 99, 111, 108, 111, 110, -118, 92, 99, 91, -10, -40, -7, 109, 3, 0, 2, 76, 0, 43, 115, 99, 97, 108, 97, 36, 99, 111, 108, 108, 101, 99, 116, 105, 111, 110, 36, 105, 109, 109, 117, 116, 97, 98, 108, 101, 36, 36, 99, 111, 108, 111, 110, 36, 99, 111, 108, 111, 110, 36, 36, 104, 100, 116, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 76, 0, 2, 116, 108, 116, 0, 33, 76, 115, 99, 97, 108, 97, 47, 99, 111, 108, 108, 101, 99, 116, 105, 111, 110, 47, 105, 109, 109, 117, 116, 97, 98, 108, 101, 47, 76, 105, 115, 116, 59, 120, 112, 115, 114, 0, 17, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 73, 110, 116, 101, 103, 101, 114, 18, -30, -96, -92, -9, -127, -121, 56, 2, 0, 1, 73, 0, 5, 118, 97, 108, 117, 101, 120, 114, 0, 16, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 78, 117, 109, 98, 101, 114, -122, -84, -107, 29, 11, -108, -32, -117, 2, 0, 0, 120, 112, 0, 0, 0, 1, 115, 113, 0, 126, 0, 4, 0, 0, 0, 2, 115, 113, 0, 126, 0, 4, 0, 0, 0, 3, 115, 114, 0, 44, 115, 99, 97, 108, 97, 46, 99, 111, 108, 108, 101, 99, 116, 105, 111, 110, 46, 105, 109, 109, 117, 116, 97, 98, 108, 101, 46, 76, 105, 115, 116, 83, 101, 114, 105, 97, 108, 105, 122, 101, 69, 110, 100, 36, -118, 92, 99, 91, -9, 83, 11, 109, 2, 0, 0, 120, 112, 120) - - def legacyList() { - val bais = new ByteArrayInputStream(listBytes) - val ois = new ObjectInputStream(bais) - val l = ois.readObject() - - println(l) - } - - class Foo extends Serializable { - val head = List(1, 2, 3) - val last = head.tail.tail - def structuralSharing: Boolean = head.tail.tail eq last - - assert(structuralSharing) - } - - def objectWithMultipleLists() { - val foo = inAndOut(new Foo) - - if (foo.structuralSharing) println("ok") - else println("no structural sharing") - } - -} -- cgit v1.2.3 From 5258b63740c9fb1be3c531e1dc1399fb0dc55569 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 8 Feb 2013 00:05:55 +0100 Subject: SI-7096 SubstSymMap copies trees before modifying their symbols I removed some strange code in a06d31f6a2 and replaced it by something incorrect: SubstSymMap should never have side-effects: otherwise, calling 'tpe1 <: tpe2' for instance would modify the symbols in annotations of tpe2. SubstSymMap now always creates new trees before changing them. --- src/reflect/scala/reflect/internal/Types.scala | 42 ++++++++++++++++++-------- test/files/run/t7096.check | 2 ++ test/files/run/t7096.scala | 36 ++++++++++++++++++++++ 3 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 test/files/run/t7096.check create mode 100644 test/files/run/t7096.scala (limited to 'test') diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 0dd98fb6ae..b06ab6c929 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4698,23 +4698,39 @@ trait Types extends api.Types { self: SymbolTable => } } - override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { - object trans extends TypeMapTransformer { + object mapTreeSymbols extends TypeMapTransformer { + val strictCopy = newStrictTreeCopier - def termMapsTo(sym: Symbol) = from indexOf sym match { - case -1 => None - case idx => Some(to(idx)) - } + def termMapsTo(sym: Symbol) = from indexOf sym match { + case -1 => None + case idx => Some(to(idx)) + } - override def transform(tree: Tree) = { - termMapsTo(tree.symbol) match { - case Some(tosym) => tree.symbol = tosym - case None => () - } - super.transform(tree) + // if tree.symbol is mapped to another symbol, passes the new symbol into the + // constructor `trans` and sets the symbol and the type on the resulting tree. + def transformIfMapped(tree: Tree)(trans: Symbol => Tree) = termMapsTo(tree.symbol) match { + case Some(toSym) => trans(toSym) setSymbol toSym setType tree.tpe + case None => tree + } + + // changes trees which refer to one of the mapped symbols. trees are copied before attributes are modified. + override def transform(tree: Tree) = { + // super.transform maps symbol references in the types of `tree`. it also copies trees where necessary. + super.transform(tree) match { + case id @ Ident(_) => + transformIfMapped(id)(toSym => + strictCopy.Ident(id, toSym.name)) + + case sel @ Select(qual, name) => + transformIfMapped(sel)(toSym => + strictCopy.Select(sel, qual, toSym.name)) + + case tree => tree } } - trans.transform(tree) + } + override def mapOver(tree: Tree, giveup: ()=>Nothing): Tree = { + mapTreeSymbols.transform(tree) } } diff --git a/test/files/run/t7096.check b/test/files/run/t7096.check new file mode 100644 index 0000000000..6f1cef6c43 --- /dev/null +++ b/test/files/run/t7096.check @@ -0,0 +1,2 @@ +testing symbol List(method foo, class Base, package ano, package ), param value x, xRefs List(x) +testing symbol List(method foo, class Sub, package ano, package ), param value x, xRefs List(x) diff --git a/test/files/run/t7096.scala b/test/files/run/t7096.scala new file mode 100644 index 0000000000..e9c0323c2e --- /dev/null +++ b/test/files/run/t7096.scala @@ -0,0 +1,36 @@ +import scala.tools.partest._ +import scala.tools.nsc._ + +object Test extends CompilerTest { + import global._ + import definitions._ + + override def code = """ +package ano + +class ann(x: Any) extends annotation.TypeConstraint + +abstract class Base { + def foo(x: String): String @ann(x.trim()) +} + +class Sub extends Base { + def foo(x: String): String @ann(x.trim()) = x +} + """ + + object syms extends SymsInPackage("ano") + import syms._ + + def check(source: String, unit: global.CompilationUnit) { + afterTyper { + terms.filter(_.name.toString == "foo").foreach(sym => { + val xParam = sym.tpe.paramss.flatten.head + val annot = sym.tpe.finalResultType.annotations.head + val xRefs = annot.args.head.filter(t => t.symbol == xParam) + println(s"testing symbol ${sym.ownerChain}, param $xParam, xRefs $xRefs") + assert(xRefs.length == 1, xRefs) + }) + } + } +} -- cgit v1.2.3 From 09ef8730d13eff1cf200bbfb0f6fda7f6d72524a Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Fri, 14 Dec 2012 17:09:55 +0400 Subject: SI-6591 Reify and path-dependent types Reification scheme changed. Now Select an SelectFromTypeTree trees reified appropriately, as Select and SelectFromTypeTree accordingly. Packages and Predef object was excluded in order not to break the existing reification scheme and not to break tests which rely on it. Reified free terms can contain flag to make reified values become stable identifiers. For example in the case of reify_newimpl_15.scala class C { type T reify { val v: List[T] = List(2) } } class C reified as free term C$value, and List[C.T] becomes List[C$value().T], so C$value.apply() need to pass stability test isExprSafeToInline at scala.reflect.internal.TreeInfo. For this purpose special case for reified free terms was added to isExprSafeToInline function. test run/reify_newipl_30 disabled due to SI-7082 test t6591_4 moved to pending due to SI-7083 --- .../scala/reflect/reify/codegen/GenSymbols.scala | 2 ++ .../scala/reflect/reify/codegen/GenTrees.scala | 24 ++++++++++++++-------- .../scala/reflect/reify/utils/Extractors.scala | 8 ++++---- .../scala/tools/reflect/ToolBoxFactory.scala | 7 +++++-- src/reflect/scala/reflect/internal/Flags.scala | 2 +- src/reflect/scala/reflect/internal/StdNames.scala | 1 + src/reflect/scala/reflect/internal/TreeInfo.scala | 6 ++++++ test/files/run/reify_newimpl_30.check | 3 ++- test/files/run/reify_newimpl_30.scala | 7 ++++--- test/files/run/t6591_1.check | 1 + test/files/run/t6591_1.scala | 19 +++++++++++++++++ test/files/run/t6591_2.check | 1 + test/files/run/t6591_2.scala | 19 +++++++++++++++++ test/files/run/t6591_3.check | 1 + test/files/run/t6591_3.scala | 17 +++++++++++++++ test/files/run/t6591_5.check | 1 + test/files/run/t6591_5.scala | 23 +++++++++++++++++++++ test/files/run/t6591_6.check | 1 + test/files/run/t6591_6.scala | 24 ++++++++++++++++++++++ test/pending/run/t6591_4.check | 1 + test/pending/run/t6591_4.scala | 17 +++++++++++++++ 21 files changed, 165 insertions(+), 20 deletions(-) create mode 100644 test/files/run/t6591_1.check create mode 100644 test/files/run/t6591_1.scala create mode 100644 test/files/run/t6591_2.check create mode 100644 test/files/run/t6591_2.scala create mode 100644 test/files/run/t6591_3.check create mode 100644 test/files/run/t6591_3.scala create mode 100644 test/files/run/t6591_5.check create mode 100644 test/files/run/t6591_5.scala create mode 100644 test/files/run/t6591_6.check create mode 100644 test/files/run/t6591_6.scala create mode 100644 test/pending/run/t6591_4.check create mode 100644 test/pending/run/t6591_4.scala (limited to 'test') diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 22a834d2e4..0d795e3783 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -100,6 +100,8 @@ trait GenSymbols { reifyIntoSymtab(binding.symbol) { sym => if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") val name = newTermName(nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else "")) + // Flag is set here to make reified free value pass stability test during reflective compilation + if (!sym.isMutable) sym setFlag reflect.internal.Flags.STABLE if (sym.isCapturedVariable) { assert(binding.isInstanceOf[Ident], showRaw(binding)) val capturedBinding = referenceCapturedVariable(sym) diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 06e287f62f..7c7a79e774 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -163,11 +163,11 @@ trait GenTrees { } } - private def reifyBoundType(tree: Tree): Tree = { + private def reifyBoundType(tree: RefTree): Tree = { val sym = tree.symbol val tpe = tree.tpe - def reifyBoundType(tree: Tree): Tree = { + def reifyBoundType(tree: RefTree): Tree = { assert(tpe != null, "unexpected: bound type that doesn't have a tpe: " + showRaw(tree)) // if a symbol or a type of the scrutinee are local to reifee @@ -196,13 +196,19 @@ trait GenTrees { mirrorBuildCall(nme.TypeTree, spliced) } } - else if (sym.isLocatable) { - if (reifyDebug) println("tpe is locatable: reify as Ident(%s)".format(sym)) - mirrorBuildCall(nme.Ident, reify(sym)) - } - else { - if (reifyDebug) println("tpe is not locatable: reify as TypeTree(%s)".format(tpe)) - mirrorBuildCall(nme.TypeTree, reify(tpe)) + else tree match { + case Select(qual, name) if !qual.symbol.isPackage && !qual.symbol.isPackageObject && qual.symbol != definitions.PredefModule => + if (reifyDebug) println(s"reifying Select($qual, $name)") + mirrorCall(nme.Select, reify(qual), reify(name)) + case SelectFromTypeTree(qual, name) => + if (reifyDebug) println(s"reifying SelectFromTypeTree($qual, $name)") + mirrorCall(nme.SelectFromTypeTree, reify(qual), reify(name)) + case _ if sym.isLocatable => + if (reifyDebug) println(s"tpe is locatable: reify as Ident($sym)") + mirrorBuildCall(nme.Ident, reify(sym)) + case _ => + if (reifyDebug) println(s"tpe is not locatable: reify as TypeTree($tpe)") + mirrorBuildCall(nme.TypeTree, reify(tpe)) } } } diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index ebbcb95481..134ae13890 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -263,12 +263,12 @@ trait Extractors { } object BoundType { - def unapply(tree: Tree): Option[Tree] = tree match { - case Select(_, name) if name.isTypeName => + def unapply(tree: Tree): Option[RefTree] = tree match { + case tree @ Select(_, name) if name.isTypeName => Some(tree) - case SelectFromTypeTree(_, name) if name.isTypeName => + case tree @ SelectFromTypeTree(_, _) => Some(tree) - case Ident(name) if name.isTypeName => + case tree @ Ident(name) if name.isTypeName => Some(tree) case _ => None diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 95135b84e0..fdc790a920 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -212,7 +212,11 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val meth = obj.moduleClass.newMethod(newTermName(wrapperMethodName)) def makeParam(schema: (FreeTermSymbol, TermName)) = { val (fv, name) = schema - meth.newValueParameter(name) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType)) + /* Free term symbol `fv` can contain flag which was set by + * `scala.reflect.reify.codegen.GenSymbols.reifyFreeTerm` method. + * It is recovered here, so value parameter can pass `isExprSafeToInline` + * test in `scala.reflect.internal.TreeInfo`. */ + meth.newValueParameter(name, newFlags = if (fv.hasStableFlag) Flags.STABLE else 0) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType)) } meth setInfo MethodType(freeTerms.map(makeParam).toList, AnyClass.tpe) minfo.decls enter meth @@ -418,4 +422,3 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => def eval(tree: u.Tree): Any = compile(tree)() } } - diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala index a0362c8921..5853315479 100644 --- a/src/reflect/scala/reflect/internal/Flags.scala +++ b/src/reflect/scala/reflect/internal/Flags.scala @@ -274,7 +274,7 @@ class Flags extends ModifierFlags { * from Modifiers. Others which may be applied at creation time are: * SYNTHETIC. */ - final val ValueParameterFlags = BYNAMEPARAM | IMPLICIT | DEFAULTPARAM + final val ValueParameterFlags = BYNAMEPARAM | IMPLICIT | DEFAULTPARAM | STABLE final val BeanPropertyFlags = DEFERRED | OVERRIDE | STATIC final val VarianceFlags = COVARIANT | CONTRAVARIANT diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index ddc5d94e70..bcda2bc1ae 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -607,6 +607,7 @@ trait StdNames { val RootPackage: NameType = "RootPackage" val RootClass: NameType = "RootClass" val Select: NameType = "Select" + val SelectFromTypeTree: NameType = "SelectFromTypeTree" val StringContext: NameType = "StringContext" val This: NameType = "This" val ThisType: NameType = "ThisType" diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 8b5dc80c83..f83106aaef 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -89,6 +89,12 @@ abstract class TreeInfo { tree.symbol.isStable && isExprSafeToInline(qual) case TypeApply(fn, _) => isExprSafeToInline(fn) + /* Special case for reified free terms. During reflective compilation, + * reified value recovered flag from free term and wrapped into a + * Function object, so it can pass stability check here to become a stable + * identifier.*/ + case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => + free.symbol.hasStableFlag && isExprSafeToInline(free) case Apply(fn, List()) => /* Note: After uncurry, field accesses are represented as Apply(getter, Nil), * so an Apply can also be pure. diff --git a/test/files/run/reify_newimpl_30.check b/test/files/run/reify_newimpl_30.check index c23af69b08..29baac911e 100644 --- a/test/files/run/reify_newimpl_30.check +++ b/test/files/run/reify_newimpl_30.check @@ -1 +1,2 @@ -List(2) +reflective toolbox has failed: +unresolved free type variables (namely: C defined by in reify_newimpl_30.scala:7:11). have you forgot to use TypeTag annotations for type parameters external to a reifee? if you have troubles tracking free type variables, consider using -Xlog-free-types diff --git a/test/files/run/reify_newimpl_30.scala b/test/files/run/reify_newimpl_30.scala index 573d05a630..bc34f1bb6c 100644 --- a/test/files/run/reify_newimpl_30.scala +++ b/test/files/run/reify_newimpl_30.scala @@ -1,5 +1,5 @@ import scala.reflect.runtime.universe._ -import scala.tools.reflect.ToolBox +import scala.tools.reflect.{ ToolBox, ToolBoxError } import scala.tools.reflect.Eval object Test extends App { @@ -9,9 +9,10 @@ object Test extends App { val code = reify { List[C#T](2) } - println(code.eval) + try { println(code.eval) } + catch { case e: ToolBoxError => println(e.getMessage) } } new C } -} \ No newline at end of file +} diff --git a/test/files/run/t6591_1.check b/test/files/run/t6591_1.check new file mode 100644 index 0000000000..b6cb6c286d --- /dev/null +++ b/test/files/run/t6591_1.check @@ -0,0 +1 @@ +Block(List(ValDef(Modifiers(), newTermName("v"), Select(Ident(A), newTypeName("I")), Select(Ident(A), newTermName("impl")))), Ident(newTermName("v"))) diff --git a/test/files/run/t6591_1.scala b/test/files/run/t6591_1.scala new file mode 100644 index 0000000000..6dd9a1d9fb --- /dev/null +++ b/test/files/run/t6591_1.scala @@ -0,0 +1,19 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox +import scala.tools.reflect.Eval + +trait O { trait I } + +object A extends O { + val impl = new I {} +} + +object Test extends App { + val code = reify { + val v: A.I = A.impl + v + } + println(showRaw(code.tree)) + + val v: A.I = code.eval +} diff --git a/test/files/run/t6591_2.check b/test/files/run/t6591_2.check new file mode 100644 index 0000000000..b2d5797cbf --- /dev/null +++ b/test/files/run/t6591_2.check @@ -0,0 +1 @@ +Block(List(ValDef(Modifiers(), newTermName("v"), SelectFromTypeTree(Ident(A), newTypeName("I")), Select(Apply(Select(New(Ident(A)), nme.CONSTRUCTOR), List()), newTermName("impl")))), Ident(newTermName("v"))) diff --git a/test/files/run/t6591_2.scala b/test/files/run/t6591_2.scala new file mode 100644 index 0000000000..6214308dab --- /dev/null +++ b/test/files/run/t6591_2.scala @@ -0,0 +1,19 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox +import scala.tools.reflect.Eval + +trait O { trait I } + +class A extends O { + val impl = new I {} +} + +object Test extends App { + val code = reify { + val v: A#I = (new A).impl + v + } + println(showRaw(code.tree)) + + val v: A#I = code.eval +} diff --git a/test/files/run/t6591_3.check b/test/files/run/t6591_3.check new file mode 100644 index 0000000000..a7b594ba65 --- /dev/null +++ b/test/files/run/t6591_3.check @@ -0,0 +1 @@ +Block(List(ValDef(Modifiers(), newTermName("v"), Select(This(newTypeName("A")), newTypeName("I")), Apply(Select(New(Select(This(newTypeName("A")), newTypeName("I"))), nme.CONSTRUCTOR), List()))), Ident(newTermName("v"))) diff --git a/test/files/run/t6591_3.scala b/test/files/run/t6591_3.scala new file mode 100644 index 0000000000..b73a7baf48 --- /dev/null +++ b/test/files/run/t6591_3.scala @@ -0,0 +1,17 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox +import scala.tools.reflect.Eval + +class O { class I } + +object A extends O { + val code = reify { + val v: I = new I + v + } + println(showRaw(code.tree)) +} + +object Test extends App { + val v: A.I = A.code.eval +} diff --git a/test/files/run/t6591_5.check b/test/files/run/t6591_5.check new file mode 100644 index 0000000000..8f1c2b3ede --- /dev/null +++ b/test/files/run/t6591_5.check @@ -0,0 +1 @@ +Expr(Block(List(ValDef(Modifiers(), newTermName("v"), Select(Select(This(newTypeName("A")), newTermName("x")), newTypeName("I")), Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("$qmark$qmark$qmark")))), Ident(newTermName("v")))) diff --git a/test/files/run/t6591_5.scala b/test/files/run/t6591_5.scala new file mode 100644 index 0000000000..18d6f90a9b --- /dev/null +++ b/test/files/run/t6591_5.scala @@ -0,0 +1,23 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox +import scala.tools.reflect.Eval +import java.lang.reflect.InvocationTargetException + +class O { class I } + +object A extends O { + val x = new O + val code = reify { + val v: x.I = ??? + v + } + println(showRaw(code)) +} + +object Test extends App { + try { + val v: A.x.I = A.code.eval + } catch { + case ex: InvocationTargetException if ex.getCause.isInstanceOf[NotImplementedError] => + } +} diff --git a/test/files/run/t6591_6.check b/test/files/run/t6591_6.check new file mode 100644 index 0000000000..5ddf7600f1 --- /dev/null +++ b/test/files/run/t6591_6.check @@ -0,0 +1 @@ +Expr(Block(List(ValDef(Modifiers(), newTermName("v"), Select(Select(Ident(newTermName("A")), newTermName("x")), newTypeName("I")), Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("$qmark$qmark$qmark")))), Ident(newTermName("v")))) diff --git a/test/files/run/t6591_6.scala b/test/files/run/t6591_6.scala new file mode 100644 index 0000000000..2eee87928d --- /dev/null +++ b/test/files/run/t6591_6.scala @@ -0,0 +1,24 @@ +import scala.language.existentials +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox +import scala.tools.reflect.Eval +import java.lang.reflect.InvocationTargetException + +class O { class I } + +class A extends O { + val x = new O + val code = reify { + val v: x.I = ??? + v + } + println(showRaw(code)) +} + +object Test extends App { + try { + val v = (new A).code.eval + } catch { + case ex: InvocationTargetException if ex.getCause.isInstanceOf[NotImplementedError] => + } +} diff --git a/test/pending/run/t6591_4.check b/test/pending/run/t6591_4.check new file mode 100644 index 0000000000..0f1c0489e9 --- /dev/null +++ b/test/pending/run/t6591_4.check @@ -0,0 +1 @@ +Expr(Block(List(ValDef(Modifiers(), newTermName("v"), Select(Ident(newTermName("A")), newTypeName("I")), Apply(Select(New(Select(Ident(newTermName("A")), newTypeName("I"))), nme.CONSTRUCTOR), List()))), Ident(newTermName("v")))) diff --git a/test/pending/run/t6591_4.scala b/test/pending/run/t6591_4.scala new file mode 100644 index 0000000000..f20c8e6127 --- /dev/null +++ b/test/pending/run/t6591_4.scala @@ -0,0 +1,17 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox +import scala.tools.reflect.Eval + +class O { class I } + +class A extends O { + val code = reify { + val v: I = new I + v + } + println(showRaw(code)) +} + +object Test extends App { + val v: A#I = (new A).code.eval +} -- cgit v1.2.3 From ce867c74572b51cfcb6ac3e3bfa9dce36cc0b638 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 8 Feb 2013 14:08:54 +0100 Subject: term and type reftrees are now reified uniformly Top-level (i.e. owned by a package) => Ident(symbol) Nested (i.e. owned by an object or a package object) => Select(owner, symbol) Inner (i.e. owned by a static class) => selectTerm/selectType(owner, name) Non-locatable (i.e. everything else) => see GenTrees.scala for more details Changes w.r.t the previous approaches: * Top-level refs are no longer reified as Select(This(package), symbol). Proposed reification scheme is as resistant to resetAttrs as previous one, but is at the same time much shorter. * Refs to definitions from package objects are no longer Ident(symbol). Otherwise reflective compilation of things like `_ :: _` fails. * Contents of Predef._ and scala._ are no longer treated specially. This increases the size of reificode, but is more hygienic. --- .../scala/reflect/reify/codegen/GenTrees.scala | 12 ++++++---- test/files/run/freetypes_false_alarm1.check | 2 +- test/files/run/idempotency-case-classes.check | 2 +- test/files/run/idempotency-lazy-vals.check | 2 +- test/files/run/idempotency-this.check | 6 ++--- .../run/macro-typecheck-macrosdisabled2.check | 2 +- test/files/run/reify_ann1a.check | 24 +++++++++---------- test/files/run/reify_ann2a.check | 28 +++++++++++----------- test/files/run/reify_copypaste2.check | 2 +- test/files/run/reify_newimpl_35.check | 2 +- test/files/run/showraw_tree.check | 4 ++-- test/files/run/showraw_tree_ids.check | 4 ++-- test/files/run/showraw_tree_kinds.check | 4 ++-- test/files/run/showraw_tree_types_ids.check | 20 +++++++++------- test/files/run/showraw_tree_types_typed.check | 20 +++++++++------- test/files/run/showraw_tree_types_untyped.check | 4 ++-- test/files/run/showraw_tree_ultimate.check | 20 +++++++++------- test/files/run/t2886.check | 2 +- test/files/run/t5225_2.check | 2 +- test/files/run/t5271_2.check | 2 +- test/files/run/t5271_3.check | 2 +- test/files/run/t6591_5.check | 2 +- test/files/run/t6591_6.check | 2 +- .../run/toolbox_typecheck_macrosdisabled2.check | 2 +- 24 files changed, 90 insertions(+), 82 deletions(-) (limited to 'test') diff --git a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala index 7c7a79e774..9894e359b4 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenTrees.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenTrees.scala @@ -153,10 +153,12 @@ trait GenTrees { else mirrorCall(nme.Ident, reify(name)) case Select(qual, name) => - if (sym == NoSymbol || sym.name == name) - reifyProduct(tree) - else - reifyProduct(Select(qual, sym.name)) + if (qual.symbol != null && qual.symbol.isPackage) { + mirrorBuildCall(nme.Ident, reify(sym)) + } else { + val effectiveName = if (sym != null && sym != NoSymbol) sym.name else name + reifyProduct(Select(qual, effectiveName)) + } case _ => throw new Error("internal error: %s (%s, %s) is not supported".format(tree, tree.productPrefix, tree.getClass)) @@ -197,7 +199,7 @@ trait GenTrees { } } else tree match { - case Select(qual, name) if !qual.symbol.isPackage && !qual.symbol.isPackageObject && qual.symbol != definitions.PredefModule => + case Select(qual, name) if !qual.symbol.isPackage => if (reifyDebug) println(s"reifying Select($qual, $name)") mirrorCall(nme.Select, reify(qual), reify(name)) case SelectFromTypeTree(qual, name) => diff --git a/test/files/run/freetypes_false_alarm1.check b/test/files/run/freetypes_false_alarm1.check index a9df3544ac..085b3ee50b 100644 --- a/test/files/run/freetypes_false_alarm1.check +++ b/test/files/run/freetypes_false_alarm1.check @@ -1 +1 @@ -List[Int] +scala.List[Int] diff --git a/test/files/run/idempotency-case-classes.check b/test/files/run/idempotency-case-classes.check index 80d178cba3..e0453883ff 100644 --- a/test/files/run/idempotency-case-classes.check +++ b/test/files/run/idempotency-case-classes.check @@ -50,6 +50,6 @@ C(2,3) Some.apply[(Int, Int)](Tuple2.apply[Int, Int](x$0.x, x$0.y)); private def readResolve(): Object = C }; - scala.this.Predef.println(C.apply(2, 3)) + Predef.println(C.apply(2, 3)) } error! diff --git a/test/files/run/idempotency-lazy-vals.check b/test/files/run/idempotency-lazy-vals.check index 4dcbf96184..15afa5303c 100644 --- a/test/files/run/idempotency-lazy-vals.check +++ b/test/files/run/idempotency-lazy-vals.check @@ -18,6 +18,6 @@ }; val c: C = new C(); import c._; - c.x.*(scala.this.Predef.implicitly[Int](c.y)) + c.x.*(Predef.implicitly[Int](c.y)) } error! diff --git a/test/files/run/idempotency-this.check b/test/files/run/idempotency-this.check index 08738c4565..8faf703d97 100644 --- a/test/files/run/idempotency-this.check +++ b/test/files/run/idempotency-this.check @@ -1,4 +1,4 @@ List() -immutable.this.List.apply[String]("") -Apply(TypeApply(Select(Select(This(newTypeName("immutable")), scala.collection.immutable.List), newTermName("apply")), List(TypeTree().setOriginal(Ident(newTypeName("String"))))), List(Literal(Constant("")))) -error! +List.apply[String]("") +Apply(TypeApply(Select(Ident(scala.collection.immutable.List), newTermName("apply")), List(TypeTree().setOriginal(Select(Ident(scala.Predef), newTypeName("String"))))), List(Literal(Constant("")))) +List() diff --git a/test/files/run/macro-typecheck-macrosdisabled2.check b/test/files/run/macro-typecheck-macrosdisabled2.check index 7bdd1d6a3a..27d15d47af 100644 --- a/test/files/run/macro-typecheck-macrosdisabled2.check +++ b/test/files/run/macro-typecheck-macrosdisabled2.check @@ -10,7 +10,7 @@ def apply[U >: Nothing <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Tree = { val $u: U = $m$untyped.universe; val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror]; - $u.Apply.apply($u.Select.apply($u.Select.apply($u.build.Ident($m.staticPackage("scala")), $u.newTermName("Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2)))) + $u.Apply.apply($u.Select.apply($u.build.Ident($m.staticModule("scala.Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2)))) } }; new $treecreator1() diff --git a/test/files/run/reify_ann1a.check b/test/files/run/reify_ann1a.check index 244be27aa7..99a966f38b 100644 --- a/test/files/run/reify_ann1a.check +++ b/test/files/run/reify_ann1a.check @@ -1,28 +1,28 @@ { - @new ann(immutable.this.List.apply("1a")) @new ann(immutable.this.List.apply("1b")) class C[@new ann(immutable.this.List.apply("2a")) @new ann(immutable.this.List.apply("2b")) T >: Nothing <: Any] extends AnyRef { - @new ann(immutable.this.List.apply("3a")) @new ann(immutable.this.List.apply("3b")) private[this] val x: T @ann(immutable.this.List.apply("4a")) @ann(immutable.this.List.apply("4b")) = _; - def (@new ann(immutable.this.List.apply("3a")) @new ann(immutable.this.List.apply("3b")) x: T @ann(immutable.this.List.apply("4a")) @ann(immutable.this.List.apply("4b"))) = { + @new ann(List.apply("1a")) @new ann(List.apply("1b")) class C[@new ann(List.apply("2a")) @new ann(List.apply("2b")) T >: Nothing <: Any] extends AnyRef { + @new ann(List.apply("3a")) @new ann(List.apply("3b")) private[this] val x: T @ann(List.apply("4a")) @ann(List.apply("4b")) = _; + def (@new ann(List.apply("3a")) @new ann(List.apply("3b")) x: T @ann(List.apply("4a")) @ann(List.apply("4b"))) = { super.(); () }; - @new ann(immutable.this.List.apply("5a")) @new ann(immutable.this.List.apply("5b")) def f(x: Int @ann(immutable.this.List.apply("6a")) @ann(immutable.this.List.apply("6b"))) = { - @new ann(immutable.this.List.apply("7a")) @new ann(immutable.this.List.apply("7b")) val r = x.$plus(3): @ann(immutable.this.List.apply("8a")): @ann(immutable.this.List.apply("8b")); - val s = (4: Int @ann(immutable.this.List.apply("9a")) @ann(immutable.this.List.apply("9b"))); + @new ann(List.apply("5a")) @new ann(List.apply("5b")) def f(x: Int @ann(List.apply("6a")) @ann(List.apply("6b"))) = { + @new ann(List.apply("7a")) @new ann(List.apply("7b")) val r = x.$plus(3): @ann(List.apply("8a")): @ann(List.apply("8b")); + val s = (4: Int @ann(List.apply("9a")) @ann(List.apply("9b"))); r.$plus(s) } }; () } { - @ann(immutable.this.List.apply[String]("1a")) @ann(immutable.this.List.apply[String]("1b")) class C[@ann(immutable.this.List.apply[String]("2a")) @ann(immutable.this.List.apply[String]("2b")) T] extends AnyRef { - @ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) private[this] val x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a")) = _; - def (@ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a"))): C[T] = { + @ann(List.apply[String]("1a")) @ann(List.apply[String]("1b")) class C[@ann(List.apply[String]("2a")) @ann(List.apply[String]("2b")) T] extends AnyRef { + @ann(List.apply[String]("3a")) @ann(List.apply[String]("3b")) private[this] val x: T @ann(List.apply[String]("4b")) @ann(List.apply[String]("4a")) = _; + def (@ann(List.apply[String]("3a")) @ann(List.apply[String]("3b")) x: T @ann(List.apply[String]("4b")) @ann(List.apply[String]("4a"))): C[T] = { C.super.(); () }; - @ann(immutable.this.List.apply[String]("5a")) @ann(immutable.this.List.apply[String]("5b")) def f(x: Int @ann(immutable.this.List.apply[String]("6b")) @ann(immutable.this.List.apply[String]("6a"))): Int = { - @ann(immutable.this.List.apply[String]("7a")) @ann(immutable.this.List.apply[String]("7b")) val r: Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a")) = ((x.+(3): Int @ann(immutable.this.List.apply[String]("8a"))): Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a"))); - val s: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a")) = (4: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a"))); + @ann(List.apply[String]("5a")) @ann(List.apply[String]("5b")) def f(x: Int @ann(List.apply[String]("6b")) @ann(List.apply[String]("6a"))): Int = { + @ann(List.apply[String]("7a")) @ann(List.apply[String]("7b")) val r: Int @ann(List.apply[String]("8b")) @ann(List.apply[String]("8a")) = ((x.+(3): Int @ann(List.apply[String]("8a"))): Int @ann(List.apply[String]("8b")) @ann(List.apply[String]("8a"))); + val s: Int @ann(List.apply[String]("9b")) @ann(List.apply[String]("9a")) = (4: Int @ann(List.apply[String]("9b")) @ann(List.apply[String]("9a"))); r.+(s) } }; diff --git a/test/files/run/reify_ann2a.check b/test/files/run/reify_ann2a.check index 934af54802..ccbcb4c31e 100644 --- a/test/files/run/reify_ann2a.check +++ b/test/files/run/reify_ann2a.check @@ -1,20 +1,20 @@ { class ann extends StaticAnnotation { - private[this] val bar: List[String] = _; - def (bar: List[String]) = { + private[this] val bar: `package`.List[Predef.String] = _; + def (bar: `package`.List[Predef.String]) = { super.(); () } }; - @new ann(immutable.this.List.apply("1a")) @new ann(immutable.this.List.apply("1b")) class C[@new ann(immutable.this.List.apply("2a")) @new ann(immutable.this.List.apply("2b")) T >: Nothing <: Any] extends AnyRef { - @new ann(immutable.this.List.apply("3a")) @new ann(immutable.this.List.apply("3b")) private[this] val x: T @ann(immutable.this.List.apply("4a")) @ann(immutable.this.List.apply("4b")) = _; - def (@new ann(immutable.this.List.apply("3a")) @new ann(immutable.this.List.apply("3b")) x: T @ann(immutable.this.List.apply("4a")) @ann(immutable.this.List.apply("4b"))) = { + @new ann(List.apply("1a")) @new ann(List.apply("1b")) class C[@new ann(List.apply("2a")) @new ann(List.apply("2b")) T >: Nothing <: Any] extends AnyRef { + @new ann(List.apply("3a")) @new ann(List.apply("3b")) private[this] val x: T @ann(List.apply("4a")) @ann(List.apply("4b")) = _; + def (@new ann(List.apply("3a")) @new ann(List.apply("3b")) x: T @ann(List.apply("4a")) @ann(List.apply("4b"))) = { super.(); () }; - @new ann(immutable.this.List.apply("5a")) @new ann(immutable.this.List.apply("5b")) def f(x: Int @ann(immutable.this.List.apply("6a")) @ann(immutable.this.List.apply("6b"))) = { - @new ann(immutable.this.List.apply("7a")) @new ann(immutable.this.List.apply("7b")) val r = x.$plus(3): @ann(immutable.this.List.apply("8a")): @ann(immutable.this.List.apply("8b")); - val s = (4: Int @ann(immutable.this.List.apply("9a")) @ann(immutable.this.List.apply("9b"))); + @new ann(List.apply("5a")) @new ann(List.apply("5b")) def f(x: Int @ann(List.apply("6a")) @ann(List.apply("6b"))) = { + @new ann(List.apply("7a")) @new ann(List.apply("7b")) val r = x.$plus(3): @ann(List.apply("8a")): @ann(List.apply("8b")); + val s = (4: Int @ann(List.apply("9a")) @ann(List.apply("9b"))); r.$plus(s) } }; @@ -28,15 +28,15 @@ () } }; - @ann(immutable.this.List.apply[String]("1a")) @ann(immutable.this.List.apply[String]("1b")) class C[@ann(immutable.this.List.apply[String]("2a")) @ann(immutable.this.List.apply[String]("2b")) T] extends AnyRef { - @ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) private[this] val x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a")) = _; - def (@ann(immutable.this.List.apply[String]("3a")) @ann(immutable.this.List.apply[String]("3b")) x: T @ann(immutable.this.List.apply[String]("4b")) @ann(immutable.this.List.apply[String]("4a"))): C[T] = { + @ann(List.apply[String]("1a")) @ann(List.apply[String]("1b")) class C[@ann(List.apply[String]("2a")) @ann(List.apply[String]("2b")) T] extends AnyRef { + @ann(List.apply[String]("3a")) @ann(List.apply[String]("3b")) private[this] val x: T @ann(List.apply[String]("4b")) @ann(List.apply[String]("4a")) = _; + def (@ann(List.apply[String]("3a")) @ann(List.apply[String]("3b")) x: T @ann(List.apply[String]("4b")) @ann(List.apply[String]("4a"))): C[T] = { C.super.(); () }; - @ann(immutable.this.List.apply[String]("5a")) @ann(immutable.this.List.apply[String]("5b")) def f(x: Int @ann(immutable.this.List.apply[String]("6b")) @ann(immutable.this.List.apply[String]("6a"))): Int = { - @ann(immutable.this.List.apply[String]("7a")) @ann(immutable.this.List.apply[String]("7b")) val r: Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a")) = ((x.+(3): Int @ann(immutable.this.List.apply[String]("8a"))): Int @ann(immutable.this.List.apply[String]("8b")) @ann(immutable.this.List.apply[String]("8a"))); - val s: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a")) = (4: Int @ann(immutable.this.List.apply[String]("9b")) @ann(immutable.this.List.apply[String]("9a"))); + @ann(List.apply[String]("5a")) @ann(List.apply[String]("5b")) def f(x: Int @ann(List.apply[String]("6b")) @ann(List.apply[String]("6a"))): Int = { + @ann(List.apply[String]("7a")) @ann(List.apply[String]("7b")) val r: Int @ann(List.apply[String]("8b")) @ann(List.apply[String]("8a")) = ((x.+(3): Int @ann(List.apply[String]("8a"))): Int @ann(List.apply[String]("8b")) @ann(List.apply[String]("8a"))); + val s: Int @ann(List.apply[String]("9b")) @ann(List.apply[String]("9a")) = (4: Int @ann(List.apply[String]("9b")) @ann(List.apply[String]("9a"))); r.+(s) } }; diff --git a/test/files/run/reify_copypaste2.check b/test/files/run/reify_copypaste2.check index 9c34f5179b..f5c1076962 100644 --- a/test/files/run/reify_copypaste2.check +++ b/test/files/run/reify_copypaste2.check @@ -1 +1 @@ -scala.reflect.runtime.`package`.universe.reify(Test.this.x) +`package`.universe.reify(Test.this.x) diff --git a/test/files/run/reify_newimpl_35.check b/test/files/run/reify_newimpl_35.check index 5545e6e005..52aaa171e0 100644 --- a/test/files/run/reify_newimpl_35.check +++ b/test/files/run/reify_newimpl_35.check @@ -10,7 +10,7 @@ scala> def foo[T: TypeTag] = reify{List[T]()} foo: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Expr[List[T]] scala> println(foo) -Expr[List[Nothing]](immutable.this.Nil) +Expr[List[Nothing]](Nil) scala> diff --git a/test/files/run/showraw_tree.check b/test/files/run/showraw_tree.check index dca272684e..b71018d4fe 100644 --- a/test/files/run/showraw_tree.check +++ b/test/files/run/showraw_tree.check @@ -1,2 +1,2 @@ -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Ident(newTypeName("String")), Ident(newTypeName("String"))))), nme.CONSTRUCTOR), List()) -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Ident(newTypeName("String")), Ident(newTypeName("String"))))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Select(Ident(scala.Predef), newTypeName("String")), Select(Ident(scala.Predef), newTypeName("String"))))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Select(Ident(scala.Predef), newTypeName("String")), Select(Ident(scala.Predef), newTypeName("String"))))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_ids.check b/test/files/run/showraw_tree_ids.check index d25599c7fc..5835ffa6de 100644 --- a/test/files/run/showraw_tree_ids.check +++ b/test/files/run/showraw_tree_ids.check @@ -1,2 +1,2 @@ -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#), List(Ident(newTypeName("String")#), Ident(newTypeName("String")#)))), nme.CONSTRUCTOR), List()) -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#), List(Ident(newTypeName("String")#), Ident(newTypeName("String")#)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#), List(Select(Ident(scala.Predef#), newTypeName("String")), Select(Ident(scala.Predef#), newTypeName("String"))))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#), List(Select(Ident(scala.Predef#), newTypeName("String")), Select(Ident(scala.Predef#), newTypeName("String"))))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_kinds.check b/test/files/run/showraw_tree_kinds.check index d0d4cd0058..c4d66856d4 100644 --- a/test/files/run/showraw_tree_kinds.check +++ b/test/files/run/showraw_tree_kinds.check @@ -1,2 +1,2 @@ -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#CLS), List(Ident(newTypeName("String")#TPE), Ident(newTypeName("String")#TPE)))), nme.CONSTRUCTOR), List()) -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#CLS), List(Ident(newTypeName("String")#TPE), Ident(newTypeName("String")#TPE)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#CLS), List(Select(Ident(scala.Predef#MOD), newTypeName("String")), Select(Ident(scala.Predef#MOD), newTypeName("String"))))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#CLS), List(Select(Ident(scala.Predef#MOD), newTypeName("String")), Select(Ident(scala.Predef#MOD), newTypeName("String"))))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_types_ids.check b/test/files/run/showraw_tree_types_ids.check index 92ee45a1e6..fccb81d8df 100644 --- a/test/files/run/showraw_tree_types_ids.check +++ b/test/files/run/showraw_tree_types_ids.check @@ -1,10 +1,12 @@ -Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap#), List(TypeTree[4]().setOriginal(Ident[4](newTypeName("String")#)), TypeTree[4]().setOriginal(Ident[4](newTypeName("String")#)))))), nme.CONSTRUCTOR#), List()) -[1] TypeRef(ThisType(scala.collection.immutable#), scala.collection.immutable.HashMap#, List(TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()), TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()))) -[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable#), scala.collection.immutable.HashMap#, List(TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()), TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List())))) +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap#), List(TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef#), newTypeName("String")#)), TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef#), newTypeName("String")#)))))), nme.CONSTRUCTOR#), List()) +[1] TypeRef(ThisType(scala.collection.immutable#), scala.collection.immutable.HashMap#, List(TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()), TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable#), scala.collection.immutable.HashMap#, List(TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()), TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List())))) [3] TypeRef(ThisType(scala.collection.immutable#), scala.collection.immutable.HashMap#, List()) -[4] TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()) -Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap#), List(TypeTree[4]().setOriginal(Ident[4](newTypeName("String")#)), TypeTree[4]().setOriginal(Ident[4](newTypeName("String")#)))))), nme.CONSTRUCTOR#), List()) -[4] TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()) -[5] TypeRef(ThisType(scala.collection.mutable#), scala.collection.mutable.HashMap#, List(TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()), TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()))) -[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable#), scala.collection.mutable.HashMap#, List(TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List()), TypeRef(ThisType(scala.Predef#), newTypeName("String")#, List())))) -[7] TypeRef(ThisType(scala.collection.mutable#), scala.collection.mutable.HashMap#, List()) +[4] TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()) +[5] SingleType(ThisType(scala#), scala.Predef#) +Apply[6](Select[7](New[6](TypeTree[6]().setOriginal(AppliedTypeTree(Ident[8](scala.collection.mutable.HashMap#), List(TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef#), newTypeName("String")#)), TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef#), newTypeName("String")#)))))), nme.CONSTRUCTOR#), List()) +[4] TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()) +[5] SingleType(ThisType(scala#), scala.Predef#) +[6] TypeRef(ThisType(scala.collection.mutable#), scala.collection.mutable.HashMap#, List(TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()), TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()))) +[7] MethodType(List(), TypeRef(ThisType(scala.collection.mutable#), scala.collection.mutable.HashMap#, List(TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List()), TypeRef(SingleType(ThisType(scala#), scala.Predef#), newTypeName("String")#, List())))) +[8] TypeRef(ThisType(scala.collection.mutable#), scala.collection.mutable.HashMap#, List()) diff --git a/test/files/run/showraw_tree_types_typed.check b/test/files/run/showraw_tree_types_typed.check index c6c20409dc..f3e0f8c5f6 100644 --- a/test/files/run/showraw_tree_types_typed.check +++ b/test/files/run/showraw_tree_types_typed.check @@ -1,10 +1,12 @@ -Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap), List(TypeTree[4]().setOriginal(Ident[4](newTypeName("String"))), TypeTree[4]().setOriginal(Ident[4](newTypeName("String"))))))), nme.CONSTRUCTOR), List()) -[1] TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(ThisType(scala.Predef), newTypeName("String"), List()), TypeRef(ThisType(scala.Predef), newTypeName("String"), List()))) -[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(ThisType(scala.Predef), newTypeName("String"), List()), TypeRef(ThisType(scala.Predef), newTypeName("String"), List())))) +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap), List(TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef), newTypeName("String"))), TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef), newTypeName("String"))))))), nme.CONSTRUCTOR), List()) +[1] TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()), TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()), TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List())))) [3] TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List()) -[4] TypeRef(ThisType(scala.Predef), newTypeName("String"), List()) -Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap), List(TypeTree[4]().setOriginal(Ident[4](newTypeName("String"))), TypeTree[4]().setOriginal(Ident[4](newTypeName("String"))))))), nme.CONSTRUCTOR), List()) -[4] TypeRef(ThisType(scala.Predef), newTypeName("String"), List()) -[5] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(ThisType(scala.Predef), newTypeName("String"), List()), TypeRef(ThisType(scala.Predef), newTypeName("String"), List()))) -[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(ThisType(scala.Predef), newTypeName("String"), List()), TypeRef(ThisType(scala.Predef), newTypeName("String"), List())))) -[7] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List()) +[4] TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()) +[5] SingleType(ThisType(scala), scala.Predef) +Apply[6](Select[7](New[6](TypeTree[6]().setOriginal(AppliedTypeTree(Ident[8](scala.collection.mutable.HashMap), List(TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef), newTypeName("String"))), TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef), newTypeName("String"))))))), nme.CONSTRUCTOR), List()) +[4] TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()) +[5] SingleType(ThisType(scala), scala.Predef) +[6] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()), TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()))) +[7] MethodType(List(), TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List()), TypeRef(SingleType(ThisType(scala), scala.Predef), newTypeName("String"), List())))) +[8] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List()) diff --git a/test/files/run/showraw_tree_types_untyped.check b/test/files/run/showraw_tree_types_untyped.check index dca272684e..b71018d4fe 100644 --- a/test/files/run/showraw_tree_types_untyped.check +++ b/test/files/run/showraw_tree_types_untyped.check @@ -1,2 +1,2 @@ -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Ident(newTypeName("String")), Ident(newTypeName("String"))))), nme.CONSTRUCTOR), List()) -Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Ident(newTypeName("String")), Ident(newTypeName("String"))))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Select(Ident(scala.Predef), newTypeName("String")), Select(Ident(scala.Predef), newTypeName("String"))))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Select(Ident(scala.Predef), newTypeName("String")), Select(Ident(scala.Predef), newTypeName("String"))))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_ultimate.check b/test/files/run/showraw_tree_ultimate.check index 46ccd4df8f..a6286ba754 100644 --- a/test/files/run/showraw_tree_ultimate.check +++ b/test/files/run/showraw_tree_ultimate.check @@ -1,10 +1,12 @@ -Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap##CLS), List(TypeTree[4]().setOriginal(Ident[4](newTypeName("String")##TPE)), TypeTree[4]().setOriginal(Ident[4](newTypeName("String")##TPE)))))), nme.CONSTRUCTOR##PCTOR), List()) -[1] TypeRef(ThisType(scala.collection.immutable##PK), scala.collection.immutable.HashMap##CLS, List(TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()), TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()))) -[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable##PK), scala.collection.immutable.HashMap##CLS, List(TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()), TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List())))) +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap##CLS), List(TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef##MOD), newTypeName("String")##TPE)), TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef##MOD), newTypeName("String")##TPE)))))), nme.CONSTRUCTOR##PCTOR), List()) +[1] TypeRef(ThisType(scala.collection.immutable##PK), scala.collection.immutable.HashMap##CLS, List(TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()), TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable##PK), scala.collection.immutable.HashMap##CLS, List(TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()), TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List())))) [3] TypeRef(ThisType(scala.collection.immutable##PK), scala.collection.immutable.HashMap##CLS, List()) -[4] TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()) -Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap##CLS), List(TypeTree[4]().setOriginal(Ident[4](newTypeName("String")##TPE)), TypeTree[4]().setOriginal(Ident[4](newTypeName("String")##TPE)))))), nme.CONSTRUCTOR##CTOR), List()) -[4] TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()) -[5] TypeRef(ThisType(scala.collection.mutable##PK), scala.collection.mutable.HashMap##CLS, List(TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()), TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()))) -[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable##PK), scala.collection.mutable.HashMap##CLS, List(TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List()), TypeRef(ThisType(scala.Predef##MODC), newTypeName("String")##TPE, List())))) -[7] TypeRef(ThisType(scala.collection.mutable##PK), scala.collection.mutable.HashMap##CLS, List()) +[4] TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()) +[5] SingleType(ThisType(scala##PK), scala.Predef##MOD) +Apply[6](Select[7](New[6](TypeTree[6]().setOriginal(AppliedTypeTree(Ident[8](scala.collection.mutable.HashMap##CLS), List(TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef##MOD), newTypeName("String")##TPE)), TypeTree[4]().setOriginal(Select[4](Ident[5](scala.Predef##MOD), newTypeName("String")##TPE)))))), nme.CONSTRUCTOR##CTOR), List()) +[4] TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()) +[5] SingleType(ThisType(scala##PK), scala.Predef##MOD) +[6] TypeRef(ThisType(scala.collection.mutable##PK), scala.collection.mutable.HashMap##CLS, List(TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()), TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()))) +[7] MethodType(List(), TypeRef(ThisType(scala.collection.mutable##PK), scala.collection.mutable.HashMap##CLS, List(TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List()), TypeRef(SingleType(ThisType(scala##PK), scala.Predef##MOD), newTypeName("String")##TPE, List())))) +[8] TypeRef(ThisType(scala.collection.mutable##PK), scala.collection.mutable.HashMap##CLS, List()) diff --git a/test/files/run/t2886.check b/test/files/run/t2886.check index ce31bc7408..a70f9935ed 100644 --- a/test/files/run/t2886.check +++ b/test/files/run/t2886.check @@ -1,4 +1,4 @@ -((x: String) => { +((x: Predef.String) => { val x$1 = x; val x$2 = x; Test.this.test(x$2, x$1) diff --git a/test/files/run/t5225_2.check b/test/files/run/t5225_2.check index 8ed54a14bb..5faa365bce 100644 --- a/test/files/run/t5225_2.check +++ b/test/files/run/t5225_2.check @@ -1,4 +1,4 @@ { - def foo(@new cloneable() x: Int) = ""; + def foo(@new `package`.cloneable() x: Int) = ""; () } diff --git a/test/files/run/t5271_2.check b/test/files/run/t5271_2.check index 585331be65..1df88872a7 100644 --- a/test/files/run/t5271_2.check +++ b/test/files/run/t5271_2.check @@ -8,7 +8,7 @@ } }; val c = C.apply(2, 2); - scala.this.Predef.println(c.foo.$times(c.bar)) + Predef.println(c.foo.$times(c.bar)) } 4 () diff --git a/test/files/run/t5271_3.check b/test/files/run/t5271_3.check index b02acd21f9..99aacc2cee 100644 --- a/test/files/run/t5271_3.check +++ b/test/files/run/t5271_3.check @@ -15,7 +15,7 @@ } }; val c = C.apply(2, 2); - scala.this.Predef.println(c.foo.$times(c.bar).$eq$eq(C.qwe)) + Predef.println(c.foo.$times(c.bar).$eq$eq(C.qwe)) } true () diff --git a/test/files/run/t6591_5.check b/test/files/run/t6591_5.check index 8f1c2b3ede..e0b6d06e6b 100644 --- a/test/files/run/t6591_5.check +++ b/test/files/run/t6591_5.check @@ -1 +1 @@ -Expr(Block(List(ValDef(Modifiers(), newTermName("v"), Select(Select(This(newTypeName("A")), newTermName("x")), newTypeName("I")), Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("$qmark$qmark$qmark")))), Ident(newTermName("v")))) +Expr(Block(List(ValDef(Modifiers(), newTermName("v"), Select(Select(This(newTypeName("A")), newTermName("x")), newTypeName("I")), Select(Ident(scala.Predef), newTermName("$qmark$qmark$qmark")))), Ident(newTermName("v")))) diff --git a/test/files/run/t6591_6.check b/test/files/run/t6591_6.check index 5ddf7600f1..0c4847b628 100644 --- a/test/files/run/t6591_6.check +++ b/test/files/run/t6591_6.check @@ -1 +1 @@ -Expr(Block(List(ValDef(Modifiers(), newTermName("v"), Select(Select(Ident(newTermName("A")), newTermName("x")), newTypeName("I")), Select(Select(This(newTypeName("scala")), newTermName("Predef")), newTermName("$qmark$qmark$qmark")))), Ident(newTermName("v")))) +Expr(Block(List(ValDef(Modifiers(), newTermName("v"), Select(Select(Ident(newTermName("A")), newTermName("x")), newTypeName("I")), Select(Ident(scala.Predef), newTermName("$qmark$qmark$qmark")))), Ident(newTermName("v")))) diff --git a/test/files/run/toolbox_typecheck_macrosdisabled2.check b/test/files/run/toolbox_typecheck_macrosdisabled2.check index f5c9b6eeab..e7011d1ae2 100644 --- a/test/files/run/toolbox_typecheck_macrosdisabled2.check +++ b/test/files/run/toolbox_typecheck_macrosdisabled2.check @@ -19,7 +19,7 @@ def apply[U <: scala.reflect.api.Universe with Singleton]($m$untyped: scala.reflect.api.Mirror[U]): U#Tree = { val $u: U = $m$untyped.universe; val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror]; - $u.Apply.apply($u.Select.apply($u.Select.apply($u.build.Ident($m.staticPackage("scala")), $u.newTermName("Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2)))) + $u.Apply.apply($u.Select.apply($u.build.Ident($m.staticModule("scala.Array")), $u.newTermName("apply")), scala.collection.immutable.List.apply[$u.Literal]($u.Literal.apply($u.Constant.apply(2)))) } }; new $treecreator1() -- cgit v1.2.3 From 57c0e63ba18c5845772d737570342e4054af459f Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 8 Feb 2013 14:08:30 +0100 Subject: accommodates pull request feedback https://github.com/scala/scala/pull/2072 --- .../scala/reflect/reify/codegen/GenSymbols.scala | 31 ++++++++++++++++++++-- .../scala/tools/reflect/ToolBoxFactory.scala | 9 +++---- src/reflect/scala/reflect/internal/TreeInfo.scala | 15 +++++------ test/files/run/t6591_7.check | 4 +++ test/files/run/t6591_7.scala | 26 ++++++++++++++++++ 5 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 test/files/run/t6591_7.check create mode 100644 test/files/run/t6591_7.scala (limited to 'test') diff --git a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala index 0d795e3783..47c966ea24 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenSymbols.scala @@ -1,6 +1,8 @@ package scala.reflect.reify package codegen +import scala.reflect.internal.Flags._ + trait GenSymbols { self: Reifier => @@ -100,8 +102,33 @@ trait GenSymbols { reifyIntoSymtab(binding.symbol) { sym => if (reifyDebug) println("Free term" + (if (sym.isCapturedVariable) " (captured)" else "") + ": " + sym + "(" + sym.accurateKindString + ")") val name = newTermName(nme.REIFY_FREE_PREFIX + sym.name + (if (sym.isType) nme.REIFY_FREE_THIS_SUFFIX else "")) - // Flag is set here to make reified free value pass stability test during reflective compilation - if (!sym.isMutable) sym setFlag reflect.internal.Flags.STABLE + // We need to note whether the free value being reified is stable or not to guide subsequent reflective compilation. + // Here's why reflection compilation needs our help. + // + // When dealing with a tree, which contain free values, toolboxes extract those and wrap the entire tree in a Function + // having parameters defined for every free values in the tree. For example, evaluating + // + // Ident(setTypeSignature(newFreeTerm("x", 2), )) + // + // Will generate something like + // + // object wrapper { + // def wrapper(x: () => Int) = { + // x() + // } + // } + // + // Note that free values get transformed into, effectively, by-name parameters. This is done to make sure + // that evaluation order is kept intact. And indeed, we cannot just evaluate all free values at once in order + // to obtain arguments for wrapper.wrapper, because if some of the free values end up being unused during evaluation, + // we might end up doing unnecessary calculations. + // + // So far, so good - we didn't need any flags at all. However, if the code being reified contains path-dependent types, + // we're in trouble, because valid code like `free.T` ends up being transformed into `free.apply().T`, which won't compile. + // + // To overcome this glitch, we note whether a given free term is stable or not (because vars can also end up being free terms). + // Then, if a free term is stable, we tell the compiler to treat `free.apply()` specially and assume that it's stable. + if (!sym.isMutable) sym setFlag STABLE if (sym.isCapturedVariable) { assert(binding.isInstanceOf[Ident], showRaw(binding)) val capturedBinding = referenceCapturedVariable(sym) diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index fdc790a920..c05c59d5ff 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -8,7 +8,7 @@ import scala.tools.nsc.typechecker.Modes import scala.tools.nsc.io.VirtualDirectory import scala.tools.nsc.interpreter.AbstractFileClassLoader import scala.tools.nsc.util.FreshNameCreator -import scala.reflect.internal.Flags +import scala.reflect.internal.Flags._ import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, NoFile} import java.lang.{Class => jClass} import scala.compat.Platform.EOL @@ -211,12 +211,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val meth = obj.moduleClass.newMethod(newTermName(wrapperMethodName)) def makeParam(schema: (FreeTermSymbol, TermName)) = { + // see a detailed explanation of the STABLE trick in `GenSymbols.reifyFreeTerm` val (fv, name) = schema - /* Free term symbol `fv` can contain flag which was set by - * `scala.reflect.reify.codegen.GenSymbols.reifyFreeTerm` method. - * It is recovered here, so value parameter can pass `isExprSafeToInline` - * test in `scala.reflect.internal.TreeInfo`. */ - meth.newValueParameter(name, newFlags = if (fv.hasStableFlag) Flags.STABLE else 0) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType)) + meth.newValueParameter(name, newFlags = if (fv.hasStableFlag) STABLE else 0) setInfo appliedType(definitions.FunctionClass(0).tpe, List(fv.tpe.resultType)) } meth setInfo MethodType(freeTerms.map(makeParam).toList, AnyClass.tpe) minfo.decls enter meth diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index f83106aaef..3040486076 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -89,18 +89,15 @@ abstract class TreeInfo { tree.symbol.isStable && isExprSafeToInline(qual) case TypeApply(fn, _) => isExprSafeToInline(fn) - /* Special case for reified free terms. During reflective compilation, - * reified value recovered flag from free term and wrapped into a - * Function object, so it can pass stability check here to become a stable - * identifier.*/ case Apply(Select(free @ Ident(_), nme.apply), _) if free.symbol.name endsWith nme.REIFY_FREE_VALUE_SUFFIX => + // see a detailed explanation of this trick in `GenSymbols.reifyFreeTerm` free.symbol.hasStableFlag && isExprSafeToInline(free) case Apply(fn, List()) => - /* Note: After uncurry, field accesses are represented as Apply(getter, Nil), - * so an Apply can also be pure. - * However, before typing, applications of nullary functional values are also - * Apply(function, Nil) trees. To prevent them from being treated as pure, - * we check that the callee is a method. */ + // Note: After uncurry, field accesses are represented as Apply(getter, Nil), + // so an Apply can also be pure. + // However, before typing, applications of nullary functional values are also + // Apply(function, Nil) trees. To prevent them from being treated as pure, + // we check that the callee is a method. fn.symbol.isMethod && !fn.symbol.isLazy && isExprSafeToInline(fn) case Typed(expr, _) => isExprSafeToInline(expr) diff --git a/test/files/run/t6591_7.check b/test/files/run/t6591_7.check new file mode 100644 index 0000000000..e21a3669b6 --- /dev/null +++ b/test/files/run/t6591_7.check @@ -0,0 +1,4 @@ +name = x, stable = true +name = y, stable = true +name = z, stable = false +name = C, stable = true diff --git a/test/files/run/t6591_7.scala b/test/files/run/t6591_7.scala new file mode 100644 index 0000000000..b6c8d399a0 --- /dev/null +++ b/test/files/run/t6591_7.scala @@ -0,0 +1,26 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.Eval + +object Test extends App { + locally { + val x = 2 + def y = 3 + var z = 4 + class C { + var w = 5 + locally { + val expr = reify(x + y + z + w) + // blocked by SI-7103, though it's not the focus of this test + // therefore I'm just commenting out the evaluation + // println(expr.eval) + expr.tree.freeTerms foreach (ft => { + // blocked by SI-7104, though it's not the focus of this test + // therefore I'm just commenting out the call to typeSignature + // println(s"name = ${ft.name}, sig = ${ft.typeSignature}, stable = ${ft.isStable}") + println(s"name = ${ft.name}, stable = ${ft.isStable}") + }) + } + } + new C() + } +} \ No newline at end of file -- cgit v1.2.3 From e0068b908517768e900a3945e483e9c379d728d8 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 5 Feb 2013 00:46:58 +0100 Subject: SI-5675 Discard duplicate feature warnings at a position When -feature has not been enabled, we were double counting identical feature warnings that were emitted at the same position. Normal error reporting only reports the first time a warning appears at a position; feature warning counter incrementing should behave the same way. @hubertp: Fixed .check files that were broken in the original commit. --- src/compiler/scala/tools/nsc/Global.scala | 6 +++--- test/files/buildmanager/t2650_1/t2650_1.check | 2 +- test/files/buildmanager/t2657/t2657.check | 2 +- test/files/jvm/interpreter.check | 4 ++-- test/files/neg/t3234.check | 2 +- test/files/neg/t5675.check | 2 ++ test/files/neg/t5675.flags | 1 + test/files/neg/t5675.scala | 7 +++++++ test/files/run/constrained-types.check | 8 ++++---- test/files/run/reflection-magicsymbols-repl.check | 2 +- test/files/run/t4172.check | 2 +- test/files/run/t4710.check | 2 +- test/files/run/t6028.check | 2 +- test/files/run/t6329_repl.check | 2 +- 14 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 test/files/neg/t5675.check create mode 100644 test/files/neg/t5675.flags create mode 100644 test/files/neg/t5675.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 757ac94dbd..116684c161 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1193,13 +1193,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Collects for certain classes of warnings during this run. */ class ConditionalWarning(what: String, option: Settings#BooleanSetting) { - val warnings = new mutable.ListBuffer[(Position, String)] + val warnings = mutable.LinkedHashMap[Position, String]() def warn(pos: Position, msg: String) = if (option.value) reporter.warning(pos, msg) - else warnings += ((pos, msg)) + else if (!(warnings contains pos)) warnings += ((pos, msg)) def summarize() = if (option.isDefault && warnings.nonEmpty) - reporter.warning(NoPosition, "there were %d %s warnings; re-run with %s for details".format(warnings.size, what, option.name)) + reporter.warning(NoPosition, "there were %d %s warning(s); re-run with %s for details".format(warnings.size, what, option.name)) } def newUnitParser(code: String) = new syntaxAnalyzer.UnitParser(newCompilationUnit(code)) diff --git a/test/files/buildmanager/t2650_1/t2650_1.check b/test/files/buildmanager/t2650_1/t2650_1.check index f1e4b1b8bc..2f9dd124af 100644 --- a/test/files/buildmanager/t2650_1/t2650_1.check +++ b/test/files/buildmanager/t2650_1/t2650_1.check @@ -1,6 +1,6 @@ builder > A.scala B.scala compiling Set(A.scala, B.scala) -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details Changes: Map() builder > A.scala compiling Set(A.scala) diff --git a/test/files/buildmanager/t2657/t2657.check b/test/files/buildmanager/t2657/t2657.check index 0d6709e58b..7bff078f56 100644 --- a/test/files/buildmanager/t2657/t2657.check +++ b/test/files/buildmanager/t2657/t2657.check @@ -1,6 +1,6 @@ builder > A.scala B.scala compiling Set(A.scala, B.scala) -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details Changes: Map() builder > A.scala compiling Set(A.scala) diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index 6145b6c4d2..96b57c7742 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -97,7 +97,7 @@ scala> case class Bar(n: Int) defined class Bar scala> implicit def foo2bar(foo: Foo) = Bar(foo.n) -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details foo2bar: (foo: Foo)Bar scala> val bar: Bar = Foo(3) @@ -271,7 +271,7 @@ scala> xs map (x => x) res6: Array[_] = Array(1, 2) scala> xs map (x => (x, x)) -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details res7: Array[(_$1, _$1)] forSome { type _$1 } = Array((1,1), (2,2)) scala> diff --git a/test/files/neg/t3234.check b/test/files/neg/t3234.check index 477b021e5e..4339950ecb 100644 --- a/test/files/neg/t3234.check +++ b/test/files/neg/t3234.check @@ -1,2 +1,2 @@ -error: there were 1 inliner warnings; re-run with -Yinline-warnings for details +error: there were 1 inliner warning(s); re-run with -Yinline-warnings for details one error found diff --git a/test/files/neg/t5675.check b/test/files/neg/t5675.check new file mode 100644 index 0000000000..da608a2b78 --- /dev/null +++ b/test/files/neg/t5675.check @@ -0,0 +1,2 @@ +error: there were 1 feature warning(s); re-run with -feature for details +one error found diff --git a/test/files/neg/t5675.flags b/test/files/neg/t5675.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t5675.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/neg/t5675.scala b/test/files/neg/t5675.scala new file mode 100644 index 0000000000..238ed0fcae --- /dev/null +++ b/test/files/neg/t5675.scala @@ -0,0 +1,7 @@ +class PostFix { + val list = List(1, 2, 3) + def main(args: Array[String]) { + val a = list filter (2 !=) + val b = list filter (2 != _) + } +} diff --git a/test/files/run/constrained-types.check b/test/files/run/constrained-types.check index da97a378e6..85c4f41872 100644 --- a/test/files/run/constrained-types.check +++ b/test/files/run/constrained-types.check @@ -75,11 +75,11 @@ scala> var four = "four" four: String = four scala> val four2 = m(four) // should have an existential bound -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details four2: String @Annot(x) forSome { val x: String } = four scala> val four3 = four2 // should have the same type as four2 -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details four3: String @Annot(x) forSome { val x: String } = four scala> val stuff = m("stuff") // should not crash @@ -102,7 +102,7 @@ scala> def m = { val y : String @Annot(x) = x y } // x should not escape the local scope with a narrow type -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details m: String @Annot(x) forSome { val x: String } scala> @@ -116,7 +116,7 @@ scala> def n(y: String) = { } m("stuff".stripMargin) } // x should be existentially bound -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details n: (y: String)String @Annot(x) forSome { val x: String } scala> diff --git a/test/files/run/reflection-magicsymbols-repl.check b/test/files/run/reflection-magicsymbols-repl.check index d3cd26f25f..2535e3f43d 100644 --- a/test/files/run/reflection-magicsymbols-repl.check +++ b/test/files/run/reflection-magicsymbols-repl.check @@ -23,7 +23,7 @@ scala> def test(n: Int): Unit = { val x = sig.asInstanceOf[MethodType].params.head println(x.typeSignature) } -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details test: (n: Int)Unit scala> for (i <- 1 to 8) test(i) diff --git a/test/files/run/t4172.check b/test/files/run/t4172.check index 94cdff4870..b48c9ca056 100644 --- a/test/files/run/t4172.check +++ b/test/files/run/t4172.check @@ -4,7 +4,7 @@ Type :help for more information. scala> scala> val c = { class C { override def toString = "C" }; ((new C, new C { def f = 2 })) } -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details c: (C, C{def f: Int}) forSome { type C <: AnyRef } = (C,C) scala> diff --git a/test/files/run/t4710.check b/test/files/run/t4710.check index 7c2b10b098..f2335d1bdd 100644 --- a/test/files/run/t4710.check +++ b/test/files/run/t4710.check @@ -2,7 +2,7 @@ Type in expressions to have them evaluated. Type :help for more information. scala> def method : String = { implicit def f(s: Symbol) = "" ; 'symbol } -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details method: String scala> diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check index 79deaacf3a..94013efd36 100644 --- a/test/files/run/t6028.check +++ b/test/files/run/t6028.check @@ -81,4 +81,4 @@ package { } } -warning: there were 1 feature warnings; re-run with -feature for details +warning: there were 1 feature warning(s); re-run with -feature for details diff --git a/test/files/run/t6329_repl.check b/test/files/run/t6329_repl.check index 8663184bde..693263a5c2 100644 --- a/test/files/run/t6329_repl.check +++ b/test/files/run/t6329_repl.check @@ -4,7 +4,7 @@ Type :help for more information. scala> scala> classManifest[List[_]] -warning: there were 1 deprecation warnings; re-run with -deprecation for details +warning: there were 1 deprecation warning(s); re-run with -deprecation for details res0: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List[Any] scala> scala.reflect.classTag[List[_]] -- cgit v1.2.3 From 13caa498fb856ac1de3b851ddc413e36d728c3a6 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 8 Feb 2013 13:10:46 -0800 Subject: Fix for paramaccessor alias regression. A binary incompatibility with 2.10.0 revealed a bug I had introduced in c58647f5f2. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- test/files/run/t7106.check | 6 ++++++ test/files/run/t7106/Analyzed_1.scala | 14 ++++++++++++++ test/files/run/t7106/test.scala | 10 ++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t7106.check create mode 100644 test/files/run/t7106/Analyzed_1.scala create mode 100644 test/files/run/t7106/test.scala (limited to 'test') diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index dc5491a509..478312116a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2037,7 +2037,7 @@ trait Typers extends Modes with Adaptations with Tags { val alias = ( superAcc.initialize.alias orElse (superAcc getter superAcc.owner) - filter (alias => superClazz.info.nonPrivateMember(alias.name) != alias) + filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias) ) if (alias.exists && !alias.accessed.isVariable) { val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match { diff --git a/test/files/run/t7106.check b/test/files/run/t7106.check new file mode 100644 index 0000000000..9a4bb430fd --- /dev/null +++ b/test/files/run/t7106.check @@ -0,0 +1,6 @@ +[ok] q1 I private final +[ok] q3 I private final +[ok] (III)V public +[ok] bippy1 ()I public +[ok] bippy2 ()I public +[ok] bippy3 ()I public diff --git a/test/files/run/t7106/Analyzed_1.scala b/test/files/run/t7106/Analyzed_1.scala new file mode 100644 index 0000000000..a2ddebceed --- /dev/null +++ b/test/files/run/t7106/Analyzed_1.scala @@ -0,0 +1,14 @@ + +abstract class Base0 { def p2: Int } +class Base(p1: Int, override val p2: Int) extends Base0 + +abstract class Sub1(q1: Int, q2: Int, q3: Int) extends Base(q1, q2) { + def bippy1 = q1 + def bippy2 = q2 + def bippy3 = q3 +} +abstract class Sub2(q1: Int, q2: Int, q3: Int) extends Base(q1, q2) { + def bippy1 = q1 + def bippy2 = p2 + def bippy3 = q3 +} diff --git a/test/files/run/t7106/test.scala b/test/files/run/t7106/test.scala new file mode 100644 index 0000000000..3584a272db --- /dev/null +++ b/test/files/run/t7106/test.scala @@ -0,0 +1,10 @@ +import scala.tools.partest.BytecodeTest + +object Test extends BytecodeTest { + def show { + val node1 = loadClassNode("Sub1") + val node2 = loadClassNode("Sub2") + + sameMethodAndFieldSignatures(node1, node2) + } +} -- cgit v1.2.3