From fe9a3e9c5ef2d2de5fa62a14fa4a613a573e5e5f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 22 Aug 2013 23:51:15 +0200 Subject: SI-7269 Rework MapLike#retains to account for desugaring change `MapLike#retains` contains a for-comprehension that relied on the strict `filter` by its generator. You can't, in general, iterate a mutable map and remove items in the same pass. Here's the history of the desugaring of: def retain[A, B](thiz: mutable.Map[A, B])(p: (A, B) => Boolean): thiz.type = { thiz.foreach { case (k, v) => if (p(k, v)) thiz -= k } Before regression (c82ecabad6~1): thiz.filter(((check$ifrefutable$1) => check$ifrefutable$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => true case _ => false })).withFilter(((x$1) => x$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => p(k, v).unary_$bang })).foreach(((x$2) => x$2: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => thiz.$minus$eq(k) })); After regression (c82ecabad6, which incorrectly assumed in the parser that no filter is required for isInstanceOf[Tuple2]) thiz.withFilter(((x$1) => x$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => p(k, v).unary_$bang })).foreach(((x$2) => x$2: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => thiz.$minus$eq(k) })); After the reversion of c82ecabad6, v2.10.2 This is also after 365bb2b4e, which uses `withFilter` rather than `filter`. thiz.withFilter(((check$q$1) => check$ifrefutable$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => true case _ => false })).withFilter(((x$1) => x$1: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => p(k, v).unary_$bang })).foreach(((x$2) => x$2: @scala.unchecked match { case scala.Tuple2((k @ _), (v @ _)) => thiz.$minus$eq(k) })); This commit does the same as `SetLike#retains`, and converts the map to an immutable list before the rest of the operation. --- src/library/scala/collection/mutable/MapLike.scala | 4 +-- src/library/scala/collection/mutable/SetLike.scala | 4 ++- test/files/run/t7269.scala | 32 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t7269.scala diff --git a/src/library/scala/collection/mutable/MapLike.scala b/src/library/scala/collection/mutable/MapLike.scala index a53aa3b76a..42e5a0a4c4 100644 --- a/src/library/scala/collection/mutable/MapLike.scala +++ b/src/library/scala/collection/mutable/MapLike.scala @@ -209,8 +209,8 @@ trait MapLike[A, B, +This <: MapLike[A, B, This] with Map[A, B]] * @param p The test predicate */ def retain(p: (A, B) => Boolean): this.type = { - for ((k, v) <- this.seq ; if !p(k, v)) - this -= k + for ((k, v) <- this.toList) // SI-7269 toList avoids ConcurrentModificationException + if (!p(k, v)) this -= k this } diff --git a/src/library/scala/collection/mutable/SetLike.scala b/src/library/scala/collection/mutable/SetLike.scala index 01f87447ae..71da4c89dc 100644 --- a/src/library/scala/collection/mutable/SetLike.scala +++ b/src/library/scala/collection/mutable/SetLike.scala @@ -120,7 +120,9 @@ trait SetLike[A, +This <: SetLike[A, This] with Set[A]] * which `p` returns `true` are retained in the set; all others * are removed. */ - def retain(p: A => Boolean): Unit = for (elem <- this.toList) if (!p(elem)) this -= elem + def retain(p: A => Boolean): Unit = + for (elem <- this.toList) // SI-7269 toList avoids ConcurrentModificationException + if (!p(elem)) this -= elem /** Removes all elements from the set. After this operation is completed, * the set will be empty. diff --git a/test/files/run/t7269.scala b/test/files/run/t7269.scala new file mode 100644 index 0000000000..d22e57dfee --- /dev/null +++ b/test/files/run/t7269.scala @@ -0,0 +1,32 @@ +import scala.collection.JavaConversions._ +import scala.collection.mutable + +object Test extends App { + + def testMap(): Unit = { + val mapJ = new java.util.HashMap[Int, String] + val mapS: mutable.Map[Int, String] = mapJ + + (10 to 20).foreach(i => mapS += ((i, i.toString))) + assert(11 == mapS.size) + + // ConcurrentModificationException thrown in the following line + mapS.retain((i, str) => i % 2 == 0) + assert(6 == mapS.size) + } + + def testSet(): Unit = { + val mapJ = new java.util.HashSet[Int] + val mapS: mutable.Set[Int] = mapJ + + (10 to 20).foreach(i => mapS += i) + assert(11 == mapS.size) + + // ConcurrentModificationException thrown in the following line + mapS.retain((i) => i % 2 == 0) + assert(6 == mapS.size) + } + + testSet() + testMap() +} -- cgit v1.2.3 From cb4b8eb1ac52ad7545f442566b522b8f711e0a20 Mon Sep 17 00:00:00 2001 From: François Garillot Date: Tue, 27 Aug 2013 16:59:38 +0200 Subject: update typesafe.artifactory-online.com to private-repo --- build.xml | 4 ++-- tools/binary-repo-lib.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.xml b/build.xml index d8d0ca3bab..8c91c62e03 100644 --- a/build.xml +++ b/build.xml @@ -364,9 +364,9 @@ TODO: - + - + diff --git a/tools/binary-repo-lib.sh b/tools/binary-repo-lib.sh index 4fe6dd67a0..92ef3a0329 100755 --- a/tools/binary-repo-lib.sh +++ b/tools/binary-repo-lib.sh @@ -4,7 +4,7 @@ remote_urlget="http://repo.typesafe.com/typesafe/scala-sha-bootstrap/org/scala-lang/bootstrap" -remote_urlpush="http://typesafe.artifactoryonline.com/typesafe/scala-sha-bootstrap/org/scala-lang/bootstrap" +remote_urlpush="http://private-repo.typesafe.com/typesafe/scala-sha-bootstrap/org/scala-lang/bootstrap" libraryJar="$(pwd)/lib/scala-library.jar" desired_ext=".desired.sha1" push_jar="$(pwd)/tools/push.jar" -- cgit v1.2.3 From 8b10daf2cb4367cd2565bd67efd2b6ead94ca18e Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 29 Aug 2013 12:33:37 -0700 Subject: [nomaster] SI-7652 Bad tools fails loudly A brief message is all that's required to alleviate the look of consternation on the face of your user. --- src/compiler/scala/tools/util/Javap.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/util/Javap.scala b/src/compiler/scala/tools/util/Javap.scala index c3264d0787..21137aca56 100644 --- a/src/compiler/scala/tools/util/Javap.scala +++ b/src/compiler/scala/tools/util/Javap.scala @@ -50,7 +50,7 @@ class JavapClass( tryFile(path) getOrElse tryClass(path) def apply(args: Seq[String]): List[JpResult] = { - if (failed) Nil + if (failed) List(new JpError("Could not load javap tool. Check that JAVA_HOME is correct.")) else args.toList filterNot (_ startsWith "-") map { path => val bytes = findBytes(path) if (bytes.isEmpty) new JpError("Could not find class bytes for '%s'".format(path)) -- cgit v1.2.3 From 7804ceca432c1d7d6ce3a669828ac0d3f6ffeac4 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 29 Aug 2013 12:47:46 -0700 Subject: [nomaster] SI-7652 REPL extended quest for tools Javap tries harder and fails louder when looking for tools.jar This is a backport of the method as it was first coded for partest. If JAVA_HOME is wrong, it will either fail with a message or succeed after rooting about. ``` apm@mara:~/tmp/q$ JAVA_HOME=$JAVA7_HOME JAVACMD='/usr/lib/jvm/java-6-openjdk-amd64/bin/java' /home/apm/projects/snytt/build/pack/bin/scala Welcome to Scala version 2.10.3-20130829-123337-59d6568daa (OpenJDK 64-Bit Server VM, Java 1.6.0_27). Type in expressions to have them evaluated. Type :help for more information. scala> :javap :javap [-lcsvp] [path1 path2 ...] scala> :javap java.lang.Object Failed: Could not load javap tool. Check that JAVA_HOME is correct. apm@mara:~/tmp/q$ JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64/jre JAVACMD='/usr/lib/jvm/java-6-openjdk-amd64/bin/java' /home/apm/projects/snytt/build/pack/bin/scala Welcome to Scala version 2.10.3-20130829-123337-59d6568daa (OpenJDK 64-Bit Server VM, Java 1.6.0_27). Type in expressions to have them evaluated. Type :help for more information. scala> scala> :javap :javap [-lcsvp] [path1 path2 ...] scala> :javap java.lang.Object Compiled from "Object.java" ``` --- .../scala/tools/nsc/interpreter/ILoop.scala | 35 ++++++++++++++-------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index b7e07ecdd6..6aef72a3b8 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -11,14 +11,14 @@ import java.io.{ BufferedReader, FileReader } import java.util.concurrent.locks.ReentrantLock import scala.sys.process.Process import session._ -import scala.util.Properties.{ jdkHome, javaVersion } +import scala.util.Properties.{ envOrNone, javaHome, jdkHome, javaVersion } import scala.tools.util.{ Javap } import scala.annotation.tailrec import scala.collection.mutable.ListBuffer import scala.concurrent.ops import util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream } import interpreter._ -import io.{ File, Directory } +import io.{ File, Directory, Path } import scala.reflect.NameTransformer._ import util.ScalaClassLoader import ScalaClassLoader._ @@ -373,18 +373,29 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) } } - private def findToolsJar() = { - val jdkPath = Directory(jdkHome) - val jar = jdkPath / "lib" / "tools.jar" toFile; + private[this] lazy val platformTools: Option[File] = { + val jarName = "tools.jar" + def jarPath(path: Path) = (path / "lib" / jarName).toFile + def jarAt(path: Path) = { + val f = jarPath(path) + if (f.isFile) Some(f) else None + } + val jdkDir = { + val d = Directory(jdkHome) + if (d.isDirectory) Some(d) else None + } + def deeply(dir: Directory) = dir.deepFiles find (_.name == jarName) - if (jar isFile) - Some(jar) - else if (jdkPath.isDirectory) - jdkPath.deepFiles find (_.name == "tools.jar") - else None - } + val home = envOrNone("JDK_HOME") orElse envOrNone("JAVA_HOME") map (p => Path(p)) + val install = Some(Path(javaHome)) + + (home flatMap jarAt) orElse + (install flatMap jarAt) orElse + (install map (_.parent) flatMap jarAt) orElse + (jdkDir flatMap deeply) + } private def addToolsJarToLoader() = { - val cl = findToolsJar match { + val cl = platformTools match { case Some(tools) => ScalaClassLoader.fromURLs(Seq(tools.toURL), intp.classLoader) case _ => intp.classLoader } -- cgit v1.2.3 From bf93057e2eb41629f69657ea4a60acac0b824fce Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 26 Aug 2013 17:26:56 -0700 Subject: Change Scala license to unmodified 3-clause BSD. No longer using the slightly reworded 3-clause BSD license. This does not change the meaning of the license, just aligns it with the standard wording. --- docs/LICENSE | 56 ++++++++++++++++++++++++-------------------------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/docs/LICENSE b/docs/LICENSE index de950bdf57..446a1a350b 100644 --- a/docs/LICENSE +++ b/docs/LICENSE @@ -1,35 +1,27 @@ -SCALA LICENSE +Copyright (c) 2002-2013 EPFL -Copyright (c) 2002-2012 EPFL, Lausanne, unless otherwise specified. All rights reserved. -This software was developed by the Programming Methods Laboratory of the -Swiss Federal Institute of Technology (EPFL), Lausanne, Switzerland. - -Permission to use, copy, modify, and distribute this software in source -or binary form for any purpose with or without fee is hereby granted, -provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of the EPFL nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. - - -THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- cgit v1.2.3 From fb43ec8dc28625c929beaf28767a955388700d0d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 5 Sep 2013 15:27:15 +0200 Subject: SI-7814 Avoid init cycle between Predef, `package`, ScalaRuntime Not every application will force these in a single thread; we have to do our best to avoid cycles between them. The enclosed test was failing every time before the change. This commit breaks the cycle by avoiding computing `tupleNames` in the constructor of `ScalaRuntime`. The new version has the added benefit of including specialized tuple subclasses, which is verified with a unit test for `isTuple`. Are there more of these lurking? It seems likely. I'm more than a little concerned about the way the `ControlThrowable` fires up `scala.SystemProperties` to check whether or not to suppress stack traces; there is already an ugly hack in place: object NoStackTrace { final def noSuppression = _noSuppression // two-stage init to make checkinit happy, // since sys.SystemProperties.noTraceSupression.value // calls back into NoStackTrace.noSuppression final private var _noSuppression = false _noSuppression = sys.SystemProperties.noTraceSupression.value } --- src/library/scala/runtime/ScalaRunTime.scala | 14 ++--- test/files/run/predef-cycle.scala | 71 +++++++++++++++++++++++++ test/junit/scala/runtime/ScalaRunTimeTest.scala | 70 ++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 test/files/run/predef-cycle.scala create mode 100644 test/junit/scala/runtime/ScalaRunTimeTest.scala diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 1d8fe5e9ad..4fee6d75a3 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -34,21 +34,13 @@ object ScalaRunTime { clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() - def isTuple(x: Any) = x != null && tupleNames(x.getClass.getName) + + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") def isAnyVal(x: Any) = x match { case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true case _ => false } - // Avoiding boxing which messes up the specialized tests. Don't ask. - private val tupleNames = { - var i = 22 - var names: List[String] = Nil - while (i >= 1) { - names ::= ("scala.Tuple" + String.valueOf(i)) - i -= 1 - } - names.toSet - } /** Return the class object representing an array with element class `clazz`. */ diff --git a/test/files/run/predef-cycle.scala b/test/files/run/predef-cycle.scala new file mode 100644 index 0000000000..ab147688bc --- /dev/null +++ b/test/files/run/predef-cycle.scala @@ -0,0 +1,71 @@ +class Force { + val t1 = new Thread { + override def run() { + scala.`package` + } + } + val t2 = new Thread { + override def run() { + scala.Predef + } + } + t1.start() + t2.start() + t1.join() + t2.join() +} + +object Test { + def main(args: Array[String]) { + new Force() + } +} + +/* Was deadlocking: +"Thread-2" prio=5 tid=7f9637268000 nid=0x119601000 in Object.wait() [119600000] + java.lang.Thread.State: RUNNABLE + at scala.Predef$.(Predef.scala:90) + at scala.Predef$.(Predef.scala) + at Force$$anon$2.run(predef-cycle.scala:10) + +"Thread-1" prio=5 tid=7f9637267800 nid=0x1194fe000 in Object.wait() [1194fb000] + java.lang.Thread.State: RUNNABLE + at scala.collection.immutable.Set$Set4.$plus(Set.scala:127) + at scala.collection.immutable.Set$Set4.$plus(Set.scala:121) + at scala.collection.mutable.SetBuilder.$plus$eq(SetBuilder.scala:24) + at scala.collection.mutable.SetBuilder.$plus$eq(SetBuilder.scala:22) + at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48) + at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48) + at scala.collection.immutable.List.foreach(List.scala:318) + at scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:48) + at scala.collection.mutable.SetBuilder.$plus$plus$eq(SetBuilder.scala:22) + at scala.collection.TraversableLike$class.to(TraversableLike.scala:629) + at scala.collection.AbstractTraversable.to(Traversable.scala:105) + at scala.collection.TraversableOnce$class.toSet(TraversableOnce.scala:267) + at scala.collection.AbstractTraversable.toSet(Traversable.scala:105) + at scala.runtime.ScalaRunTime$.(ScalaRunTime.scala:50) + at scala.runtime.ScalaRunTime$.(ScalaRunTime.scala) + at scala.collection.mutable.HashTable$HashUtils$class.elemHashCode(HashTable.scala) + at scala.collection.mutable.HashMap.elemHashCode(HashMap.scala:39) + at scala.collection.mutable.HashTable$class.findOrAddEntry(HashTable.scala:161) + at scala.collection.mutable.HashMap.findOrAddEntry(HashMap.scala:39) + at scala.collection.mutable.HashMap.put(HashMap.scala:75) + at scala.collection.mutable.HashMap.update(HashMap.scala:80) + at scala.sys.SystemProperties$.addHelp(SystemProperties.scala:64) + at scala.sys.SystemProperties$.bool(SystemProperties.scala:68) + at scala.sys.SystemProperties$.noTraceSupression$lzycompute(SystemProperties.scala:80) + - locked <7b8b0e228> (a scala.sys.SystemProperties$) + at scala.sys.SystemProperties$.noTraceSupression(SystemProperties.scala:80) + at scala.util.control.NoStackTrace$.(NoStackTrace.scala:31) + at scala.util.control.NoStackTrace$.(NoStackTrace.scala) + at scala.util.control.NoStackTrace$class.fillInStackTrace(NoStackTrace.scala:22) + at scala.util.control.BreakControl.fillInStackTrace(Breaks.scala:93) + at java.lang.Throwable.(Throwable.java:181) + at scala.util.control.BreakControl.(Breaks.scala:93) + at scala.util.control.Breaks.(Breaks.scala:28) + at scala.collection.Traversable$.(Traversable.scala:96) + at scala.collection.Traversable$.(Traversable.scala) + at scala.package$.(package.scala:46) + at scala.package$.(package.scala) + at Force$$anon$1.run(predef-cycle.scala:4) + */ \ No newline at end of file diff --git a/test/junit/scala/runtime/ScalaRunTimeTest.scala b/test/junit/scala/runtime/ScalaRunTimeTest.scala new file mode 100644 index 0000000000..9da197c71a --- /dev/null +++ b/test/junit/scala/runtime/ScalaRunTimeTest.scala @@ -0,0 +1,70 @@ +package scala.runtime + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** Tests for the private class DefaultPromise */ +@RunWith(classOf[JUnit4]) +class ScalaRunTimeTest { + @Test + def testIsTuple() { + import ScalaRunTime.isTuple + def check(v: Any) = { + assertTrue(v.toString, isTuple(v)) + } + + val s = "" + check(Tuple1(s)) + check((s, s)) + check((s, s, s)) + check((s, s, s, s)) + check((s, s, s, s, s)) + check((s, s, s, s, s, s)) + check((s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + check((s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s)) + + // some specialized variants will have mangled classnames + check(Tuple1(0)) + check((0, 0)) + check((0, 0, 0)) + check((0, 0, 0, 0)) + check((0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + check((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)) + + case class C() + val c = new C() + assertFalse(c.toString, isTuple(c)) + } +} -- cgit v1.2.3 From a19babc952c1e30d9b92452505e85752ff2d5460 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 5 Sep 2013 16:00:40 +0200 Subject: SI-7814 Updates the instrumented version of ScalaRuntime. Some tests for specialization use a modified version of the standard library that count boxing, array lookups etc. These sources are updated manually with the script: % test/instrumented/mkinstrumented.sh build Looks that that wasn't done for a while, though. This commit brings it up to date, and adjusts a few braces in ScalaRuntime.scala so the patch srt.scala (used by that script) is shorter. We should really avoid checking in the products of that script and run it as part of the build, or, better, use the bytecode instrumentation framework instead of a modified standard library. But I have to leave that for another day. --- src/library/scala/runtime/ScalaRunTime.scala | 52 ++++++----- test/instrumented/boxes.patch | 2 +- .../library/scala/runtime/BoxesRunTime.java | 7 +- .../library/scala/runtime/ScalaRunTime.scala | 100 +++++++-------------- test/instrumented/srt.patch | 65 +------------- 5 files changed, 70 insertions(+), 156 deletions(-) diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 4fee6d75a3..dcd323961e 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -67,33 +67,37 @@ object ScalaRunTime { classTag[T].runtimeClass.asInstanceOf[jClass[T]] /** Retrieve generic array element */ - def array_apply(xs: AnyRef, idx: Int): Any = xs match { - case x: Array[AnyRef] => x(idx).asInstanceOf[Any] - case x: Array[Int] => x(idx).asInstanceOf[Any] - case x: Array[Double] => x(idx).asInstanceOf[Any] - case x: Array[Long] => x(idx).asInstanceOf[Any] - case x: Array[Float] => x(idx).asInstanceOf[Any] - case x: Array[Char] => x(idx).asInstanceOf[Any] - case x: Array[Byte] => x(idx).asInstanceOf[Any] - case x: Array[Short] => x(idx).asInstanceOf[Any] - case x: Array[Boolean] => x(idx).asInstanceOf[Any] - case x: Array[Unit] => x(idx).asInstanceOf[Any] - case null => throw new NullPointerException + def array_apply(xs: AnyRef, idx: Int): Any = { + xs match { + case x: Array[AnyRef] => x(idx).asInstanceOf[Any] + case x: Array[Int] => x(idx).asInstanceOf[Any] + case x: Array[Double] => x(idx).asInstanceOf[Any] + case x: Array[Long] => x(idx).asInstanceOf[Any] + case x: Array[Float] => x(idx).asInstanceOf[Any] + case x: Array[Char] => x(idx).asInstanceOf[Any] + case x: Array[Byte] => x(idx).asInstanceOf[Any] + case x: Array[Short] => x(idx).asInstanceOf[Any] + case x: Array[Boolean] => x(idx).asInstanceOf[Any] + case x: Array[Unit] => x(idx).asInstanceOf[Any] + case null => throw new NullPointerException + } } /** update generic array element */ - def array_update(xs: AnyRef, idx: Int, value: Any): Unit = xs match { - case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] - case x: Array[Int] => x(idx) = value.asInstanceOf[Int] - case x: Array[Double] => x(idx) = value.asInstanceOf[Double] - case x: Array[Long] => x(idx) = value.asInstanceOf[Long] - case x: Array[Float] => x(idx) = value.asInstanceOf[Float] - case x: Array[Char] => x(idx) = value.asInstanceOf[Char] - case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] - case x: Array[Short] => x(idx) = value.asInstanceOf[Short] - case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] - case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] - case null => throw new NullPointerException + def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { + xs match { + case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] + case x: Array[Int] => x(idx) = value.asInstanceOf[Int] + case x: Array[Double] => x(idx) = value.asInstanceOf[Double] + case x: Array[Long] => x(idx) = value.asInstanceOf[Long] + case x: Array[Float] => x(idx) = value.asInstanceOf[Float] + case x: Array[Char] => x(idx) = value.asInstanceOf[Char] + case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] + case x: Array[Short] => x(idx) = value.asInstanceOf[Short] + case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] + case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] + case null => throw new NullPointerException + } } /** Get generic array length */ diff --git a/test/instrumented/boxes.patch b/test/instrumented/boxes.patch index 6c5ff23f9f..2bb3243221 100644 --- a/test/instrumented/boxes.patch +++ b/test/instrumented/boxes.patch @@ -1,5 +1,5 @@ 9c9 -< +< --- > /* INSTRUMENTED VERSION */ 51a52,59 diff --git a/test/instrumented/library/scala/runtime/BoxesRunTime.java b/test/instrumented/library/scala/runtime/BoxesRunTime.java index 172ed8ee14..57799bd9b1 100644 --- a/test/instrumented/library/scala/runtime/BoxesRunTime.java +++ b/test/instrumented/library/scala/runtime/BoxesRunTime.java @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2011, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -244,7 +244,7 @@ public final class BoxesRunTime * as yet have not. * * Note: Among primitives, Float.NaN != Float.NaN, but the boxed - * verisons are equal. This still needs reconciliation. + * versions are equal. This still needs reconciliation. */ public static int hashFromLong(java.lang.Long n) { int iv = n.intValue(); @@ -258,6 +258,9 @@ public final class BoxesRunTime long lv = n.longValue(); if (lv == dv) return java.lang.Long.valueOf(lv).hashCode(); + + float fv = n.floatValue(); + if (fv == dv) return java.lang.Float.valueOf(fv).hashCode(); else return n.hashCode(); } public static int hashFromFloat(java.lang.Float n) { diff --git a/test/instrumented/library/scala/runtime/ScalaRunTime.scala b/test/instrumented/library/scala/runtime/ScalaRunTime.scala index 5a3f83015f..e474ae737c 100644 --- a/test/instrumented/library/scala/runtime/ScalaRunTime.scala +++ b/test/instrumented/library/scala/runtime/ScalaRunTime.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2011, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -8,7 +8,8 @@ /* INSTRUMENTED VERSION */ -package scala.runtime +package scala +package runtime import scala.collection.{ Seq, IndexedSeq, TraversableView, AbstractIterator } import scala.collection.mutable.WrappedArray @@ -17,6 +18,7 @@ import scala.collection.generic.{ Sorted } import scala.reflect.{ ClassTag, classTag } import scala.util.control.ControlThrowable import scala.xml.{ Node, MetaData } +import java.lang.{ Class => jClass } import java.lang.Double.doubleToLongBits import java.lang.reflect.{ Modifier, Method => JMethod } @@ -30,29 +32,21 @@ object ScalaRunTime { def isArray(x: Any, atLevel: Int): Boolean = x != null && isArrayClass(x.getClass, atLevel) - private def isArrayClass(clazz: Class[_], atLevel: Int): Boolean = + private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) - def isValueClass(clazz: Class[_]) = clazz.isPrimitive() - def isTuple(x: Any) = x != null && tupleNames(x.getClass.getName) + def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() + + // includes specialized subclasses and future proofed against hypothetical TupleN (for N > 22) + def isTuple(x: Any) = x != null && x.getClass.getName.startsWith("scala.Tuple") def isAnyVal(x: Any) = x match { case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true case _ => false } - // Avoiding boxing which messes up the specialized tests. Don't ask. - private val tupleNames = { - var i = 22 - var names: List[String] = Nil - while (i >= 1) { - names ::= ("scala.Tuple" + String.valueOf(i)) - i -= 1 - } - names.toSet - } /** Return the class object representing an array with element class `clazz`. */ - def arrayClass(clazz: Class[_]): Class[_] = { + def arrayClass(clazz: jClass[_]): jClass[_] = { // newInstance throws an exception if the erasure is Void.TYPE. see SI-5680 if (clazz == java.lang.Void.TYPE) classOf[Array[Unit]] else java.lang.reflect.Array.newInstance(clazz, 0).getClass @@ -60,18 +54,19 @@ object ScalaRunTime { /** Return the class object representing elements in arrays described by a given schematic. */ - def arrayElementClass(schematic: Any): Class[_] = schematic match { - case cls: Class[_] => cls.getComponentType + def arrayElementClass(schematic: Any): jClass[_] = schematic match { + case cls: jClass[_] => cls.getComponentType case tag: ClassTag[_] => tag.runtimeClass - case _ => throw new UnsupportedOperationException("unsupported schematic %s (%s)".format(schematic, if (schematic == null) "null" else schematic.getClass)) + case _ => + throw new UnsupportedOperationException(s"unsupported schematic $schematic (${schematic.getClass})") } /** Return the class object representing an unboxed value type, * e.g. classOf[int], not classOf[java.lang.Integer]. The compiler * rewrites expressions like 5.getClass to come here. */ - def anyValClass[T <: AnyVal : ClassTag](value: T): Class[T] = - classTag[T].runtimeClass.asInstanceOf[Class[T]] + def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = + classTag[T].runtimeClass.asInstanceOf[jClass[T]] var arrayApplyCount = 0 @@ -93,11 +88,9 @@ object ScalaRunTime { } } - var arrayUpdateCount = 0 - /** update generic array element */ def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { - arrayUpdateCount += 1 + arrayApplyCount += 1 xs match { case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] case x: Array[Int] => x(idx) = value.asInstanceOf[Int] @@ -156,7 +149,7 @@ object ScalaRunTime { dest } - def toArray[T](xs: collection.Seq[T]) = { + def toArray[T](xs: scala.collection.Seq[T]) = { val arr = new Array[AnyRef](xs.length) var i = 0 for (x <- xs) { @@ -179,39 +172,10 @@ object ScalaRunTime { def checkInitialized[T <: AnyRef](x: T): T = if (x == null) throw new UninitializedError else x - abstract class Try[+A] { - def Catch[B >: A](handler: PartialFunction[Throwable, B]): B - def Finally(fin: => Unit): A - } - - def Try[A](block: => A): Try[A] = new Try[A] with Runnable { - private var result: A = _ - private var exception: Throwable = - try { run() ; null } - catch { - case e: ControlThrowable => throw e // don't catch non-local returns etc - case e: Throwable => e - } - - def run() { result = block } - - def Catch[B >: A](handler: PartialFunction[Throwable, B]): B = - if (exception == null) result - else if (handler isDefinedAt exception) handler(exception) - else throw exception - - def Finally(fin: => Unit): A = { - fin - - if (exception == null) result - else throw exception - } - } - def _toString(x: Product): String = x.productIterator.mkString(x.productPrefix + "(", ",", ")") - def _hashCode(x: Product): Int = scala.util.MurmurHash3.productHash(x) + def _hashCode(x: Product): Int = scala.util.hashing.MurmurHash3.productHash(x) /** A helper for case classes. */ def typedProductIterator[T](x: Product): Iterator[T] = { @@ -246,12 +210,12 @@ object ScalaRunTime { // Note that these are the implementations called by ##, so they // must not call ## themselves. - @inline def hash(x: Any): Int = + def hash(x: Any): Int = if (x == null) 0 else if (x.isInstanceOf[java.lang.Number]) BoxesRunTime.hashFromNumber(x.asInstanceOf[java.lang.Number]) else x.hashCode - @inline def hash(dv: Double): Int = { + def hash(dv: Double): Int = { val iv = dv.toInt if (iv == dv) return iv @@ -261,7 +225,7 @@ object ScalaRunTime { val fv = dv.toFloat if (fv == dv) fv.hashCode else dv.hashCode } - @inline def hash(fv: Float): Int = { + def hash(fv: Float): Int = { val iv = fv.toInt if (iv == fv) return iv @@ -269,29 +233,29 @@ object ScalaRunTime { if (lv == fv) return hash(lv) else fv.hashCode } - @inline def hash(lv: Long): Int = { + def hash(lv: Long): Int = { val low = lv.toInt val lowSign = low >>> 31 val high = (lv >>> 32).toInt low ^ (high + lowSign) } - @inline def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x) + def hash(x: Number): Int = runtime.BoxesRunTime.hashFromNumber(x) // The remaining overloads are here for completeness, but the compiler // inlines these definitions directly so they're not generally used. - @inline def hash(x: Int): Int = x - @inline def hash(x: Short): Int = x.toInt - @inline def hash(x: Byte): Int = x.toInt - @inline def hash(x: Char): Int = x.toInt - @inline def hash(x: Boolean): Int = if (x) true.hashCode else false.hashCode - @inline def hash(x: Unit): Int = 0 + def hash(x: Int): Int = x + def hash(x: Short): Int = x.toInt + def hash(x: Byte): Int = x.toInt + def hash(x: Char): Int = x.toInt + def hash(x: Boolean): Int = if (x) true.hashCode else false.hashCode + def hash(x: Unit): Int = 0 /** A helper method for constructing case class equality methods, * because existential types get in the way of a clean outcome and * it's performing a series of Any/Any equals comparisons anyway. * See ticket #2867 for specifics. */ - def sameElements(xs1: collection.Seq[Any], xs2: collection.Seq[Any]) = xs1 sameElements xs2 + def sameElements(xs1: scala.collection.Seq[Any], xs2: scala.collection.Seq[Any]) = xs1 sameElements xs2 /** Given any Scala value, convert it to a String. * @@ -358,7 +322,7 @@ object ScalaRunTime { case x: String => if (x.head.isWhitespace || x.last.isWhitespace) "\"" + x + "\"" else x case x if useOwnToString(x) => x.toString case x: AnyRef if isArray(x) => arrayToString(x) - case x: collection.Map[_, _] => x.iterator take maxElements map mapInner mkString (x.stringPrefix + "(", ", ", ")") + case x: scala.collection.Map[_, _] => x.iterator take maxElements map mapInner mkString (x.stringPrefix + "(", ", ", ")") case x: Iterable[_] => x.iterator take maxElements map inner mkString (x.stringPrefix + "(", ", ", ")") case x: Traversable[_] => x take maxElements map inner mkString (x.stringPrefix + "(", ", ", ")") case x: Product1[_] if isTuple(x) => "(" + inner(x._1) + ",)" // that special trailing comma diff --git a/test/instrumented/srt.patch b/test/instrumented/srt.patch index 47dcfa2197..ee619b2ecb 100644 --- a/test/instrumented/srt.patch +++ b/test/instrumented/srt.patch @@ -1,67 +1,10 @@ 8a9,10 > /* INSTRUMENTED VERSION */ > -73a76,77 +68a71,72 > var arrayApplyCount = 0 > -75,86c79,93 -< def array_apply(xs: AnyRef, idx: Int): Any = xs match { -< case x: Array[AnyRef] => x(idx).asInstanceOf[Any] -< case x: Array[Int] => x(idx).asInstanceOf[Any] -< case x: Array[Double] => x(idx).asInstanceOf[Any] -< case x: Array[Long] => x(idx).asInstanceOf[Any] -< case x: Array[Float] => x(idx).asInstanceOf[Any] -< case x: Array[Char] => x(idx).asInstanceOf[Any] -< case x: Array[Byte] => x(idx).asInstanceOf[Any] -< case x: Array[Short] => x(idx).asInstanceOf[Any] -< case x: Array[Boolean] => x(idx).asInstanceOf[Any] -< case x: Array[Unit] => x(idx).asInstanceOf[Any] -< case null => throw new NullPointerException ---- -> def array_apply(xs: AnyRef, idx: Int): Any = { +70a75 +> arrayApplyCount += 1 +87a93 > arrayApplyCount += 1 -> xs match { -> case x: Array[AnyRef] => x(idx).asInstanceOf[Any] -> case x: Array[Int] => x(idx).asInstanceOf[Any] -> case x: Array[Double] => x(idx).asInstanceOf[Any] -> case x: Array[Long] => x(idx).asInstanceOf[Any] -> case x: Array[Float] => x(idx).asInstanceOf[Any] -> case x: Array[Char] => x(idx).asInstanceOf[Any] -> case x: Array[Byte] => x(idx).asInstanceOf[Any] -> case x: Array[Short] => x(idx).asInstanceOf[Any] -> case x: Array[Boolean] => x(idx).asInstanceOf[Any] -> case x: Array[Unit] => x(idx).asInstanceOf[Any] -> case null => throw new NullPointerException -> } -88a96,97 -> var arrayUpdateCount = 0 -> -90,101c99,113 -< def array_update(xs: AnyRef, idx: Int, value: Any): Unit = xs match { -< case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] -< case x: Array[Int] => x(idx) = value.asInstanceOf[Int] -< case x: Array[Double] => x(idx) = value.asInstanceOf[Double] -< case x: Array[Long] => x(idx) = value.asInstanceOf[Long] -< case x: Array[Float] => x(idx) = value.asInstanceOf[Float] -< case x: Array[Char] => x(idx) = value.asInstanceOf[Char] -< case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] -< case x: Array[Short] => x(idx) = value.asInstanceOf[Short] -< case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] -< case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] -< case null => throw new NullPointerException ---- -> def array_update(xs: AnyRef, idx: Int, value: Any): Unit = { -> arrayUpdateCount += 1 -> xs match { -> case x: Array[AnyRef] => x(idx) = value.asInstanceOf[AnyRef] -> case x: Array[Int] => x(idx) = value.asInstanceOf[Int] -> case x: Array[Double] => x(idx) = value.asInstanceOf[Double] -> case x: Array[Long] => x(idx) = value.asInstanceOf[Long] -> case x: Array[Float] => x(idx) = value.asInstanceOf[Float] -> case x: Array[Char] => x(idx) = value.asInstanceOf[Char] -> case x: Array[Byte] => x(idx) = value.asInstanceOf[Byte] -> case x: Array[Short] => x(idx) = value.asInstanceOf[Short] -> case x: Array[Boolean] => x(idx) = value.asInstanceOf[Boolean] -> case x: Array[Unit] => x(idx) = value.asInstanceOf[Unit] -> case null => throw new NullPointerException -> } -- cgit v1.2.3 From cb028ba477f37778bccfc23e597acc0284259681 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 6 Sep 2013 17:04:53 +0200 Subject: SI-7818 Cast our way out of extended existential angst `substituteSymbols` is not sophisticated enough to operate on `TypeSkolem`-s which are based on one of the "from" symbols. The pertinant usage of `substituteSymbols` for this bug in in `Extender`. Recapping on that transform: // orig class C[T](...) extends AnyVal { def foo[U] = } // transform class C[T] extends AnyVal { ... } object C { def foo$extension[T', U'] = } Where `` has been subtituted with, among other things, `[T, U] ~> [T', U']`. In this case our expected type contains a new type parameter (of the extension method), whereas the type of the RHS contains an existential skolem still pinned to the corresponding class type parameter. tree.tpe = Observable1#7037[_$1#12344] <_$1#12344>.info = <: T#7040 pt = Observable1#7037[T#15644] The limitation of substution is lamented in the comments of `adaptMismatchedSkolems`, which faces the harder version of the issue where the skolems are in the expected type. But, we're in the "easy" case with the skolems in the tree's type; we can cast our way out of the problem. See also f335e447 / ed915c54. --- src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala | 7 ++++++- test/files/pos/t7818.scala | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t7818.scala diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index bc54054028..e0c0cd0fdb 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -231,9 +231,14 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { .substituteThis(origThis, extensionThis) .changeOwner(origMeth -> extensionMeth) ) + val castBody = + if (extensionBody.tpe <:< extensionMono.finalResultType) + extensionBody + else + gen.mkCastPreservingAnnotations(extensionBody, extensionMono.finalResultType) // SI-7818 e.g. mismatched existential skolems // Record the extension method ( FIXME: because... ? ) - extensionDefs(companion) += atPos(tree.pos)(DefDef(extensionMeth, extensionBody)) + extensionDefs(companion) += atPos(tree.pos)(DefDef(extensionMeth, castBody)) // These three lines are assembling Foo.bar$extension[T1, T2, ...]($this) // which leaves the actual argument application for extensionCall. diff --git a/test/files/pos/t7818.scala b/test/files/pos/t7818.scala new file mode 100644 index 0000000000..77b99e7d5d --- /dev/null +++ b/test/files/pos/t7818.scala @@ -0,0 +1,10 @@ +class Observable1[+T](val asJava: JObservable[_ <: T]) extends AnyVal { + private def foo[X](a: JObservable[X]): JObservable[X] = ??? + // was generating a type error as the type of the RHS included an existential + // skolem based on the class type parameter `T`, which did not conform + // to the typer parameter of the extension method into which the RHS is + // transplanted. + def synchronize: Observable1[T] = new Observable1(foo(asJava)) +} + +class JObservable[T] -- cgit v1.2.3 From 48283ca80c17b2814a27464c30bca2445ffcc7a5 Mon Sep 17 00:00:00 2001 From: François Garillot Date: Thu, 5 Sep 2013 18:18:43 +0200 Subject: SI-7767 avoid rejecting Scaladoc comments in early initializers review by @retronym --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 5476afa75e..996287dea8 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2772,6 +2772,8 @@ self => List(copyValDef(vdef)(mods = mods | Flags.PRESUPER)) case tdef @ TypeDef(mods, name, tparams, rhs) => List(treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs)) + case docdef @ DocDef(comm, rhs) => + List(treeCopy.DocDef(docdef, comm, rhs)) case stat if !stat.isEmpty => syntaxError(stat.pos, "only type definitions and concrete field definitions allowed in early object initialization section", false) List() -- cgit v1.2.3 From 23918873cbe055f22d40fa4c96651fd93186e687 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 9 Sep 2013 10:33:42 +0200 Subject: SI-7767 Test case for Scaladoc on early initializers --- test/scaladoc/run/t7767.check | 1 + test/scaladoc/run/t7767.scala | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 test/scaladoc/run/t7767.check create mode 100644 test/scaladoc/run/t7767.scala diff --git a/test/scaladoc/run/t7767.check b/test/scaladoc/run/t7767.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/t7767.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/t7767.scala b/test/scaladoc/run/t7767.scala new file mode 100644 index 0000000000..be5e9fe080 --- /dev/null +++ b/test/scaladoc/run/t7767.scala @@ -0,0 +1,20 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + // This caused an infinite recursion in method inline() in CommentFactory.scala + override def code = """ + class Docable extends { /**Doc*/ val foo = 0 } with AnyRef + """ + + // no need for special settings + def scaladocSettings = "" + + def testModel(rootPackage: Package) = { + import access._ + // if it doesn't hang, the test is passed + val comment = rootPackage._class("Docable")._value("foo").comment.map(_.body.toString.trim).getOrElse("") + assert(comment.contains("Doc"), comment) + } +} -- cgit v1.2.3 From bf0f9da50983941b8575ffae284d9c90de886020 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 9 Sep 2013 10:06:00 +0200 Subject: SI-7825 Consider DEFAULTMETHOD when refchecking concreteness A class should not be required to implement a Java default method. This commit uses `isDeferredNotDefault` in place of `isDeferred` when finding unimplemented methods. The test itself does not depend on Java 8 as we use scalac's Java source parser to set things up. --- bincompat-backward.whitelist.conf | 8 +++++ bincompat-forward.whitelist.conf | 24 +++++++++++++++ .../scala/tools/nsc/typechecker/RefChecks.scala | 4 +-- src/reflect/scala/reflect/internal/HasFlags.scala | 3 ++ test/files/run/t7825.scala | 34 ++++++++++++++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 test/files/run/t7825.scala diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 38d26c7fb7..0500ec10ab 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -303,6 +303,14 @@ filter { { matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques_=" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.HasFlags.isDeferredOrDefault" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.HasFlags.isDeferredNotDefault" + problemName=MissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index a64eb0ba5d..2ece671638 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -1407,6 +1407,30 @@ filter { { matchName="scala.reflect.internal.Types.scala$reflect$internal$Types$$uniques_=" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Symbols#Symbol.isDeferredOrDefault" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Symbols#Symbol.isDeferredNotDefault" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.HasFlags.isDeferredOrDefault" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.HasFlags.isDeferredNotDefault" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Trees#Modifiers.isDeferredOrDefault" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.internal.Trees#Modifiers.isDeferredNotDefault" + problemName=MissingMethodProblem } ] } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 09c4878b2f..83afb3a728 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -383,7 +383,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans overrideError("cannot be used here - classes can only override abstract types"); } else if (other.isEffectivelyFinal) { // (1.2) overrideError("cannot override final member"); - } else if (!other.isDeferred && !other.hasFlag(DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*) + } else if (!other.isDeferredOrDefault && !member.isAnyOverride && !member.isSynthetic) { // (*) // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. if (isNeitherInClass && !(other.owner isSubClass member.owner)) @@ -565,7 +565,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def checkNoAbstractMembers(): Unit = { // Avoid spurious duplicates: first gather any missing members. def memberList = clazz.info.nonPrivateMembersAdmitting(VBRIDGE) - val (missing, rest) = memberList partition (m => m.isDeferred && !ignoreDeferred(m)) + val (missing, rest) = memberList partition (m => m.isDeferredNotDefault && !ignoreDeferred(m)) // Group missing members by the name of the underlying symbol, // to consolidate getters and setters. val grouped = missing groupBy (sym => analyzer.underlyingSymbol(sym).name) diff --git a/src/reflect/scala/reflect/internal/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala index 4a3663b8ea..12fd3a31cd 100644 --- a/src/reflect/scala/reflect/internal/HasFlags.scala +++ b/src/reflect/scala/reflect/internal/HasFlags.scala @@ -115,6 +115,9 @@ trait HasFlags { def isSynthetic = hasFlag(SYNTHETIC) def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM) + def isDeferredOrDefault = hasFlag(DEFERRED | DEFAULTMETHOD) + def isDeferredNotDefault = isDeferred && !hasFlag(DEFAULTMETHOD) + def flagBitsToString(bits: Long): String = { // Fast path for common case if (bits == 0L) "" else { diff --git a/test/files/run/t7825.scala b/test/files/run/t7825.scala new file mode 100644 index 0000000000..65ca06fdfc --- /dev/null +++ b/test/files/run/t7825.scala @@ -0,0 +1,34 @@ +import scala.tools.partest._ + +object Test extends CompilerTest { + import global._ + + override lazy val units: List[CompilationUnit] = { + // We can test this on JDK6. + javaCompilationUnits(global)(defaultMethodSource) ++ compilationUnits(global)(scalaExtendsDefault) + } + + private def defaultMethodSource = """ +public interface Iterator { + boolean hasNext(); + E next(); + default void remove() { + throw new UnsupportedOperationException("remove"); + } +} + """ + + private def scalaExtendsDefault = """ +object Test { + object X extends Iterator[String] { + def hasNext = true + def next = "!" + } +} + """ + + // We're only checking we that the Scala compilation unit passes refchecks + // No further checks are needed here. + def check(source: String, unit: global.CompilationUnit): Unit = { + } +} -- cgit v1.2.3 From a1796aa5ed280203c1bfc086ee5a368ae628611d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 9 Sep 2013 22:32:28 +0200 Subject: SI-7398 Enable test for Java 8 source parser under Java 8 There is no need to skip it as it only depends on our changes to our JavaParser, and not on any bytecode features of Java 8. --- test/files/run/t7398.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/files/run/t7398.scala b/test/files/run/t7398.scala index dd59697b71..493c4dcf40 100644 --- a/test/files/run/t7398.scala +++ b/test/files/run/t7398.scala @@ -3,11 +3,9 @@ import scala.tools.partest._ object Test extends CompilerTest { import global._ - // This way we auto-pass on non-java8 since there's nothing to check - override lazy val units: List[CompilationUnit] = testUnderJavaAtLeast("1.8") { + override lazy val units: List[CompilationUnit] = { + // This test itself does not depend on JDK8. javaCompilationUnits(global)(defaultMethodSource) - } otherwise { - Nil } private def defaultMethodSource = """ -- cgit v1.2.3