diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index 722642074c1b..b6e61976b735 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -30,6 +30,13 @@ object MimaFilters extends AutoPlugin { // #9487 ProblemFilters.exclude[MissingClassProblem]("scala.reflect.ClassTag$cache$"), + + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.redWithRight"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.redWithLeftRight"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.blackWithLeftRight"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree#Tree.redWithLeft"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree.partitionKeys"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.RedBlackTree.filterKeys"), ) override val buildSettings = Seq( diff --git a/src/compiler/scala/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/reflect/quasiquotes/Parsers.scala index c4b9d8f78bf8..eb1310ed0d80 100644 --- a/src/compiler/scala/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/reflect/quasiquotes/Parsers.scala @@ -99,9 +99,9 @@ trait Parsers { self: Quasiquotes => override def makeFunctionTypeTree(argtpes: List[Tree], restpe: Tree): Tree = FunctionTypePlaceholder(argtpes, restpe) // make q"val (x: T) = rhs" be equivalent to q"val x: T = rhs" for sake of bug compatibility (scala/bug#8211) - override def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = pat match { - case TuplePlaceholder(inParensPat :: Nil) => super.makePatDef(mods, inParensPat, rhs) - case _ => super.makePatDef(mods, pat, rhs) + override def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree, rhsPos: Position) = pat match { + case TuplePlaceholder(inParensPat :: Nil) => super.makePatDef(mods, inParensPat, rhs, rhsPos) + case _ => super.makePatDef(mods, pat, rhs, rhsPos) } } import treeBuilder.{global => _, unit => _} diff --git a/src/compiler/scala/tools/nsc/ObjectRunner.scala b/src/compiler/scala/tools/nsc/ObjectRunner.scala index 756822931d21..82b50330b1a0 100644 --- a/src/compiler/scala/tools/nsc/ObjectRunner.scala +++ b/src/compiler/scala/tools/nsc/ObjectRunner.scala @@ -25,8 +25,10 @@ trait CommonRunner { * @throws java.lang.NoSuchMethodException * @throws java.lang.reflect.InvocationTargetException */ - def run(urls: Seq[URL], objectName: String, arguments: Seq[String]): Unit = - ScalaClassLoader.fromURLs(urls).run(objectName, arguments) + def run(urls: Seq[URL], objectName: String, arguments: Seq[String]): Unit = { + import scala.reflect.internal.util.RichClassLoader._ + ScalaClassLoader.fromURLsParallelCapable(urls).run(objectName, arguments) + } /** Catches any non-fatal exception thrown by run (in the case of InvocationTargetException, * unwrapping it) and returns it in an Option. diff --git a/src/compiler/scala/tools/nsc/PipelineMain.scala b/src/compiler/scala/tools/nsc/PipelineMain.scala index 1978abf2555f..7300d6996f33 100644 --- a/src/compiler/scala/tools/nsc/PipelineMain.scala +++ b/src/compiler/scala/tools/nsc/PipelineMain.scala @@ -55,10 +55,19 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe /** Forward errors to the (current) reporter. */ protected def scalacError(msg: String): Unit = { - reporter.error(FakePos("scalac"), msg + "\n scalac -help gives more information") + reporterError(FakePos("scalac"), msg + "\n scalac -help gives more information") } private var reporter: Reporter = _ + private def reporterEcho(pos: Position, msg: String): Unit = synchronized { + reporter.echo(pos, msg) + } + private def reporterEcho(msg: String): Unit = synchronized { + reporter.echo(NoPosition, msg) + } + private def reporterError(pos: Position, msg: String): Unit = synchronized { + reporter.echo(msg) + } private object handler extends UncaughtExceptionHandler { override def uncaughtException(t: Thread, e: Throwable): Unit = { @@ -93,14 +102,14 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe builder.append("}\n") val path = logDir.resolve("projects.dot") Files.write(path, builder.toString.getBytes(java.nio.charset.StandardCharsets.UTF_8)) - reporter.echo("Wrote project dependency graph to: " + path.toAbsolutePath) + reporterEcho("Wrote project dependency graph to: " + path.toAbsolutePath) } private case class Dependency(t: Task, isMacro: Boolean, isPlugin: Boolean) def process(): Boolean = { reporter = createReporter(new Settings(scalacError)) - reporter.echo(s"parallelism = $parallelism, strategy = $strategy") + reporterEcho(s"parallelism = $parallelism, strategy = $strategy") def commandFor(argFileArg: Path): Task = { val ss = new Settings(scalacError) @@ -137,13 +146,13 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe } else { PickleExtractor.process(entry, extracted) Files.setLastModifiedTime(extracted, sourceTimeStamp) - reporter.echo(s"Exported pickles from $entry to $extracted") + reporterEcho(s"Exported pickles from $entry to $extracted") Files.setLastModifiedTime(extracted, sourceTimeStamp) } strippedAndExportedClassPath(entry) = extracted } exportTimer.stop() - reporter.echo(f"Exported external classpath in ${exportTimer.durationMs}%.0f ms") + reporterEcho(f"Exported external classpath in ${exportTimer.durationMs}%.0f ms") } } @@ -169,14 +178,14 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe Await.result(awaitAllFutures, Duration(60, "s")) timer.stop() val numCompleted = allFutures.count(_.isCompleted) - reporter.echo(s"PROGRESS: $numCompleted / $numAllFutures") + reporterEcho(s"PROGRESS: $numCompleted / $numAllFutures") return } catch { case _: TimeoutException => val numCompleted = allFutures.count(_.isCompleted) if (numCompleted == lastNumCompleted) { - reporter.echo(s"STALLED: $numCompleted / $numAllFutures") - reporter.echo("Outline/Scala/Javac") + reporterEcho(s"STALLED: $numCompleted / $numAllFutures") + reporterEcho("Outline/Scala/Javac") projects.map { p => def toX(b: Future[_]): String = b.value match { case None => "-"; case Some(Success(_)) => "x"; case Some(Failure(_)) => "!" } @@ -184,7 +193,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe reporter.echo(s + " " + p.label) } } else { - reporter.echo(s"PROGRESS: $numCompleted / $numAllFutures") + reporterEcho(s"PROGRESS: $numCompleted / $numAllFutures") lastNumCompleted = numCompleted } } @@ -225,9 +234,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe if (parallelism == 1) { val criticalPath = projects.maxBy(_.regularCriticalPathMs) - reporter.echo(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") } else - reporter.echo(f" Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f" Wall Clock: ${timer.durationMs}%.0f ms") case Pipeline => projects.foreach { p => val depsReady = Future.traverse(dependsOn.getOrElse(p, Nil))(task => p.dependencyReadyFuture(task)) @@ -264,9 +273,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe if (parallelism == 1) { val criticalPath = projects.maxBy(_.regularCriticalPathMs) - reporter.echo(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Critical path: ${criticalPath.regularCriticalPathMs}%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") } else - reporter.echo(f" Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f" Wall Clock: ${timer.durationMs}%.0f ms") case Traditional => projects.foreach { p => val f1 = Future.traverse(dependsOn.getOrElse(p, Nil))(_.t.javaDone.future) @@ -289,9 +298,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe } if (parallelism == 1) { val maxFullCriticalPath: Double = projects.map(_.fullCriticalPathMs).max - reporter.echo(f"Critical path: $maxFullCriticalPath%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Critical path: $maxFullCriticalPath%.0f ms. Wall Clock: ${timer.durationMs}%.0f ms") } else { - reporter.echo(f"Wall Clock: ${timer.durationMs}%.0f ms") + reporterEcho(f"Wall Clock: ${timer.durationMs}%.0f ms") } } @@ -340,7 +349,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe trace.append("]}") val traceFile = logDir.resolve(s"build-${label}.trace") Files.write(traceFile, trace.toString.getBytes()) - reporter.echo("Chrome trace written to " + traceFile.toAbsolutePath) + reporterEcho("Chrome trace written to " + traceFile.toAbsolutePath) } case class Group(files: List[String]) { @@ -423,7 +432,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe if (reporter.hasErrors) reporter.flush() else if (command.shouldStopWithInfo) - reporter.echo(command.getInfoMessage(result)) + reporterEcho(command.getInfoMessage(result)) result.reporter = createReporter(result.settings) result } catch { @@ -556,9 +565,9 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe Position.range(sourceFile, diagnostic.getStartPosition.toInt, diagnostic.getPosition.toInt, diagnostic.getEndPosition.toInt) } diagnostic.getKind match { - case Kind.ERROR => reporter.error(position, msg) + case Kind.ERROR => reporterError(position, msg) case Kind.WARNING | Kind.MANDATORY_WARNING => Task.this.compiler.runReporting.warning(position, msg, WarningCategory.JavaSource, site = "") - case Kind.NOTE | Kind.OTHER => reporter.echo(position, msg) + case Kind.NOTE | Kind.OTHER => reporterEcho(position, msg) } } } @@ -577,7 +586,7 @@ class PipelineMainClass(argFiles: Seq[Path], pipelineSettings: PipelineMain.Pipe javaDone.complete(Success(())) } } - def log(msg: String): Unit = reporter.echo(this.label + ": " + msg) + def log(msg: String): Unit = reporterEcho(this.label + ": " + msg) } final class Timer() { diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 4736df60916a..36f533ea191f 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2697,10 +2697,10 @@ self => in.nextToken() val lhs = commaSeparated(stripParens(noSeq.pattern2())) val tp = typedOpt() - val rhs = + val (rhs, rhsPos) = if (!tp.isEmpty && in.token != EQUALS) { newmods = newmods | Flags.DEFERRED - EmptyTree + (EmptyTree, NoPosition) } else { accept(EQUALS) expr() match { @@ -2712,14 +2712,14 @@ self => } placeholderParams = placeholderParams.tail newmods = newmods | Flags.DEFAULTINIT - EmptyTree - case x => x + (EmptyTree, x.pos) + case x => (x, x.pos) } } def mkDefs(p: Tree, tp: Tree, rhs: Tree): List[Tree] = { val trees = { val pat = if (tp.isEmpty) p else Typed(p, tp) setPos (p.pos union tp.pos) - makePatDef(newmods, pat, rhs) + makePatDef(newmods, pat, rhs, rhsPos) } if (newmods.isDeferred) { trees match { diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala index ea7e9f1b0cc5..63249dd88a6e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala @@ -143,5 +143,6 @@ abstract class TreeBuilder { } } - def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree) = gen.mkPatDef(mods, pat, rhs) + final def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree): List[ValDef] = makePatDef(mods, pat, rhs, rhs.pos) + def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree, rhsPos: Position) = gen.mkPatDef(mods, pat, rhs, rhsPos) } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala index 5df66c34b707..7d790313f694 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BoxUnbox.scala @@ -265,6 +265,10 @@ abstract class BoxUnbox { case c: EscapingConsumer => assert(keepBox, s"found escaping consumer, but box is eliminated: $c") + case Drop(insn) => + if (keepBox) toReplace(insn) = List(getPop(1)) + else toDelete += insn + case extraction => val (slot, tp) = localSlots(boxKind.extractedValueIndex(extraction)) val loadOps = new VarInsnNode(tp.getOpcode(ILOAD), slot) :: extraction.postExtractionAdaptationOps(tp) @@ -327,31 +331,38 @@ abstract class BoxUnbox { if (boxKind.boxedTypes.lengthCompare(1) == 0) { // fast path for single-value boxes allConsumers.foreach(extraction => extraction.postExtractionAdaptationOps(boxKind.boxedTypes.head) match { - case Nil => - toDelete ++= extraction.allInsns + case Nil => extraction match { + case Drop(_) => toReplace(extraction.consumer) = boxKind.boxedTypes.map(t => getPop(t.getSize)) + case _ => toDelete ++= extraction.allInsns + } case ops => toReplace(extraction.consumer) = ops toDelete ++= extraction.allInsns - extraction.consumer }) } else { for (extraction <- allConsumers) { - val valueIndex = boxKind.extractedValueIndex(extraction) - val replacementOps = if (valueIndex == 0) { - val pops = boxKind.boxedTypes.tail.map(t => getPop(t.getSize)) - pops ::: extraction.postExtractionAdaptationOps(boxKind.boxedTypes.head) - } else { - var loadOps: List[AbstractInsnNode] = null + val replacementOps = extraction match { + case Drop(_) => + boxKind.boxedTypes.reverseIterator.map(t => getPop(t.getSize)).toList + case _ => + val valueIndex = boxKind.extractedValueIndex(extraction) + if (valueIndex == 0) { + val pops = boxKind.boxedTypes.tail.map(t => getPop(t.getSize)) + pops ::: extraction.postExtractionAdaptationOps(boxKind.boxedTypes.head) + } else { + var loadOps: List[AbstractInsnNode] = null val consumeStack = boxKind.boxedTypes.zipWithIndex.reverseIterator.map { - case (tp, i) => - if (i == valueIndex) { - val resultSlot = getLocal(tp.getSize) - loadOps = new VarInsnNode(tp.getOpcode(ILOAD), resultSlot) :: extraction.postExtractionAdaptationOps(tp) - new VarInsnNode(tp.getOpcode(ISTORE), resultSlot) - } else { - getPop(tp.getSize) - } + case (tp, i) => + if (i == valueIndex) { + val resultSlot = getLocal(tp.getSize) + loadOps = new VarInsnNode(tp.getOpcode(ILOAD), resultSlot) :: extraction.postExtractionAdaptationOps(tp) + new VarInsnNode(tp.getOpcode(ISTORE), resultSlot) + } else { + getPop(tp.getSize) + } }.to(List) - consumeStack ::: loadOps + consumeStack ::: loadOps + } } toReplace(extraction.consumer) = replacementOps toDelete ++= extraction.allInsns - extraction.consumer @@ -621,7 +632,7 @@ abstract class BoxUnbox { val afterInit = initCall.getNext val stackTopAfterInit = prodCons.frameAt(afterInit).stackTop val initializedInstanceCons = prodCons.consumersOfValueAt(afterInit, stackTopAfterInit) - if (initializedInstanceCons == dupConsWithoutInit && prodCons.producersForValueAt(afterInit, stackTopAfterInit) == Set(dupOp)) { + if (initializedInstanceCons == dupConsWithoutInit) { return Some((dupOp, initCall)) } } @@ -708,6 +719,9 @@ abstract class BoxUnbox { val success = primBoxSupertypes(kind.boxClass).contains(ti.desc) Some(BoxedPrimitiveTypeCheck(ti, success)) + case i: InsnNode if i.getOpcode == POP => + Some(Drop(i)) + case _ => None } } @@ -761,6 +775,9 @@ abstract class BoxUnbox { case ti: TypeInsnNode if ti.getOpcode == INSTANCEOF => Some(BoxedPrimitiveTypeCheck(ti, ti.desc == kind.refClass || refSupertypes.contains(ti.desc))) + case i: InsnNode if i.getOpcode == POP => + Some(Drop(i)) + case _ => None } } @@ -824,6 +841,7 @@ abstract class BoxUnbox { } case _ => + if (insn.getOpcode == POP) return Some(Drop(insn)) } None } @@ -943,6 +961,8 @@ abstract class BoxUnbox { case class StaticSetterOrInstanceWrite(consumer: AbstractInsnNode) extends BoxConsumer /** `.\$isInstanceOf[T]` (can be statically proven true or false) */ case class BoxedPrimitiveTypeCheck(consumer: AbstractInsnNode, success: Boolean) extends BoxConsumer + /** POP */ + case class Drop(consumer: AbstractInsnNode) extends BoxConsumer /** An unknown box consumer */ case class EscapingConsumer(consumer: AbstractInsnNode) extends BoxConsumer } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala index 5b44a03bde65..a3d500fb1dfa 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ByteCodeRepository.scala @@ -17,6 +17,7 @@ package opt import scala.annotation.nowarn import scala.collection.{concurrent, mutable} import scala.jdk.CollectionConverters._ +import scala.reflect.internal.util.NoPosition import scala.tools.asm import scala.tools.asm.Attribute import scala.tools.asm.tree._ @@ -34,7 +35,7 @@ abstract class ByteCodeRepository extends PerRunInit { import postProcessor.{bTypes, bTypesFromClassfile} import bTypes._ - import frontendAccess.{backendClassPath, recordPerRunCache} + import frontendAccess.{backendReporting, backendClassPath, recordPerRunCache} /** * Contains ClassNodes and the canonical path of the source file path of classes being compiled in @@ -276,25 +277,32 @@ abstract class ByteCodeRepository extends PerRunInit { private def parseClass(internalName: InternalName): Either[ClassNotFound, ClassNode] = { val fullName = internalName.replace('/', '.') - backendClassPath.findClassFile(fullName) map { classFile => + backendClassPath.findClassFile(fullName).flatMap { classFile => val classNode = new ClassNode1 val classReader = new asm.ClassReader(classFile.toByteArray) - // Passing the InlineInfoAttributePrototype makes the ClassReader invoke the specific `read` - // method of the InlineInfoAttribute class, instead of putting the byte array into a generic - // Attribute. - // We don't need frames when inlining, but we want to keep the local variable table, so we - // don't use SKIP_DEBUG. - classReader.accept(classNode, Array[Attribute](InlineInfoAttributePrototype), asm.ClassReader.SKIP_FRAMES) - // SKIP_FRAMES leaves line number nodes. Remove them because they are not correct after - // inlining. - // TODO: we need to remove them also for classes that are not parsed from classfiles, why not simplify and do it once when inlining? - // OR: instead of skipping line numbers for inlined code, use write a SourceDebugExtension - // attribute that contains JSR-45 data that encodes debugging info. + try { + // Passing the InlineInfoAttributePrototype makes the ClassReader invoke the specific `read` + // method of the InlineInfoAttribute class, instead of putting the byte array into a generic + // Attribute. + // We don't need frames when inlining, but we want to keep the local variable table, so we + // don't use SKIP_DEBUG. + classReader.accept(classNode, Array[Attribute](InlineInfoAttributePrototype), asm.ClassReader.SKIP_FRAMES) + // SKIP_FRAMES leaves line number nodes. Remove them because they are not correct after + // inlining. + // TODO: we need to remove them also for classes that are not parsed from classfiles, why not simplify and do it once when inlining? + // OR: instead of skipping line numbers for inlined code, use write a SourceDebugExtension + // attribute that contains JSR-45 data that encodes debugging info. // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.11 - // https://jcp.org/aboutJava/communityprocess/final/jsr045/index.html - removeLineNumbersAndAddLMFImplMethods(classNode) - classNode + // https://jcp.org/aboutJava/communityprocess/final/jsr045/index.html + removeLineNumbersAndAddLMFImplMethods(classNode) + Some(classNode) + } catch { + case ex: Exception => + if (frontendAccess.compilerSettings.debug) ex.printStackTrace() + backendReporting.warning(NoPosition, s"Error while reading InlineInfoAttribute from ${fullName}\n${ex.getMessage}") + None + } } match { case Some(node) => Right(node) case None => Left(ClassNotFound(internalName, javaDefinedClasses.get(internalName))) diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 298105445393..cbb403ddc465 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -596,9 +596,27 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { // with just `ArrayValue(...).$asInstanceOf[...]` // // See scala/bug#6611; we must *only* do this for literal vararg arrays. - case Apply(appMeth @ Select(appMethQual, _), Apply(wrapRefArrayMeth, (arg @ StripCast(ArrayValue(_, _))) :: Nil) :: _ :: Nil) - if wrapRefArrayMeth.symbol == currentRun.runDefinitions.wrapVarargsRefArrayMethod && appMeth.symbol == ArrayModule_genericApply && treeInfo.isQualifierSafeToElide(appMethQual) => - arg.transform(this) + case Apply(appMeth @ Select(appMethQual, _), Apply(wrapRefArrayMeth, (arg @ StripCast(ArrayValue(elemtpt, elems))) :: Nil) :: classTagEvidence :: Nil) + if (wrapRefArrayMeth.symbol == currentRun.runDefinitions.wrapVarargsRefArrayMethod || wrapRefArrayMeth.symbol == currentRun.runDefinitions.genericWrapVarargsRefArrayMethod) && appMeth.symbol == ArrayModule_genericApply && treeInfo.isQualifierSafeToElide(appMethQual) && + !elemtpt.tpe.typeSymbol.isBottomClass && !elemtpt.tpe.typeSymbol.isPrimitiveValueClass /* can happen via specialization.*/ => + classTagEvidence.attachments.get[analyzer.MacroExpansionAttachment] match { + case Some(att) if att.expandee.symbol.name == nme.materializeClassTag && tree.isInstanceOf[ApplyToImplicitArgs] => + super.transform(arg) + case _ => + localTyper.typedPos(tree.pos) { + gen.evalOnce(classTagEvidence, currentOwner, unit) { ev => + val arr = localTyper.typedPos(tree.pos)(gen.mkMethodCall(classTagEvidence, definitions.ClassTagClass.info.decl(nme.newArray), Nil, Literal(Constant(elems.size)) :: Nil)) + gen.evalOnce(arr, currentOwner, unit) { arr => + val stats = mutable.ListBuffer[Tree]() + foreachWithIndex(elems) { (elem, i) => + stats += gen.mkMethodCall(gen.mkAttributedRef(definitions.ScalaRunTimeModule), currentRun.runDefinitions.arrayUpdateMethod, + Nil, arr() :: Literal(Constant(i)) :: elem :: Nil) + } + super.transform(Block(stats.toList, arr())) + } + } + } + } case Apply(appMeth @ Select(appMethQual, _), elem0 :: Apply(wrapArrayMeth, (rest @ ArrayValue(elemtpt, _)) :: Nil) :: Nil) if wrapArrayMeth.symbol == wrapVarargsArrayMethod(elemtpt.tpe) && appMeth.symbol == ArrayModule_apply(elemtpt.tpe) && treeInfo.isQualifierSafeToElide(appMethQual) => treeCopy.ArrayValue(rest, rest.elemtpt, elem0 :: rest.elems).transform(this) @@ -607,7 +625,11 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { localTyper.typedPos(elem.pos) { ArrayValue(TypeTree(elem.tpe), elem :: Nil) } transform this - + case Apply(appMeth @ Select(appMethQual, _), elem :: (nil: RefTree) :: Nil) + if nil.symbol == NilModule && appMeth.symbol == ArrayModule_apply(elem.tpe.widen) && treeInfo.isExprSafeToInline(nil) && treeInfo.isQualifierSafeToElide(appMethQual) => + localTyper.typedPos(elem.pos) { + ArrayValue(TypeTree(elem.tpe), elem :: Nil) + } transform this // List(a, b, c) ~> new ::(a, new ::(b, new ::(c, Nil))) // Seq(a, b, c) ~> new ::(a, new ::(b, new ::(c, Nil))) case Apply(appMeth @ Select(appQual, _), List(Apply(wrapArrayMeth, List(StripCast(rest @ ArrayValue(elemtpt, _)))))) diff --git a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala index 3d6a550b08d0..19924c2ffb70 100644 --- a/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala +++ b/src/compiler/scala/tools/nsc/transform/async/AnfTransform.scala @@ -55,6 +55,8 @@ private[async] trait AnfTransform extends TransformUtils { tree case _ if !treeContainsAwait => tree + case Apply(TypeApply(sel@Select(_, _), _), arg :: Nil) if sel.symbol == definitions.Object_synchronized && containsAwait(arg) => + tree // pass through unchanged, this will be reported as an error later case Apply(sel@Select(fun, _), arg :: Nil) if isBooleanAnd(sel.symbol) && containsAwait(arg) => transform(treeCopy.If(tree, fun, arg, literalBool(false))) case Apply(sel@Select(fun, _), arg :: Nil) if isBooleanOr(sel.symbol) && containsAwait(arg) => diff --git a/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala b/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala index 2fae40c492e1..201e474628a4 100644 --- a/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/async/AsyncAnalysis.scala @@ -45,8 +45,8 @@ trait AsyncAnalysis extends TransformUtils { reportUnsupportedAwait(defDef, "nested method") } - override def byNameArgument(arg: Tree): Unit = { - reportUnsupportedAwait(arg, "by-name argument") + override def synchronizedCall(arg: Tree): Unit = { + reportUnsupportedAwait(arg, "synchronized call") } override def function(function: Function): Unit = { diff --git a/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala b/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala index f30caba93d96..01ef248e060d 100644 --- a/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala +++ b/src/compiler/scala/tools/nsc/transform/async/LiveVariables.scala @@ -91,7 +91,7 @@ trait LiveVariables extends ExprBuilder { override def nestedMethod(defdef: DefDef): Unit = capturingCheck(defdef) - override def byNameArgument(arg: Tree): Unit = capturingCheck(arg) + override def synchronizedCall(arg: Tree): Unit = capturingCheck(arg) override def function(function: Function): Unit = capturingCheck(function) override def function(expandedFunction: ClassDef): Unit = capturingCheck(expandedFunction) diff --git a/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala b/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala index 69e2888447a9..70cc6e317171 100644 --- a/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala +++ b/src/compiler/scala/tools/nsc/transform/async/TransformUtils.scala @@ -123,7 +123,7 @@ private[async] trait TransformUtils extends AsyncTransformStates { def nestedMethod(defdef: DefDef): Unit = { } - def byNameArgument(arg: Tree): Unit = { + def synchronizedCall(arg: Tree): Unit = { } def function(function: Function): Unit = { @@ -134,13 +134,14 @@ private[async] trait TransformUtils extends AsyncTransformStates { override def traverse(tree: Tree): Unit = { tree match { - case cd: ClassDef => + case cd: ClassDef => if (cd.symbol.isAnonymousClass) function(cd) else if (cd.symbol.isModuleClass) nestedModuleClass(cd) else nestedClass(cd) - case dd: DefDef => nestedMethod(dd) - case fun: Function => function(fun) - case _ => super.traverse(tree) + case dd: DefDef => nestedMethod(dd) + case fun: Function => function(fun) + case Apply(TypeApply(fun, _), arg :: Nil) if fun.symbol == definitions.Object_synchronized => synchronizedCall(arg) + case _ => super.traverse(tree) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index cdf51fa5836b..11854d63a87a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -5859,11 +5859,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (refTyped.isErrorTyped) setError(tree) else { - // .resultType unwraps NullaryMethodType (accessor of a path) + // .resultType unwraps NullaryMethodType (accessor of a path) // .deconst unwraps the ConstantType to a LiteralType (for literal-based singleton types) - tree setType refTyped.tpe.resultType.deconst if (!treeInfo.admitsTypeSelection(refTyped)) UnstableTreeError(tree) - else tree + else treeCopy.SingletonTypeTree(tree, refTyped).setType(refTyped.tpe.resultType.deconst) } } diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala index 1fb94b527097..14cdabd38555 100644 --- a/src/library/scala/Array.scala +++ b/src/library/scala/Array.scala @@ -17,8 +17,9 @@ import scala.collection.{Factory, immutable, mutable} import mutable.ArrayBuilder import immutable.ArraySeq import scala.language.implicitConversions -import scala.reflect.ClassTag +import scala.reflect.{ClassTag, classTag} import scala.runtime.BoxedUnit +import scala.runtime.ScalaRunTime import scala.runtime.ScalaRunTime.{array_apply, array_update} /** Utility methods for operating on arrays. @@ -182,13 +183,23 @@ object Array { // Subject to a compiler optimization in Cleanup. // Array(e0, ..., en) is translated to { val a = new Array(3); a(i) = ei; a } def apply[T: ClassTag](xs: T*): Array[T] = { - val array = new Array[T](xs.length) - val iterator = xs.iterator - var i = 0 - while (iterator.hasNext) { - array(i) = iterator.next(); i += 1 + val len = xs.length + xs match { + case wa: immutable.ArraySeq[_] if wa.unsafeArray.getClass.getComponentType == classTag[T].runtimeClass => + // We get here in test/files/run/sd760a.scala, `Array[T](t)` for + // a specialized type parameter `T`. While we still pay for two + // copies of the array it is better than before when we also boxed + // each element when populating the result. + ScalaRunTime.array_clone(wa.unsafeArray).asInstanceOf[Array[T]] + case _ => + val array = new Array[T](len) + val iterator = xs.iterator + var i = 0 + while (iterator.hasNext) { + array(i) = iterator.next(); i += 1 + } + array } - array } /** Creates an array of `Boolean` objects */ diff --git a/src/library/scala/Enumeration.scala b/src/library/scala/Enumeration.scala index b99a2311810b..7b6d77827e72 100644 --- a/src/library/scala/Enumeration.scala +++ b/src/library/scala/Enumeration.scala @@ -58,22 +58,22 @@ import scala.util.matching.Regex * @example {{{ * // Example of adding attributes to an enumeration by extending the Enumeration.Val class * object Planet extends Enumeration { - * protected case class Val(mass: Double, radius: Double) extends super.Val { + * protected case class PlanetVal(mass: Double, radius: Double) extends super.Val { * def surfaceGravity: Double = Planet.G * mass / (radius * radius) * def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity * } * import scala.language.implicitConversions - * implicit def valueToPlanetVal(x: Value): Val = x.asInstanceOf[Val] + * implicit def valueToPlanetVal(x: Value): PlanetVal = x.asInstanceOf[PlanetVal] * * val G: Double = 6.67300E-11 - * val Mercury = Val(3.303e+23, 2.4397e6) - * val Venus = Val(4.869e+24, 6.0518e6) - * val Earth = Val(5.976e+24, 6.37814e6) - * val Mars = Val(6.421e+23, 3.3972e6) - * val Jupiter = Val(1.9e+27, 7.1492e7) - * val Saturn = Val(5.688e+26, 6.0268e7) - * val Uranus = Val(8.686e+25, 2.5559e7) - * val Neptune = Val(1.024e+26, 2.4746e7) + * val Mercury = PlanetVal(3.303e+23, 2.4397e6) + * val Venus = PlanetVal(4.869e+24, 6.0518e6) + * val Earth = PlanetVal(5.976e+24, 6.37814e6) + * val Mars = PlanetVal(6.421e+23, 3.3972e6) + * val Jupiter = PlanetVal(1.9e+27, 7.1492e7) + * val Saturn = PlanetVal(5.688e+26, 6.0268e7) + * val Uranus = PlanetVal(8.686e+25, 2.5559e7) + * val Neptune = PlanetVal(1.024e+26, 2.4746e7) * } * * println(Planet.values.filter(_.radius > 7.0e6)) diff --git a/src/library/scala/collection/immutable/RedBlackTree.scala b/src/library/scala/collection/immutable/RedBlackTree.scala index bf4cc7e3a0ed..2e7aa7b472ad 100644 --- a/src/library/scala/collection/immutable/RedBlackTree.scala +++ b/src/library/scala/collection/immutable/RedBlackTree.scala @@ -204,19 +204,25 @@ private[collection] object RedBlackTree { def tail[A, B](tree: Tree[A, B]): Tree[A, B] = { def _tail(tree: Tree[A, B]): Tree[A, B] = - if(tree eq null) throw new NoSuchElementException("empty tree") - else if(tree.left eq null) tree.right - else if(isBlackTree(tree.left)) balLeft(tree.key, tree.value, _tail(tree.left), tree.right) - else RedTree(tree.key, tree.value, _tail(tree.left), tree.right) + if (tree eq null) throw new NoSuchElementException("empty tree") + else { + val tl = tree.left + if (tl eq null) tree.right + else if (tl.isBlack) balLeft(tree, _tail(tl), tree.right) + else tree.redWithLeft(_tail(tree.left)) + } blacken(_tail(tree)) } def init[A, B](tree: Tree[A, B]): Tree[A, B] = { def _init(tree: Tree[A, B]): Tree[A, B] = - if(tree eq null) throw new NoSuchElementException("empty tree") - else if(tree.right eq null) tree.left - else if(isBlackTree(tree.right)) balRight(tree.key, tree.value, tree.left, _init(tree.right)) - else RedTree(tree.key, tree.value, tree.left, _init(tree.right)) + if (tree eq null) throw new NoSuchElementException("empty tree") + else { + val tr = tree.right + if (tr eq null) tree.left + else if (tr.isBlack) balRight(tree, tree.left, _init(tr)) + else tree.redWithRight(_init(tr)) + } blacken(_init(tree)) } @@ -299,7 +305,7 @@ private[collection] object RedBlackTree { else tree } - def isBlack(tree: Tree[_, _]) = (tree eq null) || isBlackTree(tree) + def isBlack(tree: Tree[_, _]) = (tree eq null) || tree.isBlack @`inline` private[this] def isRedTree(tree: Tree[_, _]) = (tree ne null) && tree.isRed @`inline` private[this] def isBlackTree(tree: Tree[_, _]) = (tree ne null) && tree.isBlack @@ -311,8 +317,10 @@ private[collection] object RedBlackTree { private[this] def maybeBlacken[A, B](t: Tree[A, B]): Tree[A, B] = if(isBlack(t)) t else if(isRedTree(t.left) || isRedTree(t.right)) t.black else t - private[this] def mkTree[A, B](isBlack: Boolean, k: A, v: B, l: Tree[A, B], r: Tree[A, B]) = - if (isBlack) BlackTree(k, v, l, r) else RedTree(k, v, l, r) + private[this] def mkTree[A, B](isBlack: Boolean, key: A, value: B, left: Tree[A, B], right: Tree[A, B]) = { + val sizeAndColour = sizeOf(left) + sizeOf(right) + 1 | (if(isBlack) initialBlackCount else initialRedCount) + new Tree(key, value.asInstanceOf[AnyRef], left, right, sizeAndColour) + } /** Create a new balanced tree where `newLeft` replaces `tree.left`. */ private[this] def balanceLeft[A, B1](tree: Tree[A, B1], newLeft: Tree[A, B1]): Tree[A, B1] = { @@ -587,7 +595,7 @@ private[collection] object RedBlackTree { } else new Tree(_key, _value, _left, _right, initialBlackCount) } -// private[NewRedBlackTree] def mutableRed: Tree[A, B] = { +// private[RedBlackTree] def mutableRed: Tree[A, B] = { // if (isRed) this // else if (mutable) { // _count = initialRedCount @@ -692,6 +700,15 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialBlackCount | size) } } + private[RedBlackTree] def redWithLeft[B1 >: B](newLeft: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if ((newLeft eq _left) && isRed) this + else { + val size = sizeOf(newLeft) + sizeOf(_right) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, _right, initialRedCount | size) + } + } private[RedBlackTree] def blackWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newRight) @@ -701,6 +718,15 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialBlackCount | size) } } + private[RedBlackTree] def redWithRight[B1 >: B](newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + if ((newRight eq _right) && isRed) this + else { + val size = sizeOf(_left) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], _left, newRight, initialRedCount | size) + } + } private[RedBlackTree] def withLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { //assertNotMutable(this) //assertNotMutable(newLeft) @@ -711,6 +737,26 @@ private[collection] object RedBlackTree { new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, (_count & colourBit) | size) } } + private[RedBlackTree] def redWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + //assertNotMutable(newRight) + if ((newLeft eq _left) && (newRight eq _right) && isRed) this + else { + val size = sizeOf(newLeft) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, initialRedCount | size) + } + } + private[RedBlackTree] def blackWithLeftRight[B1 >: B](newLeft: Tree[A, B1], newRight: Tree[A, B1]): Tree[A, B1] = { + //assertNotMutable(this) + //assertNotMutable(newLeft) + //assertNotMutable(newRight) + if ((newLeft eq _left) && (newRight eq _right) && isBlack) this + else { + val size = sizeOf(newLeft) + sizeOf(newRight) + 1 + new Tree(key, value.asInstanceOf[AnyRef], newLeft, newRight, initialBlackCount | size) + } + } } //see #Tree docs "Colour, mutablity and size encoding" //we make these final vals because the optimiser inlines them, without reference to the enclosing module @@ -924,7 +970,7 @@ private[collection] object RedBlackTree { if((v2.asInstanceOf[AnyRef] eq v.asInstanceOf[AnyRef]) && (l2 eq l) && (r2 eq r)) t.asInstanceOf[Tree[A, C]] - else mkTree(isBlackTree(t), k, v2, l2, r2) + else mkTree(t.isBlack, k, v2, l2, r2) } def filterEntries[A, B](t: Tree[A, B], f: (A, B) => Boolean): Tree[A, B] = if(t eq null) null else { @@ -943,156 +989,119 @@ private[collection] object RedBlackTree { blacken(fk(t)) } - def filterKeys[A, B](t: Tree[A, B], f: A => Boolean): Tree[A, B] = if(t eq null) null else { - def fk(t: Tree[A, B]): Tree[A, B] = { - val k = t.key - val l = t.left - val r = t.right - val l2 = if(l eq null) null else fk(l) - val keep = f(k) - val r2 = if(r eq null) null else fk(r) - if(!keep) join2(l2, r2) - else if((l2 eq l) && (r2 eq r)) t - else join(l2, k, t.value, r2) - } - blacken(fk(t)) - } + private[this] val null2 = (null, null) def partitionEntries[A, B](t: Tree[A, B], p: (A, B) => Boolean): (Tree[A, B], Tree[A, B]) = if(t eq null) (null, null) else { - var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk - def fk(t: Tree[A, B]): Unit = { - val k = t.key - val v = t.value - val l = t.left - val r = t.right - var l2k, l2d, r2k, r2d = null: Tree[A, B] - if(l ne null) { - fk(l) - l2k = tmpk - l2d = tmpd - } - val keep = p(k, v) - if(r ne null) { - fk(r) - r2k = tmpk - r2d = tmpd + if (t eq null) null2 + else { + object partitioner { + var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk + def fk(t: Tree[A, B]): Unit = { + val k = t.key + val v = t.value + val l = t.left + val r = t.right + var l2k, l2d, r2k, r2d = null: Tree[A, B] + if (l ne null) { + fk(l) + l2k = tmpk + l2d = tmpd + } + val keep = p(k, v) + if (r ne null) { + fk(r) + r2k = tmpk + r2d = tmpd + } + val jk = + if (!keep) join2(l2k, r2k) + else if ((l2k eq l) && (r2k eq r)) t + else join(l2k, k, v, r2k) + val jd = + if (keep) join2(l2d, r2d) + else if ((l2d eq l) && (r2d eq r)) t + else join(l2d, k, v, r2d) + tmpk = jk + tmpd = jd + } } - val jk = - if(!keep) join2(l2k, r2k) - else if((l2k eq l) && (r2k eq r)) t - else join(l2k, k, v, r2k) - val jd = - if(keep) join2(l2d, r2d) - else if((l2d eq l) && (r2d eq r)) t - else join(l2d, k, v, r2d) - tmpk = jk - tmpd = jd - } - fk(t) - (blacken(tmpk), blacken(tmpd)) - } - def partitionKeys[A, B](t: Tree[A, B], p: A => Boolean): (Tree[A, B], Tree[A, B]) = if(t eq null) (null, null) else { - var tmpk, tmpd = null: Tree[A, B] // shared vars to avoid returning tuples from fk - def fk(t: Tree[A, B]): Unit = { - val k = t.key - val v = t.value - val l = t.left - val r = t.right - var l2k, l2d, r2k, r2d = null: Tree[A, B] - if(l ne null) { - fk(l) - l2k = tmpk - l2d = tmpd - } - val keep = p(k) - if(r ne null) { - fk(r) - r2k = tmpk - r2d = tmpd - } - val jk = - if(!keep) join2(l2k, r2k) - else if((l2k eq l) && (r2k eq r)) t - else join(l2k, k, v, r2k) - val jd = - if(keep) join2(l2d, r2d) - else if((l2d eq l) && (r2d eq r)) t - else join(l2d, k, v, r2d) - tmpk = jk - tmpd = jd + partitioner.fk(t) + (blacken(partitioner.tmpk), blacken(partitioner.tmpd)) } - fk(t) - (blacken(tmpk), blacken(tmpd)) } - // Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees // Constructing Red-Black Trees, Ralf Hinze: [[https://www.cs.ox.ac.uk/ralf.hinze/publications/WAAAPL99b.ps.gz]] // Red-Black Trees in a Functional Setting, Chris Okasaki: [[https://wiki.rice.edu/confluence/download/attachments/2761212/Okasaki-Red-Black.pdf]] */ private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { - def delLeft = - if (isBlackTree(tree.left)) balLeft(tree.key, tree.value, del(tree.left, k), tree.right) - else RedTree(tree.key, tree.value, del(tree.left, k), tree.right) - def delRight = - if (isBlackTree(tree.right)) balRight(tree.key, tree.value, tree.left, del(tree.right, k)) - else RedTree(tree.key, tree.value, tree.left, del(tree.right, k)) val cmp = ordering.compare(k, tree.key) - if (cmp < 0) delLeft - else if (cmp > 0) delRight - else append(tree.left, tree.right) + if (cmp < 0) { + val newLeft = del(tree.left, k) + if (newLeft eq tree.left) tree + else if (isBlackTree(tree.left)) balLeft(tree, newLeft, tree.right) + else tree.redWithLeft(newLeft) + } else if (cmp > 0) { + val newRight = del(tree.right, k) + if (newRight eq tree.right) tree + else if (isBlackTree(tree.right)) balRight(tree, tree.left, newRight) + else tree.redWithRight(newRight) + } else append(tree.left, tree.right) } - private[this] def balance[A, B](x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = + private[this] def balance[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = if (isRedTree(tl)) { - if (isRedTree(tr)) RedTree(x, xv, tl.black, tr.black) - else if (isRedTree(tl.left)) RedTree(tl.key, tl.value, tl.left.black, BlackTree(x, xv, tl.right, tr)) - else if (isRedTree(tl.right)) - RedTree(tl.right.key, tl.right.value, BlackTree(tl.key, tl.value, tl.left, tl.right.left), BlackTree(x, xv, tl.right.right, tr)) - else BlackTree(x, xv, tl, tr) + if (isRedTree(tr)) tree.redWithLeftRight(tl.black, tr.black) + else if (isRedTree(tl.left)) tl.withLeftRight(tl.left.black, tree.blackWithLeftRight(tl.right, tr)) + else if (isRedTree(tl.right)) tl.right.withLeftRight(tl.blackWithRight(tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) + else tree.blackWithLeftRight(tl, tr) } else if (isRedTree(tr)) { - if (isRedTree(tr.right)) RedTree(tr.key, tr.value, BlackTree(x, xv, tl, tr.left), tr.right.black) - else if (isRedTree(tr.left)) - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), BlackTree(tr.key, tr.value, tr.left.right, tr.right)) - else BlackTree(x, xv, tl, tr) - } else BlackTree(x, xv, tl, tr) - - private[this] def balLeft[A, B](x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = - if (isRedTree(tl)) RedTree(x, xv, tl.black, tr) - else if (isBlackTree(tr)) balance(x, xv, tl, tr.red) + if (isRedTree(tr.right)) tr.withLeftRight(tree.blackWithLeftRight(tl, tr.left), tr.right.black) + else if (isRedTree(tr.left)) tr.left.withLeftRight(tree.blackWithLeftRight(tl, tr.left.left), tr.blackWithLeftRight(tr.left.right, tr.right)) + else tree.blackWithLeftRight(tl, tr) + } else tree.blackWithLeftRight(tl, tr) + + private[this] def balLeft[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if (isRedTree(tl)) tree.redWithLeftRight(tl.black, tr) + else if (isBlackTree(tr)) balance(tree, tl, tr.red) else if (isRedTree(tr) && isBlackTree(tr.left)) - RedTree(tr.left.key, tr.left.value, BlackTree(x, xv, tl, tr.left.left), balance(tr.key, tr.value, tr.left.right, tr.right.red)) + tr.left.redWithLeftRight(tree.blackWithLeftRight(tl, tr.left.left), balance(tr, tr.left.right, tr.right.red)) else sys.error("Defect: invariance violation") - private[this] def balRight[A, B](x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = - if (isRedTree(tr)) RedTree(x, xv, tl, tr.black) - else if (isBlackTree(tl)) balance(x, xv, tl.red, tr) + private[this] def balRight[A, B](tree: Tree[A,B], tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + if (isRedTree(tr)) tree.redWithLeftRight(tl, tr.black) + else if (isBlackTree(tl)) balance(tree, tl.red, tr) else if (isRedTree(tl) && isBlackTree(tl.right)) - RedTree(tl.right.key, tl.right.value, balance(tl.key, tl.value, tl.left.red, tl.right.left), BlackTree(x, xv, tl.right.right, tr)) + tl.right.redWithLeftRight(balance(tl, tl.left.red, tl.right.left), tree.blackWithLeftRight(tl.right.right, tr)) else sys.error("Defect: invariance violation") /** `append` is similar to `join2` but requires that both subtrees have the same black height */ - private[this] def append[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = + private[this] def append[A, B](tl: Tree[A, B], tr: Tree[A, B]): Tree[A, B] = { if (tl eq null) tr else if (tr eq null) tl - else if (isRedTree(tl) && isRedTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, RedTree(tl.key, tl.value, tl.left, bc.left), RedTree(tr.key, tr.value, bc.right, tr.right)) - } else { - RedTree(tl.key, tl.value, tl.left, RedTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isBlackTree(tl) && isBlackTree(tr)) { - val bc = append(tl.right, tr.left) - if (isRedTree(bc)) { - RedTree(bc.key, bc.value, BlackTree(tl.key, tl.value, tl.left, bc.left), BlackTree(tr.key, tr.value, bc.right, tr.right)) - } else { - balLeft(tl.key, tl.value, tl.left, BlackTree(tr.key, tr.value, bc, tr.right)) - } - } else if (isRedTree(tr)) RedTree(tr.key, tr.value, append(tl, tr.left), tr.right) - else if (isRedTree(tl)) RedTree(tl.key, tl.value, tl.left, append(tl.right, tr)) - else sys.error("unmatched tree on append: " + tl + ", " + tr) + else if (tl.isRed) { + if (tr.isRed) { + //tl is red, tr is red + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) + else tl.withRight(tr.withLeft(bc)) + } else { + //tl is red, tr is black + tl.withRight(append(tl.right, tr)) + } + } else { + if (tr.isBlack) { + //tl is black tr is black + val bc = append(tl.right, tr.left) + if (isRedTree(bc)) bc.withLeftRight(tl.withRight(bc.left), tr.withLeft(bc.right)) + else balLeft(tl, tl.left, tr.withLeft(bc)) + } else { + //tl is black tr is red + tr.withLeft(append(tl, tr.left)) + } + } + } // Bulk operations based on "Just Join for Parallel Ordered Sets" (https://www.cs.cmu.edu/~guyb/papers/BFS16.pdf) @@ -1110,7 +1119,7 @@ private[collection] object RedBlackTree { /** Compute the rank from a tree and its black height */ @`inline` private[this] def rank(t: Tree[_, _], bh: Int): Int = { if(t eq null) 0 - else if(isBlackTree(t)) 2*(bh-1) + else if(t.isBlack) 2*(bh-1) else 2*bh-1 } @@ -1146,7 +1155,7 @@ private[collection] object RedBlackTree { private[this] def join[A, B](tl: Tree[A, B], k: A, v: B, tr: Tree[A, B]): Tree[A, B] = { @tailrec def h(t: Tree[_, _], i: Int): Int = - if(t eq null) i+1 else h(t.left, if(isBlackTree(t)) i+1 else i) + if(t eq null) i+1 else h(t.left, if(t.isBlack) i+1 else i) val bhtl = h(tl, 0) val bhtr = h(tr, 0) if(bhtl > bhtr) { diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala index f68d85421791..51e55782b19f 100644 --- a/src/library/scala/collection/immutable/TreeSet.scala +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -213,10 +213,10 @@ final class TreeSet[A] private[immutable] (private[immutable] val tree: RB.Tree[ super.diff(that) } - override def filter(f: A => Boolean): TreeSet[A] = newSetOrSelf(RB.filterKeys(tree, f)) + override def filter(f: A => Boolean): TreeSet[A] = newSetOrSelf(RB.filterEntries[A, Any](tree, {(k, _) => f(k)})) override def partition(p: A => Boolean): (TreeSet[A], TreeSet[A]) = { - val (l, r) = RB.partitionKeys(tree, p) + val (l, r) = RB.partitionEntries(tree, {(a:A, _: Any) => p(a)}) (newSetOrSelf(l), newSetOrSelf(r)) } diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index d94fae987168..dfdb2a535c7b 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -64,7 +64,6 @@ object ScalaRunTime { 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 } } @@ -81,7 +80,6 @@ object ScalaRunTime { 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 } } @@ -132,7 +130,6 @@ object ScalaRunTime { case x: Array[Byte] => copy(x) case x: Array[Short] => copy(x) case x: Array[Boolean] => copy(x) - case x: Array[Unit] => copy(x) case null => throw new NullPointerException } } diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 4683e8dedfda..5eea4620f9d4 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -282,12 +282,22 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => // !!! when annotation arguments are not literals, but any sort of // expression, there is a fair chance they will turn up here not as // Literal(const) but some arbitrary AST. - def constantAtIndex(index: Int): Option[Constant] = - if (args.nonEmpty) argAtIndex(args, index) collect { - case Literal(x) => x - } else if (assocs.nonEmpty) argAtIndex(assocs, index) collect { + // + // We recurse over Typed / Annotated trees to allow things like: + // `@implicitNotFound("$foo": @nowarn)` + def constantAtIndex(index: Int): Option[Constant] = { + @tailrec + def lit(tree: Tree): Option[Constant] = tree match { + case Literal(c) => Some(c) + case Typed(t, _) => lit(t) + case Annotated(_, t) => lit(t) + case _ => None + } + if (args.nonEmpty) argAtIndex(args, index).flatMap(lit) + else if (assocs.nonEmpty) argAtIndex(assocs, index) collect { case (_, LiteralAnnotArg(const)) => const } else None + } def argAtIndex[T](l: List[T], index: Int): Option[T] = if (index < l.size) Some(l(index)) else None diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 94f0b464e67f..56e7445a0cad 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -1783,6 +1783,7 @@ trait Definitions extends api.StandardDefinitions { lazy val ensureAccessibleMethod = getMemberMethod(ScalaRunTimeModule, nme.ensureAccessible) lazy val arrayClassMethod = getMemberMethod(ScalaRunTimeModule, nme.arrayClass) lazy val wrapVarargsRefArrayMethod = getMemberMethod(ScalaRunTimeModule, nme.wrapRefArray) + lazy val genericWrapVarargsRefArrayMethod = getMemberMethod(ScalaRunTimeModule, nme.genericWrapArray) lazy val RuntimeStatics_ioobe = getMemberMethod(RuntimeStaticsModule, nme.ioobe) diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index d1138cc510fc..6ae62eb81581 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -754,9 +754,10 @@ abstract class TreeGen { propagateNoWarnAttachment(from, to).updateAttachment(PatVarDefAttachment) /** Create tree for pattern definition */ - def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match { + def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator): List[ValDef] = mkPatDef(mods, pat, rhs, rhs.pos)(fresh) + def mkPatDef(mods: Modifiers, pat: Tree, rhs: Tree, rhsPos: Position)(implicit fresh: FreshNameCreator): List[ValDef] = matchVarPattern(pat) match { case Some((name, tpt)) => - List(atPos(pat.pos union rhs.pos) { + List(atPos(pat.pos union rhsPos) { propagateNoWarnAttachment(pat, ValDef(mods, name.toTermName, tpt, rhs)) }) @@ -784,13 +785,13 @@ abstract class TreeGen { case Typed(expr, tpt) if !expr.isInstanceOf[Ident] => val rhsTypedUnchecked = if (tpt.isEmpty) rhsUnchecked - else Typed(rhsUnchecked, tpt) setPos (rhs.pos union tpt.pos) + else Typed(rhsUnchecked, tpt) setPos (rhsPos union tpt.pos) (expr, rhsTypedUnchecked) case ok => (ok, rhsUnchecked) } val vars = getVariables(pat1) - val matchExpr = atPos((pat1.pos union rhs.pos).makeTransparent) { + val matchExpr = atPos((pat1.pos union rhsPos).makeTransparent) { Match( rhs1, List( @@ -801,7 +802,7 @@ abstract class TreeGen { } vars match { case List((vname, tpt, pos, original)) => - List(atPos(pat.pos union pos union rhs.pos) { + List(atPos(pat.pos union pos union rhsPos) { propagatePatVarDefAttachments(original, ValDef(mods, vname.toTermName, tpt, matchExpr)) }) case _ => diff --git a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala index abb75b66baf9..82f376e5c379 100644 --- a/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/ScalaClassLoader.scala @@ -29,16 +29,12 @@ trait HasClassPath { def classPathURLs: Seq[URL] } -/** A wrapper around java.lang.ClassLoader to lower the annoyance - * of java reflection. - */ -trait ScalaClassLoader extends JClassLoader { +final class RichClassLoader(private val self: JClassLoader) extends AnyVal { /** Executing an action with this classloader as context classloader */ def asContext[T](action: => T): T = { - import ScalaClassLoader.setContext val saved = Thread.currentThread.getContextClassLoader - try { setContext(this) ; action } - finally setContext(saved) + try { ScalaClassLoader.setContext(self) ; action } + finally ScalaClassLoader.setContext(saved) } /** Load and link a class with this classloader */ @@ -48,7 +44,7 @@ trait ScalaClassLoader extends JClassLoader { private def tryClass[T <: AnyRef](path: String, initialize: Boolean): Option[Class[T]] = catching(classOf[ClassNotFoundException], classOf[SecurityException]) opt - Class.forName(path, initialize, this).asInstanceOf[Class[T]] + Class.forName(path, initialize, self).asInstanceOf[Class[T]] /** Create an instance of a class with this classloader */ def create(path: String): AnyRef = @@ -59,7 +55,7 @@ trait ScalaClassLoader extends JClassLoader { def fail(msg: String) = error(msg, new IllegalArgumentException(msg)) def error(msg: String, e: Throwable) = { errorFn(msg) ; throw e } try { - val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ this) + val clazz = Class.forName(path, /*initialize =*/ true, /*loader =*/ self) if (classTag[T].runtimeClass isAssignableFrom clazz) { val ctor = { val maybes = clazz.getConstructors filter (c => c.getParameterCount == args.size && @@ -70,7 +66,7 @@ trait ScalaClassLoader extends JClassLoader { (ctor.newInstance(args: _*)).asInstanceOf[T] } else { errorFn(s"""Loader for ${classTag[T]}: [${show(classTag[T].runtimeClass.getClassLoader)}] - |Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin) + |Loader for ${clazz.getName}: [${show(clazz.getClassLoader)}]""".stripMargin) fail(s"Not a ${classTag[T]}: ${path}") } } catch { @@ -88,7 +84,7 @@ trait ScalaClassLoader extends JClassLoader { } /** An InputStream representing the given class name, or null if not found. */ - def classAsStream(className: String) = getResourceAsStream { + def classAsStream(className: String) = self.getResourceAsStream { if (className endsWith ".class") className else s"${className.replace('.', '/')}.class" // classNameToPath } @@ -107,6 +103,41 @@ trait ScalaClassLoader extends JClassLoader { } } +object RichClassLoader { + implicit def wrapClassLoader(loader: ClassLoader): RichClassLoader = new RichClassLoader(loader) +} + +/** A wrapper around java.lang.ClassLoader to lower the annoyance + * of java reflection. + */ +trait ScalaClassLoader extends JClassLoader { + private def wrap = new RichClassLoader(this) + /** Executing an action with this classloader as context classloader */ + def asContext[T](action: => T): T = wrap.asContext(action) + + /** Load and link a class with this classloader */ + def tryToLoadClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToLoadClass[T](path) + /** Load, link and initialize a class with this classloader */ + def tryToInitializeClass[T <: AnyRef](path: String): Option[Class[T]] = wrap.tryToInitializeClass(path) + + /** Create an instance of a class with this classloader */ + def create(path: String): AnyRef = wrap.create(path) + + /** Create an instance with ctor args, or invoke errorFn before throwing. */ + def create[T <: AnyRef : ClassTag](path: String, errorFn: String => Unit)(args: AnyRef*): T = + wrap.create[T](path, errorFn)(args: _*) + + /** The actual bytes for a class file, or an empty array if it can't be found. */ + def classBytes(className: String): Array[Byte] = wrap.classBytes(className) + + /** An InputStream representing the given class name, or null if not found. */ + def classAsStream(className: String) = wrap.classAsStream(className) + + /** Run the main method of a class to be loaded by this classloader */ + def run(objectName: String, arguments: Seq[String]): Unit = wrap.run(objectName, arguments) +} + + /** Methods for obtaining various classloaders. * appLoader: the application classloader. (Also called the java system classloader.) * extLoader: the extension classloader. @@ -149,6 +180,10 @@ object ScalaClassLoader { new URLClassLoader(urls, if (parent == null) bootClassLoader else parent) } + def fromURLsParallelCapable(urls: Seq[URL], parent: ClassLoader = null): JURLClassLoader = { + new JURLClassLoader(urls.toArray, if (parent == null) bootClassLoader else parent) + } + /** True if supplied class exists in supplied path */ def classExists(urls: Seq[URL], name: String): Boolean = (fromURLs(urls) tryToLoadClass name).isDefined diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 48e1f0a2c3e5..3d7b7bcd8947 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -1153,8 +1153,10 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive } case japplied: ParameterizedType => // https://stackoverflow.com/questions/5767122/parameterizedtype-getrawtype-returns-j-l-r-type-not-class - val sym = classToScala(japplied.getRawType.asInstanceOf[jClass[_]]) - val pre = if (japplied.getOwnerType ne null) typeToScala(japplied.getOwnerType) else sym.owner.thisType + val jcls = japplied.getRawType.asInstanceOf[jClass[_]] + val sym = classToScala(jcls) + val isStatic = java.lang.reflect.Modifier.isStatic(jcls.getModifiers) + val pre = if (!isStatic && (japplied.getOwnerType ne null)) typeToScala(japplied.getOwnerType) else sym.owner.thisType val args0 = japplied.getActualTypeArguments val (args, bounds) = targsToScala(pre.typeSymbol, args0.toList) newExistentialType(bounds, typeRef(pre, sym, args)) diff --git a/test/async/neg/ill-nested-await.check b/test/async/neg/ill-nested-await.check index 73d6d86e5b1e..e04df598a775 100644 --- a/test/async/neg/ill-nested-await.check +++ b/test/async/neg/ill-nested-await.check @@ -34,7 +34,13 @@ ill-nested-await.scala:71: error: await must not be used under a lazy val initia ill-nested-await.scala:77: error: await must not be used under a nested method. async { fooAsByNameLambda(await(f(""))) } ^ +ill-nested-await.scala:82: error: await must not be used under a synchronized call. + async { lock.synchronized { await(f(1)) + await(f(2)) } } + ^ +ill-nested-await.scala:82: error: await must not be used under a synchronized call. + async { lock.synchronized { await(f(1)) + await(f(2)) } } + ^ ill-nested-await.scala:11: error: `await` must be enclosed in an `async` block await[Any](f(null)) ^ -13 errors +15 errors diff --git a/test/async/neg/ill-nested-await.scala b/test/async/neg/ill-nested-await.scala index e46090d0d0e2..dc2a0f93a4f8 100644 --- a/test/async/neg/ill-nested-await.scala +++ b/test/async/neg/ill-nested-await.scala @@ -76,4 +76,9 @@ class NakedAwait { def fooAsByNameLambda = foo("") _ // : (_ => String) = String async { fooAsByNameLambda(await(f(""))) } } + + def underSynchronized(): Unit = { + val lock = new Object + async { lock.synchronized { await(f(1)) + await(f(2)) } } + } } diff --git a/test/files/instrumented/t11882a.check b/test/files/instrumented/t11882a.check new file mode 100644 index 000000000000..cfb515adcc98 --- /dev/null +++ b/test/files/instrumented/t11882a.check @@ -0,0 +1,8 @@ +Method call statistics: + 1 OptimusSeq$.apply1(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lscala/reflect/ClassTag;)LOptimusSeq; + 1 OptimusSeq$.unsafeFromAnyArray1([Ljava/lang/Object;)LOptimusSeq; + 1 Test$.doIt$1()LOptimusSeq; + 1 scala/reflect/ClassTag$.AnyRef()Lscala/reflect/ClassTag; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)Ljava/lang/Object; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)[Ljava/lang/Object; + 4 scala/runtime/ScalaRunTime$.array_update(Ljava/lang/Object;ILjava/lang/Object;)V diff --git a/test/files/instrumented/t11882a.scala b/test/files/instrumented/t11882a.scala new file mode 100644 index 000000000000..2988791c320b --- /dev/null +++ b/test/files/instrumented/t11882a.scala @@ -0,0 +1,23 @@ +import scala.reflect.ClassTag +import scala.tools.partest.instrumented._ +import scala.tools.partest.instrumented.Instrumentation._ + +class OptimusSeq[T] + +object OptimusSeq { + private def unsafeFromAnyArray1[T <: AnyRef](ts: Array[T]): OptimusSeq[T] = null; + def apply1[T <: AnyRef : ClassTag](p1: T, p2: T, p3: T, p4: T): OptimusSeq[T] = { + unsafeFromAnyArray1(Array(p1, p2, p3, p4)) + } +} + +object Test { + def main(args: Array[String]): Unit = { + def doIt = OptimusSeq.apply1[AnyRef](null, null, null, null) + doIt + startProfiling() + doIt + stopProfiling() + printStatistics() + } +} diff --git a/test/files/instrumented/t11882b.check b/test/files/instrumented/t11882b.check new file mode 100644 index 000000000000..2b4a809e6e7e --- /dev/null +++ b/test/files/instrumented/t11882b.check @@ -0,0 +1,8 @@ +Method call statistics: + 1 OptimusSeq$.apply1(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Lscala/reflect/ClassTag;)LOptimusSeq; + 1 OptimusSeq$.unsafeFromAnyArray1(Ljava/lang/Object;)LOptimusSeq; + 1 Test$.doIt$1()LOptimusSeq; + 1 scala/reflect/ClassTag$.AnyRef()Lscala/reflect/ClassTag; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)Ljava/lang/Object; + 1 scala/reflect/ManifestFactory$ObjectManifest.newArray(I)[Ljava/lang/Object; + 4 scala/runtime/ScalaRunTime$.array_update(Ljava/lang/Object;ILjava/lang/Object;)V diff --git a/test/files/instrumented/t11882b.scala b/test/files/instrumented/t11882b.scala new file mode 100644 index 000000000000..2f1c33d72a2d --- /dev/null +++ b/test/files/instrumented/t11882b.scala @@ -0,0 +1,23 @@ +import scala.reflect.ClassTag +import scala.tools.partest.instrumented._ +import scala.tools.partest.instrumented.Instrumentation._ + +class OptimusSeq[T] + +object OptimusSeq { + private def unsafeFromAnyArray1[T](ts: Array[T]): OptimusSeq[T] = null; + def apply1[T : ClassTag](p1: T, p2: T, p3: T, p4: T): OptimusSeq[T] = { + unsafeFromAnyArray1(Array(p1, p2, p3, p4)) + } +} + +object Test { + def main(args: Array[String]): Unit = { + def doIt = OptimusSeq.apply1[AnyRef](null, null, null, null) + doIt + startProfiling() + doIt + stopProfiling() + printStatistics() + } +} diff --git a/test/files/instrumented/t11882c.check b/test/files/instrumented/t11882c.check new file mode 100644 index 000000000000..f80f495cf1d3 --- /dev/null +++ b/test/files/instrumented/t11882c.check @@ -0,0 +1,2 @@ +Method call statistics: + 1 Test$.doIt$1()[Ljava/lang/String; diff --git a/test/files/instrumented/t11882c.scala b/test/files/instrumented/t11882c.scala new file mode 100644 index 000000000000..f7b33ad864ed --- /dev/null +++ b/test/files/instrumented/t11882c.scala @@ -0,0 +1,14 @@ +import scala.reflect.ClassTag +import scala.tools.partest.instrumented._ +import scala.tools.partest.instrumented.Instrumentation._ + +object Test { + def main(args: Array[String]): Unit = { + def doIt = Array[String](null, null, null, null) + doIt + startProfiling() + doIt + stopProfiling() + printStatistics() + } +} diff --git a/test/files/neg/annotated-literal-annotation-arg.check b/test/files/neg/annotated-literal-annotation-arg.check new file mode 100644 index 000000000000..220ab9a992f3 --- /dev/null +++ b/test/files/neg/annotated-literal-annotation-arg.check @@ -0,0 +1,7 @@ +annotated-literal-annotation-arg.scala:14: error: $foo + implicitly[Foo] + ^ +annotated-literal-annotation-arg.scala:15: error: bar + implicitly[Bar] + ^ +2 errors diff --git a/test/files/neg/annotated-literal-annotation-arg.scala b/test/files/neg/annotated-literal-annotation-arg.scala new file mode 100644 index 000000000000..92a089f5948f --- /dev/null +++ b/test/files/neg/annotated-literal-annotation-arg.scala @@ -0,0 +1,16 @@ + +import annotation.implicitNotFound +import scala.annotation.nowarn + +// Ensure that an annotation doesn't break the message +@implicitNotFound("$foo": @nowarn) +trait Foo + +// Ensure that a type ascription doesn't break the message +@implicitNotFound("bar": String) +trait Bar + +object Example { + implicitly[Foo] + implicitly[Bar] +} diff --git a/test/files/run/array-cleanup-optimation-specialized.scala b/test/files/run/array-cleanup-optimation-specialized.scala new file mode 100644 index 000000000000..5db397752df3 --- /dev/null +++ b/test/files/run/array-cleanup-optimation-specialized.scala @@ -0,0 +1,12 @@ +import scala.reflect.ClassTag + +object Test { + def main(args: Array[String]): Unit = { + assert(apply[String]("") == classOf[Array[String]]) + assert(apply[Double](1d) == classOf[Array[Double]]) + } + + def apply[@specialized(Double) C: ClassTag](c: C): Class[_] = { + Array(c).getClass + } +} diff --git a/test/files/run/position-val-def.check b/test/files/run/position-val-def.check index a92c77c68cff..b0ce48239ba9 100644 --- a/test/files/run/position-val-def.check +++ b/test/files/run/position-val-def.check @@ -6,14 +6,14 @@ var x = 0 val x, y = 0 [NoPosition]{ - [0:5]val x = [11]0; + [4]val x = [11]0; [7:12]val y = [11:12]0; [NoPosition]() } var x, y = 0 [NoPosition]{ - [0:5]var x = [11]0; + [4]var x = [11]0; [7:12]var y = [11:12]0; [NoPosition]() } diff --git a/test/files/run/reify-each-node-type.check b/test/files/run/reify-each-node-type.check index afc65add7af2..5cff9e63731c 100644 --- a/test/files/run/reify-each-node-type.check +++ b/test/files/run/reify-each-node-type.check @@ -23,7 +23,7 @@ 23 new r.List[Int]() New 24 0: @unchecked Annotated 25 (null: r.Outer#Inner) SelectFromTypeTree -26 (null: Nil.type) SingletonTypeTree +26 (null: r.Nil.type) SingletonTypeTree 27 (null: T forSome { type T }) ExistentialTypeTree 28 { import r.{A, B=>C}; () } Import 29 { def f: Int = return 0; () } Return diff --git a/test/files/run/sd760a.scala b/test/files/run/sd760a.scala new file mode 100644 index 000000000000..5db397752df3 --- /dev/null +++ b/test/files/run/sd760a.scala @@ -0,0 +1,12 @@ +import scala.reflect.ClassTag + +object Test { + def main(args: Array[String]): Unit = { + assert(apply[String]("") == classOf[Array[String]]) + assert(apply[Double](1d) == classOf[Array[Double]]) + } + + def apply[@specialized(Double) C: ClassTag](c: C): Class[_] = { + Array(c).getClass + } +} diff --git a/test/files/run/sd760b.scala b/test/files/run/sd760b.scala new file mode 100644 index 000000000000..fae0e9cf8a6d --- /dev/null +++ b/test/files/run/sd760b.scala @@ -0,0 +1,11 @@ +import scala.reflect.ClassTag + +object Test { + def main(args: Array[String]): Unit = { + assert(apply[Double](1d) == classOf[Array[Double]]) + } + + def apply[D <: Double: ClassTag](d: D): Class[_] = { + Array.apply[D](d).getClass + } +} diff --git a/test/files/run/t11882-class-cast.scala b/test/files/run/t11882-class-cast.scala new file mode 100644 index 000000000000..59221be93fe6 --- /dev/null +++ b/test/files/run/t11882-class-cast.scala @@ -0,0 +1,8 @@ +object Test { + def test[T <: AnyRef: reflect.ClassTag](t: T) = Array(t) + def main(args: Array[String]): Unit = { + // was: java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; + val x: Array[String] = test[String]("x") + assert(x(0) == "x") + } +} diff --git a/test/files/run/t12038a.check b/test/files/run/t12038a.check new file mode 100644 index 000000000000..13d7a9d9b34d --- /dev/null +++ b/test/files/run/t12038a.check @@ -0,0 +1,3 @@ +[BuilderType <: p1.J_1.AbstractMessageLite.Builder[BuilderType]]Object { + def (): p1.J_1.AbstractMessageLite[BuilderType] +} diff --git a/test/files/run/t12038a/J_1.java b/test/files/run/t12038a/J_1.java new file mode 100644 index 000000000000..37f545e5a6fe --- /dev/null +++ b/test/files/run/t12038a/J_1.java @@ -0,0 +1,11 @@ +package p1; + +public class J_1 { + public abstract static class AbstractMessageLite< + BuilderType extends AbstractMessageLite.Builder> { + + public abstract static class Builder>{ + + } + } +} diff --git a/test/files/run/t12038a/Test.scala b/test/files/run/t12038a/Test.scala new file mode 100644 index 000000000000..5a62349415bc --- /dev/null +++ b/test/files/run/t12038a/Test.scala @@ -0,0 +1,7 @@ +object Test { + def main(args: Array[String]): Unit = { + import reflect.runtime.universe._ + val m = scala.reflect.runtime.currentMirror + println(m.staticClass("p1.J_1.AbstractMessageLite").info.toString) // was cyclic error + } +} diff --git a/test/files/run/t12038b.check b/test/files/run/t12038b.check new file mode 100644 index 000000000000..c44e84a62530 --- /dev/null +++ b/test/files/run/t12038b.check @@ -0,0 +1 @@ +(x$1: p1.J_1.O[Object]#$I[String]): Unit diff --git a/test/files/run/t12038b/J_1.java b/test/files/run/t12038b/J_1.java new file mode 100644 index 000000000000..32c240ba98bd --- /dev/null +++ b/test/files/run/t12038b/J_1.java @@ -0,0 +1,21 @@ +package p1; + +public class J_1 { + public static abstract class O { + // non static, runtime reflection should use the generic owner type + // in the type signature below. + // + // also doing for static inner classes runs into cyclic errors (see t12038a.scala) + // in the current implementation of runtime reflection. + // + // This is fine as Java rejects the type selections in `notValidJava` below with: + // + // "cannot select a static class from a parameterized type" + public abstract class I {} + + public abstract static class J {} + } + static void test(O.I oi) {} + + // static void notValidJava(O.J oj) {} +} diff --git a/test/files/run/t12038b/Test.scala b/test/files/run/t12038b/Test.scala new file mode 100644 index 000000000000..a2b2220ad4cb --- /dev/null +++ b/test/files/run/t12038b/Test.scala @@ -0,0 +1,7 @@ +object Test { + def main(args: Array[String]): Unit = { + import reflect.runtime.universe._ + val m = scala.reflect.runtime.currentMirror + println(m.staticClass("p1.J_1").companion.info.decl(TermName("test")).asMethod.info.toString) + } +} diff --git a/test/instrumented/srt.patch b/test/instrumented/srt.patch index ff7a2357b2b9..c819cff3320b 100644 --- a/test/instrumented/srt.patch +++ b/test/instrumented/srt.patch @@ -3,16 +3,16 @@ @@ -10,6 +10,8 @@ * additional information regarding copyright ownership. */ - + +/* INSTRUMENTED VERSION */ + package scala package runtime - + @@ -52,8 +54,11 @@ def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = classTag[T].runtimeClass.asInstanceOf[jClass[T]] - + + var arrayApplyCount = 0 + /** Retrieve generic array element */ @@ -21,10 +21,10 @@ (xs: @unchecked) match { case x: Array[AnyRef] => x(idx).asInstanceOf[Any] case x: Array[Int] => x(idx).asInstanceOf[Any] -@@ -69,8 +74,11 @@ +@@ -68,8 +73,11 @@ } } - + + var arrayUpdateCount = 0 + /** update generic array element */ diff --git a/test/junit/scala/collection/immutable/TreeMapTest.scala b/test/junit/scala/collection/immutable/TreeMapTest.scala index 95c3f6d8fc16..31bfebb1cc5c 100644 --- a/test/junit/scala/collection/immutable/TreeMapTest.scala +++ b/test/junit/scala/collection/immutable/TreeMapTest.scala @@ -254,4 +254,12 @@ class TreeMapTest extends AllocationTest { def withoutOrdering(m: Map[K, V]): Map[K, V] = collection.immutable.Map.apply(m.iterator.toSeq: _*) assertEquals(withoutOrdering(expected), withoutOrdering(map)) } + + @Test def removeNonContent(): Unit = { + val src: Map[Int, String] = TreeMap(Range(0, 100, 2).map((_, "")) :_*) + for (i <- Range(-1, 101, 2)) { + src - i + assertSame(i.toString, src, nonAllocating(src - i, text = i.toString)) + } + } } diff --git a/test/junit/scala/collection/immutable/TreeSetTest.scala b/test/junit/scala/collection/immutable/TreeSetTest.scala index 871c02c2ebf7..05e436791d82 100644 --- a/test/junit/scala/collection/immutable/TreeSetTest.scala +++ b/test/junit/scala/collection/immutable/TreeSetTest.scala @@ -332,4 +332,10 @@ class TreeSetTest extends AllocationTest { assertEquals(expected, src filter set) } } + @Test def removeNonContent(): Unit = { + val src = TreeSet(Range(0, 100, 2) :_*) + for (i <- Range(-1, 101, 2)) { + assertSame(src, nonAllocating(src - i)) + } + } } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala new file mode 100644 index 000000000000..7277f3a91797 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxAndInlineTest.scala @@ -0,0 +1,106 @@ +package scala.tools.nsc.backend.jvm.opt + +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.asm.Opcodes +import scala.tools.asm.Opcodes._ +import scala.tools.asm.util.CheckMethodAdapter +import scala.tools.nsc.backend.jvm.MethodNode1 +import scala.tools.testkit.ASMConverters._ +import scala.tools.testkit.BytecodeTesting +import scala.tools.testkit.BytecodeTesting._ + +/** + * Tests for boxing/unboxing optimizations. + */ +@RunWith(classOf[JUnit4]) +class BoxUnboxAndInlineTest extends BytecodeTesting { + override def compilerArgs = "-opt:l:inline -opt-inline-from:**/*" + import compiler._ + + // Was crashing in 2.13.x once https://github.com/scala/scala/pull/9433 was merged in. + // Failure was: scala.tools.asm.tree.analysis.AnalyzerException: Error at instruction 16: Cannot pop operand off an empty stack. + // Discussion: https://github.com/scala/scala/pull/9495#issuecomment-779600132 + @Test + def unboxAsmCrash(): Unit = { + val code = + """package p1; class C { + |def f(b: java.lang.Byte) = { + | var box = 0 + | + | @inline def writeBox: Unit = { + | box = 1 + | } + | + | writeBox + |} + |}""".stripMargin + val c = compileClass(code) + assertSameSummary(getMethod(c, "f"), List(RETURN)) + + } + + // This bytecode pattern results from `unboxAsmCrash` in 2.13.x and exposes a bug in + // https://github.com/scala/scala/pull/9392, which landed originally in 2.12.x. + // + // The bytecode shape after the inliner differs in 2.12.x to masks the bug, probably due to + // https://github.com/scala/scala/pull/7133, which is 2.13.x only. + // + // This test constructs the problematic bytecode shape directly. Before the patch, it + // has an extra POP instruction which would be reported as invalid bytecode by CheckMethodAdapter. + // "Error at instruction 5: Cannot pop operand off an empty stack. m()V" + // + @Test + def unboxAsmCrashDirect(): Unit = { + val code: List[Instruction] = List( + Label(1), + Op(ACONST_NULL), + Invoke(INVOKESTATIC, "scala/runtime/ObjectRef", "create", "(Ljava/lang/Object;)Lscala/runtime/ObjectRef;", false), + VarOp(ASTORE, 1), + Op(ACONST_NULL), + VarOp(ALOAD, 1), + Op(POP), + VarOp(ASTORE, 2), + VarOp(ALOAD, 1), + VarOp(ALOAD, 2), + Field(PUTFIELD, "scala/runtime/ObjectRef", "elem", "Ljava/lang/Object;"), + Op(ACONST_NULL), + VarOp(ASTORE, 2), + Op(RETURN) + ) + val method = genMethod(localVars = List( + LocalVariable("this", "Lcom/foo/Bar;", None, Label(1), Label(1), 1), + LocalVariable("x", "Lscala/runtime/ObjectRef;", None, Label(1), Label(1), 1), + LocalVariable("y", "Lscala/runtime/ObjectRef;", None, Label(1), Label(1), 1), + // introduced by the box/unbox transform, we create the slot ahead of time. CheckMethodAdapter + // relies on it to verify the bytecode. + LocalVariable("z", "Lscala/runtime/ObjectRef;", None, Label(1), Label(1), 1) + ))(code: _*) + + val r = new compiler.global.Run() + compiler.global.enteringPhase(r.jvmPhase) { + compiler.global.genBCode.postProcessor.initialize() + val changed = compiler.global.genBCode.postProcessor.localOpt.boxUnbox.boxUnboxElimination(method, "p1.Owner") + assert(changed) + method.visitMaxs(2, 4) + val labelInsnIndices = new java.util.HashMap[scala.tools.asm.Label, java.lang.Integer]() + method.instructions.resetLabels() + + val checker = new CheckMethodAdapter(Opcodes.V1_8, "m", "()V", new MethodNode1(), labelInsnIndices) + method.accept(checker) + + assertSameCode(convertMethod(method), List( + Op(ACONST_NULL), + VarOp(ASTORE, 3), + Op(ACONST_NULL), + VarOp(ASTORE, 2), + VarOp(ALOAD, 2), + VarOp(ASTORE, 3), + Op(ACONST_NULL), + VarOp(ASTORE, 2), + Op(RETURN))) + } + } +} diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala index f9e228a19181..5cfd694aa7b1 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BoxUnboxTest.scala @@ -145,6 +145,13 @@ class BoxUnboxTest extends BytecodeTesting { | bi + li | } | + | def t17(x: Int) = { // this one's pretty contrived but tests that primitives can be unboxed through a branch + | val wat: Any = if (x > 0) x else -x + | wat match { + | case i: Int => String valueOf i + | case _ => "?" + | } + | } |} """.stripMargin @@ -198,10 +205,11 @@ class BoxUnboxTest extends BytecodeTesting { assertDoesNotInvoke(getInstructions(c, "t16"), "boxToLong") assertDoesNotInvoke(getInstructions(c, "t16"), "unboxToInt") assertDoesNotInvoke(getInstructions(c, "t16"), "unboxToLong") + assertDoesNotInvoke(getMethod(c, "t17"), "boxToInteger") } @Test - def refEliminiation(): Unit = { + def refElimination(): Unit = { val code = """class C { | import runtime._ @@ -244,6 +252,12 @@ class BoxUnboxTest extends BytecodeTesting { | val res: IntRef = if (b) r1 else r2 | res.elem // boxes remain: can't rewrite this read, don't know which local | } + | + | // this case is contemplated by BoxUnbox despite my inability to provide a motivating example + | def t7(b: Boolean) = { + | val r1 = if (b) IntRef.zero() else IntRef.create(1) + | r1.elem + | } |} """.stripMargin val c = compileClass(code) @@ -255,6 +269,7 @@ class BoxUnboxTest extends BytecodeTesting { List("scala/runtime/IntRef.elem")) assertEquals(getInstructions(c, "t6") collect { case Field(op, owner, name, _) => s"$op $owner.$name" }, List(s"$PUTFIELD scala/runtime/IntRef.elem", s"$GETFIELD scala/runtime/IntRef.elem")) + assertNoInvoke(getMethod(c, "t7")) } @Test @@ -308,6 +323,21 @@ class BoxUnboxTest extends BytecodeTesting { | case (x, y) if x == y => 0 | case (x, y) => x + y | } + | + | def t10(a: Int, b: Int) = { // tuple is optimized away + | val (greater, lesser) = if (a > b) (a, b) else (b, a) + | greater - lesser + | } + | + | def t11(n: Int)(j: Int) = { // tuple is optimized away + | val (a, b, c, _) = n match { + | case 0 => (j, 0, 1, 1) + | case 1 => (0, j, 0, 1) + | case 2 => (1, 0, j, 0) + | case 3 => (1, 1, 0, j) + | } + | a + b + c + | } |} """.stripMargin val c = compileClass(code) @@ -327,6 +357,11 @@ class BoxUnboxTest extends BytecodeTesting { ILOAD, ILOAD, IADD, ILOAD, IADD, IRETURN)) assertNoInvoke(getMethod(c, "t8")) assertNoInvoke(getMethod(c, "t9")) + assertNoInvoke(getMethod(c, "t10")) + assertInvokedMethods(getMethod(c, "t11"), List( + "scala/runtime/BoxesRunTime.boxToInteger", // only once, for the MatchError + "scala/MatchError.", + )) } @Test @@ -352,4 +387,26 @@ class BoxUnboxTest extends BytecodeTesting { VarOp(ALOAD, 0), TypeOp(CHECKCAST, "java/lang/Integer"), Op(POP), Op(ICONST_0), Op(IRETURN))) } + + @Test + def unboxAsmCrash(): Unit = { + val code = + """ + |package p1 + | + |class AssertUtil { + | + | def waitForIt(terminated: => Boolean, progress: Int = 0, label: => String = "test"): Unit = { + | val limit = 5 + | var n = 1 + | var (dormancy, factor) = progress match { + | case 0 => (10000L, 5) + | case _ => (250L, 4) + | } + | () + | } + |}""".stripMargin + val m = getMethod(compileClass(code), "waitForIt") + assertSameCode(m, List(VarOp(ILOAD, 2), TableSwitch(TABLESWITCH, 0, 0, Label(4), List(Label(4))), Label(4), Op(RETURN))) + } } diff --git a/test/junit/scala/tools/nsc/parser/ParserTest.scala b/test/junit/scala/tools/nsc/parser/ParserTest.scala index f5522b562574..dd5f562fe67e 100644 --- a/test/junit/scala/tools/nsc/parser/ParserTest.scala +++ b/test/junit/scala/tools/nsc/parser/ParserTest.scala @@ -1,9 +1,9 @@ package scala.tools.nsc.parser -import org.junit.Assert._ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.junit.Assert._ import scala.tools.testkit.BytecodeTesting @@ -30,4 +30,19 @@ class ParserTest extends BytecodeTesting{ assertFalse(reporter.hasErrors) run.compileSources(newSourceFile(crlfCode) :: Nil) } + + @Test + def rangePosOfDefaultInitializer_t12213(): Unit = { + val code = + """object Other { var x: Int = _; var y: Int = 42 }""" + import compiler._, global._ + val run = new Run + run.compileSources(newSourceFile(code) :: Nil) + assertFalse(reporter.hasErrors) + val unit = run.units.toList.head + def codeOf(pos: Position) = new String(pos.source.content.slice(pos.start, pos.end)) + val List(x, y) = unit.body.collect { case vd : ValDef => vd }.takeRight(2) + assertEquals("var y: Int = 42", codeOf(y.pos)) + assertEquals("var x: Int = _", codeOf(x.pos)) + } } diff --git a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala index a499e2aa1a1c..0c0a6b96f286 100644 --- a/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala +++ b/test/junit/scala/tools/nsc/typechecker/TypedTreeTest.scala @@ -1,6 +1,6 @@ package scala.tools.nsc.typechecker -import org.junit.Assert.assertEquals +import org.junit.Assert.{assertEquals, assertNotEquals} import org.junit.Test import scala.tools.testkit.BytecodeTesting @@ -24,4 +24,42 @@ class TypedTreeTest extends BytecodeTesting { val List(t) = tree.filter(_.attachments.all.nonEmpty).toList assertEquals("42:Set(OriginalTreeAttachment(O.x))", s"$t:${t.attachments.all}") } + + + // Ensure SingletonTypeTree#ref is typed and it has symbol after typing. + // see: https://github.com/scala/bug/issues/12296 + @Test + def singletonTypeTreeRefTyped(): Unit = { + val code = + """|object root { + | object impl + | val f: impl.type => Unit = { + | case _: impl.type => () + | } + |} + """.stripMargin + val run = compiler.newRun() + run.compileSources(List(BytecodeTesting.makeSourceFile(code, "UnitTestSingletonTypeTreeSource.scala"))) + val tree = run.units.next().body + + import compiler.global._ + + val singletonTypeTrees = collection.mutable.Buffer[SingletonTypeTree]() + object traverser extends Traverser { + override def traverse(t: Tree): Unit = { + t match { + case tt: TypeTree if tt.original != null => traverse(tt.original) + case st: SingletonTypeTree => + singletonTypeTrees += st + case _ => super.traverse(t) + } + } + } + traverser.traverse(tree) + + singletonTypeTrees.foreach { t => + assertEquals(t.ref.symbol.fullName, "root.impl") + assertNotEquals(NoPosition, t.pos) + } + } } diff --git a/test/scalacheck/redblacktree.scala b/test/scalacheck/redblacktree.scala index ea5cab8c1dcd..3d4cfdd145a4 100644 --- a/test/scalacheck/redblacktree.scala +++ b/test/scalacheck/redblacktree.scala @@ -400,7 +400,7 @@ object TestPartitionLeft extends RedBlackTreeTest("RedBlackTree.partitionKeysLef override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, 0) override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = - partitionKeys[String, Int](tree, k => k.hashCode % 2 == 0)._1 + partitionEntries[String, Int](tree, (k, v) => k.hashCode % 2 == 0)._1 property("partition") = forAll(genInput) { case (tree, parm, newTree) => iterator(tree).filter(t => t._1.hashCode % 2 == 0).toList == iterator(newTree).toList @@ -413,7 +413,7 @@ object TestPartitionRight extends RedBlackTreeTest("RedBlackTree.partitionKeysRi override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, 0) override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = - partitionKeys[String, Int](tree, k => k.hashCode % 2 == 0)._2 + partitionEntries[String, Int](tree, (k, v) => k.hashCode % 2 == 0)._2 property("partition") = forAll(genInput) { case (tree, parm, newTree) => iterator(tree).filter(t => t._1.hashCode % 2 != 0).toList == iterator(newTree).toList diff --git a/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala b/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala index 6d4526bfcb47..01cdea398f55 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/DefinitionConstructionProps.scala @@ -12,8 +12,10 @@ object DefinitionConstructionProps with PatDefConstruction with DefConstruction with PackageConstruction - with ImportConstruction { + with ImportConstruction + with QuasiquoteSliceTypeTests +trait QuasiquoteSliceTypeTests { self: QuasiquoteProperties => val x: Tree = q"val x: Int" property("scala/bug#6842 a1") = test { assertEqAst(q"def f($x) = 0", "def f(x: Int) = 0") } property("scala/bug#6842 a2") = test { assertEqAst(q"class C($x)", "class C(val x: Int)") } @@ -229,7 +231,7 @@ trait ValDefConstruction { self: QuasiquoteProperties => q"var $name: $tpt = $rhs" ≈ ValDef(Modifiers(MUTABLE), name, tpt, rhs) } - // left tree is not a pattern due to Si-8211 + // left tree is not a pattern due to scala/bug#8211 property("scala/bug#8202") = test { assertEqAst(q"val (x: Int) = 1", "val x: Int = 1") } diff --git a/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala b/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala index f0d900363be7..6dd0cd5c0644 100644 --- a/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala +++ b/test/scalacheck/scala/reflect/quasiquotes/QuasiquoteProperties.scala @@ -74,7 +74,13 @@ trait Helpers { assert(false, "exception wasn't thrown") } - def assertEqAst(tree: Tree, code: String) = assert(eqAst(tree, code)) + def assertEqAst(tree: Tree, code: String) = + assert(eqAst(tree, code), + s"""quasiquote tree != parse(code) tree + |quasiquote: $tree + |parse tree: ${parse(code)} + |code (str): $code""".stripMargin) + def eqAst(tree: Tree, code: String) = tree ≈ parse(code) val toolbox = currentMirror.mkToolBox()