From 0dea3d5a05d838c9ad710cbcb14fbc6d83035851 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Jun 2012 19:44:42 +0200 Subject: SI-4270 Rename implicits to avoid name clashes. This will fail implicit search in the next commit. --- src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index a2f0e053a8..960f85f612 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -90,7 +90,7 @@ trait ContextErrors { import infer.setError object TyperErrorGen { - implicit val context0: Context = infer.getContext + implicit val contextTyperErrorGen: Context = infer.getContext def UnstableTreeError(tree: Tree) = { def addendum = { @@ -637,7 +637,7 @@ trait ContextErrors { object InferErrorGen { - implicit val context0 = getContext + implicit val contextInferErrorGen = getContext object PolyAlternativeErrorKind extends Enumeration { type ErrorType = Value @@ -823,7 +823,7 @@ trait ContextErrors { object NamerErrorGen { - implicit val context0 = context + implicit val contextNamerErrorGen = context object SymValidateErrors extends Enumeration { val ImplicitConstr, ImplicitNotTermOrClass, ImplicitAtToplevel, @@ -858,7 +858,7 @@ trait ContextErrors { case CyclicReference(sym, info: TypeCompleter) => issueNormalTypeError(tree, typer.cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) case _ => - context0.issue(TypeErrorWithUnderlyingTree(tree, ex)) + contextNamerErrorGen.issue(TypeErrorWithUnderlyingTree(tree, ex)) } } -- cgit v1.2.3 From 9129cfe9117e41d44cda30222ffef22b70767cfb Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 4 Jun 2012 19:59:51 +0200 Subject: SI-4270 Disqualify in scope implicits that are shadowed. If an expression wouldn't type check explicitly, it shouldn't be allowed implicitly. Employs typedIdent, which already does this sort of thing rather well, instead of continuing to reimplement it in Implicits. Remove check for non-implicit synonym, which is subsumed by typing an Ident. Workaround Scaladoc oddity, by using an attributed select when the context is deficient. --- .../scala/tools/nsc/typechecker/Contexts.scala | 17 +++-- .../scala/tools/nsc/typechecker/Implicits.scala | 87 +++++++++++----------- test/files/neg/t4270.check | 4 + test/files/neg/t4270.scala | 6 ++ test/files/neg/t5376.check | 11 +++ test/files/neg/t5376.scala | 24 ++++++ 6 files changed, 100 insertions(+), 49 deletions(-) create mode 100644 test/files/neg/t4270.check create mode 100644 test/files/neg/t4270.scala create mode 100644 test/files/neg/t5376.check create mode 100644 test/files/neg/t5376.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index ac3c94c47a..bcf529ecd2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -100,6 +100,12 @@ trait Contexts { self: Analyzer => var outer: Context = _ // The next outer context var enclClass: Context = _ // The next outer context whose tree is a // template or package definition + @inline final def savingEnclClass[A](c: Context)(a: => A): A = { + val saved = enclClass + enclClass = c + try a finally enclClass = saved + } + var enclMethod: Context = _ // The next outer context whose tree is a method var variance: Int = _ // Variance relative to enclosing class private var _undetparams: List[Symbol] = List() // Undetermined type parameters, @@ -638,11 +644,12 @@ trait Contexts { self: Analyzer => if (owner != nextOuter.owner && owner.isClass && !owner.isPackageClass && !inSelfSuperCall) { if (!owner.isInitialized) return nextOuter.implicitss // debuglog("collect member implicits " + owner + ", implicit members = " + owner.thisType.implicitMembers)//DEBUG - val savedEnclClass = enclClass - this.enclClass = this - val res = collectImplicits(owner.thisType.implicitMembers, owner.thisType) - this.enclClass = savedEnclClass - res + savingEnclClass(this) { + // !!! In the body of `class C(implicit a: A) { }`, `implicitss` returns `List(List(a), List(a), List( tree1.symbol == tree.symbol && dominates(pt, tp)}) match { case Some(pending) => //println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG @@ -401,7 +402,7 @@ trait Implicits { try { context.openImplicits = (pt, tree) :: context.openImplicits // println(" "*context.openImplicits.length+"typed implicit "+info+" for "+pt) //@MDEBUG - typedImplicit0(info, ptChecked) + typedImplicit0(info, ptChecked, isLocal) } catch { case ex: DivergentImplicit => //println("DivergentImplicit for pt:"+ pt +", open implicits:"+context.openImplicits) //@MDEBUG @@ -534,7 +535,7 @@ trait Implicits { case _ => false } - private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean): SearchResult = { + private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = { incCounter(plausiblyCompatibleImplicits) printTyping ( ptBlock("typedImplicit0", @@ -549,17 +550,24 @@ trait Implicits { ) if (ptChecked || matchesPt(info)) - typedImplicit1(info) + typedImplicit1(info, isLocal) else SearchFailure } - private def typedImplicit1(info: ImplicitInfo): SearchResult = { + private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = { incCounter(matchingImplicits) val itree = atPos(pos.focus) { - if (info.pre == NoPrefix) Ident(info.name) - else { + // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints + val isScalaDoc = context.tree == EmptyTree + + if (isLocal && !isScalaDoc) { + // SI-4270 SI-5376 Always use an unattributed Ident for implicits in the local scope, + // rather than an attributed Select, to detect shadowing. + Ident(info.name) + } else { + assert(info.pre != NoPrefix, info) // SI-2405 Not info.name, which might be an aliased import val implicitMemberName = info.sym.name Select(gen.mkAttributedQualifier(info.pre), implicitMemberName) @@ -607,8 +615,8 @@ trait Implicits { if (context.hasErrors) fail("hasMatchingSymbol reported threw error(s)") - else if (!hasMatchingSymbol(itree1)) - fail("candidate implicit %s is shadowed by other implicit %s".format( + else if (isLocal && !hasMatchingSymbol(itree1)) + fail("candidate implicit %s is shadowed by %s".format( info.sym.fullLocationString, itree1.symbol.fullLocationString)) else { val tvars = undetParams map freshVar @@ -683,17 +691,6 @@ trait Implicits { } } - // #3453: in addition to the implicit symbols that may shadow the implicit with - // name `name`, this method tests whether there's a non-implicit symbol with name - // `name` in scope. Inspired by logic in typedIdent. - private def nonImplicitSynonymInScope(name: Name) = { - // the implicit ones are handled by the `shadowed` set above - context.scope.lookupEntry(name) match { - case x: ScopeEntry => reallyExists(x.sym) && !x.sym.isImplicit - case _ => false - } - } - /** Should implicit definition symbol `sym` be considered for applicability testing? * This is the case if one of the following holds: * - the symbol's type is initialized @@ -737,14 +734,15 @@ trait Implicits { /** Prune ImplicitInfos down to either all the eligible ones or the best one. * * @param iss list of list of infos - * @param shadowed set in which to record names that are shadowed by implicit infos - * If it is null, no shadowing. + * @param isLocal if true, `iss` represents in-scope implicits, which must respect the normal rules of + * shadowing. The head of the list `iss` must represent implicits from the closest + * enclosing scope, and so on. */ - class ImplicitComputation(iss: Infoss, shadowed: util.HashSet[Name]) { + class ImplicitComputation(iss: Infoss, isLocal: Boolean) { + private val shadowed = util.HashSet[Name](512) private var best: SearchResult = SearchFailure private def isShadowed(name: Name) = ( - (shadowed != null) - && (shadowed(name) || nonImplicitSynonymInScope(name)) + isLocal && shadowed(name) ) private def isIneligible(info: ImplicitInfo) = ( info.isCyclicOrErroneous @@ -788,9 +786,7 @@ trait Implicits { val eligible = { val matches = iss flatMap { is => val result = is filter (info => checkValid(info.sym) && survives(info)) - if (shadowed ne null) - shadowed addEntries (is map (_.name)) - + if (isLocal) shadowed addEntries (is map (_.name)) result } @@ -812,7 +808,7 @@ trait Implicits { case Nil => acc case i :: is => def tryImplicitInfo(i: ImplicitInfo) = - try typedImplicit(i, true) + try typedImplicit(i, ptChecked = true, isLocal) catch divergenceHandler tryImplicitInfo(i) match { @@ -842,7 +838,7 @@ trait Implicits { /** Returns all eligible ImplicitInfos and their SearchResults in a map. */ - def findAll() = mapFrom(eligible)(typedImplicit(_, false)) + def findAll() = mapFrom(eligible)(typedImplicit(_, ptChecked = false, isLocal)) /** Returns the SearchResult of the best match. */ @@ -891,7 +887,7 @@ trait Implicits { */ def applicableInfos(iss: Infoss, isLocal: Boolean): Map[ImplicitInfo, SearchResult] = { val start = startCounter(subtypeAppInfos) - val computation = new ImplicitComputation(iss, if (isLocal) util.HashSet[Name](512) else null) { } + val computation = new ImplicitComputation(iss, isLocal) { } val applicable = computation.findAll() stopCounter(subtypeAppInfos, start) @@ -909,7 +905,7 @@ trait Implicits { */ def searchImplicit(implicitInfoss: Infoss, isLocal: Boolean): SearchResult = if (implicitInfoss.forall(_.isEmpty)) SearchFailure - else new ImplicitComputation(implicitInfoss, if (isLocal) util.HashSet[Name](128) else null) findBest() + else new ImplicitComputation(implicitInfoss, isLocal) findBest() /** Produce an implicict info map, i.e. a map from the class symbols C of all parts of this type to * the implicit infos in the companion objects of these class symbols C. @@ -1397,19 +1393,22 @@ trait Implicits { def allImplicitsPoly(tvars: List[TypeVar]): List[(SearchResult, List[TypeConstraint])] = { def resetTVars() = tvars foreach { _.constr = new TypeConstraint } - def eligibleInfos(iss: Infoss, isLocal: Boolean) = new ImplicitComputation(iss, if (isLocal) util.HashSet[Name](512) else null).eligible - val allEligibleInfos = (eligibleInfos(context.implicitss, true) ++ eligibleInfos(implicitsOfExpectedType, false)).toList - - allEligibleInfos flatMap { ii => - // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) - // thus, start each type var off with a fresh for every typedImplicit - resetTVars() - // any previous errors should not affect us now - context.flushBuffer() - val res = typedImplicit(ii, false) - if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) - else Nil + def eligibleInfos(iss: Infoss, isLocal: Boolean) = { + val eligible = new ImplicitComputation(iss, isLocal).eligible + eligible.toList.flatMap { + (ii: ImplicitInfo) => + // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) + // thus, start each type var off with a fresh for every typedImplicit + resetTVars() + // any previous errors should not affect us now + context.flushBuffer() + + val res = typedImplicit(ii, ptChecked = false, isLocal) + if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) + else Nil + } } + eligibleInfos(context.implicitss, isLocal = true) ++ eligibleInfos(implicitsOfExpectedType, isLocal = false) } } diff --git a/test/files/neg/t4270.check b/test/files/neg/t4270.check new file mode 100644 index 0000000000..cfe0a93e00 --- /dev/null +++ b/test/files/neg/t4270.check @@ -0,0 +1,4 @@ +t4270.scala:5: error: could not find implicit value for parameter e: Int + implicitly[Int] + ^ +one error found diff --git a/test/files/neg/t4270.scala b/test/files/neg/t4270.scala new file mode 100644 index 0000000000..2c7c71d8c2 --- /dev/null +++ b/test/files/neg/t4270.scala @@ -0,0 +1,6 @@ +object Test1 { + object A { implicit val x: Int = 1 } + import A.x + def x: Int = 0 + implicitly[Int] +} diff --git a/test/files/neg/t5376.check b/test/files/neg/t5376.check new file mode 100644 index 0000000000..0376163c35 --- /dev/null +++ b/test/files/neg/t5376.check @@ -0,0 +1,11 @@ +t5376.scala:12: error: type mismatch; + found : String("a") + required: Int + "a": Int + ^ +t5376.scala:22: error: type mismatch; + found : String("a") + required: Int + "a": Int + ^ +two errors found diff --git a/test/files/neg/t5376.scala b/test/files/neg/t5376.scala new file mode 100644 index 0000000000..8da3868566 --- /dev/null +++ b/test/files/neg/t5376.scala @@ -0,0 +1,24 @@ +object Test { + object O1 { implicit def f(s: String): Int = 1 } + object O2 { implicit def f(s: String): Int = 2 } + object O3 { def f(s: String): Int = 3 } + + // Import two implicits with the same name in the same scope. + def m1 = { + import O1._ + import O2._ + + // Implicit usage compiles. + "a": Int + } + + // Import one implict and one non-implicit method with the + // same name in the same scope. + def m2 = { + import O1._ + import O3._ + + // Implicit usage compiles. + "a": Int + } +} \ No newline at end of file -- cgit v1.2.3 From 120766fa33adbbb13bc21d5622a6a30ed6b2b230 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 13 Jun 2012 19:17:42 +0200 Subject: Don't crash if cpsParam is not on the classpath. When checking if a piece of code needs the continuations plugin, the cpsParam classes may not be on the class path. Assume it does not need it in that case. --- .../plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 862b19d0a4..a20ff1667b 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -4,6 +4,7 @@ package scala.tools.selectivecps import scala.tools.nsc.Global import scala.tools.nsc.typechecker.Modes +import scala.tools.nsc.MissingRequirementError abstract class CPSAnnotationChecker extends CPSUtils with Modes { val global: Global @@ -356,8 +357,9 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { * for a tree. All this should do is add annotations. */ override def addAnnotations(tree: Tree, tpe: Type): Type = { + import scala.util.control._ if (!cpsEnabled) { - if (hasCpsParamTypes(tpe)) + if (Exception.failAsValue(classOf[MissingRequirementError])(false)(hasCpsParamTypes(tpe))) global.reporter.error(tree.pos, "this code must be compiled with the Scala continuations plugin enabled") return tpe } -- cgit v1.2.3 From fdca21eb130a782580cf13df0eb74eb4bceb2370 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 13 Jun 2012 19:20:35 +0200 Subject: Don't use the BrowsingLoader for Java sources. When the presentation compiler needs a SourceLoader, instead of immediately adding the file to the compilation round it uses a fast parser to create symbols for all top-level class definitions. The fast parser assumes Scala sources, which is not always the case. --- src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala index 25d835790e..52e971f1e7 100644 --- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala @@ -105,7 +105,7 @@ abstract class BrowsingLoaders extends SymbolLoaders { */ override def enterToplevelsFromSource(root: Symbol, name: String, src: AbstractFile) { try { - if (root.isEffectiveRoot) // RootClass or EmptyPackageClass + if (root.isEffectiveRoot || !src.name.endsWith(".scala")) // RootClass or EmptyPackageClass super.enterToplevelsFromSource(root, name, src) else browseTopLevel(root, src) -- cgit v1.2.3 From f4d2678c42a82a2716eac8bc50e39a1d96fb67c0 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 13 Jun 2012 19:22:52 +0200 Subject: Don't require symbols to be loaded in the parser. The parser special-cases primitive types. For comparing the name, it relied on having the symbols for primitive types already loaded, which is not always the case. The presentation compiler may parse sources for, say, scala.Int, and therefore the symbol for Int may not be yet available. --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index fd154fe796..bce9f28847 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -253,7 +253,16 @@ self => final val InBlock = 1 final val InTemplate = 2 - lazy val ScalaValueClassNames = tpnme.AnyVal :: definitions.ScalaValueClasses.map(_.name) + lazy val ScalaValueClassNames = Seq(tpnme.AnyVal, + tpnme.Unit, + tpnme.Boolean, + tpnme.Byte, + tpnme.Short, + tpnme.Char, + tpnme.Int, + tpnme.Long, + tpnme.Float, + tpnme.Double) import nme.raw -- cgit v1.2.3 From eaa3dd57f1a56151d0d6abe82f196e92aadaa843 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 13 Jun 2012 19:24:37 +0200 Subject: Remove NPE when `compileLate` sees sources during initialization. When a symbol needed in Definitions is loaded, it may be that the corresponding source is newer than the classfile. That will send it to `compileLate`, but no phase is yet set. We simply add it to the compilation pipeline and continue gracefully. --- src/compiler/scala/tools/nsc/Global.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 787c9c7f57..a701e41153 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1667,13 +1667,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Compile abstract file until `globalPhase`, but at least to phase "namer". */ def compileLate(unit: CompilationUnit) { - val maxId = math.max(globalPhase.id, typerPhase.id) addUnit(unit) - firstPhase.iterator takeWhile (_.id < maxId) foreach (ph => - atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit) - ) - refreshProgress + if (firstPhase ne null) { // we might get here during initialization, is a source is newer than the binary + val maxId = math.max(globalPhase.id, typerPhase.id) + firstPhase.iterator takeWhile (_.id < maxId) foreach (ph => + atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit)) + refreshProgress + } } /** Reset package class to state at typer (not sure what this -- cgit v1.2.3 From 456799bf08d27e1572a479c6b8d09a7d2d2445a0 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Wed, 13 Jun 2012 19:55:57 -0400 Subject: Refactoring and temporary commit before finishing the fix. --- project/Build.scala | 265 +++++---------------------------------- project/Packaging.scala | 129 +++++++++++++++++++ project/Release.scala | 1 + project/RemoteDependencies.scala | 46 +++++++ project/Sametest.scala | 3 - project/ScalaBuildKeys.scala | 34 ++--- project/ScalaToolRunner.scala | 21 ++++ project/Testing.scala | 44 +++++++ 8 files changed, 290 insertions(+), 253 deletions(-) create mode 100644 project/Packaging.scala create mode 100644 project/RemoteDependencies.scala create mode 100644 project/ScalaToolRunner.scala create mode 100644 project/Testing.scala diff --git a/project/Build.scala b/project/Build.scala index e2658e3405..42c84c0edd 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1,15 +1,14 @@ import sbt._ import Keys._ import partest._ -import SameTest._ import ScalaBuildKeys._ +import Release._ - -object ScalaBuild extends Build with Layers { +object ScalaBuild extends Build with Layers with Packaging with Testing { // Build wide settings: - override lazy val settings = super.settings ++ Versions.settings ++ Seq( + override lazy val settings = super.settings ++ Versions.settings ++ RemoteDependencies.buildSettings(Set(scalacheck), fullQuickScalaReference) ++ Seq( autoScalaLibrary := false, resolvers += Resolver.url( "Typesafe nightlies", @@ -21,36 +20,13 @@ object ScalaBuild extends Build with Layers { ), organization := "org.scala-lang", version <<= Versions.mavenVersion, - pomExtra := epflPomExtra, - commands += Command.command("fix-uri-projects") { (state: State) => - if(state.get(buildFixed) getOrElse false) state - else { - // TODO -fix up scalacheck's dependencies! - val extracted = Project.extract(state) - import extracted._ - def fix(s: Setting[_]): Setting[_] = s match { - case ScopedExternalSetting(`scalacheck`, scalaInstance.key, setting) => fullQuickScalaReference mapKey Project.mapScope(_ => s.key.scope) - case s => s - } - val transformed = session.mergeSettings map ( s => fix(s) ) - val scopes = transformed collect { case ScopedExternalSetting(`scalacheck`, _, s) => s.key.scope } toSet - // Create some fixers so we don't download scala or rely on it. - val fixers = for { scope <- scopes - setting <- Seq(autoScalaLibrary := false, crossPaths := false) - } yield setting mapKey Project.mapScope(_ => scope) - val newStructure = Load.reapply(transformed ++ fixers, structure) - Project.setProject(session, newStructure, state).put(buildFixed, true) - } - }, - onLoad in Global <<= (onLoad in Global) apply (_ andThen { (state: State) => - "fix-uri-projects" :: state - }) - ) + pomExtra := epflPomExtra + ) // Collections of projects to run 'compile' on. - lazy val compiledProjects = Seq(quickLib, quickComp, continuationsLibrary, actors, swing, forkjoin, fjbg) + lazy val compiledProjects = Seq(quickLib, quickComp, continuationsLibrary, actors, actorsMigration, swing, forkjoin, fjbg) // Collection of projects to 'package' and 'publish' together. - lazy val packagedBinaryProjects = Seq(scalaLibrary, scalaCompiler, swing, continuationsPlugin, jline, scalap) + lazy val packagedBinaryProjects = Seq(scalaLibrary, scalaCompiler, swing, actors, actorsMigration, continuationsPlugin, jline, scalap) lazy val partestRunProjects = Seq(testsuite, continuationsTestsuite) private def epflPomExtra = ( @@ -187,6 +163,7 @@ object ScalaBuild extends Build with Layers { // Reference to quick scala instance. lazy val quickScalaInstance = makeScalaReference("quick", quickLib, quickReflect, quickComp) def quickScalaLibraryDependency = unmanagedClasspath in Compile <++= (exportedProducts in quickLib in Compile).identity + def quickScalaReflectDependency = unmanagedClasspath in Compile <++= (exportedProducts in quickReflect in Compile).identity def quickScalaCompilerDependency = unmanagedClasspath in Compile <++= (exportedProducts in quickComp in Compile).identity // Strapp is used to test binary 'sameness' between things built with locker and things built with quick. @@ -222,17 +199,16 @@ object ScalaBuild extends Build with Layers { } // TODO - in sabbus, these all use locker to build... I think tihs way is better, but let's farm this idea around. - // TODO - Actors + swing separate jars... lazy val dependentProjectSettings = settingOverrides ++ Seq(quickScalaInstance, quickScalaLibraryDependency, addCheaterDependency("scala-library")) lazy val actors = Project("actors", file(".")) settings(dependentProjectSettings:_*) dependsOn(forkjoin % "provided") - // TODO - Remove actors dependency from pom... lazy val swing = Project("swing", file(".")) settings(dependentProjectSettings:_*) dependsOn(actors % "provided") + lazy val actorsMigration = Project("actors-migration", file(".")) settings(dependentProjectSettings:_*) dependsOn(actors % "provided") // This project will generate man pages (in man1 and html) for scala. lazy val manmakerSettings: Seq[Setting[_]] = dependentProjectSettings :+ externalDeps lazy val manmaker = Project("manual", file(".")) settings(manmakerSettings:_*) // Things that compile against the compiler. - lazy val compilerDependentProjectSettings = dependentProjectSettings ++ Seq(quickScalaCompilerDependency, addCheaterDependency("scala-compiler")) + lazy val compilerDependentProjectSettings = dependentProjectSettings ++ Seq(quickScalaReflectDependency, quickScalaCompilerDependency, addCheaterDependency("scala-compiler")) lazy val partestSettings = compilerDependentProjectSettings :+ externalDeps lazy val partest = Project("partest", file(".")) settings(partestSettings:_*) dependsOn(actors,forkjoin,scalap) lazy val scalapSettings = compilerDependentProjectSettings ++ Seq( @@ -267,7 +243,7 @@ object ScalaBuild extends Build with Layers { // -------------------------------------------------------------- val allSubpathsCopy = (dir: File) => (dir.*** --- dir) x (relativeTo(dir)|flat) def productTaskToMapping(products : Seq[File]) = products flatMap { p => allSubpathsCopy(p) } - lazy val packageScalaLibBinTask = Seq(quickLib, continuationsLibrary, forkjoin, actors).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping) + lazy val packageScalaLibBinTask = Seq(quickLib, continuationsLibrary, forkjoin).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping) lazy val scalaLibArtifactSettings: Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaLibBinTask)) ++ Seq( name := "scala-library", crossPaths := false, @@ -282,14 +258,13 @@ object ScalaBuild extends Build with Layers { ) lazy val scalaLibrary = Project("scala-library", file(".")) settings(publishSettings:_*) settings(scalaLibArtifactSettings:_*) - // TODO - Real Reflect instance - // -------------------------------------------------------------- - // Real Compiler Artifact + // Real Reflect Artifact // -------------------------------------------------------------- - lazy val packageScalaBinTask = Seq(quickComp, fjbg, asm).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping) - lazy val scalaBinArtifactSettings : Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaBinTask)) ++ Seq( - name := "scala-compiler", + + lazy val packageScalaReflect = Seq(quickReflect).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping) + lazy val scalaReflectArtifactSettings : Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaBinTask)) ++ Seq( + name := "scala-reflect", crossPaths := false, exportJars := true, autoScalaLibrary := false, @@ -298,52 +273,33 @@ object ScalaBuild extends Build with Layers { quickScalaInstance, target <<= (baseDirectory, name) apply (_ / "target" / _) ) - lazy val scalaCompiler = Project("scala-compiler", file(".")) settings(publishSettings:_*) settings(scalaBinArtifactSettings:_*) dependsOn(scalaLibrary) - lazy val fullQuickScalaReference = makeScalaReference("pack", scalaLibrary, quickReflect, scalaCompiler) + lazy val scalaReflect = Project("scala-reflect", file(".")) settings(publishSettings:_*) settings(scalaReflectArtifactSettings:_*) dependsOn(scalaLibrary) + // -------------------------------------------------------------- - // Testing + // Real Compiler Artifact // -------------------------------------------------------------- - /* lazy val scalacheckSettings: Seq[Setting[_]] = Seq(fullQuickScalaReference, crossPaths := false)*/ - lazy val scalacheck = uri("git://github.com/jsuereth/scalacheck.git#scala-build") - - lazy val testsuiteSettings: Seq[Setting[_]] = compilerDependentProjectSettings ++ partestTaskSettings ++ VerifyClassLoad.settings ++ Seq( - unmanagedBase <<= baseDirectory / "test/files/lib", - fullClasspath in VerifyClassLoad.checkClassLoad <<= (fullClasspath in scalaLibrary in Runtime).identity, + lazy val packageScalaBinTask = Seq(quickComp, fjbg, asm).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping) + lazy val scalaBinArtifactSettings : Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaBinTask)) ++ Seq( + name := "scala-compiler", + crossPaths := false, + exportJars := true, autoScalaLibrary := false, - checkSameLibrary <<= checkSameBinaryProjects(quickLib, strappLib), - checkSameCompiler <<= checkSameBinaryProjects(quickComp, strappComp), - checkSame <<= (checkSameLibrary, checkSameCompiler) map ((a,b) => ()), - autoScalaLibrary := false - ) - lazy val continuationsTestsuiteSettings: Seq[Setting[_]] = testsuiteSettings ++ Seq( - scalacOptions in Test <++= (exportedProducts in Compile in continuationsPlugin) map { - case Seq(cpDir) => Seq("-Xplugin-require:continuations", "-P:continuations:enable", "-Xplugin:"+cpDir.data.getAbsolutePath) - }, - partestDirs <<= baseDirectory apply { bd => - def mkFile(name: String) = bd / "test" / "files" / name - def mkTestType(name: String) = name.drop("continuations-".length).toString - Seq("continuations-neg", "continuations-run") map (t => mkTestType(t) -> mkFile(t)) toMap - } - ) - val testsuite = ( - Project("testsuite", file(".")) - settings (testsuiteSettings:_*) - dependsOn (swing, scalaLibrary, scalaCompiler, fjbg, partest, scalacheck) - ) - val continuationsTestsuite = ( - Project("continuations-testsuite", file(".")) - settings (continuationsTestsuiteSettings:_*) - dependsOn (partest, swing, scalaLibrary, scalaCompiler, fjbg) + unmanagedJars in Compile := Seq(), + fullClasspath in Runtime <<= (exportedProducts in Compile).identity, + quickScalaInstance, + target <<= (baseDirectory, name) apply (_ / "target" / _) ) + lazy val scalaCompiler = Project("scala-compiler", file(".")) settings(publishSettings:_*) settings(scalaBinArtifactSettings:_*) dependsOn(scalaReflect) + lazy val fullQuickScalaReference = makeScalaReference("pack", scalaLibrary, scalaReflect, scalaCompiler) + // -------------------------------------------------------------- // Generating Documentation. // -------------------------------------------------------------- // TODO - Migrate this into the dist project. // Scaladocs - def distScalaInstance = makeScalaReference("dist", scalaLibrary, quickReflect, scalaCompiler) lazy val documentationSettings: Seq[Setting[_]] = dependentProjectSettings ++ Seq( // TODO - Make these work for realz. defaultExcludes in unmanagedSources in Compile := ((".*" - ".") || HiddenFileFilter || @@ -373,163 +329,4 @@ object ScalaBuild extends Build with Layers { settings (documentationSettings: _*) dependsOn(quickLib, quickComp, actors, fjbg, forkjoin, swing, continuationsLibrary) ) - - // -------------------------------------------------------------- - // Packaging a distro - // -------------------------------------------------------------- - - class ScalaToolRunner(classpath: Classpath) { - // TODO - Don't use the ant task directly... - lazy val classLoader = new java.net.URLClassLoader(classpath.map(_.data.toURI.toURL).toArray, null) - lazy val mainClass = classLoader.loadClass("scala.tools.ant.ScalaTool") - lazy val executeMethod = mainClass.getMethod("execute") - lazy val setFileMethod = mainClass.getMethod("setFile", classOf[java.io.File]) - lazy val setClassMethod = mainClass.getMethod("setClass", classOf[String]) - lazy val setClasspathMethod = mainClass.getMethod("setClassPath", classOf[String]) - lazy val instance = mainClass.newInstance() - - def setClass(cls: String): Unit = setClassMethod.invoke(instance, cls) - def setFile(file: File): Unit = setFileMethod.invoke(instance, file) - def setClasspath(cp: String): Unit = setClasspathMethod.invoke(instance, cp) - def execute(): Unit = executeMethod.invoke(instance) - } - - def genBinTask( - runner: ScopedTask[ScalaToolRunner], - outputDir: ScopedSetting[File], - classpath: ScopedTask[Classpath], - useClasspath: Boolean - ): Project.Initialize[sbt.Task[Map[File,String]]] = { - (runner, outputDir, classpath, streams) map { (runner, outDir, cp, s) => - IO.createDirectory(outDir) - val classToFilename = Map( - "scala.tools.nsc.MainGenericRunner" -> "scala", - "scala.tools.nsc.Main" -> "scalac", - "scala.tools.nsc.ScalaDoc" -> "scaladoc", - "scala.tools.nsc.CompileClient" -> "fsc", - "scala.tools.scalap.Main" -> "scalap" - ) - if (useClasspath) { - val classpath = Build.data(cp).map(_.getCanonicalPath).distinct.mkString(",") - s.log.debug("Setting classpath = " + classpath) - runner setClasspath classpath - } - def genBinFiles(cls: String, dest: File) = { - runner.setClass(cls) - runner.setFile(dest) - runner.execute() - // TODO - Mark generated files as executable (755 or a+x) that is *not* JDK6 specific... - dest.setExecutable(true) - } - def makeBinMappings(cls: String, binName: String): Map[File,String] = { - val file = outDir / binName - val winBinName = binName + ".bat" - genBinFiles(cls, file) - Map( file -> ("bin/"+binName), outDir / winBinName -> ("bin/"+winBinName) ) - } - classToFilename.flatMap((makeBinMappings _).tupled).toMap - } - } - def runManmakerTask(classpath: ScopedTask[Classpath], scalaRun: ScopedTask[ScalaRun], mainClass: String, dir: String, ext: String): Project.Initialize[Task[Map[File,String]]] = - (classpath, scalaRun, streams, target) map { (cp, runner, s, target) => - val binaries = Seq("fsc", "scala", "scalac", "scaladoc", "scalap") - binaries map { bin => - val file = target / "man" / dir / (bin + ext) - val classname = "scala.man1." + bin - IO.createDirectory(file.getParentFile) - toError(runner.run(mainClass, Build.data(cp), Seq(classname, file.getAbsolutePath), s.log)) - file -> ("man/" + dir + "/" + bin + ext) - } toMap - } - - val genBinRunner = TaskKey[ScalaToolRunner]("gen-bin-runner", - "Creates a utility to generate script files for Scala.") - val genBin = TaskKey[Map[File,String]]("gen-bin", - "Creates script files for Scala distribution.") - val binDir = SettingKey[File]("binaries-directory", - "Directory where binary scripts will be located.") - val genBinQuick = TaskKey[Map[File,String]]("gen-quick-bin", - "Creates script files for testing against current Scala build classfiles (not local dist).") - val runManmakerMan = TaskKey[Map[File,String]]("make-man", - "Runs the man maker project to generate man pages") - val runManmakerHtml = TaskKey[Map[File,String]]("make-html", - "Runs the man maker project to generate html pages") - - lazy val scalaDistSettings: Seq[Setting[_]] = Seq( - crossPaths := false, - target <<= (baseDirectory, name) apply (_ / "target" / _), - scalaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _), - autoScalaLibrary := false, - unmanagedJars in Compile := Seq(), - genBinRunner <<= (fullClasspath in quickComp in Runtime) map (new ScalaToolRunner(_)), - binDir <<= target(_/"bin"), - genBin <<= genBinTask(genBinRunner, binDir, fullClasspath in Runtime, false), - binDir in genBinQuick <<= baseDirectory apply (_ / "target" / "bin"), - // Configure the classpath this way to avoid having .jar files and previous layers on the classpath. - fullClasspath in Runtime in genBinQuick <<= Seq(quickComp,quickLib,scalap,actors,swing,fjbg,jline,forkjoin).map(classDirectory in Compile in _).join.map(Attributed.blankSeq), - fullClasspath in Runtime in genBinQuick <++= (fullClasspath in Compile in jline), - genBinQuick <<= genBinTask(genBinRunner, binDir in genBinQuick, fullClasspath in Runtime in genBinQuick, true), - runManmakerMan <<= runManmakerTask(fullClasspath in Runtime in manmaker, runner in manmaker, "scala.tools.docutil.EmitManPage", "man1", ".1"), - runManmakerHtml <<= runManmakerTask(fullClasspath in Runtime in manmaker, runner in manmaker, "scala.tools.docutil.EmitHtml", "doc", ".html"), - // TODO - We could *really* clean this up in many ways. Let's look into making a a Seq of "direct jars" (scalaLibrary, scalaCompiler, jline, scalap) - // a seq of "plugin jars" (continuationsPlugin) and "binaries" (genBin) and "documentation" mappings (genBin) that this can aggregate. - // really need to figure out a better way to pull jline + jansi. - makeDistMappings <<= (genBin, - runManmakerMan, - runManmakerHtml, - packageBin in scalaLibrary in Compile, - packageBin in scalaCompiler in Compile, - packageBin in jline in Compile, - packageBin in continuationsPlugin in Compile, - managedClasspath in jline in Compile, - packageBin in scalap in Compile) map { - (binaries, man, html, lib, comp, jline, continuations, jlineDeps, scalap) => - val jlineDepMap: Seq[(File, String)] = jlineDeps.map(_.data).flatMap(_ x Path.flat) map { case(a,b) => a -> ("lib/"+b) } - binaries ++ man ++ html ++ jlineDepMap ++ Seq( - lib -> "lib/scala-library.jar", - comp -> "lib/scala-compiler.jar", - jline -> "lib/jline.jar", - continuations -> "misc/scala-devel/plugins/continuations.jar", - scalap -> "lib/scalap.jar" - ) toMap - }, - // Add in some more dependencies - makeDistMappings <<= (makeDistMappings, - packageBin in swing in Compile) map { - (dist, s) => - dist ++ Seq(s -> "lib/scala-swing.jar") - }, - makeDist <<= (makeDistMappings, baseDirectory, streams) map { (maps, dir, s) => - s.log.debug("Map = " + maps.mkString("\n")) - val file = dir / "target" / "scala-dist.zip" - IO.zip(maps, file) - s.log.info("Created " + file.getAbsolutePath) - file - }, - makeExplodedDist <<= (makeDistMappings, target, streams) map { (maps, dir, s) => - def sameFile(f: File, f2: File) = f.getCanonicalPath == f2.getCanonicalPath - IO.createDirectory(dir) - IO.copy(for { - (file, name) <- maps - val file2 = dir / name - if !sameFile(file,file2) - } yield (file, file2)) - // Hack to make binaries be executable. TODO - Fix for JDK 5 and below... - maps.values filter (_ startsWith "bin/") foreach (dir / _ setExecutable true) - dir - } - ) - lazy val scaladist = ( - Project("dist", file(".")) - settings (scalaDistSettings: _*) - ) -} - -/** Matcher to make updated remote project references easier. */ -object ScopedExternalSetting { - def unapply[T](s: Setting[_]): Option[(URI, AttributeKey[_], Setting[_])] = - s.key.scope.project match { - case Select(p @ ProjectRef(uri, _)) => Some((uri, s.key.key, s)) - case _ => None - } } diff --git a/project/Packaging.scala b/project/Packaging.scala new file mode 100644 index 0000000000..eb4e69f99e --- /dev/null +++ b/project/Packaging.scala @@ -0,0 +1,129 @@ +import sbt._ +import Keys._ +import ScalaBuildKeys._ + +/** All the settings related to *packaging* the built scala software. */ +trait Packaging { self: ScalaBuild.type => + + // -------------------------------------------------------------- + // Packaging a distro + // -------------------------------------------------------------- + lazy val scalaDistSettings: Seq[Setting[_]] = Seq( + crossPaths := false, + target <<= (baseDirectory, name) apply (_ / "target" / _), + scalaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _), + autoScalaLibrary := false, + unmanagedJars in Compile := Seq(), + genBinRunner <<= (fullClasspath in quickComp in Runtime) map (new ScalaToolRunner(_)), + binDir <<= target(_/"bin"), + genBin <<= genBinTask(genBinRunner, binDir, fullClasspath in Runtime, false), + binDir in genBinQuick <<= baseDirectory apply (_ / "target" / "bin"), + // Configure the classpath this way to avoid having .jar files and previous layers on the classpath. + fullClasspath in Runtime in genBinQuick <<= Seq(quickComp,quickLib,scalap,actors,swing,fjbg,jline,forkjoin).map(classDirectory in Compile in _).join.map(Attributed.blankSeq), + fullClasspath in Runtime in genBinQuick <++= (fullClasspath in Compile in jline), + genBinQuick <<= genBinTask(genBinRunner, binDir in genBinQuick, fullClasspath in Runtime in genBinQuick, true), + runManmakerMan <<= runManmakerTask(fullClasspath in Runtime in manmaker, runner in manmaker, "scala.tools.docutil.EmitManPage", "man1", ".1"), + runManmakerHtml <<= runManmakerTask(fullClasspath in Runtime in manmaker, runner in manmaker, "scala.tools.docutil.EmitHtml", "doc", ".html"), + // TODO - We could *really* clean this up in many ways. Let's look into making a a Seq of "direct jars" (scalaLibrary, scalaCompiler, jline, scalap) + // a seq of "plugin jars" (continuationsPlugin) and "binaries" (genBin) and "documentation" mappings (genBin) that this can aggregate. + // really need to figure out a better way to pull jline + jansi. + makeDistMappings <<= (genBin, + runManmakerMan, + runManmakerHtml, + packageBin in scalaLibrary in Compile, + packageBin in scalaCompiler in Compile, + packageBin in jline in Compile, + packageBin in continuationsPlugin in Compile, + managedClasspath in jline in Compile, + packageBin in scalap in Compile) map { + (binaries, man, html, lib, comp, jline, continuations, jlineDeps, scalap) => + val jlineDepMap: Seq[(File, String)] = jlineDeps.map(_.data).flatMap(_ x Path.flat) map { case(a,b) => a -> ("lib/"+b) } + binaries ++ man ++ html ++ jlineDepMap ++ Seq( + lib -> "lib/scala-library.jar", + comp -> "lib/scala-compiler.jar", + jline -> "lib/jline.jar", + continuations -> "misc/scala-devel/plugins/continuations.jar", + scalap -> "lib/scalap.jar" + ) + }, + // Add in some more dependencies + makeDistMappings <+= (packageBin in swing in Compile) map (s => s -> "lib/scala-swing.jar"), + makeDistMappings <+= (packageBin in scalaReflect in Compile) map (s => s -> "lib/scala-reflect.jar"), + makeDist <<= (makeDistMappings, baseDirectory, streams) map { (maps, dir, s) => + s.log.debug("Map = " + maps.mkString("\n")) + val file = dir / "target" / "scala-dist.zip" + IO.zip(maps, file) + s.log.info("Created " + file.getAbsolutePath) + file + }, + makeExplodedDist <<= (makeDistMappings, target, streams) map { (maps, dir, s) => + def sameFile(f: File, f2: File) = f.getCanonicalPath == f2.getCanonicalPath + IO.createDirectory(dir) + IO.copy(for { + (file, name) <- maps + val file2 = dir / name + if !sameFile(file,file2) + } yield (file, file2)) + // Hack to make binaries be executable. TODO - Fix for JDK 5 and below... + maps map (_._2) filter (_ startsWith "bin/") foreach (dir / _ setExecutable true) + dir + } + ) + lazy val scaladist = ( + Project("dist", file(".")) + settings (scalaDistSettings: _*) + ) + + +// Helpers to make a distribution + + /** Generates runner scripts for distribution. */ + def genBinTask( + runner: ScopedTask[ScalaToolRunner], + outputDir: ScopedSetting[File], + classpath: ScopedTask[Classpath], + useClasspath: Boolean + ): Project.Initialize[sbt.Task[Seq[(File,String)]]] = { + (runner, outputDir, classpath, streams) map { (runner, outDir, cp, s) => + IO.createDirectory(outDir) + val classToFilename = Seq( + "scala.tools.nsc.MainGenericRunner" -> "scala", + "scala.tools.nsc.Main" -> "scalac", + "scala.tools.nsc.ScalaDoc" -> "scaladoc", + "scala.tools.nsc.CompileClient" -> "fsc", + "scala.tools.scalap.Main" -> "scalap" + ) + if (useClasspath) { + val classpath = Build.data(cp).map(_.getCanonicalPath).distinct.mkString(",") + s.log.debug("Setting classpath = " + classpath) + runner setClasspath classpath + } + def genBinFiles(cls: String, dest: File) = { + runner.setClass(cls) + runner.setFile(dest) + runner.execute() + // TODO - Mark generated files as executable (755 or a+x) that is *not* JDK6 specific... + dest.setExecutable(true) + } + def makeBinMappings(cls: String, binName: String): Seq[(File,String)] = { + val file = outDir / binName + val winBinName = binName + ".bat" + genBinFiles(cls, file) + Seq( file -> ("bin/"+binName), outDir / winBinName -> ("bin/"+winBinName) ) + } + classToFilename.flatMap((makeBinMappings _).tupled) + } + } + /** Creates man pages for distribution. */ + def runManmakerTask(classpath: ScopedTask[Classpath], scalaRun: ScopedTask[ScalaRun], mainClass: String, dir: String, ext: String): Project.Initialize[Task[Seq[(File,String)]]] = + (classpath, scalaRun, streams, target) map { (cp, runner, s, target) => + val binaries = Seq("fsc", "scala", "scalac", "scaladoc", "scalap") + binaries map { bin => + val file = target / "man" / dir / (bin + ext) + val classname = "scala.man1." + bin + IO.createDirectory(file.getParentFile) + toError(runner.run(mainClass, Build.data(cp), Seq(classname, file.getAbsolutePath), s.log)) + file -> ("man/" + dir + "/" + bin + ext) + } + } +} diff --git a/project/Release.scala b/project/Release.scala index 1a17956c13..9d4d4ab09c 100644 --- a/project/Release.scala +++ b/project/Release.scala @@ -12,4 +12,5 @@ object Release { state } + } diff --git a/project/RemoteDependencies.scala b/project/RemoteDependencies.scala new file mode 100644 index 0000000000..3f0fc2abf8 --- /dev/null +++ b/project/RemoteDependencies.scala @@ -0,0 +1,46 @@ +import sbt._ +import Keys._ +import ScalaBuildKeys._ + + +object RemoteDependencies { + def buildSettings(externalProjects: Set[URI], localScala: Setting[_]): Seq[Setting[_]] = Seq( + commands += Command.command("fix-uri-projects") { (state: State) => + if(state.get(buildFixed) getOrElse false) state + else { + // TODO -fix up scalacheck's dependencies! + val extracted = Project.extract(state) + import extracted._ + def fix(s: Setting[_]): Setting[_] = s match { + case ScopedExternalSetting(p, scalaInstance.key, setting) if externalProjects(p) => localScala mapKey Project.mapScope(_ => s.key.scope) + case s => s + } + val transformed = session.mergeSettings map ( s => fix(s) ) + val scopes = transformed collect { case ScopedExternalSetting(p, _, s) if externalProjects(p) => s.key.scope } toSet + // Create some fixers so we don't download scala or rely on it. + val fixers = for { scope <- scopes + setting <- Seq(autoScalaLibrary := false, crossPaths := false) + } yield setting mapKey Project.mapScope(_ => scope) + val newStructure = Load.reapply(transformed ++ fixers, structure) + Project.setProject(session, newStructure, state).put(buildFixed, true) + } + }, + onLoad in Global <<= (onLoad in Global) apply (_ andThen { (state: State) => + "fix-uri-projects" :: state + }) + ) +} + + + +/** Matcher to make updated remote project references easier. */ +object ScopedExternalSetting { + def unapply[T](s: Setting[_]): Option[(URI, AttributeKey[_], Setting[_])] = + s.key.scope.project match { + case Select(p @ ProjectRef(uri, _)) => Some((uri, s.key.key, s)) + case _ => None + } +} + + + diff --git a/project/Sametest.scala b/project/Sametest.scala index f44fe8ec65..6f12eb24b3 100644 --- a/project/Sametest.scala +++ b/project/Sametest.scala @@ -5,9 +5,6 @@ import Keys._ // This code is adapted from scala.tools.ant.Same by Gilles Dubochet. object SameTest { - lazy val checkSame: TaskKey[Unit] = TaskKey("check-same-binaries", "checks whether or not the class files generated by scala are the same.") - lazy val checkSameLibrary: TaskKey[Unit] = TaskKey("check-same-lib-binaries", "checks whether or not the librayr class files generated by scala are the same.") - lazy val checkSameCompiler: TaskKey[Unit] = TaskKey("check-same-comp-binaries", "checks whether or not the compiler class files generated by scala are the same.") def checkSameBinaryProjects(lhs: Project, rhs: Project): Project.Initialize[Task[Unit]] = (classDirectory in Compile in lhs, classDirectory in Compile in rhs, diff --git a/project/ScalaBuildKeys.scala b/project/ScalaBuildKeys.scala index dbde6bd18c..e846a884b9 100644 --- a/project/ScalaBuildKeys.scala +++ b/project/ScalaBuildKeys.scala @@ -2,21 +2,23 @@ import sbt._ import Keys._ object ScalaBuildKeys { - val lockerLock: TaskKey[Unit] = TaskKey("locker-lock", - "Locks the locker layer of the compiler build such that it won't rebuild on changed source files.") - val lockerUnlock: TaskKey[Unit] = TaskKey("locker-unlock", - "Unlocks the locker layer of the compiler so that it will be recompiled on changed source files.") - val lockFile: SettingKey[File] = SettingKey("lock-file", - "Location of the lock file compiling this project.") - // New tasks/settings specific to the scala build. - val lock: TaskKey[Unit] = TaskKey("lock", "Locks this project so it won't be recompiled.") - val unlock: TaskKey[Unit] = TaskKey("unlock", "Unlocks this project so it will be recompiled.") - val makeDist: TaskKey[File] = TaskKey("make-dist", - "Creates a mini-distribution (scala home directory) for this build in a zip file.") - val makeExplodedDist: TaskKey[File] = TaskKey("make-exploded-dist", - "Creates a mini-distribution (scala home directory) for this build in a directory.") - val makeDistMappings: TaskKey[Map[File, String]] = TaskKey("make-dist-mappings", - "Creates distribution mappings for creating zips,jars,directorys,etc.") - val buildFixed = AttributeKey[Boolean]("build-uri-fixed") + val lockerLock = TaskKey[Unit]("locker-lock", "Locks the locker layer of the compiler build such that it won't rebuild on changed source files.") + val lockerUnlock = TaskKey[Unit]("locker-unlock", "Unlocks the locker layer of the compiler so that it will be recompiled on changed source files.") + val lockFile = SettingKey[File]("lock-file", "Location of the lock file compiling this project.") + val lock = TaskKey[Unit]("lock", "Locks this project so it won't be recompiled.") + val unlock = TaskKey[Unit]("unlock", "Unlocks this project so it will be recompiled.") + val makeDist = TaskKey[File]("make-dist", "Creates a mini-distribution (scala home directory) for this build in a zip file.") + val makeExplodedDist = TaskKey[File]("make-exploded-dist", "Creates a mini-distribution (scala home directory) for this build in a directory.") + val makeDistMappings = TaskKey[Seq[(File, String)]]("make-dist-mappings", "Creates distribution mappings for creating zips,jars,directorys,etc.") + val buildFixed = AttributeKey[Boolean]("build-uri-fixed") + val genBinRunner = TaskKey[ScalaToolRunner]("gen-bin-runner", "Creates a utility to generate script files for Scala.") + val genBin = TaskKey[Seq[(File,String)]]("gen-bin", "Creates script files for Scala distribution.") + val binDir = SettingKey[File]("binaries-directory", "Directory where binary scripts will be located.") + val genBinQuick = TaskKey[Seq[(File,String)]]("gen-quick-bin", "Creates script files for testing against current Scala build classfiles (not local dist).") + val runManmakerMan = TaskKey[Seq[(File,String)]]("make-man", "Runs the man maker project to generate man pages") + val runManmakerHtml = TaskKey[Seq[(File,String)]]("make-html", "Runs the man maker project to generate html pages") + val checkSame = TaskKey[Unit]("check-same-binaries", "checks whether or not the class files generated by scala are the same.") + val checkSameLibrary = TaskKey[Unit]("check-same-lib-binaries", "checks whether or not the librayr class files generated by scala are the same.") + val checkSameCompiler = TaskKey[Unit]("check-same-comp-binaries", "checks whether or not the compiler class files generated by scala are the same.") } diff --git a/project/ScalaToolRunner.scala b/project/ScalaToolRunner.scala new file mode 100644 index 0000000000..d7338a54b3 --- /dev/null +++ b/project/ScalaToolRunner.scala @@ -0,0 +1,21 @@ +import sbt._ +import Keys._ + +/** Reflection helper that runs ScalaTool. + * TODO - When SBT is on 2.10.x try to use Dynamic + Reflection. COULD BE FUN. + */ +class ScalaToolRunner(classpath: Classpath) { + // TODO - Don't use the ant task directly... + lazy val classLoader = new java.net.URLClassLoader(classpath.map(_.data.toURI.toURL).toArray, null) + lazy val mainClass = classLoader.loadClass("scala.tools.ant.ScalaTool") + lazy val executeMethod = mainClass.getMethod("execute") + lazy val setFileMethod = mainClass.getMethod("setFile", classOf[java.io.File]) + lazy val setClassMethod = mainClass.getMethod("setClass", classOf[String]) + lazy val setClasspathMethod = mainClass.getMethod("setClassPath", classOf[String]) + lazy val instance = mainClass.newInstance() + + def setClass(cls: String): Unit = setClassMethod.invoke(instance, cls) + def setFile(file: File): Unit = setFileMethod.invoke(instance, file) + def setClasspath(cp: String): Unit = setClasspathMethod.invoke(instance, cp) + def execute(): Unit = executeMethod.invoke(instance) +} diff --git a/project/Testing.scala b/project/Testing.scala new file mode 100644 index 0000000000..61676c81bd --- /dev/null +++ b/project/Testing.scala @@ -0,0 +1,44 @@ +import sbt._ +import Keys._ +import partest._ +import SameTest._ +import ScalaBuildKeys._ + +/** All settings/projects relating to testing. */ +trait Testing { self: ScalaBuild.type => + + /* lazy val scalacheckSettings: Seq[Setting[_]] = Seq(fullQuickScalaReference, crossPaths := false)*/ + lazy val scalacheck = uri("git://github.com/jsuereth/scalacheck.git#scala-build") + + lazy val testsuiteSettings: Seq[Setting[_]] = compilerDependentProjectSettings ++ partestTaskSettings ++ VerifyClassLoad.settings ++ Seq( + unmanagedBase <<= baseDirectory / "test/files/lib", + fullClasspath in VerifyClassLoad.checkClassLoad <<= (fullClasspath in scalaLibrary in Runtime).identity, + autoScalaLibrary := false, + checkSameLibrary <<= checkSameBinaryProjects(quickLib, strappLib), + checkSameCompiler <<= checkSameBinaryProjects(quickComp, strappComp), + checkSame <<= (checkSameLibrary, checkSameCompiler) map ((a,b) => ()), + autoScalaLibrary := false + ) + lazy val continuationsTestsuiteSettings: Seq[Setting[_]] = testsuiteSettings ++ Seq( + scalacOptions in Test <++= (exportedProducts in Compile in continuationsPlugin) map { + case Seq(cpDir) => Seq("-Xplugin-require:continuations", "-P:continuations:enable", "-Xplugin:"+cpDir.data.getAbsolutePath) + }, + partestDirs <<= baseDirectory apply { bd => + def mkFile(name: String) = bd / "test" / "files" / name + def mkTestType(name: String) = name.drop("continuations-".length).toString + Seq("continuations-neg", "continuations-run") map (t => mkTestType(t) -> mkFile(t)) toMap + } + ) + val testsuite = ( + Project("testsuite", file(".")) + settings (testsuiteSettings:_*) + dependsOn (scalaLibrary, scalaCompiler, fjbg, partest, scalacheck) + ) + val continuationsTestsuite = ( + Project("continuations-testsuite", file(".")) + settings (continuationsTestsuiteSettings:_*) + dependsOn (partest, scalaLibrary, scalaCompiler, fjbg) + ) + +} + -- cgit v1.2.3 From d3393306e3899e638b1c5ebe577f3fe1b2729cbd Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 14 Jun 2012 08:40:11 +0200 Subject: SI-4270 Refactor for efficiency and clarity. Avoids allocation of the hashset used by shadowing checks when searching in the implicit scope, and replaces three checks for `if (isLocal)` with a sprinkling of polymorphism. --- .../scala/tools/nsc/typechecker/Implicits.scala | 30 +++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 97a229a18d..22c0ba3a1c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -739,15 +739,33 @@ trait Implicits { * enclosing scope, and so on. */ class ImplicitComputation(iss: Infoss, isLocal: Boolean) { - private val shadowed = util.HashSet[Name](512) + abstract class Shadower { + def addInfos(infos: Infos) + def isShadowed(name: Name): Boolean + } + private val shadower: Shadower = { + /** Used for exclude implicits from outer scopes that are shadowed by same-named implicits */ + final class LocalShadower extends Shadower { + val shadowed = util.HashSet[Name](512) + def addInfos(infos: Infos) { + shadowed addEntries infos.map(_.name) + } + def isShadowed(name: Name) = shadowed(name) + } + /** Used for the implicits of expected type, when no shadowing checks are needed. */ + object NoShadower extends Shadower { + def addInfos(infos: Infos) {} + def isShadowed(name: Name) = false + } + if (isLocal) new LocalShadower else NoShadower + } + private var best: SearchResult = SearchFailure - private def isShadowed(name: Name) = ( - isLocal && shadowed(name) - ) + private def isIneligible(info: ImplicitInfo) = ( info.isCyclicOrErroneous || isView && isPredefMemberNamed(info.sym, nme.conforms) - || isShadowed(info.name) + || shadower.isShadowed(info.name) || (!context.macrosEnabled && info.sym.isTermMacro) ) @@ -786,7 +804,7 @@ trait Implicits { val eligible = { val matches = iss flatMap { is => val result = is filter (info => checkValid(info.sym) && survives(info)) - if (isLocal) shadowed addEntries (is map (_.name)) + shadower addInfos is result } -- cgit v1.2.3 From 48fc0393808408cd974a80d6ca65b33003599e14 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Thu, 14 Jun 2012 09:08:37 -0400 Subject: More fixups. --- project/Build.scala | 9 ++++----- project/RemoteDependencies.scala | 29 ++++++++++++++++++----------- project/ScalaBuildKeys.scala | 1 - 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 42c84c0edd..6a69e00da2 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -112,7 +112,6 @@ object ScalaBuild extends Build with Layers with Packaging with Testing { javacOptions ++= Seq("-target", "1.5", "-source", "1.5"), scalaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _), javaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _), - autoScalaLibrary := false, unmanagedJars in Compile := Seq(), // Most libs in the compiler use this order to build. compileOrder in Compile := CompileOrder.JavaThenScala, @@ -200,9 +199,9 @@ object ScalaBuild extends Build with Layers with Packaging with Testing { // TODO - in sabbus, these all use locker to build... I think tihs way is better, but let's farm this idea around. lazy val dependentProjectSettings = settingOverrides ++ Seq(quickScalaInstance, quickScalaLibraryDependency, addCheaterDependency("scala-library")) - lazy val actors = Project("actors", file(".")) settings(dependentProjectSettings:_*) dependsOn(forkjoin % "provided") - lazy val swing = Project("swing", file(".")) settings(dependentProjectSettings:_*) dependsOn(actors % "provided") - lazy val actorsMigration = Project("actors-migration", file(".")) settings(dependentProjectSettings:_*) dependsOn(actors % "provided") + lazy val actors = Project("scala-actors", file(".")) settings(dependentProjectSettings:_*) dependsOn(forkjoin % "provided") + lazy val swing = Project("scala-swing", file(".")) settings(dependentProjectSettings:_*) dependsOn(actors % "provided") + lazy val actorsMigration = Project("scala-actors-migration", file(".")) settings(dependentProjectSettings:_*) dependsOn(actors % "provided") // This project will generate man pages (in man1 and html) for scala. lazy val manmakerSettings: Seq[Setting[_]] = dependentProjectSettings :+ externalDeps lazy val manmaker = Project("manual", file(".")) settings(manmakerSettings:_*) @@ -263,7 +262,7 @@ object ScalaBuild extends Build with Layers with Packaging with Testing { // -------------------------------------------------------------- lazy val packageScalaReflect = Seq(quickReflect).map(p => products in p in Compile).join.map(_.flatten).map(productTaskToMapping) - lazy val scalaReflectArtifactSettings : Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaBinTask)) ++ Seq( + lazy val scalaReflectArtifactSettings : Seq[Setting[_]] = inConfig(Compile)(Defaults.packageTasks(packageBin, packageScalaReflect)) ++ Seq( name := "scala-reflect", crossPaths := false, exportJars := true, diff --git a/project/RemoteDependencies.scala b/project/RemoteDependencies.scala index 3f0fc2abf8..705b9dc402 100644 --- a/project/RemoteDependencies.scala +++ b/project/RemoteDependencies.scala @@ -11,18 +11,25 @@ object RemoteDependencies { // TODO -fix up scalacheck's dependencies! val extracted = Project.extract(state) import extracted._ + val scalaVersionString = extracted get version + def fix(s: Setting[_]): Setting[_] = s match { - case ScopedExternalSetting(p, scalaInstance.key, setting) if externalProjects(p) => localScala mapKey Project.mapScope(_ => s.key.scope) - case s => s - } - val transformed = session.mergeSettings map ( s => fix(s) ) - val scopes = transformed collect { case ScopedExternalSetting(p, _, s) if externalProjects(p) => s.key.scope } toSet - // Create some fixers so we don't download scala or rely on it. - val fixers = for { scope <- scopes - setting <- Seq(autoScalaLibrary := false, crossPaths := false) - } yield setting mapKey Project.mapScope(_ => scope) - val newStructure = Load.reapply(transformed ++ fixers, structure) - Project.setProject(session, newStructure, state).put(buildFixed, true) + case ScopedExternalSetting(p, scalaInstance.key, setting) if externalProjects(p) => localScala mapKey Project.mapScope(_ => s.key.scope) + // TODO - Fix Actors dependency... + //case ScopedExternalSetting(p, libraryDependencies.key, setting) if externalProjects(p) => fixProjectDeps(s) + case s => s + } + val transformed = session.mergeSettings map ( s => fix(s) ) + val scopes = transformed collect { case ScopedExternalSetting(p, _, s) if externalProjects(p) => s.key.scope } toSet + // Create some fixers so we don't download scala or rely on it. + // Also add dependencies that disappear in 2.10 for now... + val fixers = for { scope <- scopes + setting <- Seq(autoScalaLibrary := false, + crossPaths := false, + scalaVersion := scalaVersionString) + } yield setting mapKey Project.mapScope(_ => scope) + val newStructure = Load.reapply(transformed ++ fixers, structure) + Project.setProject(session, newStructure, state).put(buildFixed, true) } }, onLoad in Global <<= (onLoad in Global) apply (_ andThen { (state: State) => diff --git a/project/ScalaBuildKeys.scala b/project/ScalaBuildKeys.scala index e846a884b9..9e495de19f 100644 --- a/project/ScalaBuildKeys.scala +++ b/project/ScalaBuildKeys.scala @@ -20,5 +20,4 @@ object ScalaBuildKeys { val checkSame = TaskKey[Unit]("check-same-binaries", "checks whether or not the class files generated by scala are the same.") val checkSameLibrary = TaskKey[Unit]("check-same-lib-binaries", "checks whether or not the librayr class files generated by scala are the same.") val checkSameCompiler = TaskKey[Unit]("check-same-comp-binaries", "checks whether or not the compiler class files generated by scala are the same.") - } -- cgit v1.2.3 From 796024c7429a03e974a7d8e1dc5c80b84f82467d Mon Sep 17 00:00:00 2001 From: phaller Date: Fri, 25 May 2012 17:36:19 +0200 Subject: CPS: enable return expressions in CPS code if they are in tail position Adds a stack of context trees to AnnotationChecker(s). Here, it is used to enforce that adaptAnnotations will only adapt the annotation of a return expression if the expected type is a CPS type. The remove-tail-return transform is reasonably general, covering cases such as try-catch-finally. Moreover, an error is thrown if, in a CPS method, a return is encountered which is not in a tail position such that it will be removed subsequently. --- .../scala/tools/nsc/typechecker/Typers.scala | 4 ++ .../tools/selectivecps/CPSAnnotationChecker.scala | 27 +++++++++++++ .../plugin/scala/tools/selectivecps/CPSUtils.scala | 36 ++++++++++++++++++ .../tools/selectivecps/SelectiveANFTransform.scala | 15 +++++++- .../reflect/internal/AnnotationCheckers.scala | 14 +++++++ .../continuations-neg/ts-1681-nontail-return.check | 4 ++ .../continuations-neg/ts-1681-nontail-return.scala | 18 +++++++++ test/files/continuations-run/ts-1681-2.check | 5 +++ test/files/continuations-run/ts-1681-2.scala | 44 ++++++++++++++++++++++ test/files/continuations-run/ts-1681.check | 3 ++ test/files/continuations-run/ts-1681.scala | 29 ++++++++++++++ 11 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 test/files/continuations-neg/ts-1681-nontail-return.check create mode 100644 test/files/continuations-neg/ts-1681-nontail-return.scala create mode 100644 test/files/continuations-run/ts-1681-2.check create mode 100644 test/files/continuations-run/ts-1681-2.scala create mode 100644 test/files/continuations-run/ts-1681.check create mode 100644 test/files/continuations-run/ts-1681.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2bdae4164a..7e4f50ecd7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3987,7 +3987,11 @@ trait Typers extends Modes with Adaptations with Tags { ReturnWithoutTypeError(tree, enclMethod.owner) } else { context.enclMethod.returnsSeen = true + //TODO: also pass enclMethod.tree, so that adaptAnnotations can check whether return is in tail position + pushAnnotationContext(tree) val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe) + popAnnotationContext() + // Warn about returning a value if no value can be returned. if (restpt.tpe.typeSymbol == UnitClass) { // The typing in expr1 says expr is Unit (it has already been coerced if diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 862b19d0a4..574a76484c 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -17,6 +17,8 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { * Checks whether @cps annotations conform */ object checker extends AnnotationChecker { + private var contextStack: List[Tree] = List() + private def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker() private def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker() @@ -25,6 +27,12 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { private def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) = cleanPlus(tp) withAnnotations newAnnots.toList + override def pushAnnotationContext(tree: Tree): Unit = + contextStack = tree :: contextStack + + override def popAnnotationContext(): Unit = + contextStack = contextStack.tail + /** Check annotations to decide whether tpe1 <:< tpe2 */ def annotationsConform(tpe1: Type, tpe2: Type): Boolean = { if (!cpsEnabled) return true @@ -116,6 +124,11 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { bounds } + private def inReturnContext(tree: Tree): Boolean = !contextStack.isEmpty && (contextStack.head match { + case Return(tree1) => tree1 == tree + case _ => false + }) + override def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = { if (!cpsEnabled) return false vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) @@ -170,6 +183,9 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { vprintln("yes we can!! (byval)") return true } + } else if (inReturnContext(tree)) { + vprintln("yes we can!! (return)") + return true } } false @@ -209,6 +225,12 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { val res = tree modifyType addMinusMarker vprintln("adapted annotations (by val) of " + tree + " to " + res.tpe) res + } else if (inReturnContext(tree) && !hasPlusMarker(tree.tpe) && annotsTree.isEmpty && annotsExpected.nonEmpty) { + // add a marker annotation that will make tree.tpe behave as pt, subtyping wise + // tree will look like having no annotation + val res = tree modifyType (_ withAnnotations List(newPlusMarker())) + vprintln("adapted annotations (return) of " + tree + " to " + res.tpe) + res } else tree } @@ -464,6 +486,11 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } tpe + case ret @ Return(expr) => + if (hasPlusMarker(expr.tpe)) + ret setType expr.tpe + ret.tpe + case _ => tpe } diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 3a1dc87a6a..1e4d9f21de 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -3,6 +3,7 @@ package scala.tools.selectivecps import scala.tools.nsc.Global +import scala.collection.mutable.ListBuffer trait CPSUtils { val global: Global @@ -135,4 +136,39 @@ trait CPSUtils { case _ => None } } + + def isTailReturn(retExpr: Tree, body: Tree): Boolean = { + val removedIds = ListBuffer[Int]() + removeTailReturn(body, removedIds) + removedIds contains retExpr.id + } + + def removeTailReturn(tree: Tree, ids: ListBuffer[Int]): Tree = tree match { + case Block(stms, r @ Return(expr)) => + ids += r.id + treeCopy.Block(tree, stms, expr) + + case Block(stms, expr) => + treeCopy.Block(tree, stms, removeTailReturn(expr, ids)) + + case If(cond, thenExpr, elseExpr) => + treeCopy.If(tree, cond, removeTailReturn(thenExpr, ids), removeTailReturn(elseExpr, ids)) + + case Try(block, catches, finalizer) => + treeCopy.Try(tree, + removeTailReturn(block, ids), + (catches map (t => removeTailReturn(t, ids))).asInstanceOf[List[CaseDef]], + removeTailReturn(finalizer, ids)) + + case CaseDef(pat, guard, r @ Return(expr)) => + ids += r.id + treeCopy.CaseDef(tree, pat, guard, expr) + + case CaseDef(pat, guard, body) => + treeCopy.CaseDef(tree, pat, guard, removeTailReturn(body, ids)) + + case _ => + tree + } + } diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index 017c8d24fd..e02e02d975 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -9,6 +9,8 @@ import scala.tools.nsc.plugins._ import scala.tools.nsc.ast._ +import scala.collection.mutable.ListBuffer + /** * In methods marked @cps, explicitly name results of calls to other @cps methods */ @@ -46,10 +48,20 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // this would cause infinite recursion. But we could remove the // ValDef case here. - case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs0) => debuglog("transforming " + dd.symbol) atOwner(dd.symbol) { + val tailReturns = ListBuffer[Int]() + val rhs = removeTailReturn(rhs0, tailReturns) + // throw an error if there is a Return tree which is not in tail position + rhs0 foreach { + case r @ Return(_) => + if (!tailReturns.contains(r.id)) + unit.error(r.pos, "return expressions in CPS code must be in tail position") + case _ => /* do nothing */ + } + val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe)) debuglog("result "+rhs1) @@ -153,7 +165,6 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } } - def transExpr(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo): Tree = { transTailValue(tree, cpsA, cpsR) match { case (Nil, b) => b diff --git a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala index 449b0ca0bc..3848ab51b8 100644 --- a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala +++ b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala @@ -47,6 +47,10 @@ trait AnnotationCheckers { * before. If the implementing class cannot do the adaptiong, it * should return the tree unchanged.*/ def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree + + def pushAnnotationContext(tree: Tree): Unit = {} + + def popAnnotationContext(): Unit = {} } // Syncnote: Annotation checkers inaccessible to reflection, so no sync in var necessary. @@ -118,4 +122,14 @@ trait AnnotationCheckers { annotationCheckers.foldLeft(tree)((tree, checker) => checker.adaptAnnotations(tree, mode, pt)) } + + def pushAnnotationContext(tree: Tree): Unit = { + annotationCheckers.foreach(checker => + checker.pushAnnotationContext(tree)) + } + + def popAnnotationContext(): Unit = { + annotationCheckers.foreach(checker => + checker.popAnnotationContext()) + } } diff --git a/test/files/continuations-neg/ts-1681-nontail-return.check b/test/files/continuations-neg/ts-1681-nontail-return.check new file mode 100644 index 0000000000..8fe15f154b --- /dev/null +++ b/test/files/continuations-neg/ts-1681-nontail-return.check @@ -0,0 +1,4 @@ +ts-1681-nontail-return.scala:10: error: return expressions in CPS code must be in tail position + return v + ^ +one error found diff --git a/test/files/continuations-neg/ts-1681-nontail-return.scala b/test/files/continuations-neg/ts-1681-nontail-return.scala new file mode 100644 index 0000000000..af86ad304f --- /dev/null +++ b/test/files/continuations-neg/ts-1681-nontail-return.scala @@ -0,0 +1,18 @@ +import scala.util.continuations._ + +class ReturnRepro { + def s1: Int @cpsParam[Any, Unit] = shift { k => k(5) } + def caller = reset { println(p(3)) } + + def p(i: Int): Int @cpsParam[Unit, Any] = { + val v= s1 + 3 + if (v == 8) + return v + v + 1 + } +} + +object Test extends App { + val repro = new ReturnRepro + repro.caller +} diff --git a/test/files/continuations-run/ts-1681-2.check b/test/files/continuations-run/ts-1681-2.check new file mode 100644 index 0000000000..35b3c93780 --- /dev/null +++ b/test/files/continuations-run/ts-1681-2.check @@ -0,0 +1,5 @@ +8 +hi +8 +from try +8 diff --git a/test/files/continuations-run/ts-1681-2.scala b/test/files/continuations-run/ts-1681-2.scala new file mode 100644 index 0000000000..8a896dec2c --- /dev/null +++ b/test/files/continuations-run/ts-1681-2.scala @@ -0,0 +1,44 @@ +import scala.util.continuations._ + +class ReturnRepro { + def s1: Int @cps[Any] = shift { k => k(5) } + def caller = reset { println(p(3)) } + def caller2 = reset { println(p2(3)) } + def caller3 = reset { println(p3(3)) } + + def p(i: Int): Int @cps[Any] = { + val v= s1 + 3 + return v + } + + def p2(i: Int): Int @cps[Any] = { + val v = s1 + 3 + if (v > 0) { + println("hi") + return v + } else { + println("hi") + return 8 + } + } + + def p3(i: Int): Int @cps[Any] = { + val v = s1 + 3 + try { + println("from try") + return v + } catch { + case e: Exception => + println("from catch") + return 7 + } + } + +} + +object Test extends App { + val repro = new ReturnRepro + repro.caller + repro.caller2 + repro.caller3 +} diff --git a/test/files/continuations-run/ts-1681.check b/test/files/continuations-run/ts-1681.check new file mode 100644 index 0000000000..85176d8e66 --- /dev/null +++ b/test/files/continuations-run/ts-1681.check @@ -0,0 +1,3 @@ +8 +hi +8 diff --git a/test/files/continuations-run/ts-1681.scala b/test/files/continuations-run/ts-1681.scala new file mode 100644 index 0000000000..efb1abae15 --- /dev/null +++ b/test/files/continuations-run/ts-1681.scala @@ -0,0 +1,29 @@ +import scala.util.continuations._ + +class ReturnRepro { + def s1: Int @cpsParam[Any, Unit] = shift { k => k(5) } + def caller = reset { println(p(3)) } + def caller2 = reset { println(p2(3)) } + + def p(i: Int): Int @cpsParam[Unit, Any] = { + val v= s1 + 3 + return v + } + + def p2(i: Int): Int @cpsParam[Unit, Any] = { + val v = s1 + 3 + if (v > 0) { + println("hi") + return v + } else { + println("hi") + return 8 + } + } +} + +object Test extends App { + val repro = new ReturnRepro + repro.caller + repro.caller2 +} -- cgit v1.2.3 From cdfbe8e39fbbec00c969cd74f117ae410b98b40b Mon Sep 17 00:00:00 2001 From: phaller Date: Thu, 14 Jun 2012 17:49:13 +0200 Subject: Remove stray comment --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7e4f50ecd7..8f84431d52 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3987,7 +3987,6 @@ trait Typers extends Modes with Adaptations with Tags { ReturnWithoutTypeError(tree, enclMethod.owner) } else { context.enclMethod.returnsSeen = true - //TODO: also pass enclMethod.tree, so that adaptAnnotations can check whether return is in tail position pushAnnotationContext(tree) val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe) popAnnotationContext() -- cgit v1.2.3 From 5a8b937510094d4e92f37dba113c231ec2e69705 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 14 Jun 2012 17:08:11 -0700 Subject: Fix for java parser edge case. Empty statements are A-OK. Closes SI-5910. Review by @dragos. --- src/compiler/scala/tools/nsc/javac/JavaParsers.scala | 3 ++- test/files/pos/t5910.java | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/t5910.java diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index e2203e07b3..3797d32d8b 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -911,7 +911,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { buf ++= importDecl() while (in.token != EOF && in.token != RBRACE) { while (in.token == SEMI) in.nextToken - buf ++= typeDecl(modifiers(false)) + if (in.token != EOF) + buf ++= typeDecl(modifiers(false)) } accept(EOF) atPos(pos) { diff --git a/test/files/pos/t5910.java b/test/files/pos/t5910.java new file mode 100644 index 0000000000..e007a1fbb5 --- /dev/null +++ b/test/files/pos/t5910.java @@ -0,0 +1,2 @@ +class Foo { +};;;;;;; \ No newline at end of file -- cgit v1.2.3 From 52c08914ed751a2d9e39fa6365c4f2b3b9ee8d12 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 15 Jun 2012 07:45:45 -0700 Subject: Remove unused type parameter. There should probably be a warning about non-overriding methods which have unreferenced type parameters. --- src/library/scala/collection/mutable/ArrayOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/scala/collection/mutable/ArrayOps.scala b/src/library/scala/collection/mutable/ArrayOps.scala index 01636eb54e..7a595f211d 100644 --- a/src/library/scala/collection/mutable/ArrayOps.scala +++ b/src/library/scala/collection/mutable/ArrayOps.scala @@ -64,7 +64,7 @@ abstract class ArrayOps[T] extends ArrayLike[T, Array[T]] with CustomParalleliza * @param asTrav A function that converts elements of this array to rows - arrays of type `U`. * @return An array obtained by concatenating rows of this array. */ - def flatten[U, To](implicit asTrav: T => collection.Traversable[U], m: ClassTag[U]): Array[U] = { + def flatten[U](implicit asTrav: T => collection.Traversable[U], m: ClassTag[U]): Array[U] = { val b = Array.newBuilder[U] b.sizeHint(map{case is: collection.IndexedSeq[_] => is.size case _ => 0}.sum) for (xs <- this) -- cgit v1.2.3 From 51c92f02229098d0b402a65a72267f7a17984022 Mon Sep 17 00:00:00 2001 From: phaller Date: Fri, 15 Jun 2012 16:53:19 +0200 Subject: Replace context stack of AnnotationChecker with new mode for typing returns --- .../scala/tools/nsc/typechecker/Modes.scala | 4 ++++ .../scala/tools/nsc/typechecker/Typers.scala | 4 +--- .../tools/selectivecps/CPSAnnotationChecker.scala | 18 +++------------ .../plugin/scala/tools/selectivecps/CPSUtils.scala | 4 ++++ .../reflect/internal/AnnotationCheckers.scala | 14 ----------- test/files/continuations-run/ts-1681-3.check | 4 ++++ test/files/continuations-run/ts-1681-3.scala | 27 ++++++++++++++++++++++ 7 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 test/files/continuations-run/ts-1681-3.check create mode 100644 test/files/continuations-run/ts-1681-3.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Modes.scala b/src/compiler/scala/tools/nsc/typechecker/Modes.scala index 3eff5ef024..bde3ad98c9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Modes.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Modes.scala @@ -86,6 +86,10 @@ trait Modes { */ final val TYPEPATmode = 0x10000 + /** RETmode is set when we are typing a return expression. + */ + final val RETmode = 0x20000 + final private val StickyModes = EXPRmode | PATTERNmode | TYPEmode | ALTmode final def onlyStickyModes(mode: Int) = diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8f84431d52..1193d3013a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3987,9 +3987,7 @@ trait Typers extends Modes with Adaptations with Tags { ReturnWithoutTypeError(tree, enclMethod.owner) } else { context.enclMethod.returnsSeen = true - pushAnnotationContext(tree) - val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe) - popAnnotationContext() + val expr1: Tree = typed(expr, EXPRmode | BYVALmode | RETmode, restpt.tpe) // Warn about returning a value if no value can be returned. if (restpt.tpe.typeSymbol == UnitClass) { diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 574a76484c..5b8e9baa21 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -17,8 +17,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { * Checks whether @cps annotations conform */ object checker extends AnnotationChecker { - private var contextStack: List[Tree] = List() - private def addPlusMarker(tp: Type) = tp withAnnotation newPlusMarker() private def addMinusMarker(tp: Type) = tp withAnnotation newMinusMarker() @@ -27,12 +25,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { private def cleanPlusWith(tp: Type)(newAnnots: AnnotationInfo*) = cleanPlus(tp) withAnnotations newAnnots.toList - override def pushAnnotationContext(tree: Tree): Unit = - contextStack = tree :: contextStack - - override def popAnnotationContext(): Unit = - contextStack = contextStack.tail - /** Check annotations to decide whether tpe1 <:< tpe2 */ def annotationsConform(tpe1: Type, tpe2: Type): Boolean = { if (!cpsEnabled) return true @@ -124,11 +116,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { bounds } - private def inReturnContext(tree: Tree): Boolean = !contextStack.isEmpty && (contextStack.head match { - case Return(tree1) => tree1 == tree - case _ => false - }) - override def canAdaptAnnotations(tree: Tree, mode: Int, pt: Type): Boolean = { if (!cpsEnabled) return false vprintln("can adapt annotations? " + tree + " / " + tree.tpe + " / " + Integer.toHexString(mode) + " / " + pt) @@ -183,7 +170,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { vprintln("yes we can!! (byval)") return true } - } else if (inReturnContext(tree)) { + } else if ((mode & global.analyzer.RETmode) != 0) { vprintln("yes we can!! (return)") return true } @@ -199,6 +186,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { val patMode = (mode & global.analyzer.PATTERNmode) != 0 val exprMode = (mode & global.analyzer.EXPRmode) != 0 val byValMode = (mode & global.analyzer.BYVALmode) != 0 + val retMode = (mode & global.analyzer.RETmode) != 0 val annotsTree = cpsParamAnnotation(tree.tpe) val annotsExpected = cpsParamAnnotation(pt) @@ -225,7 +213,7 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { val res = tree modifyType addMinusMarker vprintln("adapted annotations (by val) of " + tree + " to " + res.tpe) res - } else if (inReturnContext(tree) && !hasPlusMarker(tree.tpe) && annotsTree.isEmpty && annotsExpected.nonEmpty) { + } else if (retMode && !hasPlusMarker(tree.tpe) && annotsTree.isEmpty && annotsExpected.nonEmpty) { // add a marker annotation that will make tree.tpe behave as pt, subtyping wise // tree will look like having no annotation val res = tree modifyType (_ withAnnotations List(newPlusMarker())) diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 1e4d9f21de..1f5ccd3d09 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -151,6 +151,10 @@ trait CPSUtils { case Block(stms, expr) => treeCopy.Block(tree, stms, removeTailReturn(expr, ids)) + case If(cond, r1 @ Return(thenExpr), r2 @ Return(elseExpr)) => + ids ++= Seq(r1.id, r2.id) + treeCopy.If(tree, cond, removeTailReturn(thenExpr, ids), removeTailReturn(elseExpr, ids)) + case If(cond, thenExpr, elseExpr) => treeCopy.If(tree, cond, removeTailReturn(thenExpr, ids), removeTailReturn(elseExpr, ids)) diff --git a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala index 3848ab51b8..449b0ca0bc 100644 --- a/src/reflect/scala/reflect/internal/AnnotationCheckers.scala +++ b/src/reflect/scala/reflect/internal/AnnotationCheckers.scala @@ -47,10 +47,6 @@ trait AnnotationCheckers { * before. If the implementing class cannot do the adaptiong, it * should return the tree unchanged.*/ def adaptAnnotations(tree: Tree, mode: Int, pt: Type): Tree = tree - - def pushAnnotationContext(tree: Tree): Unit = {} - - def popAnnotationContext(): Unit = {} } // Syncnote: Annotation checkers inaccessible to reflection, so no sync in var necessary. @@ -122,14 +118,4 @@ trait AnnotationCheckers { annotationCheckers.foldLeft(tree)((tree, checker) => checker.adaptAnnotations(tree, mode, pt)) } - - def pushAnnotationContext(tree: Tree): Unit = { - annotationCheckers.foreach(checker => - checker.pushAnnotationContext(tree)) - } - - def popAnnotationContext(): Unit = { - annotationCheckers.foreach(checker => - checker.popAnnotationContext()) - } } diff --git a/test/files/continuations-run/ts-1681-3.check b/test/files/continuations-run/ts-1681-3.check new file mode 100644 index 0000000000..71489f097c --- /dev/null +++ b/test/files/continuations-run/ts-1681-3.check @@ -0,0 +1,4 @@ +enter return expr +8 +hi +8 diff --git a/test/files/continuations-run/ts-1681-3.scala b/test/files/continuations-run/ts-1681-3.scala new file mode 100644 index 0000000000..62c547f5a2 --- /dev/null +++ b/test/files/continuations-run/ts-1681-3.scala @@ -0,0 +1,27 @@ +import scala.util.continuations._ + +class ReturnRepro { + def s1: Int @cpsParam[Any, Unit] = shift { k => k(5) } + def caller = reset { println(p(3)) } + def caller2 = reset { println(p2(3)) } + + def p(i: Int): Int @cpsParam[Unit, Any] = { + val v= s1 + 3 + return { println("enter return expr"); v } + } + + def p2(i: Int): Int @cpsParam[Unit, Any] = { + val v = s1 + 3 + if (v > 0) { + return { println("hi"); v } + } else { + return { println("hi"); 8 } + } + } +} + +object Test extends App { + val repro = new ReturnRepro + repro.caller + repro.caller2 +} -- cgit v1.2.3 From 0ada0706746c9c603bf5bc8a0e6780e5783297cf Mon Sep 17 00:00:00 2001 From: phaller Date: Fri, 15 Jun 2012 17:04:54 +0200 Subject: Remove unneeded use of Tree#id --- .../plugin/scala/tools/selectivecps/CPSUtils.scala | 28 +++++++++++----------- .../tools/selectivecps/SelectiveANFTransform.scala | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 1f5ccd3d09..765cde5a81 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -138,38 +138,38 @@ trait CPSUtils { } def isTailReturn(retExpr: Tree, body: Tree): Boolean = { - val removedIds = ListBuffer[Int]() - removeTailReturn(body, removedIds) - removedIds contains retExpr.id + val removed = ListBuffer[Tree]() + removeTailReturn(body, removed) + removed contains retExpr } - def removeTailReturn(tree: Tree, ids: ListBuffer[Int]): Tree = tree match { + def removeTailReturn(tree: Tree, removed: ListBuffer[Tree]): Tree = tree match { case Block(stms, r @ Return(expr)) => - ids += r.id + removed += r treeCopy.Block(tree, stms, expr) case Block(stms, expr) => - treeCopy.Block(tree, stms, removeTailReturn(expr, ids)) + treeCopy.Block(tree, stms, removeTailReturn(expr, removed)) case If(cond, r1 @ Return(thenExpr), r2 @ Return(elseExpr)) => - ids ++= Seq(r1.id, r2.id) - treeCopy.If(tree, cond, removeTailReturn(thenExpr, ids), removeTailReturn(elseExpr, ids)) + removed ++= Seq(r1, r2) + treeCopy.If(tree, cond, removeTailReturn(thenExpr, removed), removeTailReturn(elseExpr, removed)) case If(cond, thenExpr, elseExpr) => - treeCopy.If(tree, cond, removeTailReturn(thenExpr, ids), removeTailReturn(elseExpr, ids)) + treeCopy.If(tree, cond, removeTailReturn(thenExpr, removed), removeTailReturn(elseExpr, removed)) case Try(block, catches, finalizer) => treeCopy.Try(tree, - removeTailReturn(block, ids), - (catches map (t => removeTailReturn(t, ids))).asInstanceOf[List[CaseDef]], - removeTailReturn(finalizer, ids)) + removeTailReturn(block, removed), + (catches map (t => removeTailReturn(t, removed))).asInstanceOf[List[CaseDef]], + removeTailReturn(finalizer, removed)) case CaseDef(pat, guard, r @ Return(expr)) => - ids += r.id + removed += r treeCopy.CaseDef(tree, pat, guard, expr) case CaseDef(pat, guard, body) => - treeCopy.CaseDef(tree, pat, guard, removeTailReturn(body, ids)) + treeCopy.CaseDef(tree, pat, guard, removeTailReturn(body, removed)) case _ => tree diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index e02e02d975..fe465aad0d 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -52,12 +52,12 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with debuglog("transforming " + dd.symbol) atOwner(dd.symbol) { - val tailReturns = ListBuffer[Int]() + val tailReturns = ListBuffer[Tree]() val rhs = removeTailReturn(rhs0, tailReturns) // throw an error if there is a Return tree which is not in tail position rhs0 foreach { case r @ Return(_) => - if (!tailReturns.contains(r.id)) + if (!tailReturns.contains(r)) unit.error(r.pos, "return expressions in CPS code must be in tail position") case _ => /* do nothing */ } -- cgit v1.2.3 From ebf00a8a27e3e86df7c7a95f5d8dd9c4f57fb755 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 15 Jun 2012 20:55:29 +0200 Subject: exposes extra tests for symbols as discussed on reflection meeting review by @odersky --- src/reflect/scala/reflect/api/Symbols.scala | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 1d266dc778..b2eb598cc0 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -116,11 +116,28 @@ trait Symbols extends base.Symbols { self: Universe => */ def isValue: Boolean + /** Does this symbol denote a stable value? */ + def isStable: Boolean + /** Does this symbol represent a mutable value? * If yes, `isTerm` and `isValue` are also guaranteed to be true. */ def isVariable: Boolean + /** Does this symbol represent a getter or a setter? + */ + def isAccessor: Boolean + + /** Does this symbol represent a getter of a field? + * If yes, `isTerm` and `isMethod` are also guaranteed to be true. + */ + def isGetter: Boolean + + /** Does this symbol represent a setter of a field? + * If yes, `isTerm` and `isMethod` are also guaranteed to be true. + */ + def isSetter: Boolean + /** Does this symbol represent the definition of a package? * If yes, `isTerm` is also guaranteed to be true. */ -- cgit v1.2.3 From 2d3b6bd321724c565fd458a2bbffb4ad211c54ff Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 17 Jun 2012 11:47:11 +0200 Subject: SI-5617 Better error message for "x overrides nothing". "It looks like you're trying to override a method", notes Clippy. --- .../scala/tools/nsc/typechecker/RefChecks.scala | 11 ++++++++++- test/files/neg/t5429.check | 20 +++++++++++++++----- test/files/neg/t5617.check | 8 ++++++++ test/files/neg/t5617.scala | 14 ++++++++++++++ 4 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 test/files/neg/t5617.check create mode 100644 test/files/neg/t5617.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 26cf246ed7..119bb0852c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -766,7 +766,16 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R for (member <- clazz.info.decls) if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) { // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG - unit.error(member.pos, member.toString() + " overrides nothing"); + + val nonMatching: List[Symbol] = clazz.info.member(member.name).alternatives.filterNot(_.owner == clazz).filterNot(_.isFinal) + def issueError(suffix: String) = unit.error(member.pos, member.toString() + " overrides nothing" + suffix); + nonMatching match { + case Nil => + issueError("") + case ms => + val superSigs = ms.map(m => m.defStringSeenAs(clazz.tpe memberType m)).mkString("\n") + issueError(s".\nNote: the super classes of ${member.owner} contain the following, non final members named ${member.name}:\n${superSigs}") + } member resetFlag (OVERRIDE | ABSOVERRIDE) // Any Override } } diff --git a/test/files/neg/t5429.check b/test/files/neg/t5429.check index 1b89c59587..4350696bc8 100644 --- a/test/files/neg/t5429.check +++ b/test/files/neg/t5429.check @@ -46,14 +46,18 @@ t5429.scala:38: error: overriding method emptyArg in class A of type ()Int; object emptyArg has incompatible type override object emptyArg // fail ^ -t5429.scala:39: error: object oneArg overrides nothing +t5429.scala:39: error: object oneArg overrides nothing. +Note: the super classes of class C contain the following, non final members named oneArg: +def oneArg(x: String): Int override object oneArg // fail ^ t5429.scala:43: error: overriding lazy value lazyvalue in class A0 of type Any; object lazyvalue must be declared lazy to override a concrete lazy value override object lazyvalue // !!! this fails, but should succeed (lazy over lazy) ^ -t5429.scala:46: error: object oneArg overrides nothing +t5429.scala:46: error: object oneArg overrides nothing. +Note: the super classes of class C0 contain the following, non final members named oneArg: +def oneArg(x: String): Any override object oneArg // fail ^ t5429.scala:50: error: overriding value value in class A of type Int; @@ -76,7 +80,9 @@ t5429.scala:58: error: overriding lazy value lazyvalue in class A0 of type Any; value lazyvalue must be declared lazy to override a concrete lazy value override val lazyvalue = 0 // fail (non-lazy) ^ -t5429.scala:61: error: value oneArg overrides nothing +t5429.scala:61: error: value oneArg overrides nothing. +Note: the super classes of class D0 contain the following, non final members named oneArg: +def oneArg(x: String): Any override val oneArg = 15 // fail ^ t5429.scala:65: error: overriding value value in class A of type Int; @@ -103,7 +109,9 @@ t5429.scala:73: error: overriding lazy value lazyvalue in class A0 of type Any; method lazyvalue needs to be a stable, immutable value override def lazyvalue = 2 // fail ^ -t5429.scala:76: error: method oneArg overrides nothing +t5429.scala:76: error: method oneArg overrides nothing. +Note: the super classes of class E0 contain the following, non final members named oneArg: +def oneArg(x: String): Any override def oneArg = 15 // fail ^ t5429.scala:80: error: overriding value value in class A of type Int; @@ -126,7 +134,9 @@ t5429.scala:87: error: overriding value value in class A0 of type Any; lazy value value cannot override a concrete non-lazy value override lazy val value = 0 // fail (strict over lazy) ^ -t5429.scala:91: error: value oneArg overrides nothing +t5429.scala:91: error: value oneArg overrides nothing. +Note: the super classes of class F0 contain the following, non final members named oneArg: +def oneArg(x: String): Any override lazy val oneArg = 15 // fail ^ 34 errors found diff --git a/test/files/neg/t5617.check b/test/files/neg/t5617.check new file mode 100644 index 0000000000..79cc3a1e32 --- /dev/null +++ b/test/files/neg/t5617.check @@ -0,0 +1,8 @@ +t5617.scala:12: error: method foo overrides nothing. +Note: the super classes of trait C contain the following, non final members named foo: +def foo(u: Unit): Int +def foo(x: Boolean): Int +def foo(i: Int)(b: String): Int + override def foo(s: String): Int + ^ +one error found diff --git a/test/files/neg/t5617.scala b/test/files/neg/t5617.scala new file mode 100644 index 0000000000..41541b5e90 --- /dev/null +++ b/test/files/neg/t5617.scala @@ -0,0 +1,14 @@ +trait A { + def foo(i: Int)(b: String): Int + def foo(u: Unit): Int // not reported + def foo(x: Float): Int // not reported +} +trait B[X] extends A { + def foo(x: X): Int + def foo(u: Unit): Int + final def foo(x: Float): Int = 0 // not reported +} +trait C extends B[Boolean] { + override def foo(s: String): Int + val foo = 0 // not reported +} -- cgit v1.2.3 From 72ee06de4cc0b8c12acf07c892302a3043a1e578 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 10 Jun 2012 17:51:21 +0200 Subject: SI-4989 Reject super.x if an intermediate class declares x abstract. This is in line with Java's treatment. Without this, an AbstractMethodError is thrown at runtime. --- .../tools/nsc/typechecker/SuperAccessors.scala | 8 +++ test/files/neg/t4989.check | 7 +++ test/files/neg/t4989.scala | 68 ++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 test/files/neg/t4989.check create mode 100644 test/files/neg/t4989.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index daae69590f..f67cec730b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -124,7 +124,15 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT !(member.isAbstractOverride && member.isIncompleteIn(clazz))) unit.error(sel.pos, ""+sym.fullLocationString+" is accessed from super. It may not be abstract "+ "unless it is overridden by a member declared `abstract' and `override'"); + } else if (mix == tpnme.EMPTY && !sym.owner.isTrait){ + // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract. + val intermediateClasses = clazz.info.baseClasses.tail.takeWhile(_ != sym.owner) + intermediateClasses.map(sym.overridingSymbol).find(s => s.isDeferred && !s.isAbstractOverride && !s.owner.isTrait).foreach { + absSym => + unit.error(sel.pos, s"${sym.fullLocationString} cannot be directly accessed from ${clazz} because ${absSym.owner} redeclares it as abstract") + } } + if (name.isTermName && mix == tpnme.EMPTY && (clazz.isTrait || clazz != currentClass || !validCurrentOwner)) ensureAccessor(sel) else sel diff --git a/test/files/neg/t4989.check b/test/files/neg/t4989.check new file mode 100644 index 0000000000..814507fc3f --- /dev/null +++ b/test/files/neg/t4989.check @@ -0,0 +1,7 @@ +t4989.scala:14: error: method print in class A cannot be directly accessed from class C because class B redeclares it as abstract + override def print(): String = super.print() // should be an error + ^ +t4989.scala:18: error: method print in class A cannot be directly accessed from trait T because class B redeclares it as abstract + override def print(): String = super.print() // should be an error + ^ +two errors found diff --git a/test/files/neg/t4989.scala b/test/files/neg/t4989.scala new file mode 100644 index 0000000000..e7ff80ed74 --- /dev/null +++ b/test/files/neg/t4989.scala @@ -0,0 +1,68 @@ +abstract class A0 { + def print(): String +} + +class A extends A0 { + def print(): String = "A" +} + +abstract class B extends A { + def print() : String +} + +class C extends B { + override def print(): String = super.print() // should be an error +} + +trait T extends B { + override def print(): String = super.print() // should be an error +} + +class D extends A { + override def print(): String = super.print() // okay +} + + +// it's okay do this when trait are in the mix, as the +// suitable super accessor methods are used. +object ConcreteMethodAndIntermediaryAreTraits { + trait T1 { + def print(): String = "" + } + + trait T2 extends T1 { + def print(): String + } + + class C3 extends T2 { + def print(): String = super.print() // okay + } +} + +object IntermediaryIsTrait { + class T1 { + def print(): String = "" + } + + trait T2 extends T1 { + def print(): String + } + + class C3 extends T2 { + override def print(): String = super.print() // okay + } +} + +object ConcreteMethodIsTrait { + trait T1 { + def print(): String = "" + } + + abstract class T2 extends T1 { + def print(): String + } + + class C3 extends T2 { + override def print(): String = super.print() // okay + } +} -- cgit v1.2.3 From 6aa5762fa0333625ec93378e2147649a8bafde34 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sun, 17 Jun 2012 23:23:49 +0200 Subject: SI-4842 Forbid access to in-construction this in self-constructor args The check was already in place for direct calls to the super constructor. Without this additional check, ExplicitOuter crashes, as it doesn't create an $outer pointer for the constructor-arg scoped inner object, but expects one to exist when transforming the Outer.this reference. --- .../tools/nsc/typechecker/ContextErrors.scala | 8 ++- .../scala/tools/nsc/typechecker/Typers.scala | 62 ++++++++++++++++------ test/files/neg/t4842a.check | 4 ++ test/files/neg/t4842a.scala | 3 ++ test/files/neg/t4842b.check | 4 ++ test/files/neg/t4842b.scala | 3 ++ test/files/pos/t4842.scala | 26 +++++++++ 7 files changed, 94 insertions(+), 16 deletions(-) create mode 100644 test/files/neg/t4842a.check create mode 100644 test/files/neg/t4842a.scala create mode 100644 test/files/neg/t4842b.check create mode 100644 test/files/neg/t4842b.scala create mode 100644 test/files/pos/t4842.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index bcf2ba6b71..fcb73f5947 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -222,7 +222,13 @@ trait ContextErrors { NormalTypeError(tree, "super constructor cannot be passed a self reference unless parameter is declared by-name") def SuperConstrArgsThisReferenceError(tree: Tree) = - NormalTypeError(tree, "super constructor arguments cannot reference unconstructed `this`") + ConstrArgsThisReferenceError("super", tree) + + def SelfConstrArgsThisReferenceError(tree: Tree) = + ConstrArgsThisReferenceError("self", tree) + + private def ConstrArgsThisReferenceError(prefix: String, tree: Tree) = + NormalTypeError(tree, s"$prefix constructor arguments cannot reference unconstructed `this`") def TooManyArgumentListsForConstructor(tree: Tree) = { issueNormalTypeError(tree, "too many argument lists for constructor invocation") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2bdae4164a..b58a7832ac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1841,16 +1841,13 @@ trait Typers extends Modes with Adaptations with Tags { val pending = ListBuffer[AbsTypeError]() // an object cannot be allowed to pass a reference to itself to a superconstructor // because of initialization issues; bug #473 - for (arg <- superArgs ; tree <- arg) { - val sym = tree.symbol - if (sym != null && (sym.info.baseClasses contains clazz)) { - if (sym.isModule) - pending += SuperConstrReferenceError(tree) - tree match { - case This(qual) => - pending += SuperConstrArgsThisReferenceError(tree) - case _ => () - } + foreachSubTreeBoundTo(superArgs, clazz) { tree => + if (tree.symbol.isModule) + pending += SuperConstrReferenceError(tree) + tree match { + case This(qual) => + pending += SuperConstrArgsThisReferenceError(tree) + case _ => () } } @@ -1884,7 +1881,39 @@ trait Typers extends Modes with Adaptations with Tags { pending.foreach(ErrorUtils.issueTypeError) } - /** Check if a structurally defined method violates implementation restrictions. + // Check for SI-4842. + private def checkSelfConstructorArgs(ddef: DefDef, clazz: Symbol) { + val pending = ListBuffer[AbsTypeError]() + ddef.rhs match { + case Block(stats, expr) => + val selfConstructorCall = stats.headOption.getOrElse(expr) + foreachSubTreeBoundTo(List(selfConstructorCall), clazz) { + case tree @ This(qual) => + pending += SelfConstrArgsThisReferenceError(tree) + case _ => () + } + case _ => + } + pending.foreach(ErrorUtils.issueTypeError) + } + + /** + * Run the provided function for each sub tree of `trees` that + * are bound to a symbol with `clazz` as a base class. + * + * @param f This function can assume that `tree.symbol` is non null + */ + private def foreachSubTreeBoundTo[A](trees: List[Tree], clazz: Symbol)(f: Tree => Unit): Unit = + for { + tree <- trees + subTree <- tree + } { + val sym = subTree.symbol + if (sym != null && sym.info.baseClasses.contains(clazz)) + f(subTree) + } + + /** Check if a structurally defined method violates implementation restrictions. * A method cannot be called if it is a non-private member of a refinement type * and if its parameter's types are any of: * - the self-type of the refinement @@ -2002,11 +2031,14 @@ trait Typers extends Modes with Adaptations with Tags { transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe) } - if (meth.isPrimaryConstructor && meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) { - // At this point in AnyVal there is no supercall, which will blow up - // in computeParamAliases; there's nothing to be computed for Anyval anyway. + if (meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) { + // At this point in AnyVal there is no supercall, which will blow up + // in computeParamAliases; there's nothing to be computed for Anyval anyway. + if (meth.isPrimaryConstructor) computeParamAliases(meth.owner, vparamss1, rhs1) - } + else + checkSelfConstructorArgs(ddef, meth.owner) + } if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass) rhs1 = checkDead(rhs1) diff --git a/test/files/neg/t4842a.check b/test/files/neg/t4842a.check new file mode 100644 index 0000000000..39d77bfc48 --- /dev/null +++ b/test/files/neg/t4842a.check @@ -0,0 +1,4 @@ +t4842a.scala:2: error: self constructor arguments cannot reference unconstructed `this` + def this(x: Int) = this(new { println(Foo.this)}) // error + ^ +one error found diff --git a/test/files/neg/t4842a.scala b/test/files/neg/t4842a.scala new file mode 100644 index 0000000000..78360effb4 --- /dev/null +++ b/test/files/neg/t4842a.scala @@ -0,0 +1,3 @@ +class Foo (x: AnyRef) { + def this(x: Int) = this(new { println(Foo.this)}) // error +} diff --git a/test/files/neg/t4842b.check b/test/files/neg/t4842b.check new file mode 100644 index 0000000000..c7ccd5e059 --- /dev/null +++ b/test/files/neg/t4842b.check @@ -0,0 +1,4 @@ +t4842b.scala:2: error: self constructor arguments cannot reference unconstructed `this` + def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error + ^ +one error found diff --git a/test/files/neg/t4842b.scala b/test/files/neg/t4842b.scala new file mode 100644 index 0000000000..a7996cc061 --- /dev/null +++ b/test/files/neg/t4842b.scala @@ -0,0 +1,3 @@ +class TypeArg[X](val x: X)(a: AnyRef) { + def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error +} diff --git a/test/files/pos/t4842.scala b/test/files/pos/t4842.scala new file mode 100644 index 0000000000..17ff684833 --- /dev/null +++ b/test/files/pos/t4842.scala @@ -0,0 +1,26 @@ +class Foo (x: AnyRef) { + def this() = { + this(new { } ) // okay + } +} + + +class Blerg (x: AnyRef) { + def this() = { + this(new { class Bar { println(Bar.this); new { println(Bar.this) } }; new Bar } ) // okay + } +} + + +class Outer { + class Inner (x: AnyRef) { + def this() = { + this(new { class Bar { println(Bar.this); new { println(Bar.this) } }; new Bar } ) // okay + } + + def this(x: Boolean) = { + this(new { println(Outer.this) } ) // okay + } + } +} + -- cgit v1.2.3 From ec2082c616f1b4be2772dd2e786166e11395848c Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Mon, 18 Jun 2012 13:19:37 +0200 Subject: Revert "Documented SyncVar" This reverts commit 556065151c10685240dd907691dab5e094c87efc. --- src/library/scala/concurrent/SyncVar.scala | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/library/scala/concurrent/SyncVar.scala b/src/library/scala/concurrent/SyncVar.scala index 292014706d..5a6d95c2ed 100644 --- a/src/library/scala/concurrent/SyncVar.scala +++ b/src/library/scala/concurrent/SyncVar.scala @@ -53,8 +53,6 @@ class SyncVar[A] { value } - /** Waits for this SyncVar to become defined and returns - * the result */ def take(): A = synchronized { try get finally unsetVal() @@ -66,8 +64,7 @@ class SyncVar[A] { * the SyncVar. * * @param timeout the amount of milliseconds to wait, 0 means forever - * @return the value or a throws an exception if the timeout occurs - * @throws NoSuchElementException on timeout + * @return `None` if variable is undefined after `timeout`, `Some(value)` otherwise */ def take(timeout: Long): A = synchronized { try get(timeout).get @@ -75,28 +72,25 @@ class SyncVar[A] { } // TODO: this method should be private - // [Heather] the reason why: it doesn't take into consideration + // [Heather] the reason why: it doesn't take into consideration // whether or not the SyncVar is already defined. So, set has been // deprecated in order to eventually be able to make "setting" private @deprecated("Use `put` instead, as `set` is potentionally error-prone", "2.10.0") def set(x: A): Unit = setVal(x) - /** Places a value in the SyncVar. If the SyncVar already has a stored value, - * it waits until another thread takes it */ def put(x: A): Unit = synchronized { while (isDefined) wait() setVal(x) } - /** Checks whether a value is stored in the synchronized variable */ def isSet: Boolean = synchronized { isDefined } // TODO: this method should be private - // [Heather] the reason why: it doesn't take into consideration + // [Heather] the reason why: it doesn't take into consideration // whether or not the SyncVar is already defined. So, unset has been - // deprecated in order to eventually be able to make "unsetting" private + // deprecated in order to eventually be able to make "unsetting" private @deprecated("Use `take` instead, as `unset` is potentionally error-prone", "2.10.0") def unset(): Unit = synchronized { isDefined = false @@ -104,7 +98,7 @@ class SyncVar[A] { notifyAll() } - // `setVal` exists so as to retroactively deprecate `set` without + // `setVal` exists so as to retroactively deprecate `set` without // deprecation warnings where we use `set` internally. The // implementation of `set` was moved to `setVal` to achieve this private def setVal(x: A): Unit = synchronized { @@ -113,13 +107,13 @@ class SyncVar[A] { notifyAll() } - // `unsetVal` exists so as to retroactively deprecate `unset` without + // `unsetVal` exists so as to retroactively deprecate `unset` without // deprecation warnings where we use `unset` internally. The // implementation of `unset` was moved to `unsetVal` to achieve this private def unsetVal(): Unit = synchronized { isDefined = false value = None - notifyAll() + notifyAll() } } -- cgit v1.2.3 From 55348f7a44f1ac52232bcceedce8d0e9c036f8d8 Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Mon, 18 Jun 2012 13:19:41 +0200 Subject: Revert "Scaladoc class diagrams part 2" This reverts commit fba4098ca592bdd7d6609017daa4a08f274f35ec. --- .../scala/tools/nsc/doc/html/HtmlFactory.scala | 12 - .../scala/tools/nsc/doc/html/page/Template.scala | 64 +-- .../doc/html/page/diagram/DiagramGenerator.scala | 53 --- .../html/page/diagram/DotDiagramGenerator.scala | 464 --------------------- .../nsc/doc/html/page/diagram/DotRunner.scala | 227 ---------- .../nsc/doc/html/resource/lib/class_diagram.png | Bin 3910 -> 0 bytes .../tools/nsc/doc/html/resource/lib/diagrams.css | 135 ------ .../tools/nsc/doc/html/resource/lib/diagrams.js | 324 -------------- .../nsc/doc/html/resource/lib/modernizr.custom.js | 4 - .../nsc/doc/html/resource/lib/object_diagram.png | Bin 3903 -> 0 bytes .../tools/nsc/doc/html/resource/lib/raphael-min.js | 10 - .../tools/nsc/doc/html/resource/lib/template.css | 12 +- .../tools/nsc/doc/html/resource/lib/template.js | 18 +- .../nsc/doc/html/resource/lib/trait_diagram.png | Bin 3882 -> 0 bytes 14 files changed, 22 insertions(+), 1301 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramGenerator.scala delete mode 100644 src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala delete mode 100644 src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala delete mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/class_diagram.png delete mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css delete mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.js delete mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/modernizr.custom.js delete mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/object_diagram.png delete mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/raphael-min.js delete mode 100644 src/compiler/scala/tools/nsc/doc/html/resource/lib/trait_diagram.png diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala index 84d703aa58..914824d523 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala @@ -12,8 +12,6 @@ import java.io.{ File => JFile } import io.{ Streamable, Directory } import scala.collection._ -import html.page.diagram.DiagramGenerator - /** A class that can generate Scaladoc sites to some fixed root folder. * @author David Bernard * @author Gilles Dubochet */ @@ -31,27 +29,21 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { "jquery.js", "jquery.layout.js", "scheduler.js", - "diagrams.js", "template.js", "tools.tooltip.js", - "modernizr.custom.js", "index.css", "ref-index.css", "template.css", - "diagrams.css", "class.png", "class_big.png", - "class_diagram.png", "object.png", "object_big.png", - "object_diagram.png", "package.png", "package_big.png", "trait.png", "trait_big.png", - "trait_diagram.png", "class_to_object_big.png", "object_to_class_big.png", @@ -113,8 +105,6 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { finally out.close() } - DiagramGenerator.initialize(universe.settings) - libResources foreach (s => copyResource("lib/" + s)) new page.Index(universe, index) writeFor this @@ -125,8 +115,6 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) { for (letter <- index.firstLetterIndex) { new html.page.ReferenceIndex(letter._1, index, universe) writeFor this } - - DiagramGenerator.cleanup() } def writeTemplates(writeForThis: HtmlPage => Unit) { diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index 262e91b510..ce0e6b7856 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -8,16 +8,10 @@ package doc package html package page -import model._ -import model.diagram._ -import diagram._ - import scala.xml.{ NodeSeq, Text, UnprefixedAttribute } import language.postfixOps import model._ -import model.diagram._ -import diagram._ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { @@ -36,22 +30,10 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage val headers = - - + - { if (universe.settings.docDiagrams.isSetByUser) { - - - } else NodeSeq.Empty } - val valueMembers = @@ -634,19 +616,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage case _ => NodeSeq.Empty } - val typeHierarchy = if (s.docDiagrams.isSetByUser) mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.inheritanceDiagram.isDefined => - makeDiagramHtml(dtpl, dtpl.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram") - case _ => NodeSeq.Empty - } else NodeSeq.Empty // diagrams not generated - - val contentHierarchy = if (s.docDiagrams.isSetByUser) mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.contentDiagram.isDefined => - makeDiagramHtml(dtpl, dtpl.contentDiagram, "Content Hierarchy", "content-diagram") - case _ => NodeSeq.Empty - } else NodeSeq.Empty // diagrams not generated - - memberComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy + memberComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses } def kindToString(mbr: MemberEntity): String = { @@ -952,40 +922,10 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage xml.Text(ub.typeParamName + " is a subclass of " + ub.upperBound.name + " (" + ub.typeParamName + " <: ") ++ typeToHtml(ub.upperBound, true) ++ xml.Text(")") } - - def makeDiagramHtml(tpl: DocTemplateEntity, diagram: Option[Diagram], description: String, id: String) = { - val s = universe.settings - val diagramSvg = Template.diagramGenerator(s).generate(diagram.get, tpl, this) - if (diagramSvg != NodeSeq.Empty) { -
- { description } - Learn more about scaladoc diagrams -
{ - diagramSvg - }
-
- } else NodeSeq.Empty - } } object Template { - private var _diagramGenerator: DiagramGenerator = null - /** - * Get a diagram generator instance based on the given settings. - * NOTE: This function does not take into account changing settings, so the first setting will initialize the diagram - * generator and the other settings will be ignored! - */ - def diagramGenerator(settings: doc.Settings): DiagramGenerator = { - if (_diagramGenerator == null) { - // for now, the diagram generator is hard-coded to use GraphViz, in the future we should use a more general - // approach. Also the DiagramGenerator should be stateless and a single instance should be used to generate all - // the diagrams - _diagramGenerator = new DotDiagramGenerator(settings) - } - _diagramGenerator - } - def isImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined def isShadowedImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined && mbr.byConversion.get.isShadowed def isAmbiguousImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined && mbr.byConversion.get.isAmbiguous diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramGenerator.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramGenerator.scala deleted file mode 100644 index 61c1819d11..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramGenerator.scala +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @author Damien Obrist - * @author Vlad Ureche - */ -package scala.tools.nsc -package doc -package html -package page -package diagram - -import scala.xml.NodeSeq -import scala.tools.nsc.doc.html.HtmlPage -import scala.tools.nsc.doc.model.diagram.Diagram -import scala.tools.nsc.doc.model.DocTemplateEntity - -trait DiagramGenerator { - - /** - * Generates a visualization of the internal representation - * of a diagram. - * - * @param d The model of the diagram - * @param p The page the diagram will be embedded in (needed for link generation) - * @return The HTML to be embedded in the Scaladoc page - */ - def generate(d: Diagram, t: DocTemplateEntity, p: HtmlPage):NodeSeq -} - -object DiagramGenerator { - - // TODO: This is tailored towards the dot generator, since it's the only generator. In the future it should be more - // general. - - private[this] var dotRunner: DotRunner = null - private[this] var settings: doc.Settings = null - - def initialize(s: doc.Settings) = - settings = s - - def getDotRunner() = { - if (dotRunner == null) - dotRunner = new DotRunner(settings) - dotRunner - } - - def cleanup() = { - DiagramStats.printStats(settings) - if (dotRunner != null) { - dotRunner.cleanup() - dotRunner = null - } - } -} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala deleted file mode 100644 index 4291fc6810..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala +++ /dev/null @@ -1,464 +0,0 @@ -/** - * @author Damien Obrist - * @author Vlad Ureche - */ -package scala.tools.nsc -package doc -package html -package page -package diagram - -import scala.xml.{NodeSeq, XML, PrefixedAttribute, Elem, MetaData, Null, UnprefixedAttribute} -import scala.collection.immutable._ -import javax.xml.parsers.SAXParser -import model._ -import model.diagram._ - -class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { - - // the page where the diagram will be embedded - private var page: HtmlPage = null - // path to the "lib" folder relative to the page - private var pathToLib: String = null - // maps nodes to unique indices - private var node2Index: Map[Node, Int] = null - // maps an index to its corresponding node - private var index2Node: Map[Int, Node] = null - // true if the current diagram is a class diagram - private var isClassDiagram = false - // incoming implicit nodes (needed for determining the CSS class of a node) - private var incomingImplicitNodes: List[Node] = List() - // the suffix used when there are two many classes to show - private final val MultiSuffix = " classes/traits" - // used to generate unique node and edge ids (i.e. avoid conflicts with multiple diagrams) - private var counter = 0 - - def generate(diagram: Diagram, template: DocTemplateEntity, page: HtmlPage):NodeSeq = { - counter = counter + 1; - this.page = page - pathToLib = "../" * (page.templateToPath(template).size - 1) + "lib/" - val dot = generateDot(diagram) - generateSVG(dot, template) - } - - /** - * Generates a dot string for a given diagram. - */ - private def generateDot(d: Diagram) = { - // inheritance nodes (all nodes except thisNode and implicit nodes) - var nodes: List[Node] = null - // inheritance edges (all edges except implicit edges) - var edges: List[(Node, List[Node])] = null - - // timing - var tDot = -System.currentTimeMillis - - // variables specific to class diagrams: - // current node of a class diagram - var thisNode:Node = null - var subClasses = List[Node]() - var superClasses = List[Node]() - var incomingImplicits = List[Node]() - var outgoingImplicits = List[Node]() - var subClassesTooltip: Option[String] = None - var superClassesTooltip: Option[String] = None - var incomingImplicitsTooltip: Option[String] = None - var outgoingImplicitsTooltip: Option[String] = None - isClassDiagram = false - - d match { - case ClassDiagram(_thisNode, _superClasses, _subClasses, _incomingImplicits, _outgoingImplicits) => - - def textTypeEntity(text: String) = - new TypeEntity { - val name = text - def refEntity: SortedMap[Int, (TemplateEntity, Int)] = SortedMap() - } - - // it seems dot chokes on node names over 8000 chars, so let's limit the size of the string - // conservatively, we'll limit at 4000, to be sure: - def limitSize(str: String) = if (str.length > 4000) str.substring(0, 3996) + " ..." else str - - // avoid overcrowding the diagram: - // if there are too many super / sub / implicit nodes, represent - // them by on node with a corresponding tooltip - superClasses = if (_superClasses.length > settings.docDiagramsMaxNormalClasses.value) { - superClassesTooltip = Some(limitSize(_superClasses.map(_.tpe.name).mkString(", "))) - List(NormalNode(textTypeEntity(_superClasses.length + MultiSuffix), None)) - } else _superClasses - - subClasses = if (_subClasses.length > settings.docDiagramsMaxNormalClasses.value) { - subClassesTooltip = Some(limitSize(_subClasses.map(_.tpe.name).mkString(", "))) - List(NormalNode(textTypeEntity(_subClasses.length + MultiSuffix), None)) - } else _subClasses - - incomingImplicits = if (_incomingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) { - incomingImplicitsTooltip = Some(limitSize(_incomingImplicits.map(_.tpe.name).mkString(", "))) - List(ImplicitNode(textTypeEntity(_incomingImplicits.length + MultiSuffix), None)) - } else _incomingImplicits - - outgoingImplicits = if (_outgoingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) { - outgoingImplicitsTooltip = Some(limitSize(_outgoingImplicits.map(_.tpe.name).mkString(", "))) - List(ImplicitNode(textTypeEntity(_outgoingImplicits.length + MultiSuffix), None)) - } else _outgoingImplicits - - thisNode = _thisNode - nodes = List() - edges = (thisNode -> superClasses) :: subClasses.map(_ -> List(thisNode)) - node2Index = (thisNode::subClasses:::superClasses:::incomingImplicits:::outgoingImplicits).zipWithIndex.toMap - isClassDiagram = true - incomingImplicitNodes = incomingImplicits - case _ => - nodes = d.nodes - edges = d.edges - node2Index = d.nodes.zipWithIndex.toMap - incomingImplicitNodes = List() - } - index2Node = node2Index map {_.swap} - - val implicitsDot = { - if (!isClassDiagram) "" - else { - // dot cluster containing thisNode - val thisCluster = "subgraph clusterThis {\n" + - "style=\"invis\"\n" + - node2Dot(thisNode, None) + - "}" - // dot cluster containing incoming implicit nodes, if any - val incomingCluster = { - if(incomingImplicits.isEmpty) "" - else "subgraph clusterIncoming {\n" + - "style=\"invis\"\n" + - incomingImplicits.reverse.map(n => node2Dot(n, incomingImplicitsTooltip)).mkString + - (if (incomingImplicits.size > 1) - incomingImplicits.map(n => "node" + node2Index(n)).mkString(" -> ") + - " [constraint=\"false\", style=\"invis\", minlen=\"0.0\"];\n" - else "") + - "}" - } - // dot cluster containing outgoing implicit nodes, if any - val outgoingCluster = { - if(outgoingImplicits.isEmpty) "" - else "subgraph clusterOutgoing {\n" + - "style=\"invis\"\n" + - outgoingImplicits.reverse.map(n => node2Dot(n, outgoingImplicitsTooltip)).mkString + - (if (outgoingImplicits.size > 1) - outgoingImplicits.map(n => "node" + node2Index(n)).mkString(" -> ") + - " [constraint=\"false\", style=\"invis\", minlen=\"0.0\"];\n" - else "") + - "}" - } - - // assemble clusters into another cluster - val incomingTooltip = incomingImplicits.map(_.name).mkString(", ") + " can be implicitly converted to " + thisNode.name - val outgoingTooltip = thisNode.name + " can be implicitly converted to " + outgoingImplicits.map(_.name).mkString(", ") - "subgraph clusterAll {\n" + - "style=\"invis\"\n" + - outgoingCluster + "\n" + - thisCluster + "\n" + - incomingCluster + "\n" + - // incoming implicit edge - (if (!incomingImplicits.isEmpty) { - val n = incomingImplicits.last - "node" + node2Index(n) +" -> node" + node2Index(thisNode) + - " [id=\"" + cssClass(n, thisNode) + "|" + node2Index(n) + "_" + node2Index(thisNode) + "\", tooltip=\"" + incomingTooltip + "\"" + - ", constraint=\"false\", minlen=\"2\", ltail=\"clusterIncoming\", lhead=\"clusterThis\", label=\"implicitly\"];\n" - } else "") + - // outgoing implicit edge - (if (!outgoingImplicits.isEmpty) { - val n = outgoingImplicits.head - "node" + node2Index(thisNode) + " -> node" + node2Index(n) + - " [id=\"" + cssClass(thisNode, n) + "|" + node2Index(thisNode) + "_" + node2Index(n) + "\", tooltip=\"" + outgoingTooltip + "\"" + - ", constraint=\"false\", minlen=\"2\", ltail=\"clusterThis\", lhead=\"clusterOutgoing\", label=\"implicitly\"];\n" - } else "") + - "}" - } - } - - // assemble graph - val graph = "digraph G {\n" + - // graph / node / edge attributes - graphAttributesStr + - "node [" + nodeAttributesStr + "];\n" + - "edge [" + edgeAttributesStr + "];\n" + - implicitsDot + "\n" + - // inheritance nodes - nodes.map(n => node2Dot(n, None)).mkString + - subClasses.map(n => node2Dot(n, subClassesTooltip)).mkString + - superClasses.map(n => node2Dot(n, superClassesTooltip)).mkString + - // inheritance edges - edges.map{ case (from, tos) => tos.map(to => { - val id = "graph" + counter + "_" + node2Index(to) + "_" + node2Index(from) - // the X -> Y edge is inverted twice to keep the diagram flowing the right way - // that is, an edge from node X to Y will result in a dot instruction nodeY -> nodeX [dir="back"] - "node" + node2Index(to) + " -> node" + node2Index(from) + - " [id=\"" + cssClass(to, from) + "|" + id + "\", " + - "tooltip=\"" + from.name + (if (from.name.endsWith(MultiSuffix)) " are subtypes of " else " is a subtype of ") + - to.name + "\", dir=\"back\", arrowtail=\"empty\"];\n" - }).mkString}.mkString + - "}" - - tDot += System.currentTimeMillis - DiagramStats.addDotGenerationTime(tDot) - - graph - } - - /** - * Generates the dot string of a given node. - */ - private def node2Dot(node: Node, tooltip: Option[String]) = { - - // escape HTML characters in node names - def escape(name: String) = name.replace("&", "&").replace("<", "<").replace(">", ">"); - - // assemble node attribues in a map - var attr = scala.collection.mutable.Map[String, String]() - - // link - node.doctpl match { - case Some(tpl) => attr += "URL" -> (page.relativeLinkTo(tpl) + "#inheritance-diagram") - case _ => - } - - // tooltip - tooltip match { - case Some(text) => attr += "tooltip" -> text - // show full name where available (instead of TraversableOps[A] show scala.collection.parallel.TraversableOps[A]) - case None if node.tpl.isDefined => attr += "tooltip" -> node.tpl.get.qualifiedName - case _ => - } - - // styles - if(node.isImplicitNode) - attr ++= implicitStyle - else if(node.isOutsideNode) - attr ++= outsideStyle - else if(node.isTraitNode) - attr ++= traitStyle - else if(node.isClassNode) - attr ++= classStyle - else if(node.isObjectNode) - attr ++= objectStyle - else - attr ++= defaultStyle - - // HTML label - var name = escape(node.name) - var img = "" - if(node.isTraitNode) - img = "trait_diagram.png" - else if(node.isClassNode) - img = "class_diagram.png" - else if(node.isObjectNode) - img = "object_diagram.png" - - if(!img.equals("")) { - img = "" - name = name + " " - } - val label = "<" + - "" + img + "" + - "
" + name + "
>" - - // dot does not allow to specify a CSS class, therefore - // set the id to "{class}|{id}", which will be used in - // the transform method - val id = "graph" + counter + "_" + node2Index(node) - attr += ("id" -> (cssClass(node) + "|" + id)) - - // return dot string - "node" + node2Index(node) + " [label=" + label + "," + flatten(attr.toMap) + "];\n" - } - - /** - * Returns the CSS class for an edge connecting node1 and node2. - */ - private def cssClass(node1: Node, node2: Node): String = { - if (node1.isImplicitNode && node2.isThisNode) - "implicit-incoming" - else if (node1.isThisNode && node2.isImplicitNode) - "implicit-outgoing" - else - "inheritance" - } - - /** - * Returns the CSS class for a node. - */ - private def cssClass(node: Node): String = - if (node.isImplicitNode && incomingImplicitNodes.contains(node)) - "implicit-incoming" - else if (node.isImplicitNode) - "implicit-outgoing" - else if (node.isObjectNode) - "object" - else if (node.isThisNode) - "this" + cssBaseClass(node, "") - else if (node.isOutsideNode) - "outside" + cssBaseClass(node, "") - else - cssBaseClass(node, "default") - - private def cssBaseClass(node: Node, default: String) = - if (node.isClassNode) - " class" - else if (node.isTraitNode) - " trait" - else if (node.isObjectNode) - " trait" - else - default - - /** - * Calls dot with a given dot string and returns the SVG output. - */ - private def generateSVG(dotInput: String, template: DocTemplateEntity) = { - val dotOutput = DiagramGenerator.getDotRunner.feedToDot(dotInput, template) - var tSVG = -System.currentTimeMillis - - val result = if (dotOutput != null) { - val src = scala.io.Source.fromString(dotOutput); - try { - val cpa = scala.xml.parsing.ConstructingParser.fromSource(src, false) - val doc = cpa.document() - if (doc != null) - transform(doc.docElem) - else - NodeSeq.Empty - } catch { - case exc => - if (settings.docDiagramsDebug.value) { - settings.printMsg("\n\n**********************************************************************") - settings.printMsg("Encountered an error while generating page for " + template.qualifiedName) - settings.printMsg(dotInput.toString.split("\n").mkString("\nDot input:\n\t","\n\t","")) - settings.printMsg(dotOutput.toString.split("\n").mkString("\nDot output:\n\t","\n\t","")) - settings.printMsg(exc.getStackTrace.mkString("\nException: " + exc.toString + ":\n\tat ", "\n\tat ","")) - settings.printMsg("\n\n**********************************************************************") - } else { - settings.printMsg("\nThe diagram for " + template.qualifiedName + " could not be created due to an internal error.") - settings.printMsg("Use " + settings.docDiagramsDebug.name + " for more information and please file this as a bug.") - } - NodeSeq.Empty - } - } else - NodeSeq.Empty - - tSVG += System.currentTimeMillis - DiagramStats.addSvgTime(tSVG) - - result - } - - /** - * Transforms the SVG generated by dot: - * - adds a class attribute to the SVG element - * - changes the path of the node images from absolute to relative - * - assigns id and class attributes to nodes and edges - * - removes title elements - */ - private def transform(e:scala.xml.Node): scala.xml.Node = e match { - // add an id and class attribute to the SVG element - case Elem(prefix, "svg", attribs, scope, child @ _*) => { - val klass = if (isClassDiagram) "class-diagram" else "package-diagram" - Elem(prefix, "svg", attribs, scope, child map(x => transform(x)) : _*) % - new UnprefixedAttribute("id", "graph" + counter, Null) % - new UnprefixedAttribute("class", klass, Null) - } - // change the path of the node images from absolute to relative - case img @ => { - val href = (img \ "@{http://www.w3.org/1999/xlink}href").toString - val file = href.substring(href.lastIndexOf("/") + 1, href.size) - img.asInstanceOf[Elem] % - new PrefixedAttribute("xlink", "href", pathToLib + file, Null) - } - // assign id and class attributes to edges and nodes: - // the id attribute generated by dot has the format: "{class}|{id}" - case g @ Elem(prefix, "g", attribs, scope, children @ _*) if (List("edge", "node").contains((g \ "@class").toString)) => { - val res = new Elem(prefix, "g", attribs, scope, (children map(x => transform(x))): _*) - val dotId = (g \ "@id").toString - if (dotId.count(_ == '|') == 1) { - val Array(klass, id) = dotId.toString.split("\\|") - res % new UnprefixedAttribute("id", id, Null) % - new UnprefixedAttribute("class", (g \ "@class").toString + " " + klass, Null) - } - else res - } - // remove titles - case { _* } => - scala.xml.Text("") - // apply recursively - case Elem(prefix, label, attribs, scope, child @ _*) => - Elem(prefix, label, attribs, scope, child map(x => transform(x)) : _*) - case x => x - } - - /* graph / node / edge attributes */ - - private val graphAttributes: Map[String, String] = Map( - "compound" -> "true", - "rankdir" -> "TB" - ) - - private val nodeAttributes = Map( - "shape" -> "rectangle", - "style" -> "filled", - "penwidth" -> "1", - "margin" -> "0.08,0.01", - "width" -> "0.0", - "height" -> "0.0", - "fontname" -> "Arial", - "fontsize" -> "10.00" - ) - - private val edgeAttributes = Map( - "color" -> "#d4d4d4", - "arrowsize" -> "0.5", - "fontcolor" -> "#aaaaaa", - "fontsize" -> "10.00", - "fontname" -> "Arial" - ) - - private val defaultStyle = Map( - "color" -> "#ababab", - "fillcolor" -> "#e1e1e1", - "fontcolor" -> "#7d7d7d", - "margin" -> "0.1,0.04" - ) - - private val implicitStyle = Map( - "color" -> "#ababab", - "fillcolor" -> "#e1e1e1", - "fontcolor" -> "#7d7d7d" - ) - - private val outsideStyle = Map( - "color" -> "#ababab", - "fillcolor" -> "#e1e1e1", - "fontcolor" -> "#7d7d7d" - ) - - private val traitStyle = Map( - "color" -> "#37657D", - "fillcolor" -> "#498AAD", - "fontcolor" -> "#ffffff" - ) - - private val classStyle = Map( - "color" -> "#115F3B", - "fillcolor" -> "#0A955B", - "fontcolor" -> "#ffffff" - ) - - private val objectStyle = Map( - "color" -> "#102966", - "fillcolor" -> "#3556a7", - "fontcolor" -> "#ffffff" - ) - - private def flatten(attributes: Map[String, String]) = attributes.map{ case (key, value) => key + "=\"" + value + "\"" }.mkString(", ") - - private val graphAttributesStr = graphAttributes.map{ case (key, value) => key + "=\"" + value + "\";\n" }.mkString - private val nodeAttributesStr = flatten(nodeAttributes) - private val edgeAttributesStr = flatten(edgeAttributes) -} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala deleted file mode 100644 index 37600fa908..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DotRunner.scala +++ /dev/null @@ -1,227 +0,0 @@ -package scala.tools.nsc -package doc -package html -package page -package diagram - -import java.io.InputStream -import java.io.OutputStream -import java.io.InputStreamReader -import java.io.OutputStreamWriter -import java.io.BufferedWriter -import java.io.BufferedReader -import java.io.IOException -import scala.sys.process._ -import scala.concurrent.SyncVar - -import model._ -import model.diagram._ - -/** This class takes care of running the graphviz dot utility */ -class DotRunner(settings: doc.Settings) { - - private[this] var dotRestarts = 0 - private[this] var dotProcess: DotProcess = null - - def feedToDot(dotInput: String, template: DocTemplateEntity): String = { - - if (dotProcess == null) { - if (dotRestarts < settings.docDiagramsDotRestart.value) { - if (dotRestarts != 0) - settings.printMsg("A new graphviz dot process will be created...\n") - dotRestarts += 1 - dotProcess = new DotProcess(settings) - } else - return null - } - - val tStart = System.currentTimeMillis - val result = dotProcess.feedToDot(dotInput, template.qualifiedName) - val tFinish = System.currentTimeMillis - DiagramStats.addDotRunningTime(tFinish - tStart) - - if (result == null) { - dotProcess.cleanup() - dotProcess = null - if (dotRestarts == settings.docDiagramsDotRestart.value) { - settings.printMsg("\n") - settings.printMsg("**********************************************************************") - settings.printMsg("Diagrams will be disabled for this run beucause the graphviz dot tool") - settings.printMsg("has malfunctioned too many times. These scaladoc flags may help:") - settings.printMsg("") - val baseList = List(settings.docDiagramsDebug, - settings.docDiagramsDotPath, - settings.docDiagramsDotRestart, - settings.docDiagramsDotTimeout) - val width = (baseList map (_.helpSyntax.length)).max - def helpStr(s: doc.Settings#Setting) = ("%-" + width + "s") format (s.helpSyntax) + " " + s.helpDescription - baseList.foreach((sett: doc.Settings#Setting) => settings.printMsg(helpStr(sett))) - settings.printMsg("\nPlease note that graphviz package version 2.26 or above is required.") - settings.printMsg("**********************************************************************\n\n") - - } - } - - result - } - - def cleanup() = - if (dotProcess != null) - dotProcess.cleanup() -} - -class DotProcess(settings: doc.Settings) { - - @volatile var error: Boolean = false // signal an error - val inputString = new SyncVar[String] // used for the dot process input - val outputString = new SyncVar[String] // used for the dot process output - val errorBuffer: StringBuffer = new StringBuffer() // buffer used for both dot process error console AND logging - - // set in only one place, in the main thread - var process: Process = null - var templateName: String = "" - var templateInput: String = "" - - def feedToDot(input: String, template: String): String = { - - templateName = template - templateInput = input - - try { - - // process creation - if (process == null) { - val procIO = new ProcessIO(inputFn(_), outputFn(_), errorFn(_)) - val processBuilder: ProcessBuilder = Seq(settings.docDiagramsDotPath.value, "-Tsvg") - process = processBuilder.run(procIO) - } - - // pass the input and wait for the output - assert(!inputString.isSet) - assert(!outputString.isSet) - inputString.put(input) - var result = outputString.take(settings.docDiagramsDotTimeout.value * 1000) - if (error) result = null - - result - - } catch { - case exc => - errorBuffer.append(" Main thread in " + templateName + ": " + - (if (exc.isInstanceOf[NoSuchElementException]) "Timeout" else "Exception: " + exc)) - error = true - return null - } - } - - def cleanup(): Unit = { - - // we'll need to know if there was any error for reporting - val _error = error - - if (process != null) { - // if there's no error, this should exit cleanly - if (!error) feedToDot("", "") - - // just in case there's any thread hanging, this will take it out of the loop - error = true - process.destroy() - // we'll need to unblock the input again - if (!inputString.isSet) inputString.put("") - if (outputString.isSet) outputString.take() - } - - if (_error) { - if (settings.docDiagramsDebug.value) { - settings.printMsg("\n**********************************************************************") - settings.printMsg("The graphviz dot diagram tool has malfunctioned and will be restarted.") - settings.printMsg("\nThe following is the log of the failure:") - settings.printMsg(errorBuffer.toString) - settings.printMsg(" Cleanup: Last template: " + templateName) - settings.printMsg(" Cleanup: Last dot input: \n " + templateInput.replaceAll("\n","\n ") + "\n") - settings.printMsg(" Cleanup: Dot path: " + settings.docDiagramsDotPath.value) - if (process != null) - settings.printMsg(" Cleanup: Dot exit code: " + process.exitValue) - settings.printMsg("**********************************************************************") - } else { - // we shouldn't just sit there for 50s not reporting anything, no? - settings.printMsg("Graphviz dot encountered an error when generating the diagram for") - settings.printMsg(templateName + ". Use the " + settings.docDiagramsDebug.name + " flag") - settings.printMsg("for more information.") - } - } - } - - /* The standard input passing function */ - private[this] def inputFn(stdin: OutputStream): Unit = { - val writer = new BufferedWriter(new OutputStreamWriter(stdin)) - try { - var input = inputString.take() - - while (!error) { - if (input == "") { - // empty => signal to finish - stdin.close() - return - } else { - // send output to dot - writer.write(input + "\n\n") - writer.flush() - } - - if (!error) input = inputString.take() - } - stdin.close() - } catch { - case exc => - error = true - stdin.close() - errorBuffer.append(" Input thread in " + templateName + ": Exception: " + exc + "\n") - } - } - - private[this] def outputFn(stdOut: InputStream): Unit = { - val reader = new BufferedReader(new InputStreamReader(stdOut)) - var buffer: StringBuilder = new StringBuilder() - try { - var line = reader.readLine - while (!error && line != null) { - buffer.append(line + "\n") - // signal the last element in the svg (only for output) - if (line == "") { - outputString.put(buffer.toString) - buffer.setLength(0) - } - if (error) { stdOut.close(); return } - line = reader.readLine - } - assert(!outputString.isSet) - outputString.put(buffer.toString) - stdOut.close() - } catch { - case exc => - error = true - stdOut.close() - errorBuffer.append(" Output thread in " + templateName + ": Exception: " + exc + "\n") - } - } - - private[this] def errorFn(stdErr: InputStream): Unit = { - val reader = new BufferedReader(new InputStreamReader(stdErr)) - var buffer: StringBuilder = new StringBuilder() - try { - var line = reader.readLine - while (line != null) { - errorBuffer.append(" DOT : " + line + "\n") - error = true - line = reader.readLine - } - stdErr.close() - } catch { - case exc => - error = true - stdErr.close() - errorBuffer.append(" Error thread in " + templateName + ": Exception: " + exc + "\n") - } - } -} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/class_diagram.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/class_diagram.png deleted file mode 100644 index 9d7aec792b..0000000000 Binary files a/src/compiler/scala/tools/nsc/doc/html/resource/lib/class_diagram.png and /dev/null differ diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css deleted file mode 100644 index 04d29580b7..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.css +++ /dev/null @@ -1,135 +0,0 @@ -.diagram-container -{ - display: none; -} - -.diagram -{ - overflow: hidden; - padding-top:15px; -} - -.diagram svg -{ - display: block; - position: absolute; - visibility: hidden; - margin: auto; -} - -.diagram-help -{ - float:right; - display:none; -} - -.magnifying -{ - cursor: -webkit-zoom-in ! important; - cursor: -moz-zoom-in ! important; - cursor: pointer; -} - -#close-link -{ - position: absolute; - z-index: 100; - font-family: Arial, sans-serif; - font-size: 10pt; - text-decoration: underline; - color: #315479; -} - -#close:hover -{ - text-decoration: none; -} - -svg a -{ - cursor:pointer; -} - -svg text -{ - font-size: 10px; -} - -/* try to move the node text 1px in order to be vertically - centered (does not work in all browsers) */ -svg .node text -{ - transform: translate(0px,1px); - -ms-transform: translate(0px,1px); - -webkit-transform: translate(0px,1px); - -o-transform: translate(0px,1px); - -moz-transform: translate(0px,1px); -} - -/* hover effect for edges */ - -svg .edge.over text, -svg .edge.implicit-incoming.over polygon, -svg .edge.implicit-outgoing.over polygon -{ - fill: #202020; -} - -svg .edge.over path, -svg .edge.over polygon -{ - stroke: #202020; -} - -/* hover effect for nodes in class diagrams */ - -svg.class-diagram .node -{ - opacity: 0.75; -} - -svg.class-diagram .node.this -{ - opacity: 1.0; -} - -svg.class-diagram .node.over -{ - opacity: 1.0; -} - -svg .node.over polygon -{ - stroke: #202020; -} - -/* hover effect for nodes in package diagrams */ - -svg.package-diagram .node.class.over polygon, -svg.class-diagram .node.this.class.over polygon -{ - fill: #098552; - fill: #04663e; -} - -svg.package-diagram .node.trait.over polygon, -svg.class-diagram .node.this.trait.over polygon -{ - fill: #3c7b9b; - fill: #235d7b; -} - -svg.package-diagram .node.object.over polygon -{ - fill: #183377; -} - -svg.package-diagram .node.outside.over polygon -{ - fill: #d4d4d4; -} - -svg.package-diagram .node.default.over polygon -{ - fill: #d4d4d4; -} diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.js deleted file mode 100644 index 478f2e38ac..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/diagrams.js +++ /dev/null @@ -1,324 +0,0 @@ -/** - * JavaScript functions enhancing the SVG diagrams. - * - * @author Damien Obrist - */ - -var diagrams = {}; - -/** - * Initializes the diagrams in the main window. - */ -$(document).ready(function() -{ - // hide diagrams in browsers not supporting SVG - if(Modernizr && !Modernizr.inlinesvg) - return; - - // only execute this in the main window - if(diagrams.isPopup) - return; - - if($("#content-diagram").length) - $("#inheritance-diagram").css("padding-bottom", "20px"); - - $(".diagram-container").css("display", "block"); - - $(".diagram").each(function() { - // store inital dimensions - $(this).data("width", $("svg", $(this)).width()); - $(this).data("height", $("svg", $(this)).height()); - // store unscaled clone of SVG element - $(this).data("svg", $(this).get(0).childNodes[0].cloneNode(true)); - }); - - // make diagram visible, hide container - $(".diagram").css("display", "none"); - $(".diagram svg").css({ - "position": "static", - "visibility": "visible", - "z-index": "auto" - }); - - // enable linking to diagrams - if($(location).attr("hash") == "#inheritance-diagram") { - diagrams.toggle($("#inheritance-diagram-container"), true); - } else if($(location).attr("hash") == "#content-diagram") { - diagrams.toggle($("#content-diagram-container"), true); - } - - $(".diagram-link").click(function() { - diagrams.toggle($(this).parent()); - }); - - // register resize function - $(window).resize(diagrams.resize); - - // don't bubble event to parent div - // when clicking on a node of a resized - // diagram - $("svg a").click(function(e) { - e.stopPropagation(); - }); - - diagrams.initHighlighting(); -}); - -/** - * Initializes the diagrams in the popup. - */ -diagrams.initPopup = function(id) -{ - // copy diagram from main window - if(!jQuery.browser.msie) - $("body").append(opener.$("#" + id).data("svg")); - - // positioning - $("svg").css("position", "absolute"); - $(window).resize(function() - { - var svg_w = $("svg").css("width").replace("px", ""); - var svg_h = $("svg").css("height").replace("px", ""); - var x = $(window).width() / 2 - svg_w / 2; - if(x < 0) x = 0; - var y = $(window).height() / 2 - svg_h / 2; - if(y < 0) y = 0; - $("svg").css("left", x + "px"); - $("svg").css("top", y + "px"); - }); - $(window).resize(); - - diagrams.initHighlighting(); - $("svg a").click(function(e) { - opener.diagrams.redirectFromPopup(this.href.baseVal); - window.close(); - }); - $(document).keyup(function(e) { - if (e.keyCode == 27) window.close(); - }); -} - -/** - * Initializes highlighting for nodes and edges. - */ -diagrams.initHighlighting = function() -{ - // helper function since $.hover doesn't work in IE - - function hover(elements, fn) - { - elements.mouseover(fn); - elements.mouseout(fn); - } - - // inheritance edges - - hover($("svg .edge.inheritance"), function(evt){ - var toggleClass = evt.type == "mouseout" ? diagrams.removeClass : diagrams.addClass; - var parts = $(this).attr("id").split("_"); - toggleClass($("#" + parts[0] + "_" + parts[1])); - toggleClass($("#" + parts[0] + "_" + parts[2])); - toggleClass($(this)); - }); - - // nodes - - hover($("svg .node"), function(evt){ - var toggleClass = evt.type == "mouseout" ? diagrams.removeClass : diagrams.addClass; - toggleClass($(this)); - var parts = $(this).attr("id").split("_"); - var index = parts[1]; - $("svg#" + parts[0] + " .edge.inheritance").each(function(){ - var parts2 = $(this).attr("id").split("_"); - if(parts2[1] == index) - { - toggleClass($("#" + parts2[0] + "_" + parts2[2])); - toggleClass($(this)); - } else if(parts2[2] == index) - { - toggleClass($("#" + parts2[0] + "_" + parts2[1])); - toggleClass($(this)); - } - }); - }); - - // incoming implicits - - hover($("svg .node.implicit-incoming"), function(evt){ - var toggleClass = evt.type == "mouseout" ? diagrams.removeClass : diagrams.addClass; - toggleClass($(this)); - toggleClass($("svg .edge.implicit-incoming")); - toggleClass($("svg .node.this")); - }); - - hover($("svg .edge.implicit-incoming"), function(evt){ - var toggleClass = evt.type == "mouseout" ? diagrams.removeClass : diagrams.addClass; - toggleClass($(this)); - toggleClass($("svg .node.this")); - $("svg .node.implicit-incoming").each(function(){ - toggleClass($(this)); - }); - }); - - // implicit outgoing nodes - - hover($("svg .node.implicit-outgoing"), function(evt){ - var toggleClass = evt.type == "mouseout" ? diagrams.removeClass : diagrams.addClass; - toggleClass($(this)); - toggleClass($("svg .edge.implicit-outgoing")); - toggleClass($("svg .node.this")); - }); - - hover($("svg .edge.implicit-outgoing"), function(evt){ - var toggleClass = evt.type == "mouseout" ? diagrams.removeClass : diagrams.addClass; - toggleClass($(this)); - toggleClass($("svg .node.this")); - $("svg .node.implicit-outgoing").each(function(){ - toggleClass($(this)); - }); - }); -}; - -/** - * Resizes the diagrams according to the available width. - */ -diagrams.resize = function() -{ - // available width - var availableWidth = $("body").width() - 20; - - $(".diagram-container").each(function() { - // unregister click event on whole div - $(".diagram", this).unbind("click"); - var diagramWidth = $(".diagram", this).data("width"); - var diagramHeight = $(".diagram", this).data("height"); - - if(diagramWidth > availableWidth) - { - // resize diagram - var height = diagramHeight / diagramWidth * availableWidth; - $(".diagram svg", this).width(availableWidth); - $(".diagram svg", this).height(height); - - // register click event on whole div - $(".diagram", this).click(function() { - diagrams.popup($(this)); - }); - $(".diagram", this).addClass("magnifying"); - } - else - { - // restore full size of diagram - $(".diagram svg", this).width(diagramWidth); - $(".diagram svg", this).height(diagramHeight); - // don't show custom cursor any more - $(".diagram", this).removeClass("magnifying"); - } - }); -}; - -/** - * Shows or hides a diagram depending on its current state. - */ -diagrams.toggle = function(container, dontAnimate) -{ - // change class of link - $(".diagram-link", container).toggleClass("open"); - // get element to show / hide - var div = $(".diagram", container); - if (div.is(':visible')) - { - $(".diagram-help", container).hide(); - div.unbind("click"); - div.removeClass("magnifying"); - div.slideUp(100); - } - else - { - diagrams.resize(); - if(dontAnimate) - div.show(); - else - div.slideDown(100); - $(".diagram-help", container).show(); - } -}; - -/** - * Opens a popup containing a copy of a diagram. - */ -diagrams.windows = {}; -diagrams.popup = function(diagram) -{ - var id = diagram.attr("id"); - if(!diagrams.windows[id] || diagrams.windows[id].closed) { - var title = $(".symbol .name", $("#signature")).text(); - // cloning from parent window to popup somehow doesn't work in IE - // therefore include the SVG as a string into the HTML - var svgIE = jQuery.browser.msie ? $("
").append(diagram.data("svg")).html() : ""; - var html = '' + - '\n' + - '\n' + - '\n' + - ' \n' + - ' ' + title + '\n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' \n' + - ' Close this window\n' + - ' ' + svgIE + '\n' + - ' \n' + - ''; - - var padding = 30; - var screenHeight = screen.availHeight; - var screenWidth = screen.availWidth; - var w = Math.min(screenWidth, diagram.data("width") + 2 * padding); - var h = Math.min(screenHeight, diagram.data("height") + 2 * padding); - var left = (screenWidth - w) / 2; - var top = (screenHeight - h) / 2; - var parameters = "height=" + h + ", width=" + w + ", left=" + left + ", top=" + top + ", scrollbars=yes, location=no, resizable=yes"; - var win = window.open("about:blank", "_blank", parameters); - win.document.open(); - win.document.write(html); - win.document.close(); - diagrams.windows[id] = win; - } - win.focus(); -}; - -/** - * This method is called from within the popup when a node is clicked. - */ -diagrams.redirectFromPopup = function(url) -{ - window.location = url; -}; - -/** - * Helper method that adds a class to a SVG element. - */ -diagrams.addClass = function(svgElem, newClass) { - newClass = newClass || "over"; - var classes = svgElem.attr("class"); - if ($.inArray(newClass, classes.split(/\s+/)) == -1) { - classes += (classes ? ' ' : '') + newClass; - svgElem.attr("class", classes); - } -}; - -/** - * Helper method that removes a class from a SVG element. - */ -diagrams.removeClass = function(svgElem, oldClass) { - oldClass = oldClass || "over"; - var classes = svgElem.attr("class"); - classes = $.grep(classes.split(/\s+/), function(n, i) { return n != oldClass; }).join(' '); - svgElem.attr("class", classes); -}; - diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/modernizr.custom.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/modernizr.custom.js deleted file mode 100644 index 4688d633fe..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/modernizr.custom.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Modernizr 2.5.3 (Custom Build) | MIT & BSD - * Build: http://www.modernizr.com/download/#-inlinesvg - */ -;window.Modernizr=function(a,b,c){function u(a){i.cssText=a}function v(a,b){return u(prefixes.join(a+";")+(b||""))}function w(a,b){return typeof a===b}function x(a,b){return!!~(""+a).indexOf(b)}function y(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:w(f,"function")?f.bind(d||b):f}return!1}var d="2.5.3",e={},f=b.documentElement,g="modernizr",h=b.createElement(g),i=h.style,j,k={}.toString,l={svg:"http://www.w3.org/2000/svg"},m={},n={},o={},p=[],q=p.slice,r,s={}.hasOwnProperty,t;!w(s,"undefined")&&!w(s.call,"undefined")?t=function(a,b){return s.call(a,b)}:t=function(a,b){return b in a&&w(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=q.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(q.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(q.call(arguments)))};return e}),m.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==l.svg};for(var z in m)t(m,z)&&(r=z.toLowerCase(),e[r]=m[z](),p.push((e[r]?"":"no-")+r));return u(""),h=j=null,e._version=d,e}(this,this.document); \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_diagram.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_diagram.png deleted file mode 100644 index 6e9f2f743f..0000000000 Binary files a/src/compiler/scala/tools/nsc/doc/html/resource/lib/object_diagram.png and /dev/null differ diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/raphael-min.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/raphael-min.js deleted file mode 100644 index d30dbad858..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/raphael-min.js +++ /dev/null @@ -1,10 +0,0 @@ -// ┌────────────────────────────────────────────────────────────────────┐ \\ -// │ Raphaël 2.1.0 - JavaScript Vector Library │ \\ -// ├────────────────────────────────────────────────────────────────────┤ \\ -// │ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com) │ \\ -// │ Copyright © 2008-2012 Sencha Labs (http://sencha.com) │ \\ -// ├────────────────────────────────────────────────────────────────────┤ \\ -// │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\ -// └────────────────────────────────────────────────────────────────────┘ \\ - -(function(a){var b="0.3.4",c="hasOwnProperty",d=/[\.\/]/,e="*",f=function(){},g=function(a,b){return a-b},h,i,j={n:{}},k=function(a,b){var c=j,d=i,e=Array.prototype.slice.call(arguments,2),f=k.listeners(a),l=0,m=!1,n,o=[],p={},q=[],r=h,s=[];h=a,i=0;for(var t=0,u=f.length;tf*b.top){e=b.percents[y],p=b.percents[y-1]||0,t=t/b.top*(e-p),o=b.percents[y+1],j=b.anim[e];break}f&&d.attr(b.anim[b.percents[y]])}if(!!j){if(!k){for(var A in j)if(j[g](A))if(U[g](A)||d.paper.customAttributes[g](A)){u[A]=d.attr(A),u[A]==null&&(u[A]=T[A]),v[A]=j[A];switch(U[A]){case C:w[A]=(v[A]-u[A])/t;break;case"colour":u[A]=a.getRGB(u[A]);var B=a.getRGB(v[A]);w[A]={r:(B.r-u[A].r)/t,g:(B.g-u[A].g)/t,b:(B.b-u[A].b)/t};break;case"path":var D=bR(u[A],v[A]),E=D[1];u[A]=D[0],w[A]=[];for(y=0,z=u[A].length;yd)return d;while(cf?c=e:d=e,e=(d-c)/2+c}return e}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function m(a){return((i*a+h)*a+g)*a}var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;return n(a,1/(200*f))}function cq(){return this.x+q+this.y+q+this.width+" × "+this.height}function cp(){return this.x+q+this.y}function cb(a,b,c,d,e,f){a!=null?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function bH(b,c,d){b=a._path2curve(b),c=a._path2curve(c);var e,f,g,h,i,j,k,l,m,n,o=d?0:[];for(var p=0,q=b.length;p=0&&y<=1&&A>=0&&A<=1&&(d?n++:n.push({x:x.x,y:x.y,t1:y,t2:A}))}}return n}function bF(a,b){return bG(a,b,1)}function bE(a,b){return bG(a,b)}function bD(a,b,c,d,e,f,g,h){if(!(x(a,c)x(e,g)||x(b,d)x(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(!k)return;var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(n<+y(a,c).toFixed(2)||n>+x(a,c).toFixed(2)||n<+y(e,g).toFixed(2)||n>+x(e,g).toFixed(2)||o<+y(b,d).toFixed(2)||o>+x(b,d).toFixed(2)||o<+y(f,h).toFixed(2)||o>+x(f,h).toFixed(2))return;return{x:l,y:m}}}function bC(a,b,c,d,e,f,g,h,i){if(!(i<0||bB(a,b,c,d,e,f,g,h)n)k/=2,l+=(m1?1:i<0?0:i;var j=i/2,k=12,l=[-0.1252,.1252,-0.3678,.3678,-0.5873,.5873,-0.7699,.7699,-0.9041,.9041,-0.9816,.9816],m=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],n=0;for(var o=0;od;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function bx(){return this.hex}function bv(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),h=d.cache=d.cache||{},i=d.count=d.count||[];if(h[g](f)){bu(i,f);return c?c(h[f]):h[f]}i.length>=1e3&&delete h[i.shift()],i.push(f),h[f]=a[m](b,e);return c?c(h[f]):h[f]}return d}function bu(a,b){for(var c=0,d=a.length;c',bl=bk.firstChild,bl.style.behavior="url(#default#VML)";if(!bl||typeof bl.adj!="object")return a.type=p;bk=null}a.svg=!(a.vml=a.type=="VML"),a._Paper=j,a.fn=k=j.prototype=a.prototype,a._id=0,a._oid=0,a.is=function(a,b){b=v.call(b);if(b=="finite")return!M[g](+a);if(b=="array")return a instanceof Array;return b=="null"&&a===null||b==typeof a&&a!==null||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||H.call(a).slice(8,-1).toLowerCase()==b},a.angle=function(b,c,d,e,f,g){if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return(180+w.atan2(-i,-h)*180/B+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)},a.rad=function(a){return a%360*B/180},a.deg=function(a){return a*180/B%360},a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,E)){var e=b.length;while(e--)if(z(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(fb-d)return c-f+b}return c};var bn=a.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}(/[xy]/g,function(a){var b=w.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)});a.setWindow=function(b){eve("raphael.setWindow",a,h.win,b),h.win=b,h.doc=h.win.document,a._engine.initWin&&a._engine.initWin(h.win)};var bo=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write(""),e.close(),d=e.body}catch(f){d=createPopup().document.body}var g=d.createTextRange();bo=bv(function(a){try{d.style.color=r(a).replace(c,p);var b=g.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=h.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",h.doc.body.appendChild(i),bo=bv(function(a){i.style.color=a;return h.doc.defaultView.getComputedStyle(i,p).getPropertyValue("color")})}return bo(b)},bp=function(){return"hsb("+[this.h,this.s,this.b]+")"},bq=function(){return"hsl("+[this.h,this.s,this.l]+")"},br=function(){return this.hex},bs=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,D)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;return[b,c,d]},bt=function(b,c,d,e){b*=255,c*=255,d*=255;var f={r:b,g:c,b:d,hex:a.rgb(b,c,d),toString:br};a.is(e,"finite")&&(f.opacity=e);return f};a.color=function(b){var c;a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b?(c=a.hsb2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b?(c=a.hsl2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):(a.is(b,"string")&&(b=a.getRGB(b)),a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b?(c=a.rgb2hsl(b),b.h=c.h,b.s=c.s,b.l=c.l,c=a.rgb2hsb(b),b.v=c.b):(b={hex:"none"},b.r=b.g=b.b=b.h=b.s=b.v=b.l=-1)),b.toString=br;return b},a.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,a=a.h,d=a.o),a*=360;var e,f,g,h,i;a=a%360/60,i=c*b,h=i*(1-z(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h);if(a>1||b>1||c>1)a/=360,b/=100,c/=100;a*=360;var e,f,g,h,i;a=a%360/60,i=2*b*(c<.5?c:1-c),h=i*(1-z(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.rgb2hsb=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;f=x(a,b,c),g=f-y(a,b,c),d=g==0?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=g==0?0:g/f;return{h:d,s:e,b:f,toString:bp}},a.rgb2hsl=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;g=x(a,b,c),h=y(a,b,c),i=g-h,d=i==0?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=i==0?0:f<.5?i/(2*f):i/(2-2*f);return{h:d,s:e,l:f,toString:bq}},a._path2string=function(){return this.join(",").replace(Y,"$1")};var bw=a._preload=function(a,b){var c=h.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,h.doc.body.removeChild(this)},c.onerror=function(){h.doc.body.removeChild(this)},h.doc.body.appendChild(c),c.src=a};a.getRGB=bv(function(b){if(!b||!!((b=r(b)).indexOf("-")+1))return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none",toString:bx};!X[g](b.toLowerCase().substring(0,2))&&b.charAt()!="#"&&(b=bo(b));var c,d,e,f,h,i,j,k=b.match(L);if(k){k[2]&&(f=R(k[2].substring(5),16),e=R(k[2].substring(3,5),16),d=R(k[2].substring(1,3),16)),k[3]&&(f=R((i=k[3].charAt(3))+i,16),e=R((i=k[3].charAt(2))+i,16),d=R((i=k[3].charAt(1))+i,16)),k[4]&&(j=k[4][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),k[1].toLowerCase().slice(0,4)=="rgba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100));if(k[5]){j=k[5][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,f,h)}if(k[6]){j=k[6][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsla"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,f,h)}k={r:d,g:e,b:f,toString:bx},k.hex="#"+(16777216|f|e<<8|d<<16).toString(16).slice(1),a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx}},a),a.hsb=bv(function(b,c,d){return a.hsb2rgb(b,c,d).hex}),a.hsl=bv(function(b,c,d){return a.hsl2rgb(b,c,d).hex}),a.rgb=bv(function(a,b,c){return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)}),a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b}));return c.hex},a.getColor.reset=function(){delete this.start},a.parsePathString=function(b){if(!b)return null;var c=bz(b);if(c.arr)return bJ(c.arr);var d={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},e=[];a.is(b,E)&&a.is(b[0],E)&&(e=bJ(b)),e.length||r(b).replace(Z,function(a,b,c){var f=[],g=b.toLowerCase();c.replace(_,function(a,b){b&&f.push(+b)}),g=="m"&&f.length>2&&(e.push([b][n](f.splice(0,2))),g="l",b=b=="m"?"l":"L");if(g=="r")e.push([b][n](f));else while(f.length>=d[g]){e.push([b][n](f.splice(0,d[g])));if(!d[g])break}}),e.toString=a._path2string,c.arr=bJ(e);return e},a.parseTransformString=bv(function(b){if(!b)return null;var c={r:3,s:4,t:2,m:6},d=[];a.is(b,E)&&a.is(b[0],E)&&(d=bJ(b)),d.length||r(b).replace($,function(a,b,c){var e=[],f=v.call(b);c.replace(_,function(a,b){b&&e.push(+b)}),d.push([b][n](e))}),d.toString=a._path2string;return d});var bz=function(a){var b=bz.ps=bz.ps||{};b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[g](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])});return b[a]};a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=A(j,3),l=A(j,2),m=i*i,n=m*i,o=k*a+l*3*i*c+j*3*i*i*e+n*g,p=k*b+l*3*i*d+j*3*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,x=j*e+i*g,y=j*f+i*h,z=90-w.atan2(q-s,r-t)*180/B;(q>s||r=a.x&&b<=a.x2&&c>=a.y&&c<=a.y2},a.isBBoxIntersect=function(b,c){var d=a.isPointInsideBBox;return d(c,b.x,b.y)||d(c,b.x2,b.y)||d(c,b.x,b.y2)||d(c,b.x2,b.y2)||d(b,c.x,c.y)||d(b,c.x2,c.y)||d(b,c.x,c.y2)||d(b,c.x2,c.y2)||(b.xc.x||c.xb.x)&&(b.yc.y||c.yb.y)},a.pathIntersection=function(a,b){return bH(a,b)},a.pathIntersectionNumber=function(a,b){return bH(a,b,1)},a.isPointInsidePath=function(b,c,d){var e=a.pathBBox(b);return a.isPointInsideBBox(e,c,d)&&bH(b,[["M",c,d],["H",e.x2+10]],1)%2==1},a._removedFactory=function(a){return function(){eve("raphael.log",null,"Raphaël: you are calling to method “"+a+"” of removed object",a)}};var bI=a.pathBBox=function(a){var b=bz(a);if(b.bbox)return b.bbox;if(!a)return{x:0,y:0,width:0,height:0,x2:0,y2:0};a=bR(a);var c=0,d=0,e=[],f=[],g;for(var h=0,i=a.length;h1&&(v=w.sqrt(v),c=v*c,d=v*d);var x=c*c,y=d*d,A=(f==g?-1:1)*w.sqrt(z((x*y-x*u*u-y*t*t)/(x*u*u+y*t*t))),C=A*c*u/d+(a+h)/2,D=A*-d*t/c+(b+i)/2,E=w.asin(((b-D)/d).toFixed(9)),F=w.asin(((i-D)/d).toFixed(9));E=aF&&(E=E-B*2),!g&&F>E&&(F=F-B*2)}else E=j[0],F=j[1],C=j[2],D=j[3];var G=F-E;if(z(G)>k){var H=F,I=h,J=i;F=E+k*(g&&F>E?1:-1),h=C+c*w.cos(F),i=D+d*w.sin(F),m=bO(h,i,c,d,e,0,g,I,J,[F,H,C,D])}G=F-E;var K=w.cos(E),L=w.sin(E),M=w.cos(F),N=w.sin(F),O=w.tan(G/4),P=4/3*c*O,Q=4/3*d*O,R=[a,b],S=[a+P*L,b-Q*K],T=[h+P*N,i-Q*M],U=[h,i];S[0]=2*R[0]-S[0],S[1]=2*R[1]-S[1];if(j)return[S,T,U][n](m);m=[S,T,U][n](m).join()[s](",");var V=[];for(var W=0,X=m.length;W"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y)),i=f-2*d+b-(h-2*f+d),j=2*(d-b)-2*(f-d),k=b-d,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y));return{min:{x:y[m](0,p),y:y[m](0,o)},max:{x:x[m](0,p),y:x[m](0,o)}}}),bR=a._path2curve=bv(function(a,b){var c=!b&&bz(a);if(!b&&c.curve)return bJ(c.curve);var d=bL(a),e=b&&bL(b),f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},h=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][n](bO[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"][n](bN(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][n](bN(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](bM(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](bM(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](bM(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](bM(b.x,b.y,b.X,b.Y))}return a},i=function(a,b){if(a[b].length>7){a[b].shift();var c=a[b];while(c.length)a.splice(b++,0,["C"][n](c.splice(0,6)));a.splice(b,1),l=x(d.length,e&&e.length||0)}},j=function(a,b,c,f,g){a&&b&&a[g][0]=="M"&&b[g][0]!="M"&&(b.splice(g,0,["M",f.x,f.y]),c.bx=0,c.by=0,c.x=a[g][1],c.y=a[g][2],l=x(d.length,e&&e.length||0))};for(var k=0,l=x(d.length,e&&e.length||0);ke){if(c&&!l.start){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),k+=["C"+m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k,k=["M"+m.x,m.y+"C"+m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}if(!b&&!c){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j,g=+i[5],h=+i[6]}k+=i.shift()+i}l.end=k,m=b?n:c?l:a.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cu=ct(1),cv=ct(),cw=ct(0,1);a.getTotalLength=cu,a.getPointAtLength=cv,a.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return cw(a,b).end;var d=cw(a,c,1);return b?cw(d,b).end:d},cl.getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return cu(this.attrs.path)}},cl.getPointAtLength=function(a){if(this.type=="path")return cv(this.attrs.path,a)},cl.getSubpath=function(b,c){if(this.type=="path")return a.getSubpath(this.attrs.path,b,c)};var cx=a.easing_formulas={linear:function(a){return a},"<":function(a){return A(a,1.7)},">":function(a){return A(a,.48)},"<>":function(a){var b=.48-a/1.04,c=w.sqrt(.1734+b*b),d=c-b,e=A(z(d),1/3)*(d<0?-1:1),f=-c-b,g=A(z(f),1/3)*(f<0?-1:1),h=e+g+.5;return(1-h)*3*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==!!a)return a;return A(2,-10*a)*w.sin((a-.075)*2*B/.3)+1},bounce:function(a){var b=7.5625,c=2.75,d;a<1/c?d=b*a*a:a<2/c?(a-=1.5/c,d=b*a*a+.75):a<2.5/c?(a-=2.25/c,d=b*a*a+.9375):(a-=2.625/c,d=b*a*a+.984375);return d}};cx.easeIn=cx["ease-in"]=cx["<"],cx.easeOut=cx["ease-out"]=cx[">"],cx.easeInOut=cx["ease-in-out"]=cx["<>"],cx["back-in"]=cx.backIn,cx["back-out"]=cx.backOut;var cy=[],cz=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,16)},cA=function(){var b=+(new Date),c=0;for(;c1&&!d.next){for(s in k)k[g](s)&&(r[s]=d.totalOrigin[s]);d.el.attr(r),cE(d.anim,d.el,d.anim.percents[0],null,d.totalOrigin,d.repeat-1)}d.next&&!d.stop&&cE(d.anim,d.el,d.next,null,d.totalOrigin,d.repeat)}}a.svg&&m&&m.paper&&m.paper.safari(),cy.length&&cz(cA)},cB=function(a){return a>255?255:a<0?0:a};cl.animateWith=function(b,c,d,e,f,g){var h=this;if(h.removed){g&&g.call(h);return h}var i=d instanceof cD?d:a.animation(d,e,f,g),j,k;cE(i,h,i.percents[0],null,h.attr());for(var l=0,m=cy.length;l.5)*2-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&n!=.5&&(n=n.toFixed(5)-1e-5*e)}return l}),e=e.split(/\s*\-\s*/);if(j=="linear"){var t=e.shift();t=-d(t);if(isNaN(t))return null;var u=[0,0,f.cos(a.rad(t)),f.sin(a.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}var w=a._parseDots(e);if(!w)return null;k=k.replace(/[\(\)\s,\xb0#]/g,"_"),b.gradient&&k!=b.gradient.id&&(p.defs.removeChild(b.gradient),delete b.gradient);if(!b.gradient){s=q(j+"Gradient",{id:k}),b.gradient=s,q(s,j=="radial"?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:b.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;x1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(p),i.setAttribute(o,G.hex),o=="stroke"&&G[b]("opacity")&&q(i,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity}),o=="stroke"&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":(d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p);break;case"opacity":k.gradient&&!k[b]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),H&&(I=H.getElementsByTagName("stop"),q(I[I.length-1],{"stop-opacity":p}));break};default:o=="font-size"&&(p=e(p,10)+"px");var J=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[J]=p,d._.dirty=1,i.setAttribute(o,p)}}y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if(d.type=="text"&&!!(f[b]("text")||f[b]("font")||f[b]("font-size")||f[b]("x")||f[b]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(a._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[b]("text")){g.text=f.text;while(h.firstChild)h.removeChild(h.firstChild);var j=c(f.text).split("\n"),k=[],m;for(var n=0,o=j.length;n"));var $=X.getBoundingClientRect();t.W=m.w=($.right-$.left)/Y,t.H=m.h=($.bottom-$.top)/Y,t.X=m.x,t.Y=m.y+t.H/2,("x"in i||"y"in i)&&(t.path.v=a.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));var _=["x","y","text","font","font-family","font-weight","font-style","font-size"];for(var ba=0,bb=_.length;ba.25&&(c=e.sqrt(.25-i(b-.5,2))*((c>.5)*2-1)+.5),m=b+n+c);return o}),f=f.split(/\s*\-\s*/);if(l=="linear"){var p=f.shift();p=-d(p);if(isNaN(p))return null}var q=a._parseDots(f);if(!q)return null;b=b.shape||b.node;if(q.length){b.removeChild(g),g.on=!0,g.method="none",g.color=q[0].color,g.color2=q[q.length-1].color;var r=[];for(var s=0,t=q.length;s')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},a._engine.initWin(a._g.win),a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b.container,d=b.height,e,f=b.width,g=b.x,h=b.y;if(!c)throw new Error("VML container not found.");var i=new a._Paper,j=i.canvas=a._g.doc.createElement("div"),k=j.style;g=g||0,h=h||0,f=f||512,d=d||342,i.width=f,i.height=d,f==+f&&(f+="px"),d==+d&&(d+="px"),i.coordsize=u*1e3+n+u*1e3,i.coordorigin="0 0",i.span=a._g.doc.createElement("span"),i.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",j.appendChild(i.span),k.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d),c==1?(a._g.doc.body.appendChild(j),k.left=g+"px",k.top=h+"px",k.position="absolute"):c.firstChild?c.insertBefore(j,c.firstChild):c.appendChild(j),i.renderfix=function(){};return i},a.prototype.clear=function(){a.eve("raphael.clear",this),this.canvas.innerHTML=o,this.span=a._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},a.prototype.remove=function(){a.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;return!0};var G=a.st;for(var H in E)E[b](H)&&!G[b](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(H))}(window.Raphael) \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css index 1bee55313b..b25f0d0b3f 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css @@ -163,7 +163,7 @@ text-decoration: none; background: url("arrow-right.png") no-repeat 0 3px transparent; } -.toggleContainer .toggle.open { +.toggleContainer.open .toggle { background: url("arrow-down.png") no-repeat 0 3px transparent; } @@ -171,6 +171,10 @@ text-decoration: none; margin-top: 5px; } +.toggleContainer .showElement { + padding-left: 15px; +} + .value #definition { background-color: #2C475C; /* blue */ background-image:url('defbg-blue.gif'); @@ -337,7 +341,11 @@ div.members > ol > li:last-child { font-style: italic; } -.signature .symbol .deprecated { +.signature .symbol .implicit.deprecated { + text-decoration: line-through; +} + +.signature .symbol .name.deprecated { text-decoration: line-through; } diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js index c418c3280b..33fbd83bee 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.js @@ -8,8 +8,7 @@ $(document).ready(function(){ name == 'scala.Predef.any2stringfmt' || name == 'scala.Predef.any2stringadd' || name == 'scala.Predef.any2ArrowAssoc' || - name == 'scala.Predef.any2Ensuring' || - name == 'scala.collection.TraversableOnce.alternateImplicit' + name == 'scala.Predef.any2Ensuring' }; $("#linearization li:gt(0)").filter(function(){ @@ -185,18 +184,21 @@ $(document).ready(function(){ }); /* Linear super types and known subclasses */ - function toggleShowContentFct(e){ - e.toggleClass("open"); - var content = $(".hiddenContent", e.parent().get(0)); - if (content.is(':visible')) { + function toggleShowContentFct(outerElement){ + var content = $(".hiddenContent", outerElement); + var vis = $(":visible", content); + if (vis.length > 0) { content.slideUp(100); + $(".showElement", outerElement).show(); + $(".hideElement", outerElement).hide(); } else { content.slideDown(100); + $(".showElement", outerElement).hide(); + $(".hideElement", outerElement).show(); } }; - - $(".toggle:not(.diagram-link)").click(function() { + $(".toggleContainer").click(function() { toggleShowContentFct($(this)); }); diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/trait_diagram.png b/src/compiler/scala/tools/nsc/doc/html/resource/lib/trait_diagram.png deleted file mode 100644 index 88983254ce..0000000000 Binary files a/src/compiler/scala/tools/nsc/doc/html/resource/lib/trait_diagram.png and /dev/null differ -- cgit v1.2.3 From 6167ec678e8fceaacc51e6d8c496ebbc8c8485af Mon Sep 17 00:00:00 2001 From: Vlad Ureche Date: Mon, 18 Jun 2012 13:19:42 +0200 Subject: Revert "Scaladoc class diagrams part 1" This reverts commit 831f09bb6d00c152bd8aef3ce8bf9e200080fe36. --- src/compiler/scala/tools/nsc/ScalaDoc.scala | 3 +- src/compiler/scala/tools/nsc/doc/DocFactory.scala | 6 +- src/compiler/scala/tools/nsc/doc/Settings.scala | 58 +---- .../scala/tools/nsc/doc/html/HtmlPage.scala | 16 +- src/compiler/scala/tools/nsc/doc/html/Page.scala | 14 ++ .../scala/tools/nsc/doc/html/page/Index.scala | 2 +- .../tools/nsc/doc/html/page/IndexScript.scala | 2 +- .../scala/tools/nsc/doc/html/page/Template.scala | 91 ++------ .../nsc/doc/html/page/diagram/DiagramStats.scala | 58 ----- .../tools/nsc/doc/html/resource/lib/template.css | 6 +- .../scala/tools/nsc/doc/model/Entity.scala | 96 ++------ .../scala/tools/nsc/doc/model/ModelFactory.scala | 109 ++------- .../doc/model/ModelFactoryImplicitSupport.scala | 134 ++--------- .../scala/tools/nsc/doc/model/comment/Body.scala | 2 +- .../tools/nsc/doc/model/comment/Comment.scala | 6 - .../nsc/doc/model/comment/CommentFactory.scala | 140 +++++------- .../tools/nsc/doc/model/diagram/Diagram.scala | 143 ------------ .../doc/model/diagram/DiagramDirectiveParser.scala | 248 --------------------- .../nsc/doc/model/diagram/DiagramFactory.scala | 197 ---------------- .../scala/tools/partest/ScaladocModelTest.scala | 4 +- .../resources/implicits-ambiguating-res.scala | 72 ------ test/scaladoc/resources/implicits-base-res.scala | 16 +- .../resources/implicits-elimination-res.scala | 6 +- test/scaladoc/run/diagrams-base.check | 1 - test/scaladoc/run/diagrams-base.scala | 73 ------ test/scaladoc/run/diagrams-determinism.check | 1 - test/scaladoc/run/diagrams-determinism.scala | 67 ------ test/scaladoc/run/diagrams-filtering.check | 1 - test/scaladoc/run/diagrams-filtering.scala | 93 -------- test/scaladoc/run/implicits-ambiguating.check | 1 - test/scaladoc/run/implicits-ambiguating.scala | 111 --------- test/scaladoc/run/implicits-base.scala | 36 +-- test/scaladoc/run/implicits-elimination.check | 1 + test/scaladoc/run/implicits-elimination.scala | 23 ++ test/scaladoc/run/implicits-shadowing.scala | 32 ++- test/scaladoc/scalacheck/CommentFactoryTest.scala | 6 +- 36 files changed, 225 insertions(+), 1650 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala delete mode 100644 src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala delete mode 100644 src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala delete mode 100644 src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala delete mode 100644 test/scaladoc/resources/implicits-ambiguating-res.scala delete mode 100644 test/scaladoc/run/diagrams-base.check delete mode 100644 test/scaladoc/run/diagrams-base.scala delete mode 100644 test/scaladoc/run/diagrams-determinism.check delete mode 100644 test/scaladoc/run/diagrams-determinism.scala delete mode 100644 test/scaladoc/run/diagrams-filtering.check delete mode 100644 test/scaladoc/run/diagrams-filtering.scala delete mode 100644 test/scaladoc/run/implicits-ambiguating.check delete mode 100644 test/scaladoc/run/implicits-ambiguating.scala create mode 100644 test/scaladoc/run/implicits-elimination.check create mode 100644 test/scaladoc/run/implicits-elimination.scala diff --git a/src/compiler/scala/tools/nsc/ScalaDoc.scala b/src/compiler/scala/tools/nsc/ScalaDoc.scala index c6fdb4b891..5a4b4172c6 100644 --- a/src/compiler/scala/tools/nsc/ScalaDoc.scala +++ b/src/compiler/scala/tools/nsc/ScalaDoc.scala @@ -20,8 +20,7 @@ class ScalaDoc { def process(args: Array[String]): Boolean = { var reporter: ConsoleReporter = null - val docSettings = new doc.Settings(msg => reporter.error(FakePos("scaladoc"), msg + "\n scaladoc -help gives more information"), - msg => reporter.printMessage(msg)) + val docSettings = new doc.Settings(msg => reporter.error(FakePos("scaladoc"), msg + "\n scaladoc -help gives more information")) reporter = new ConsoleReporter(docSettings) { // need to do this so that the Global instance doesn't trash all the // symbols just because there was an error diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index c6a9422974..e2e1ddf065 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -81,7 +81,6 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor new { override val global: compiler.type = compiler } with model.ModelFactory(compiler, settings) with model.ModelFactoryImplicitSupport - with model.diagram.DiagramFactory with model.comment.CommentFactory with model.TreeFactory { override def templateShouldDocument(sym: compiler.Symbol) = @@ -91,12 +90,11 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor modelFactory.makeModel match { case Some(madeModel) => - if (!settings.scaladocQuietRun) + if (settings.reportModel) println("model contains " + modelFactory.templatesCount + " documentable templates") Some(madeModel) case None => - if (!settings.scaladocQuietRun) - println("no documentable class found in compilation units") + println("no documentable class found in compilation units") None } diff --git a/src/compiler/scala/tools/nsc/doc/Settings.scala b/src/compiler/scala/tools/nsc/doc/Settings.scala index f8b2711c0c..4458889d55 100644 --- a/src/compiler/scala/tools/nsc/doc/Settings.scala +++ b/src/compiler/scala/tools/nsc/doc/Settings.scala @@ -11,9 +11,8 @@ import java.lang.System import language.postfixOps /** An extended version of compiler settings, with additional Scaladoc-specific options. - * @param error A function that prints a string to the appropriate error stream - * @param print A function that prints the string, without any extra boilerplate of error */ -class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) extends scala.tools.nsc.Settings(error) { + * @param error A function that prints a string to the appropriate error stream. */ +class Settings(error: String => Unit) extends scala.tools.nsc.Settings(error) { /** A setting that defines in which format the documentation is output. ''Note:'' this setting is currently always * `html`. */ @@ -105,12 +104,6 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "(for example conversions that require Numeric[String] to be in scope)" ) - val docImplicitsSoundShadowing = BooleanSetting ( - "-implicits-sound-shadowing", - "Use a sound implicit shadowing calculation. Note: this interacts badly with usecases, so " + - "only use it if you haven't defined usecase for implicitly inherited members." - ) - val docDiagrams = BooleanSetting ( "-diagrams", "Create inheritance diagrams for classes, traits and packages." @@ -123,44 +116,10 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) val docDiagramsDotPath = PathSetting ( "-diagrams-dot-path", - "The path to the dot executable used to generate the inheritance diagrams. Eg: /usr/bin/dot", + "The path to the dot executable used to generate the inheritance diagrams. Ex: /usr/bin/dot", "dot" // by default, just pick up the system-wide dot ) - /** The maxium nuber of normal classes to show in the diagram */ - val docDiagramsMaxNormalClasses = IntSetting( - "-diagrams-max-classes", - "The maximum number of superclasses or subclasses to show in a diagram", - 15, - None, - _ => None - ) - - /** The maxium nuber of implcit classes to show in the diagram */ - val docDiagramsMaxImplicitClasses = IntSetting( - "-diagrams-max-implicits", - "The maximum number of implicitly converted classes to show in a diagram", - 10, - None, - _ => None - ) - - val docDiagramsDotTimeout = IntSetting( - "-diagrams-dot-timeout", - "The timeout before the graphviz dot util is forecefully closed, in seconds (default: 10)", - 10, - None, - _ => None - ) - - val docDiagramsDotRestart = IntSetting( - "-diagrams-dot-restart", - "The number of times to restart a malfunctioning dot process before disabling diagrams (default: 5)", - 5, - None, - _ => None - ) - // Somewhere slightly before r18708 scaladoc stopped building unless the // self-type check was suppressed. I hijacked the slotted-for-removal-anyway // suppress-vt-warnings option and renamed it for this purpose. @@ -170,16 +129,14 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) def scaladocSpecific = Set[Settings#Setting]( docformat, doctitle, docfooter, docversion, docUncompilable, docsourceurl, docgenerator, docRootContent, useStupidTypes, docDiagrams, docDiagramsDebug, docDiagramsDotPath, - docDiagramsDotTimeout, docDiagramsDotRestart, - docImplicits, docImplicitsDebug, docImplicitsShowAll, - docDiagramsMaxNormalClasses, docDiagramsMaxImplicitClasses + docImplicits, docImplicitsDebug, docImplicitsShowAll ) val isScaladocSpecific: String => Boolean = scaladocSpecific map (_.name) override def isScaladoc = true - // set by the testsuite, when checking test output - var scaladocQuietRun = false + // unset by the testsuite, we don't need to count the entities in the model + var reportModel = true /** * This is the hardcoded area of Scaladoc. This is where "undesirable" stuff gets eliminated. I know it's not pretty, @@ -225,8 +182,7 @@ class Settings(error: String => Unit, val printMsg: String => Unit = println(_)) "scala.Predef.any2stringfmt", "scala.Predef.any2stringadd", "scala.Predef.any2ArrowAssoc", - "scala.Predef.any2Ensuring", - "scala.collection.TraversableOnce.alternateImplicit") + "scala.Predef.any2Ensuring") /** There's a reason all these are specialized by hand but documenting each of them is beyond the point */ val arraySkipConversions = List( diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala index 34e2f7bc53..e3da8bddea 100644 --- a/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/compiler/scala/tools/nsc/doc/html/HtmlPage.scala @@ -116,25 +116,11 @@ abstract class HtmlPage extends Page { thisPage => case Superscript(in) => { inlineToHtml(in) } case Subscript(in) => { inlineToHtml(in) } case Link(raw, title) => { inlineToHtml(title) } + case EntityLink(entity) => templateToHtml(entity) case Monospace(in) => { inlineToHtml(in) } case Text(text) => xml.Text(text) case Summary(in) => inlineToHtml(in) case HtmlTag(tag) => xml.Unparsed(tag) - case EntityLink(target, template) => template() match { - case Some(tpl) => - templateToHtml(tpl) - case None => - xml.Text(target) - } - } - - def typeToHtml(tpes: List[model.TypeEntity], hasLinks: Boolean): NodeSeq = tpes match { - case Nil => - sys.error("Internal Scaladoc error") - case List(tpe) => - typeToHtml(tpe, hasLinks) - case tpe :: rest => - typeToHtml(tpe, hasLinks) ++ scala.xml.Text(" with ") ++ typeToHtml(rest, hasLinks) } def typeToHtml(tpe: model.TypeEntity, hasLinks: Boolean): NodeSeq = { diff --git a/src/compiler/scala/tools/nsc/doc/html/Page.scala b/src/compiler/scala/tools/nsc/doc/html/Page.scala index 59f4c81632..c5bf3e0e37 100644 --- a/src/compiler/scala/tools/nsc/doc/html/Page.scala +++ b/src/compiler/scala/tools/nsc/doc/html/Page.scala @@ -83,4 +83,18 @@ abstract class Page { } relativize(thisPage.path.reverse, destPath.reverse).mkString("/") } + + def isExcluded(dtpl: DocTemplateEntity) = { + val qname = dtpl.qualifiedName + ( ( qname.startsWith("scala.Tuple") || qname.startsWith("scala.Product") || + qname.startsWith("scala.Function") || qname.startsWith("scala.runtime.AbstractFunction") + ) && !( + qname == "scala.Tuple1" || qname == "scala.Tuple2" || + qname == "scala.Product" || qname == "scala.Product1" || qname == "scala.Product2" || + qname == "scala.Function" || qname == "scala.Function1" || qname == "scala.Function2" || + qname == "scala.runtime.AbstractFunction0" || qname == "scala.runtime.AbstractFunction1" || + qname == "scala.runtime.AbstractFunction2" + ) + ) + } } diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala index 0e894a03bf..8ed13e0da2 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Index.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Index.scala @@ -61,7 +61,7 @@ class Index(universe: doc.Universe, index: doc.Index) extends HtmlPage { }
    { val tpls: Map[String, Seq[DocTemplateEntity]] = - (pack.templates filter (t => !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) )) groupBy (_.name) + (pack.templates filter (t => !t.isPackage && !isExcluded(t) )) groupBy (_.name) val placeholderSeq: NodeSeq =
    diff --git a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala index 1b0599d145..7edd4937c4 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/IndexScript.scala @@ -68,7 +68,7 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page { def allPackagesWithTemplates = { Map(allPackages.map((key) => { - key -> key.templates.filter(t => !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName)) + key -> key.templates.filter(t => !t.isPackage && !isExcluded(t)) }) : _*) } } diff --git a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala index ce0e6b7856..66189a6854 100644 --- a/src/compiler/scala/tools/nsc/doc/html/page/Template.scala +++ b/src/compiler/scala/tools/nsc/doc/html/page/Template.scala @@ -8,11 +8,10 @@ package doc package html package page +import model._ import scala.xml.{ NodeSeq, Text, UnprefixedAttribute } import language.postfixOps -import model._ - class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { val path = @@ -42,12 +41,9 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage val (absValueMembers, nonAbsValueMembers) = valueMembers partition (_.isAbstract) - val (deprValueMembers, nonDeprValueMembers) = + val (deprValueMembers, concValueMembers) = nonAbsValueMembers partition (_.deprecation.isDefined) - val (concValueMembers, shadowedImplicitMembers) = - nonDeprValueMembers partition (!Template.isShadowedOrAmbiguousImplicit(_)) - val typeMembers = tpl.abstractTypes ++ tpl.aliasTypes ++ tpl.templates.filter(x => x.isTrait || x.isClass) sorted @@ -167,13 +163,6 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage
} - { if (shadowedImplicitMembers.isEmpty) NodeSeq.Empty else -
-

Shadowed Implict Value Members

-
    { shadowedImplicitMembers map (memberToHtml(_)) }
-
- } - { if (deprValueMembers.isEmpty) NodeSeq.Empty else

Deprecated Value Members

@@ -254,13 +243,11 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage case _ => "" } val memberComment = memberToCommentHtml(mbr, false) -
  • - - { signature(mbr, false) } - { memberComment } +
  • + + { signature(mbr, false) } + { memberComment }
  • } @@ -312,7 +299,6 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage

    { inlineToHtml(mbr.comment.get.short) }

    def memberToCommentBodyHtml(mbr: MemberEntity, isSelf: Boolean, isReduced: Boolean = false): NodeSeq = { - val s = universe.settings val memberComment = if (mbr.comment.isEmpty) NodeSeq.Empty @@ -368,8 +354,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } val implicitInformation = mbr.byConversion match { - case Some(info) => - val conv = info.conversion + case Some(conv) =>
    Implicit information
    ++ { val targetType = typeToHtml(conv.targetType, true) @@ -402,35 +387,6 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { targetType } performed by method { conversionMethod } in { conversionOwner }. { constraintText } - } ++ { - if (Template.isShadowedOrAmbiguousImplicit(mbr)) { - // These are the members that are shadowing or ambiguating the current implicit - // see ImplicitMemberShadowing trait for more information - val shadowingSuggestion = { - val params = mbr match { - case d: Def => d.valueParams map (_ map (_ name) mkString("(", ", ", ")")) mkString - case _ => "" // no parameters - } -
    ++ xml.Text("To access this member you can use a ") ++ -
    type ascription ++ xml.Text(":") ++ -
    ++
    {"(" + Template.lowerFirstLetter(tpl.name) + ": " + conv.targetType.name + ")." + mbr.name + params }
    - } - - val shadowingWarning: NodeSeq = - if (Template.isShadowedImplicit(mbr)) - xml.Text("This implicitly inherited member is shadowed by one or more members in this " + - "class.") ++ shadowingSuggestion - else if (Template.isAmbiguousImplicit(mbr)) - xml.Text("This implicitly inherited member is ambiguous. One or more implicitly " + - "inherited members have similar signatures, so calling this member may produce an ambiguous " + - "implicit conversion compiler error.") ++ shadowingSuggestion - else NodeSeq.Empty - -
    Shadowing
    ++ -
    { shadowingWarning }
    - - } else NodeSeq.Empty } case _ => NodeSeq.Empty @@ -606,11 +562,11 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } val subclasses = mbr match { - case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.allSubClasses.nonEmpty => + case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.subClasses.nonEmpty =>
    Known Subclasses
    { - templatesToHtml(dtpl.allSubClasses.sortBy(_.name), xml.Text(", ")) + templatesToHtml(dtpl.subClasses.sortBy(_.name), xml.Text(", ")) }
    case _ => NodeSeq.Empty @@ -649,13 +605,13 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage case PrivateInTemplate(owner) if (owner == mbr.inTemplate) => Some(Paragraph(CText("private"))) case PrivateInTemplate(owner) => - Some(Paragraph(Chain(List(CText("private["), EntityLink(owner.qualifiedName, () => Some(owner)), CText("]"))))) + Some(Paragraph(Chain(List(CText("private["), EntityLink(owner), CText("]"))))) case ProtectedInInstance() => Some(Paragraph(CText("protected[this]"))) case ProtectedInTemplate(owner) if (owner == mbr.inTemplate) => Some(Paragraph(CText("protected"))) case ProtectedInTemplate(owner) => - Some(Paragraph(Chain(List(CText("protected["), EntityLink(owner.qualifiedName, () => Some(owner)), CText("]"))))) + Some(Paragraph(Chain(List(CText("protected["), EntityLink(owner), CText("]"))))) case Public() => None } @@ -671,15 +627,7 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage { - val nameClass = - if (Template.isImplicit(mbr)) - if (Template.isShadowedOrAmbiguousImplicit(mbr)) - "implicit shadowed" - else - "implicit" - else - "name" - + val nameClass = if (mbr.byConversion.isDefined) "implicit" else "name" val nameHtml = { val value = if (mbr.isConstructor) tpl.name else mbr.name val span = if (mbr.deprecation.isDefined) @@ -751,8 +699,8 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage } }{ if (isReduced) NodeSeq.Empty else { mbr match { - case tpl: DocTemplateEntity if !tpl.parentTypes.isEmpty => - extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) } + case tpl: DocTemplateEntity if tpl.parentType.isDefined => + extends { typeToHtml(tpl.parentType.get, hasLinks) } case tme: MemberEntity if (tme.isDef || tme.isVal || tme.isLazyVal || tme.isVar) => : { typeToHtml(tme.resultType, hasLinks) } @@ -922,14 +870,5 @@ class Template(universe: doc.Universe, tpl: DocTemplateEntity) extends HtmlPage xml.Text(ub.typeParamName + " is a subclass of " + ub.upperBound.name + " (" + ub.typeParamName + " <: ") ++ typeToHtml(ub.upperBound, true) ++ xml.Text(")") } -} - -object Template { - - def isImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined - def isShadowedImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined && mbr.byConversion.get.isShadowed - def isAmbiguousImplicit(mbr: MemberEntity) = mbr.byConversion.isDefined && mbr.byConversion.get.isAmbiguous - def isShadowedOrAmbiguousImplicit(mbr: MemberEntity) = isShadowedImplicit(mbr) || isAmbiguousImplicit(mbr) - def lowerFirstLetter(s: String) = if (s.length >= 1) s.substring(0,1).toLowerCase() + s.substring(1) else s } diff --git a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala b/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala deleted file mode 100644 index 16d859894f..0000000000 --- a/src/compiler/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @author Vlad Ureche - */ -package scala.tools.nsc.doc -package html.page.diagram - -object DiagramStats { - - class TimeTracker(title: String) { - var totalTime: Long = 0l - var maxTime: Long = 0l - var instances: Int = 0 - - def addTime(ms: Long) = { - if (maxTime < ms) - maxTime = ms - totalTime += ms - instances += 1 - } - - def printStats(print: String => Unit) = { - if (instances == 0) - print(title + ": no stats gathered") - else { - print(" " + title) - print(" " + "=" * title.length) - print(" count: " + instances + " items") - print(" total time: " + totalTime + " ms") - print(" average time: " + (totalTime/instances) + " ms") - print(" maximum time: " + maxTime + " ms") - print("") - } - } - } - - private[this] val filterTrack = new TimeTracker("diagrams model filtering") - private[this] val modelTrack = new TimeTracker("diagrams model generation") - private[this] val dotGenTrack = new TimeTracker("dot diagram generation") - private[this] val dotRunTrack = new TimeTracker("dot process runnning") - private[this] val svgTrack = new TimeTracker("svg processing") - - def printStats(settings: Settings) = { - if (settings.docDiagramsDebug.value) { - settings.printMsg("\nDiagram generation running time breakdown:\n") - filterTrack.printStats(settings.printMsg) - modelTrack.printStats(settings.printMsg) - dotGenTrack.printStats(settings.printMsg) - dotRunTrack.printStats(settings.printMsg) - svgTrack.printStats(settings.printMsg) - } - } - - def addFilterTime(ms: Long) = filterTrack.addTime(ms) - def addModelTime(ms: Long) = modelTrack.addTime(ms) - def addDotGenerationTime(ms: Long) = dotGenTrack.addTime(ms) - def addDotRunningTime(ms: Long) = dotRunTrack.addTime(ms) - def addSvgTime(ms: Long) = svgTrack.addTime(ms) -} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css index b25f0d0b3f..5a1779bba5 100644 --- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css +++ b/src/compiler/scala/tools/nsc/doc/html/resource/lib/template.css @@ -333,10 +333,6 @@ div.members > ol > li:last-child { color: darkgreen; } -.signature .symbol .shadowed { - color: darkseagreen; -} - .signature .symbol .params > .implicit { font-style: italic; } @@ -806,4 +802,4 @@ div.fullcomment dl.paramcmts > dd { #mbrsel .showall span { color: #4C4C4C; font-weight: bold; -}*/ +}*/ \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/Entity.scala b/src/compiler/scala/tools/nsc/doc/model/Entity.scala index 5d1413abd4..6488847049 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Entity.scala +++ b/src/compiler/scala/tools/nsc/doc/model/Entity.scala @@ -10,7 +10,7 @@ package model import scala.collection._ import comment._ -import diagram._ + /** An entity in a Scaladoc universe. Entities are declarations in the program and correspond to symbols in the * compiler. Entities model the following Scala concepts: @@ -86,20 +86,9 @@ trait TemplateEntity extends Entity { /** Whether this template is a case class. */ def isCaseClass: Boolean - /** The type of this entity, with type members */ - def ownType: TypeEntity - /** The self-type of this template, if it differs from the template type. */ def selfType : Option[TypeEntity] - /** The full template name `[kind] qualifiedName` */ - def fullName = - (if (isPackage) "package " - else if (isCaseClass) "case class " - else if (isClass) "class " - else if (isTrait) "trait " - else if (isObject) "object " - else "") + qualifiedName } @@ -178,12 +167,8 @@ trait MemberEntity extends Entity { /** Whether this member is abstract. */ def isAbstract: Boolean - /** If this symbol is a use case, the useCaseOf will contain the member it was derived from, containing the full - * signature and the complete parameter descriptions. */ - def useCaseOf: Option[MemberEntity] = None - /** If this member originates from an implicit conversion, we set the implicit information to the correct origin */ - def byConversion: Option[ImplicitConversionInfo] + def byConversion: Option[ImplicitConversion] } object MemberEntity { // Oh contravariance, contravariance, wherefore art thou contravariance? @@ -221,10 +206,8 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { * only if the `docsourceurl` setting has been set. */ def sourceUrl: Option[java.net.URL] - /** The direct super-type of this template - e.g: {{{class A extends B[C[Int]] with D[E]}}} will have two direct parents: class B and D - NOTE: we are dropping the refinement here! */ - def parentTypes: List[(TemplateEntity, TypeEntity)] + /** The direct super-type of this template. */ + def parentType: Option[TypeEntity] @deprecated("Use `linearizationTemplates` and `linearizationTypes` instead", "2.9.0") def linearization: List[(TemplateEntity, TypeEntity)] @@ -237,13 +220,9 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { * This template's linearization contains all of its direct and indirect super-types. */ def linearizationTypes: List[TypeEntity] - /** All class, trait and object templates for which this template is a direct or indirect super-class or super-trait. - * Only templates for which documentation is available in the universe (`DocTemplateEntity`) are listed. */ - def allSubClasses: List[DocTemplateEntity] - - /** All class, trait and object templates for which this template is a *direct* super-class or super-trait. - * Only templates for which documentation is available in the universe (`DocTemplateEntity`) are listed. */ - def directSubClasses: List[DocTemplateEntity] + /**All class, trait and object templates for which this template is a direct or indirect super-class or super-trait. + * Only templates for which documentation is available in the universe (`DocTemplateEntity`) are listed. */ + def subClasses: List[DocTemplateEntity] /** All members of this template. If this template is a package, only templates for which documentation is available * in the universe (`DocTemplateEntity`) are listed. */ @@ -271,19 +250,6 @@ trait DocTemplateEntity extends TemplateEntity with MemberEntity { /** The implicit conversions this template (class or trait, objects and packages are not affected) */ def conversions: List[ImplicitConversion] - - /** Classes that can be implcitly converted to this class */ - def incomingImplicitlyConvertedClasses: List[DocTemplateEntity] - - /** Classes to which this class can be implicitly converted to - NOTE: Some classes might not be included in the scaladoc run so they will be NoDocTemplateEntities */ - def outgoingImplicitlyConvertedClasses: List[(TemplateEntity, TypeEntity)] - - /** If this template takes place in inheritance and implicit conversion relations, it will be shown in this diagram */ - def inheritanceDiagram: Option[Diagram] - - /** If this template contains other templates, such as classes and traits, they will be shown in this diagram */ - def contentDiagram: Option[Diagram] } @@ -339,6 +305,10 @@ trait NonTemplateMemberEntity extends MemberEntity { * It corresponds to a real member, and provides a simplified, yet compatible signature for that member. */ def isUseCase: Boolean + /** If this symbol is a use case, the useCaseOf will contain the member it was derived from, containing the full + * signature and the complete parameter descriptions. */ + def useCaseOf: Option[MemberEntity] + /** Whether this member is a bridge member. A bridge member does only exist for binary compatibility reasons * and should not appear in ScalaDoc. */ def isBridge: Boolean @@ -457,15 +427,6 @@ trait ImplicitConversion { /** The result type after the conversion */ def targetType: TypeEntity - /** The result type after the conversion - * Note: not all targetTypes have a corresponding template. Examples include conversions resulting in refinement - * types. Need to check it's not option! - */ - def targetTemplate: Option[TemplateEntity] - - /** The components of the implicit conversion type parents */ - def targetTypeComponents: List[(TemplateEntity, TypeEntity)] - /** The entity for the method that performed the conversion, if it's documented (or just its name, otherwise) */ def convertorMethod: Either[MemberEntity, String] @@ -485,38 +446,11 @@ trait ImplicitConversion { def members: List[MemberEntity] } -trait ImplicitConversionInfo { - /** The implicit conversion this member originates from */ - def conversion: ImplicitConversion - - /** The shadowing information for this member */ - def shadowing: ImplicitMemberShadowing - - /* Quick getters */ - def isShadowed: Boolean = !shadowing.shadowingMembers.isEmpty - def isAmbiguous: Boolean = !shadowing.ambiguatingMembers.isEmpty -} - -/** Shadowing captures the information that the member is shadowed by some other members - * There are two cases of implicitly added member shadowing: - * 1) shadowing from a original class member (the class already has that member) - * in this case, it won't be possible to call the member directly, the type checker will fail attempting to adapt - * the call arguments (or if they fit it will call the original class' method) - * 2) shadowing from other possible implicit conversions () - * this will result in an ambiguous implicit converion error - */ -trait ImplicitMemberShadowing { - /** The members that shadow the current entry use .inTemplate to get to the template name */ - def shadowingMembers: List[MemberEntity] - - /** The members that ambiguate this implicit conversion - Note: for ambiguatingMembers you have the following invariant: - assert(ambiguatingMembers.foreach(_.byConversion.isDefined) */ - def ambiguatingMembers: List[MemberEntity] -} - /** A trait that encapsulates a constraint necessary for implicit conversion */ -trait Constraint +trait Constraint { + // /** The implicit conversion during which this constraint appears */ + // def conversion: ImplicitConversion +} /** A constraint involving a type parameter which must be in scope */ trait ImplicitInScopeConstraint extends Constraint { diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index eb916333ab..3dd77d47da 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -6,8 +6,6 @@ package model import comment._ -import diagram._ - import scala.collection._ import scala.util.matching.Regex @@ -19,7 +17,7 @@ import model.{ RootPackage => RootPackageEntity } /** This trait extracts all required information for documentation from compilation units */ class ModelFactory(val global: Global, val settings: doc.Settings) { - thisFactory: ModelFactory with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with TreeFactory => + thisFactory: ModelFactory with ModelFactoryImplicitSupport with CommentFactory with TreeFactory => import global._ import definitions.{ ObjectClass, NothingClass, AnyClass, AnyValClass, AnyRefClass } @@ -91,7 +89,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isObject = sym.isModule && !sym.isPackage def isCaseClass = sym.isCaseClass def isRootPackage = false - def ownType = makeType(sym.tpe, this) def selfType = if (sym.thisSym eq sym) None else Some(makeType(sym.thisSym.typeOfThis, this)) } @@ -99,7 +96,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def isDocTemplate = false } - abstract class MemberImpl(sym: Symbol, implConv: ImplicitConversionInfoImpl = null, inTpl: => DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { + abstract class MemberImpl(sym: Symbol, implConv: ImplicitConversionImpl = null, inTpl: => DocTemplateImpl) extends EntityImpl(sym, inTpl) with MemberEntity { lazy val comment = if (inTpl == null) None else thisFactory.comment(sym, inTpl) override def inTemplate = inTpl @@ -173,7 +170,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { case NullaryMethodType(res) => resultTpe(res) case _ => tpe } - val tpe = if (implConv eq null) sym.tpe else implConv.conversion.toType memberInfo sym + val tpe = if (implConv eq null) sym.tpe else implConv.toType memberInfo sym makeTypeInTemplateContext(resultTpe(tpe), inTemplate, sym) } def isDef = false @@ -229,26 +226,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } else None } - - def parentTemplates = - if (sym.isPackage || sym == AnyClass) - List() - else - sym.tpe.parents.flatMap { tpe: Type => - val tSym = tpe.typeSymbol - if (tSym != NoSymbol) - List(makeTemplate(tSym)) - else - List() - } filter (_.isInstanceOf[DocTemplateEntity]) - - def parentTypes = - if (sym.isPackage || sym == AnyClass) List() else { + def parentType = { + if (sym.isPackage || sym == AnyClass) None else { val tps = sym.tpe.parents map { _.asSeenFrom(sym.thisType, sym) } - makeParentTypes(RefinedType(tps, EmptyScope), inTpl) + Some(makeType(RefinedType(tps, EmptyScope), inTpl)) } + } - protected def linearizationFromSymbol(symbol: Symbol): List[(TemplateEntity, TypeEntity)] = { + protected def linearizationFromSymbol(symbol: Symbol) = { symbol.ancestors map { ancestor => val typeEntity = makeType(symbol.info.baseType(ancestor), this) val tmplEntity = makeTemplate(ancestor) match { @@ -263,7 +248,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def linearizationTemplates = linearization map { _._1 } def linearizationTypes = linearization map { _._2 } - /* Subclass cache */ private lazy val subClassesCache = ( if (noSubclassCache(sym)) null else mutable.ListBuffer[DocTemplateEntity]() @@ -272,47 +256,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { if (subClassesCache != null) subClassesCache += sc } - def allSubClasses = if (subClassesCache == null) Nil else subClassesCache.toList - def directSubClasses = allSubClasses.filter(_.parentTypes.map(_._1).contains(this)) - - /* Implcitly convertible class cache */ - private var implicitlyConvertibleClassesCache: mutable.ListBuffer[DocTemplateEntity] = null - def registerImplicitlyConvertibleClass(sc: DocTemplateEntity): Unit = { - if (implicitlyConvertibleClassesCache == null) - implicitlyConvertibleClassesCache = mutable.ListBuffer[DocTemplateEntity]() - implicitlyConvertibleClassesCache += sc - } - - def incomingImplicitlyConvertedClasses: List[DocTemplateEntity] = - if (implicitlyConvertibleClassesCache == null) - List() - else - implicitlyConvertibleClassesCache.toList + def subClasses = if (subClassesCache == null) Nil else subClassesCache.toList val conversions = if (settings.docImplicits.value) makeImplicitConversions(sym, this) else Nil - val outgoingImplicitlyConvertedClasses: List[(TemplateEntity, TypeEntity)] = conversions flatMap (conv => - if (!implicitExcluded(conv.conversionQualifiedName)) - conv.targetTypeComponents map { - case pair@(template, tpe) => - template match { - case d: DocTemplateImpl => d.registerImplicitlyConvertibleClass(this) - case _ => // nothing - } - pair - } - else List() - ) - lazy val memberSyms = // Only this class's constructors are part of its members, inherited constructors are not. sym.info.members.filter(s => localShouldDocument(s) && (!s.isConstructor || s.owner == sym) && !isPureBridge(sym) ) - // in members, we also take in the members from implicit conversions - lazy val ownMembers = (memberSyms.flatMap(makeMember(_, null, this))) - lazy val allOwnMembers = (ownMembers ::: ownMembers.flatMap(_.useCaseOf.map(_.asInstanceOf[MemberImpl]))).distinct - - val members = ownMembers ::: (conversions.flatMap((_.members))) + val members = (memberSyms.flatMap(makeMember(_, null, this))) ::: + (conversions.flatMap((_.members))) // also take in the members from implicit conversions val templates = members collect { case c: DocTemplateEntity => c } val methods = members collect { case d: Def => d } @@ -327,10 +280,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { Some(makeDocTemplate(comSym, inTpl)) case _ => None } - - // We make the diagram a lazy val, since we're not sure we'll include the diagrams in the page - lazy val inheritanceDiagram = makeInheritanceDiagram(this) - lazy val contentDiagram = makeContentDiagram(this) } abstract class PackageImpl(sym: Symbol, inTpl: => PackageImpl) extends DocTemplateImpl(sym, inTpl) with Package { @@ -347,18 +296,18 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { abstract class RootPackageImpl(sym: Symbol) extends PackageImpl(sym, null) with RootPackageEntity - abstract class NonTemplateMemberImpl(sym: Symbol, implConv: ImplicitConversionInfoImpl, inTpl: => DocTemplateImpl) extends MemberImpl(sym, implConv, inTpl) with NonTemplateMemberEntity { + abstract class NonTemplateMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl) extends MemberImpl(sym, implConv, inTpl) with NonTemplateMemberEntity { override def qualifiedName = optimize(inTemplate.qualifiedName + "#" + name) lazy val definitionName = if (implConv == null) optimize(inDefinitionTemplates.head.qualifiedName + "#" + name) - else optimize(implConv.conversion.conversionQualifiedName + "#" + name) + else optimize(implConv.conversionQualifiedName + "#" + name) def isUseCase = sym.isSynthetic def isBridge = sym.isBridge } - abstract class NonTemplateParamMemberImpl(sym: Symbol, implConv: ImplicitConversionInfoImpl, inTpl: => DocTemplateImpl) extends NonTemplateMemberImpl(sym, implConv, inTpl) { + abstract class NonTemplateParamMemberImpl(sym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl) extends NonTemplateMemberImpl(sym, implConv, inTpl) { def valueParams = { - val info = if (implConv eq null) sym.info else implConv.conversion.toType memberInfo sym + val info = if (implConv eq null) sym.info else implConv.toType memberInfo sym info.paramss map { ps => (ps.zipWithIndex) map { case (p, i) => if (p.nameString contains "$") makeValueParam(p, inTpl, optimize("arg" + i)) else makeValueParam(p, inTpl) }} @@ -386,6 +335,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def typeParams = sym.typeParams map (makeTypeParam(_, inTemplate)) } + /* ============== MAKER METHODS ============== */ /** */ @@ -533,7 +483,8 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { } /** */ - def makeMember(aSym: Symbol, implConv: ImplicitConversionInfoImpl, inTpl: => DocTemplateImpl): List[MemberImpl] = { + // TODO: Should be able to override the type + def makeMember(aSym: Symbol, implConv: ImplicitConversionImpl, inTpl: => DocTemplateImpl): List[MemberImpl] = { def makeMember0(bSym: Symbol, _useCaseOf: Option[MemberImpl]): Option[MemberImpl] = { if (bSym.isGetter && bSym.isLazy) @@ -553,10 +504,10 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { if (bSym == definitions.Object_synchronized) { val cSymInfo = (bSym.info: @unchecked) match { case PolyType(ts, MethodType(List(bp), mt)) => - val cp = bp.cloneSymbol.setPos(bp.pos).setInfo(definitions.byNameType(bp.info)) + val cp = bp.cloneSymbol.setInfo(definitions.byNameType(bp.info)) PolyType(ts, MethodType(List(cp), mt)) } - bSym.cloneSymbol.setPos(bSym.pos).setInfo(cSymInfo) + bSym.cloneSymbol.setInfo(cSymInfo) } else bSym } @@ -668,20 +619,6 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { makeType(tpe, inTpl) } - /** Get the types of the parents of the current class, ignoring the refinements */ - def makeParentTypes(aType: Type, inTpl: => TemplateImpl): List[(TemplateEntity, TypeEntity)] = aType match { - case RefinedType(parents, defs) => - val ignoreParents = Set[Symbol](AnyClass, ObjectClass) - val filtParents = parents filterNot (x => ignoreParents(x.typeSymbol)) - filtParents.map(parent => { - val templateEntity = makeTemplate(parent.typeSymbol) - val typeEntity = makeType(parent, inTpl) - (templateEntity, typeEntity) - }) - case _ => - List((makeTemplate(aType.typeSymbol), makeType(aType, inTpl))) - } - /** */ def makeType(aType: Type, inTpl: => TemplateImpl): TypeEntity = { def templatePackage = closestPackage(inTpl.sym) @@ -801,10 +738,4 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { /** Filter '@bridge' methods only if *they don't override non-bridge methods*. See SI-5373 for details */ def isPureBridge(sym: Symbol) = sym.isBridge && sym.allOverriddenSymbols.forall(_.isBridge) - - // the classes that are excluded from the index should also be excluded from the diagrams - def classExcluded(clazz: TemplateEntity): Boolean = settings.hardcoded.isExcluded(clazz.qualifiedName) - - // the implicit conversions that are excluded from the pages should not appear in the diagram - def implicitExcluded(convertorMethod: String): Boolean = settings.hardcoded.commonConversionTargets.contains(convertorMethod) } diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index 64205104aa..c3525037cd 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -63,8 +63,8 @@ trait ModelFactoryImplicitSupport { // debugging: val DEBUG: Boolean = settings.docImplicitsDebug.value val ERROR: Boolean = true // currently we show all errors - @inline final def debug(msg: => String) = if (DEBUG) settings.printMsg(msg) - @inline final def error(msg: => String) = if (ERROR) settings.printMsg(msg) + @inline final def debug(msg: => String) = if (DEBUG) println(msg) + @inline final def error(msg: => String) = if (ERROR) println(msg) /** This is a flag that indicates whether to eliminate implicits that cannot be satisfied within the current scope. * For example, if an implicit conversion requires that there is a Numeric[T] in scope: @@ -96,17 +96,6 @@ trait ModelFactoryImplicitSupport { def targetType: TypeEntity = makeType(toType, inTpl) - def targetTemplate: Option[TemplateEntity] = toType match { - // @Vlad: I'm being extra conservative in template creation -- I don't want to create templates for complex types - // such as refinement types because the template can't represent the type corectly (a template corresponds to a - // package, class, trait or object) - case t: TypeRef => Some(makeTemplate(t.sym)) - case RefinedType(parents, decls) => None - case _ => error("Scaladoc implicits: Could not create template for: " + toType + " of type " + toType.getClass); None - } - - def targetTypeComponents: List[(TemplateEntity, TypeEntity)] = makeParentTypes(toType, inTpl) - def convertorOwner: TemplateEntity = if (convSym != NoSymbol) makeTemplate(convSym.owner) @@ -137,15 +126,20 @@ trait ModelFactoryImplicitSupport { lazy val constraints: List[Constraint] = constrs - private val memberImpls: List[MemberImpl] = { + val members: List[MemberEntity] = { // Obtain the members inherited by the implicit conversion - val memberSyms = toType.members.filter(implicitShouldDocument(_)) - val existingSyms = sym.info.members + var memberSyms = toType.members.filter(implicitShouldDocument(_)) + val existingMembers = sym.info.members // Debugging part :) debug(sym.nameString + "\n" + "=" * sym.nameString.length()) debug(" * conversion " + convSym + " from " + sym.tpe + " to " + toType) + // Members inherited by implicit conversions cannot override actual members + memberSyms = memberSyms.filterNot((sym1: Symbol) => + existingMembers.exists(sym2 => sym1.name == sym2.name && + !isDistinguishableFrom(toType.memberInfo(sym1), sym.info.memberInfo(sym2)))) + debug(" -> full type: " + toType) if (constraints.length != 0) { debug(" -> constraints: ") @@ -155,89 +149,10 @@ trait ModelFactoryImplicitSupport { memberSyms foreach (sym => debug(" - "+ sym.decodedName +" : " + sym.info)) debug("") - memberSyms.flatMap({ aSym => - makeTemplate(aSym.owner) match { - case d: DocTemplateImpl => - // we can't just pick up nodes from the previous template, although that would be very convenient: - // they need the byConversion field to be attached to themselves -- this is design decision I should - // revisit soon - // - // d.ownMembers.collect({ - // // it's either a member or has a couple of usecases it's hidden behind - // case m: MemberImpl if m.sym == aSym => - // m // the member itself - // case m: MemberImpl if m.useCaseOf.isDefined && m.useCaseOf.get.asInstanceOf[MemberImpl].sym == aSym => - // m.useCaseOf.get.asInstanceOf[MemberImpl] // the usecase - // }) - makeMember(aSym, new ImplicitConversionInfoImpl(this), d) - case _ => - // should only happen if the code for this template is not part of the scaladoc run => - // members won't have any comments - makeMember(aSym, new ImplicitConversionInfoImpl(this), inTpl) - } - }) - } - - def members: List[MemberEntity] = memberImpls - - def populateShadowingTables(allConvs: List[ImplicitConversionImpl]): Unit = { - - // TODO: This is not clean, we need to put sym.info.members here instead of tpl.memberSyms to avoid - // the localShouldDocument(_) filtering in ModelFactory - val originalClassMembers = inTpl.memberSyms - val otherConversions = allConvs.filterNot(_ == this) - assert(otherConversions.length == allConvs.length - 1) - - for (member <- memberImpls) { - // for each member in our list - val sym1 = member.sym - val tpe1 = toType.memberInfo(sym1) - - // check if it's shadowed by a member in the original class - var shadowedBySyms: List[Symbol] = List() - for (sym2 <- originalClassMembers) - if (sym1.name == sym2.name) { - val shadowed = !settings.docImplicitsSoundShadowing.value || { - val tpe2 = inTpl.sym.info.memberInfo(sym2) - !isDistinguishableFrom(tpe1, tpe2) - } - if (shadowed) - shadowedBySyms ::= sym2 - } - - val shadowedByMembers = inTpl.allOwnMembers.filter((mb: MemberImpl) => shadowedBySyms.contains(mb.sym)) - - // check if it's shadowed by another member - var ambiguousByMembers: List[MemberEntity] = List() - for (conv <- otherConversions) - for (member2 <- conv.memberImpls) { - val sym2 = member2.sym - if (sym1.name == sym2.name) { - val tpe2 = conv.toType.memberInfo(sym2) - // Ambiguity should be an equivalence relation - val ambiguated = !isDistinguishableFrom(tpe1, tpe2) || !isDistinguishableFrom(tpe2, tpe1) - if (ambiguated) - ambiguousByMembers ::= member2 - } - } - - // we finally have the shadowing info - val shadowing = new ImplicitMemberShadowing { - def shadowingMembers: List[MemberEntity] = shadowedByMembers - def ambiguatingMembers: List[MemberEntity] = ambiguousByMembers - } - - member.byConversion.get.asInstanceOf[ImplicitConversionInfoImpl].shadowing = shadowing - } + memberSyms.flatMap((makeMember(_, this, inTpl))) } } - class ImplicitConversionInfoImpl( - val conversion: ImplicitConversionImpl) extends ImplicitConversionInfo { - // this will be updated as a side effect - var shadowing: ImplicitMemberShadowing = null - } - /* ============== MAKER METHODS ============== */ /** @@ -256,22 +171,18 @@ trait ModelFactoryImplicitSupport { val results = global.analyzer.allViewsFrom(sym.tpe, context, sym.typeParams) var conversions = results.flatMap(result => makeImplicitConversion(sym, result._1, result._2, context, inTpl)) - // also keep empty conversions, so they appear in diagrams - // conversions = conversions.filter(!_.members.isEmpty) + conversions = conversions.filterNot(_.members.isEmpty) // Filter out specialized conversions from array if (sym == ArrayClass) - conversions = conversions.filterNot((conv: ImplicitConversionImpl) => + conversions = conversions.filterNot((conv: ImplicitConversion) => hardcoded.arraySkipConversions.contains(conv.conversionQualifiedName)) // Filter out non-sensical conversions from value types if (isPrimitiveValueType(sym.tpe)) - conversions = conversions.filter((ic: ImplicitConversionImpl) => + conversions = conversions.filter((ic: ImplicitConversion) => hardcoded.valueClassFilter(sym.nameString, ic.conversionQualifiedName)) - // side-effecting and ugly shadowing table population -- ugly but effective - conversions.map(_.populateShadowingTables(conversions)) - // Put the class-specific conversions in front val (ownConversions, commonConversions) = conversions.partition(conv => !hardcoded.commonConversionTargets.contains(conv.conversionQualifiedName)) @@ -307,7 +218,7 @@ trait ModelFactoryImplicitSupport { * - we also need to transform implicit parameters in the view's signature into constraints, such that Numeric[T4] * appears as a constraint */ - def makeImplicitConversion(sym: Symbol, result: SearchResult, constrs: List[TypeConstraint], context: Context, inTpl: => DocTemplateImpl): List[ImplicitConversionImpl] = + def makeImplicitConversion(sym: Symbol, result: SearchResult, constrs: List[TypeConstraint], context: Context, inTpl: => DocTemplateImpl): List[ImplicitConversion] = if (result.tree == EmptyTree) Nil else { // `result` will contain the type of the view (= implicit conversion method) @@ -582,8 +493,8 @@ trait ModelFactoryImplicitSupport { // - common methods (in Any, AnyRef, Object) as they are automatically removed // - private and protected members (not accessible following an implicit conversion) // - members starting with _ (usually reserved for internal stuff) - localShouldDocument(aSym) && (!aSym.isConstructor) && (aSym.owner != AnyValClass) && - (aSym.owner != AnyClass) && (aSym.owner != ObjectClass) && + localShouldDocument(aSym) && (!aSym.isConstructor) && (aSym.owner != ObjectClass) && + (aSym.owner != AnyClass) && (aSym.owner != AnyRefClass) && (!aSym.isProtected) && (!aSym.isPrivate) && (!aSym.name.startsWith("_")) && (aSym.isMethod || aSym.isGetter || aSym.isSetter) && (aSym.nameString != "getClass") @@ -598,11 +509,12 @@ trait ModelFactoryImplicitSupport { def isDistinguishableFrom(t1: Type, t2: Type): Boolean = if (t1.paramss.map(_.length) == t2.paramss.map(_.length)) { for ((t1p, t2p) <- t1.paramss.flatten zip t2.paramss.flatten) - if (!isSubType(t1 memberInfo t1p, t2 memberInfo t2p)) - return true // if on the corresponding parameter you give a type that is in t1 but not in t2 - // def foo(a: Either[Int, Double]): Int = 3 - // def foo(b: Left[T1]): Int = 6 - // a.foo(Right(4.5d)) prints out 3 :) + if (!isSubType(t1 memberInfo t1p, t2 memberInfo t2p)) + return true // if on the corresponding parameter you give a type that is in t1 but not in t2 + // example: + // def foo(a: Either[Int, Double]): Int = 3 + // def foo(b: Left[T1]): Int = 6 + // a.foo(Right(4.5d)) prints out 3 :) false } else true // the member structure is different foo(3, 5) vs foo(3)(5) } \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala index ecc3273903..ef4047cebf 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/Body.scala @@ -67,8 +67,8 @@ final case class Bold(text: Inline) extends Inline final case class Underline(text: Inline) extends Inline final case class Superscript(text: Inline) extends Inline final case class Subscript(text: Inline) extends Inline -final case class EntityLink(target: String, template: () => Option[TemplateEntity]) extends Inline final case class Link(target: String, title: Inline) extends Inline +final case class EntityLink(target: TemplateEntity) extends Inline final case class Monospace(text: Inline) extends Inline final case class Text(text: String) extends Inline final case class HtmlTag(data: String) extends Inline { diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala index 7b70683db5..914275dd8d 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/Comment.scala @@ -108,12 +108,6 @@ abstract class Comment { /** A description for the primary constructor */ def constructor: Option[Body] - /** A set of diagram directives for the inheritance diagram */ - def inheritDiagram: List[String] - - /** A set of diagram directives for the content diagram */ - def contentDiagram: List[String] - override def toString = body.toString + "\n" + (authors map ("@author " + _.toString)).mkString("\n") + diff --git a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala index a46be37d60..996223b9f9 100644 --- a/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/comment/CommentFactory.scala @@ -97,41 +97,37 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => /* Creates comments with necessary arguments */ def createComment ( - body0: Option[Body] = None, - authors0: List[Body] = List.empty, - see0: List[Body] = List.empty, - result0: Option[Body] = None, - throws0: Map[String,Body] = Map.empty, - valueParams0: Map[String,Body] = Map.empty, - typeParams0: Map[String,Body] = Map.empty, - version0: Option[Body] = None, - since0: Option[Body] = None, - todo0: List[Body] = List.empty, - deprecated0: Option[Body] = None, - note0: List[Body] = List.empty, - example0: List[Body] = List.empty, - constructor0: Option[Body] = None, - source0: Option[String] = None, - inheritDiagram0: List[String] = List.empty, - contentDiagram0: List[String] = List.empty + body0: Option[Body] = None, + authors0: List[Body] = List.empty, + see0: List[Body] = List.empty, + result0: Option[Body] = None, + throws0: Map[String,Body] = Map.empty, + valueParams0: Map[String,Body] = Map.empty, + typeParams0: Map[String,Body] = Map.empty, + version0: Option[Body] = None, + since0: Option[Body] = None, + todo0: List[Body] = List.empty, + deprecated0: Option[Body] = None, + note0: List[Body] = List.empty, + example0: List[Body] = List.empty, + constructor0: Option[Body] = None, + source0: Option[String] = None ) : Comment = new Comment{ - val body = if(body0 isDefined) body0.get else Body(Seq.empty) - val authors = authors0 - val see = see0 - val result = result0 - val throws = throws0 - val valueParams = valueParams0 - val typeParams = typeParams0 - val version = version0 - val since = since0 - val todo = todo0 - val deprecated = deprecated0 - val note = note0 - val example = example0 - val constructor = constructor0 - val source = source0 - val inheritDiagram = inheritDiagram0 - val contentDiagram = contentDiagram0 + val body = if(body0 isDefined) body0.get else Body(Seq.empty) + val authors = authors0 + val see = see0 + val result = result0 + val throws = throws0 + val valueParams = valueParams0 + val typeParams = typeParams0 + val version = version0 + val since = since0 + val todo = todo0 + val deprecated = deprecated0 + val note = note0 + val example = example0 + val constructor = constructor0 + val source = source0 } protected val endOfText = '\u0003' @@ -190,10 +186,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => protected val safeTagMarker = '\u000E' - /** A Scaladoc tag not linked to a symbol and not followed by text */ - protected val SingleTag = - new Regex("""\s*@(\S+)\s*""") - /** A Scaladoc tag not linked to a symbol. Returns the name of the tag, and the rest of the line. */ protected val SimpleTag = new Regex("""\s*@(\S+)\s+(.*)""") @@ -314,11 +306,6 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => val value = body :: tags.getOrElse(key, Nil) parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case SingleTag(name) :: ls if (!inCodeBlock) => - val key = SimpleTagKey(name) - val value = "" :: tags.getOrElse(key, Nil) - parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock) - case line :: ls if (lastTagKey.isDefined) => val key = lastTagKey.get val value = @@ -334,24 +321,9 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => parse0(docBody, tags, lastTagKey, ls, inCodeBlock) case Nil => - // Take the {inheritance, content} diagram keys aside, as it doesn't need any parsing - val inheritDiagramTag = SimpleTagKey("inheritanceDiagram") - val contentDiagramTag = SimpleTagKey("contentDiagram") - - val inheritDiagramText: List[String] = tags.get(inheritDiagramTag) match { - case Some(list) => list - case None => List.empty - } - - val contentDiagramText: List[String] = tags.get(contentDiagramTag) match { - case Some(list) => list - case None => List.empty - } - - val tagsWithoutDiagram = tags.filterNot(pair => pair._1 == inheritDiagramTag || pair._1 == contentDiagramTag) val bodyTags: mutable.Map[TagKey, List[Body]] = - mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWiki(_, pos))} toSeq: _*) + mutable.Map(tags mapValues {tag => tag map (parseWiki(_, pos))} toSeq: _*) def oneTag(key: SimpleTagKey): Option[Body] = ((bodyTags remove key): @unchecked) match { @@ -384,23 +356,21 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => } val com = createComment ( - body0 = Some(parseWiki(docBody.toString, pos)), - authors0 = allTags(SimpleTagKey("author")), - see0 = allTags(SimpleTagKey("see")), - result0 = oneTag(SimpleTagKey("return")), - throws0 = allSymsOneTag(SimpleTagKey("throws")), - valueParams0 = allSymsOneTag(SimpleTagKey("param")), - typeParams0 = allSymsOneTag(SimpleTagKey("tparam")), - version0 = oneTag(SimpleTagKey("version")), - since0 = oneTag(SimpleTagKey("since")), - todo0 = allTags(SimpleTagKey("todo")), - deprecated0 = oneTag(SimpleTagKey("deprecated")), - note0 = allTags(SimpleTagKey("note")), - example0 = allTags(SimpleTagKey("example")), - constructor0 = oneTag(SimpleTagKey("constructor")), - source0 = Some(clean(src).mkString("\n")), - inheritDiagram0 = inheritDiagramText, - contentDiagram0 = contentDiagramText + body0 = Some(parseWiki(docBody.toString, pos)), + authors0 = allTags(SimpleTagKey("author")), + see0 = allTags(SimpleTagKey("see")), + result0 = oneTag(SimpleTagKey("return")), + throws0 = allSymsOneTag(SimpleTagKey("throws")), + valueParams0 = allSymsOneTag(SimpleTagKey("param")), + typeParams0 = allSymsOneTag(SimpleTagKey("tparam")), + version0 = oneTag(SimpleTagKey("version")), + since0 = oneTag(SimpleTagKey("since")), + todo0 = allTags(SimpleTagKey("todo")), + deprecated0 = oneTag(SimpleTagKey("deprecated")), + note0 = allTags(SimpleTagKey("note")), + example0 = allTags(SimpleTagKey("example")), + constructor0 = oneTag(SimpleTagKey("constructor")), + source0 = Some(clean(src).mkString("\n")) ) for ((key, _) <- bodyTags) @@ -716,6 +686,13 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => ) } + def entityLink(query: String): Inline = findTemplate(query) match { + case Some(tpl) => + EntityLink(tpl) + case None => + Text(query) + } + def link(): Inline = { val SchemeUri = """([^:]+:.*)""".r jump("[[") @@ -740,8 +717,7 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => if (!qualName.contains(".") && !definitions.packageExists(qualName)) reportError(pos, "entity link to " + qualName + " should be a fully qualified name") - // move the template resolution as late as possible - EntityLink(qualName, () => findTemplate(qualName)) + entityLink(qualName) } } @@ -757,8 +733,8 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => nextChar() } - /** - * Eliminates the (common) leading spaces in all lines, based on the first line + /** + * Eliminates the (common) leading spaces in all lines, based on the first line * For indented pieces of code, it reduces the indent to the least whitespace prefix: * {{{ * indented example @@ -781,11 +757,11 @@ trait CommentFactory { thisFactory: ModelFactory with CommentFactory => while (index < code.length) { code(index) match { case ' ' => - if (wsArea) + if (wsArea) crtSkip += 1 case c => wsArea = (c == '\n') - maxSkip = if (firstLine || emptyLine) maxSkip else if (maxSkip <= crtSkip) maxSkip else crtSkip + maxSkip = if (firstLine || emptyLine) maxSkip else if (maxSkip <= crtSkip) maxSkip else crtSkip crtSkip = if (c == '\n') 0 else crtSkip firstLine = if (c == '\n') false else firstLine emptyLine = if (c == '\n') true else false diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala deleted file mode 100644 index 28a8c7d37d..0000000000 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/Diagram.scala +++ /dev/null @@ -1,143 +0,0 @@ -package scala.tools.nsc.doc -package model -package diagram - -import model._ - -/** - * The diagram base classes - * - * @author Damien Obrist - * @author Vlad Ureche - */ -abstract class Diagram { - def nodes: List[Node] - def edges: List[(Node, List[Node])] - def isPackageDiagram = false - def isClassDiagram = false - def depthInfo: DepthInfo -} - -case class PackageDiagram(nodes:List[/*Class*/Node], edges:List[(Node, List[Node])]) extends Diagram { - override def isPackageDiagram = true - lazy val depthInfo = new PackageDiagramDepth(this) -} - -/** A class diagram */ -case class ClassDiagram(thisNode: ThisNode, - superClasses: List[/*Class*/Node], - subClasses: List[/*Class*/Node], - incomingImplicits: List[ImplicitNode], - outgoingImplicits: List[ImplicitNode]) extends Diagram { - def nodes = thisNode :: superClasses ::: subClasses ::: incomingImplicits ::: outgoingImplicits - def edges = (thisNode -> (superClasses ::: outgoingImplicits)) :: - (subClasses ::: incomingImplicits).map(_ -> List(thisNode)) - - override def isClassDiagram = true - lazy val depthInfo = new DepthInfo { - def maxDepth = 3 - def nodeDepth(node: Node) = - if (node == thisNode) 1 - else if (superClasses.contains(node)) 0 - else if (subClasses.contains(node)) 2 - else if (incomingImplicits.contains(node) || outgoingImplicits.contains(node)) 1 - else -1 - } -} - -trait DepthInfo { - /** Gives the maximum depth */ - def maxDepth: Int - /** Gives the depth of any node in the diagram or -1 if the node is not in the diagram */ - def nodeDepth(node: Node): Int -} - -abstract class Node { - def name = tpe.name - def tpe: TypeEntity - def tpl: Option[TemplateEntity] - /** shortcut to get a DocTemplateEntity */ - def doctpl: Option[DocTemplateEntity] = tpl match { - case Some(tpl) => tpl match { - case d: DocTemplateEntity => Some(d) - case _ => None - } - case _ => None - } - /* shortcuts to find the node type without matching */ - def isThisNode = false - def isNormalNode = false - def isClassNode = if (tpl.isDefined) (tpl.get.isClass || tpl.get.qualifiedName == "scala.AnyRef") else false - def isTraitNode = if (tpl.isDefined) tpl.get.isTrait else false - def isObjectNode= if (tpl.isDefined) tpl.get.isObject else false - def isOtherNode = !(isClassNode || isTraitNode || isObjectNode) - def isImplicitNode = false - def isOutsideNode = false -} - -// different matchers, allowing you to use the pattern matcher against any node -// NOTE: A ThisNode or ImplicitNode can at the same time be ClassNode/TraitNode/OtherNode, not exactly according to -// case class specification -- thus a complete match would be: -// node match { -// case ThisNode(tpe, _) => /* case for this node, you can still use .isClass, .isTrait and .isOther */ -// case ImplicitNode(tpe, _) => /* case for an implicit node, you can still use .isClass, .isTrait and .isOther */ -// case _ => node match { -// case ClassNode(tpe, _) => /* case for a non-this, non-implicit Class node */ -// case TraitNode(tpe, _) => /* case for a non-this, non-implicit Trait node */ -// case OtherNode(tpe, _) => /* case for a non-this, non-implicit Other node */ -// } -// } -object Node { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = Some((n.tpe, n.tpl)) } -object ClassNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isClassNode) Some((n.tpe, n.tpl)) else None } -object TraitNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isTraitNode) Some((n.tpe, n.tpl)) else None } -object ObjectNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isObjectNode) Some((n.tpe, n.tpl)) else None } -object OutsideNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOutsideNode) Some((n.tpe, n.tpl)) else None } -object OtherNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOtherNode) Some((n.tpe, n.tpl)) else None } - - - -/** The node for the current class */ -case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isThisNode = true } - -/** The usual node */ -case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isNormalNode = true } - -/** A class or trait the thisnode can be converted to by an implicit conversion - * TODO: I think it makes more sense to use the tpe links to templates instead of the TemplateEntity for implicit nodes - * since some implicit conversions convert the class to complex types that cannot be represented as a single tmeplate - */ -case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isImplicitNode = true } - -/** An outside node is shown in packages when a class from a different package makes it to the package diagram due to - * its relation to a class in the package (and @contentDiagram showInheritedNodes annotation) */ -case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity]) extends Node { override def isOutsideNode = true } - - -// Computing and offering node depth information -class PackageDiagramDepth(pack: PackageDiagram) extends DepthInfo { - private[this] var _maxDepth = 0 - private[this] var _nodeDepth = Map[Node, Int]() - private[this] var seedNodes = Set[Node]() - private[this] val invertedEdges: Map[Node, List[Node]] = - pack.edges.flatMap({case (node: Node, outgoing: List[Node]) => outgoing.map((_, node))}).groupBy(_._1).map({case (k, values) => (k, values.map(_._2))}).withDefaultValue(Nil) - private[this] val directEdges: Map[Node, List[Node]] = pack.edges.toMap.withDefaultValue(Nil) - - // seed base nodes, to minimize noise - they can't all have parents, else there would only be cycles - seedNodes ++= pack.nodes.filter(directEdges(_).isEmpty) - - while (!seedNodes.isEmpty) { - var newSeedNodes = Set[Node]() - for (node <- seedNodes) { - val depth = 1 + (-1 :: directEdges(node).map(_nodeDepth.getOrElse(_, -1))).max - if (depth != _nodeDepth.getOrElse(node, -1)) { - _nodeDepth += (node -> depth) - newSeedNodes ++= invertedEdges(node) - if (depth > _maxDepth) _maxDepth = depth - } - } - seedNodes = newSeedNodes - } - - val maxDepth = _maxDepth - def nodeDepth(node: Node) = _nodeDepth.getOrElse(node, -1) -} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala deleted file mode 100644 index c06b5d50b7..0000000000 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala +++ /dev/null @@ -1,248 +0,0 @@ -package scala.tools.nsc.doc -package model -package diagram - -import model._ -import comment.CommentFactory -import java.util.regex.{Pattern, Matcher} -import scala.util.matching.Regex - -// statistics -import html.page.diagram.DiagramStats - -/** - * This trait takes care of parsing @{inheritance, content}Diagram annotations - * - * @author Damien Obrist - * @author Vlad Ureche - */ -trait DiagramDirectiveParser { - this: ModelFactory with DiagramFactory with CommentFactory with TreeFactory => - - ///// DIAGRAM FILTERS ////////////////////////////////////////////////////////////////////////////////////////////// - - /** - * The DiagramFilter trait directs the diagram engine about the way the diagram should be displayed - * - * Vlad: There's an explanation I owe to people using diagrams and not finding a way to hide a specific class from - * all diagrams at once. So why did I choose to allow you to only control the diagrams at class level? So, the - * reason is you would break the separate scaladoc compilation: - * If you have an "@diagram hideMyClass" annotation in class A and you run scaladoc on it along with its subclass B - * A will not appear in B's diagram. But if you scaladoc only on B, A's comment will not be parsed and the - * instructions to hide class A from all diagrams will not be available. Thus I prefer to force you to control the - * diagrams of each class locally. The problem does not appear with scalac, as scalac stores all its necessary - * information (like scala signatures) serialized in the .class file. But we couldn't store doc comments in the class - * file, could we? (Turns out we could, but that's another story) - * - * Any flaming for this decision should go to scala-internals@googlegroups.com - */ - trait DiagramFilter { - /** A flag to hide the diagram completely */ - def hideDiagram: Boolean - /** Hide incoming implicit conversions (for type hierarchy diagrams) */ - def hideIncomingImplicits: Boolean - /** Hide outgoing implicit conversions (for type hierarchy diagrams) */ - def hideOutgoingImplicits: Boolean - /** Hide superclasses (for type hierarchy diagrams) */ - def hideSuperclasses: Boolean - /** Hide subclasses (for type hierarchy diagrams) */ - def hideSubclasses: Boolean - /** Show related classes from other objects/traits/packages (for content diagrams) */ - def showInheritedNodes: Boolean - /** Hide a node from the diagram */ - def hideNode(clazz: TemplateEntity): Boolean - /** Hide an edge from the diagram */ - def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean - } - - /** Main entry point into this trait: generate the filter for inheritance diagrams */ - def makeInheritanceDiagramFilter(template: DocTemplateImpl): DiagramFilter = { - val defaultFilter = if (template.isClass || template.isTrait) FullDiagram else NoDiagramAtAll - if (template.comment.isDefined) - makeDiagramFilter(template, template.comment.get.inheritDiagram, defaultFilter, true) - else - defaultFilter - } - - /** Main entry point into this trait: generate the filter for content diagrams */ - def makeContentDiagramFilter(template: DocTemplateImpl): DiagramFilter = { - val defaultFilter = if (template.isPackage || template.isObject) FullDiagram else NoDiagramAtAll - if (template.comment.isDefined) - makeDiagramFilter(template, template.comment.get.contentDiagram, defaultFilter, false) - else - defaultFilter - } - - protected var tFilter = 0l - protected var tModel = 0l - - /** Show the entire diagram, no filtering */ - case object FullDiagram extends DiagramFilter { - val hideDiagram: Boolean = false - val hideIncomingImplicits: Boolean = false - val hideOutgoingImplicits: Boolean = false - val hideSuperclasses: Boolean = false - val hideSubclasses: Boolean = false - val showInheritedNodes: Boolean = false - def hideNode(clazz: TemplateEntity): Boolean = false - def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean = false - } - - /** Hide the diagram completely, no need for special filtering */ - case object NoDiagramAtAll extends DiagramFilter { - val hideDiagram: Boolean = true - val hideIncomingImplicits: Boolean = true - val hideOutgoingImplicits: Boolean = true - val hideSuperclasses: Boolean = true - val hideSubclasses: Boolean = true - val showInheritedNodes: Boolean = false - def hideNode(clazz: TemplateEntity): Boolean = true - def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean = true - } - - /** The AnnotationDiagramFilter trait directs the diagram engine according to an annotation - * TODO: Should document the annotation, for now see parseDiagramAnnotation in ModelFactory.scala */ - case class AnnotationDiagramFilter(hideDiagram: Boolean, - hideIncomingImplicits: Boolean, - hideOutgoingImplicits: Boolean, - hideSuperclasses: Boolean, - hideSubclasses: Boolean, - showInheritedNodes: Boolean, - hideNodesFilter: List[Pattern], - hideEdgesFilter: List[(Pattern, Pattern)]) extends DiagramFilter { - - def hideNode(clazz: TemplateEntity): Boolean = { - val qualifiedName = clazz.qualifiedName - for (hideFilter <- hideNodesFilter) - if (hideFilter.matcher(qualifiedName).matches) { - // println(hideFilter + ".matcher(" + qualifiedName + ").matches = " + hideFilter.matcher(qualifiedName).matches) - return true - } - false - } - - def hideEdge(clazz1: TemplateEntity, clazz2: TemplateEntity): Boolean = { - val clazz1Name = clazz1.qualifiedName - val clazz2Name = clazz2.qualifiedName - for ((clazz1Filter, clazz2Filter) <- hideEdgesFilter) { - if (clazz1Filter.matcher(clazz1Name).matches && - clazz2Filter.matcher(clazz2Name).matches) { - // println(clazz1Filter + ".matcher(" + clazz1Name + ").matches = " + clazz1Filter.matcher(clazz1Name).matches) - // println(clazz2Filter + ".matcher(" + clazz2Name + ").matches = " + clazz2Filter.matcher(clazz2Name).matches) - return true - } - } - false - } - } - - // TODO: This could certainly be improved -- right now the only regex is *, but there's no way to match a single identifier - private val NodeSpecRegex = "\\\"[A-Za-z\\*][A-Za-z\\.\\*]*\\\"" - private val NodeSpecPattern = Pattern.compile(NodeSpecRegex) - private val EdgeSpecRegex = "\\(" + NodeSpecRegex + "\\s*\\->\\s*" + NodeSpecRegex + "\\)" - private val EdgeSpecPattern = Pattern.compile(NodeSpecRegex) - // And the composed regexes: - private val HideNodesRegex = new Regex("^hideNodes(\\s*" + NodeSpecRegex + ")+$") - private val HideEdgesRegex = new Regex("^hideEdges(\\s*" + EdgeSpecRegex + ")+$") - - private def makeDiagramFilter(template: DocTemplateImpl, - directives: List[String], - defaultFilter: DiagramFilter, - isInheritanceDiagram: Boolean): DiagramFilter = directives match { - - // if there are no specific diagram directives, return the default filter (either FullDiagram or NoDiagramAtAll) - case Nil => - defaultFilter - - // compute the exact filters. By including the annotation, the diagram is autmatically added - case _ => - tFilter -= System.currentTimeMillis - var hideDiagram0: Boolean = false - var hideIncomingImplicits0: Boolean = false - var hideOutgoingImplicits0: Boolean = false - var hideSuperclasses0: Boolean = false - var hideSubclasses0: Boolean = false - var showInheritedNodes0: Boolean = false - var hideNodesFilter0: List[Pattern] = Nil - var hideEdgesFilter0: List[(Pattern, Pattern)] = Nil - - def warning(message: String) = { - // we need the position from the package object (well, ideally its comment, but yeah ...) - val sym = if (template.sym.isPackage) template.sym.info.member(global.nme.PACKAGE) else template.sym - assert((sym != global.NoSymbol) || (sym == global.definitions.RootPackage)) - global.reporter.warning(sym.pos, message) - } - - def preparePattern(className: String) = - "^" + className.stripPrefix("\"").stripSuffix("\"").replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*") + "$" - - // separate entries: - val entries = directives.foldRight("")(_ + " " + _).split(",").map(_.trim) - for (entry <- entries) - entry match { - case "hideDiagram" => - hideDiagram0 = true - case "hideIncomingImplicits" if isInheritanceDiagram => - hideIncomingImplicits0 = true - case "hideOutgoingImplicits" if isInheritanceDiagram => - hideOutgoingImplicits0 = true - case "hideSuperclasses" if isInheritanceDiagram => - hideSuperclasses0 = true - case "hideSubclasses" if isInheritanceDiagram => - hideSubclasses0 = true - case "showInheritedNodes" if !isInheritanceDiagram => - showInheritedNodes0 = true - case HideNodesRegex(last) => - val matcher = NodeSpecPattern.matcher(entry) - while (matcher.find()) { - val classPattern = Pattern.compile(preparePattern(matcher.group())) - hideNodesFilter0 ::= classPattern - } - case HideEdgesRegex(last) => - val matcher = NodeSpecPattern.matcher(entry) - while (matcher.find()) { - val class1Pattern = Pattern.compile(preparePattern(matcher.group())) - assert(matcher.find()) // it's got to be there, just matched it! - val class2Pattern = Pattern.compile(preparePattern(matcher.group())) - hideEdgesFilter0 ::= ((class1Pattern, class2Pattern)) - } - case "" => - // don't need to do anything about it - case _ => - warning("Could not understand diagram annotation in " + template.fullName + ": unmatched entry \"" + - entry + "\".\n" + - " This could be because:\n" + - " - you forgot to separate entries by commas\n" + - " - you used a tag that is not allowed in the current context (like @contentDiagram hideSuperclasses)\n"+ - " - you did not use one of the allowed tags (see docs.scala-lang.org for scaladoc annotations)") - } - val result = - if (hideDiagram0) - NoDiagramAtAll - else if ((hideNodesFilter0.isEmpty) && - (hideEdgesFilter0.isEmpty) && - (hideIncomingImplicits0 == false) && - (hideOutgoingImplicits0 == false) && - (hideSuperclasses0 == false) && - (hideSubclasses0 == false) && - (showInheritedNodes0 == false) && - (hideDiagram0 == false)) - FullDiagram - else - AnnotationDiagramFilter( - hideDiagram = hideDiagram0, - hideIncomingImplicits = hideIncomingImplicits0, - hideOutgoingImplicits = hideOutgoingImplicits0, - hideSuperclasses = hideSuperclasses0, - hideSubclasses = hideSubclasses0, - showInheritedNodes = showInheritedNodes0, - hideNodesFilter = hideNodesFilter0, - hideEdgesFilter = hideEdgesFilter0) - - if (settings.docDiagramsDebug.value && result != NoDiagramAtAll && result != FullDiagram) - settings.printMsg(template.fullName + " filter: " + result) - tFilter += System.currentTimeMillis - - result - } -} \ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala b/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala deleted file mode 100644 index 4ae5e7a5cb..0000000000 --- a/src/compiler/scala/tools/nsc/doc/model/diagram/DiagramFactory.scala +++ /dev/null @@ -1,197 +0,0 @@ -package scala.tools.nsc.doc -package model -package diagram - -import model._ -import comment.CommentFactory -import collection.mutable - -// statistics -import html.page.diagram.DiagramStats - -/** - * This trait takes care of generating the diagram for classes and packages - * - * @author Damien Obrist - * @author Vlad Ureche - */ -trait DiagramFactory extends DiagramDirectiveParser { - this: ModelFactory with DiagramFactory with CommentFactory with TreeFactory => - - /** Create the inheritance diagram for this template */ - def makeInheritanceDiagram(tpl: DocTemplateImpl): Option[Diagram] = { - - tFilter = 0 - tModel = -System.currentTimeMillis - - // the diagram filter - val diagramFilter = makeInheritanceDiagramFilter(tpl) - - val result = - if (diagramFilter == NoDiagramAtAll) - None - else { - // the main node - val thisNode = ThisNode(tpl.ownType, Some(tpl)) - - // superclasses - var superclasses = List[Node]() - tpl.parentTypes.collect { case p: (TemplateEntity, TypeEntity) if !classExcluded(p._1) => p } foreach { - t: (TemplateEntity, TypeEntity) => - val n = NormalNode(t._2, Some(t._1)) - superclasses ::= n - } - val filteredSuperclasses = if (diagramFilter.hideSuperclasses) Nil else superclasses - - // incoming implcit conversions - lazy val incomingImplicitNodes = tpl.incomingImplicitlyConvertedClasses.map(tpl => ImplicitNode(tpl.ownType, Some(tpl))) - val filteredIncomingImplicits = if (diagramFilter.hideIncomingImplicits) Nil else incomingImplicitNodes - - // subclasses - val subclasses = tpl.directSubClasses.flatMap { - case d: TemplateEntity if !classExcluded(d) => List(NormalNode(d.ownType, Some(d))) - case _ => Nil - } - val filteredSubclasses = if (diagramFilter.hideSubclasses) Nil else subclasses - - // outgoing implicit coversions - lazy val implicitNodes = tpl.outgoingImplicitlyConvertedClasses.map(pair => ImplicitNode(pair._2, Some(pair._1))) - val filteredImplicitOutgoingNodes = if (diagramFilter.hideOutgoingImplicits) Nil else implicitNodes - - // final diagram filter - filterDiagram(ClassDiagram(thisNode, filteredSuperclasses.reverse, filteredSubclasses.reverse, filteredIncomingImplicits, filteredImplicitOutgoingNodes), diagramFilter) - } - - tModel += System.currentTimeMillis - DiagramStats.addFilterTime(tFilter) - DiagramStats.addModelTime(tModel-tFilter) - - result - } - - /** Create the content diagram for this template */ - def makeContentDiagram(pack: DocTemplateImpl): Option[Diagram] = { - - tFilter = 0 - tModel = -System.currentTimeMillis - - // the diagram filter - val diagramFilter = makeContentDiagramFilter(pack) - - val result = - if (diagramFilter == NoDiagramAtAll) - None - else { - var mapNodes = Map[DocTemplateEntity, Node]() - var nodesShown = Set[DocTemplateEntity]() - var edgesAll = List[(DocTemplateEntity, List[DocTemplateEntity])]() - - // classes is the entire set of classes and traits in the package, they are the superset of nodes in the diagram - // we collect classes, traits and objects without a companion, which are usually used as values(e.g. scala.None) - val dnodes = pack.members collect { - case d: DocTemplateEntity if d.isClass || d.isTrait || (d.isObject && !d.companion.isDefined) && - ((d.inTemplate == pack) || diagramFilter.showInheritedNodes) => d - } - - // for each node, add its subclasses - for (node <- dnodes if !classExcluded(node)) { - val superClasses = node.parentTypes.collect { - case (tpl: DocTemplateEntity, tpe) if tpl.inTemplate == pack && !classExcluded(tpl) => tpl - case (tpl: DocTemplateEntity, tpe) if tpl.inTemplate != pack && !classExcluded(tpl) && diagramFilter.showInheritedNodes && (pack.members contains tpl) => tpl - } - - if (!superClasses.isEmpty) { - nodesShown += node - nodesShown ++= superClasses - } - - edgesAll ::= node -> superClasses - mapNodes += node -> (if (node.inTemplate == pack) NormalNode(node.ownType, Some(node)) else OutsideNode(node.ownType, Some(node))) - } - - if (nodesShown.isEmpty) - None - else { - val nodes = dnodes.filter(nodesShown.contains(_)).map(mapNodes(_)) - val edges = edgesAll.map(pair => (mapNodes(pair._1), pair._2.map(mapNodes(_)))).filterNot(pair => pair._2.isEmpty) - filterDiagram(PackageDiagram(nodes, edges), diagramFilter) - } - } - - tModel += System.currentTimeMillis - DiagramStats.addFilterTime(tFilter) - DiagramStats.addModelTime(tModel-tFilter) - - result - } - - /** Diagram filtering logic */ - private def filterDiagram(diagram: Diagram, diagramFilter: DiagramFilter): Option[Diagram] = { - tFilter -= System.currentTimeMillis - - val result = - if (diagramFilter == FullDiagram) - Some(diagram) - else if (diagramFilter == NoDiagramAtAll) - None - else { - // Final diagram, with the filtered nodes and edges - diagram match { - case ClassDiagram(thisNode, _, _, _, _) if diagramFilter.hideNode(thisNode.tpl.get) => - None - - case ClassDiagram(thisNode, superClasses, subClasses, incomingImplicits, outgoingImplicits) => - - def hideIncoming(node: Node): Boolean = - if (node.tpl.isDefined) diagramFilter.hideNode(node.tpl.get) || diagramFilter.hideEdge(node.tpl.get, thisNode.tpl.get) - else false // hopefully we won't need to fallback here - - def hideOutgoing(node: Node): Boolean = - if (node.tpl.isDefined) diagramFilter.hideNode(node.tpl.get) || diagramFilter.hideEdge(thisNode.tpl.get, node.tpl.get) - else false // hopefully we won't need to fallback here - - // println(thisNode) - // println(superClasses.map(cl => "super: " + cl + " " + hideOutgoing(cl)).mkString("\n")) - // println(subClasses.map(cl => "sub: " + cl + " " + hideIncoming(cl)).mkString("\n")) - Some(ClassDiagram(thisNode, - superClasses.filterNot(hideOutgoing(_)), - subClasses.filterNot(hideIncoming(_)), - incomingImplicits.filterNot(hideIncoming(_)), - outgoingImplicits.filterNot(hideOutgoing(_)))) - - case PackageDiagram(nodes0, edges0) => - // Filter out all edges that: - // (1) are sources of hidden classes - // (2) are manually hidden by the user - // (3) are destinations of hidden classes - val edges: List[(Node, List[Node])] = - diagram.edges.flatMap({ - case (source@Node(_, Some(tpl1)), dests) if !diagramFilter.hideNode(tpl1) => - val dests2 = dests.collect({ case node@Node(_, Some(tpl2)) if (!(diagramFilter.hideEdge(tpl1, tpl2) || diagramFilter.hideNode(tpl2))) => node }) - if (dests2 != Nil) - List((source, dests2)) - else - Nil - case _ => Nil - }) - - // Only show the the non-isolated nodes - // TODO: Decide if we really want to hide package members, I'm not sure that's a good idea (!!!) - // TODO: Does .distinct cause any stability issues? - val sourceNodes = edges.map(_._1) - val sinkNodes = edges.map(_._2).flatten - val nodes = (sourceNodes ::: sinkNodes).distinct - Some(PackageDiagram(nodes, edges)) - } - } - - tFilter += System.currentTimeMillis - - // eliminate all empty diagrams - if (result.isDefined && result.get.edges.forall(_._2.isEmpty)) - None - else - result - } - -} diff --git a/src/partest/scala/tools/partest/ScaladocModelTest.scala b/src/partest/scala/tools/partest/ScaladocModelTest.scala index de5354d4a0..142f2baea5 100644 --- a/src/partest/scala/tools/partest/ScaladocModelTest.scala +++ b/src/partest/scala/tools/partest/ScaladocModelTest.scala @@ -81,9 +81,9 @@ abstract class ScaladocModelTest extends DirectTest { private[this] var settings: Settings = null // create a new scaladoc compiler - private[this] def newDocFactory: DocFactory = { + def newDocFactory: DocFactory = { settings = new Settings(_ => ()) - settings.scaladocQuietRun = true // yaay, no more "model contains X documentable templates"! + settings.reportModel = false // yaay, no more "model contains X documentable templates"! val args = extraSettings + " " + scaladocSettings val command = new ScalaDoc.Command((CommandLineParser tokenize (args)), settings) val docFact = new DocFactory(new ConsoleReporter(settings), settings) diff --git a/test/scaladoc/resources/implicits-ambiguating-res.scala b/test/scaladoc/resources/implicits-ambiguating-res.scala deleted file mode 100644 index 6ed51366cb..0000000000 --- a/test/scaladoc/resources/implicits-ambiguating-res.scala +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Test scaladoc implicits distinguishing -- supress all members by implicit conversion that are shadowed by the - * class' own members - * - * {{{ - * scala> class A { def foo(t: String) = 4 } - * defined class A - * - * scala> class B { def foo(t: Any) = 5 } - * defined class B - * - * scala> implicit def AtoB(a:A) = new B - * AtoB: (a: A)B - * - * scala> val a = new A - * a: A = A@28f553e3 - * - * scala> a.foo("T") - * res1: Int = 4 - * - * scala> a.foo(4) - * res2: Int = 5 - * }}} - */ -package scala.test.scaladoc.implicits.ambiguating -import language.implicitConversions // according to SIP18 - -/** - conv1-5 should be ambiguous - * - conv6-7 should not be ambiguous - * - conv8 should be ambiguous - * - conv9 should be ambiguous - * - conv10 and conv11 should not be ambiguous */ -class A[T] -/** conv1-9 should be the same, conv10 should be ambiguous, conv11 should be okay */ -class B extends A[Int] -/** conv1-9 should be the same, conv10 and conv11 should not be ambiguous */ -class C extends A[Double] - /** conv1-9 should be the same, conv10 should not be ambiguous while conv11 should be ambiguous */ -class D extends A[AnyRef] - -class X[T] { - def conv1: AnyRef = ??? - def conv2: T = ??? - def conv3(l: Int): AnyRef = ??? - def conv4(l: AnyRef): AnyRef = ??? - def conv5(l: AnyRef): String = ??? - def conv6(l: String)(m: String): AnyRef = ??? - def conv7(l: AnyRef)(m: AnyRef): AnyRef = ??? - def conv8(l: AnyRef): AnyRef = ??? - def conv9(l: String): AnyRef = ??? - def conv10(l: T): T = ??? - def conv11(l: T): T = ??? -} - -class Z[T] { - def conv1: AnyRef = ??? - def conv2: T = ??? - def conv3(p: Int): AnyRef = ??? - def conv4(p: AnyRef): String = ??? - def conv5(p: AnyRef): AnyRef = ??? - def conv6(p: String, q: String): AnyRef = ??? - def conv7(p: AnyRef, q: AnyRef): AnyRef = ??? - def conv8(p: String): AnyRef = ??? - def conv9(p: AnyRef): AnyRef = ??? - def conv10(p: Int): T = ??? - def conv11(p: String): T = ??? -} - -object A { - implicit def AtoX[T](a: A[T]) = new X[T] - implicit def AtoZ[T](a: A[T]) = new Z[T] -} diff --git a/test/scaladoc/resources/implicits-base-res.scala b/test/scaladoc/resources/implicits-base-res.scala index d6c0332c10..65d7bdf67c 100644 --- a/test/scaladoc/resources/implicits-base-res.scala +++ b/test/scaladoc/resources/implicits-base-res.scala @@ -16,9 +16,8 @@ trait MyNumeric[R] * def convToManifestA(x: T) // pimpA7: with 2 constraints: T: Manifest and T <: Double * def convToMyNumericA(x: T) // pimpA6: with a constraint that there is x: MyNumeric[T] implicit in scope * def convToNumericA(x: T) // pimpA1: with a constraint that there is x: Numeric[T] implicit in scope - * def convToPimpedA(x: Bar[Foo[T]]) // pimpA5: no constraints, SHADOWED - * def convToPimpedA(x: S) // pimpA4: with 3 constraints: T = Foo[Bar[S]], S: Foo and S: Bar, SHADOWED - * def convToPimpedA(x: T) // pimpA0: with no constraints, SHADOWED + * def convToPimpedA(x: Bar[Foo[T]]) // pimpA5: no constraints + * def convToPimpedA(x: S) // pimpA4: with 3 constraints: T = Foo[Bar[S]], S: Foo and S: Bar * def convToTraversableOps(x: T) // pimpA7: with 2 constraints: T: Manifest and T <: Double * // should not be abstract! * }}} @@ -53,10 +52,9 @@ object A { * def convToManifestA(x: Double) // pimpA7: no constraints * def convToMyNumericA(x: Double) // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Double] implicit in scope * def convToNumericA(x: Double) // pimpA1: no constraintsd - * def convToPimpedA(x: Bar[Foo[Double]]) // pimpA5: no constraints, SHADOWED - * def convToPimpedA(x: Double) // pimpA0: no constraints, SHADOWED + * def convToPimpedA(x: Bar[Foo[Double]]) // pimpA5: no constraints * def convToTraversableOps(x: Double) // pimpA7: no constraints - * // should not be abstract! + * // should not be abstract! * }}} */ class B extends A[Double] @@ -70,8 +68,7 @@ object B extends A * def convToIntA(x: Int) // pimpA2: no constraints * def convToMyNumericA(x: Int) // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Int] implicit in scope * def convToNumericA(x: Int) // pimpA1: no constraints - * def convToPimpedA(x: Int) // pimpA0: no constraints, SHADOWED - * def convToPimpedA(x: Bar[Foo[Int]]) // pimpA5: no constraints, SHADOWED + * def convToPimpedA(x: Bar[Foo[Int]]) // pimpA5: no constraints * }}} */ class C extends A[Int] @@ -84,8 +81,7 @@ object C extends A * {{{ * def convToMyNumericA(x: String) // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[String] implicit in scope * def convToNumericA(x: String) // pimpA1: (if showAll is set) with a constraint that there is x: Numeric[String] implicit in scope - * def convToPimpedA(x: Bar[Foo[String]]) // pimpA5: no constraints, SHADOWED - * def convToPimpedA(x: String) // pimpA0: no constraints, SHADOWED + * def convToPimpedA(x: Bar[Foo[String]]) // pimpA5: no constraints * }}} */ class D extends A[String] diff --git a/test/scaladoc/resources/implicits-elimination-res.scala b/test/scaladoc/resources/implicits-elimination-res.scala index 5f7135c9e8..b23667440c 100644 --- a/test/scaladoc/resources/implicits-elimination-res.scala +++ b/test/scaladoc/resources/implicits-elimination-res.scala @@ -2,13 +2,13 @@ * Testing scaladoc implicits elimination */ package scala.test.scaladoc.implicits.elimination { - + import language.implicitConversions // according to SIP18 /** No conversion, as B doesn't bring any member */ class A class B { class C; trait V; type T; } - object A { - implicit def toB(a: A): B = null + object A { + implicit def toB(a: A): B = null } } diff --git a/test/scaladoc/run/diagrams-base.check b/test/scaladoc/run/diagrams-base.check deleted file mode 100644 index 619c56180b..0000000000 --- a/test/scaladoc/run/diagrams-base.check +++ /dev/null @@ -1 +0,0 @@ -Done. diff --git a/test/scaladoc/run/diagrams-base.scala b/test/scaladoc/run/diagrams-base.scala deleted file mode 100644 index 38bed06502..0000000000 --- a/test/scaladoc/run/diagrams-base.scala +++ /dev/null @@ -1,73 +0,0 @@ -import scala.tools.nsc.doc.model._ -import scala.tools.nsc.doc.model.diagram._ -import scala.tools.partest.ScaladocModelTest - -object Test extends ScaladocModelTest { - - override def code = """ - package scala.test.scaladoc.diagrams - - import language.implicitConversions - - trait A - trait B - trait C - class E extends A with B with C - object E { implicit def eToT(e: E) = new T } - - class F extends E - class G extends E - private class H extends E /* since it's private, it won't go into the diagram */ - class T { def t = true } - - class X - object X { implicit def xToE(x: X) = new E} - class Y extends X - class Z - object Z { implicit def zToE(z: Z) = new E} - """ - - // diagrams must be started. In case there's an error with dot, it should not report anything - def scaladocSettings = "-diagrams -implicits" - - def testModel(rootPackage: Package) = { - // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) - import access._ - - val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("diagrams") - val E = base._class("E") - val diag = E.inheritanceDiagram.get - - // there must be a single this node - assert(diag.nodes.filter(_.isThisNode).length == 1) - - // 1. check class E diagram - assert(diag.isClassDiagram) - - val (incoming, outgoing) = diag.edges.partition(!_._1.isThisNode) - assert(incoming.length == 5) - assert(outgoing.head._2.length == 4) - - val (outgoingSuperclass, outgoingImplicit) = outgoing.head._2.partition(_.isNormalNode) - assert(outgoingSuperclass.length == 3) - assert(outgoingImplicit.length == 1) - - val (incomingSubclass, incomingImplicit) = incoming.partition(_._1.isNormalNode) - assert(incomingSubclass.length == 2) - assert(incomingImplicit.length == 3) - - val classDiag = diag.asInstanceOf[ClassDiagram] - assert(classDiag.incomingImplicits.length == 3) - assert(classDiag.outgoingImplicits.length == 1) - - // 2. check package diagram - // NOTE: Z should be eliminated because it's isolated - val packDiag = base.contentDiagram.get - assert(packDiag.isPackageDiagram) - assert(packDiag.nodes.length == 8) // check singular object removal - assert(packDiag.edges.length == 4) - assert(packDiag.edges.foldLeft(0)(_ + _._2.length) == 6) - - // TODO: Should check numbering - } -} \ No newline at end of file diff --git a/test/scaladoc/run/diagrams-determinism.check b/test/scaladoc/run/diagrams-determinism.check deleted file mode 100644 index 619c56180b..0000000000 --- a/test/scaladoc/run/diagrams-determinism.check +++ /dev/null @@ -1 +0,0 @@ -Done. diff --git a/test/scaladoc/run/diagrams-determinism.scala b/test/scaladoc/run/diagrams-determinism.scala deleted file mode 100644 index 6c8db05d78..0000000000 --- a/test/scaladoc/run/diagrams-determinism.scala +++ /dev/null @@ -1,67 +0,0 @@ -import scala.tools.nsc.doc.model._ -import scala.tools.nsc.doc.model.diagram._ -import scala.tools.partest.ScaladocModelTest - -object Test extends ScaladocModelTest { - - override def code = """ - package scala.test.scaladoc.diagrams - - trait A - trait B extends A - trait C extends B - trait D extends C with A - trait E extends C with A with D - """ - - // diagrams must be started. In case there's an error with dot, it should not report anything - def scaladocSettings = "-diagrams -implicits" - - def testModel(rootPackage: Package) = { - // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) - import access._ - - def diagramString(rootPackage: Package) = { - val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("diagrams") - val A = base._trait("A") - val B = base._trait("B") - val C = base._trait("C") - val D = base._trait("D") - val E = base._trait("E") - - base.contentDiagram.get.toString + "\n" + - A.inheritanceDiagram.get.toString + "\n" + - B.inheritanceDiagram.get.toString + "\n" + - C.inheritanceDiagram.get.toString + "\n" + - D.inheritanceDiagram.get.toString + "\n" + - E.inheritanceDiagram.get.toString - } - - // 1. check that several runs produce the same output - val run0 = diagramString(rootPackage) - val run1 = diagramString(model.getOrElse({sys.error("Scaladoc Model Test ERROR: No universe generated!")}).rootPackage) - val run2 = diagramString(model.getOrElse({sys.error("Scaladoc Model Test ERROR: No universe generated!")}).rootPackage) - val run3 = diagramString(model.getOrElse({sys.error("Scaladoc Model Test ERROR: No universe generated!")}).rootPackage) - - // any variance in the order of the diagram elements should crash the following tests: - assert(run0 == run1) - assert(run1 == run2) - assert(run2 == run3) - - // 2. check the order in the diagram: this node, subclasses, and then implicit conversions - def assertRightOrder(diagram: Diagram) = { - for ((node, subclasses) <- diagram.edges) - assert(subclasses == subclasses.filter(_.isThisNode) ::: - subclasses.filter(_.isNormalNode) ::: - subclasses.filter(_.isImplicitNode)) - } - - val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("diagrams") - assertRightOrder(base.contentDiagram.get) - assertRightOrder(base._trait("A").inheritanceDiagram.get) - assertRightOrder(base._trait("B").inheritanceDiagram.get) - assertRightOrder(base._trait("C").inheritanceDiagram.get) - assertRightOrder(base._trait("D").inheritanceDiagram.get) - assertRightOrder(base._trait("E").inheritanceDiagram.get) - } -} \ No newline at end of file diff --git a/test/scaladoc/run/diagrams-filtering.check b/test/scaladoc/run/diagrams-filtering.check deleted file mode 100644 index 619c56180b..0000000000 --- a/test/scaladoc/run/diagrams-filtering.check +++ /dev/null @@ -1 +0,0 @@ -Done. diff --git a/test/scaladoc/run/diagrams-filtering.scala b/test/scaladoc/run/diagrams-filtering.scala deleted file mode 100644 index dfde5cac52..0000000000 --- a/test/scaladoc/run/diagrams-filtering.scala +++ /dev/null @@ -1,93 +0,0 @@ -import scala.tools.nsc.doc.model._ -import scala.tools.nsc.doc.model.diagram._ -import scala.tools.partest.ScaladocModelTest - -object Test extends ScaladocModelTest { - - override def code = """ - package scala.test.scaladoc - - /** @contentDiagram hideNodes "scala.test.*.A" "java.*", hideEdges ("*G" -> "*E") */ - package object diagrams { - def foo = 4 - } - - package diagrams { - import language.implicitConversions - - /** @inheritanceDiagram hideIncomingImplicits, hideNodes "*E" */ - trait A - trait AA extends A - trait B - trait AAA extends B - - /** @inheritanceDiagram hideDiagram */ - trait C - trait AAAA extends C - - /** @inheritanceDiagram hideEdges("*E" -> "*A") */ - class E extends A with B with C - class F extends E - /** @inheritanceDiagram hideNodes "*G" "G" */ - class G extends E - private class H extends E /* since it's private, it won't go into the diagram */ - class T { def t = true } - object E { - implicit def eToT(e: E) = new T - implicit def eToA(e: E) = new A { } - } - } - """ - - // diagrams must be started. In case there's an error with dot, it should not report anything - def scaladocSettings = "-diagrams -implicits" - - def testModel(rootPackage: Package) = { - // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) - import access._ - - // base package - // Assert we have 7 nodes and 6 edges - val base = rootPackage._package("scala")._package("test")._package("scaladoc")._package("diagrams") - val packDiag = base.contentDiagram.get - assert(packDiag.nodes.length == 6) - assert(packDiag.edges.map(_._2.length).sum == 5) - - // trait A - // Assert we have just 2 nodes and 1 edge - val A = base._trait("A") - val ADiag = A.inheritanceDiagram.get - assert(ADiag.nodes.length == 2) - assert(ADiag.edges.map(_._2.length).sum == 1) - - // trait C - val C = base._trait("C") - assert(!C.inheritanceDiagram.isDefined) - - // trait G - val G = base._trait("G") - assert(!G.inheritanceDiagram.isDefined) - - // trait E - val E = base._class("E") - val EDiag = E.inheritanceDiagram.get - - // there must be a single this node - assert(EDiag.nodes.filter(_.isThisNode).length == 1) - - // 1. check class E diagram - val (incoming, outgoing) = EDiag.edges.partition(!_._1.isThisNode) - assert(incoming.length == 2) // F and G - assert(outgoing.head._2.length == 3) // B, C and T - - val (outgoingSuperclass, outgoingImplicit) = outgoing.head._2.partition(_.isNormalNode) - assert(outgoingSuperclass.length == 2) // B and C - assert(outgoingImplicit.length == 1) // T - - val (incomingSubclass, incomingImplicit) = incoming.partition(_._1.isNormalNode) - assert(incomingSubclass.length == 2) // F and G - assert(incomingImplicit.length == 0) - - assert(EDiag.nodes.length == 6) // E, B and C, F and G and the implicit conversion to T - } -} \ No newline at end of file diff --git a/test/scaladoc/run/implicits-ambiguating.check b/test/scaladoc/run/implicits-ambiguating.check deleted file mode 100644 index 619c56180b..0000000000 --- a/test/scaladoc/run/implicits-ambiguating.check +++ /dev/null @@ -1 +0,0 @@ -Done. diff --git a/test/scaladoc/run/implicits-ambiguating.scala b/test/scaladoc/run/implicits-ambiguating.scala deleted file mode 100644 index 19e0f72b7b..0000000000 --- a/test/scaladoc/run/implicits-ambiguating.scala +++ /dev/null @@ -1,111 +0,0 @@ -import scala.tools.nsc.doc.model._ -import scala.tools.partest.ScaladocModelTest - -object Test extends ScaladocModelTest { - - // test a file instead of a piece of code - override def resourceFile = "implicits-ambiguating-res.scala" - - // start implicits - def scaladocSettings = "-implicits" - - def testModel(root: Package) = { - // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) - import access._ - - // SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: - val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._object("ambiguating") - var conv1: ImplicitConversion = null - var conv2: ImplicitConversion = null - -//// class A /////////////////////////////////////////////////////////////////////////////////////////////////////////// - - val A = base._class("A") - - conv1 = A._conversion(base._object("A").qualifiedName + ".AtoX") - conv2 = A._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv1.members.length == 11) - assert(conv2.members.length == 11) - assert(conv1.constraints.length == 0) - assert(conv2.constraints.length == 0) - - /** - conv1-5 should be ambiguous - * - conv6-7 should not be ambiguous - * - conv8 should be ambiguous - * - conv9 should be ambiguous - * - conv10 and conv11 should not be ambiguous */ - def check1to9(cls: String): Unit = { - for (conv <- (1 to 5).map("conv" + _)) { - assert(conv1._member(conv).byConversion.get.isAmbiguous, cls + " - AtoX." + conv + " is ambiguous") - assert(conv2._member(conv).byConversion.get.isAmbiguous, cls + " - AtoZ." + conv + " is ambiguous") - } - for (conv <- (6 to 7).map("conv" + _)) { - assert(!conv1._member(conv).byConversion.get.isAmbiguous, cls + " - AtoX." + conv + " is not ambiguous") - assert(!conv2._member(conv).byConversion.get.isAmbiguous, cls + " - AtoZ." + conv + " is not ambiguous") - } - assert(conv1._member("conv8").byConversion.get.isAmbiguous, cls + " - AtoX.conv8 is ambiguous") - assert(conv2._member("conv8").byConversion.get.isAmbiguous, cls + " - AtoZ.conv8 is ambiguous") - assert(conv1._member("conv9").byConversion.get.isAmbiguous, cls + " - AtoX.conv9 is ambiguous") - assert(conv2._member("conv9").byConversion.get.isAmbiguous, cls + " - AtoZ.conv9 is ambiguous") - } - check1to9("A") - assert(!conv1._member("conv10").byConversion.get.isAmbiguous, "A - AtoX.conv10 is not ambiguous") - assert(!conv2._member("conv10").byConversion.get.isAmbiguous, "A - AtoZ.conv10 is not ambiguous") - assert(!conv1._member("conv11").byConversion.get.isAmbiguous, "A - AtoX.conv11 is not ambiguous") - assert(!conv2._member("conv11").byConversion.get.isAmbiguous, "A - AtoZ.conv11 is not ambiguous") - -//// class B /////////////////////////////////////////////////////////////////////////////////////////////////////////// - - val B = base._class("B") - - conv1 = B._conversion(base._object("A").qualifiedName + ".AtoX") - conv2 = B._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv1.members.length == 11) - assert(conv2.members.length == 11) - assert(conv1.constraints.length == 0) - assert(conv2.constraints.length == 0) - - /** conv1-9 should be the same, conv10 should be ambiguous, conv11 should be okay */ - check1to9("B") - assert(conv1._member("conv10").byConversion.get.isAmbiguous, "B - AtoX.conv10 is ambiguous") - assert(conv2._member("conv10").byConversion.get.isAmbiguous, "B - AtoZ.conv10 is ambiguous") - assert(!conv1._member("conv11").byConversion.get.isAmbiguous, "B - AtoX.conv11 is not ambiguous") - assert(!conv2._member("conv11").byConversion.get.isAmbiguous, "B - AtoZ.conv11 is not ambiguous") - -//// class C /////////////////////////////////////////////////////////////////////////////////////////////////////////// - - val C = base._class("C") - - conv1 = C._conversion(base._object("A").qualifiedName + ".AtoX") - conv2 = C._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv1.members.length == 11) - assert(conv2.members.length == 11) - assert(conv1.constraints.length == 0) - assert(conv2.constraints.length == 0) - - /** conv1-9 should be the same, conv10 and conv11 should not be ambiguous */ - check1to9("C") - assert(!conv1._member("conv10").byConversion.get.isAmbiguous, "C - AtoX.conv10 is not ambiguous") - assert(!conv2._member("conv10").byConversion.get.isAmbiguous, "C - AtoZ.conv10 is not ambiguous") - assert(!conv1._member("conv11").byConversion.get.isAmbiguous, "C - AtoX.conv11 is not ambiguous") - assert(!conv2._member("conv11").byConversion.get.isAmbiguous, "C - AtoZ.conv11 is not ambiguous") - -//// class D /////////////////////////////////////////////////////////////////////////////////////////////////////////// - - val D = base._class("D") - - conv1 = D._conversion(base._object("A").qualifiedName + ".AtoX") - conv2 = D._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv1.members.length == 11) - assert(conv2.members.length == 11) - assert(conv1.constraints.length == 0) - assert(conv2.constraints.length == 0) - - /** conv1-9 should be the same, conv10 should not be ambiguous while conv11 should be ambiguous */ - check1to9("D") - assert(!conv1._member("conv10").byConversion.get.isAmbiguous, "D - AtoX.conv10 is not ambiguous") - assert(!conv2._member("conv10").byConversion.get.isAmbiguous, "D - AtoZ.conv10 is not ambiguous") - assert(conv1._member("conv11").byConversion.get.isAmbiguous, "D - AtoX.conv11 is ambiguous") - assert(conv2._member("conv11").byConversion.get.isAmbiguous, "D - AtoZ.conv11 is ambiguous") - } -} \ No newline at end of file diff --git a/test/scaladoc/run/implicits-base.scala b/test/scaladoc/run/implicits-base.scala index ce2d025511..06d017ed70 100644 --- a/test/scaladoc/run/implicits-base.scala +++ b/test/scaladoc/run/implicits-base.scala @@ -22,12 +22,8 @@ object Test extends ScaladocModelTest { val A = base._class("A") - // def convToPimpedA(x: T) // pimpA0: with no constraints, SHADOWED - conv = A._conversion(A.qualifiedName + ".pimpA0") - assert(conv.members.length == 1) - assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) - assert(conv._member("convToPimpedA").resultType.name == "T") + // the method pimped on by pimpA0 should be shadowed by the method in class A + assert(A._conversions(A.qualifiedName + ".pimpA0").isEmpty) // def convToNumericA: T // pimpA1: with a constraint that there is x: Numeric[T] implicit in scope conv = A._conversion(A.qualifiedName + ".pimpA1") @@ -57,7 +53,6 @@ object Test extends ScaladocModelTest { conv = A._conversion(A.qualifiedName + ".pimpA5") assert(conv.members.length == 1) assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[T]]") // def convToMyNumericA: T // pimpA6: with a constraint that there is x: MyNumeric[T] implicit in scope @@ -81,16 +76,10 @@ object Test extends ScaladocModelTest { val B = base._class("B") // these conversions should not affect B + assert(B._conversions(A.qualifiedName + ".pimpA0").isEmpty) assert(B._conversions(A.qualifiedName + ".pimpA2").isEmpty) assert(B._conversions(A.qualifiedName + ".pimpA4").isEmpty) - // def convToPimpedA(x: Double) // pimpA0: no constraints, SHADOWED - conv = B._conversion(A.qualifiedName + ".pimpA0") - assert(conv.members.length == 1) - assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) - assert(conv._member("convToPimpedA").resultType.name == "Double") - // def convToNumericA: Double // pimpA1: no constraintsd conv = B._conversion(A.qualifiedName + ".pimpA1") assert(conv.members.length == 1) @@ -107,7 +96,6 @@ object Test extends ScaladocModelTest { conv = B._conversion(A.qualifiedName + ".pimpA5") assert(conv.members.length == 1) assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[Double]]") // def convToMyNumericA: Double // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Double] implicit in scope @@ -131,17 +119,11 @@ object Test extends ScaladocModelTest { val C = base._class("C") // these conversions should not affect C + assert(C._conversions(A.qualifiedName + ".pimpA0").isEmpty) assert(C._conversions(A.qualifiedName + ".pimpA3").isEmpty) assert(C._conversions(A.qualifiedName + ".pimpA4").isEmpty) assert(C._conversions(A.qualifiedName + ".pimpA7").isEmpty) - // def convToPimpedA(x: Int) // pimpA0: no constraints, SHADOWED - conv = C._conversion(A.qualifiedName + ".pimpA0") - assert(conv.members.length == 1) - assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) - assert(conv._member("convToPimpedA").resultType.name == "Int") - // def convToNumericA: Int // pimpA1: no constraints conv = C._conversion(A.qualifiedName + ".pimpA1") assert(conv.members.length == 1) @@ -158,7 +140,6 @@ object Test extends ScaladocModelTest { conv = C._conversion(A.qualifiedName + ".pimpA5") assert(conv.members.length == 1) assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[Int]]") // def convToMyNumericA: Int // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Int] implicit in scope @@ -172,18 +153,12 @@ object Test extends ScaladocModelTest { val D = base._class("D") // these conversions should not affect D + assert(D._conversions(A.qualifiedName + ".pimpA0").isEmpty) assert(D._conversions(A.qualifiedName + ".pimpA2").isEmpty) assert(D._conversions(A.qualifiedName + ".pimpA3").isEmpty) assert(D._conversions(A.qualifiedName + ".pimpA4").isEmpty) assert(D._conversions(A.qualifiedName + ".pimpA7").isEmpty) - // def convToPimpedA(x: String) // pimpA0: no constraints, SHADOWED - conv = D._conversion(A.qualifiedName + ".pimpA0") - assert(conv.members.length == 1) - assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) - assert(conv._member("convToPimpedA").resultType.name == "String") - // def convToNumericA: String // pimpA1: (if showAll is set) with a constraint that there is x: Numeric[String] implicit in scope conv = D._conversion(A.qualifiedName + ".pimpA1") assert(conv.members.length == 1) @@ -194,7 +169,6 @@ object Test extends ScaladocModelTest { conv = D._conversion(A.qualifiedName + ".pimpA5") assert(conv.members.length == 1) assert(conv.constraints.length == 0) - assert(conv._member("convToPimpedA").byConversion.get.isShadowed) assert(conv._member("convToPimpedA").resultType.name == "Bar[Foo[String]]") // def convToMyNumericA: String // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[String] implicit in scope diff --git a/test/scaladoc/run/implicits-elimination.check b/test/scaladoc/run/implicits-elimination.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/implicits-elimination.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/implicits-elimination.scala b/test/scaladoc/run/implicits-elimination.scala new file mode 100644 index 0000000000..ed37b9cd90 --- /dev/null +++ b/test/scaladoc/run/implicits-elimination.scala @@ -0,0 +1,23 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest +import language._ + +object Test extends ScaladocModelTest { + + // test a file instead of a piece of code + override def resourceFile = "implicits-elimination-res.scala" + + // start implicits + def scaladocSettings = "-implicits" + + def testModel(root: Package) = { + // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s)) + import access._ + + // SEE THE test/resources/implicits-elimination-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE: + val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._package("elimination") + val A = base._class("A") + + assert(A._conversions(A.qualifiedName + ".toB").isEmpty) + } +} diff --git a/test/scaladoc/run/implicits-shadowing.scala b/test/scaladoc/run/implicits-shadowing.scala index f8a016fac9..7835223d21 100644 --- a/test/scaladoc/run/implicits-shadowing.scala +++ b/test/scaladoc/run/implicits-shadowing.scala @@ -22,8 +22,12 @@ object Test extends ScaladocModelTest { val A = base._class("A") conv = A._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv.members.length == 11) - assert(conv.members.forall(_.byConversion.get.isShadowed)) + assert(conv.members.length == 5) + conv._member("conv5") + conv._member("conv8") + conv._member("conv9") + conv._member("conv10") + conv._member("conv11") assert(conv.constraints.length == 0) //// class B /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -31,8 +35,11 @@ object Test extends ScaladocModelTest { val B = base._class("B") conv = B._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv.members.length == 11) - assert(conv.members.forall(_.byConversion.get.isShadowed)) + assert(conv.members.length == 4) + conv._member("conv5") + conv._member("conv8") + conv._member("conv9") + conv._member("conv11") assert(conv.constraints.length == 0) //// class C /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -40,8 +47,12 @@ object Test extends ScaladocModelTest { val C = base._class("C") conv = C._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv.members.length == 11) - assert(conv.members.forall(_.byConversion.get.isShadowed)) + assert(conv.members.length == 5) + conv._member("conv5") + conv._member("conv8") + conv._member("conv9") + conv._member("conv10") + conv._member("conv11") assert(conv.constraints.length == 0) //// class D /////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -49,8 +60,11 @@ object Test extends ScaladocModelTest { val D = base._class("D") conv = D._conversion(base._object("A").qualifiedName + ".AtoZ") - assert(conv.members.length == 11) - assert(conv.members.forall(_.byConversion.get.isShadowed)) + assert(conv.members.length == 4) + conv._member("conv5") + conv._member("conv8") + conv._member("conv9") + conv._member("conv10") assert(conv.constraints.length == 0) } -} +} \ No newline at end of file diff --git a/test/scaladoc/scalacheck/CommentFactoryTest.scala b/test/scaladoc/scalacheck/CommentFactoryTest.scala index b576ba5544..68ca68efdd 100644 --- a/test/scaladoc/scalacheck/CommentFactoryTest.scala +++ b/test/scaladoc/scalacheck/CommentFactoryTest.scala @@ -5,12 +5,10 @@ import scala.tools.nsc.Global import scala.tools.nsc.doc import scala.tools.nsc.doc.model._ import scala.tools.nsc.doc.model.comment._ -import scala.tools.nsc.doc.model._ -import scala.tools.nsc.doc.model.diagram._ class Factory(val g: Global, val s: doc.Settings) extends doc.model.ModelFactory(g, s) { - thisFactory: Factory with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with doc.model.TreeFactory => + thisFactory: Factory with ModelFactoryImplicitSupport with CommentFactory with doc.model.TreeFactory => def strip(c: Comment): Option[Inline] = { c.body match { @@ -31,7 +29,7 @@ object Test extends Properties("CommentFactory") { val settings = new doc.Settings((str: String) => {}) val reporter = new scala.tools.nsc.reporters.ConsoleReporter(settings) val g = new Global(settings, reporter) - (new Factory(g, settings) with ModelFactoryImplicitSupport with DiagramFactory with CommentFactory with doc.model.TreeFactory) + (new Factory(g, settings) with ModelFactoryImplicitSupport with CommentFactory with doc.model.TreeFactory) } def parse(src: String, dst: Inline) = { -- cgit v1.2.3 From 18aab246de965099f8b1c9b82164fb8d77b75de1 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 18 Jun 2012 14:52:13 +0200 Subject: fixes checkinit, now it's for real --- src/reflect/scala/reflect/internal/SymbolTable.scala | 2 +- src/reflect/scala/reflect/runtime/JavaUniverse.scala | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index cadd76b1ba..a0c57042f3 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -40,7 +40,7 @@ abstract class SymbolTable extends makro.Universe { val gen = new TreeGen { val global: SymbolTable.this.type = SymbolTable.this } - val treeBuild = gen + lazy val treeBuild = gen def log(msg: => AnyRef): Unit def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index 7ce71166c9..629df76178 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -12,14 +12,6 @@ class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.S def picklerPhase = SomePhase - type TreeGen = internal.TreeGen - - override type Position = scala.reflect.internal.util.Position - - override val gen = new TreeGen { val global: self.type = self } - - override val treeBuild = gen - lazy val settings = new Settings def forInteractive = false def forScaladoc = false -- cgit v1.2.3 From da7235afe89dc0b94898b76fb347fed61811bf1f Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Mon, 18 Jun 2012 16:15:20 +0200 Subject: Fix SI-4954. Override inner classes in `LinkedHashMap` that correspond to `filterKeys`, `mapValues` and `keys` to retain a proper ordering of elements when they are transformed. --- src/library/scala/collection/MapLike.scala | 28 ++++++++------ .../scala/collection/mutable/LinkedHashMap.scala | 21 +++++++++- test/files/run/t4954.scala | 45 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 test/files/run/t4954.scala diff --git a/src/library/scala/collection/MapLike.scala b/src/library/scala/collection/MapLike.scala index b9b8f62574..75f9ff93db 100644 --- a/src/library/scala/collection/MapLike.scala +++ b/src/library/scala/collection/MapLike.scala @@ -227,30 +227,34 @@ self => def default(key: A): B = throw new NoSuchElementException("key not found: " + key) - /** Filters this map by retaining only keys satisfying a predicate. - * @param p the predicate used to test keys - * @return an immutable map consisting only of those key value pairs of this map where the key satisfies - * the predicate `p`. The resulting map wraps the original map without copying any elements. - */ - def filterKeys(p: A => Boolean): Map[A, B] = new AbstractMap[A, B] with DefaultMap[A, B] { + protected class FilteredKeys(p: A => Boolean) extends AbstractMap[A, B] with DefaultMap[A, B] { override def foreach[C](f: ((A, B)) => C): Unit = for (kv <- self) if (p(kv._1)) f(kv) def iterator = self.iterator.filter(kv => p(kv._1)) override def contains(key: A) = self.contains(key) && p(key) def get(key: A) = if (!p(key)) None else self.get(key) } - - /** Transforms this map by applying a function to every retrieved value. - * @param f the function used to transform values of this map. - * @return a map view which maps every key of this map - * to `f(this(key))`. The resulting map wraps the original map without copying any elements. + + /** Filters this map by retaining only keys satisfying a predicate. + * @param p the predicate used to test keys + * @return an immutable map consisting only of those key value pairs of this map where the key satisfies + * the predicate `p`. The resulting map wraps the original map without copying any elements. */ - def mapValues[C](f: B => C): Map[A, C] = new AbstractMap[A, C] with DefaultMap[A, C] { + def filterKeys(p: A => Boolean): Map[A, B] = new FilteredKeys(p) + + protected class MappedValues[C](f: B => C) extends AbstractMap[A, C] with DefaultMap[A, C] { override def foreach[D](g: ((A, C)) => D): Unit = for ((k, v) <- self) g((k, f(v))) def iterator = for ((k, v) <- self.iterator) yield (k, f(v)) override def size = self.size override def contains(key: A) = self.contains(key) def get(key: A) = self.get(key).map(f) } + + /** Transforms this map by applying a function to every retrieved value. + * @param f the function used to transform values of this map. + * @return a map view which maps every key of this map + * to `f(this(key))`. The resulting map wraps the original map without copying any elements. + */ + def mapValues[C](f: B => C): Map[A, C] = new MappedValues(f) // The following 5 operations (updated, two times +, two times ++) should really be // generic, returning This[B]. We need better covariance support to express that though. diff --git a/src/library/scala/collection/mutable/LinkedHashMap.scala b/src/library/scala/collection/mutable/LinkedHashMap.scala index 4150cf9eba..5643e070f8 100644 --- a/src/library/scala/collection/mutable/LinkedHashMap.scala +++ b/src/library/scala/collection/mutable/LinkedHashMap.scala @@ -49,7 +49,8 @@ class LinkedHashMap[A, B] extends AbstractMap[A, B] with Map[A, B] with MapLike[A, B, LinkedHashMap[A, B]] with HashTable[A, LinkedEntry[A, B]] - with Serializable { + with Serializable +{ override def empty = LinkedHashMap.empty[A, B] override def size = tableSize @@ -107,7 +108,25 @@ class LinkedHashMap[A, B] extends AbstractMap[A, B] if (hasNext) { val res = (cur.key, cur.value); cur = cur.later; res } else Iterator.empty.next } + + protected class FilteredKeys(p: A => Boolean) extends super.FilteredKeys(p) { + override def empty = LinkedHashMap.empty + } + + override def filterKeys(p: A => Boolean): scala.collection.Map[A, B] = new FilteredKeys(p) + protected class MappedValues[C](f: B => C) extends super.MappedValues[C](f) { + override def empty = LinkedHashMap.empty + } + + override def mapValues[C](f: B => C): scala.collection.Map[A, C] = new MappedValues(f) + + protected class DefaultKeySet extends super.DefaultKeySet { + override def empty = LinkedHashSet.empty + } + + override def keySet: scala.collection.Set[A] = new DefaultKeySet + override def keysIterator: Iterator[A] = new AbstractIterator[A] { private var cur = firstEntry def hasNext = cur ne null diff --git a/test/files/run/t4954.scala b/test/files/run/t4954.scala new file mode 100644 index 0000000000..b4916e651d --- /dev/null +++ b/test/files/run/t4954.scala @@ -0,0 +1,45 @@ + + +import collection._ + + +object Test { + + def main(args: Array[String]) { + val m = scala.collection.mutable.LinkedHashMap("one" -> 1, "two" -> 2, "three" -> 3, "four" -> 4, "five" -> 5) + val expected = List("one", "two", "three", "four", "five") + assert(m.keys.iterator.toList == expected) + assert(m.keys.drop(0).iterator.toList == expected) + assert(m.keys.drop(1).iterator.toList == expected.drop(1)) + assert(m.keys.drop(2).iterator.toList == expected.drop(2)) + assert(m.keys.drop(3).iterator.toList == expected.drop(3)) + assert(m.keys.drop(4).iterator.toList == expected.drop(4)) + assert(m.keys.drop(5).iterator.toList == expected.drop(5)) + + val expvals = List(1, 2, 3, 4, 5) + assert(m.values.iterator.toList == expvals) + assert(m.values.drop(0).iterator.toList == expvals) + assert(m.values.drop(1).iterator.toList == expvals.drop(1)) + assert(m.values.drop(2).iterator.toList == expvals.drop(2)) + assert(m.values.drop(3).iterator.toList == expvals.drop(3)) + assert(m.values.drop(4).iterator.toList == expvals.drop(4)) + assert(m.values.drop(5).iterator.toList == expvals.drop(5)) + + val pred = (x: String) => x.length < 6 + val filtered = m.filterKeys(pred) + assert(filtered.drop(0).keys.toList == expected.filter(pred)) + assert(filtered.drop(1).keys.toList == expected.filter(pred).drop(1)) + assert(filtered.drop(2).keys.toList == expected.filter(pred).drop(2)) + assert(filtered.drop(3).keys.toList == expected.filter(pred).drop(3)) + assert(filtered.drop(4).keys.toList == expected.filter(pred).drop(4)) + + val mapped = m.mapValues(-_) + assert(mapped.drop(0).keys.toList == expected) + assert(mapped.drop(1).keys.toList == expected.drop(1)) + assert(mapped.drop(2).keys.toList == expected.drop(2)) + assert(mapped.drop(3).keys.toList == expected.drop(3)) + assert(mapped.drop(4).keys.toList == expected.drop(4)) + assert(mapped.drop(5).keys.toList == expected.drop(5)) + } + +} -- cgit v1.2.3 From bec459a554d978f19dc0b40289d9179dbc28de78 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Mon, 18 Jun 2012 14:11:00 +0200 Subject: enables reification of anonymous classes --- src/compiler/scala/reflect/reify/Errors.scala | 5 ----- src/compiler/scala/reflect/reify/Reifier.scala | 14 -------------- test/files/neg/t5334_1.check | 4 ---- test/files/neg/t5334_1.scala | 9 --------- test/files/neg/t5334_2.check | 4 ---- test/files/neg/t5334_2.scala | 9 --------- test/pending/run/t5334_1.scala | 9 +++++++++ test/pending/run/t5334_2.scala | 9 +++++++++ 8 files changed, 18 insertions(+), 45 deletions(-) delete mode 100644 test/files/neg/t5334_1.check delete mode 100644 test/files/neg/t5334_1.scala delete mode 100644 test/files/neg/t5334_2.check delete mode 100644 test/files/neg/t5334_2.scala create mode 100644 test/pending/run/t5334_1.scala create mode 100644 test/pending/run/t5334_2.scala diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 714795503b..1b72b3075b 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -17,11 +17,6 @@ trait Errors { // expected errors: these can happen if the user casually writes whatever.reify(...) // hence we don't crash here, but nicely report a typechecking error and bail out asap - def CannotReifyReifeeThatHasTypeLocalToReifee(tree: Tree) = { - val msg = "implementation restriction: cannot reify block of type %s that involves a type declared inside the block being reified. consider casting the return value to a suitable type".format(tree.tpe) - throw new ReificationError(tree.pos, msg) - } - def CannotReifyType(tpe: Type) = { val msg = "implementation restriction: cannot reify type %s (%s)".format(tpe, tpe.kind) throw new ReificationError(defaultErrorPosition, msg) diff --git a/src/compiler/scala/reflect/reify/Reifier.scala b/src/compiler/scala/reflect/reify/Reifier.scala index 00f25f0d8b..8fba7274be 100644 --- a/src/compiler/scala/reflect/reify/Reifier.scala +++ b/src/compiler/scala/reflect/reify/Reifier.scala @@ -68,20 +68,6 @@ abstract class Reifier extends States val pipeline = mkReificationPipeline val rtree = pipeline(tree) - // consider the following code snippet - // - // val x = reify { class C; new C } - // - // inferred type for x will be C - // but C ceases to exist after reification so this type is clearly incorrect - // however, reify is "just" a library function, so it cannot affect type inference - // - // hence we crash here even though the reification itself goes well - // fortunately, all that it takes to fix the error is to cast "new C" to Object - // so I'm not very much worried about introducing this restriction - if (tree.tpe exists (sub => sub.typeSymbol.isLocalToReifee)) - CannotReifyReifeeThatHasTypeLocalToReifee(tree) - val tpe = typer.packedType(tree, NoSymbol) val ReifiedType(_, _, tpeSymtab, _, rtpe, tpeReificationIsConcrete) = `package`.reifyType(global)(typer, universe, mirror, tpe, concrete = false) state.reificationIsConcrete &= tpeReificationIsConcrete diff --git a/test/files/neg/t5334_1.check b/test/files/neg/t5334_1.check deleted file mode 100644 index eca854964a..0000000000 --- a/test/files/neg/t5334_1.check +++ /dev/null @@ -1,4 +0,0 @@ -t5334_1.scala:5: error: implementation restriction: cannot reify block of type C that involves a type declared inside the block being reified. consider casting the return value to a suitable type - reify { - ^ -one error found diff --git a/test/files/neg/t5334_1.scala b/test/files/neg/t5334_1.scala deleted file mode 100644 index b75badb145..0000000000 --- a/test/files/neg/t5334_1.scala +++ /dev/null @@ -1,9 +0,0 @@ -import scala.reflect.runtime.universe._ -import scala.tools.reflect.Eval - -object Test extends App { - reify { - class C { override def toString = "C" } - new C - }.eval -} \ No newline at end of file diff --git a/test/files/neg/t5334_2.check b/test/files/neg/t5334_2.check deleted file mode 100644 index e21f0d5967..0000000000 --- a/test/files/neg/t5334_2.check +++ /dev/null @@ -1,4 +0,0 @@ -t5334_2.scala:5: error: implementation restriction: cannot reify block of type List[(C, C)] that involves a type declared inside the block being reified. consider casting the return value to a suitable type - reify { - ^ -one error found diff --git a/test/files/neg/t5334_2.scala b/test/files/neg/t5334_2.scala deleted file mode 100644 index e082e3b8e3..0000000000 --- a/test/files/neg/t5334_2.scala +++ /dev/null @@ -1,9 +0,0 @@ -import scala.reflect.runtime.universe._ -import scala.tools.reflect.Eval - -object Test extends App { - reify { - class C { override def toString() = "C" } - List((new C, new C)) - }.eval -} \ No newline at end of file diff --git a/test/pending/run/t5334_1.scala b/test/pending/run/t5334_1.scala new file mode 100644 index 0000000000..b75badb145 --- /dev/null +++ b/test/pending/run/t5334_1.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.Eval + +object Test extends App { + reify { + class C { override def toString = "C" } + new C + }.eval +} \ No newline at end of file diff --git a/test/pending/run/t5334_2.scala b/test/pending/run/t5334_2.scala new file mode 100644 index 0000000000..e082e3b8e3 --- /dev/null +++ b/test/pending/run/t5334_2.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.Eval + +object Test extends App { + reify { + class C { override def toString() = "C" } + List((new C, new C)) + }.eval +} \ No newline at end of file -- cgit v1.2.3 From f559505c1f254c98ae34bb4cdf1e2b624d25a84c Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 11:43:18 -0400 Subject: Adding copyInto and toVector methods to collections. * Added generic copyInto method for collections. For any collection with a CanBuildFrom, can convert a generic collection into it using the builder. * Added specifici toVector method for collections. This is more efficient than copyInto if the collection is a Vector. --- .../scala/collection/GenTraversableOnce.scala | 19 ++++ src/library/scala/collection/Iterator.scala | 10 ++ src/library/scala/collection/TraversableLike.scala | 7 ++ .../scala/collection/immutable/Vector.scala | 2 + .../collection/parallel/ParIterableLike.scala | 5 + .../collection/parallel/immutable/ParVector.scala | 2 + test/files/run/collection-conversions.check | 104 +++++++++++++++++++++ test/files/run/collection-conversions.scala | 60 ++++++++++++ 8 files changed, 209 insertions(+) create mode 100644 test/files/run/collection-conversions.check create mode 100644 test/files/run/collection-conversions.scala diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index eadacd9209..d22fb0f3ad 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -9,6 +9,8 @@ package scala.collection import scala.reflect.ClassTag +import scala.collection.generic.CanBuildFrom +import scala.annotation.unchecked.{ uncheckedVariance => uV } /** A template trait for all traversable-once objects which may be * traversed in parallel. @@ -552,4 +554,21 @@ trait GenTraversableOnce[+A] extends Any { * containing all key/value pairs of type `(T, U)` of this $coll. */ def toMap[K, V](implicit ev: A <:< (K, V)): GenMap[K, V] + + /** Converts this $coll to a Vector. + * $willNotTerminateInf + * @return a vector containing all elements of this $coll. + */ + def toVector: Vector[A] + + /** Converts this $coll into another by copying all elemnents. + * $willNotTerminateInf + * @return a new collection containing all elements of this $coll. + * + * @usecase def copyInto[Col[_]]: Col[A] + * @inheritdoc + * $willNotTerminateInf + * @return a new collection containing all elemnts of this $coll. + */ + def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index b2bbc8d888..62449c7712 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -11,6 +11,8 @@ package scala.collection import mutable.ArrayBuffer import annotation.migration import immutable.Stream +import scala.collection.generic.CanBuildFrom +import scala.annotation.unchecked.{ uncheckedVariance => uV } /** The `Iterator` object provides various functions for creating specialized iterators. * @@ -1139,6 +1141,14 @@ trait Iterator[+A] extends TraversableOnce[A] { if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] + def toVector: Vector[A] = copyInto[Vector] + def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + val b = cbf() + while(hasNext) b += next + b.result + } + + /** Converts this iterator to a string. * * @return `"empty iterator"` or `"non-empty iterator"`, depending on diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 3716a318d9..898ea4d654 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,6 +616,13 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream + def toVector: Vector[A] = copyInto[Vector] + def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + val b = cbf() + b.sizeHint(this) + b ++= thisCollection + b.result + } /** Converts this $coll to a string. * diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 1395a8f52d..d100bf93df 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -77,6 +77,8 @@ override def companion: GenericCompanion[Vector] = Vector override def par = new ParVector(this) + override def toVector: Vector[A] = this + override def lengthCompare(len: Int): Int = length - len private[collection] final def initIterator[B >: A](s: VectorIterator[B]) { diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index a447f1b5e4..ed667d85a0 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -851,6 +851,11 @@ self: ParIterableLike[T, Repr, Sequential] => override def toMap[K, V](implicit ev: T <:< (K, V)): immutable.ParMap[K, V] = toParMap[K, V, immutable.ParMap[K, V]](() => immutable.ParMap.newCombiner[K, V]) + override def toVector: Vector[T] = seq.toVector + + override def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyInto[Col] + + /* tasks */ protected trait StrictSplitterCheckTask[R, Tp] extends Task[R, Tp] { diff --git a/src/library/scala/collection/parallel/immutable/ParVector.scala b/src/library/scala/collection/parallel/immutable/ParVector.scala index 1ece663a1d..e4099f1809 100644 --- a/src/library/scala/collection/parallel/immutable/ParVector.scala +++ b/src/library/scala/collection/parallel/immutable/ParVector.scala @@ -62,6 +62,8 @@ extends ParSeq[T] override def seq: Vector[T] = vector + override def toVector: Vector[T] = vector + class ParVectorIterator(_start: Int, _end: Int) extends VectorIterator[T](_start, _end) with SeqSplitter[T] { def remaining: Int = remainingElementCount def dup: SeqSplitter[T] = (new ParVector(remainingVector)).splitter diff --git a/test/files/run/collection-conversions.check b/test/files/run/collection-conversions.check new file mode 100644 index 0000000000..08d0fa32c5 --- /dev/null +++ b/test/files/run/collection-conversions.check @@ -0,0 +1,104 @@ +-- Testing iterator --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing Vector --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing List --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing Buffer --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing ParVector --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing Set --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing SetView --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK +-- Testing BufferView --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala new file mode 100644 index 0000000000..df12134c04 --- /dev/null +++ b/test/files/run/collection-conversions.scala @@ -0,0 +1,60 @@ +import collection._ +import mutable.Buffer +import parallel.immutable.ParVector +import reflect.ClassTag + +object Test { + + def printResult[A,B](msg: String, obj: A, expected: B)(implicit tag: ClassTag[A], tag2: ClassTag[B]) = { + print(" :" + msg +": ") + val isArray = obj match { + case x: Array[Int] => true + case _ => false + } + val expectedEquals = + if(isArray) obj.asInstanceOf[Array[Int]].toSeq == expected.asInstanceOf[Array[Int]].toSeq + else obj == expected + val tagEquals = tag == tag2 + if(expectedEquals && tagEquals) print("OK") + else print("FAILED") + if(!expectedEquals) print(", " + obj + " != " + expected) + if(!tagEquals) print(", " + tag + " != " + tag2) + println("") + } + + val testVector = Vector(1,2,3) + val testBuffer = Buffer(1,2,3) + val testGenSeq = GenSeq(1,2,3) + val testSeq = Seq(1,2,3) + val testStream = Stream(1,2,3) + val testArray = Array(1,2,3) + val testParVector = ParVector(1,2,3) + + def testConversion[A: ClassTag](name: String, col: => GenTraversableOnce[A]): Unit = { + val tmp = col + println("-- Testing " + name + " ---") + printResult("[Direct] Vector ", col.toVector, testVector) + printResult("[Copy] Vector ", col.copyInto[Vector], testVector) + printResult("[Direct] Buffer ", col.toBuffer, testBuffer) + printResult("[Copy] Buffer ", col.copyInto[Buffer], testBuffer) + printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) + printResult("[Copy] GenSeq ", col.copyInto[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.copyInto[Seq], testSeq) + printResult("[Direct] Stream ", col.toStream, testStream) + printResult("[Copy] Stream ", col.copyInto[Stream], testStream) + printResult("[Direct] Array ", col.toArray, testArray) + printResult("[Copy] Array ", col.copyInto[Array], testArray) + printResult("[Copy] ParVector", col.copyInto[ParVector], testParVector) + } + + def main(args: Array[String]): Unit = { + testConversion("iterator", (1 to 3).iterator) + testConversion("Vector", Vector(1,2,3)) + testConversion("List", List(1,2,3)) + testConversion("Buffer", Buffer(1,2,3)) + testConversion("ParVector", ParVector(1,2,3)) + testConversion("Set", Set(1,2,3)) + testConversion("SetView", Set(1,2,3).view) + testConversion("BufferView", Buffer(1,2,3).view) + } +} -- cgit v1.2.3 From de6519952582a74a3f9769b1dbde26c617a819f1 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 12:14:44 -0400 Subject: Fixes from review. * Fixed typo * Renamed copyInto to copyTo * Added tparam doc. --- src/library/scala/collection/GenTraversableOnce.scala | 8 ++++---- src/library/scala/collection/Iterator.scala | 4 ++-- src/library/scala/collection/TraversableLike.scala | 4 ++-- .../scala/collection/parallel/ParIterableLike.scala | 2 +- test/files/run/collection-conversions.scala | 14 +++++++------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index d22fb0f3ad..2996071d71 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -562,13 +562,13 @@ trait GenTraversableOnce[+A] extends Any { def toVector: Vector[A] /** Converts this $coll into another by copying all elemnents. - * $willNotTerminateInf + * @tparam Col The collection type to build. * @return a new collection containing all elements of this $coll. * - * @usecase def copyInto[Col[_]]: Col[A] + * @usecase def copyTo[Col[_]]: Col[A] * @inheritdoc * $willNotTerminateInf - * @return a new collection containing all elemnts of this $coll. + * @return a new collection containing all elements of this $coll. */ - def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] + def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 62449c7712..4283120519 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -1141,8 +1141,8 @@ trait Iterator[+A] extends TraversableOnce[A] { if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] - def toVector: Vector[A] = copyInto[Vector] - def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = copyTo[Vector] + def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() while(hasNext) b += next b.result diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 898ea4d654..68abc36bf7 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,8 +616,8 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream - def toVector: Vector[A] = copyInto[Vector] - def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = copyTo[Vector] + def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() b.sizeHint(this) b ++= thisCollection diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index ed667d85a0..12b777832e 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -853,7 +853,7 @@ self: ParIterableLike[T, Repr, Sequential] => override def toVector: Vector[T] = seq.toVector - override def copyInto[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyInto[Col] + override def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyTo[Col] /* tasks */ diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index df12134c04..390ba06dac 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -34,17 +34,17 @@ object Test { val tmp = col println("-- Testing " + name + " ---") printResult("[Direct] Vector ", col.toVector, testVector) - printResult("[Copy] Vector ", col.copyInto[Vector], testVector) + printResult("[Copy] Vector ", col.copyTo[Vector], testVector) printResult("[Direct] Buffer ", col.toBuffer, testBuffer) - printResult("[Copy] Buffer ", col.copyInto[Buffer], testBuffer) + printResult("[Copy] Buffer ", col.copyTo[Buffer], testBuffer) printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) - printResult("[Copy] GenSeq ", col.copyInto[GenSeq], testGenSeq) - printResult("[Copy] Seq ", col.copyInto[Seq], testSeq) + printResult("[Copy] GenSeq ", col.copyTo[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.copyTo[Seq], testSeq) printResult("[Direct] Stream ", col.toStream, testStream) - printResult("[Copy] Stream ", col.copyInto[Stream], testStream) + printResult("[Copy] Stream ", col.copyTo[Stream], testStream) printResult("[Direct] Array ", col.toArray, testArray) - printResult("[Copy] Array ", col.copyInto[Array], testArray) - printResult("[Copy] ParVector", col.copyInto[ParVector], testParVector) + printResult("[Copy] Array ", col.copyTo[Array], testArray) + printResult("[Copy] ParVector", col.copyTo[ParVector], testParVector) } def main(args: Array[String]): Unit = { -- cgit v1.2.3 From 6f5979f00118e19b1e3e143d366f2d6029a63466 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Mon, 18 Jun 2012 18:09:12 +0200 Subject: Use `ThreadLocalRandom` in `TrieMap.size`. --- src/library/scala/collection/concurrent/TrieMap.scala | 6 +++++- test/benchmarking/ParCtrie-size.scala | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/library/scala/collection/concurrent/TrieMap.scala b/src/library/scala/collection/concurrent/TrieMap.scala index 08e9125bd8..2d8217551a 100644 --- a/src/library/scala/collection/concurrent/TrieMap.scala +++ b/src/library/scala/collection/concurrent/TrieMap.scala @@ -473,7 +473,11 @@ extends CNodeBase[K, V] { private def computeSize(ct: TrieMap[K, V]): Int = { var i = 0 var sz = 0 - val offset = math.abs(util.Random.nextInt()) % array.length + val offset = + if (array.length > 0) + //util.Random.nextInt(array.length) /* <-- benchmarks show that this causes observable contention */ + scala.concurrent.forkjoin.ThreadLocalRandom.current.nextInt(0, array.length) + else 0 while (i < array.length) { val pos = (i + offset) % array.length array(pos) match { diff --git a/test/benchmarking/ParCtrie-size.scala b/test/benchmarking/ParCtrie-size.scala index 5a6191fb62..3f47dc23fd 100644 --- a/test/benchmarking/ParCtrie-size.scala +++ b/test/benchmarking/ParCtrie-size.scala @@ -2,16 +2,18 @@ -import collection.parallel.mutable.ParCtrie +import collection.parallel.mutable.ParTrieMap object Size extends testing.Benchmark { val length = sys.props("length").toInt val par = sys.props("par").toInt - var parctrie = ParCtrie((0 until length) zip (0 until length): _*) + var parctrie = ParTrieMap((0 until length) zip (0 until length): _*) - collection.parallel.ForkJoinTasks.defaultForkJoinPool.setParallelism(par) + //collection.parallel.ForkJoinTasks.defaultForkJoinPool.setParallelism(par) + val ts = new collection.parallel.ForkJoinTaskSupport(new concurrent.forkjoin.ForkJoinPool(par)) + parctrie.tasksupport = ts def run = { parctrie.size @@ -21,7 +23,8 @@ object Size extends testing.Benchmark { override def tearDown() { iteration += 1 - if (iteration % 4 == 0) parctrie = ParCtrie((0 until length) zip (0 until length): _*) + if (iteration % 4 == 0) parctrie = ParTrieMap((0 until length) zip (0 until length): _*) + parctrie.tasksupport = ts } } -- cgit v1.2.3 From bc3b1e2c9453ef90f6fb7aaa3dea6e24ba19d017 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 12:59:33 -0400 Subject: Rename copyTo to build based on consensus of 3 --- src/library/scala/collection/GenTraversableOnce.scala | 4 ++-- src/library/scala/collection/Iterator.scala | 4 ++-- src/library/scala/collection/TraversableLike.scala | 4 ++-- .../scala/collection/parallel/ParIterableLike.scala | 2 +- test/files/run/collection-conversions.scala | 14 +++++++------- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 2996071d71..46be27f1cd 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -565,10 +565,10 @@ trait GenTraversableOnce[+A] extends Any { * @tparam Col The collection type to build. * @return a new collection containing all elements of this $coll. * - * @usecase def copyTo[Col[_]]: Col[A] + * @usecase def build[Col[_]]: Col[A] * @inheritdoc * $willNotTerminateInf * @return a new collection containing all elements of this $coll. */ - def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] + def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 4283120519..6b96c8dba5 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -1141,8 +1141,8 @@ trait Iterator[+A] extends TraversableOnce[A] { if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] - def toVector: Vector[A] = copyTo[Vector] - def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = build[Vector] + def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() while(hasNext) b += next b.result diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 68abc36bf7..e2e9195f4d 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,8 +616,8 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream - def toVector: Vector[A] = copyTo[Vector] - def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + def toVector: Vector[A] = build[Vector] + def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() b.sizeHint(this) b ++= thisCollection diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index 12b777832e..20d0a65a4c 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -853,7 +853,7 @@ self: ParIterableLike[T, Repr, Sequential] => override def toVector: Vector[T] = seq.toVector - override def copyTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.copyTo[Col] + override def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.build[Col] /* tasks */ diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index 390ba06dac..d7fcbb352a 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -34,17 +34,17 @@ object Test { val tmp = col println("-- Testing " + name + " ---") printResult("[Direct] Vector ", col.toVector, testVector) - printResult("[Copy] Vector ", col.copyTo[Vector], testVector) + printResult("[Copy] Vector ", col.build[Vector], testVector) printResult("[Direct] Buffer ", col.toBuffer, testBuffer) - printResult("[Copy] Buffer ", col.copyTo[Buffer], testBuffer) + printResult("[Copy] Buffer ", col.build[Buffer], testBuffer) printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) - printResult("[Copy] GenSeq ", col.copyTo[GenSeq], testGenSeq) - printResult("[Copy] Seq ", col.copyTo[Seq], testSeq) + printResult("[Copy] GenSeq ", col.build[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.build[Seq], testSeq) printResult("[Direct] Stream ", col.toStream, testStream) - printResult("[Copy] Stream ", col.copyTo[Stream], testStream) + printResult("[Copy] Stream ", col.build[Stream], testStream) printResult("[Direct] Array ", col.toArray, testArray) - printResult("[Copy] Array ", col.copyTo[Array], testArray) - printResult("[Copy] ParVector", col.copyTo[ParVector], testParVector) + printResult("[Copy] Array ", col.build[Array], testArray) + printResult("[Copy] ParVector", col.build[ParVector], testParVector) } def main(args: Array[String]): Unit = { -- cgit v1.2.3 From a83586a4815acd35df0801ed0e9f067e113c8664 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Mon, 18 Jun 2012 19:33:06 +0200 Subject: Fix SI-4541. Catch type errors when duplicating trees. In this case, to access a protected member from a specialized class is an error, so we would have to make the member public anyway. Better it is then to report an error and have the user make the field public explicitly. Review by @dragos. --- .../scala/tools/nsc/transform/SpecializeTypes.scala | 19 +++++++++++++++---- .../scala/tools/nsc/typechecker/Duplicators.scala | 2 +- test/files/neg/t4541.check | 7 +++++++ test/files/neg/t4541.scala | 16 ++++++++++++++++ test/files/neg/t4541b.check | 7 +++++++ test/files/neg/t4541b.scala | 16 ++++++++++++++++ 6 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 test/files/neg/t4541.check create mode 100644 test/files/neg/t4541.scala create mode 100644 test/files/neg/t4541b.check create mode 100644 test/files/neg/t4541b.scala diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 4b488a6437..c4c769d7cf 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1448,20 +1448,29 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case ddef @ DefDef(_, _, _, vparamss, _, _) if info.isDefinedAt(symbol) => // log("--> method: " + ddef + " in " + ddef.symbol.owner + ", " + info(symbol)) + def reportTypeError(body: =>Tree) = + try body + catch { + case te: TypeError => + reporter.error(te.pos, te.toString) + ddef + } if (symbol.isConstructor) { val t = atOwner(symbol)(forwardCtorCall(tree.pos, gen.mkSuperSelect, vparamss, symbol.owner)) if (symbol.isPrimaryConstructor) localTyper.typedPos(symbol.pos)(deriveDefDef(tree)(_ => Block(List(t), Literal(Constant())))) - else // duplicate the original constructor - duplicateBody(ddef, info(symbol).target) + else // duplicate the original constructor + reportTypeError(duplicateBody(ddef, info(symbol).target)) } else info(symbol) match { case Implementation(target) => assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName) // we have an rhs, specialize it - val tree1 = duplicateBody(ddef, target) + val tree1 = reportTypeError { + duplicateBody(ddef, target) + } debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) @@ -1472,7 +1481,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else { // we have an rhs, specialize it - val tree1 = duplicateBody(ddef, target) + val tree1 = reportTypeError { + duplicateBody(ddef, target) + } debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index b7a6ea677e..6386273c9d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -360,7 +360,7 @@ abstract class Duplicators extends Analyzer { tree case _ => - debuglog("Duplicators default case: " + tree.summaryString) + // log("Duplicators default case: " + tree.summaryString + " -> " + tree) if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } diff --git a/test/files/neg/t4541.check b/test/files/neg/t4541.check new file mode 100644 index 0000000000..c01226685f --- /dev/null +++ b/test/files/neg/t4541.check @@ -0,0 +1,7 @@ +t4541.scala:11: error: scala.reflect.internal.Types$TypeError: variable data in class Sparse cannot be accessed in Sparse[Int] + Access to protected method data not permitted because + prefix type Sparse[Int] does not conform to + class Sparse$mcI$sp where the access take place + that.data + ^ +one error found \ No newline at end of file diff --git a/test/files/neg/t4541.scala b/test/files/neg/t4541.scala new file mode 100644 index 0000000000..744af1c288 --- /dev/null +++ b/test/files/neg/t4541.scala @@ -0,0 +1,16 @@ + + + + + + +@SerialVersionUID(1L) +final class Sparse[@specialized(Int) T](d: Array[T]) extends Serializable { + protected var data: Array[T] = d + def set(that: Sparse[T]) = { + that.data + } +} + + + diff --git a/test/files/neg/t4541b.check b/test/files/neg/t4541b.check new file mode 100644 index 0000000000..54d9c3d1ee --- /dev/null +++ b/test/files/neg/t4541b.check @@ -0,0 +1,7 @@ +t4541b.scala:13: error: scala.reflect.internal.Types$TypeError: variable data in class SparseArray cannot be accessed in SparseArray[Int] + Access to protected method data not permitted because + prefix type SparseArray[Int] does not conform to + class SparseArray$mcI$sp where the access take place + use(that.data.clone) + ^ +one error found \ No newline at end of file diff --git a/test/files/neg/t4541b.scala b/test/files/neg/t4541b.scala new file mode 100644 index 0000000000..7a21ffc156 --- /dev/null +++ b/test/files/neg/t4541b.scala @@ -0,0 +1,16 @@ + + + + + +@SerialVersionUID(1L) +final class SparseArray[@specialized(Int) T](private var data: Array[T]) extends Serializable { + def use(inData: Array[T]) = { + data = inData; + } + + def set(that: SparseArray[T]) = { + use(that.data.clone) + } +} + -- cgit v1.2.3 From fae3e8925a2125f723517d5f1eaa0a2087507df1 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 15:26:07 -0400 Subject: Migrate build to @odersky's suggestion of convertTo. * Move method into TraversableOnce from Iterator and Traversable to make the build pass. * Udpate IDE tests with new collection methods. * Rewire default toXYZ methods to use convertTo. --- src/library/scala/collection/GenTraversableOnce.scala | 4 ++-- src/library/scala/collection/Iterator.scala | 7 ------- src/library/scala/collection/TraversableLike.scala | 4 ++-- src/library/scala/collection/TraversableOnce.scala | 17 +++++++++++++---- .../scala/collection/parallel/ParIterableLike.scala | 3 ++- test/files/presentation/ide-bug-1000531.check | 4 +++- test/files/run/collection-conversions.scala | 14 +++++++------- 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 46be27f1cd..37f726c8fb 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -565,10 +565,10 @@ trait GenTraversableOnce[+A] extends Any { * @tparam Col The collection type to build. * @return a new collection containing all elements of this $coll. * - * @usecase def build[Col[_]]: Col[A] + * @usecase def convertTo[Col[_]]: Col[A] * @inheritdoc * $willNotTerminateInf * @return a new collection containing all elements of this $coll. */ - def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] + def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 6b96c8dba5..5f369de3b7 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -1140,13 +1140,6 @@ trait Iterator[+A] extends TraversableOnce[A] { def toStream: Stream[A] = if (self.hasNext) Stream.cons(self.next, self.toStream) else Stream.empty[A] - - def toVector: Vector[A] = build[Vector] - def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { - val b = cbf() - while(hasNext) b += next - b.result - } /** Converts this iterator to a string. diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index e2e9195f4d..e5861f5760 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -616,8 +616,8 @@ trait TraversableLike[+A, +Repr] extends Any def toTraversable: Traversable[A] = thisCollection def toIterator: Iterator[A] = toStream.iterator def toStream: Stream[A] = toBuffer.toStream - def toVector: Vector[A] = build[Vector] - def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + // Override to provide size hint. + override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { val b = cbf() b.sizeHint(this) b ++= thisCollection diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala index 386ce2d95a..8dc6184d88 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -9,6 +9,7 @@ package scala.collection import mutable.{ Buffer, Builder, ListBuffer, ArrayBuffer } +import generic.CanBuildFrom import annotation.unchecked.{ uncheckedVariance => uV } import language.{implicitConversions, higherKinds} import reflect.ClassTag @@ -239,17 +240,25 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] { def toTraversable: Traversable[A] - def toList: List[A] = (new ListBuffer[A] ++= seq).toList + def toList: List[A] = convertTo[List] def toIterable: Iterable[A] = toStream def toSeq: Seq[A] = toStream - def toIndexedSeq: immutable.IndexedSeq[A] = immutable.IndexedSeq() ++ seq + def toIndexedSeq: immutable.IndexedSeq[A] = convertTo[immutable.IndexedSeq] - def toBuffer[B >: A]: mutable.Buffer[B] = new ArrayBuffer[B] ++= seq + def toBuffer[B >: A]: mutable.Buffer[B] = convertTo[ArrayBuffer].asInstanceOf[mutable.Buffer[B]] - def toSet[B >: A]: immutable.Set[B] = immutable.Set() ++ seq + def toSet[B >: A]: immutable.Set[B] = convertTo[immutable.Set].asInstanceOf[immutable.Set[B]] + + def toVector: Vector[A] = convertTo[Vector] + + def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]]): Col[A @uV] = { + val b = cbf() + b ++= seq + b.result + } def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = { val b = immutable.Map.newBuilder[T, U] diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index 20d0a65a4c..a7ec833193 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -851,9 +851,10 @@ self: ParIterableLike[T, Repr, Sequential] => override def toMap[K, V](implicit ev: T <:< (K, V)): immutable.ParMap[K, V] = toParMap[K, V, immutable.ParMap[K, V]](() => immutable.ParMap.newCombiner[K, V]) + // TODO(@alex22): make these better override def toVector: Vector[T] = seq.toVector - override def build[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.build[Col] + override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.convertTo[Col] /* tasks */ diff --git a/test/files/presentation/ide-bug-1000531.check b/test/files/presentation/ide-bug-1000531.check index e813ce119b..9d4674d7c7 100644 --- a/test/files/presentation/ide-bug-1000531.check +++ b/test/files/presentation/ide-bug-1000531.check @@ -3,7 +3,7 @@ reload: CrashOnLoad.scala askTypeCompletion at CrashOnLoad.scala(6,12) ================================================================================ [response] aksTypeCompletion at (6,12) -retrieved 124 members +retrieved 126 members [accessible: true] `class GroupedIteratorIterator[B]#GroupedIterator` [accessible: true] `method !=(x$1: Any)Boolean` [accessible: true] `method !=(x$1: AnyRef)Boolean` @@ -25,6 +25,7 @@ retrieved 124 members [accessible: true] `method collectFirst[B](pf: PartialFunction[B,B])Option[B]` [accessible: true] `method collect[B](pf: PartialFunction[B,B])Iterator[B]` [accessible: true] `method contains(elem: Any)Boolean` +[accessible: true] `method convertTo[Col[_]](implicit cbf: scala.collection.generic.CanBuildFrom[Nothing,B,Col[B]])Col[B]` [accessible: true] `method copyToArray[B >: B](xs: Array[B])Unit` [accessible: true] `method copyToArray[B >: B](xs: Array[B], start: Int)Unit` [accessible: true] `method copyToArray[B >: B](xs: Array[B], start: Int, len: Int)Unit` @@ -109,6 +110,7 @@ retrieved 124 members [accessible: true] `method toStream=> scala.collection.immutable.Stream[B]` [accessible: true] `method toString()String` [accessible: true] `method toTraversable=> Traversable[B]` +[accessible: true] `method toVector=> Vector[B]` [accessible: true] `method wait()Unit` [accessible: true] `method wait(x$1: Long)Unit` [accessible: true] `method wait(x$1: Long, x$2: Int)Unit` diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index d7fcbb352a..b5c4d8e261 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -34,17 +34,17 @@ object Test { val tmp = col println("-- Testing " + name + " ---") printResult("[Direct] Vector ", col.toVector, testVector) - printResult("[Copy] Vector ", col.build[Vector], testVector) + printResult("[Copy] Vector ", col.convertTo[Vector], testVector) printResult("[Direct] Buffer ", col.toBuffer, testBuffer) - printResult("[Copy] Buffer ", col.build[Buffer], testBuffer) + printResult("[Copy] Buffer ", col.convertTo[Buffer], testBuffer) printResult("[Direct] GenSeq ", col.toSeq, testGenSeq) - printResult("[Copy] GenSeq ", col.build[GenSeq], testGenSeq) - printResult("[Copy] Seq ", col.build[Seq], testSeq) + printResult("[Copy] GenSeq ", col.convertTo[GenSeq], testGenSeq) + printResult("[Copy] Seq ", col.convertTo[Seq], testSeq) printResult("[Direct] Stream ", col.toStream, testStream) - printResult("[Copy] Stream ", col.build[Stream], testStream) + printResult("[Copy] Stream ", col.convertTo[Stream], testStream) printResult("[Direct] Array ", col.toArray, testArray) - printResult("[Copy] Array ", col.build[Array], testArray) - printResult("[Copy] ParVector", col.build[ParVector], testParVector) + printResult("[Copy] Array ", col.convertTo[Array], testArray) + printResult("[Copy] ParVector", col.convertTo[ParVector], testParVector) } def main(args: Array[String]): Unit = { -- cgit v1.2.3 From 9d5bfd3b81eeef310e14d801e26166f7d7b63045 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 18:07:01 -0400 Subject: Fixing embarassing typo. --- src/library/scala/collection/GenTraversableOnce.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/scala/collection/GenTraversableOnce.scala b/src/library/scala/collection/GenTraversableOnce.scala index 37f726c8fb..e475865391 100644 --- a/src/library/scala/collection/GenTraversableOnce.scala +++ b/src/library/scala/collection/GenTraversableOnce.scala @@ -561,7 +561,7 @@ trait GenTraversableOnce[+A] extends Any { */ def toVector: Vector[A] - /** Converts this $coll into another by copying all elemnents. + /** Converts this $coll into another by copying all elements. * @tparam Col The collection type to build. * @return a new collection containing all elements of this $coll. * -- cgit v1.2.3 From 82032eb2696c44490d8504059dc95790225f79bf Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Mon, 18 Jun 2012 22:26:31 -0400 Subject: Final clean up to get the build/testing working again. --- project/Build.scala | 14 +++++++--- project/Release.scala | 28 ++++++++++++++----- project/ShaResolve.scala | 70 +++++++++++++++++++++++++++++++++--------------- project/Testing.scala | 7 ++--- 4 files changed, 82 insertions(+), 37 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 6a69e00da2..58d322108b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -8,7 +8,7 @@ import Release._ object ScalaBuild extends Build with Layers with Packaging with Testing { // Build wide settings: - override lazy val settings = super.settings ++ Versions.settings ++ RemoteDependencies.buildSettings(Set(scalacheck), fullQuickScalaReference) ++ Seq( + override lazy val settings = super.settings ++ Versions.settings ++ Seq( autoScalaLibrary := false, resolvers += Resolver.url( "Typesafe nightlies", @@ -86,7 +86,6 @@ object ScalaBuild extends Build with Layers with Packaging with Testing { }, // TODO - Make exported products == makeDist so we can use this when creating a *real* distribution. commands += Release.pushStarr - //commands += Release.setStarrHome ) // Note: Root project is determined by lowest-alphabetical project that has baseDirectory as file("."). we use aaa_ to 'win'. lazy val aaa_root = Project("scala", file(".")) settings(projectSettings: _*) settings(ShaResolve.settings: _*) @@ -99,6 +98,11 @@ object ScalaBuild extends Build with Layers with Packaging with Testing { ) ) + def fixArtifactSrc(dir: File, name: String) = name match { + case x if x startsWith "scala-" => dir / "src" / (name drop 6) + case x => dir / "src" / name + } + // These are setting overrides for most artifacts in the Scala build file. def settingOverrides: Seq[Setting[_]] = publishSettings ++ Seq( crossPaths := false, @@ -110,8 +114,8 @@ object ScalaBuild extends Build with Layers with Packaging with Testing { target <<= (baseDirectory, name) apply (_ / "target" / _), (classDirectory in Compile) <<= target(_ / "classes"), javacOptions ++= Seq("-target", "1.5", "-source", "1.5"), - scalaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _), - javaSource in Compile <<= (baseDirectory, name) apply (_ / "src" / _), + scalaSource in Compile <<= (baseDirectory, name) apply fixArtifactSrc, + javaSource in Compile <<= (baseDirectory, name) apply fixArtifactSrc, unmanagedJars in Compile := Seq(), // Most libs in the compiler use this order to build. compileOrder in Compile := CompileOrder.JavaThenScala, @@ -208,6 +212,8 @@ object ScalaBuild extends Build with Layers with Packaging with Testing { // Things that compile against the compiler. lazy val compilerDependentProjectSettings = dependentProjectSettings ++ Seq(quickScalaReflectDependency, quickScalaCompilerDependency, addCheaterDependency("scala-compiler")) + + lazy val scalacheck = Project("scalacheck", file(".")) settings(compilerDependentProjectSettings:_*) dependsOn(actors % "provided") lazy val partestSettings = compilerDependentProjectSettings :+ externalDeps lazy val partest = Project("partest", file(".")) settings(partestSettings:_*) dependsOn(actors,forkjoin,scalap) lazy val scalapSettings = compilerDependentProjectSettings ++ Seq( diff --git a/project/Release.scala b/project/Release.scala index 9d4d4ab09c..feab8bdc8c 100644 --- a/project/Release.scala +++ b/project/Release.scala @@ -5,12 +5,26 @@ object Release { // TODO - Just make the STARR artifacts and dump the sha1 files. + val starrLibs = Seq("scala-library.jar", "scala-reflect.jar", "scala-compiler.jar", "jline.jar") - lazy val pushStarr = Command.command("push-starr") { (state: State) => - // TODO do something - // Revert to previous project state. - state - } - - + val pushStarr = Command.command("new-starr") { (state: State) => + /*val extracted = Project.extract(state) + import extracted._ + // First run tests + val (s1, result) = runTask(test in Test, state) + // If successful, package artifacts + val (s2, distDir) = runTask(makeExplodedDist, s1) + // Then copy new libs in place + val bd = extracted get baseDirectory + for { + jarName <- starrLibs + jar = distDir / "lib" / jarName + if jar.exists + } IO.copyFile(jar, bd / "lib" / jarName) + // Invalidate SHA1 files. + ShaResolve.removeInvalidShaFiles(bd) + // Now run tests *again*? + s2*/ + state + } } diff --git a/project/ShaResolve.scala b/project/ShaResolve.scala index e6824ee464..cea2b2d6cc 100644 --- a/project/ShaResolve.scala +++ b/project/ShaResolve.scala @@ -7,6 +7,7 @@ import scala.collection.{ mutable, immutable } import scala.collection.parallel.CompositeThrowable import java.security.MessageDigest +case class Credentials(user: String, pw: String) /** Helpers to resolve SHA artifacts from typesafe repo. */ object ShaResolve { @@ -19,7 +20,8 @@ object ShaResolve { def settings: Seq[Setting[_]] = Seq( binaryLibCache in ThisBuild := file(System.getProperty("user.home")) / ".sbt" / "cache" / "scala", - pullBinaryLibs in ThisBuild <<= (baseDirectory, binaryLibCache, streams) map resolveLibs + pullBinaryLibs in ThisBuild <<= (baseDirectory, binaryLibCache, streams) map resolveLibs, + pushBinaryLibs in ThisBuild <<= (baseDirectory, streams) map getCredentialsAndPushFiles ) def resolveLibs(dir: File, cacheDir: File, s: TaskStreams): Unit = loggingParallelExceptions(s) { @@ -33,6 +35,34 @@ object ShaResolve { } pullFile(jar, sha + "/" + uri, cacheDir, sha, s) } + /** This method removes all SHA1 files that don't match their corresponding JAR. */ + def removeInvalidShaFiles(dir: File): Unit = { + val files = (dir / "test" / "files" ** "*.desired.sha1") +++ (dir / "lib" ** "*.desired.sha1") + for { + (file, name) <- (files x relativeTo(dir)).par + uri = name.dropRight(13).replace('\\', '/') + jar = dir / uri + if !jar.exists || !isValidSha(file) + } IO.delete(jar) + } + def getCredentials: Credentials = System.out.synchronized { + val user = (SimpleReader.readLine("Please enter your STARR username> ") getOrElse error("No username provided.")) + val password = (SimpleReader.readLine("Please enter your STARR password> ", Some('*')) getOrElse error("No password provided.")) + Credentials(user, password) + } + + def getCredentialsAndPushFiles(dir: File, s: TaskStreams): Unit = + pushFiles(dir, getCredentials, s) + + def pushFiles(dir: File, cred: Credentials, s: TaskStreams): Unit = loggingParallelExceptions(s) { + val files = (dir / "test" / "files" ** "*.jar") +++ (dir / "lib" ** "*.jar") + for { + (jar, name) <- (files x relativeTo(dir)).par + shafile = dir / (name + ".desired.sha1") + if !shafile.exists || !isValidSha(shafile) + } pushFile(jar, name, cred, s) + } + @inline final def loggingParallelExceptions[U](s: TaskStreams)(f: => U): U = try f catch { case t: CompositeThrowable => s.log.error("Error during parallel execution, GET READY FOR STACK TRACES!!") @@ -60,24 +90,17 @@ object ShaResolve { sha } - // TODO - Prettier way of doing this... - private def convertToHex(data: Array[Byte]): String = { + def convertToHex(data: Array[Byte]): String = { + def byteToHex(b: Int) = + if ((0 <= b) && (b <= 9)) ('0' + b).toChar + else ('a' + (b-10)).toChar val buf = new StringBuffer - for (i <- 0 until data.length) { - var halfbyte = (data(i) >>> 4) & 0x0F; - var two_halfs = 0; - while(two_halfs < 2) { - if ((0 <= halfbyte) && (halfbyte <= 9)) - buf.append(('0' + halfbyte).toChar) - else - buf.append(('a' + (halfbyte - 10)).toChar); - halfbyte = data(i) & 0x0F; - two_halfs += 1 - } - } - return buf.toString - } - + for (i <- 0 until data.length) { + buf append byteToHex((data(i) >>> 4) & 0x0F) + buf append byteToHex(data(i) & 0x0F) + } + buf.toString + } // Parses a sha file into a file and a sha. def parseShaFile(file: File): (File, String) = IO.read(file).split("\\s") match { @@ -110,10 +133,15 @@ object ShaResolve { IO.copyFile(cachedFile, file) } - def pushFile(file: File, uri: String, user: String, pw: String): Unit = { - val url = remote_urlbase + "/" + uri - val sender = dispatch.url(url).PUT.as(user,pw) <<< (file, "application/java-archive") + // Pushes a file and writes the new .desired.sha1 for git. + def pushFile(file: File, uri: String, cred: Credentials, s: TaskStreams): Unit = { + val sha = calculateSha(file) + val url = remote_urlbase + "/" + sha + "/" + uri + val sender = dispatch.url(url).PUT.as(cred.user,cred.pw) <<< (file, "application/java-archive") // TODO - output to logger. Http(sender >>> System.out) + val shafile = file.getParentFile / (file.getName + ".desired.sha1") + IO.touch(shafile) + IO.write(shafile, sha + " ?" + file.getName) } } diff --git a/project/Testing.scala b/project/Testing.scala index 61676c81bd..cec1d8c60b 100644 --- a/project/Testing.scala +++ b/project/Testing.scala @@ -7,9 +7,6 @@ import ScalaBuildKeys._ /** All settings/projects relating to testing. */ trait Testing { self: ScalaBuild.type => - /* lazy val scalacheckSettings: Seq[Setting[_]] = Seq(fullQuickScalaReference, crossPaths := false)*/ - lazy val scalacheck = uri("git://github.com/jsuereth/scalacheck.git#scala-build") - lazy val testsuiteSettings: Seq[Setting[_]] = compilerDependentProjectSettings ++ partestTaskSettings ++ VerifyClassLoad.settings ++ Seq( unmanagedBase <<= baseDirectory / "test/files/lib", fullClasspath in VerifyClassLoad.checkClassLoad <<= (fullClasspath in scalaLibrary in Runtime).identity, @@ -32,12 +29,12 @@ trait Testing { self: ScalaBuild.type => val testsuite = ( Project("testsuite", file(".")) settings (testsuiteSettings:_*) - dependsOn (scalaLibrary, scalaCompiler, fjbg, partest, scalacheck) + dependsOn (scalaLibrary, scalaCompiler, fjbg, partest, scalacheck, actorsMigration) ) val continuationsTestsuite = ( Project("continuations-testsuite", file(".")) settings (continuationsTestsuiteSettings:_*) - dependsOn (partest, scalaLibrary, scalaCompiler, fjbg) + dependsOn (partest, scalaLibrary, scalaCompiler, fjbg, actorsMigration) ) } -- cgit v1.2.3 From 3be520bcfc84f207d172934f9b147e31355cd877 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 15 Jun 2012 13:35:54 +0200 Subject: improves showRaw addresses concerns raised in http://groups.google.com/group/scala-user/browse_thread/thread/de5a5be2e083cf8e --- .../scala/reflect/reify/utils/NodePrinters.scala | 39 +- src/compiler/scala/tools/nsc/Global.scala | 2 +- .../scala/tools/nsc/ast/NodePrinters.scala | 2 +- src/compiler/scala/tools/nsc/ast/Printers.scala | 295 ++++++++++ .../scala/tools/nsc/ast/TreePrinters.scala | 295 ---------- .../scala/tools/nsc/matching/MatchSupport.scala | 2 +- src/library/scala/reflect/base/Base.scala | 2 +- src/library/scala/reflect/base/Trees.scala | 4 +- src/reflect/scala/reflect/api/Printers.scala | 94 +++ src/reflect/scala/reflect/api/Symbols.scala | 19 + src/reflect/scala/reflect/api/TreePrinters.scala | 87 --- src/reflect/scala/reflect/api/Universe.scala | 2 +- src/reflect/scala/reflect/internal/Printers.scala | 642 +++++++++++++++++++++ .../scala/reflect/internal/SymbolTable.scala | 2 +- .../scala/reflect/internal/TreePrinters.scala | 478 --------------- src/reflect/scala/reflect/internal/Trees.scala | 2 +- src/reflect/scala/reflect/makro/Universe.scala | 19 - test/files/run/showraw_mods.check | 1 + test/files/run/showraw_mods.scala | 6 + test/files/run/showraw_tree.check | 2 + test/files/run/showraw_tree.scala | 8 + test/files/run/showraw_tree_ids.check | 2 + test/files/run/showraw_tree_ids.scala | 8 + test/files/run/showraw_tree_kinds.check | 2 + test/files/run/showraw_tree_kinds.scala | 8 + test/files/run/showraw_tree_types_ids.check | 10 + test/files/run/showraw_tree_types_ids.scala | 10 + test/files/run/showraw_tree_types_typed.check | 10 + test/files/run/showraw_tree_types_typed.scala | 10 + test/files/run/showraw_tree_types_untyped.check | 2 + test/files/run/showraw_tree_types_untyped.scala | 8 + test/files/run/showraw_tree_ultimate.check | 10 + test/files/run/showraw_tree_ultimate.scala | 10 + 33 files changed, 1168 insertions(+), 925 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/ast/Printers.scala delete mode 100644 src/compiler/scala/tools/nsc/ast/TreePrinters.scala create mode 100644 src/reflect/scala/reflect/api/Printers.scala delete mode 100644 src/reflect/scala/reflect/api/TreePrinters.scala create mode 100644 src/reflect/scala/reflect/internal/Printers.scala delete mode 100644 src/reflect/scala/reflect/internal/TreePrinters.scala create mode 100644 test/files/run/showraw_mods.check create mode 100644 test/files/run/showraw_mods.scala create mode 100644 test/files/run/showraw_tree.check create mode 100644 test/files/run/showraw_tree.scala create mode 100644 test/files/run/showraw_tree_ids.check create mode 100644 test/files/run/showraw_tree_ids.scala create mode 100644 test/files/run/showraw_tree_kinds.check create mode 100644 test/files/run/showraw_tree_kinds.scala create mode 100644 test/files/run/showraw_tree_types_ids.check create mode 100644 test/files/run/showraw_tree_types_ids.scala create mode 100644 test/files/run/showraw_tree_types_typed.check create mode 100644 test/files/run/showraw_tree_types_typed.scala create mode 100644 test/files/run/showraw_tree_types_untyped.check create mode 100644 test/files/run/showraw_tree_types_untyped.scala create mode 100644 test/files/run/showraw_tree_ultimate.check create mode 100644 test/files/run/showraw_tree_ultimate.scala diff --git a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala index ce0ab2196a..7214da597e 100644 --- a/src/compiler/scala/reflect/reify/utils/NodePrinters.scala +++ b/src/compiler/scala/reflect/reify/utils/NodePrinters.scala @@ -15,41 +15,6 @@ trait NodePrinters { import Flag._ object reifiedNodeToString extends (Tree => String) { - // [Eugene++ to Martin] can we do better? - // didn't want to invent anything myself in order not to interfere with your line of thought - def bitsToFlags(bits: String): String = { - val flags = bits.toLong - if (flags == NoFlags) nme.NoFlags.toString - else { - val s_flags = new collection.mutable.ListBuffer[String] - if (flags containsAll TRAIT) s_flags += "TRAIT" - if (flags containsAll MODULE) s_flags += "MODULE" - if (flags containsAll MUTABLE) s_flags += "MUTABLE" - if (flags containsAll PACKAGE) s_flags += "PACKAGE" - if (flags containsAll METHOD) s_flags += "METHOD" - if (flags containsAll DEFERRED) s_flags += "DEFERRED" - if (flags containsAll ABSTRACT) s_flags += "ABSTRACT" - if (flags containsAll FINAL) s_flags += "FINAL" - if (flags containsAll SEALED) s_flags += "SEALED" - if (flags containsAll IMPLICIT) s_flags += "IMPLICIT" - if (flags containsAll LAZY) s_flags += "LAZY" - if (flags containsAll OVERRIDE) s_flags += "OVERRIDE" - if (flags containsAll PRIVATE) s_flags += "PRIVATE" - if (flags containsAll PROTECTED) s_flags += "PROTECTED" - if (flags containsAll CASE) s_flags += "CASE" - if (flags containsAll ABSOVERRIDE) s_flags += "ABSOVERRIDE" - if (flags containsAll BYNAMEPARAM) s_flags += "BYNAMEPARAM" - if (flags containsAll PARAM) s_flags += "PARAM" - if (flags containsAll PARAMACCESSOR) s_flags += "PARAMACCESSOR" - if (flags containsAll CASEACCESSOR) s_flags += "CASEACCESSOR" - if (flags containsAll COVARIANT) s_flags += "COVARIANT" - if (flags containsAll CONTRAVARIANT) s_flags += "CONTRAVARIANT" - if (flags containsAll DEFAULTPARAM) s_flags += "DEFAULTPARAM" - if (flags containsAll INTERFACE) s_flags += "INTERFACE" - s_flags mkString " | " - } - } - def apply(tree: Tree): String = { var mirrorIsUsed = false var flagsAreUsed = false @@ -70,7 +35,7 @@ trait NodePrinters { s = s.replace("immutable.this.Nil", "List()") s = """build\.flagsFromBits\((\d+)[lL]\)""".r.replaceAllIn(s, m => { flagsAreUsed = true - bitsToFlags(m.group(1)) + show(m.group(1).toLong) }) s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()") s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => { @@ -87,7 +52,7 @@ trait NodePrinters { val bits = m.group(1) if (buf.nonEmpty || bits != "0L") { flagsAreUsed = true - buf.append(bitsToFlags(bits)) + buf.append(show(bits.toLong)) } val replacement = "Modifiers(" + buf.reverse.mkString(", ") + ")" diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 787c9c7f57..38a08bbd60 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -39,7 +39,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) with Plugins with PhaseAssembly with Trees - with TreePrinters + with Printers with DocComments with Positions { self => diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index ba1f3b2e3c..4afd3545b9 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -146,7 +146,7 @@ abstract class NodePrinters { } def printModifiers(tree: MemberDef) { // [Eugene++] there's most likely a bug here (?) - // see `TreePrinters.printAnnotations` for more information + // see `Printers.printAnnotations` for more information val annots0 = tree.symbol.annotations match { case Nil => tree.mods.annotations case xs => xs map annotationInfoToString diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala new file mode 100644 index 0000000000..94d0c4f45e --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -0,0 +1,295 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package ast + +import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } +import symtab.Flags._ +import symtab.SymbolTable + +trait Printers extends reflect.internal.Printers { this: Global => + + import treeInfo.{ IsTrue, IsFalse } + + class TreePrinter(out: PrintWriter) extends super.TreePrinter(out) { + + override def print(args: Any*): Unit = args foreach { + case tree: Tree => + printPosition(tree) + printTree( + if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) { + tree match { + case ClassDef(_, _, _, impl @ Template(ps, emptyValDef, body)) + if (tree.symbol.thisSym != tree.symbol) => + ClassDef(tree.symbol, Template(ps, ValDef(tree.symbol.thisSym), body)) + case ClassDef(_, _, _, impl) => ClassDef(tree.symbol, impl) + case ModuleDef(_, _, impl) => ModuleDef(tree.symbol, impl) + case ValDef(_, _, _, rhs) => ValDef(tree.symbol, rhs) + case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs) + case TypeDef(_, _, _, rhs) => TypeDef(tree.symbol, rhs) + case _ => tree + } + } else tree) + case unit: CompilationUnit => + print("// Scala source: " + unit.source + "\n") + if (unit.body == null) print("") + else { print(unit.body); println() } + println() + out.flush() + case arg => + super.print(arg) + } + } + + // overflow cases missing from TreePrinter in reflect.api + override def xprintTree(treePrinter: super.TreePrinter, tree: Tree) = tree match { + case DocDef(comment, definition) => + treePrinter.print(comment.raw) + treePrinter.println() + treePrinter.print(definition) + + case TypeTreeWithDeferredRefCheck() => + treePrinter.print("") + + case SelectFromArray(qualifier, name, _) => + treePrinter.print(qualifier, ".", symName(tree, name)) + + case _ => + super.xprintTree(treePrinter, tree) + } + + /** A tree printer which is stingier about vertical whitespace and unnecessary + * punctuation than the standard one. + */ + class CompactTreePrinter(out: PrintWriter) extends TreePrinter(out) { + override def printRow(ts: List[Tree], start: String, sep: String, end: String) { + print(start) + printSeq(ts)(print(_))(print(sep)) + print(end) + } + + // drill down through Blocks and pull out the real statements. + def allStatements(t: Tree): List[Tree] = t match { + case Block(stmts, expr) => (stmts flatMap allStatements) ::: List(expr) + case _ => List(t) + } + + def printLogicalOr(t1: (Tree, Boolean), t2: (Tree, Boolean)) = + printLogicalOp(t1, t2, "||") + + def printLogicalAnd(t1: (Tree, Boolean), t2: (Tree, Boolean)) = + printLogicalOp(t1, t2, "&&") + + def printLogicalOp(t1: (Tree, Boolean), t2: (Tree, Boolean), op: String) = { + def maybenot(tvalue: Boolean) = if (tvalue) "" else "!" + + print("%s(" format maybenot(t1._2)) + printTree(t1._1) + print(") %s %s(".format(op, maybenot(t2._2))) + printTree(t2._1) + print(")") + } + + override def printTree(tree: Tree): Unit = { + // routing supercalls through this for debugging ease + def s() = super.printTree(tree) + + tree match { + // labels used for jumps - does not map to valid scala code + case LabelDef(name, params, rhs) => + print("labeldef %s(%s) = ".format(name, params mkString ",")) + printTree(rhs) + + case Ident(name) => + print(decodedSymName(tree, name)) + + // target.method(arg) ==> target method arg + case Apply(Select(target, method), List(arg)) => + if (method.decode.toString == "||") + printLogicalOr(target -> true, arg -> true) + else if (method.decode.toString == "&&") + printLogicalAnd(target -> true, arg -> true) + else (target, arg) match { + case (_: Ident, _: Literal | _: Ident) => + printTree(target) + print(" ") + printTree(Ident(method)) + print(" ") + printTree(arg) + case _ => s() + } + + // target.unary_! ==> !target + case Select(qualifier, name) if (name.decode startsWith "unary_") => + print(name.decode drop 6) + printTree(qualifier) + + case Select(qualifier, name) => + printTree(qualifier) + print(".") + print(quotedName(name, true)) + + // target.toString() ==> target.toString + case Apply(fn, Nil) => printTree(fn) + + // if a Block only continues one actual statement, just print it. + case Block(stats, expr) => + allStatements(tree) match { + case List(x) => printTree(x) + case xs => s() + } + + // We get a lot of this stuff + case If( IsTrue(), x, _) => printTree(x) + case If(IsFalse(), _, x) => printTree(x) + + case If(cond, IsTrue(), elsep) => printLogicalOr(cond -> true, elsep -> true) + case If(cond, IsFalse(), elsep) => printLogicalAnd(cond -> false, elsep -> true) + case If(cond, thenp, IsTrue()) => printLogicalOr(cond -> false, thenp -> true) + case If(cond, thenp, IsFalse()) => printLogicalAnd(cond -> true, thenp -> true) + + // If thenp or elsep has only one statement, it doesn't need more than one line. + case If(cond, thenp, elsep) => + def ifIndented(x: Tree) = { + indent ; println() ; printTree(x) ; undent + } + + val List(thenStmts, elseStmts) = List(thenp, elsep) map allStatements + print("if ("); print(cond); print(") ") + + thenStmts match { + case List(x: If) => ifIndented(x) + case List(x) => printTree(x) + case _ => printTree(thenp) + } + + if (elseStmts.nonEmpty) { + print(" else") + indent ; println() + elseStmts match { + case List(x) => printTree(x) + case _ => printTree(elsep) + } + undent ; println() + } + case _ => s() + } + } + } + + /** This must guarantee not to force any evaluation, so we can learn + * a little bit about trees in the midst of compilation without altering + * the natural course of events. + */ + class SafeTreePrinter(out: PrintWriter) extends TreePrinter(out) { + + private def default(t: Tree) = t.getClass.getName.reverse.takeWhile(_ != '.').reverse + private def params(trees: List[Tree]): String = trees map safe mkString ", " + + private def safe(name: Name): String = name.decode + private def safe(tree: Tree): String = tree match { + case Apply(fn, args) => "%s(%s)".format(safe(fn), params(args)) + case Select(qual, name) => safe(qual) + "." + safe(name) + case This(qual) => safe(qual) + ".this" + case Ident(name) => safe(name) + case Literal(value) => value.stringValue + case _ => "(?: %s)".format(default(tree)) + } + + override def printTree(tree: Tree) { print(safe(tree)) } + } + + class TreeMatchTemplate { + // non-trees defined in Trees + // + // case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) + // case class Modifiers(flags: Long, privateWithin: Name, annotations: List[Tree], positions: Map[Long, Position]) + // + def apply(t: Tree): Unit = t match { + // eliminated by typer + case Annotated(annot, arg) => + case AssignOrNamedArg(lhs, rhs) => + case DocDef(comment, definition) => + case Import(expr, selectors) => + + // eliminated by refchecks + case ModuleDef(mods, name, impl) => + case TypeTreeWithDeferredRefCheck() => + + // eliminated by erasure + case TypeDef(mods, name, tparams, rhs) => + case Typed(expr, tpt) => + + // eliminated by cleanup + case ApplyDynamic(qual, args) => + + // eliminated by explicitouter + case Alternative(trees) => + case Bind(name, body) => + case CaseDef(pat, guard, body) => + case Star(elem) => + case UnApply(fun, args) => + + // eliminated by lambdalift + case Function(vparams, body) => + + // eliminated by uncurry + case AppliedTypeTree(tpt, args) => + case CompoundTypeTree(templ) => + case ExistentialTypeTree(tpt, whereClauses) => + case SelectFromTypeTree(qual, selector) => + case SingletonTypeTree(ref) => + case TypeBoundsTree(lo, hi) => + + // survivors + case Apply(fun, args) => + case ArrayValue(elemtpt, trees) => + case Assign(lhs, rhs) => + case Block(stats, expr) => + case ClassDef(mods, name, tparams, impl) => + case DefDef(mods, name, tparams, vparamss, tpt, rhs) => + case EmptyTree => + case Ident(name) => + case If(cond, thenp, elsep) => + case LabelDef(name, params, rhs) => + case Literal(value) => + case Match(selector, cases) => + case New(tpt) => + case PackageDef(pid, stats) => + case Return(expr) => + case Select(qualifier, selector) => + case Super(qual, mix) => + case Template(parents, self, body) => + case This(qual) => + case Throw(expr) => + case Try(block, catches, finalizer) => + case TypeApply(fun, args) => + case TypeTree() => + case ValDef(mods, name, tpt, rhs) => + + // missing from the Trees comment + case Parens(args) => // only used during parsing + case SelectFromArray(qual, name, erasure) => // only used during erasure + } + } + + def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) + def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) + + def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) + def newStandardTreePrinter(stream: OutputStream): TreePrinter = newStandardTreePrinter(new PrintWriter(stream)) + def newStandardTreePrinter(): TreePrinter = newStandardTreePrinter(new PrintWriter(ConsoleWriter)) + + def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) + def newCompactTreePrinter(stream: OutputStream): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(stream)) + def newCompactTreePrinter(): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(ConsoleWriter)) + + override def newTreePrinter(writer: PrintWriter): TreePrinter = + if (settings.Ycompacttrees.value) newCompactTreePrinter(writer) + else newStandardTreePrinter(writer) + override def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) + override def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) +} diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala deleted file mode 100644 index 3371353f25..0000000000 --- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala +++ /dev/null @@ -1,295 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package ast - -import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } -import symtab.Flags._ -import symtab.SymbolTable - -trait TreePrinters extends reflect.internal.TreePrinters { this: Global => - - import treeInfo.{ IsTrue, IsFalse } - - class TreePrinter(out: PrintWriter) extends super.TreePrinter(out) { - - override def print(args: Any*): Unit = args foreach { - case tree: Tree => - printPosition(tree) - printTree( - if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) { - tree match { - case ClassDef(_, _, _, impl @ Template(ps, emptyValDef, body)) - if (tree.symbol.thisSym != tree.symbol) => - ClassDef(tree.symbol, Template(ps, ValDef(tree.symbol.thisSym), body)) - case ClassDef(_, _, _, impl) => ClassDef(tree.symbol, impl) - case ModuleDef(_, _, impl) => ModuleDef(tree.symbol, impl) - case ValDef(_, _, _, rhs) => ValDef(tree.symbol, rhs) - case DefDef(_, _, _, vparamss, _, rhs) => DefDef(tree.symbol, vparamss, rhs) - case TypeDef(_, _, _, rhs) => TypeDef(tree.symbol, rhs) - case _ => tree - } - } else tree) - case unit: CompilationUnit => - print("// Scala source: " + unit.source + "\n") - if (unit.body == null) print("") - else { print(unit.body); println() } - println() - out.flush() - case arg => - super.print(arg) - } - } - - // overflow cases missing from TreePrinter in reflect.api - override def xprintTree(treePrinter: super.TreePrinter, tree: Tree) = tree match { - case DocDef(comment, definition) => - treePrinter.print(comment.raw) - treePrinter.println() - treePrinter.print(definition) - - case TypeTreeWithDeferredRefCheck() => - treePrinter.print("") - - case SelectFromArray(qualifier, name, _) => - treePrinter.print(qualifier, ".", symName(tree, name)) - - case _ => - super.xprintTree(treePrinter, tree) - } - - /** A tree printer which is stingier about vertical whitespace and unnecessary - * punctuation than the standard one. - */ - class CompactTreePrinter(out: PrintWriter) extends TreePrinter(out) { - override def printRow(ts: List[Tree], start: String, sep: String, end: String) { - print(start) - printSeq(ts)(print(_))(print(sep)) - print(end) - } - - // drill down through Blocks and pull out the real statements. - def allStatements(t: Tree): List[Tree] = t match { - case Block(stmts, expr) => (stmts flatMap allStatements) ::: List(expr) - case _ => List(t) - } - - def printLogicalOr(t1: (Tree, Boolean), t2: (Tree, Boolean)) = - printLogicalOp(t1, t2, "||") - - def printLogicalAnd(t1: (Tree, Boolean), t2: (Tree, Boolean)) = - printLogicalOp(t1, t2, "&&") - - def printLogicalOp(t1: (Tree, Boolean), t2: (Tree, Boolean), op: String) = { - def maybenot(tvalue: Boolean) = if (tvalue) "" else "!" - - print("%s(" format maybenot(t1._2)) - printTree(t1._1) - print(") %s %s(".format(op, maybenot(t2._2))) - printTree(t2._1) - print(")") - } - - override def printTree(tree: Tree): Unit = { - // routing supercalls through this for debugging ease - def s() = super.printTree(tree) - - tree match { - // labels used for jumps - does not map to valid scala code - case LabelDef(name, params, rhs) => - print("labeldef %s(%s) = ".format(name, params mkString ",")) - printTree(rhs) - - case Ident(name) => - print(decodedSymName(tree, name)) - - // target.method(arg) ==> target method arg - case Apply(Select(target, method), List(arg)) => - if (method.decode.toString == "||") - printLogicalOr(target -> true, arg -> true) - else if (method.decode.toString == "&&") - printLogicalAnd(target -> true, arg -> true) - else (target, arg) match { - case (_: Ident, _: Literal | _: Ident) => - printTree(target) - print(" ") - printTree(Ident(method)) - print(" ") - printTree(arg) - case _ => s() - } - - // target.unary_! ==> !target - case Select(qualifier, name) if (name.decode startsWith "unary_") => - print(name.decode drop 6) - printTree(qualifier) - - case Select(qualifier, name) => - printTree(qualifier) - print(".") - print(quotedName(name, true)) - - // target.toString() ==> target.toString - case Apply(fn, Nil) => printTree(fn) - - // if a Block only continues one actual statement, just print it. - case Block(stats, expr) => - allStatements(tree) match { - case List(x) => printTree(x) - case xs => s() - } - - // We get a lot of this stuff - case If( IsTrue(), x, _) => printTree(x) - case If(IsFalse(), _, x) => printTree(x) - - case If(cond, IsTrue(), elsep) => printLogicalOr(cond -> true, elsep -> true) - case If(cond, IsFalse(), elsep) => printLogicalAnd(cond -> false, elsep -> true) - case If(cond, thenp, IsTrue()) => printLogicalOr(cond -> false, thenp -> true) - case If(cond, thenp, IsFalse()) => printLogicalAnd(cond -> true, thenp -> true) - - // If thenp or elsep has only one statement, it doesn't need more than one line. - case If(cond, thenp, elsep) => - def ifIndented(x: Tree) = { - indent ; println() ; printTree(x) ; undent - } - - val List(thenStmts, elseStmts) = List(thenp, elsep) map allStatements - print("if ("); print(cond); print(") ") - - thenStmts match { - case List(x: If) => ifIndented(x) - case List(x) => printTree(x) - case _ => printTree(thenp) - } - - if (elseStmts.nonEmpty) { - print(" else") - indent ; println() - elseStmts match { - case List(x) => printTree(x) - case _ => printTree(elsep) - } - undent ; println() - } - case _ => s() - } - } - } - - /** This must guarantee not to force any evaluation, so we can learn - * a little bit about trees in the midst of compilation without altering - * the natural course of events. - */ - class SafeTreePrinter(out: PrintWriter) extends TreePrinter(out) { - - private def default(t: Tree) = t.getClass.getName.reverse.takeWhile(_ != '.').reverse - private def params(trees: List[Tree]): String = trees map safe mkString ", " - - private def safe(name: Name): String = name.decode - private def safe(tree: Tree): String = tree match { - case Apply(fn, args) => "%s(%s)".format(safe(fn), params(args)) - case Select(qual, name) => safe(qual) + "." + safe(name) - case This(qual) => safe(qual) + ".this" - case Ident(name) => safe(name) - case Literal(value) => value.stringValue - case _ => "(?: %s)".format(default(tree)) - } - - override def printTree(tree: Tree) { print(safe(tree)) } - } - - class TreeMatchTemplate { - // non-trees defined in Trees - // - // case class ImportSelector(name: Name, namePos: Int, rename: Name, renamePos: Int) - // case class Modifiers(flags: Long, privateWithin: Name, annotations: List[Tree], positions: Map[Long, Position]) - // - def apply(t: Tree): Unit = t match { - // eliminated by typer - case Annotated(annot, arg) => - case AssignOrNamedArg(lhs, rhs) => - case DocDef(comment, definition) => - case Import(expr, selectors) => - - // eliminated by refchecks - case ModuleDef(mods, name, impl) => - case TypeTreeWithDeferredRefCheck() => - - // eliminated by erasure - case TypeDef(mods, name, tparams, rhs) => - case Typed(expr, tpt) => - - // eliminated by cleanup - case ApplyDynamic(qual, args) => - - // eliminated by explicitouter - case Alternative(trees) => - case Bind(name, body) => - case CaseDef(pat, guard, body) => - case Star(elem) => - case UnApply(fun, args) => - - // eliminated by lambdalift - case Function(vparams, body) => - - // eliminated by uncurry - case AppliedTypeTree(tpt, args) => - case CompoundTypeTree(templ) => - case ExistentialTypeTree(tpt, whereClauses) => - case SelectFromTypeTree(qual, selector) => - case SingletonTypeTree(ref) => - case TypeBoundsTree(lo, hi) => - - // survivors - case Apply(fun, args) => - case ArrayValue(elemtpt, trees) => - case Assign(lhs, rhs) => - case Block(stats, expr) => - case ClassDef(mods, name, tparams, impl) => - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - case EmptyTree => - case Ident(name) => - case If(cond, thenp, elsep) => - case LabelDef(name, params, rhs) => - case Literal(value) => - case Match(selector, cases) => - case New(tpt) => - case PackageDef(pid, stats) => - case Return(expr) => - case Select(qualifier, selector) => - case Super(qual, mix) => - case Template(parents, self, body) => - case This(qual) => - case Throw(expr) => - case Try(block, catches, finalizer) => - case TypeApply(fun, args) => - case TypeTree() => - case ValDef(mods, name, tpt, rhs) => - - // missing from the Trees comment - case Parens(args) => // only used during parsing - case SelectFromArray(qual, name, erasure) => // only used during erasure - } - } - - def asString(t: Tree): String = show(t, newStandardTreePrinter) - def asCompactString(t: Tree): String = show(t, newCompactTreePrinter) - - def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) - def newStandardTreePrinter(stream: OutputStream): TreePrinter = newStandardTreePrinter(new PrintWriter(stream)) - def newStandardTreePrinter(): TreePrinter = newStandardTreePrinter(new PrintWriter(ConsoleWriter)) - - def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) - def newCompactTreePrinter(stream: OutputStream): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(stream)) - def newCompactTreePrinter(): CompactTreePrinter = newCompactTreePrinter(new PrintWriter(ConsoleWriter)) - - override def newTreePrinter(writer: PrintWriter): TreePrinter = - if (settings.Ycompacttrees.value) newCompactTreePrinter(writer) - else newStandardTreePrinter(writer) - override def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) - override def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) -} diff --git a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala index 72e6f32af1..16761144d7 100644 --- a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala +++ b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala @@ -7,7 +7,7 @@ package scala.tools.nsc package matching import transform.ExplicitOuter -import ast.{ TreePrinters, Trees } +import ast.{ Printers, Trees } import java.io.{ StringWriter, PrintWriter } import annotation.elidable import language.postfixOps diff --git a/src/library/scala/reflect/base/Base.scala b/src/library/scala/reflect/base/Base.scala index 461eaa2e9e..490a9e8c03 100644 --- a/src/library/scala/reflect/base/Base.scala +++ b/src/library/scala/reflect/base/Base.scala @@ -451,7 +451,7 @@ class Base extends Universe { self => } } - def show(tree: Tree) = s"" + def treeToString(tree: Tree) = s"" trait TermTree extends Tree diff --git a/src/library/scala/reflect/base/Trees.scala b/src/library/scala/reflect/base/Trees.scala index 298d229570..2814450ae3 100644 --- a/src/library/scala/reflect/base/Trees.scala +++ b/src/library/scala/reflect/base/Trees.scala @@ -28,11 +28,11 @@ trait Trees { self: Universe => def isType: Boolean /** Obtains string representation of a tree */ - override def toString: String = show(this) + override def toString: String = treeToString(this) } /** Obtains string representation of a tree */ - def show(tree: Tree): String + protected def treeToString(tree: Tree): String /** Tree is the basis for scala's abstract syntax. The nodes are * implemented as case classes, and the parameters which initialize diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala new file mode 100644 index 0000000000..7f4ff8a7fb --- /dev/null +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -0,0 +1,94 @@ +package scala.reflect +package api + +import java.io.{ PrintWriter, StringWriter } + +trait Printers { self: Universe => + + trait TreePrinter { + def print(args: Any*) + protected var printTypes = false + protected var printIds = false + protected var printKinds = false + def withTypes: this.type = { printTypes = true; this } + def withoutTypes: this.type = { printTypes = false; this } + def withIds: this.type = { printIds = true; this } + def withoutIds: this.type = { printIds = false; this } + def withKinds: this.type = { printKinds = true; this } + def withoutKinds: this.type = { printKinds = false; this } + } + + case class BooleanFlag(val value: Option[Boolean]) + object BooleanFlag { + import language.implicitConversions + implicit def booleanToBooleanFlag(value: Boolean): BooleanFlag = BooleanFlag(Some(value)) + implicit def optionToBooleanFlag(value: Option[Boolean]): BooleanFlag = BooleanFlag(value) + } + + protected def render(what: Any, mkPrinter: PrintWriter => TreePrinter, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None): String = { + val buffer = new StringWriter() + val writer = new PrintWriter(buffer) + var printer = mkPrinter(writer) + printTypes.value.map(printTypes => if (printTypes) printer.withTypes else printer.withoutTypes) + printIds.value.map(printIds => if (printIds) printer.withIds else printer.withoutIds) + printKinds.value.map(printKinds => if (printKinds) printer.withKinds else printer.withoutKinds) + printer.print(what) + writer.flush() + buffer.toString + } + + /** By default trees are printed with `show` */ + override protected def treeToString(tree: Tree) = show(tree) + + /** Renders a prettified representation of a tree. + * Typically it looks very close to the Scala code it represents. + * This function is used in Tree.toString. + */ + def show(tree: Tree, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None): String = + render(tree, newTreePrinter(_), printTypes, printIds, printKinds) + + /** Hook to define what `show(tree)` means. + */ + def newTreePrinter(out: PrintWriter): TreePrinter + + /** Renders internal structure of a tree. + */ + def showRaw(tree: Tree, printTypes: BooleanFlag = None, printIds: BooleanFlag = None, printKinds: BooleanFlag = None): String = + render(tree, newRawTreePrinter(_), printTypes, printIds, printKinds) + + /** Hook to define what `showRaw(tree)` means. + */ + def newRawTreePrinter(out: PrintWriter): TreePrinter + + /** Renders a prettified representation of a symbol. + */ + def show(sym: Symbol): String = sym.toString + + /** Renders internal structure of a symbol. + */ + def showRaw(sym: Symbol): String = render(sym, newRawTreePrinter(_)) + + /** Renders a prettified representation of a type. + */ + def show(tpe: Type): String = tpe.toString + + /** Renders internal structure of a type. + */ + def showRaw(tpe: Type): String = render(tpe, newRawTreePrinter(_)) + + /** Renders a prettified representation of a name. + */ + def show(name: Name): String + + /** Renders internal structure of a name. + */ + def showRaw(name: Name): String = name.toString + + /** Renders a prettified representation of a flag set. + */ + def show(flags: FlagSet): String + + /** Renders internal structure of a flag set. + */ + def showRaw(flags: FlagSet): String = flags.toString +} diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 1d266dc778..130f9e612a 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -177,6 +177,25 @@ trait Symbols extends base.Symbols { self: Universe => */ def isErroneous : Boolean + /** Can this symbol be loaded by a reflective mirror? + * + * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. + * Such annotations (also called "pickles") are applied on top-level classes and include information + * about all symbols reachable from the annotee. However, local symbols (e.g. classes or definitions local to a block) + * are typically unreachable and information about them gets lost. + * + * This method is useful for macro writers who wish to save certain ASTs to be used at runtime. + * With `isLocatable' it's possible to check whether a tree can be retained as is, or it needs special treatment. + */ + def isLocatable: Boolean + + /** Is this symbol static (i.e. with no outer instance)? + * Q: When exactly is a sym marked as STATIC? + * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. + * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 + */ + def isStatic: Boolean + /** The type signature of this symbol seen as a member of given type `site`. */ def typeSignatureIn(site: Type): Type diff --git a/src/reflect/scala/reflect/api/TreePrinters.scala b/src/reflect/scala/reflect/api/TreePrinters.scala deleted file mode 100644 index 08a08e7b90..0000000000 --- a/src/reflect/scala/reflect/api/TreePrinters.scala +++ /dev/null @@ -1,87 +0,0 @@ -package scala.reflect -package api - -import java.io.{ PrintWriter, StringWriter } - -trait TreePrinters { self: Universe => - - trait TreePrinter { - def print(args: Any*) - protected var typesPrinted = false - protected var uniqueIds = false - def withTypesPrinted: this.type = { typesPrinted = true; this } - def withUniqueIds: this.type = { uniqueIds = true; this } - } - - def show(tree: Tree): String = show(tree, newTreePrinter) - - def show(tree: Tree, mkPrinter: PrintWriter => TreePrinter): String = { - val buffer = new StringWriter() - val writer = new PrintWriter(buffer) - val printer = mkPrinter(writer) - printer.print(tree) - writer.flush() - buffer.toString - } - - def showRaw(tree: Tree): String = show(tree, new RawTreePrinter(_)) - - /** Hook to define what `show(tree)` means. - */ - def newTreePrinter(out: PrintWriter): TreePrinter - - // emits more or less verbatim representation of the provided tree - // [Eugene] todo. needs to be refined - // http://groups.google.com/group/scala-user/browse_thread/thread/de5a5be2e083cf8e - class RawTreePrinter(out: PrintWriter) extends TreePrinter { - def print(args: Any*): Unit = args foreach { - case EmptyTree => - print("EmptyTree") - case tree @ TypeTree() => - print("TypeTree()") - if (tree.tpe != null) - print(".setType(", tree.tpe, ")") - else if (tree.original != null) - print(".setOriginal(", tree.original, ")") - case Literal(Constant(s: String)) => - print("Literal(Constant(\"" + s + "\"))") - case tree: Tree => - print(tree.productPrefix+"(") - val it = tree.productIterator - while (it.hasNext) { - it.next() match { - case name: Name if uniqueIds && tree.hasSymbol && tree.symbol != NoSymbol => - print(tree.symbol.name, "#", tree.symbol.id) - case arg => - print(arg) - } - print(if (it.hasNext) ", " else "") - } - print(")") - if (typesPrinted) - print(".setType(", tree.tpe, ")") - case list: List[_] => - print("List(") - val it = list.iterator - while (it.hasNext) { - print(it.next()) - print(if (it.hasNext) ", " else "") - } - print(")") - case mods: Modifiers => - val parts = collection.mutable.ListBuffer[String]() - parts += mods.flagString - if (mods.privateWithin.toString.nonEmpty) - parts += "newTypeName(\"" + mods.privateWithin.toString + "\")" - if (mods.annotations.nonEmpty) - parts += mods.annotations map showRaw mkString ("List(", ", ", ")") - print(parts mkString ("Modifiers(", ", ", ")")) - case name: Name => - if (name.isTermName) print("newTermName(\"") else print("newTypeName(\"") - print(name.toString) - print("\")") - case arg => - out.print(arg) - } - } -} diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala index 002cd2e673..85d8adc44f 100644 --- a/src/reflect/scala/reflect/api/Universe.scala +++ b/src/reflect/scala/reflect/api/Universe.scala @@ -9,7 +9,7 @@ abstract class Universe extends base.Universe with FlagSets with Names with Trees - with TreePrinters + with Printers with Constants with Positions with Mirrors diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala new file mode 100644 index 0000000000..82a8c42f7c --- /dev/null +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -0,0 +1,642 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +// [Eugene++ to Martin] we need to unify this prettyprinter with NodePrinters + +package scala.reflect +package internal + +import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } +import Flags._ + +trait Printers extends api.Printers { self: SymbolTable => + + //nsc import treeInfo.{ IsTrue, IsFalse } + + final val showOuterTests = false + + /** Adds backticks if the name is a scala keyword. */ + def quotedName(name: Name, decode: Boolean): String = { + val s = if (decode) name.decode else name.toString + val term = name.toTermName + if (nme.keywords(term) && term != nme.USCOREkw) "`%s`" format s + else s + } + def quotedName(name: Name): String = quotedName(name, false) + def quotedName(name: String): String = quotedName(newTermName(name), false) + + private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { + val sym = tree.symbol + if (sym.name.toString == nme.ERROR.toString) { + "<" + quotedName(name, decoded) + ": error>" + } else if (sym != null && sym != NoSymbol) { + val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" + var suffix = "" + if (settings.uniqid.value) suffix += ("#" + sym.id) + if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) + prefix + quotedName(tree.symbol.decodedName) + suffix + } else { + quotedName(name, decoded) + } + } + + def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) + def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) + + /** Turns a path into a String, introducing backquotes + * as necessary. + */ + def backquotedPath(t: Tree): String = { + t match { + case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) + case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) + case Ident(name) => symName(t, name) + case _ => t.toString + } + } + + class TreePrinter(out: PrintWriter) extends super.TreePrinter { + protected var indentMargin = 0 + protected val indentStep = 2 + protected var indentString = " " // 40 + + printTypes = settings.printtypes.value + printIds = settings.uniqid.value + printKinds = settings.Yshowsymkinds.value + protected def doPrintPositions = settings.Xprintpos.value + + def indent() = indentMargin += indentStep + def undent() = indentMargin -= indentStep + + def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) + + def println() { + out.println() + while (indentMargin > indentString.length()) + indentString += indentString + if (indentMargin > 0) + out.write(indentString, 0, indentMargin) + } + + def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit) { + ls match { + case List() => + case List(x) => printelem(x) + case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) + } + } + + def printColumn(ts: List[Tree], start: String, sep: String, end: String) { + print(start); indent; println() + printSeq(ts){print(_)}{print(sep); println()}; undent; println(); print(end) + } + + def printRow(ts: List[Tree], start: String, sep: String, end: String) { + print(start); printSeq(ts){print(_)}{print(sep)}; print(end) + } + + def printRow(ts: List[Tree], sep: String) { printRow(ts, "", sep, "") } + + def printTypeParams(ts: List[TypeDef]) { + if (!ts.isEmpty) { + print("["); printSeq(ts){ t => + printAnnotations(t) + printParam(t) + }{print(", ")}; print("]") + } + } + + def printLabelParams(ps: List[Ident]) { + print("(") + printSeq(ps){printLabelParam}{print(", ")} + print(")") + } + + def printLabelParam(p: Ident) { + print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe) + } + + def printValueParams(ts: List[ValDef]) { + print("(") + if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "") + printSeq(ts){printParam}{print(", ")} + print(")") + } + + def printParam(tree: Tree) { + tree match { + case ValDef(mods, name, tp, rhs) => + printPosition(tree) + printAnnotations(tree) + print(symName(tree, name)); printOpt(": ", tp); printOpt(" = ", rhs) + case TypeDef(mods, name, tparams, rhs) => + printPosition(tree) + print(symName(tree, name)) + printTypeParams(tparams); print(rhs) + } + } + + def printBlock(tree: Tree) { + tree match { + case Block(_, _) => + print(tree) + case _ => + printColumn(List(tree), "{", ";", "}") + } + } + + private def symFn[T](tree: Tree, f: Symbol => T, orElse: => T): T = tree.symbol match { + case null | NoSymbol => orElse + case sym => f(sym) + } + private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) + + def printOpt(prefix: String, tree: Tree) { + if (!tree.isEmpty) { print(prefix, tree) } + } + + def printModifiers(tree: Tree, mods: Modifiers): Unit = printFlags( + if (tree.symbol == NoSymbol) mods.flags else tree.symbol.flags, "" + ( + if (tree.symbol == NoSymbol) mods.privateWithin + else if (tree.symbol.hasAccessBoundary) tree.symbol.privateWithin.name + else "" + ) + ) + + def printFlags(flags: Long, privateWithin: String) { + var mask: Long = if (settings.debug.value) -1L else PrintableFlags + val s = flagsToString(flags & mask, privateWithin) + if (s != "") print(s + " ") + } + + def printAnnotations(tree: Tree) { + if (!isCompilerUniverse && tree.symbol != null && tree.symbol != NoSymbol) + // [Eugene++] todo. this is not 100% correct, but is necessary for sane printing + // the problem is that getting annotations doesn't automatically initialize the symbol + // so we might easily print something as if it doesn't have annotations, whereas it does + tree.symbol.initialize + + val annots = tree.symbol.annotations match { + case Nil => tree.asInstanceOf[MemberDef].mods.annotations + case anns => anns + } + annots foreach (annot => print("@"+annot+" ")) + } + + private var currentOwner: Symbol = NoSymbol + private var selectorType: Type = NoType + + def printTree(tree: Tree) { + tree match { + case EmptyTree => + print("") + + case ClassDef(mods, name, tparams, impl) => + printAnnotations(tree) + printModifiers(tree, mods) + val word = + if (mods.isTrait) "trait" + else if (ifSym(tree, _.isModuleClass)) "object" + else "class" + + print(word, " ", symName(tree, name)) + printTypeParams(tparams) + print(if (mods.isDeferred) " <: " else " extends ", impl) + + case PackageDef(packaged, stats) => + printAnnotations(tree) + print("package ", packaged); printColumn(stats, " {", ";", "}") + + case ModuleDef(mods, name, impl) => + printAnnotations(tree) + printModifiers(tree, mods); + print("object " + symName(tree, name), " extends ", impl) + + case ValDef(mods, name, tp, rhs) => + printAnnotations(tree) + printModifiers(tree, mods) + print(if (mods.isMutable) "var " else "val ", symName(tree, name)) + printOpt(": ", tp) + if (!mods.isDeferred) + print(" = ", if (rhs.isEmpty) "_" else rhs) + + case DefDef(mods, name, tparams, vparamss, tp, rhs) => + printAnnotations(tree) + printModifiers(tree, mods) + print("def " + symName(tree, name)) + printTypeParams(tparams); vparamss foreach printValueParams + printOpt(": ", tp); printOpt(" = ", rhs) + + case TypeDef(mods, name, tparams, rhs) => + if (mods hasFlag (PARAM | DEFERRED)) { + printAnnotations(tree) + printModifiers(tree, mods); print("type "); printParam(tree) + } else { + printAnnotations(tree) + printModifiers(tree, mods); print("type " + symName(tree, name)) + printTypeParams(tparams); printOpt(" = ", rhs) + } + + case LabelDef(name, params, rhs) => + print(symName(tree, name)); printLabelParams(params); printBlock(rhs) + + case Import(expr, selectors) => + // Is this selector remapping a name (i.e, {name1 => name2}) + def isNotRemap(s: ImportSelector) : Boolean = (s.name == nme.WILDCARD || s.name == s.rename) + def selectorToString(s: ImportSelector): String = { + val from = quotedName(s.name) + if (isNotRemap(s)) from + else from + "=>" + quotedName(s.rename) + } + print("import ", backquotedPath(expr), ".") + selectors match { + case List(s) => + // If there is just one selector and it is not remapping a name, no braces are needed + if (isNotRemap(s)) print(selectorToString(s)) + else print("{", selectorToString(s), "}") + // If there is more than one selector braces are always needed + case many => + print(many.map(selectorToString).mkString("{", ", ", "}")) + } + + case Template(parents, self, body) => + val currentOwner1 = currentOwner + if (tree.symbol != NoSymbol) currentOwner = tree.symbol.owner +// if (parents exists isReferenceToAnyVal) { +// print("AnyVal") +// } +// else { + printRow(parents, " with ") + if (!body.isEmpty) { + if (self.name != nme.WILDCARD) { + print(" { ", self.name); printOpt(": ", self.tpt); print(" => ") + } else if (!self.tpt.isEmpty) { + print(" { _ : ", self.tpt, " => ") + } else { + print(" {") + } + printColumn(body, "", ";", "}") + } +// } + currentOwner = currentOwner1 + + case Block(stats, expr) => + printColumn(stats ::: List(expr), "{", ";", "}") + + case Match(selector, cases) => + val selectorType1 = selectorType + selectorType = selector.tpe + print(selector); printColumn(cases, " match {", "", "}") + selectorType = selectorType1 + + case CaseDef(pat, guard, body) => + print("case ") + def patConstr(pat: Tree): Tree = pat match { + case Apply(fn, args) => patConstr(fn) + case _ => pat + } + if (showOuterTests && + needsOuterTest( + patConstr(pat).tpe.finalResultType, selectorType, currentOwner)) + print("???") + print(pat); printOpt(" if ", guard) + print(" => ", body) + + case Alternative(trees) => + printRow(trees, "(", "| ", ")") + + case Star(elem) => + print("(", elem, ")*") + + case Bind(name, t) => + print("(", symName(tree, name), " @ ", t, ")") + + case UnApply(fun, args) => + print(fun, " "); printRow(args, "(", ", ", ")") + + case ArrayValue(elemtpt, trees) => + print("Array[", elemtpt); printRow(trees, "]{", ", ", "}") + + case Function(vparams, body) => + print("("); printValueParams(vparams); print(" => ", body, ")") + if (printIds && tree.symbol != null) print("#"+tree.symbol.id) + + case Assign(lhs, rhs) => + print(lhs, " = ", rhs) + + case AssignOrNamedArg(lhs, rhs) => + print(lhs, " = ", rhs) + + case If(cond, thenp, elsep) => + print("if (", cond, ")"); indent; println() + print(thenp); undent + if (!elsep.isEmpty) { + println(); print("else"); indent; println(); print(elsep); undent + } + + case Return(expr) => + print("return ", expr) + + case Try(block, catches, finalizer) => + print("try "); printBlock(block) + if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") + printOpt(" finally ", finalizer) + + case Throw(expr) => + print("throw ", expr) + + case New(tpe) => + print("new ", tpe) + + case Typed(expr, tp) => + print("(", expr, ": ", tp, ")") + + case TypeApply(fun, targs) => + print(fun); printRow(targs, "[", ", ", "]") + + case Apply(fun, vargs) => + print(fun); printRow(vargs, "(", ", ", ")") + + case ApplyDynamic(qual, vargs) => + print("(", qual, "#", tree.symbol.nameString) + printRow(vargs, ", (", ", ", "))") + + case Super(This(qual), mix) => + if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + ".") + print("super") + if (!mix.isEmpty) + print("[" + mix + "]") + + case Super(qual, mix) => + print(qual, ".super") + if (!mix.isEmpty) + print("[" + mix + "]") + + case This(qual) => + if (!qual.isEmpty) print(symName(tree, qual) + ".") + print("this") + + case Select(qual @ New(tpe), name) if (!settings.debug.value) => + print(qual) + + case Select(qualifier, name) => + print(backquotedPath(qualifier), ".", symName(tree, name)) + + case id @ Ident(name) => + val str = symName(tree, name) + print( if (id.isBackquoted) "`" + str + "`" else str ) + + case Literal(x) => + print(x.escapedStringValue) + + case tt: TypeTree => + if ((tree.tpe eq null) || (doPrintPositions && tt.original != null)) { + if (tt.original != null) print("") + else print("") + } else if ((tree.tpe.typeSymbol ne null) && tree.tpe.typeSymbol.isAnonymousClass) { + print(tree.tpe.typeSymbol.toString) + } else { + print(tree.tpe.toString) + } + + case Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => + def printAnnot() { + print("@", tpt) + if (!args.isEmpty) + printRow(args, "(", ",", ")") + } + print(tree, if (tree.isType) " " else ": ") + printAnnot() + + case SingletonTypeTree(ref) => + print(ref, ".type") + + case SelectFromTypeTree(qualifier, selector) => + print(qualifier, "#", symName(tree, selector)) + + case CompoundTypeTree(templ) => + print(templ) + + case AppliedTypeTree(tp, args) => + print(tp); printRow(args, "[", ", ", "]") + + case TypeBoundsTree(lo, hi) => + printOpt(" >: ", lo); printOpt(" <: ", hi) + + case ExistentialTypeTree(tpt, whereClauses) => + print(tpt); + printColumn(whereClauses, " forSome { ", ";", "}") + +// SelectFromArray is no longer visible in reflect.internal. +// eliminated until we figure out what we will do with both Printers and +// SelectFromArray. +// case SelectFromArray(qualifier, name, _) => +// print(qualifier); print("."); print(symName(tree, name)) + + case tree => + xprintTree(this, tree) + } + if (printTypes && tree.isTerm && !tree.isEmpty) { + print("{", if (tree.tpe eq null) "" else tree.tpe.toString, "}") + } + } + + def print(args: Any*): Unit = args foreach { + case tree: Tree => + printPosition(tree) + printTree(tree) + case name: Name => + print(quotedName(name)) + case arg => + out.print(if (arg == null) "null" else arg.toString) + } + } + + /** Hook for extensions */ + def xprintTree(treePrinter: TreePrinter, tree: Tree) = + treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) + + def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) + def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) + def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) + + /** A writer that writes to the current Console and + * is sensitive to replacement of the Console's + * output stream. + */ + object ConsoleWriter extends Writer { + override def write(str: String) { Console.print(str) } + + def write(cbuf: Array[Char], off: Int, len: Int) { + write(new String(cbuf, off, len)) + } + + def close = { /* do nothing */ } + def flush = { /* do nothing */ } + } + + // provides footnotes for types + private var typeCounter = 0 + private val typeMap = collection.mutable.WeakHashMap[Type, Int]() + + def newRawTreePrinter(writer: PrintWriter): RawTreePrinter = new RawTreePrinter(writer) + def newRawTreePrinter(stream: OutputStream): RawTreePrinter = newRawTreePrinter(new PrintWriter(stream)) + def newRawTreePrinter(): RawTreePrinter = newRawTreePrinter(new PrintWriter(ConsoleWriter)) + + // emits more or less verbatim representation of the provided tree + class RawTreePrinter(out: PrintWriter) extends super.TreePrinter { + private var depth = 0 + private var footnotes = collection.mutable.Map[Int, Type]() + private var printingFootnotes = false + private var printTypesInFootnotes = true + + def print(args: Any*): Unit = { + if (depth == 0 && args.length == 1 && args(0) != null && args(0).isInstanceOf[Type]) + printTypesInFootnotes = false + + depth += 1 + args foreach { + case EmptyTree => + print("EmptyTree") + case emptyValDef: AnyRef if emptyValDef eq self.emptyValDef => + print("emptyValDef") + case Literal(Constant(value)) => + def print(s: String) = this.print("Literal(Constant(" + s + "))") + value match { + case s: String => print("\"" + s + "\"") + case null => print(null) + case _ => print(value.toString) + } + case tree: Tree => + val hasSymbol = tree.hasSymbol && tree.symbol != NoSymbol + val isError = hasSymbol && tree.symbol.name.toString == nme.ERROR.toString + printProduct( + tree, + preamble = _ => { + print(tree.productPrefix) + if (printTypes && tree.tpe != null) print(tree.tpe) + }, + body = { + case name: Name => + if (isError) { + if (isError) print("<") + print(name) + if (isError) print(": error>") + } else if (hasSymbol) { + tree match { + case _: Ident | _: Select | _: SelectFromTypeTree => print(tree.symbol) + case _ => print(tree.symbol.name) + } + } else { + print(name) + } + case arg => + print(arg) + }, + postamble = { + case tree @ TypeTree() if tree.original != null => print(".setOriginal(", tree.original, ")") + case _ => // do nothing + }) + case sym: Symbol => + if (sym.isStatic && (sym.isClass || sym.isModule)) print(sym.fullName) + else print(sym.name) + if (printIds) print("#", sym.id) + if (printKinds) print("#", sym.abbreviatedKindString) + case NoType => + print("NoType") + case NoPrefix => + print("NoPrefix") + case tpe: Type if printTypesInFootnotes && !printingFootnotes => + val index = typeMap.getOrElseUpdate(tpe, { typeCounter += 1; typeCounter }) + footnotes(index) = tpe + print("[", index, "]") + case mods: Modifiers => + print("Modifiers(") + if (mods.flags != NoFlags || mods.privateWithin != tpnme.EMPTY || mods.annotations.nonEmpty) print(show(mods.flags)) + if (mods.privateWithin != tpnme.EMPTY || mods.annotations.nonEmpty) { print(", "); print(mods.privateWithin) } + if (mods.annotations.nonEmpty) { print(", "); print(mods.annotations); } + print(")") + case name: Name => + print(show(name)) + case list: List[_] => + print("List") + printIterable(list) + case product: Product => + printProduct(product) + case arg => + out.print(arg) + } + depth -= 1 + if (depth == 0 && footnotes.nonEmpty && !printingFootnotes) { + printingFootnotes = true + out.println() + val typeIndices = footnotes.keys.toList.sorted + typeIndices.zipWithIndex foreach { + case (typeIndex, i) => + print("[" + typeIndex + "] ") + print(footnotes(typeIndex)) + if (i < typeIndices.length - 1) out.println() + } + } + } + + def printProduct( + p: Product, + preamble: Product => Unit = p => print(p.productPrefix), + body: Any => Unit = print(_), + postamble: Product => Unit = p => print("")): Unit = + { + preamble(p) + printIterable(p.productIterator.toList, body = body) + postamble(p) + } + + def printIterable( + iterable: List[_], + preamble: => Unit = print(""), + body: Any => Unit = print(_), + postamble: => Unit = print("")): Unit = + { + preamble + print("(") + val it = iterable.iterator + while (it.hasNext) { + body(it.next) + print(if (it.hasNext) ", " else "") + } + print(")") + postamble + } + } + + def show(name: Name): String = name match { + // base.StandardNames + case tpnme.EMPTY => "tpnme.EMPTY" + case tpnme.ROOT => "tpnme.ROOT" + case tpnme.EMPTY_PACKAGE_NAME => "tpnme.EMPTY_PACKAGE_NAME" + case tpnme.WILDCARD => "tpnme.WILDCARD" + case nme.CONSTRUCTOR => "nme.CONSTRUCTOR" + case nme.NO_NAME => "nme.NO_NAME" + // api.StandardNames + case tpnme.ERROR => "tpnme.ERROR" + case nme.ERROR => "nme.ERROR" + case nme.EMPTY => "nme.EMPTY" + case tpnme.PACKAGE => "tpnme.PACKAGE" + case nme.PACKAGE => "nme.PACKAGE" + case _ => + val prefix = if (name.isTermName) "newTermName(\"" else "newTypeName(\"" + prefix + name.toString + "\")" + } + + def show(flags: FlagSet): String = { + if (flags == NoFlags) nme.NoFlags.toString + else { + val s_flags = new collection.mutable.ListBuffer[String] + for (i <- 0 to 63 if (flags containsAll (1L << i))) + s_flags += flagToString(1L << i).replace("<", "").replace(">", "").toUpperCase + s_flags mkString " | " + } + } +} diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index cadd76b1ba..6def4d9409 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -28,7 +28,7 @@ abstract class SymbolTable extends makro.Universe with AnnotationInfos with AnnotationCheckers with Trees - with TreePrinters + with Printers with Positions with TypeDebugging with Importers diff --git a/src/reflect/scala/reflect/internal/TreePrinters.scala b/src/reflect/scala/reflect/internal/TreePrinters.scala deleted file mode 100644 index 6d035c8b9d..0000000000 --- a/src/reflect/scala/reflect/internal/TreePrinters.scala +++ /dev/null @@ -1,478 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Martin Odersky - */ - -// [Eugene++ to Martin] we need to unify this prettyprinter with NodePrinters - -package scala.reflect -package internal - -import java.io.{ OutputStream, PrintWriter, StringWriter, Writer } -import Flags._ - -trait TreePrinters extends api.TreePrinters { self: SymbolTable => - - //nsc import treeInfo.{ IsTrue, IsFalse } - - final val showOuterTests = false - - /** Adds backticks if the name is a scala keyword. */ - def quotedName(name: Name, decode: Boolean): String = { - val s = if (decode) name.decode else name.toString - val term = name.toTermName - if (nme.keywords(term) && term != nme.USCOREkw) "`%s`" format s - else s - } - def quotedName(name: Name): String = quotedName(name, false) - def quotedName(name: String): String = quotedName(newTermName(name), false) - - private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { - val sym = tree.symbol - if (sym.name.toString == nme.ERROR.toString) { - "<" + quotedName(name, decoded) + ": error>" - } else if (sym != null && sym != NoSymbol) { - val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" - var suffix = "" - if (settings.uniqid.value) suffix += ("#" + sym.id) - if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) - prefix + quotedName(tree.symbol.decodedName) + suffix - } else { - quotedName(name, decoded) - } - } - - def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, true) - def symName(tree: Tree, name: Name) = symNameInternal(tree, name, false) - - /** Turns a path into a String, introducing backquotes - * as necessary. - */ - def backquotedPath(t: Tree): String = { - t match { - case Select(qual, name) if name.isTermName => "%s.%s".format(backquotedPath(qual), symName(t, name)) - case Select(qual, name) if name.isTypeName => "%s#%s".format(backquotedPath(qual), symName(t, name)) - case Ident(name) => symName(t, name) - case _ => t.toString - } - } - - class TreePrinter(out: PrintWriter) extends super.TreePrinter { - protected var indentMargin = 0 - protected val indentStep = 2 - protected var indentString = " " // 40 - - typesPrinted = settings.printtypes.value - uniqueIds = settings.uniqid.value - protected def doPrintPositions = settings.Xprintpos.value - - def indent() = indentMargin += indentStep - def undent() = indentMargin -= indentStep - - def printPosition(tree: Tree) = if (doPrintPositions) print(tree.pos.show) - - def println() { - out.println() - while (indentMargin > indentString.length()) - indentString += indentString - if (indentMargin > 0) - out.write(indentString, 0, indentMargin) - } - - def printSeq[a](ls: List[a])(printelem: a => Unit)(printsep: => Unit) { - ls match { - case List() => - case List(x) => printelem(x) - case x :: rest => printelem(x); printsep; printSeq(rest)(printelem)(printsep) - } - } - - def printColumn(ts: List[Tree], start: String, sep: String, end: String) { - print(start); indent; println() - printSeq(ts){print(_)}{print(sep); println()}; undent; println(); print(end) - } - - def printRow(ts: List[Tree], start: String, sep: String, end: String) { - print(start); printSeq(ts){print(_)}{print(sep)}; print(end) - } - - def printRow(ts: List[Tree], sep: String) { printRow(ts, "", sep, "") } - - def printTypeParams(ts: List[TypeDef]) { - if (!ts.isEmpty) { - print("["); printSeq(ts){ t => - printAnnotations(t) - printParam(t) - }{print(", ")}; print("]") - } - } - - def printLabelParams(ps: List[Ident]) { - print("(") - printSeq(ps){printLabelParam}{print(", ")} - print(")") - } - - def printLabelParam(p: Ident) { - print(symName(p, p.name)); printOpt(": ", TypeTree() setType p.tpe) - } - - def printValueParams(ts: List[ValDef]) { - print("(") - if (!ts.isEmpty) printFlags(ts.head.mods.flags & IMPLICIT, "") - printSeq(ts){printParam}{print(", ")} - print(")") - } - - def printParam(tree: Tree) { - tree match { - case ValDef(mods, name, tp, rhs) => - printPosition(tree) - printAnnotations(tree) - print(symName(tree, name)); printOpt(": ", tp); printOpt(" = ", rhs) - case TypeDef(mods, name, tparams, rhs) => - printPosition(tree) - print(symName(tree, name)) - printTypeParams(tparams); print(rhs) - } - } - - def printBlock(tree: Tree) { - tree match { - case Block(_, _) => - print(tree) - case _ => - printColumn(List(tree), "{", ";", "}") - } - } - - private def symFn[T](tree: Tree, f: Symbol => T, orElse: => T): T = tree.symbol match { - case null | NoSymbol => orElse - case sym => f(sym) - } - private def ifSym(tree: Tree, p: Symbol => Boolean) = symFn(tree, p, false) - - def printOpt(prefix: String, tree: Tree) { - if (!tree.isEmpty) { print(prefix, tree) } - } - - def printModifiers(tree: Tree, mods: Modifiers): Unit = printFlags( - if (tree.symbol == NoSymbol) mods.flags else tree.symbol.flags, "" + ( - if (tree.symbol == NoSymbol) mods.privateWithin - else if (tree.symbol.hasAccessBoundary) tree.symbol.privateWithin.name - else "" - ) - ) - - def printFlags(flags: Long, privateWithin: String) { - var mask: Long = if (settings.debug.value) -1L else PrintableFlags - val s = flagsToString(flags & mask, privateWithin) - if (s != "") print(s + " ") - } - - def printAnnotations(tree: Tree) { - if (!isCompilerUniverse && tree.symbol != null && tree.symbol != NoSymbol) - // [Eugene++] todo. this is not 100% correct, but is necessary for sane printing - // the problem is that getting annotations doesn't automatically initialize the symbol - // so we might easily print something as if it doesn't have annotations, whereas it does - tree.symbol.initialize - - val annots = tree.symbol.annotations match { - case Nil => tree.asInstanceOf[MemberDef].mods.annotations - case anns => anns - } - annots foreach (annot => print("@"+annot+" ")) - } - - private var currentOwner: Symbol = NoSymbol - private var selectorType: Type = NoType - - def printTree(tree: Tree) { - tree match { - case EmptyTree => - print("") - - case ClassDef(mods, name, tparams, impl) => - printAnnotations(tree) - printModifiers(tree, mods) - val word = - if (mods.isTrait) "trait" - else if (ifSym(tree, _.isModuleClass)) "object" - else "class" - - print(word, " ", symName(tree, name)) - printTypeParams(tparams) - print(if (mods.isDeferred) " <: " else " extends ", impl) - - case PackageDef(packaged, stats) => - printAnnotations(tree) - print("package ", packaged); printColumn(stats, " {", ";", "}") - - case ModuleDef(mods, name, impl) => - printAnnotations(tree) - printModifiers(tree, mods); - print("object " + symName(tree, name), " extends ", impl) - - case ValDef(mods, name, tp, rhs) => - printAnnotations(tree) - printModifiers(tree, mods) - print(if (mods.isMutable) "var " else "val ", symName(tree, name)) - printOpt(": ", tp) - if (!mods.isDeferred) - print(" = ", if (rhs.isEmpty) "_" else rhs) - - case DefDef(mods, name, tparams, vparamss, tp, rhs) => - printAnnotations(tree) - printModifiers(tree, mods) - print("def " + symName(tree, name)) - printTypeParams(tparams); vparamss foreach printValueParams - printOpt(": ", tp); printOpt(" = ", rhs) - - case TypeDef(mods, name, tparams, rhs) => - if (mods hasFlag (PARAM | DEFERRED)) { - printAnnotations(tree) - printModifiers(tree, mods); print("type "); printParam(tree) - } else { - printAnnotations(tree) - printModifiers(tree, mods); print("type " + symName(tree, name)) - printTypeParams(tparams); printOpt(" = ", rhs) - } - - case LabelDef(name, params, rhs) => - print(symName(tree, name)); printLabelParams(params); printBlock(rhs) - - case Import(expr, selectors) => - // Is this selector remapping a name (i.e, {name1 => name2}) - def isNotRemap(s: ImportSelector) : Boolean = (s.name == nme.WILDCARD || s.name == s.rename) - def selectorToString(s: ImportSelector): String = { - val from = quotedName(s.name) - if (isNotRemap(s)) from - else from + "=>" + quotedName(s.rename) - } - print("import ", backquotedPath(expr), ".") - selectors match { - case List(s) => - // If there is just one selector and it is not remapping a name, no braces are needed - if (isNotRemap(s)) print(selectorToString(s)) - else print("{", selectorToString(s), "}") - // If there is more than one selector braces are always needed - case many => - print(many.map(selectorToString).mkString("{", ", ", "}")) - } - - case Template(parents, self, body) => - val currentOwner1 = currentOwner - if (tree.symbol != NoSymbol) currentOwner = tree.symbol.owner -// if (parents exists isReferenceToAnyVal) { -// print("AnyVal") -// } -// else { - printRow(parents, " with ") - if (!body.isEmpty) { - if (self.name != nme.WILDCARD) { - print(" { ", self.name); printOpt(": ", self.tpt); print(" => ") - } else if (!self.tpt.isEmpty) { - print(" { _ : ", self.tpt, " => ") - } else { - print(" {") - } - printColumn(body, "", ";", "}") - } -// } - currentOwner = currentOwner1 - - case Block(stats, expr) => - printColumn(stats ::: List(expr), "{", ";", "}") - - case Match(selector, cases) => - val selectorType1 = selectorType - selectorType = selector.tpe - print(selector); printColumn(cases, " match {", "", "}") - selectorType = selectorType1 - - case CaseDef(pat, guard, body) => - print("case ") - def patConstr(pat: Tree): Tree = pat match { - case Apply(fn, args) => patConstr(fn) - case _ => pat - } - if (showOuterTests && - needsOuterTest( - patConstr(pat).tpe.finalResultType, selectorType, currentOwner)) - print("???") - print(pat); printOpt(" if ", guard) - print(" => ", body) - - case Alternative(trees) => - printRow(trees, "(", "| ", ")") - - case Star(elem) => - print("(", elem, ")*") - - case Bind(name, t) => - print("(", symName(tree, name), " @ ", t, ")") - - case UnApply(fun, args) => - print(fun, " "); printRow(args, "(", ", ", ")") - - case ArrayValue(elemtpt, trees) => - print("Array[", elemtpt); printRow(trees, "]{", ", ", "}") - - case Function(vparams, body) => - print("("); printValueParams(vparams); print(" => ", body, ")") - if (uniqueIds && tree.symbol != null) print("#"+tree.symbol.id) - - case Assign(lhs, rhs) => - print(lhs, " = ", rhs) - - case AssignOrNamedArg(lhs, rhs) => - print(lhs, " = ", rhs) - - case If(cond, thenp, elsep) => - print("if (", cond, ")"); indent; println() - print(thenp); undent - if (!elsep.isEmpty) { - println(); print("else"); indent; println(); print(elsep); undent - } - - case Return(expr) => - print("return ", expr) - - case Try(block, catches, finalizer) => - print("try "); printBlock(block) - if (!catches.isEmpty) printColumn(catches, " catch {", "", "}") - printOpt(" finally ", finalizer) - - case Throw(expr) => - print("throw ", expr) - - case New(tpe) => - print("new ", tpe) - - case Typed(expr, tp) => - print("(", expr, ": ", tp, ")") - - case TypeApply(fun, targs) => - print(fun); printRow(targs, "[", ", ", "]") - - case Apply(fun, vargs) => - print(fun); printRow(vargs, "(", ", ", ")") - - case ApplyDynamic(qual, vargs) => - print("(", qual, "#", tree.symbol.nameString) - printRow(vargs, ", (", ", ", "))") - - case Super(This(qual), mix) => - if (!qual.isEmpty || tree.symbol != NoSymbol) print(symName(tree, qual) + ".") - print("super") - if (!mix.isEmpty) - print("[" + mix + "]") - - case Super(qual, mix) => - print(qual, ".super") - if (!mix.isEmpty) - print("[" + mix + "]") - - case This(qual) => - if (!qual.isEmpty) print(symName(tree, qual) + ".") - print("this") - - case Select(qual @ New(tpe), name) if (!settings.debug.value) => - print(qual) - - case Select(qualifier, name) => - print(backquotedPath(qualifier), ".", symName(tree, name)) - - case id @ Ident(name) => - val str = symName(tree, name) - print( if (id.isBackquoted) "`" + str + "`" else str ) - - case Literal(x) => - print(x.escapedStringValue) - - case tt: TypeTree => - if ((tree.tpe eq null) || (doPrintPositions && tt.original != null)) { - if (tt.original != null) print("") - else print("") - } else if ((tree.tpe.typeSymbol ne null) && tree.tpe.typeSymbol.isAnonymousClass) { - print(tree.tpe.typeSymbol.toString) - } else { - print(tree.tpe.toString) - } - - case Annotated(Apply(Select(New(tpt), nme.CONSTRUCTOR), args), tree) => - def printAnnot() { - print("@", tpt) - if (!args.isEmpty) - printRow(args, "(", ",", ")") - } - print(tree, if (tree.isType) " " else ": ") - printAnnot() - - case SingletonTypeTree(ref) => - print(ref, ".type") - - case SelectFromTypeTree(qualifier, selector) => - print(qualifier, "#", symName(tree, selector)) - - case CompoundTypeTree(templ) => - print(templ) - - case AppliedTypeTree(tp, args) => - print(tp); printRow(args, "[", ", ", "]") - - case TypeBoundsTree(lo, hi) => - printOpt(" >: ", lo); printOpt(" <: ", hi) - - case ExistentialTypeTree(tpt, whereClauses) => - print(tpt); - printColumn(whereClauses, " forSome { ", ";", "}") - -// SelectFromArray is no longer visible in reflect.internal. -// eliminated until we figure out what we will do with both TreePrinters and -// SelectFromArray. -// case SelectFromArray(qualifier, name, _) => -// print(qualifier); print("."); print(symName(tree, name)) - - case tree => - xprintTree(this, tree) - } - if (typesPrinted && tree.isTerm && !tree.isEmpty) { - print("{", if (tree.tpe eq null) "" else tree.tpe.toString, "}") - } - } - - def print(args: Any*): Unit = args foreach { - case tree: Tree => - printPosition(tree) - printTree(tree) - case name: Name => - print(quotedName(name)) - case arg => - out.print(if (arg == null) "null" else arg.toString) - } - } - - /** Hook for extensions */ - def xprintTree(treePrinter: TreePrinter, tree: Tree) = - treePrinter.print(tree.productPrefix+tree.productIterator.mkString("(", ", ", ")")) - - def newTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) - def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) - def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) - - /** A writer that writes to the current Console and - * is sensitive to replacement of the Console's - * output stream. - */ - object ConsoleWriter extends Writer { - override def write(str: String) { Console.print(str) } - - def write(cbuf: Array[Char], off: Int, len: Int) { - write(new String(cbuf, off, len)) - } - - def close = { /* do nothing */ } - def flush = { /* do nothing */ } - } -} diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 75bb0e6d49..562a49519f 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -809,7 +809,7 @@ trait Trees extends api.Trees { self: SymbolTable => } } - // Belongs in TreeInfo but then I can't reach it from TreePrinters. + // Belongs in TreeInfo but then I can't reach it from Printers. def isReferenceToScalaMember(t: Tree, Id: Name) = t match { case Ident(Id) => true case Select(Ident(nme.scala_), Id) => true diff --git a/src/reflect/scala/reflect/makro/Universe.scala b/src/reflect/scala/reflect/makro/Universe.scala index ffc4042a0a..98046be555 100644 --- a/src/reflect/scala/reflect/makro/Universe.scala +++ b/src/reflect/scala/reflect/makro/Universe.scala @@ -15,25 +15,6 @@ abstract class Universe extends scala.reflect.api.Universe { // [Eugene++ to Martin] should we also add mutability methods here (similarly to what's done below for trees)? // I'm talking about `setAnnotations` and friends - - /** Can this symbol be loaded by a reflective mirror? - * - * Scalac relies on `ScalaSignature' annotation to retain symbols across compilation runs. - * Such annotations (also called "pickles") are applied on top-level classes and include information - * about all symbols reachable from the annotee. However, local symbols (e.g. classes or definitions local to a block) - * are typically unreachable and information about them gets lost. - * - * This method is useful for macro writers who wish to save certain ASTs to be used at runtime. - * With `isLocatable' it's possible to check whether a tree can be retained as is, or it needs special treatment. - */ - def isLocatable: Boolean - - /** Is this symbol static (i.e. with no outer instance)? - * Q: When exactly is a sym marked as STATIC? - * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep. - * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6 - */ - def isStatic: Boolean } // Tree extensions --------------------------------------------------------------- diff --git a/test/files/run/showraw_mods.check b/test/files/run/showraw_mods.check new file mode 100644 index 0000000000..83055f2b70 --- /dev/null +++ b/test/files/run/showraw_mods.check @@ -0,0 +1 @@ +Block(List(ClassDef(Modifiers(ABSTRACT | DEFAULTPARAM/TRAIT), newTypeName("C"), List(), Template(List(Ident(java.lang.Object)), emptyValDef, List(DefDef(Modifiers(), newTermName("$init$"), List(), List(List()), TypeTree(), Block(List(), Literal(Constant(())))), ValDef(Modifiers(PRIVATE | LOCAL), newTermName("x"), TypeTree(), Literal(Constant(2))), ValDef(Modifiers(MUTABLE), newTermName("y"), TypeTree(), Select(This(newTypeName("C")), newTermName("x"))), ValDef(Modifiers(LAZY), newTermName("z"), TypeTree(), Select(This(newTypeName("C")), newTermName("y"))))))), Literal(Constant(()))) diff --git a/test/files/run/showraw_mods.scala b/test/files/run/showraw_mods.scala new file mode 100644 index 0000000000..a10e4821dc --- /dev/null +++ b/test/files/run/showraw_mods.scala @@ -0,0 +1,6 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree = reify{trait C { private[this] val x = 2; var y = x; lazy val z = y }} + println(showRaw(tree.tree)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree.check b/test/files/run/showraw_tree.check new file mode 100644 index 0000000000..82724cae44 --- /dev/null +++ b/test/files/run/showraw_tree.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree.scala b/test/files/run/showraw_tree.scala new file mode 100644 index 0000000000..3624a24d6a --- /dev/null +++ b/test/files/run/showraw_tree.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree)) + println(showRaw(tree2.tree)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_ids.check b/test/files/run/showraw_tree_ids.check new file mode 100644 index 0000000000..c6dbd6f1ce --- /dev/null +++ b/test/files/run/showraw_tree_ids.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#1903), List(Ident(java.lang.String#129), Ident(java.lang.String#129)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#1908), List(Ident(java.lang.String#129), Ident(java.lang.String#129)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_ids.scala b/test/files/run/showraw_tree_ids.scala new file mode 100644 index 0000000000..b56b8b4476 --- /dev/null +++ b/test/files/run/showraw_tree_ids.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree, printIds = true)) + println(showRaw(tree2.tree, printIds = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_kinds.check b/test/files/run/showraw_tree_kinds.check new file mode 100644 index 0000000000..a12e21c611 --- /dev/null +++ b/test/files/run/showraw_tree_kinds.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap#CLS), List(Ident(java.lang.String#CLS), Ident(java.lang.String#CLS)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap#CLS), List(Ident(java.lang.String#CLS), Ident(java.lang.String#CLS)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_kinds.scala b/test/files/run/showraw_tree_kinds.scala new file mode 100644 index 0000000000..0ca5a387da --- /dev/null +++ b/test/files/run/showraw_tree_kinds.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree, printKinds = true)) + println(showRaw(tree2.tree, printKinds = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_types_ids.check b/test/files/run/showraw_tree_types_ids.check new file mode 100644 index 0000000000..02e7aeed7c --- /dev/null +++ b/test/files/run/showraw_tree_types_ids.check @@ -0,0 +1,10 @@ +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap#1903), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)))))), nme.CONSTRUCTOR#1913), List()) +[1] TypeRef(ThisType(scala.collection.immutable#1898), scala.collection.immutable.HashMap#1903, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable#1898), scala.collection.immutable.HashMap#1903, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List())))) +[3] TypeRef(ThisType(scala.collection.immutable#1898), scala.collection.immutable.HashMap#1903, List()) +[4] TypeRef(ThisType(java.lang#128), java.lang.String#129, List()) +Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap#1908), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129)))))), nme.CONSTRUCTOR#2231), List()) +[4] TypeRef(ThisType(java.lang#128), java.lang.String#129, List()) +[5] TypeRef(ThisType(scala.collection.mutable#1907), scala.collection.mutable.HashMap#1908, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List()))) +[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable#1907), scala.collection.mutable.HashMap#1908, List(TypeRef(ThisType(java.lang#128), java.lang.String#129, List()), TypeRef(ThisType(java.lang#128), java.lang.String#129, List())))) +[7] TypeRef(ThisType(scala.collection.mutable#1907), scala.collection.mutable.HashMap#1908, List()) diff --git a/test/files/run/showraw_tree_types_ids.scala b/test/files/run/showraw_tree_types_ids.scala new file mode 100644 index 0000000000..cb2c2bfb0f --- /dev/null +++ b/test/files/run/showraw_tree_types_ids.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tb.typeCheck(tree1.tree), printIds = true, printTypes = true)) + println(showRaw(tb.typeCheck(tree2.tree), printIds = true, printTypes = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_types_typed.check b/test/files/run/showraw_tree_types_typed.check new file mode 100644 index 0000000000..60176c7192 --- /dev/null +++ b/test/files/run/showraw_tree_types_typed.check @@ -0,0 +1,10 @@ +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String)), TypeTree[4]().setOriginal(Ident[4](java.lang.String)))))), nme.CONSTRUCTOR), List()) +[1] TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List())))) +[3] TypeRef(ThisType(scala.collection.immutable), scala.collection.immutable.HashMap, List()) +[4] TypeRef(ThisType(java.lang), java.lang.String, List()) +Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String)), TypeTree[4]().setOriginal(Ident[4](java.lang.String)))))), nme.CONSTRUCTOR), List()) +[4] TypeRef(ThisType(java.lang), java.lang.String, List()) +[5] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List()))) +[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List(TypeRef(ThisType(java.lang), java.lang.String, List()), TypeRef(ThisType(java.lang), java.lang.String, List())))) +[7] TypeRef(ThisType(scala.collection.mutable), scala.collection.mutable.HashMap, List()) diff --git a/test/files/run/showraw_tree_types_typed.scala b/test/files/run/showraw_tree_types_typed.scala new file mode 100644 index 0000000000..d7ccc84ea3 --- /dev/null +++ b/test/files/run/showraw_tree_types_typed.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tb.typeCheck(tree1.tree), printTypes = true)) + println(showRaw(tb.typeCheck(tree2.tree), printTypes = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_types_untyped.check b/test/files/run/showraw_tree_types_untyped.check new file mode 100644 index 0000000000..82724cae44 --- /dev/null +++ b/test/files/run/showraw_tree_types_untyped.check @@ -0,0 +1,2 @@ +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.immutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) +Apply(Select(New(AppliedTypeTree(Ident(scala.collection.mutable.HashMap), List(Ident(java.lang.String), Ident(java.lang.String)))), nme.CONSTRUCTOR), List()) diff --git a/test/files/run/showraw_tree_types_untyped.scala b/test/files/run/showraw_tree_types_untyped.scala new file mode 100644 index 0000000000..4df2eb66b2 --- /dev/null +++ b/test/files/run/showraw_tree_types_untyped.scala @@ -0,0 +1,8 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tree1.tree, printTypes = true)) + println(showRaw(tree2.tree, printTypes = true)) +} \ No newline at end of file diff --git a/test/files/run/showraw_tree_ultimate.check b/test/files/run/showraw_tree_ultimate.check new file mode 100644 index 0000000000..0b409554a0 --- /dev/null +++ b/test/files/run/showraw_tree_ultimate.check @@ -0,0 +1,10 @@ +Apply[1](Select[2](New[1](TypeTree[1]().setOriginal(AppliedTypeTree(Ident[3](scala.collection.immutable.HashMap#1903#CLS), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)))))), nme.CONSTRUCTOR#1913#PCTOR), List()) +[1] TypeRef(ThisType(scala.collection.immutable#1898#PK), scala.collection.immutable.HashMap#1903#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()))) +[2] MethodType(List(), TypeRef(ThisType(scala.collection.immutable#1898#PK), scala.collection.immutable.HashMap#1903#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List())))) +[3] TypeRef(ThisType(scala.collection.immutable#1898#PK), scala.collection.immutable.HashMap#1903#CLS, List()) +[4] TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()) +Apply[5](Select[6](New[5](TypeTree[5]().setOriginal(AppliedTypeTree(Ident[7](scala.collection.mutable.HashMap#1908#CLS), List(TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)), TypeTree[4]().setOriginal(Ident[4](java.lang.String#129#CLS)))))), nme.CONSTRUCTOR#2231#CTOR), List()) +[4] TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()) +[5] TypeRef(ThisType(scala.collection.mutable#1907#PK), scala.collection.mutable.HashMap#1908#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()))) +[6] MethodType(List(), TypeRef(ThisType(scala.collection.mutable#1907#PK), scala.collection.mutable.HashMap#1908#CLS, List(TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List()), TypeRef(ThisType(java.lang#128#PK), java.lang.String#129#CLS, List())))) +[7] TypeRef(ThisType(scala.collection.mutable#1907#PK), scala.collection.mutable.HashMap#1908#CLS, List()) diff --git a/test/files/run/showraw_tree_ultimate.scala b/test/files/run/showraw_tree_ultimate.scala new file mode 100644 index 0000000000..dfd7abde52 --- /dev/null +++ b/test/files/run/showraw_tree_ultimate.scala @@ -0,0 +1,10 @@ +import scala.reflect.runtime.universe._ +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = runtimeMirror(getClass.getClassLoader).mkToolBox() + val tree1 = reify(new collection.immutable.HashMap[String, String]) + val tree2 = reify(new collection.mutable.HashMap[String, String]) + println(showRaw(tb.typeCheck(tree1.tree), printIds = true, printKinds = true, printTypes = true)) + println(showRaw(tb.typeCheck(tree2.tree), printIds = true, printKinds = true, printTypes = true)) +} \ No newline at end of file -- cgit v1.2.3 From 8284486a155f145bf61019fc0bd8b56b8d327ff2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jun 2012 17:20:14 +0200 Subject: Statistics reorganization Statistics were broken since the move to reflect.internal. They are now re-organized, made more robost and modular. --- src/compiler/scala/tools/nsc/Global.scala | 2 +- .../scala/tools/nsc/settings/ScalaSettings.scala | 2 +- .../scala/tools/nsc/symtab/SymbolLoaders.scala | 12 +- .../scala/tools/nsc/typechecker/Analyzer.scala | 7 +- .../scala/tools/nsc/typechecker/Implicits.scala | 153 +++++++----- .../scala/tools/nsc/typechecker/Macros.scala | 15 +- .../tools/nsc/typechecker/PatternMatching.scala | 39 +-- .../scala/tools/nsc/typechecker/Typers.scala | 91 +++---- src/compiler/scala/tools/nsc/util/Statistics.scala | 200 ---------------- .../scala/tools/nsc/util/StatisticsInfo.scala | 38 +++ .../scala/reflect/internal/BaseTypeSeqs.scala | 12 +- src/reflect/scala/reflect/internal/Symbols.scala | 18 +- src/reflect/scala/reflect/internal/Trees.scala | 10 + src/reflect/scala/reflect/internal/Types.scala | 86 ++++--- .../scala/reflect/internal/util/StatBase.scala | 97 -------- .../scala/reflect/internal/util/Statistics.scala | 261 ++++++++++++++++++--- 16 files changed, 546 insertions(+), 497 deletions(-) delete mode 100644 src/compiler/scala/tools/nsc/util/Statistics.scala create mode 100644 src/compiler/scala/tools/nsc/util/StatisticsInfo.scala delete mode 100644 src/reflect/scala/reflect/internal/util/StatBase.scala diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 787c9c7f57..954e98742a 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -12,7 +12,7 @@ import scala.tools.util.PathResolver import scala.collection.{ mutable, immutable } import io.{ SourceReader, AbstractFile, Path } import reporters.{ Reporter, ConsoleReporter } -import util.{ Exceptional, ClassPath, MergedClassPath, Statistics, StatisticsInfo, ScalaClassLoader, returning } +import util.{ Exceptional, ClassPath, MergedClassPath, StatisticsInfo, ScalaClassLoader, returning } import scala.reflect.internal.util.{ NoPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile } import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat } import settings.{ AestheticSettings } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 04acba06e8..9b223a13ba 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -160,7 +160,7 @@ trait ScalaSettings extends AbsScalaSettings val Ygenjavap = StringSetting ("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") val Ydumpclasses = StringSetting ("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.") - val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (util.Statistics.enabled = _) + val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") andThen (scala.reflect.internal.util.Statistics.enabled = _) val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat val stopBefore = PhasesSetting ("-Ystop-before", "Stop before") val refinementMethodDispatch diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 15edac16d5..0c988ceae4 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -12,7 +12,7 @@ import scala.tools.nsc.util.{ ClassPath } import classfile.ClassfileParser import reflect.internal.Flags._ import reflect.internal.MissingRequirementError -import util.Statistics._ +import reflect.internal.util.Statistics import scala.tools.nsc.io.{ AbstractFile, MsilFile } /** This class ... @@ -23,6 +23,7 @@ import scala.tools.nsc.io.{ AbstractFile, MsilFile } abstract class SymbolLoaders { val global: Global import global._ + import SymbolLoadersStats._ protected def enterIfNew(owner: Symbol, member: Symbol, completer: SymbolLoader): Symbol = { assert(owner.info.decls.lookup(member.name) == NoSymbol, owner.fullName + "." + member.name) @@ -236,7 +237,7 @@ abstract class SymbolLoaders { protected def description = "class file "+ classfile.toString protected def doComplete(root: Symbol) { - val start = startTimer(classReadNanos) + val start = Statistics.startTimer(classReadNanos) classfileParser.parse(classfile, root) if (root.associatedFile eq null) { root match { @@ -248,7 +249,7 @@ abstract class SymbolLoaders { debuglog("Not setting associatedFile to %s because %s is a %s".format(classfile, root.name, root.shortSymbolClass)) } } - stopTimer(classReadNanos, start) + Statistics.stopTimer(classReadNanos, start) } override def sourcefile: Option[AbstractFile] = classfileParser.srcfile } @@ -284,3 +285,8 @@ abstract class SymbolLoaders { var parentsLevel = 0 var pendingLoadActions: List[() => Unit] = Nil } + +object SymbolLoadersStats { + import reflect.internal.TypesStats.typerNanos + val classReadNanos = Statistics.newSubTimer ("time classfilereading", typerNanos) +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index a77df71312..2077ab0997 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package typechecker -import util.Statistics._ +import reflect.internal.util.Statistics /** The main attribution phase. */ @@ -72,6 +72,7 @@ trait Analyzer extends AnyRef } object typerFactory extends SubComponent { + import reflect.internal.TypesStats.typerNanos val global: Analyzer.this.global.type = Analyzer.this.global val phaseName = "typer" val runsAfter = List[String]() @@ -84,13 +85,13 @@ trait Analyzer extends AnyRef // compiler run). This is good enough for the resident compiler, which was the most affected. undoLog.clear() override def run() { - val start = startTimer(typerNanos) + val start = Statistics.startTimer(typerNanos) global.echoPhaseSummary(this) currentRun.units foreach applyPhase undoLog.clear() // need to clear it after as well or 10K+ accumulated entries are // uncollectable the rest of the way. - stopTimer(typerNanos, start) + Statistics.stopTimer(typerNanos, start) } def apply(unit: CompilationUnit) { try { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 37380fd5c0..f7e00109ae 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -16,7 +16,7 @@ import scala.collection.{ mutable, immutable } import mutable.{ LinkedHashMap, ListBuffer } import scala.util.matching.Regex import symtab.Flags._ -import util.Statistics._ +import scala.reflect.internal.util.Statistics import language.implicitConversions /** This trait provides methods to find various kinds of implicits. @@ -29,6 +29,7 @@ trait Implicits { import global._ import definitions._ + import ImplicitsStats._ import typeDebug.{ ptTree, ptBlock, ptLine } import global.typer.{ printTyping, deindentTyping, indentTyping, printInference } @@ -71,10 +72,10 @@ trait Implicits { ) indentTyping() - val rawTypeStart = startCounter(rawTypeImpl) - val findMemberStart = startCounter(findMemberImpl) - val subtypeStart = startCounter(subtypeImpl) - val start = startTimer(implicitNanos) + val rawTypeStart = Statistics.startCounter(rawTypeImpl) + val findMemberStart = Statistics.startCounter(findMemberImpl) + val subtypeStart = Statistics.startCounter(subtypeImpl) + val start = Statistics.startTimer(implicitNanos) if (printInfers && !tree.isEmpty && !context.undetparams.isEmpty) printTyping("typing implicit: %s %s".format(tree, context.undetparamsString)) val implicitSearchContext = context.makeImplicit(reportAmbiguous) @@ -86,10 +87,10 @@ trait Implicits { printInference("[infer implicit] inferred " + result) context.undetparams = context.undetparams filterNot result.subst.from.contains - stopTimer(implicitNanos, start) - stopCounter(rawTypeImpl, rawTypeStart) - stopCounter(findMemberImpl, findMemberStart) - stopCounter(subtypeImpl, subtypeStart) + Statistics.stopTimer(implicitNanos, start) + Statistics.stopCounter(rawTypeImpl, rawTypeStart) + Statistics.stopCounter(findMemberImpl, findMemberStart) + Statistics.stopCounter(subtypeImpl, subtypeStart) deindentTyping() printTyping("Implicit search yielded: "+ result) result @@ -307,12 +308,12 @@ trait Implicits { /** Is implicit info `info1` better than implicit info `info2`? */ def improves(info1: ImplicitInfo, info2: ImplicitInfo) = { - incCounter(improvesCount) + Statistics.incCounter(improvesCount) (info2 == NoImplicitInfo) || (info1 != NoImplicitInfo) && { if (info1.sym.isStatic && info2.sym.isStatic) { improvesCache get (info1, info2) match { - case Some(b) => incCounter(improvesCachedCount); b + case Some(b) => Statistics.incCounter(improvesCachedCount); b case None => val result = isStrictlyMoreSpecific(info1.tpe, info2.tpe, info1.sym, info2.sym) improvesCache((info1, info2)) = result @@ -376,7 +377,7 @@ trait Implicits { overlaps(dtor1, dted1) && (dtor1 =:= dted1 || complexity(dtor1) > complexity(dted1)) } - incCounter(implicitSearchCount) + Statistics.incCounter(implicitSearchCount) /** The type parameters to instantiate */ val undetParams = if (isView) List() else context.outer.undetparams @@ -428,7 +429,7 @@ trait Implicits { * This method is performance critical: 5-8% of typechecking time. */ private def matchesPt(tp: Type, pt: Type, undet: List[Symbol]): Boolean = { - val start = startTimer(matchesPtNanos) + val start = Statistics.startTimer(matchesPtNanos) val result = normSubType(tp, pt) || isView && { pt match { case TypeRef(_, Function1.Sym, args) => @@ -437,7 +438,7 @@ trait Implicits { false } } - stopTimer(matchesPtNanos, start) + Statistics.stopTimer(matchesPtNanos, start) result } private def matchesPt(info: ImplicitInfo): Boolean = ( @@ -536,7 +537,7 @@ trait Implicits { } private def typedImplicit0(info: ImplicitInfo, ptChecked: Boolean, isLocal: Boolean): SearchResult = { - incCounter(plausiblyCompatibleImplicits) + Statistics.incCounter(plausiblyCompatibleImplicits) printTyping ( ptBlock("typedImplicit0", "info.name" -> info.name, @@ -556,7 +557,7 @@ trait Implicits { } private def typedImplicit1(info: ImplicitInfo, isLocal: Boolean): SearchResult = { - incCounter(matchingImplicits) + Statistics.incCounter(matchingImplicits) val itree = atPos(pos.focus) { // workaround for deficient context provided by ModelFactoryImplicitSupport#makeImplicitConstraints @@ -594,7 +595,7 @@ trait Implicits { if (context.hasErrors) return fail("typed implicit %s has errors".format(info.sym.fullLocationString)) - incCounter(typedImplicits) + Statistics.incCounter(typedImplicits) printTyping("typed implicit %s:%s, pt=%s".format(itree1, itree1.tpe, wildPt)) val itree2 = if (isView) (itree1: @unchecked) match { case Apply(fun, _) => fun } @@ -677,7 +678,7 @@ trait Implicits { fail("typing TypeApply reported errors for the implicit tree") else { val result = new SearchResult(itree2, subst) - incCounter(foundImplicits) + Statistics.incCounter(foundImplicits) printInference("[success] found %s for pt %s".format(result, ptInstantiated)) result } @@ -904,11 +905,11 @@ trait Implicits { * @return map from infos to search results */ def applicableInfos(iss: Infoss, isLocal: Boolean): Map[ImplicitInfo, SearchResult] = { - val start = startCounter(subtypeAppInfos) + val start = Statistics.startCounter(subtypeAppInfos) val computation = new ImplicitComputation(iss, isLocal) { } val applicable = computation.findAll() - stopCounter(subtypeAppInfos, start) + Statistics.stopCounter(subtypeAppInfos, start) applicable } @@ -1123,26 +1124,28 @@ trait Implicits { * These are all implicits found in companion objects of classes C * such that some part of `tp` has C as one of its superclasses. */ - private def implicitsOfExpectedType: Infoss = implicitsCache get pt match { - case Some(implicitInfoss) => - incCounter(implicitCacheHits) - implicitInfoss - case None => - incCounter(implicitCacheMisses) - val start = startTimer(subtypeETNanos) -// val implicitInfoss = companionImplicits(pt) - val implicitInfoss1 = companionImplicitMap(pt).valuesIterator.toList -// val is1 = implicitInfoss.flatten.toSet -// val is2 = implicitInfoss1.flatten.toSet -// for (i <- is1) -// if (!(is2 contains i)) println("!!! implicit infos of "+pt+" differ, new does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) -// for (i <- is2) -// if (!(is1 contains i)) println("!!! implicit infos of "+pt+" differ, old does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) - stopTimer(subtypeETNanos, start) - implicitsCache(pt) = implicitInfoss1 - if (implicitsCache.size >= sizeLimit) - implicitsCache -= implicitsCache.keysIterator.next - implicitInfoss1 + private def implicitsOfExpectedType: Infoss = { + Statistics.incCounter(implicitCacheHits) + implicitsCache get pt match { + case Some(implicitInfoss) => + Statistics.incCounter(implicitCacheHits) + implicitInfoss + case None => + val start = Statistics.startTimer(subtypeETNanos) + // val implicitInfoss = companionImplicits(pt) + val implicitInfoss1 = companionImplicitMap(pt).valuesIterator.toList + // val is1 = implicitInfoss.flatten.toSet + // val is2 = implicitInfoss1.flatten.toSet + // for (i <- is1) + // if (!(is2 contains i)) println("!!! implicit infos of "+pt+" differ, new does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) + // for (i <- is2) + // if (!(is1 contains i)) println("!!! implicit infos of "+pt+" differ, old does not contain "+i+",\nold: "+implicitInfoss+",\nnew: "+implicitInfoss1) + Statistics.stopTimer(subtypeETNanos, start) + implicitsCache(pt) = implicitInfoss1 + if (implicitsCache.size >= sizeLimit) + implicitsCache -= implicitsCache.keysIterator.next + implicitInfoss1 + } } private def TagSymbols = TagMaterializers.keySet @@ -1368,30 +1371,30 @@ trait Implicits { * If all fails return SearchFailure */ def bestImplicit: SearchResult = { - val failstart = startTimer(inscopeFailNanos) - val succstart = startTimer(inscopeSucceedNanos) + val failstart = Statistics.startTimer(inscopeFailNanos) + val succstart = Statistics.startTimer(inscopeSucceedNanos) var result = searchImplicit(context.implicitss, true) if (result == SearchFailure) { - stopTimer(inscopeFailNanos, failstart) + Statistics.stopTimer(inscopeFailNanos, failstart) } else { - stopTimer(inscopeSucceedNanos, succstart) - incCounter(inscopeImplicitHits) + Statistics.stopTimer(inscopeSucceedNanos, succstart) + Statistics.incCounter(inscopeImplicitHits) } if (result == SearchFailure) { val previousErrs = context.flushAndReturnBuffer() - val failstart = startTimer(oftypeFailNanos) - val succstart = startTimer(oftypeSucceedNanos) + val failstart = Statistics.startTimer(oftypeFailNanos) + val succstart = Statistics.startTimer(oftypeSucceedNanos) result = implicitTagOrOfExpectedType(pt) if (result == SearchFailure) { context.updateBuffer(previousErrs) - stopTimer(oftypeFailNanos, failstart) + Statistics.stopTimer(oftypeFailNanos, failstart) } else { - stopTimer(oftypeSucceedNanos, succstart) - incCounter(oftypeImplicitHits) + Statistics.stopTimer(oftypeSucceedNanos, succstart) + Statistics.incCounter(oftypeImplicitHits) } } @@ -1415,19 +1418,19 @@ trait Implicits { val eligible = new ImplicitComputation(iss, isLocal).eligible eligible.toList.flatMap { (ii: ImplicitInfo) => - // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) - // thus, start each type var off with a fresh for every typedImplicit - resetTVars() - // any previous errors should not affect us now - context.flushBuffer() + // each ImplicitInfo contributes a distinct set of constraints (generated indirectly by typedImplicit) + // thus, start each type var off with a fresh for every typedImplicit + resetTVars() + // any previous errors should not affect us now + context.flushBuffer() val res = typedImplicit(ii, ptChecked = false, isLocal) - if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) - else Nil - } + if (res.tree ne EmptyTree) List((res, tvars map (_.constr))) + else Nil } - eligibleInfos(context.implicitss, isLocal = true) ++ eligibleInfos(implicitsOfExpectedType, isLocal = false) } + eligibleInfos(context.implicitss, isLocal = true) ++ eligibleInfos(implicitsOfExpectedType, isLocal = false) + } } object ImplicitNotFoundMsg { @@ -1472,5 +1475,37 @@ trait Implicits { } } } + +object ImplicitsStats { + + import reflect.internal.TypesStats._ + + val rawTypeImpl = Statistics.newSubCounter (" of which in implicits", rawTypeCount) + val subtypeImpl = Statistics.newSubCounter(" of which in implicit", subtypeCount) + val findMemberImpl = Statistics.newSubCounter(" of which in implicit", findMemberCount) + val subtypeAppInfos = Statistics.newSubCounter(" of which in app impl", subtypeCount) + val subtypeImprovCount = Statistics.newSubCounter(" of which in improves", subtypeCount) + val implicitSearchCount = Statistics.newCounter ("#implicit searches", "typer") + val triedImplicits = Statistics.newSubCounter(" #tried", implicitSearchCount) + val plausiblyCompatibleImplicits + = Statistics.newSubCounter(" #plausibly compatible", implicitSearchCount) + val matchingImplicits = Statistics.newSubCounter(" #matching", implicitSearchCount) + val typedImplicits = Statistics.newSubCounter(" #typed", implicitSearchCount) + val foundImplicits = Statistics.newSubCounter(" #found", implicitSearchCount) + val improvesCount = Statistics.newSubCounter("implicit improves tests", implicitSearchCount) + val improvesCachedCount = Statistics.newSubCounter("#implicit improves cached ", implicitSearchCount) + val inscopeImplicitHits = Statistics.newSubCounter("#implicit inscope hits", implicitSearchCount) + val oftypeImplicitHits = Statistics.newSubCounter("#implicit oftype hits ", implicitSearchCount) + val implicitNanos = Statistics.newSubTimer ("time spent in implicits", typerNanos) + val inscopeSucceedNanos = Statistics.newSubTimer (" successful in scope", typerNanos) + val inscopeFailNanos = Statistics.newSubTimer (" failed in scope", typerNanos) + val oftypeSucceedNanos = Statistics.newSubTimer (" successful of type", typerNanos) + val oftypeFailNanos = Statistics.newSubTimer (" failed of type", typerNanos) + val subtypeETNanos = Statistics.newSubTimer (" assembling parts", typerNanos) + val matchesPtNanos = Statistics.newSubTimer (" matchesPT", typerNanos) + val implicitCacheAccs = Statistics.newCounter ("implicit cache accesses", "typer") + val implicitCacheHits = Statistics.newSubCounter("implicit cache hits", implicitCacheAccs) +} + class DivergentImplicit extends Exception object DivergentImplicit extends DivergentImplicit diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index ec14476d1a..d157666e47 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -7,7 +7,7 @@ import scala.tools.nsc.util.ClassPath._ import scala.reflect.runtime.ReflectionUtils import scala.collection.mutable.ListBuffer import scala.compat.Platform.EOL -import util.Statistics._ +import reflect.internal.util.Statistics import scala.reflect.makro.util._ import java.lang.{Class => jClass} import java.lang.reflect.{Array => jArray, Method => jMethod} @@ -42,6 +42,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { import global._ import definitions._ + import MacrosStats._ def globalSettings = global.settings val globalMacroCache = collection.mutable.Map[Any, Any]() @@ -945,8 +946,8 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { this.fail(typer, tree, err.errPos, "failed to %s: %s".format(what, err.errMsg)) return expandee } - val start = startTimer(macroExpandNanos) - incCounter(macroExpandCount) + val start = Statistics.startTimer(macroExpandNanos) + Statistics.incCounter(macroExpandCount) try { macroExpand1(typer, expandee) match { case Success(expanded0) => @@ -993,7 +994,7 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { result } } finally { - stopTimer(macroExpandNanos, start) + Statistics.stopTimer(macroExpandNanos, start) } } @@ -1292,3 +1293,9 @@ trait Macros extends scala.tools.reflect.FastTrack with Traces { }) }.transform(expandee) } + +object MacrosStats { + import reflect.internal.TypesStats.typerNanos + val macroExpandCount = Statistics.newCounter ("#macro expansions", "typer") + val macroExpandNanos = Statistics.newSubTimer("time spent in macroExpand", typerNanos) +} diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 4e8f416b16..9b8ddffb49 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -14,7 +14,7 @@ import scala.tools.nsc.transform.TypingTransformers import scala.tools.nsc.transform.Transform import scala.collection.mutable.HashSet import scala.collection.mutable.HashMap -import scala.tools.nsc.util.Statistics +import reflect.internal.util.Statistics /** Translate pattern matching. * @@ -38,6 +38,7 @@ import scala.tools.nsc.util.Statistics */ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL { // self: Analyzer => import Statistics._ + import PatternMatchingStats._ val global: Global // need to repeat here because otherwise last mixin defines global as // SymbolTable. If we had DOT this would not be an issue @@ -183,7 +184,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL case _ => tp } - val start = startTimer(patmatNanos) + val start = Statistics.startTimer(patmatNanos) val selectorTp = repeatedToSeq(elimAnonymousClass(selector.tpe.widen.withoutAnnotations)) @@ -210,7 +211,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // pt = Any* occurs when compiling test/files/pos/annotDepMethType.scala with -Xexperimental val combined = combineCases(selector, selectorSym, cases map translateCase(selectorSym, pt), pt, matchOwner, matchFailGenOverride) - stopTimer(patmatNanos, start) + Statistics.stopTimer(patmatNanos, start) combined } @@ -1694,7 +1695,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // TODO: for V1 representing x1 and V2 standing for x1.head, encode that // V1 = Nil implies -(V2 = Ci) for all Ci in V2's domain (i.e., it is unassignable) def removeVarEq(props: List[Prop], considerNull: Boolean = false): (Prop, List[Prop]) = { - val start = startTimer(patmatAnaVarEq) + val start = Statistics.startTimer(patmatAnaVarEq) val vars = new collection.mutable.HashSet[Var] @@ -1766,7 +1767,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // patmatDebug("eqAxioms:\n"+ cnfString(eqFreePropToSolvable(eqAxioms))) // patmatDebug("pure:\n"+ cnfString(eqFreePropToSolvable(pure))) - stopTimer(patmatAnaVarEq, start) + Statistics.stopTimer(patmatAnaVarEq, start) (eqAxioms, pure) } @@ -1865,10 +1866,10 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - val start = startTimer(patmatCNF) + val start = Statistics.startTimer(patmatCNF) val res = conjunctiveNormalForm(negationNormalForm(p)) - stopTimer(patmatCNF, start) - patmatCNFSizes(res.size) += 1 + Statistics.stopTimer(patmatCNF, start) + patmatCNFSizes(res.size).value += 1 // patmatDebug("cnf for\n"+ p +"\nis:\n"+cnfString(res)) res @@ -1945,7 +1946,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // patmatDebug("dpll\n"+ cnfString(f)) - val start = startTimer(patmatAnaDPLL) + val start = Statistics.startTimer(patmatAnaDPLL) val satisfiableWithModel: Model = if (f isEmpty) EmptyModel @@ -1983,7 +1984,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - stopTimer(patmatAnaDPLL, start) + Statistics.stopTimer(patmatAnaDPLL, start) satisfiableWithModel } @@ -2291,7 +2292,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def makeCondPessimistic(tm: TreeMaker)(recurse: TreeMaker => Cond): Cond = makeCond(tm)(recurse) } - val start = startTimer(patmatAnaReach) + val start = Statistics.startTimer(patmatAnaReach) // use the same approximator so we share variables, // but need different conditions depending on whether we're conservatively looking for failure or success @@ -2340,7 +2341,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } - stopTimer(patmatAnaReach, start) + Statistics.stopTimer(patmatAnaReach, start) if (reachable) None else Some(caseIndex) } catch { @@ -2428,7 +2429,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // - back off (to avoid crying exhaustive too often) when: // - there are guards --> // - there are extractor calls (that we can't secretly/soundly) rewrite - val start = startTimer(patmatAnaExhaust) + val start = Statistics.startTimer(patmatAnaExhaust) var backoff = false object exhaustivityApproximation extends TreeMakersToConds(prevBinder) { def makeCondExhaustivity(tm: TreeMaker)(recurse: TreeMaker => Cond): Cond = tm match { @@ -2503,7 +2504,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val pruned = CounterExample.prune(counterExamples).map(_.toString).sorted - stopTimer(patmatAnaExhaust, start) + Statistics.stopTimer(patmatAnaExhaust, start) pruned } catch { case e : CNFBudgetExceeded => @@ -3186,3 +3187,13 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } } } + +object PatternMatchingStats { + val patmatNanos = Statistics.newTimer ("time spent in patmat", "patmat") + val patmatAnaDPLL = Statistics.newSubTimer (" of which DPLL", patmatNanos) + val patmatCNF = Statistics.newSubTimer (" of which in CNF conversion", patmatNanos) + val patmatCNFSizes = Statistics.newQuantMap[Int, Statistics.Counter](" CNF size counts", "patmat")(Statistics.newCounter("")) + val patmatAnaVarEq = Statistics.newSubTimer (" of which variable equality", patmatNanos) + val patmatAnaExhaust = Statistics.newSubTimer (" of which in exhaustivity", patmatNanos) + val patmatAnaReach = Statistics.newSubTimer (" of which in unreachability", patmatNanos) +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 1193d3013a..6bd1117689 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -16,8 +16,7 @@ import scala.collection.mutable import scala.reflect.internal.util.BatchSourceFile import mutable.ListBuffer import symtab.Flags._ -import util.Statistics -import util.Statistics._ +import reflect.internal.util.Statistics // Suggestion check whether we can do without priming scopes with symbols of outer scopes, // like the IDE does. @@ -31,6 +30,7 @@ trait Typers extends Modes with Adaptations with Tags { import global._ import definitions._ + import TypersStats._ import patmat.DefaultOverrideMatchAttachment final def forArgMode(fun: Tree, mode: Int) = @@ -707,10 +707,16 @@ trait Typers extends Modes with Adaptations with Tags { def silent[T](op: Typer => T, reportAmbiguousErrors: Boolean = context.ambiguousErrors, newtree: Tree = context.tree): SilentResult[T] = { - val rawTypeStart = startCounter(rawTypeFailed) - val findMemberStart = startCounter(findMemberFailed) - val subtypeStart = startCounter(subtypeFailed) - val failedSilentStart = startTimer(failedSilentNanos) + val rawTypeStart = Statistics.startCounter(rawTypeFailed) + val findMemberStart = Statistics.startCounter(findMemberFailed) + val subtypeStart = Statistics.startCounter(subtypeFailed) + val failedSilentStart = Statistics.startTimer(failedSilentNanos) + def stopStats() = { + Statistics.stopCounter(rawTypeFailed, rawTypeStart) + Statistics.stopCounter(findMemberFailed, findMemberStart) + Statistics.stopCounter(subtypeFailed, subtypeStart) + Statistics.stopTimer(failedSilentNanos, failedSilentStart) + } try { if (context.reportErrors || reportAmbiguousErrors != context.ambiguousErrors || @@ -724,8 +730,10 @@ trait Typers extends Modes with Adaptations with Tags { context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - if (context1.hasErrors) SilentTypeError(context1.errBuffer.head) - else SilentResultValue(result) + if (context1.hasErrors) { + stopStats() + SilentTypeError(context1.errBuffer.head) + } else SilentResultValue(result) } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") withSavedContext(context){ @@ -739,10 +747,7 @@ trait Typers extends Modes with Adaptations with Tags { case ex: TypeError => // fallback in case TypeError is still thrown // @H this happens for example in cps annotation checker - stopCounter(rawTypeFailed, rawTypeStart) - stopCounter(findMemberFailed, findMemberStart) - stopCounter(subtypeFailed, subtypeStart) - stopTimer(failedSilentNanos, failedSilentStart) + stopStats() SilentTypeError(TypeErrorWrapper(ex)) } } @@ -3481,9 +3486,9 @@ trait Typers extends Modes with Adaptations with Tags { def isCapturedExistential(sym: Symbol) = (sym hasAllFlags (EXISTENTIAL | CAPTURED)) && { - val start = startTimer(isReferencedNanos) + val start = Statistics.startTimer(isReferencedNanos) try !isReferencedFrom(context, sym) - finally stopTimer(isReferencedNanos, start) + finally Statistics.stopTimer(isReferencedNanos, start) } def packCaptured(tpe: Type): Type = { @@ -4110,10 +4115,10 @@ trait Typers extends Modes with Adaptations with Tags { * insert an implicit conversion. */ def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { - val start = startTimer(failedApplyNanos) + val start = Statistics.startTimer(failedApplyNanos) def onError(typeError: AbsTypeError): Tree = { - stopTimer(failedApplyNanos, start) + Statistics.stopTimer(failedApplyNanos, start) // If the problem is with raw types, copnvert to existentials and try again. // See #4712 for a case where this situation arises, @@ -4176,8 +4181,8 @@ trait Typers extends Modes with Adaptations with Tags { typed1(tree, mode & ~PATTERNmode | EXPRmode, pt) } else { val funpt = if (isPatternMode) pt else WildcardType - val appStart = startTimer(failedApplyNanos) - val opeqStart = startTimer(failedOpEqNanos) + val appStart = Statistics.startTimer(failedApplyNanos) + val opeqStart = Statistics.startTimer(failedOpEqNanos) def onError(reportError: => Tree): Tree = { fun match { @@ -4185,14 +4190,14 @@ trait Typers extends Modes with Adaptations with Tags { if !isPatternMode && nme.isOpAssignmentName(newTermName(name.decode)) => val qual1 = typedQualifier(qual) if (treeInfo.isVariableOrGetter(qual1)) { - stopTimer(failedOpEqNanos, opeqStart) + Statistics.stopTimer(failedOpEqNanos, opeqStart) convertToAssignment(fun, qual1, name, args) } else { - stopTimer(failedApplyNanos, appStart) + Statistics.stopTimer(failedApplyNanos, appStart) reportError } case _ => - stopTimer(failedApplyNanos, appStart) + Statistics.stopTimer(failedApplyNanos, appStart) reportError } } @@ -4201,7 +4206,7 @@ trait Typers extends Modes with Adaptations with Tags { if ((mode & EXPRmode) != 0) tree else context.tree) match { case SilentResultValue(fun1) => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 - incCounter(typedApplyCount) + Statistics.incCounter(typedApplyCount) def isImplicitMethod(tpe: Type) = tpe match { case mt: MethodType => mt.isImplicit case _ => false @@ -4977,7 +4982,7 @@ trait Typers extends Modes with Adaptations with Tags { typedSelect(qual1, nme.CONSTRUCTOR) case Select(qual, name) => - incCounter(typedSelectCount) + Statistics.incCounter(typedSelectCount) var qual1 = checkDead(typedQualifier(qual, mode)) if (name.isTypeName) qual1 = checkStable(qual1) @@ -5009,7 +5014,7 @@ trait Typers extends Modes with Adaptations with Tags { else tree1 case Ident(name) => - incCounter(typedIdentCount) + Statistics.incCounter(typedIdentCount) if ((name == nme.WILDCARD && (mode & (PATTERNmode | FUNmode)) == PATTERNmode) || (name == tpnme.WILDCARD && (mode & TYPEmode) != 0)) tree setType makeFullyDefined(pt) @@ -5084,15 +5089,9 @@ trait Typers extends Modes with Adaptations with Tags { indentTyping() var alreadyTyped = false + val startByType = Statistics.pushTimerClass(byTypeNanos, tree.getClass) + Statistics.incCounter(visitsByType, tree.getClass) try { - if (Statistics.enabled) { - val t = currentTime() - if (pendingTreeTypes.nonEmpty) { - microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt - } - typerTime = t - pendingTreeTypes = tree.getClass :: pendingTreeTypes - } if (context.retyping && (tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) { tree.tpe = null @@ -5146,14 +5145,7 @@ trait Typers extends Modes with Adaptations with Tags { } finally { deindentTyping() - - if (Statistics.enabled) { - val t = currentTime() - microsByType(pendingTreeTypes.head) += ((t - typerTime) / 1000).toInt - visitsByType(pendingTreeTypes.head) += 1 - typerTime = t - pendingTreeTypes = pendingTreeTypes.tail - } + Statistics.popTimerClass(byTypeNanos, startByType) } } @@ -5329,3 +5321,22 @@ trait Typers extends Modes with Adaptations with Tags { } } +object TypersStats { + import reflect.internal.TypesStats._ + import reflect.internal.BaseTypeSeqsStats._ + val typedIdentCount = Statistics.newCounter("#typechecked identifiers") + val typedSelectCount = Statistics.newCounter("#typechecked selections") + val typedApplyCount = Statistics.newCounter("#typechecked applications") + val rawTypeFailed = Statistics.newSubCounter (" of which in failed", rawTypeCount) + val subtypeFailed = Statistics.newSubCounter(" of which in failed", subtypeCount) + val findMemberFailed = Statistics.newSubCounter(" of which in failed", findMemberCount) + val compoundBaseTypeSeqCount = Statistics.newSubCounter(" of which for compound types", baseTypeSeqCount) + val typerefBaseTypeSeqCount = Statistics.newSubCounter(" of which for typerefs", baseTypeSeqCount) + val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) + val failedSilentNanos = Statistics.newSubTimer ("time spent in failed", typerNanos) + val failedApplyNanos = Statistics.newSubTimer (" failed apply", typerNanos) + val failedOpEqNanos = Statistics.newSubTimer (" failed op=", typerNanos) + val isReferencedNanos = Statistics.newSubTimer ("time spent ref scanning", typerNanos) + val visitsByType = Statistics.newByClass ("#visits by tree node", "typer")(Statistics.newCounter("")) + val byTypeNanos = Statistics.newByClassTimerStack("time spent by tree node", typerNanos) +} diff --git a/src/compiler/scala/tools/nsc/util/Statistics.scala b/src/compiler/scala/tools/nsc/util/Statistics.scala deleted file mode 100644 index 087111a7ba..0000000000 --- a/src/compiler/scala/tools/nsc/util/Statistics.scala +++ /dev/null @@ -1,200 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Martin Odersky - */ - -package scala.tools.nsc -package util - -class Statistics extends scala.reflect.internal.util.Statistics { - - var nodeByType = new ClassCounts - - var microsByType = new ClassCounts - var visitsByType = new ClassCounts - var pendingTreeTypes: List[Class[_]] = List() - var typerTime: Long = 0L - - val typedApplyCount = new Counter - val typedIdentCount = new Counter - val typedSelectCount = new Counter - val typerNanos = new Timer - val classReadNanos = new Timer - - val failedApplyNanos = new Timer - val failedOpEqNanos = new Timer - val failedSilentNanos = new Timer - - val implicitSearchCount = new Counter - val implicitNanos = new Timer - val oftypeImplicitHits = new Counter - val inscopeImplicitHits = new Counter - - val triedImplicits = new Counter - val plausiblyCompatibleImplicits = new Counter - val matchingImplicits = new Counter - val typedImplicits = new Counter - val foundImplicits = new Counter - - val inscopeSucceedNanos = new Timer - val inscopeFailNanos = new Timer - val oftypeSucceedNanos = new Timer - val oftypeFailNanos = new Timer - val implicitCacheHits = new Counter - val implicitCacheMisses = new Counter - val improvesCount = new Counter - val improvesCachedCount = new Counter - val subtypeAppInfos = new SubCounter(subtypeCount) - val subtypeImprovCount = new SubCounter(subtypeCount) - val subtypeETNanos = new Timer - val matchesPtNanos = new Timer - val isReferencedNanos = new Timer - val ctr1 = new Counter - val ctr2 = new Counter - val ctr3 = new Counter - val ctr4 = new Counter - val counter1: SubCounter = new SubCounter(subtypeCount) - val counter2: SubCounter = new SubCounter(subtypeCount) - val timer1: Timer = new Timer - val timer2: Timer = new Timer - - val macroExpandCount = new Counter - val macroExpandNanos = new Timer - - val patmatNanos = new Timer - val patmatAnaDPLL = new Timer - val patmatAnaVarEq = new Timer - val patmatCNF = new Timer - val patmatAnaExhaust = new Timer - val patmatAnaReach = new Timer - val patmatCNFSizes = new collection.mutable.HashMap[Int, Int] withDefaultValue 0 -} - -object Statistics extends Statistics - -abstract class StatisticsInfo { - - import Statistics._ - - val global: Global - import global._ - - var phasesShown = List("parser", "typer", "patmat", "erasure", "cleanup") - - def countNodes(tree: Tree, counts: ClassCounts) { - for (t <- tree) counts(t.getClass) += 1 - } - - def showRelative(base: Long)(value: Long) = - value+showPercent(value, base) - - def showRelTyper(timer: Timer) = - timer+showPercent(timer.nanos, typerNanos.nanos) - - def showRelPatmat(timer: Timer) = - timer+showPercent(timer.nanos, patmatNanos.nanos) - - def showCounts[T](counts: scala.collection.mutable.Map[T, Int]) = - counts.toSeq.sortWith(_._2 > _._2).map { - case (cls: Class[_], cnt) => - cls.toString.substring(cls.toString.lastIndexOf("$") + 1)+": "+cnt - case (o, cnt) => - o.toString +": "+cnt - } - - def print(phase: Phase) = if (phasesShown contains phase.name) { - inform("*** Cumulative statistics at phase " + phase) - inform("#created tree nodes : " + nodeCount) - inform("#created tree nodes by type: "+showCounts(nodeByType)) - if (phase.name != "parser") { - val counts = new ClassCounts - for (u <- currentRun.units; t <- u.body) counts(t.getClass) += 1 - inform("#retained nodes : " + counts.values.sum) - inform("#retained nodes by type : " + showCounts(counts)) - inform("#typechecked identifiers : " + typedIdentCount) - inform("#typechecked selections : " + typedSelectCount) - inform("#typechecked applications: " + typedApplyCount) - inform("#raw type creations : " + rawTypeCount) - inform(" of which in failed : " + rawTypeFailed) - inform(" of which in implicits : " + rawTypeImpl) - inform("#unique types : " + uniqueTypeCount) - inform("#symbols : " + symbolCount) - inform(" of which type symbols : " + typeSymbolCount) - inform(" of which class symbols : " + classSymbolCount) - inform("#base type seqs : " + baseTypeSeqCount) - inform("avg base type seq length : " + baseTypeSeqLenTotal.value.toFloat / baseTypeSeqCount.value) - inform("#singleton base type seqs: " + singletonBaseTypeSeqCount) - inform("#compound base type seqs : " + compoundBaseTypeSeqCount) - inform("#typeref base type seqs : " + typerefBaseTypeSeqCount) - inform("#findMember ops : " + findMemberCount) - inform(" of which in failed : " + findMemberFailed) - inform(" of which in implicits : " + findMemberImpl) - inform("#notfound member : " + noMemberCount) - inform("#multiple member : " + multMemberCount) - inform("#asSeenFrom ops : " + asSeenFromCount) - inform("#subtype : " + subtypeCount) - inform(" of which in failed : " + subtypeFailed) - inform(" of which in implicits : " + subtypeImpl) - inform(" of which in app impl : " + subtypeAppInfos) - inform(" of which in improv : " + subtypeImprovCount) - inform("#sametype : " + sametypeCount) - inform("#toplevel lub : " + lubCount) - inform("#all lub : " + nestedLubCount) - inform("ms type-flow-analysis: " + analysis.timer.millis) - - if (phase.name == "typer") { - inform("time spent typechecking : " + showRelTyper(typerNanos)) - inform("time classfilereading : " + showRelTyper(classReadNanos)) - inform("time spent in implicits : " + showRelTyper(implicitNanos)) - inform(" successful in scope : " + showRelTyper(inscopeSucceedNanos)) - inform(" failed in scope : " + showRelTyper(inscopeFailNanos)) - inform(" successful of type : " + showRelTyper(oftypeSucceedNanos)) - inform(" failed of type : " + showRelTyper(oftypeFailNanos)) - inform(" assembling parts : " + showRelTyper(subtypeETNanos)) - inform(" matchesPT : " + showRelTyper(matchesPtNanos)) - inform("implicit cache hits : " + showRelative(implicitCacheHits.value + implicitCacheMisses.value)(implicitCacheHits.value)) - inform("time spent in failed : " + showRelTyper(failedSilentNanos)) - inform(" failed apply : " + showRelTyper(failedApplyNanos)) - inform(" failed op= : " + showRelTyper(failedOpEqNanos)) - inform("time spent ref scanning : " + showRelTyper(isReferencedNanos)) - inform("time spent in lubs : " + showRelTyper(lubNanos)) - inform("micros by tree node : " + showCounts(microsByType)) - inform("#visits by tree node : " + showCounts(visitsByType)) - val average = new ClassCounts - for (c <- microsByType.keysIterator) average(c) = microsByType(c)/visitsByType(c) - inform("avg micros by tree node : " + showCounts(average)) - inform("time spent in <:< : " + showRelTyper(subtypeNanos)) - inform("time spent in findmember : " + showRelTyper(findMemberNanos)) - inform("time spent in asSeenFrom : " + showRelTyper(asSeenFromNanos)) - inform("#implicit searches : " + implicitSearchCount) - inform("#tried, plausible, matching, typed, found implicits: "+triedImplicits+", "+plausiblyCompatibleImplicits+", "+matchingImplicits+", "+typedImplicits+", "+foundImplicits) - inform("#implicit improves tests : " + improvesCount) - inform("#implicit improves cached : " + improvesCachedCount) - inform("#implicit inscope hits : " + inscopeImplicitHits) - inform("#implicit oftype hits : " + oftypeImplicitHits) - inform("#macro expansions : " + macroExpandCount) - inform("#time spent in macroExpand : " + showRelTyper(macroExpandNanos)) - } - - if (ctr1 != null) inform("#ctr1 : " + ctr1) - if (ctr2 != null) inform("#ctr2 : " + ctr2) - if (ctr3 != null) inform("#ctr3 : " + ctr3) - if (ctr4 != null) inform("#ctr4 : " + ctr4) - if (counter1 != null) inform("#counter1 : " + counter1) - if (counter2 != null) inform("#counter2 : " + counter2) - if (timer1 != null) inform("#timer1 : " + timer1) - if (timer2 != null) inform("#timer2 : " + timer2) - //for (t <- uniques.iterator) println("unique: "+t) - - if (phase.name == "patmat") { - inform("time spent in patmat : " + patmatNanos ) - inform(" of which DPLL : " + showRelPatmat(patmatAnaDPLL )) - inform("of which in CNF conversion : " + showRelPatmat(patmatCNF )) - inform(" CNF size counts : " + showCounts(patmatCNFSizes )) - inform("of which variable equality : " + showRelPatmat(patmatAnaVarEq )) - inform(" of which in exhaustivity : " + showRelPatmat(patmatAnaExhaust)) - inform("of which in unreachability : " + showRelPatmat(patmatAnaReach )) - } - } - } -} diff --git a/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala new file mode 100644 index 0000000000..f6a1ae1414 --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/StatisticsInfo.scala @@ -0,0 +1,38 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc +package util + +import reflect.internal.util.Statistics + +abstract class StatisticsInfo { + + val global: Global + import global._ + import reflect.internal.TreesStats.nodeByType + + val phasesShown = List("parser", "typer", "patmat", "erasure", "cleanup") + + val retainedCount = Statistics.newCounter("#retained tree nodes") + val retainedByType = Statistics.newByClass("#retained tree nodes by type")(Statistics.newCounter("")) + + def print(phase: Phase) = if (phasesShown contains phase.name) { + inform("*** Cumulative statistics at phase " + phase) + retainedCount.value = 0 + for (c <- retainedByType.keys) + retainedByType(c).value = 0 + for (u <- currentRun.units; t <- u.body) { + retainedCount.value += 1 + retainedByType(t.getClass).value += 1 + } + + val quants = + if (phase.name == "parser") Seq(treeNodeCount, nodeByType, retainedCount, retainedByType) + else Statistics.allQuantities + + for (q <- quants if q.showAt(phase.name)) inform(q.line) + } +} \ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 5f78671012..fa758edf05 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -8,7 +8,7 @@ package internal // todo implement in terms of BitSet import scala.collection.{ mutable, immutable } import math.max -import util.Statistics._ +import util.Statistics /** A base type sequence (BaseTypeSeq) is an ordered sequence spanning all the base types * of a type. It characterized by the following two laws: @@ -28,6 +28,7 @@ import util.Statistics._ trait BaseTypeSeqs { this: SymbolTable => import definitions._ + import BaseTypeSeqsStats._ protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = new BaseTypeSeq(parents, elems) @@ -38,8 +39,8 @@ trait BaseTypeSeqs { */ class BaseTypeSeq protected[BaseTypeSeqs] (private[BaseTypeSeqs] val parents: List[Type], private[BaseTypeSeqs] val elems: Array[Type]) { self => - incCounter(baseTypeSeqCount) - incCounter(baseTypeSeqLenTotal, elems.length) + Statistics.incCounter(baseTypeSeqCount) + Statistics.incCounter(baseTypeSeqLenTotal, elems.length) /** The number of types in the sequence */ def length: Int = elems.length @@ -231,3 +232,8 @@ trait BaseTypeSeqs { val CyclicInheritance = new Throwable } + +object BaseTypeSeqsStats { + val baseTypeSeqCount = Statistics.newCounter("#base type seqs") + val baseTypeSeqLenTotal = Statistics.newRelCounter("avg base type seq length", baseTypeSeqCount) +} diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 288eb63332..4b0ceeb86b 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -8,18 +8,17 @@ package internal import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer -import util.Statistics._ +import util.Statistics import Flags._ trait Symbols extends api.Symbols { self: SymbolTable => import definitions._ + import SymbolsStats._ protected var ids = 0 val emptySymbolArray = new Array[Symbol](0) - def symbolCount = ids // statistics - protected def nextId() = { ids += 1; ids } /** Used for deciding in the IDE whether we can interrupt the compiler */ @@ -666,7 +665,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def isClassLocalToConstructor = false final def isDerivedValueClass = - isClass && !hasFlag(PACKAGE | TRAIT) && + isClass && !hasFlag(PACKAGE | TRAIT) && info.firstParent.typeSymbol == AnyValClass && !isPrimitiveValueClass final def isMethodWithExtension = @@ -2713,7 +2712,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } } - incCounter(typeSymbolCount) + Statistics.incCounter(typeSymbolCount) } implicit val TypeSymbolTag = ClassTag[TypeSymbol](classOf[TypeSymbol]) @@ -2929,7 +2928,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def children = childSet override def addChild(sym: Symbol) { childSet = childSet + sym } - incCounter(classSymbolCount) + Statistics.incCounter(classSymbolCount) } implicit val ClassSymbolTag = ClassTag[ClassSymbol](classOf[ClassSymbol]) @@ -3188,4 +3187,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) } + + Statistics.newView("#symbols")(ids) +} + +object SymbolsStats { + val typeSymbolCount = Statistics.newCounter("#type symbols") + val classSymbolCount = Statistics.newCounter("#class symbols") } diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index 75bb0e6d49..e100e0c1a3 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -9,6 +9,7 @@ package internal import Flags._ import base.Attachments import collection.mutable.{ListBuffer, LinkedHashSet} +import util.Statistics trait Trees extends api.Trees { self: SymbolTable => @@ -18,6 +19,8 @@ trait Trees extends api.Trees { self: SymbolTable => val id = nodeCount // TODO: add to attachment? nodeCount += 1 + Statistics.incCounter(TreesStats.nodeByType, getClass) + @inline final def pos: Position = rawatt.pos def pos_=(pos: Position): Unit = rawatt = (rawatt withPos pos) def setPos(newpos: Position): this.type = { pos = newpos; this } @@ -1592,4 +1595,11 @@ trait Trees extends api.Trees { self: SymbolTable => implicit val TypeBoundsTreeTag = ClassTag[TypeBoundsTree](classOf[TypeBoundsTree]) implicit val ExistentialTypeTreeTag = ClassTag[ExistentialTypeTree](classOf[ExistentialTypeTree]) implicit val TypeTreeTag = ClassTag[TypeTree](classOf[TypeTree]) + + val treeNodeCount = Statistics.newView("#created tree nodes")(nodeCount) +} + +object TreesStats { + // statistics + val nodeByType = Statistics.newByClass("#created tree nodes by type")(Statistics.newCounter("")) } diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 8a4394bf1d..d4b895bcb4 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -13,8 +13,7 @@ import mutable.ListBuffer import Flags._ import scala.util.control.ControlThrowable import scala.annotation.tailrec -import util.Statistics._ -import language.postfixOps +import util.Statistics /* A standard type pattern match: case ErrorType => @@ -73,9 +72,7 @@ import language.postfixOps trait Types extends api.Types { self: SymbolTable => import definitions._ - - //statistics - def uniqueTypeCount = if (uniques == null) 0 else uniques.size + import TypesStats._ private var explainSwitch = false private final val emptySymbolSet = immutable.Set.empty[Symbol] @@ -681,8 +678,8 @@ trait Types extends api.Types { self: SymbolTable => if (isTrivial || phase.erasedTypes && pre.typeSymbol != ArrayClass) this else { // scala.tools.nsc.util.trace.when(pre.isInstanceOf[ExistentialType])("X "+this+".asSeenfrom("+pre+","+clazz+" = ") { - incCounter(asSeenFromCount) - val start = startTimer(asSeenFromNanos) + Statistics.incCounter(asSeenFromCount) + val start = Statistics.startTimer(asSeenFromNanos) val m = new AsSeenFromMap(pre.normalize, clazz) val tp = m apply this val tp1 = existentialAbstraction(m.capturedParams, tp) @@ -690,7 +687,7 @@ trait Types extends api.Types { self: SymbolTable => if (m.capturedSkolems.isEmpty) tp1 else deriveType(m.capturedSkolems, _.cloneSymbol setFlag CAPTURED)(tp1) - stopTimer(asSeenFromNanos, start) + Statistics.stopTimer(asSeenFromNanos, start) result } } @@ -828,26 +825,26 @@ trait Types extends api.Types { self: SymbolTable => } def stat_<:<(that: Type): Boolean = { - incCounter(subtypeCount) - val start = startTimer(subtypeNanos) + Statistics.incCounter(subtypeCount) + val start = Statistics.startTimer(subtypeNanos) val result = (this eq that) || (if (explainSwitch) explain("<:", isSubType, this, that) else isSubType(this, that, AnyDepth)) - stopTimer(subtypeNanos, start) + Statistics.stopTimer(subtypeNanos, start) result } /** Is this type a weak subtype of that type? True also for numeric types, i.e. Int weak_<:< Long. */ def weak_<:<(that: Type): Boolean = { - incCounter(subtypeCount) - val start = startTimer(subtypeNanos) + Statistics.incCounter(subtypeCount) + val start = Statistics.startTimer(subtypeNanos) val result = ((this eq that) || (if (explainSwitch) explain("weak_<:", isWeakSubType, this, that) else isWeakSubType(this, that))) - stopTimer(subtypeNanos, start) + Statistics.stopTimer(subtypeNanos, start) result } @@ -1020,8 +1017,8 @@ trait Types extends api.Types { self: SymbolTable => // See (t0851) for a situation where this happens. val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) - incCounter(findMemberCount) - val start = startTimer(findMemberNanos) + Statistics.incCounter(findMemberCount) + val start = Statistics.startTimer(findMemberNanos) //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG var members: Scope = null @@ -1048,7 +1045,7 @@ trait Types extends api.Types { self: SymbolTable => !sym.isPrivateLocal || (bcs0.head.hasTransOwner(bcs.head)))) { if (name.isTypeName || stableOnly && sym.isStable) { - stopTimer(findMemberNanos, start) + Statistics.stopTimer(findMemberNanos, start) if (suspension ne null) suspension foreach (_.suspended = false) return sym } else if (member == NoSymbol) { @@ -1094,13 +1091,13 @@ trait Types extends api.Types { self: SymbolTable => } // while (!bcs.isEmpty) excluded = excludedFlags } // while (continue) - stopTimer(findMemberNanos, start) + Statistics.stopTimer(findMemberNanos, start) if (suspension ne null) suspension foreach (_.suspended = false) if (members eq null) { - if (member == NoSymbol) incCounter(noMemberCount) + if (member == NoSymbol) Statistics.incCounter(noMemberCount) member } else { - incCounter(multMemberCount) + Statistics.incCounter(multMemberCount) baseClasses.head.newOverloaded(this, members.toList) } } @@ -1185,7 +1182,7 @@ trait Types extends api.Types { self: SymbolTable => override def isVolatile = underlying.isVolatile override def widen: Type = underlying.widen override def baseTypeSeq: BaseTypeSeq = { - incCounter(singletonBaseTypeSeqCount) + Statistics.incCounter(singletonBaseTypeSeqCount) underlying.baseTypeSeq prepend this } override def isHigherKinded = false // singleton type classifies objects, thus must be kind * @@ -1536,7 +1533,7 @@ trait Types extends api.Types { self: SymbolTable => val bts = copyRefinedType(tpe.asInstanceOf[RefinedType], tpe.parents map varToParam, varToParam mapOver tpe.decls).baseTypeSeq tpe.baseTypeSeqCache = bts lateMap paramToVar } else { - incCounter(compoundBaseTypeSeqCount) + Statistics.incCounter(compoundBaseTypeSeqCount) tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = if (tpe.typeSymbol.isRefinementClass) tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe) @@ -2392,7 +2389,7 @@ trait Types extends api.Types { self: SymbolTable => if (period != currentPeriod) { tpe.baseTypeSeqPeriod = currentPeriod if (!isValidForBaseClasses(period)) { - incCounter(typerefBaseTypeSeqCount) + Statistics.incCounter(typerefBaseTypeSeqCount) tpe.baseTypeSeqCache = undetBaseTypeSeq tpe.baseTypeSeqCache = tpe.baseTypeSeqImpl } @@ -3702,7 +3699,7 @@ trait Types extends api.Types { self: SymbolTable => private var uniqueRunId = NoRunId protected def unique[T <: Type](tp: T): T = { - incCounter(rawTypeCount) + Statistics.incCounter(rawTypeCount) if (uniqueRunId != currentRunId) { uniques = util.HashSet[Type]("uniques", initialUniquesCapacity) uniqueRunId = currentRunId @@ -5104,7 +5101,7 @@ trait Types extends api.Types { self: SymbolTable => /** Do `tp1` and `tp2` denote equivalent types? */ def isSameType(tp1: Type, tp2: Type): Boolean = try { - incCounter(sametypeCount) + Statistics.incCounter(sametypeCount) subsametypeRecursions += 1 undoLog undoUnless { isSameType1(tp1, tp2) @@ -6308,14 +6305,14 @@ trait Types extends api.Types { self: SymbolTable => case List() => NothingClass.tpe case List(t) => t case _ => - incCounter(lubCount) - val start = startTimer(lubNanos) + Statistics.incCounter(lubCount) + val start = Statistics.startTimer(lubNanos) try { lub(ts, lubDepth(ts)) } finally { lubResults.clear() glbResults.clear() - stopTimer(lubNanos, start) + Statistics.stopTimer(lubNanos, start) } } @@ -6431,7 +6428,7 @@ trait Types extends api.Types { self: SymbolTable => indent = indent + " " assert(indent.length <= 100) } - incCounter(nestedLubCount) + Statistics.incCounter(nestedLubCount) val res = lub0(ts) if (printLubs) { indent = indent stripSuffix " " @@ -6456,14 +6453,14 @@ trait Types extends api.Types { self: SymbolTable => case List() => AnyClass.tpe case List(t) => t case ts0 => - incCounter(lubCount) - val start = startTimer(lubNanos) + Statistics.incCounter(lubCount) + val start = Statistics.startTimer(lubNanos) try { glbNorm(ts0, lubDepth(ts0)) } finally { lubResults.clear() glbResults.clear() - stopTimer(lubNanos, start) + Statistics.stopTimer(lubNanos, start) } } @@ -6578,7 +6575,7 @@ trait Types extends api.Types { self: SymbolTable => } // if (settings.debug.value) { println(indent + "glb of " + ts + " at depth "+depth); indent = indent + " " } //DEBUG - incCounter(nestedLubCount) + Statistics.incCounter(nestedLubCount) val res = glb0(ts) // if (settings.debug.value) { indent = indent.substring(0, indent.length() - 2); log(indent + "glb of " + ts + " is " + res) }//DEBUG @@ -6871,4 +6868,27 @@ trait Types extends api.Types { self: SymbolTable => implicit val TypeBoundsTag = ClassTag[TypeBounds](classOf[TypeBounds]) implicit val TypeRefTag = ClassTag[TypeRef](classOf[TypeRef]) implicit val TypeTagg = ClassTag[Type](classOf[Type]) + + Statistics.newView("#unique types") { if (uniques == null) 0 else uniques.size } +} + +object TypesStats { + import BaseTypeSeqsStats._ + val rawTypeCount = Statistics.newCounter ("#raw type creations") + val asSeenFromCount = Statistics.newCounter ("#asSeenFrom ops") + val subtypeCount = Statistics.newCounter ("#subtype ops") + val sametypeCount = Statistics.newCounter ("#sametype ops") + val lubCount = Statistics.newCounter ("#toplevel lubs/glbs") + val nestedLubCount = Statistics.newCounter ("#all lubs/glbs") + val findMemberCount = Statistics.newCounter ("#findMember ops") + val noMemberCount = Statistics.newSubCounter(" of which not found", findMemberCount) + val multMemberCount = Statistics.newSubCounter(" of which multiple overloaded", findMemberCount) + val typerNanos = Statistics.newTimer ("time spent typechecking", "typer") + val lubNanos = Statistics.newSubTimer ("time spent in lubs", typerNanos) + val subtypeNanos = Statistics.newSubTimer ("time spent in <:<", typerNanos) + val findMemberNanos = Statistics.newSubTimer ("time spent in findmember", typerNanos) + val asSeenFromNanos = Statistics.newSubTimer ("time spent in asSeenFrom", typerNanos) + val compoundBaseTypeSeqCount = Statistics.newSubCounter(" of which for compound types", baseTypeSeqCount) + val typerefBaseTypeSeqCount = Statistics.newSubCounter(" of which for typerefs", baseTypeSeqCount) + val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) } diff --git a/src/reflect/scala/reflect/internal/util/StatBase.scala b/src/reflect/scala/reflect/internal/util/StatBase.scala deleted file mode 100644 index b033ff98bc..0000000000 --- a/src/reflect/scala/reflect/internal/util/StatBase.scala +++ /dev/null @@ -1,97 +0,0 @@ -package scala.reflect.internal.util - -class StatBase { - - private var _enabled = false - - def enabled = _enabled - def enabled_=(cond: Boolean) = { - if (cond && !_enabled) { - val test = new Timer() - val start = System.nanoTime() - var total = 0L - for (i <- 1 to 10000) { - val time = System.nanoTime() - total += System.nanoTime() - time - } - val total2 = System.nanoTime() - start - println("Enabling statistics, measuring overhead = "+ - total/10000.0+"ns to "+total2/10000.0+"ns per timer") - _enabled = true - } - } - - def currentTime() = - if (_enabled) System.nanoTime() else 0L - - def showPercent(x: Double, base: Double) = - if (base == 0) "" else " ("+"%2.1f".format(x / base * 100)+"%)" - - def incCounter(c: Counter) { - if (_enabled) c.value += 1 - } - - def incCounter(c: Counter, delta: Int) { - if (_enabled) c.value += delta - } - - def startCounter(sc: SubCounter): IntPair = - if (_enabled) sc.start() else null - - def stopCounter(sc: SubCounter, start: IntPair) { - if (_enabled) sc.stop(start) - } - - def startTimer(tm: Timer): LongPair = - if (_enabled) tm.start() else null - - def stopTimer(tm: Timer, start: LongPair) { - if (_enabled) tm.stop(start) - } - - case class IntPair(x: Int, y: Int) - case class LongPair(x: Long, y: Long) - - class Counter { - var value: Int = 0 - override def toString = value.toString - } - - class SubCounter(c: Counter) { - var value: Int = 0 - def start(): IntPair = - if (_enabled) IntPair(value, c.value) else null - def stop(prev: IntPair) { - if (_enabled) { - val IntPair(value0, cvalue0) = prev - value = value0 + c.value - cvalue0 - } - } - override def toString = - value+showPercent(value, c.value) - } - - class Timer { - var nanos: Long = 0 - var timings = 0 - def start(): LongPair = - if (_enabled) { - timings += 1 - LongPair(nanos, System.nanoTime()) - } else null - def stop(prev: LongPair) { - if (_enabled) { - val LongPair(nanos0, start) = prev - nanos = nanos0 + System.nanoTime() - start - timings += 1 - } - } - override def toString = (timings/2)+" spans, "+nanos.toString+"ns" - } - - import Predef.Class - - class ClassCounts extends scala.collection.mutable.HashMap[Class[_], Int] { - override def default(key: Class[_]) = 0 - } -} \ No newline at end of file diff --git a/src/reflect/scala/reflect/internal/util/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala index de0830aa3a..57c9e98174 100644 --- a/src/reflect/scala/reflect/internal/util/Statistics.scala +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala @@ -1,37 +1,232 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Martin Odersky - */ package scala.reflect.internal.util -class Statistics extends StatBase { - val singletonBaseTypeSeqCount = new Counter - val compoundBaseTypeSeqCount = new Counter - val typerefBaseTypeSeqCount = new Counter - val findMemberCount = new Counter - val noMemberCount = new Counter - val multMemberCount = new Counter - val findMemberNanos = new Timer - val asSeenFromCount = new Counter - val asSeenFromNanos = new Timer - val subtypeCount = new Counter - val subtypeNanos = new Timer - val sametypeCount = new Counter - val rawTypeCount = new Counter - val rawTypeFailed = new SubCounter(rawTypeCount) - val findMemberFailed = new SubCounter(findMemberCount) - val subtypeFailed = new SubCounter(subtypeCount) - val rawTypeImpl = new SubCounter(rawTypeCount) - val findMemberImpl = new SubCounter(findMemberCount) - val subtypeImpl = new SubCounter(subtypeCount) - val baseTypeSeqCount = new Counter - val baseTypeSeqLenTotal = new Counter - val typeSymbolCount = new Counter - val classSymbolCount = new Counter - val lubCount = new Counter - val nestedLubCount = new Counter - val lubNanos = new Timer -} +import collection.mutable + +object Statistics { + + /** If enabled, increment counter by one */ + @inline final def incCounter(c: Counter) { + if (_enabled && c != null) c.value += 1 + } + + /** If enabled, increment counter by given delta */ + @inline final def incCounter(c: Counter, delta: Int) { + if (_enabled && c != null) c.value += delta + } + + /** If enabled, increment counter in map `ctrs` at index `key` by one */ + @inline final def incCounter[K](ctrs: QuantMap[K, Counter], key: K) = + if (_enabled && ctrs != null) ctrs(key).value += 1 + + /** If enabled, start subcounter. While active it will track all increments of + * its base counter. + */ + @inline final def startCounter(sc: SubCounter): (Int, Int) = + if (_enabled && sc != null) sc.start() else null + + /** If enabled, stop subcounter from tracking its base counter. */ + @inline final def stopCounter(sc: SubCounter, start: (Int, Int)) { + if (_enabled && sc != null) sc.stop(start) + } + + /** If enabled, start timer */ + @inline final def startTimer(tm: Timer): (Long, Long) = + if (_enabled && tm != null) tm.start() else null + + /** If enabled, stop timer */ + @inline final def stopTimer(tm: Timer, start: (Long, Long)) { + if (_enabled && tm != null) tm.stop(start) + } + + /** If enabled, push and start a new timer in timer stack */ + @inline final def pushTimerClass(timers: ByClassTimerStack, cls: Class[_]): (Long, Long) = + if (_enabled && timers != null) timers.push(cls) else null + + /** If enabled, stop and pop timer from timer stack */ + @inline final def popTimerClass(timers: ByClassTimerStack, prev: (Long, Long)) { + if (_enabled && timers != null) timers.pop(prev) + } + + /** Create a new counter that shows as `prefix` and is active in given phases */ + def newCounter(prefix: String, phases: String*) = new Counter(prefix, phases) + + /** Create a new relative counter that shows as `prefix` and is active + * in the same phases as its base counter. Relative counters print as percentages + * of their base counters. + */ + def newRelCounter(prefix: String, ctr: Counter): Counter = new RelCounter(prefix, ctr) + + /** Create a new subcounter that shows as `prefix` and is active + * in the same phases as its base counter. Subcounters can track + * increments of their base counters and print as percentages + * of their base counters. + */ + def newSubCounter(prefix: String, ctr: Counter): SubCounter = new SubCounter(prefix, ctr) + + /** Create a new counter that shows as `prefix` and is active in given phases */ + def newTimer(prefix: String, phases: String*): Timer = new Timer(prefix, phases) + + /** Create a new subtimer that shows as `prefix` and is active + * in the same phases as its base timer. Subtimers can track + * increments of their base timers and print as percentages + * of their base timers. + */ + def newSubTimer(prefix: String, timer: Timer): Timer = new SubTimer(prefix, timer) + + /** Create a new view that shows as `prefix` and is active in given phases. + * The view always reflects the current value of `quant` as a quantity. + */ + def newView(prefix: String, phases: String*)(quant: => Any): View = new View(prefix, phases, +quant) -object Statistics extends Statistics + /** Create a new quantity map that shows as `prefix` and is active in given phases. + */ + def newQuantMap[K, V <% Ordered[V]](prefix: String, phases: String*)(initValue: => V): QuantMap[K, V] = new QuantMap(prefix, phases, initValue) + /** Same as newQuantMap, where the key type is fixed to be Class[_] */ + def newByClass[V <% Ordered[V]](prefix: String, phases: String*)(initValue: => V): QuantMap[Class[_], V] = new QuantMap(prefix, phases, initValue) + + /** Create a new timer stack map, indexed by Class[_]. */ + def newByClassTimerStack(prefix: String, underlying: Timer) = new ByClassTimerStack(prefix, underlying) + + def allQuantities: Iterable[Quantity] = + for ((q, _) <- qs if !q.isInstanceOf[SubQuantity]; + r <- q :: q.children.toList if r.prefix.nonEmpty) yield r + + private def showPercent(x: Double, base: Double) = + if (base == 0) "" else f" (${x / base * 100}%2.1f%)" + + trait Quantity { + qs += (this -> ()) + val prefix: String + val phases: Seq[String] + def showAt(phase: String) = phases.isEmpty || (phases contains phase) + def line = f"$prefix%-30s: ${this}" + val children = new mutable.ListBuffer[Quantity] + } + + trait SubQuantity extends Quantity { + protected def underlying: Quantity + underlying.children += this + } + + class Counter(val prefix: String, val phases: Seq[String]) extends Quantity with Ordered[Counter] { + var value: Int = 0 + def compare(that: Counter): Int = + if (this.value < that.value) -1 + else if (this.value > that.value) 1 + else 0 + override def toString = value.toString + } + + class View(val prefix: String, val phases: Seq[String], quant: => Any) extends Quantity { + override def toString = quant.toString + } + + private class RelCounter(prefix: String, val underlying: Counter) extends Counter(prefix, underlying.phases) with SubQuantity { + override def toString = + if (value == 0) "0" + else { + assert(underlying.value != 0, prefix+"/"+underlying.line) + f"${value.toFloat / underlying.value}%2.1f" + } + } + + class SubCounter(prefix: String, override val underlying: Counter) extends Counter(prefix, underlying.phases) with SubQuantity { + def start() = (value, underlying.value) + def stop(prev: (Int, Int)) { + val (value0, uvalue0) = prev + value = value0 + underlying.value - uvalue0 + } + override def toString = + value + showPercent(value, underlying.value) + } + + class Timer(val prefix: String, val phases: Seq[String]) extends Quantity with Ordered[Timer] { + var nanos: Long = 0 + var timings = 0 + def compare(that: Timer): Int = + if (this.nanos < that.nanos) -1 + else if (this.nanos > that.nanos) 1 + else 0 + def start() = { + (nanos, System.nanoTime()) + } + def stop(prev: (Long, Long)) { + val (nanos0, start) = prev + nanos = nanos0 + System.nanoTime() - start + timings += 1 + } + override def toString = s"$timings spans, ${nanos/1000}ms" + } + + private class SubTimer(prefix: String, override val underlying: Timer) extends Timer(prefix, underlying.phases) with SubQuantity { + override def toString: String = super.toString + showPercent(nanos, underlying.nanos) + } + + /** A mutable map quantity where missing elements are automatically inserted + * on access by executing `initValue`. + */ + class QuantMap[K, V <% Ordered[V]](val prefix: String, val phases: Seq[String], initValue: => V) + extends scala.collection.mutable.HashMap[K, V] with Quantity { + override def default(key: K) = { + val elem = initValue + this(key) = elem + elem + } + override def toString = + this.toSeq.sortWith(_._2 > _._2).map { + case (cls: Class[_], elem) => + s"${cls.toString.substring(cls.toString.lastIndexOf("$") + 1)}: $elem" + case (key, elem) => + s"$key: $elem" + }.mkString(", ") + } + + /** A mutable map quantity that takes class keys to subtimer values, relative to + * some `underlying` timer. In addition, class timers can be pushed and popped. + * Pushing the timer for a class means stopping the currently active timer. + */ + class ByClassTimerStack(prefix: String, val underlying: Timer) + extends QuantMap[Class[_], Timer](prefix, underlying.phases, new SubTimer("", underlying)) with SubQuantity { + private var elems: List[(Timer, Long)] = Nil + def push(cls: Class[_]): (Long, Long) = { + val topTimer = this(cls) + elems = (topTimer, 0L) :: elems + topTimer.start() + } + def pop(prev: (Long, Long)) = { + val (nanos0, start) = prev + val duration = System.nanoTime() - start + val (topTimer, nestedNanos) :: rest = elems + topTimer.nanos = nanos0 + duration - nestedNanos + topTimer.timings += 1 + elems = rest match { + case (outerTimer, outerNested) :: elems1 => + (outerTimer, outerNested + duration) :: elems1 + case Nil => + Nil + } + } + } + + private var _enabled = false + private val qs = new mutable.WeakHashMap[Quantity, Unit] + + def enabled = _enabled + def enabled_=(cond: Boolean) = { + if (cond && !_enabled) { + val test = new Timer("", Nil) + val start = System.nanoTime() + var total = 0L + for (i <- 1 to 10000) { + val time = System.nanoTime() + total += System.nanoTime() - time + } + val total2 = System.nanoTime() - start + println("Enabling statistics, measuring overhead = "+ + total/10000.0+"ns to "+total2/10000.0+"ns per timer") + _enabled = true + } + } +} -- cgit v1.2.3 From 10b44339c5fb4c35b57f5675a924b3c5de871c20 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 13 Jun 2012 09:28:16 -0700 Subject: Updated scalacheck sources. To current scalacheck head 7ffda752d8 except for this diff: diff -rw src/scalacheck/org/scalacheck/Arbitrary.scala /s/scalacheck/src/main/scala/org/scalacheck/Arbitrary.scala 13d12 < import scala.reflect.ClassTag 281c280 < implicit def arbArray[T](implicit a: Arbitrary[T], c: ClassTag[T] --- > implicit def arbArray[T](implicit a: Arbitrary[T], c: ClassManifest[T] diff -rw src/scalacheck/org/scalacheck/Prop.scala /s/scalacheck/src/main/scala/org/scalacheck/Prop.scala 63c63 < def mainCallsExit = false --- > def mainCallsExit = true Only in /s/scalacheck/src/main/scala/org/scalacheck: ScalaCheckFramework.scala diff -rw src/scalacheck/org/scalacheck/util/Buildable.scala /s/scalacheck/src/main/scala/org/scalacheck/util/Buildable.scala 13d12 < import scala.reflect.ClassTag 34c33 < implicit def buildableArray[T](implicit cm: ClassTag[T]) = --- > implicit def buildableArray[T](implicit cm: ClassManifest[T]) = --- src/scalacheck/org/scalacheck/Arbitrary.scala | 36 ++-- src/scalacheck/org/scalacheck/Arg.scala | 2 +- src/scalacheck/org/scalacheck/Commands.scala | 23 +-- .../org/scalacheck/ConsoleReporter.scala | 27 +-- src/scalacheck/org/scalacheck/Gen.scala | 50 +----- src/scalacheck/org/scalacheck/Pretty.scala | 4 +- src/scalacheck/org/scalacheck/Prop.scala | 36 ++-- src/scalacheck/org/scalacheck/Properties.scala | 2 +- src/scalacheck/org/scalacheck/Shrink.scala | 2 +- src/scalacheck/org/scalacheck/Test.scala | 191 ++++++++------------- src/scalacheck/org/scalacheck/util/Buildable.scala | 4 +- .../org/scalacheck/util/CmdLineParser.scala | 9 +- src/scalacheck/org/scalacheck/util/FreqMap.scala | 2 +- src/scalacheck/org/scalacheck/util/StdRand.scala | 2 +- test/files/lib/scalacheck.jar.desired.sha1 | 2 +- test/files/scalacheck/CheckEither.scala | 1 - 16 files changed, 145 insertions(+), 248 deletions(-) diff --git a/src/scalacheck/org/scalacheck/Arbitrary.scala b/src/scalacheck/org/scalacheck/Arbitrary.scala index 28e116b479..8c43cdaafe 100644 --- a/src/scalacheck/org/scalacheck/Arbitrary.scala +++ b/src/scalacheck/org/scalacheck/Arbitrary.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck @@ -178,9 +178,10 @@ object Arbitrary { import java.math.MathContext._ val mcGen = oneOf(UNLIMITED, DECIMAL32, DECIMAL64, DECIMAL128) val bdGen = for { - mc <- mcGen - scale <- arbInt.arbitrary x <- arbBigInt.arbitrary + mc <- mcGen + limit <- value(if(mc == UNLIMITED) 0 else math.max(x.abs.toString.length - mc.getPrecision, 0)) + scale <- Gen.chooseNum(Int.MinValue + limit , Int.MaxValue) } yield BigDecimal(x, scale, mc) Arbitrary(bdGen) } @@ -197,24 +198,37 @@ object Arbitrary { } /** Generates an arbitrary property */ - implicit lazy val arbProp: Arbitrary[Prop] = + implicit lazy val arbProp: Arbitrary[Prop] = { + import Prop._ + val undecidedOrPassed = forAll { b: Boolean => + b ==> true + } Arbitrary(frequency( - (5, Prop.proved), - (4, Prop.falsified), - (2, Prop.undecided), - (1, Prop.exception(null)) + (4, falsified), + (4, passed), + (3, proved), + (3, undecidedOrPassed), + (2, undecided), + (1, exception(null)) )) + } /** Arbitrary instance of test params */ implicit lazy val arbTestParams: Arbitrary[Test.Params] = Arbitrary(for { - minSuccTests <- choose(10,150) - maxDiscTests <- choose(100,500) + minSuccTests <- choose(10,200) + maxDiscardRatio <- choose(0.2f,10f) minSize <- choose(0,500) sizeDiff <- choose(0,500) maxSize <- choose(minSize, minSize + sizeDiff) ws <- choose(1,4) - } yield Test.Params(minSuccTests,maxDiscTests,minSize,maxSize,workers = ws)) + } yield Test.Params( + minSuccessfulTests = minSuccTests, + maxDiscardRatio = maxDiscardRatio, + minSize = minSize, + maxSize = maxSize, + workers = ws + )) /** Arbitrary instance of gen params */ implicit lazy val arbGenParams: Arbitrary[Gen.Params] = diff --git a/src/scalacheck/org/scalacheck/Arg.scala b/src/scalacheck/org/scalacheck/Arg.scala index 908bce2a81..8959211f09 100644 --- a/src/scalacheck/org/scalacheck/Arg.scala +++ b/src/scalacheck/org/scalacheck/Arg.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck diff --git a/src/scalacheck/org/scalacheck/Commands.scala b/src/scalacheck/org/scalacheck/Commands.scala index 112dda28a7..5ad82c513d 100644 --- a/src/scalacheck/org/scalacheck/Commands.scala +++ b/src/scalacheck/org/scalacheck/Commands.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck @@ -46,13 +46,6 @@ trait Commands extends Prop { def run(s: State): Any def nextState(s: State): State - /** @deprecated Use preConditions += ... instead. */ - @deprecated("Use 'preConditions += ...' instead.", "1.6") - def preCondition_=(f: State => Boolean) = { - preConditions.clear - preConditions += f - } - /** Returns all preconditions merged into a single function */ def preCondition: (State => Boolean) = s => preConditions.toList.forall(_.apply(s)) @@ -62,20 +55,6 @@ trait Commands extends Prop { * conditions to the precondition list */ val preConditions = new collection.mutable.ListBuffer[State => Boolean] - /** @deprecated Use postConditions += ... instead. */ - @deprecated("Use 'postConditions += ...' instead.", "1.6") - def postCondition_=(f: (State,Any) => Prop) = { - postConditions.clear - postConditions += ((s0,s1,r) => f(s0,r)) - } - - /** @deprecated Use postConditions += ... instead. */ - @deprecated("Use 'postConditions += ...' instead.", "1.6") - def postCondition_=(f: (State,State,Any) => Prop) = { - postConditions.clear - postConditions += f - } - /** Returns all postconditions merged into a single function */ def postCondition: (State,State,Any) => Prop = (s0,s1,r) => all(postConditions.map(_.apply(s0,s1,r)): _*) diff --git a/src/scalacheck/org/scalacheck/ConsoleReporter.scala b/src/scalacheck/org/scalacheck/ConsoleReporter.scala index c3af6c83a3..93f1dc222e 100644 --- a/src/scalacheck/org/scalacheck/ConsoleReporter.scala +++ b/src/scalacheck/org/scalacheck/ConsoleReporter.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck @@ -37,31 +37,6 @@ object ConsoleReporter { * the given verbosity */ def apply(verbosity: Int = 0) = new ConsoleReporter(verbosity) - @deprecated("(v1.8)", "1.8") - def propReport(s: Int, d: Int) = { - if(d == 0) printf("\rPassed %s tests\r", s) - else printf("\rPassed %s tests; %s discarded\r", s, d) - Console.flush - } - - @deprecated("(v1.8)", "1.8") - def propReport(pName: String, s: Int, d: Int) = { - if(d == 0) printf("\r %s: Passed %s tests\r", pName, s) - else printf("\r %s: Passed %s tests; %s discarded\r", pName, s, d) - Console.flush - } - - @deprecated("(v1.8)", "1.8") - def testReport(res: Test.Result) = { - print(List.fill(78)(' ').mkString) - val s = (if(res.passed) "+ " else "! ") + pretty(res, Params(0)) - printf("\r%s\n", format(s, "", "", 75)) - res - } - - @deprecated("(v1.8)", "1.8") - def testStatsEx(res: Test.Result): Unit = testStatsEx("", res) - def testStatsEx(msg: String, res: Test.Result) = { lazy val m = if(msg.length == 0) "" else msg + ": " res.status match { diff --git a/src/scalacheck/org/scalacheck/Gen.scala b/src/scalacheck/org/scalacheck/Gen.scala index a253b040cd..64bb61c2d3 100644 --- a/src/scalacheck/org/scalacheck/Gen.scala +++ b/src/scalacheck/org/scalacheck/Gen.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck @@ -59,6 +59,12 @@ object Choose { } } +case class FiniteGenRes[+T]( + r: T +) + +sealed trait FiniteGen[+T] extends Gen[FiniteGenRes[T]] + /** Class that represents a generator. */ sealed trait Gen[+T] { @@ -148,13 +154,6 @@ sealed trait Gen[+T] { override def toString = if(label.length == 0) "Gen()" else "Gen(\"" + label + "\")" - /** Returns a new property that holds if and only if both this - * and the given generator generates the same result, or both - * generators generate no result. - * @deprecated Use == instead */ - @deprecated("Use == instead", "1.7") - def ===[U](g: Gen[U]): Prop = this == g - /** Returns a new property that holds if and only if both this * and the given generator generates the same result, or both * generators generate no result. */ @@ -221,11 +220,6 @@ object Gen { } } - /* Default generator parameters - * @deprecated Use Gen.Params() instead */ - @deprecated("Use Gen.Params() instead", "1.8") - val defaultParams = Params() - /* Generator factory method */ def apply[T](g: Gen.Params => Option[T]) = new Gen[T] { def apply(p: Gen.Params) = g(p) @@ -310,20 +304,6 @@ object Gen { x <- if(i == 0) g1 else if(i == 1) g2 else gs(i-2) } yield x - /** Chooses one of the given values, with a weighted random distribution. - * @deprecated Use frequency with constant generators - * instead. */ - @deprecated("Use 'frequency' with constant generators instead.", "1.6") - def elementsFreq[T](vs: (Int, T)*): Gen[T] = - frequency(vs.map { case (w,v) => (w, value(v)) } : _*) - - /** A generator that returns a random element from a list - * @deprecated Use oneOf with constant generators instead. */ - @deprecated("Use 'oneOf' with constant generators instead.", "1.6") - def elements[T](xs: T*): Gen[T] = if(xs.isEmpty) fail else for { - i <- choose(0,xs.length-1) - } yield xs(i) - //// List Generators //// @@ -368,12 +348,6 @@ object Gen { * containerOfN[List,T](n,g). */ def listOfN[T](n: Int, g: Gen[T]) = containerOfN[List,T](n,g) - /** Generates a list of the given length. This method is equal to calling - * containerOfN[List,T](n,g). - * @deprecated Use the method listOfN instead. */ - @deprecated("Use 'listOfN' instead.", "1.6") - def vectorOf[T](n: Int, g: Gen[T]) = containerOfN[List,T](n,g) - /** A generator that picks a random number of elements from a list */ def someOf[T](l: Iterable[T]) = choose(0,l.size) flatMap (pick(_,l)) @@ -438,16 +412,6 @@ object Gen { //// Number Generators //// - /* Generates positive integers - * @deprecated Use posNum[Int]code> instead */ - @deprecated("Use posNum[Int] instead", "1.7") - def posInt: Gen[Int] = sized(max => choose(1, max)) - - /* Generates negative integers - * @deprecated Use negNum[Int]code> instead */ - @deprecated("Use negNum[Int] instead", "1.7") - def negInt: Gen[Int] = sized(max => choose(-max, -1)) - /** Generates positive numbers of uniform distribution, with an * upper bound of the generation size parameter. */ def posNum[T](implicit num: Numeric[T], c: Choose[T]): Gen[T] = { diff --git a/src/scalacheck/org/scalacheck/Pretty.scala b/src/scalacheck/org/scalacheck/Pretty.scala index f59ac315c7..c40e4aa718 100644 --- a/src/scalacheck/org/scalacheck/Pretty.scala +++ b/src/scalacheck/org/scalacheck/Pretty.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck @@ -49,6 +49,8 @@ object Pretty { implicit def prettyAny(t: Any) = Pretty { p => t.toString } + implicit def prettyString(t: String) = Pretty { p => "\""++t++"\"" } + implicit def prettyList(l: List[Any]) = Pretty { p => l.map("\""+_+"\"").mkString("List(", ", ", ")") } diff --git a/src/scalacheck/org/scalacheck/Prop.scala b/src/scalacheck/org/scalacheck/Prop.scala index 3ae9f22234..dfd85a832a 100644 --- a/src/scalacheck/org/scalacheck/Prop.scala +++ b/src/scalacheck/org/scalacheck/Prop.scala @@ -5,12 +5,13 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck import util.{FreqMap,Buildable} import scala.collection._ +import scala.annotation.tailrec /** A property is a generator that generates a property result */ trait Prop { @@ -102,15 +103,6 @@ trait Prop { } } - /** Returns a new property that holds if and only if both this - * and the given property generates a result with the exact - * same status. Note that this means that if one of the properties is - * proved, and the other one passed, then the resulting property - * will fail. - * @deprecated Use == instead */ - @deprecated("Use == instead.", "1.7") - def ===(p: Prop): Prop = this == p - override def toString = "Prop" /** Put a label on the property to make test reports clearer */ @@ -201,7 +193,7 @@ object Prop { case (_,Undecided) => r case (_,Proof) => merge(this, r, this.status) - case (Proof,_) => merge(this, r, this.status) + case (Proof,_) => merge(this, r, r.status) case (True,True) => merge(this, r, True) } @@ -337,15 +329,12 @@ object Prop { /** A property that depends on the generator size */ def sizedProp(f: Int => Prop): Prop = Prop { prms => + // provedToTrue since if the property is proved for + // one size, it shouldn't be regarded as proved for + // all sizes. provedToTrue(f(prms.genPrms.size)(prms)) } - /** Implication - * @deprecated Use the implication operator of the Prop class instead - */ - @deprecated("Use the implication operator of the Prop class instead", "1.7") - def ==>(b: => Boolean, p: => Prop): Prop = (b: Prop) ==> p - /** Implication with several conditions */ def imply[T](x: T, f: PartialFunction[T,Prop]): Prop = secure(if(f.isDefinedAt(x)) f(x) else undecided) @@ -758,4 +747,17 @@ object Prop { a8: Arbitrary[A8], s8: Shrink[A8], pp8: A8 => Pretty ): Prop = forAll((a: A1) => forAll(f(a, _:A2, _:A3, _:A4, _:A5, _:A6, _:A7, _:A8))) + /** Ensures that the property expression passed in completes within the given space of time. */ + def within(maximumMs: Long)(wrappedProp: => Prop): Prop = new Prop { + @tailrec private def attempt(prms: Params, endTime: Long): Result = { + val result = wrappedProp.apply(prms) + if (System.currentTimeMillis > endTime) { + (if (result.failure) result else Result(False)).label("Timeout") + } else { + if (result.success) result + else attempt(prms, endTime) + } + } + def apply(prms: Params) = attempt(prms, System.currentTimeMillis + maximumMs) + } } diff --git a/src/scalacheck/org/scalacheck/Properties.scala b/src/scalacheck/org/scalacheck/Properties.scala index 8a5b3febc9..26059231d6 100644 --- a/src/scalacheck/org/scalacheck/Properties.scala +++ b/src/scalacheck/org/scalacheck/Properties.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck diff --git a/src/scalacheck/org/scalacheck/Shrink.scala b/src/scalacheck/org/scalacheck/Shrink.scala index a077f21573..ae15bd9616 100644 --- a/src/scalacheck/org/scalacheck/Shrink.scala +++ b/src/scalacheck/org/scalacheck/Shrink.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck diff --git a/src/scalacheck/org/scalacheck/Test.scala b/src/scalacheck/org/scalacheck/Test.scala index 48b0a151a1..4368184823 100644 --- a/src/scalacheck/org/scalacheck/Test.scala +++ b/src/scalacheck/org/scalacheck/Test.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck @@ -19,12 +19,18 @@ object Test { /** Test parameters */ case class Params( minSuccessfulTests: Int = 100, - maxDiscardedTests: Int = 500, + + /** @deprecated Use maxDiscardRatio instead. */ + @deprecated("Use maxDiscardRatio instead.", "1.10") + maxDiscardedTests: Int = -1, + minSize: Int = 0, maxSize: Int = Gen.Params().size, rng: java.util.Random = Gen.Params().rng, workers: Int = 1, - testCallback: TestCallback = new TestCallback {} + testCallback: TestCallback = new TestCallback {}, + maxDiscardRatio: Float = 5, + customClassLoader: Option[ClassLoader] = None ) /** Test statistics */ @@ -90,7 +96,7 @@ object Test { import prms._ if( minSuccessfulTests <= 0 || - maxDiscardedTests < 0 || + maxDiscardRatio <= 0 || minSize < 0 || maxSize < minSize || workers <= 0 @@ -106,12 +112,13 @@ object Test { val names = Set("minSuccessfulTests", "s") val help = "Number of tests that must succeed in order to pass a property" } - object OptMaxDiscarded extends IntOpt { - val default = Test.Params().maxDiscardedTests - val names = Set("maxDiscardedTests", "d") + object OptMaxDiscardRatio extends FloatOpt { + val default = Test.Params().maxDiscardRatio + val names = Set("maxDiscardRatio", "r") val help = - "Number of tests that can be discarded before ScalaCheck stops " + - "testing a property" + "The maximum ratio between discarded and succeeded tests " + + "allowed before ScalaCheck stops testing a property. At " + + "least minSuccessfulTests will always be tested, though." } object OptMinSize extends IntOpt { val default = Test.Params().minSize @@ -135,45 +142,54 @@ object Test { } val opts = Set[Opt[_]]( - OptMinSuccess, OptMaxDiscarded, OptMinSize, + OptMinSuccess, OptMaxDiscardRatio, OptMinSize, OptMaxSize, OptWorkers, OptVerbosity ) def parseParams(args: Array[String]) = parseArgs(args) { optMap => Test.Params( - optMap(OptMinSuccess), - optMap(OptMaxDiscarded), - optMap(OptMinSize), - optMap(OptMaxSize), - Test.Params().rng, - optMap(OptWorkers), - ConsoleReporter(optMap(OptVerbosity)) + minSuccessfulTests = optMap(OptMinSuccess), + maxDiscardRatio = optMap(OptMaxDiscardRatio), + minSize = optMap(OptMinSize), + maxSize = optMap(OptMaxSize), + rng = Test.Params().rng, + workers = optMap(OptWorkers), + testCallback = ConsoleReporter(optMap(OptVerbosity)) ) } } /** Tests a property with the given testing parameters, and returns * the test results. */ - def check(prms: Params, p: Prop): Result = { + def check(params: Params, p: Prop): Result = { + + // maxDiscardedTests is deprecated, but if someone + // uses it let it override maxDiscardRatio + val mdr = + if(params.maxDiscardedTests < 0) params.maxDiscardRatio + else (params.maxDiscardedTests: Float)/(params.minSuccessfulTests: Float) + val prms = params.copy( maxDiscardRatio = mdr) + import prms._ - import actors.Futures.future + import scala.actors.Futures.future assertParams(prms) if(workers > 1) assert(!p.isInstanceOf[Commands], "Commands cannot be checked multi-threaded") - val iterations = minSuccessfulTests / workers - val sizeStep = (maxSize-minSize) / (minSuccessfulTests: Float) + val iterations = math.ceil(minSuccessfulTests / (workers: Double)) + val sizeStep = (maxSize-minSize) / (iterations*workers) var stop = false - def worker(workerdIdx: Int) = future { - var n = 0 - var d = 0 - var size = minSize + (workerdIdx*sizeStep*iterations) + def worker(workerIdx: Int) = future { + params.customClassLoader.map(Thread.currentThread.setContextClassLoader(_)) + var n = 0 // passed tests + var d = 0 // discarded tests var res: Result = null var fm = FreqMap.empty[immutable.Set[Any]] while(!stop && res == null && n < iterations) { - val propPrms = Prop.Params(Gen.Params(size.round, prms.rng), fm) + val size = (minSize: Double) + (sizeStep * (workerIdx + (workers*(n+d)))) + val propPrms = Prop.Params(Gen.Params(size.round.toInt, prms.rng), fm) secure(p(propPrms)) match { case Right(e) => res = Result(GenException(e), n, d, FreqMap.empty[immutable.Set[Any]]) @@ -184,35 +200,48 @@ object Test { propRes.status match { case Prop.Undecided => d += 1 - testCallback.onPropEval("", workerdIdx, n, d) - if(d >= maxDiscardedTests) res = Result(Exhausted, n, d, fm) + testCallback.onPropEval("", workerIdx, n, d) + // The below condition is kind of hacky. We have to have + // some margin, otherwise workers might stop testing too + // early because they have been exhausted, but the overall + // test has not. + if (n+d > minSuccessfulTests && 1+workers*maxDiscardRatio*n < d) + res = Result(Exhausted, n, d, fm) case Prop.True => n += 1 - testCallback.onPropEval("", workerdIdx, n, d) + testCallback.onPropEval("", workerIdx, n, d) case Prop.Proof => n += 1 res = Result(Proved(propRes.args), n, d, fm) - case Prop.False => res = - Result(Failed(propRes.args, propRes.labels), n, d, fm) - case Prop.Exception(e) => res = - Result(PropException(propRes.args, e, propRes.labels), n, d, fm) + stop = true + case Prop.False => + res = Result(Failed(propRes.args,propRes.labels), n, d, fm) + stop = true + case Prop.Exception(e) => + res = Result(PropException(propRes.args,e,propRes.labels), n, d, fm) + stop = true } } - size += sizeStep } - if(res != null) stop = true - else res = Result(Passed, n, d, fm) - res + if (res == null) { + if (maxDiscardRatio*n > d) Result(Passed, n, d, fm) + else Result(Exhausted, n, d, fm) + } else res } - def mergeResults(r1: () => Result, r2: () => Result) = r1() match { - case Result(Passed, s1, d1, fm1, t) => r2() match { - case Result(Passed, s2, d2, fm2, t) if d1+d2 >= maxDiscardedTests => - () => Result(Exhausted, s1+s2, d1+d2, fm1++fm2, t) - case Result(st, s2, d2, fm2, t) => - () => Result(st, s1+s2, d1+d2, fm1++fm2, t) + def mergeResults(r1: () => Result, r2: () => Result) = { + val Result(st1, s1, d1, fm1, _) = r1() + val Result(st2, s2, d2, fm2, _) = r2() + if (st1 != Passed && st1 != Exhausted) + () => Result(st1, s1+s2, d1+d2, fm1++fm2, 0) + else if (st2 != Passed && st2 != Exhausted) + () => Result(st2, s1+s2, d1+d2, fm1++fm2, 0) + else { + if (s1+s2 >= minSuccessfulTests && maxDiscardRatio*(s1+s2) >= (d1+d2)) + () => Result(Passed, s1+s2, d1+d2, fm1++fm2, 0) + else + () => Result(Exhausted, s1+s2, d1+d2, fm1++fm2, 0) } - case r => () => r } val start = System.currentTimeMillis @@ -237,78 +266,4 @@ object Test { (name,res) } - - // Deprecated methods // - - /** Default testing parameters - * @deprecated Use Test.Params() instead */ - @deprecated("Use Test.Params() instead", "1.8") - val defaultParams = Params() - - /** Property evaluation callback. Takes number of passed and - * discarded tests, respectively */ - @deprecated("(v1.8)", "1.8") - type PropEvalCallback = (Int,Int) => Unit - - /** Property evaluation callback. Takes property name, and number of passed - * and discarded tests, respectively */ - @deprecated("(v1.8)", "1.8") - type NamedPropEvalCallback = (String,Int,Int) => Unit - - /** Test callback. Takes property name, and test results. */ - @deprecated("(v1.8)", "1.8") - type TestResCallback = (String,Result) => Unit - - /** @deprecated (v1.8) Use check(prms.copy(testCallback = myCallback), p) instead. */ - @deprecated("Use check(prms.copy(testCallback = myCallback), p) instead", "1.8") - def check(prms: Params, p: Prop, propCallb: PropEvalCallback): Result = { - val testCallback = new TestCallback { - override def onPropEval(n: String, t: Int, s: Int, d: Int) = propCallb(s,d) - } - check(prms copy (testCallback = testCallback), p) - } - - /** Tests a property and prints results to the console. The - * maxDiscarded parameter specifies how many - * discarded tests that should be allowed before ScalaCheck - * @deprecated (v1.8) Use check(Params(maxDiscardedTests = n, testCallback = ConsoleReporter()), p) instead. */ - @deprecated("Use check(Params(maxDiscardedTests = n, testCallback = ConsoleReporter()), p) instead.", "1.8") - def check(p: Prop, maxDiscarded: Int): Result = - check(Params(maxDiscardedTests = maxDiscarded, testCallback = ConsoleReporter()), p) - - /** Tests a property and prints results to the console - * @deprecated (v1.8) Use check(Params(testCallback = ConsoleReporter()), p) instead. */ - @deprecated("Use check(Params(testCallback = ConsoleReporter()), p) instead.", "1.8") - def check(p: Prop): Result = check(Params(testCallback = ConsoleReporter()), p) - - /** Tests all properties with the given testing parameters, and returns - * the test results. f is a function which is called each - * time a property is evaluted. g is a function called each - * time a property has been fully tested. - * @deprecated (v1.8) Use checkProperties(prms.copy(testCallback = myCallback), ps) instead. */ - @deprecated("Use checkProperties(prms.copy(testCallback = myCallback), ps) instead.", "1.8") - def checkProperties(ps: Properties, prms: Params, - propCallb: NamedPropEvalCallback, testCallb: TestResCallback - ): Seq[(String,Result)] = { - val testCallback = new TestCallback { - override def onPropEval(n: String, t: Int, s: Int, d: Int) = propCallb(n,s,d) - override def onTestResult(n: String, r: Result) = testCallb(n,r) - } - checkProperties(prms copy (testCallback = testCallback), ps) - } - - /** Tests all properties with the given testing parameters, and returns - * the test results. - * @deprecated (v1.8) Use checkProperties(prms, ps) instead */ - @deprecated("Use checkProperties(prms, ps) instead", "1.8") - def checkProperties(ps: Properties, prms: Params): Seq[(String,Result)] = - checkProperties(ps, prms, (n,s,d) => (), (n,s) => ()) - - /** Tests all properties with default testing parameters, and returns - * the test results. The results are also printed on the console during - * testing. - * @deprecated (v1.8) Use checkProperties(Params(), ps) instead. */ - @deprecated("Use checkProperties(Params(), ps) instead.", "1.8") - def checkProperties(ps: Properties): Seq[(String,Result)] = - checkProperties(Params(), ps) } diff --git a/src/scalacheck/org/scalacheck/util/Buildable.scala b/src/scalacheck/org/scalacheck/util/Buildable.scala index 5c960c3ba8..221b8a61c3 100644 --- a/src/scalacheck/org/scalacheck/util/Buildable.scala +++ b/src/scalacheck/org/scalacheck/util/Buildable.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck.util @@ -31,7 +31,7 @@ object Buildable { def builder = (new mutable.ListBuffer[T]).mapResult(_.toStream) } - implicit def buildableArray[T](implicit t: ClassTag[T]) = + implicit def buildableArray[T](implicit cm: ClassTag[T]) = new Buildable[T,Array] { def builder = mutable.ArrayBuilder.make[T] } diff --git a/src/scalacheck/org/scalacheck/util/CmdLineParser.scala b/src/scalacheck/org/scalacheck/util/CmdLineParser.scala index a63e4ba10e..16ac1940b2 100644 --- a/src/scalacheck/org/scalacheck/util/CmdLineParser.scala +++ b/src/scalacheck/org/scalacheck/util/CmdLineParser.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck.util @@ -26,6 +26,7 @@ trait CmdLineParser extends Parsers { } trait Flag extends Opt[Unit] trait IntOpt extends Opt[Int] + trait FloatOpt extends Opt[Float] trait StrOpt extends Opt[String] class OptMap { @@ -68,11 +69,17 @@ trait CmdLineParser extends Parsers { case s if s != null && s.length > 0 && s.forall(_.isDigit) => s.toInt }) + private val floatVal: Parser[Float] = accept("float", { + case s if s != null && s.matches("[0987654321]+\\.?[0987654321]*") + => s.toFloat + }) + private case class OptVal[T](o: Opt[T], v: T) private val optVal: Parser[OptVal[Any]] = opt into { case o: Flag => success(OptVal(o, ())) case o: IntOpt => intVal ^^ (v => OptVal(o, v)) + case o: FloatOpt => floatVal ^^ (v => OptVal(o, v)) case o: StrOpt => strVal ^^ (v => OptVal(o, v)) } diff --git a/src/scalacheck/org/scalacheck/util/FreqMap.scala b/src/scalacheck/org/scalacheck/util/FreqMap.scala index 902c148d67..c7474d3b87 100644 --- a/src/scalacheck/org/scalacheck/util/FreqMap.scala +++ b/src/scalacheck/org/scalacheck/util/FreqMap.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck.util diff --git a/src/scalacheck/org/scalacheck/util/StdRand.scala b/src/scalacheck/org/scalacheck/util/StdRand.scala index 4cc83a4172..317b0ccd10 100644 --- a/src/scalacheck/org/scalacheck/util/StdRand.scala +++ b/src/scalacheck/org/scalacheck/util/StdRand.scala @@ -5,7 +5,7 @@ ** ** ** This software is released under the terms of the Revised BSD License. ** ** There is NO WARRANTY. See the file LICENSE for the full text. ** -\*-------------------------------------------------------------------------*/ +\*------------------------------------------------------------------------ */ package org.scalacheck.util diff --git a/test/files/lib/scalacheck.jar.desired.sha1 b/test/files/lib/scalacheck.jar.desired.sha1 index 2be7479415..e6ed543d73 100644 --- a/test/files/lib/scalacheck.jar.desired.sha1 +++ b/test/files/lib/scalacheck.jar.desired.sha1 @@ -1 +1 @@ -f8cd51e0f78e30b3ac444b741b0b2249ac8248bb ?scalacheck.jar +b6f4dbb29f0c2ec1eba682414f60d52fea84f703 ?scalacheck.jar diff --git a/test/files/scalacheck/CheckEither.scala b/test/files/scalacheck/CheckEither.scala index 0145d3321f..4e8480d72e 100644 --- a/test/files/scalacheck/CheckEither.scala +++ b/test/files/scalacheck/CheckEither.scala @@ -3,7 +3,6 @@ import org.scalacheck.Arbitrary.{arbitrary, arbThrowable} import org.scalacheck.Gen.oneOf import org.scalacheck.util.StdRand import org.scalacheck.Prop._ -import org.scalacheck.ConsoleReporter.{testReport, propReport} import org.scalacheck.Test.{Params, check} import org.scalacheck.ConsoleReporter.testStatsEx import Function.tupled -- cgit v1.2.3 From 47fad25adba895e8b27aee479373ea364dd316dd Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 19 Jun 2012 09:45:49 -0700 Subject: Fix for SI-5953, extension methods crasher. As usual, .tpe -> .tpeHK. As a side note following an old theme, if symbols of type parameters knew that they were symbols of type parameters, they could call tpeHK themselves rather than every call site having to do it. It's the operation which injects dummies which should require explicit programmer action, not the operation which faithfully reproduces the unapplied type. Were it that way, errors could be caught much more quickly via ill-kindedness. Seems like an improvement over lurking compiler crashes at every call to tparam.tpe. --- .../scala/tools/nsc/transform/ExtensionMethods.scala | 4 ++-- test/files/pos/t5953.scala | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 test/files/pos/t5953.scala diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 1c97eaad8b..5f66cadbc9 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -107,7 +107,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { def extensionMethInfo(extensionMeth: Symbol, origInfo: Type, clazz: Symbol): Type = { var newTypeParams = cloneSymbolsAtOwner(clazz.typeParams, extensionMeth) - val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpe)) + val thisParamType = appliedType(clazz.typeConstructor, newTypeParams map (_.tpeHK)) val thisParam = extensionMeth.newValueParameter(nme.SELF, extensionMeth.pos) setInfo thisParamType def transform(clonedType: Type): Type = clonedType match { case MethodType(params, restpe) => @@ -159,7 +159,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { .changeOwner((origMeth, extensionMeth)) extensionDefs(companion) += atPos(tree.pos) { DefDef(extensionMeth, extensionBody) } val extensionCallPrefix = Apply( - gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpe)), + gen.mkTypeApply(gen.mkAttributedRef(companion), extensionMeth, origTpeParams map (_.tpeHK)), List(This(currentOwner))) val extensionCall = atOwner(origMeth) { localTyper.typedPos(rhs.pos) { diff --git a/test/files/pos/t5953.scala b/test/files/pos/t5953.scala new file mode 100644 index 0000000000..90e7d84646 --- /dev/null +++ b/test/files/pos/t5953.scala @@ -0,0 +1,16 @@ +import scala.collection.{ mutable, immutable, generic, GenTraversableOnce } + +package object foo { + @inline implicit class TravOps[A, CC[A] <: GenTraversableOnce[A]](val coll: CC[A]) extends AnyVal { + def build[CC2[X]](implicit cbf: generic.CanBuildFrom[Nothing, A, CC2[A]]): CC2[A] = { + cbf() ++= coll.toIterator result + } + } +} + +package foo { + object Test { + def f1[T](xs: Traversable[T]) = xs.convertTo[immutable.Vector] + def f2[T](xs: Traversable[T]) = xs.build[immutable.Vector] + } +} -- cgit v1.2.3 From a2d1b23dbee55a905eecdfeeafad8701b726621b Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 19 Jun 2012 19:19:54 +0200 Subject: adds `narrow` to the reflection API as per https://issues.scala-lang.org/browse/SI-5951 --- src/reflect/scala/reflect/api/Types.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala index b62a92cbd7..b797c71f6d 100644 --- a/src/reflect/scala/reflect/api/Types.scala +++ b/src/reflect/scala/reflect/api/Types.scala @@ -121,7 +121,7 @@ trait Types extends base.Types { self: Universe => * class C extends p.D[Int] * T.asSeenFrom(ThisType(C), D) (where D is owner of m) * = Int - * }}} + * }}} */ def asSeenFrom(pre: Type, clazz: Symbol): Type @@ -171,6 +171,15 @@ trait Types extends base.Types { self: Universe => */ def widen: Type + /** Map to a singleton type which is a subtype of this type. + * The fallback implemented here gives: + * {{{ + * T.narrow = (T {}).this.type + * }}} + * Overridden where we know more about where types come from. + */ + def narrow: Type + /** The string discriminator of this type; useful for debugging */ def kind: String } @@ -365,4 +374,3 @@ trait Types extends base.Types { self: Universe => */ def existentialAbstraction(tparams: List[Symbol], tpe0: Type): Type } - -- cgit v1.2.3 From 7d8527b40a3bd6d93cf155b39ee50577751f3e91 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 16 Jun 2012 23:35:06 +0200 Subject: SI-2807 Resurrect and refine the promiscuous catch warning. The previous incarnation didn't survive 4fb3473. This version can be cleared by using a typed pattern: `catch { case _: Throwable => }`. This is motivated by the recent appearance of such a catch in `util.Try`, and by battle scars left by one too many processes bravely but stupidly catching and logging OutOfMemoryErrors. -Y status has been skipped: this warning is enabled by default and can only be silenced with use of a typed pattern. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 10 ++++++++++ test/files/continuations-neg/trycatch2.scala | 6 +++--- test/files/neg/catch-all.check | 10 ++++++++++ test/files/neg/catch-all.flags | 1 + test/files/neg/catch-all.scala | 19 +++++++++++++++++++ 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 test/files/neg/catch-all.check create mode 100644 test/files/neg/catch-all.flags create mode 100644 test/files/neg/catch-all.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 2bdae4164a..a23bc35e77 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4825,6 +4825,16 @@ trait Typers extends Modes with Adaptations with Tags { case Try(block, catches, finalizer) => var block1 = typed(block, pt) var catches1 = typedCases(catches, ThrowableClass.tpe, pt) + + for (cdef <- catches1 if cdef.guard.isEmpty) { + def warn(name: Name) = context.warning(cdef.pat.pos, s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning.") + cdef.pat match { + case Bind(name, Ident(_)) => warn(name) + case Ident(name) => warn(name) + case _ => + } + } + val finalizer1 = if (finalizer.isEmpty) finalizer else typed(finalizer, UnitClass.tpe) val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)), pt) diff --git a/test/files/continuations-neg/trycatch2.scala b/test/files/continuations-neg/trycatch2.scala index d61419169b..d329a3b530 100644 --- a/test/files/continuations-neg/trycatch2.scala +++ b/test/files/continuations-neg/trycatch2.scala @@ -12,7 +12,7 @@ object Test { fatal[Int] cpsIntStringInt } catch { - case ex => + case ex: Throwable => cpsIntStringInt } @@ -20,7 +20,7 @@ object Test { fatal[Int] cpsIntStringInt } catch { - case ex => + case ex: Throwable => cpsIntStringInt } @@ -30,4 +30,4 @@ object Test { println(reset { foo2; "3" }) } -} \ No newline at end of file +} diff --git a/test/files/neg/catch-all.check b/test/files/neg/catch-all.check new file mode 100644 index 0000000000..ab3d28777d --- /dev/null +++ b/test/files/neg/catch-all.check @@ -0,0 +1,10 @@ +catch-all.scala:2: error: This catches all Throwables. If this is really intended, use `case _ : Throwable` to clear this warning. + try { "warn" } catch { case _ => } + ^ +catch-all.scala:4: error: This catches all Throwables. If this is really intended, use `case x : Throwable` to clear this warning. + try { "warn" } catch { case x => } + ^ +catch-all.scala:6: error: This catches all Throwables. If this is really intended, use `case x : Throwable` to clear this warning. + try { "warn" } catch { case _: RuntimeException => ; case x => } + ^ +three errors found diff --git a/test/files/neg/catch-all.flags b/test/files/neg/catch-all.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/catch-all.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/catch-all.scala b/test/files/neg/catch-all.scala new file mode 100644 index 0000000000..35a6d7af91 --- /dev/null +++ b/test/files/neg/catch-all.scala @@ -0,0 +1,19 @@ +object CatchAll { + try { "warn" } catch { case _ => } + + try { "warn" } catch { case x => } + + try { "warn" } catch { case _: RuntimeException => ; case x => } + + try { "okay" } catch { case _: Throwable => } + + try { "okay" } catch { case _: Exception => } + + try { "okay" } catch { case okay: Throwable => } + + try { "okay" } catch { case okay: Exception => } + + try { "okay" } catch { case _ if "".isEmpty => } + + "okay" match { case _ => "" } +} -- cgit v1.2.3 From 93d8af58fb5ec0c366c5f16f53a22fe84e0ebd89 Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Wed, 20 Jun 2012 14:05:38 +0200 Subject: Making Actor Migration Tests deterministic. Review by: @phaller --- test/files/jvm/actmig-PinS.check | 19 +++ test/files/jvm/actmig-PinS.scala | 112 ++++++++++++++++ test/files/jvm/actmig-PinS_1.check | 19 +++ test/files/jvm/actmig-PinS_1.scala | 135 +++++++++++++++++++ test/files/jvm/actmig-PinS_2.check | 19 +++ test/files/jvm/actmig-PinS_2.scala | 155 ++++++++++++++++++++++ test/files/jvm/actmig-PinS_3.check | 19 +++ test/files/jvm/actmig-PinS_3.scala | 161 +++++++++++++++++++++++ test/files/jvm/actmig-hierarchy.check | 2 + test/files/jvm/actmig-hierarchy_1.check | 2 + test/files/jvm/actmig-instantiation.check | 8 ++ test/files/jvm/actmig-loop-react.check | 15 +++ test/files/jvm/actmig-loop-react.scala | 188 +++++++++++++++++++++++++++ test/files/jvm/actmig-public-methods.check | 6 + test/files/jvm/actmig-public-methods_1.check | 6 + test/files/jvm/actmig-public-methods_1.scala | 88 +++++++++++++ test/files/jvm/actmig-react-receive.check | 16 +++ test/files/jvm/actmig-react-receive.scala | 111 ++++++++++++++++ 18 files changed, 1081 insertions(+) create mode 100644 test/files/jvm/actmig-PinS.check create mode 100644 test/files/jvm/actmig-PinS.scala create mode 100644 test/files/jvm/actmig-PinS_1.check create mode 100644 test/files/jvm/actmig-PinS_1.scala create mode 100644 test/files/jvm/actmig-PinS_2.check create mode 100644 test/files/jvm/actmig-PinS_2.scala create mode 100644 test/files/jvm/actmig-PinS_3.check create mode 100644 test/files/jvm/actmig-PinS_3.scala create mode 100644 test/files/jvm/actmig-hierarchy.check create mode 100644 test/files/jvm/actmig-hierarchy_1.check create mode 100644 test/files/jvm/actmig-instantiation.check create mode 100644 test/files/jvm/actmig-loop-react.check create mode 100644 test/files/jvm/actmig-loop-react.scala create mode 100644 test/files/jvm/actmig-public-methods.check create mode 100644 test/files/jvm/actmig-public-methods_1.check create mode 100644 test/files/jvm/actmig-public-methods_1.scala create mode 100644 test/files/jvm/actmig-react-receive.check create mode 100644 test/files/jvm/actmig-react-receive.scala diff --git a/test/files/jvm/actmig-PinS.check b/test/files/jvm/actmig-PinS.check new file mode 100644 index 0000000000..bdbdf8a692 --- /dev/null +++ b/test/files/jvm/actmig-PinS.check @@ -0,0 +1,19 @@ +I'm acting! +I'm acting! +I'm acting! +I'm acting! +I'm acting! +Post stop +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +That is the question. +That is the question. +That is the question. +That is the question. +That is the question. +received message: hi there +received message: 15 +Got an Int: 12 diff --git a/test/files/jvm/actmig-PinS.scala b/test/files/jvm/actmig-PinS.scala new file mode 100644 index 0000000000..39f8f04b3b --- /dev/null +++ b/test/files/jvm/actmig-PinS.scala @@ -0,0 +1,112 @@ +import scala.actors._ +import scala.concurrent.util.duration._ +import scala.concurrent.{ Promise, Await } + +import scala.actors.Actor._ + +/* PinS, Listing 32.1: A simple actor + */ +object SillyActor extends Actor { + def act() { + for (i <- 1 to 5) + println("I'm acting!") + + println("Post stop") + } +} + +object SeriousActor extends Actor { + def act() { + for (i <- 1 to 5) + println("To be or not to be.") + } +} + +/* PinS, Listing 32.3: An actor that calls react + */ +object NameResolver extends Actor { + import java.net.{InetAddress, UnknownHostException} + + def act() { + react { + case (name: String, actor: Actor) => + actor ! getIp(name) + act() + case "EXIT" => + println("Name resolver exiting.") + // quit + case msg => + println("Unhandled message: " + msg) + act() + } + } + + def getIp(name: String): Option[InetAddress] = { + try { + Some(InetAddress.getByName(name)) + } catch { + case _: UnknownHostException => None + } + } + +} + +object Test extends App { + /* PinS, Listing 32.2: An actor that calls receive + */ + def makeEchoActor(): Actor = actor { + while (true) { + receive { + case 'stop => + exit() + case msg => + println("received message: " + msg) + } + } + } + + /* PinS, page 696 + */ + def makeIntActor(): Actor = actor { + receive { + case x: Int => // I only want Ints + println("Got an Int: " + x) + } + } + + actor { + self.trapExit = true + self.link(SillyActor) + SillyActor.start() + react { + case Exit(SillyActor, _) => + self.link(SeriousActor) + SeriousActor.start() + react { + case Exit(SeriousActor, _) => + val seriousPromise2 = Promise[Boolean] + // PinS, page 694 + val seriousActor2 = actor { + for (i <- 1 to 5) + println("That is the question.") + seriousPromise2.success(true) + } + + Await.ready(seriousPromise2.future, 5 seconds) + val echoActor = makeEchoActor() + self.link(echoActor) + echoActor ! "hi there" + echoActor ! 15 + echoActor ! 'stop + react { + case Exit(_, _) => + val intActor = makeIntActor() + intActor ! "hello" + intActor ! math.Pi + // only the following send leads to output + intActor ! 12 + } + } + } + } +} diff --git a/test/files/jvm/actmig-PinS_1.check b/test/files/jvm/actmig-PinS_1.check new file mode 100644 index 0000000000..bdbdf8a692 --- /dev/null +++ b/test/files/jvm/actmig-PinS_1.check @@ -0,0 +1,19 @@ +I'm acting! +I'm acting! +I'm acting! +I'm acting! +I'm acting! +Post stop +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +That is the question. +That is the question. +That is the question. +That is the question. +That is the question. +received message: hi there +received message: 15 +Got an Int: 12 diff --git a/test/files/jvm/actmig-PinS_1.scala b/test/files/jvm/actmig-PinS_1.scala new file mode 100644 index 0000000000..1fb50567b9 --- /dev/null +++ b/test/files/jvm/actmig-PinS_1.scala @@ -0,0 +1,135 @@ +import scala.actors._ +import scala.concurrent.util.duration._ +import scala.concurrent.{ Promise, Await } + +object SillyActor { + val startPromise = Promise[Boolean]() + val ref = MigrationSystem.actorOf(Props(() => new SillyActor, "akka.actor.default-stash-dispatcher")) +} + +/* PinS, Listing 32.1: A simple actor + */ +class SillyActor extends Actor { + + def act() { + Await.ready(SillyActor.startPromise.future, 5 seconds) + for (i <- 1 to 5) + println("I'm acting!") + + println("Post stop") + } +} + +object SeriousActor { + val startPromise = Promise[Boolean]() + val ref = MigrationSystem.actorOf(Props(() => new SeriousActor, "akka.actor.default-stash-dispatcher")) +} + +class SeriousActor extends Actor { + def act() { + // used to make this test deterministic + Await.ready(SeriousActor.startPromise.future, 5 seconds) + for (i <- 1 to 5) + println("To be or not to be.") + } +} + +/* PinS, Listing 32.3: An actor that calls react + */ +object NameResolver extends Actor { + import java.net.{ InetAddress, UnknownHostException } + + def act() { + react { + case (name: String, actor: Actor) => + actor ! getIp(name) + act() + case "EXIT" => + println("Name resolver exiting.") + // quit + case msg => + println("Unhandled message: " + msg) + act() + } + } + + def getIp(name: String): Option[InetAddress] = { + try { + Some(InetAddress.getByName(name)) + } catch { + case _: UnknownHostException => None + } + } + +} + +object Test extends App { + + /* PinS, Listing 32.2: An actor that calls receive + */ + def makeEchoActor(): ActorRef = MigrationSystem.actorOf(Props(() => new Actor { + def act() { + while (true) { + receive { + case 'stop => + exit() + case msg => + println("received message: " + msg) + } + } + } + }, "akka.actor.default-stash-dispatcher")) + + /* PinS, page 696 + */ + def makeIntActor(): ActorRef = MigrationSystem.actorOf(Props(() => new Actor { + def act() { + receive { + case x: Int => // I only want Ints + println("Got an Int: " + x) + } + } + }, "akka.actor.default-stash-dispatcher")) + + MigrationSystem.actorOf(Props(() => new Actor { + def act() { + trapExit = true + link(SillyActor.ref) + SillyActor.startPromise.success(true) + react { + case Exit(_: SillyActor, _) => + link(SeriousActor.ref) + SeriousActor.startPromise.success(true) + react { + case Exit(_: SeriousActor, _) => + val seriousPromise2 = Promise[Boolean]() + // PinS, page 694 + val seriousActor2 = MigrationSystem.actorOf(Props(() => + new Actor { + def act() { + for (i <- 1 to 5) + println("That is the question.") + seriousPromise2.success(true) + } + } + , "akka.actor.default-stash-dispatcher")) + + Await.ready(seriousPromise2.future, 5 seconds) + val echoActor = makeEchoActor() + link(echoActor) + echoActor ! "hi there" + echoActor ! 15 + echoActor ! 'stop + react { + case Exit(_, _) => + val intActor = makeIntActor() + intActor ! "hello" + intActor ! math.Pi + // only the following send leads to output + intActor ! 12 + } + } + } + } + }, "akka.actor.default-stash-dispatcher")) +} diff --git a/test/files/jvm/actmig-PinS_2.check b/test/files/jvm/actmig-PinS_2.check new file mode 100644 index 0000000000..bdbdf8a692 --- /dev/null +++ b/test/files/jvm/actmig-PinS_2.check @@ -0,0 +1,19 @@ +I'm acting! +I'm acting! +I'm acting! +I'm acting! +I'm acting! +Post stop +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +That is the question. +That is the question. +That is the question. +That is the question. +That is the question. +received message: hi there +received message: 15 +Got an Int: 12 diff --git a/test/files/jvm/actmig-PinS_2.scala b/test/files/jvm/actmig-PinS_2.scala new file mode 100644 index 0000000000..46277efd43 --- /dev/null +++ b/test/files/jvm/actmig-PinS_2.scala @@ -0,0 +1,155 @@ +import scala.actors.{ MigrationSystem, StashingActor, ActorRef, Props, Exit } +import scala.concurrent.util.duration._ +import scala.concurrent.{ Promise, Await } + +object SillyActor { + val startPromise = Promise[Boolean]() + val ref = MigrationSystem.actorOf(Props(() => new SillyActor, "default-stash-dispatcher")) +} + +/* PinS, Listing 32.1: A simple actor + */ +class SillyActor extends StashingActor { + + def receive = { case _ => println("Nop") } + + override def act() { + Await.ready(SillyActor.startPromise.future, 5 seconds) + for (i <- 1 to 5) + println("I'm acting!") + + println("Post stop") + } +} + +object SeriousActor { + val startPromise = Promise[Boolean]() + val ref = MigrationSystem.actorOf(Props(() => new SeriousActor, "default-stash-dispatcher")) +} + +class SeriousActor extends StashingActor { + def receive = { case _ => println("Nop") } + override def act() { + Await.ready(SeriousActor.startPromise.future, 5 seconds) + for (i <- 1 to 5) + println("To be or not to be.") + } +} + +/* PinS, Listing 32.3: An actor that calls react + */ +object NameResolver { + val ref = MigrationSystem.actorOf(Props(() => new NameResolver, "default-stash-dispatcher")) +} + +class NameResolver extends StashingActor { + import java.net.{ InetAddress, UnknownHostException } + + def receive = { case _ => println("Nop") } + + override def act() { + react { + case (name: String, actor: ActorRef) => + actor ! getIp(name) + act() + case "EXIT" => + println("Name resolver exiting.") + // quit + case msg => + println("Unhandled message: " + msg) + act() + } + } + + def getIp(name: String): Option[InetAddress] = { + try { + Some(InetAddress.getByName(name)) + } catch { + case _: UnknownHostException => None + } + } + +} + +object Test extends App { + + /* PinS, Listing 32.2: An actor that calls receive + */ + def makeEchoActor(): ActorRef = MigrationSystem.actorOf(Props(() => + new StashingActor { + def receive = { case _ => println("Nop") } + + override def act() { + loop { + react { + case 'stop => + exit() + case msg => + println("received message: " + msg) + } + } + } + }, "default-stash-dispatcher")) + + /* PinS, page 696 + */ + def makeIntActor(): ActorRef = MigrationSystem.actorOf(Props(() =>new StashingActor { + + def receive = { case _ => println("Nop") } + + override def act() { + react { + case x: Int => // I only want Ints + println("Got an Int: " + x) + } + } + }, "default-stash-dispatcher")) + + MigrationSystem.actorOf(Props(() => new StashingActor { + + def receive = { case _ => println("Nop") } + + override def act() { + trapExit = true + link(SillyActor.ref) + SillyActor.startPromise.success(true) + react { + case Exit(_: SillyActor, _) => + link(SeriousActor.ref) + SeriousActor.startPromise.success(true) + react { + case Exit(_: SeriousActor, _) => + val seriousPromise2 = Promise[Boolean]() + // PinS, page 694 + val seriousActor2 = MigrationSystem.actorOf(Props(() =>{ + new StashingActor { + + def receive = { case _ => println("Nop") } + + override def act() { + for (i <- 1 to 5) + println("That is the question.") + seriousPromise2.success(true) + } + } + }, "default-stash-dispatcher")) + + Await.ready(seriousPromise2.future, 5 seconds) + val echoActor = makeEchoActor() + link(echoActor) + echoActor ! "hi there" + echoActor ! 15 + echoActor ! 'stop + react { + case Exit(_, _) => + val intActor = makeIntActor() + intActor ! "hello" + intActor ! math.Pi + // only the following send leads to output + intActor ! 12 + } + } + } + } + }, "default-stash-dispatcher")) +} diff --git a/test/files/jvm/actmig-PinS_3.check b/test/files/jvm/actmig-PinS_3.check new file mode 100644 index 0000000000..bdbdf8a692 --- /dev/null +++ b/test/files/jvm/actmig-PinS_3.check @@ -0,0 +1,19 @@ +I'm acting! +I'm acting! +I'm acting! +I'm acting! +I'm acting! +Post stop +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +To be or not to be. +That is the question. +That is the question. +That is the question. +That is the question. +That is the question. +received message: hi there +received message: 15 +Got an Int: 12 diff --git a/test/files/jvm/actmig-PinS_3.scala b/test/files/jvm/actmig-PinS_3.scala new file mode 100644 index 0000000000..321e99b1c2 --- /dev/null +++ b/test/files/jvm/actmig-PinS_3.scala @@ -0,0 +1,161 @@ +import scala.actors.{ MigrationSystem, StashingActor, ActorRef, Terminated, Props } +import scala.concurrent.util.duration._ +import scala.concurrent.{ Promise, Await } + + +object SillyActor { + val startPromise = Promise[Boolean]() + val ref = MigrationSystem.actorOf(Props(() => new SillyActor, "default-stash-dispatcher")) +} + +/* PinS, Listing 32.1: A simple actor + */ +class SillyActor extends StashingActor { + def receive = { case _ => println("Why are you not dead"); context.stop(self) } + + override def preStart() { + Await.ready(SillyActor.startPromise.future, 5 seconds) + for (i <- 1 to 5) + println("I'm acting!") + context.stop(self) + } + + override def postStop() { + println("Post stop") + } +} + +object SeriousActor { + val startPromise = Promise[Boolean]() + val ref = MigrationSystem.actorOf(Props(() => new SeriousActor, "default-stash-dispatcher")) +} + +class SeriousActor extends StashingActor { + def receive = { case _ => println("Nop") } + override def preStart() { + Await.ready(SeriousActor.startPromise.future, 5 seconds) + for (i <- 1 to 5) + println("To be or not to be.") + context.stop(self) + } +} + +/* PinS, Listing 32.3: An actor that calls react + */ +object NameResolver { + val ref = MigrationSystem.actorOf(Props(() => new NameResolver, "default-stash-dispatcher")) +} + +class NameResolver extends StashingActor { + import java.net.{ InetAddress, UnknownHostException } + + def receive = { + case (name: String, actor: ActorRef) => + actor ! getIp(name) + case "EXIT" => + println("Name resolver exiting.") + context.stop(self) // quit + case msg => + println("Unhandled message: " + msg) + } + + def getIp(name: String): Option[InetAddress] = { + try { + Some(InetAddress.getByName(name)) + } catch { + case _: UnknownHostException => None + } + } + +} + +object Test extends App { + + /* PinS, Listing 32.2: An actor that calls receive + */ + def makeEchoActor(): ActorRef = MigrationSystem.actorOf(Props(() => new StashingActor { + + def receive = { // how to handle receive + case 'stop => + context.stop(self) + case msg => + println("received message: " + msg) + } + }, "default-stash-dispatcher")) + + /* PinS, page 696 + */ + def makeIntActor(): ActorRef = MigrationSystem.actorOf(Props(() => new StashingActor { + + def receive = { + case x: Int => // I only want Ints + unstashAll() + println("Got an Int: " + x) + context.stop(self) + case _ => stash() + } + }, "default-stash-dispatcher")) + + MigrationSystem.actorOf(Props(() => new StashingActor { + val silly = SillyActor.ref + + override def preStart() { + context.watch(SillyActor.ref) + SillyActor.startPromise.success(true) + } + + def receive = { + case Terminated(`silly`) => + unstashAll() + val serious = SeriousActor.ref + context.watch(SeriousActor.ref) + SeriousActor.startPromise.success(true) + context.become { + case Terminated(`serious`) => + val seriousPromise2 = Promise[Boolean]() + // PinS, page 694 + val seriousActor2 = MigrationSystem.actorOf(Props(() => { + new StashingActor { + + def receive = { case _ => context.stop(self) } + + override def preStart() = { + for (i <- 1 to 5) + println("That is the question.") + seriousPromise2.success(true) + context.stop(self) + } + } + }, "default-stash-dispatcher")) + + Await.ready(seriousPromise2.future, 5 seconds) + val echoActor = makeEchoActor() + context.watch(echoActor) + echoActor ! "hi there" + echoActor ! 15 + echoActor ! 'stop + context.become { + case Terminated(_) => + unstashAll() + val intActor = makeIntActor() + intActor ! "hello" + intActor ! math.Pi + // only the following send leads to output + intActor ! 12 + context.unbecome() + context.unbecome() + context.stop(self) + case m => + println("Stash 1 " + m) + stash(m) + } + case m => + println("Stash 2 " + m) + stash(m) + } + case m => + println("Stash 3 " + m) + stash(m) + } + }, "default-stash-dispatcher")) +} \ No newline at end of file diff --git a/test/files/jvm/actmig-hierarchy.check b/test/files/jvm/actmig-hierarchy.check new file mode 100644 index 0000000000..317e9677c3 --- /dev/null +++ b/test/files/jvm/actmig-hierarchy.check @@ -0,0 +1,2 @@ +hello +hello diff --git a/test/files/jvm/actmig-hierarchy_1.check b/test/files/jvm/actmig-hierarchy_1.check new file mode 100644 index 0000000000..317e9677c3 --- /dev/null +++ b/test/files/jvm/actmig-hierarchy_1.check @@ -0,0 +1,2 @@ +hello +hello diff --git a/test/files/jvm/actmig-instantiation.check b/test/files/jvm/actmig-instantiation.check new file mode 100644 index 0000000000..4c13d5c0a1 --- /dev/null +++ b/test/files/jvm/actmig-instantiation.check @@ -0,0 +1,8 @@ +OK error: java.lang.RuntimeException: In order to create StashingActor one must use actorOf. +OK error: java.lang.RuntimeException: Only one actor can be created per actorOf call. +0 +100 +200 +300 +400 +500 diff --git a/test/files/jvm/actmig-loop-react.check b/test/files/jvm/actmig-loop-react.check new file mode 100644 index 0000000000..54cbe942c0 --- /dev/null +++ b/test/files/jvm/actmig-loop-react.check @@ -0,0 +1,15 @@ +do task +do task +do task +do task +working +scala got exception +working +akka got exception +do task 1 +do string I am a String +do task 42 +after react +do task 1 +do string I am a String +do task 42 diff --git a/test/files/jvm/actmig-loop-react.scala b/test/files/jvm/actmig-loop-react.scala new file mode 100644 index 0000000000..d714b26594 --- /dev/null +++ b/test/files/jvm/actmig-loop-react.scala @@ -0,0 +1,188 @@ +import scala.actors.MigrationSystem._ +import scala.actors.Actor._ +import scala.actors.{ Actor, StashingActor, ActorRef, Props, MigrationSystem, PoisonPill } +import java.util.concurrent.{ TimeUnit, CountDownLatch } +import scala.collection.mutable.ArrayBuffer +import scala.concurrent.util.duration._ +import scala.concurrent.{ Promise, Await } + + +object Test { + val finishedLWCR, finishedTNR, finishedEH = Promise[Boolean] + val finishedLWCR1, finishedTNR1, finishedEH1 = Promise[Boolean] + + def testLoopWithConditionReact() = { + // Snippet showing composition of receives + // Loop with Condition Snippet - before + val myActor = actor { + var c = true + loopWhile(c) { + react { + case x: Int => + // do task + println("do task") + if (x == 42) { + c = false + finishedLWCR1.success(true) + } + } + } + } + + myActor.start() + myActor ! 1 + myActor ! 42 + + Await.ready(finishedLWCR1.future, 5 seconds) + + // Loop with Condition Snippet - migrated + val myAkkaActor = MigrationSystem.actorOf(Props(() => new StashingActor { + + def receive = { + case x: Int => + // do task + println("do task") + if (x == 42) { + finishedLWCR.success(true) + context.stop(self) + } + } + }, "default-stashing-dispatcher")) + myAkkaActor ! 1 + myAkkaActor ! 42 + } + + def testNestedReact() = { + // Snippet showing composition of receives + // Loop with Condition Snippet - before + val myActor = actor { + var c = true + loopWhile(c) { + react { + case x: Int => + // do task + println("do task " + x) + if (x == 42) { + c = false + finishedTNR1.success(true) + } else + react { + case y: String => + println("do string " + y) + } + println("after react") + } + } + } + myActor.start() + + myActor ! 1 + myActor ! "I am a String" + myActor ! 42 + + Await.ready(finishedTNR1.future, 5 seconds) + + // Loop with Condition Snippet - migrated + val myAkkaActor = MigrationSystem.actorOf(Props(() => new StashingActor { + + def receive = { + case x: Int => + // do task + println("do task " + x) + if (x == 42) { + finishedTNR.success(true) + context.stop(self) + } else + context.become(({ + case y: String => + println("do string " + y) + }: Receive).andThen(x => { + unstashAll() + context.unbecome() + }).orElse { case x => stash() }) + } + }, "default-stashing-dispatcher")) + + myAkkaActor ! 1 + myAkkaActor ! "I am a String" + myAkkaActor ! 42 + + } + + def exceptionHandling() = { + // Stashing actor with act and exception handler + val myActor = MigrationSystem.actorOf(Props(() => new StashingActor { + + def receive = { case _ => println("Dummy method.") } + override def act() = { + loop { + react { + case "fail" => + throw new Exception("failed") + case "work" => + println("working") + case "die" => + finishedEH1.success(true) + exit() + } + } + } + + override def exceptionHandler = { + case x: Exception => println("scala got exception") + } + + }, "default-stashing-dispatcher")) + + myActor ! "work" + myActor ! "fail" + myActor ! "die" + + Await.ready(finishedEH1.future, 5 seconds) + // Stashing actor in Akka style + val myAkkaActor = MigrationSystem.actorOf(Props(() => new StashingActor { + def receive = PFCatch({ + case "fail" => + throw new Exception("failed") + case "work" => + println("working") + case "die" => + finishedEH.success(true) + context.stop(self) + }, { case x: Exception => println("akka got exception") }) + }, "default-stashing-dispatcher")) + + myAkkaActor ! "work" + myAkkaActor ! "fail" + myAkkaActor ! "die" + } + + def main(args: Array[String]) = { + testLoopWithConditionReact() + Await.ready(finishedLWCR.future, 5 seconds) + exceptionHandling() + Await.ready(finishedEH.future, 5 seconds) + testNestedReact() + Await.ready(finishedTNR.future, 5 seconds) + } + +} + +// As per Jim Mcbeath's blog (http://jim-mcbeath.blogspot.com/2008/07/actor-exceptions.html) +class PFCatch(f: PartialFunction[Any, Unit], handler: PartialFunction[Exception, Unit]) + extends PartialFunction[Any, Unit] { + + def apply(x: Any) = { + try { + f(x) + } catch { + case e: Exception if handler.isDefinedAt(e) => handler(e) + } + } + + def isDefinedAt(x: Any) = f.isDefinedAt(x) +} + +object PFCatch { + def apply(f: PartialFunction[Any, Unit], handler: PartialFunction[Exception, Unit]) = new PFCatch(f, handler) +} diff --git a/test/files/jvm/actmig-public-methods.check b/test/files/jvm/actmig-public-methods.check new file mode 100644 index 0000000000..bb6530c926 --- /dev/null +++ b/test/files/jvm/actmig-public-methods.check @@ -0,0 +1,6 @@ +None +Some(bang qmark after 1) +bang +bang qmark after 0 +bang qmark in future after 0 +typed bang qmark in future after 0 diff --git a/test/files/jvm/actmig-public-methods_1.check b/test/files/jvm/actmig-public-methods_1.check new file mode 100644 index 0000000000..bb6530c926 --- /dev/null +++ b/test/files/jvm/actmig-public-methods_1.check @@ -0,0 +1,6 @@ +None +Some(bang qmark after 1) +bang +bang qmark after 0 +bang qmark in future after 0 +typed bang qmark in future after 0 diff --git a/test/files/jvm/actmig-public-methods_1.scala b/test/files/jvm/actmig-public-methods_1.scala new file mode 100644 index 0000000000..7e5bc24210 --- /dev/null +++ b/test/files/jvm/actmig-public-methods_1.scala @@ -0,0 +1,88 @@ +import scala.collection.mutable.ArrayBuffer +import scala.actors.Actor._ +import scala.actors._ +import scala.util._ +import java.util.concurrent.{ TimeUnit, CountDownLatch } +import scala.concurrent.util.Duration +import scala.actors.pattern._ + +object Test { + val NUMBER_OF_TESTS = 6 + + // used for sorting non-deterministic output + val buff = ArrayBuffer[String]() + val latch = new CountDownLatch(NUMBER_OF_TESTS) + val toStop = ArrayBuffer[ActorRef]() + + def append(v: String) = synchronized { + buff += v + } + + def main(args: Array[String]) = { + + val respActor = MigrationSystem.actorOf(Props(() => actor { + loop { + react { + case (x: String, time: Long) => + Thread.sleep(time) + reply(x + " after " + time) + case str: String => + append(str) + latch.countDown() + case x => + exit() + } + } + }, "akka.actor.default-stash-dispatcher")) + + toStop += respActor + + respActor ! "bang" + + implicit val timeout = Timeout(Duration(500, TimeUnit.MILLISECONDS)) + val msg = ("bang qmark", 0L) + val res1 = respActor.?(msg)(Timeout(Duration.Inf)) + append(res1().toString) + latch.countDown() + + val msg1 = ("bang qmark", 1L) + val res2 = respActor.?(msg1)(Timeout(Duration(500, TimeUnit.MILLISECONDS))) + append((res2() match { + case x: AskTimeoutException => None + case v => Some(v) + }).toString) + latch.countDown() + + // this one should time out + val msg11 = ("bang qmark", 500L) + val res21 = respActor.?(msg11)(Timeout(Duration(1, TimeUnit.MILLISECONDS))) + append((res21() match { + case x: AskTimeoutException => None + case v => Some(v) + }).toString) + latch.countDown() + + val msg2 = ("bang qmark in future", 0L) + val fut1 = respActor.?(msg2)(Duration.Inf) + append(fut1().toString()) + latch.countDown() + + val handler: PartialFunction[Any, String] = { + case x: String => x.toString + } + + val msg3 = ("typed bang qmark in future", 0L) + val fut2 = (respActor.?(msg3)(Duration.Inf)) + append(Futures.future { handler.apply(fut2()) }().toString) + latch.countDown() + + // output + latch.await(200, TimeUnit.MILLISECONDS) + if (latch.getCount() > 0) { + println("Error: Tasks have not finished!!!") + } + + buff.sorted.foreach(println) + toStop.foreach(_ ! PoisonPill) + } +} diff --git a/test/files/jvm/actmig-react-receive.check b/test/files/jvm/actmig-react-receive.check new file mode 100644 index 0000000000..cc2a426e68 --- /dev/null +++ b/test/files/jvm/actmig-react-receive.check @@ -0,0 +1,16 @@ +do before +do task +do after +do before +do task +do after +do before +do task +do in between +do string +do after +do before +do task +do in between +do string +do after diff --git a/test/files/jvm/actmig-react-receive.scala b/test/files/jvm/actmig-react-receive.scala new file mode 100644 index 0000000000..8464a2af79 --- /dev/null +++ b/test/files/jvm/actmig-react-receive.scala @@ -0,0 +1,111 @@ +import scala.actors.MigrationSystem._ +import scala.actors.Actor._ +import scala.actors.{ Actor, StashingActor, ActorRef, Props, MigrationSystem, PoisonPill } +import java.util.concurrent.{ TimeUnit, CountDownLatch } +import scala.collection.mutable.ArrayBuffer +import scala.concurrent.util.duration._ +import scala.concurrent.{ Promise, Await } + +object Test { + val finishedRS, finishedRS1, finishedRSC, finishedRSC1 = Promise[Boolean] + def testComposition() = { + // Snippet showing composition of receives + // React Snippet - before + val myActor = actor { + // do before + println("do before") + receive { + case x: Int => + // do task + println("do task") + } + println("do in between") + receive { + case x: String => + // do string now + println("do string") + } + println("do after") + finishedRSC1.success(true) + } + myActor.start() + myActor ! 1 + myActor ! "1" + Await.ready(finishedRSC1.future, 5 seconds) + + // React Snippet - migrated + val myAkkaActor = MigrationSystem.actorOf(Props(() => new StashingActor { + override def preStart() = { + println("do before") + } + + def receive = ({ + case x: Int => + // do task + println("do task") + }: Receive) andThen { v => + context.become { + case x: String => + //do string + println("do string") + context.stop(self) + } + println("do in between") + } + + override def postStop() = { + println("do after") + finishedRSC.success(true) + } + + }, "default-stashing-dispatcher")) + myAkkaActor ! 1 + myAkkaActor ! "1" + Await.ready(finishedRSC.future, 5 seconds) + } + + def main(args: Array[String]) = { + // React Snippet - before + val myActor = actor { + // do before + println("do before") + receive { + case x: Int => + // do task + println("do task") + } + println("do after") + finishedRS1.success(true) + } + myActor.start() + myActor ! 1 + + Await.ready(finishedRS1.future, 5 seconds) + + // React Snippet - migrated + val myAkkaActor = MigrationSystem.actorOf(Props(() => new StashingActor { + override def preStart() = { + println("do before") + } + + def receive = { + case x: Int => + // do task + println("do task") + context.stop(self) + } + + override def postStop() = { + println("do after") + finishedRS.success(true) + } + + }, "default-stashing-dispatcher")) + myAkkaActor ! 1 + + Await.ready(finishedRS.future, 5 seconds) + // Starting composition test + testComposition() + + } +} -- cgit v1.2.3 From 78ef5406ca01e65432df83a4b6bbd6dfef55376a Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Wed, 13 Jun 2012 18:26:36 +0200 Subject: removes pre-M4 compatibility stubs for the IDE --- src/library/scala/reflect/compat.scala | 33 ---------------------- .../scala/reflect/makro/internal/package.scala | 2 +- src/library/scala/reflect/package.scala | 2 +- .../neg/macro-invalidret-nonuniversetree.check | 4 +-- .../macro-invalidret-nonuniversetree/Impls_1.scala | 2 +- .../run/macro-reify-splice-splice/Macros_1.scala | 1 - 6 files changed, 5 insertions(+), 39 deletions(-) delete mode 100644 src/library/scala/reflect/compat.scala diff --git a/src/library/scala/reflect/compat.scala b/src/library/scala/reflect/compat.scala deleted file mode 100644 index fc0e5fbf9c..0000000000 --- a/src/library/scala/reflect/compat.scala +++ /dev/null @@ -1,33 +0,0 @@ -// [Eugene++] delete this once we merge with trunk and have a working IDE - -package scala.reflect { - trait ArrayTag[T] - trait ErasureTag[T] - trait ConcreteTypeTag[T] -} - -package scala.reflect.api { - trait TypeTags { - trait TypeTag[T] - trait ConcreteTypeTag[T] - } -} - -package scala { - import scala.reflect.base.{Universe => BaseUniverse} - - trait reflect_compat { - lazy val mirror: BaseUniverse = ??? - } -} - -package scala.reflect { - import language.experimental.macros - import scala.reflect.base.{Universe => BaseUniverse} - - trait internal_compat { - private[scala] def materializeArrayTag[T](u: BaseUniverse): ArrayTag[T] = ??? - private[scala] def materializeErasureTag[T](u: BaseUniverse): ErasureTag[T] = ??? - private[scala] def materializeConcreteTypeTag[T](u: BaseUniverse): ConcreteTypeTag[T] = ??? - } -} \ No newline at end of file diff --git a/src/library/scala/reflect/makro/internal/package.scala b/src/library/scala/reflect/makro/internal/package.scala index d31a0f0d14..78cb0ffb10 100644 --- a/src/library/scala/reflect/makro/internal/package.scala +++ b/src/library/scala/reflect/makro/internal/package.scala @@ -9,7 +9,7 @@ import scala.reflect.base.{Universe => BaseUniverse} // // todo. once we have implicit macros for tag generation, we can remove these anchors // [Eugene++] how do I hide this from scaladoc? -package object internal extends scala.reflect.internal_compat { +package object internal { private[scala] def materializeClassTag[T](u: BaseUniverse): ClassTag[T] = macro ??? private[scala] def materializeAbsTypeTag[T](u: BaseUniverse): u.AbsTypeTag[T] = macro ??? private[scala] def materializeTypeTag[T](u: BaseUniverse): u.TypeTag[T] = macro ??? diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 0ee58df2cd..2ebc82875e 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -1,6 +1,6 @@ package scala -package object reflect extends reflect_compat { +package object reflect { lazy val basis: base.Universe = new base.Base diff --git a/test/files/neg/macro-invalidret-nonuniversetree.check b/test/files/neg/macro-invalidret-nonuniversetree.check index 4fc06b5ceb..1b9487982f 100644 --- a/test/files/neg/macro-invalidret-nonuniversetree.check +++ b/test/files/neg/macro-invalidret-nonuniversetree.check @@ -1,7 +1,7 @@ Macros_Test_2.scala:2: error: macro implementation has wrong shape: required: (c: scala.reflect.makro.Context): c.Expr[Any] - found : (c: scala.reflect.makro.Context): reflect.mirror.Literal -type mismatch for return type: reflect.mirror.Literal does not conform to c.Expr[Any] + found : (c: scala.reflect.makro.Context): reflect.basis.Literal +type mismatch for return type: reflect.basis.Literal does not conform to c.Expr[Any] def foo = macro Impls.foo ^ one error found diff --git a/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala b/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala index 86b7c8d8d0..da0eb0ac83 100644 --- a/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala +++ b/test/files/neg/macro-invalidret-nonuniversetree/Impls_1.scala @@ -1,5 +1,5 @@ import scala.reflect.makro.{Context => Ctx} object Impls { - def foo(c: Ctx) = scala.reflect.mirror.Literal(scala.reflect.mirror.Constant(42)) + def foo(c: Ctx) = scala.reflect.basis.Literal(scala.reflect.basis.Constant(42)) } diff --git a/test/files/run/macro-reify-splice-splice/Macros_1.scala b/test/files/run/macro-reify-splice-splice/Macros_1.scala index 030a0a217e..4f1b600f63 100644 --- a/test/files/run/macro-reify-splice-splice/Macros_1.scala +++ b/test/files/run/macro-reify-splice-splice/Macros_1.scala @@ -1,5 +1,4 @@ import scala.reflect.makro.{Context => Ctx} -import scala.reflect.{mirror => mr} object Macros { def foo = macro Impls.foo -- cgit v1.2.3 From b29c01b710b67dd329ad418c7d5ef4e61845c6d1 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Tue, 19 Jun 2012 19:54:02 +0200 Subject: Fix SI-5284. The problem was the false assumption that methods specialized on their type parameter, such as this one: class Foo[@spec(Int) T](val x: T) { def bar[@spec(Int) S >: T](f: S => S) = f(x) } have their normalized versions (`bar$mIc$sp`) never called from the base specialization class `Foo`. This meant that the implementation of `bar$mIc$sp` in `Foo` simply threw an exception. This assumption is not true, however. See this: object Baz { def apply[T]() = new Foo[T] } Calling `Baz.apply[Int]()` will create an instance of the base specialization class `Foo` at `Int`. Calling `bar` on this instance will be rewritten by specialization to calling `bar$mIc$sp`, hence the error. So, we have to emit a valid implementation for `bar`, obviously. Problem is, such an implementation would have conflicting type bounds in the base specialization class `Foo`, since we don't know if `T` is a subtype of `S = Int`. In other words, we cannot emit: def bar$mIc$sp(f: Int => Int) = f(x) // x: T without typechecking errors. However, notice that the bounds are valid if and only if `T = Int`. In the same time, invocations of `bar$mIc$sp` will only be emitted in callsites where the type bounds hold. This means we can cast the expressions in method applications to the required specialized type bound. The following changes have been made: 1) The decision of whether or not to create a normalized version of the specialized method is not done on the `conflicting` relation anymore. Instead, it's done based on the `satisfiable` relation, which is true if there is possibly an instantiation of the type parameters where the bounds hold. 2) The `satisfiable` method has a new variant called `satisfiableConstraints`, which does unification to figure out how the type parameters should be instantiated in order to satisfy the bounds. 3) The `Duplicators` are changed to transform a tree using the `castType` method which just returns the tree by default. In specialization, the `castType` in `Duplicators` is overridden, and uses a map from type parameters to types. This map is obtained by `satisfiableConstraints` from 2). If the type of the expression is not equal to the expected type, and this map contains a mapping to the expected type, then the tree is cast, as discussed above. Additional tests added. Review by @dragos Review by @VladUreche Conflicts: src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala src/compiler/scala/tools/nsc/typechecker/Duplicators.scala --- .../scala/tools/nsc/transform/Constructors.scala | 2 +- .../tools/nsc/transform/SpecializeTypes.scala | 85 ++++++++++++++++++---- .../scala/tools/nsc/typechecker/Duplicators.scala | 31 +++++--- test/files/pos/spec-params-new.scala | 2 +- test/files/run/t5284.check | 1 + test/files/run/t5284.scala | 25 +++++++ test/files/run/t5284b.check | 1 + test/files/run/t5284b.scala | 28 +++++++ test/files/run/t5284c.check | 1 + test/files/run/t5284c.scala | 30 ++++++++ 10 files changed, 180 insertions(+), 26 deletions(-) create mode 100644 test/files/run/t5284.check create mode 100644 test/files/run/t5284.scala create mode 100644 test/files/run/t5284b.check create mode 100644 test/files/run/t5284b.scala create mode 100644 test/files/run/t5284c.check create mode 100644 test/files/run/t5284c.scala diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index bc4483923a..e5119eac71 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -323,7 +323,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { // statements coming from the original class need retyping in the current context debuglog("retyping " + stat2) - val d = new specializeTypes.Duplicator + val d = new specializeTypes.Duplicator(Map[Symbol, Type]()) d.retyped(localTyper.context1.asInstanceOf[d.Context], stat2, genericClazz, diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index c4c769d7cf..1d820afe11 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -450,7 +450,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Type parameters that survive when specializing in the specified environment. */ def survivingParams(params: List[Symbol], env: TypeEnv) = - params.filter(p => !p.isSpecialized || !isPrimitiveValueType(env(p))) + params filter { + p => + !p.isSpecialized || + !env.contains(p) || + !isPrimitiveValueType(env(p)) + } /** Produces the symbols from type parameters `syms` of the original owner, * in the given type environment `env`. The new owner is `nowner`. @@ -1176,7 +1181,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { || specializedTypeVars(t1).nonEmpty || specializedTypeVars(t2).nonEmpty) } - + env forall { case (tvar, tpe) => matches(tvar.info.bounds.lo, tpe) && matches(tpe, tvar.info.bounds.hi) || { if (warnings) @@ -1192,10 +1197,58 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } } + + def satisfiabilityConstraints(env: TypeEnv): Option[TypeEnv] = { + val noconstraints = Some(emptyEnv) + def matches(tpe1: Type, tpe2: Type): Option[TypeEnv] = { + val t1 = subst(env, tpe1) + val t2 = subst(env, tpe2) + // log("---------> " + tpe1 + " matches " + tpe2) + // log(t1 + ", " + specializedTypeVars(t1)) + // log(t2 + ", " + specializedTypeVars(t2)) + // log("unify: " + unify(t1, t2, env, false, false) + " in " + env) + if (t1 <:< t2) noconstraints + else if (specializedTypeVars(t1).nonEmpty) Some(unify(t1, t2, env, false, false) -- env.keys) + else if (specializedTypeVars(t2).nonEmpty) Some(unify(t2, t1, env, false, false) -- env.keys) + else None + } + + env.foldLeft[Option[TypeEnv]](noconstraints) { + case (constraints, (tvar, tpe)) => + val loconstraints = matches(tvar.info.bounds.lo, tpe) + val hiconstraints = matches(tpe, tvar.info.bounds.hi) + val allconstraints = for (c <- constraints; l <- loconstraints; h <- hiconstraints) yield c ++ l ++ h + allconstraints + } + } - class Duplicator extends { + /** This duplicator additionally performs casts of expressions if that is allowed by the `casts` map. */ + class Duplicator(casts: Map[Symbol, Type]) extends { val global: SpecializeTypes.this.global.type = SpecializeTypes.this.global - } with typechecker.Duplicators + } with typechecker.Duplicators { + private val (castfrom, castto) = casts.unzip + private object CastMap extends SubstTypeMap(castfrom.toList, castto.toList) + + class BodyDuplicator(_context: Context) extends super.BodyDuplicator(_context) { + override def castType(tree: Tree, pt: Type): Tree = { + // log(" expected type: " + pt) + // log(" tree type: " + tree.tpe) + tree.tpe = if (tree.tpe != null) fixType(tree.tpe) else null + // log(" tree type: " + tree.tpe) + val ntree = if (tree.tpe != null && !(tree.tpe <:< pt)) { + val casttpe = CastMap(tree.tpe) + if (casttpe <:< pt) gen.mkCast(tree, casttpe) + else if (casttpe <:< CastMap(pt)) gen.mkCast(tree, pt) + else tree + } else tree + ntree.tpe = null + ntree + } + } + + protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context) + + } /** A tree symbol substituter that substitutes on type skolems. * If a type parameter is a skolem, it looks for the original @@ -1475,14 +1528,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { deriveDefDef(tree1)(transform) case NormalizedMember(target) => - debuglog("Normalized member: " + symbol + ", target: " + target) - if (target.isDeferred || conflicting(typeEnv(symbol))) { + val constraints = satisfiabilityConstraints(typeEnv(symbol)) + log("constraints: " + constraints) + if (target.isDeferred || constraints == None) { deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called.")) - } - else { + } else { // we have an rhs, specialize it val tree1 = reportTypeError { - duplicateBody(ddef, target) + duplicateBody(ddef, target, constraints.get) } debuglog("implementation: " + tree1) deriveDefDef(tree1)(transform) @@ -1546,7 +1599,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate) debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName) - val d = new Duplicator + val d = new Duplicator(emptyEnv) val newValDef = d.retyped( localTyper.context1.asInstanceOf[d.Context], tree1, @@ -1571,12 +1624,18 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { super.transform(tree) } } - - private def duplicateBody(tree: DefDef, source: Symbol) = { + + /** Duplicate the body of the given method `tree` to the new symbol `source`. + * + * Knowing that the method can be invoked only in the `castmap` type environment, + * this method will insert casts for all the expressions of types mappend in the + * `castmap`. + */ + private def duplicateBody(tree: DefDef, source: Symbol, castmap: TypeEnv = emptyEnv) = { val symbol = tree.symbol val meth = addBody(tree, source) - val d = new Duplicator + val d = new Duplicator(castmap) debuglog("-->d DUPLICATING: " + meth) d.retyped( localTyper.context1.asInstanceOf[d.Context], diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala index 6386273c9d..63d1bd0e9f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala @@ -21,7 +21,7 @@ abstract class Duplicators extends Analyzer { def retyped(context: Context, tree: Tree): Tree = { resetClassOwners - (new BodyDuplicator(context)).typed(tree) + (newBodyDuplicator(context)).typed(tree) } /** Retype the given tree in the given context. Use this method when retyping @@ -37,15 +37,17 @@ abstract class Duplicators extends Analyzer { envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList) debuglog("retyped with env: " + env) - (new BodyDuplicator(context)).typed(tree) + newBodyDuplicator(context).typed(tree) } + protected def newBodyDuplicator(context: Context) = new BodyDuplicator(context) + def retypedMethod(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol): Tree = - (new BodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) + (newBodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis) /** Return the special typer for duplicate method bodies. */ override def newTyper(context: Context): Typer = - new BodyDuplicator(context) + newBodyDuplicator(context) private def resetClassOwners() { oldClassOwner = null @@ -209,6 +211,11 @@ abstract class Duplicators extends Analyzer { } } + /** Optionally cast this tree into some other type, if required. + * Unless overridden, just returns the tree. + */ + def castType(tree: Tree, pt: Type): Tree = tree + /** Special typer method for re-type checking trees. It expects a typed tree. * Returns a typed tree that has fresh symbols for all definitions in the original tree. * @@ -319,10 +326,10 @@ abstract class Duplicators extends Analyzer { super.typed(atPos(tree.pos)(tree1), mode, pt) case This(_) => - // log("selection on this, plain: " + tree) + debuglog("selection on this, plain: " + tree) tree.symbol = updateSym(tree.symbol) - tree.tpe = null - val tree1 = super.typed(tree, mode, pt) + val ntree = castType(tree, pt) + val tree1 = super.typed(ntree, mode, pt) // log("plain this typed to: " + tree1) tree1 /* no longer needed, because Super now contains a This(...) @@ -358,16 +365,18 @@ abstract class Duplicators extends Analyzer { case EmptyTree => // no need to do anything, in particular, don't set the type to null, EmptyTree.tpe_= asserts tree - + case _ => - // log("Duplicators default case: " + tree.summaryString + " -> " + tree) + debuglog("Duplicators default case: " + tree.summaryString) + debuglog(" ---> " + tree) if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) { tree.symbol = NoSymbol // maybe we can find a more specific member in a subclass of Any (see AnyVal members, like ==) } - tree.tpe = null - super.typed(tree, mode, pt) + val ntree = castType(tree, pt) + super.typed(ntree, mode, pt) } } + } } diff --git a/test/files/pos/spec-params-new.scala b/test/files/pos/spec-params-new.scala index 661e686f0e..959ce1591c 100644 --- a/test/files/pos/spec-params-new.scala +++ b/test/files/pos/spec-params-new.scala @@ -31,4 +31,4 @@ class Foo[@specialized A: ClassTag] { val xs = new Array[A](1) xs(0) = x } -} \ No newline at end of file +} diff --git a/test/files/run/t5284.check b/test/files/run/t5284.check new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/test/files/run/t5284.check @@ -0,0 +1 @@ +2 diff --git a/test/files/run/t5284.scala b/test/files/run/t5284.scala new file mode 100644 index 0000000000..ba0845fb8e --- /dev/null +++ b/test/files/run/t5284.scala @@ -0,0 +1,25 @@ + + + + + +/** Here we have a situation where a normalized method parameter `W` + * is used in a position which accepts an instance of type `T` - we know we can + * safely cast `T` to `W` whenever type bounds on `W` hold. + */ +object Test { + def main(args: Array[String]) { + val a = Blarg(Array(1, 2, 3)) + println(a.m((x: Int) => x + 1)) + } +} + + +object Blarg { + def apply[T: Manifest](a: Array[T]) = new Blarg(a) +} + + +class Blarg[@specialized(Int) T: Manifest](val a: Array[T]) { + def m[@specialized(Int) W >: T, @specialized(Int) S](f: W => S) = f(a(0)) +} diff --git a/test/files/run/t5284b.check b/test/files/run/t5284b.check new file mode 100644 index 0000000000..98d9bcb75a --- /dev/null +++ b/test/files/run/t5284b.check @@ -0,0 +1 @@ +17 diff --git a/test/files/run/t5284b.scala b/test/files/run/t5284b.scala new file mode 100644 index 0000000000..a9282a895f --- /dev/null +++ b/test/files/run/t5284b.scala @@ -0,0 +1,28 @@ + + + + + + +/** Here we have a situation where a normalized method parameter `W` + * is used in a position which expects a type `T` - we know we can + * safely cast `W` to `T` whenever typebounds of `W` hold. + */ +object Test { + def main(args: Array[String]) { + val foo = Foo.createUnspecialized[Int] + println(foo.bar(17)) + } +} + + +object Foo { + def createUnspecialized[T] = new Foo[T] +} + + +class Foo[@specialized(Int) T] { + val id: T => T = x => x + + def bar[@specialized(Int) W <: T, @specialized(Int) S](w: W) = id(w) +} diff --git a/test/files/run/t5284c.check b/test/files/run/t5284c.check new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/test/files/run/t5284c.check @@ -0,0 +1 @@ +3 diff --git a/test/files/run/t5284c.scala b/test/files/run/t5284c.scala new file mode 100644 index 0000000000..383b84c2cc --- /dev/null +++ b/test/files/run/t5284c.scala @@ -0,0 +1,30 @@ + + + + + + +/** Here we have a compound type `List[W]` used in + * a position where `List[T]` is expected. The cast + * emitted in the normalized `bar` is safe because the + * normalized `bar` can only be called if the type + * bounds hold. + */ +object Test { + def main(args: Array[String]) { + val foo = Foo.createUnspecialized[Int] + println(foo.bar(List(1, 2, 3))) + } +} + + +object Foo { + def createUnspecialized[T] = new Foo[T] +} + + +class Foo[@specialized(Int) T] { + val len: List[T] => Int = xs => xs.length + + def bar[@specialized(Int) W <: T](ws: List[W]) = len(ws) +} -- cgit v1.2.3 From f84659b46ee25cdc4b63e6d59f5015fc5314a63d Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Thu, 21 Jun 2012 14:07:38 +0200 Subject: Fix SI-4809. --- src/library/scala/util/control/Breaks.scala | 8 +++---- test/files/run/t4809.scala | 34 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 test/files/run/t4809.scala diff --git a/src/library/scala/util/control/Breaks.scala b/src/library/scala/util/control/Breaks.scala index d7f5a57f50..accda5b8f7 100644 --- a/src/library/scala/util/control/Breaks.scala +++ b/src/library/scala/util/control/Breaks.scala @@ -41,8 +41,8 @@ class Breaks { } } - trait TryBlock { - def catchBreak(onBreak: => Unit): Unit + sealed trait TryBlock[T] { + def catchBreak(onBreak: =>T): T } /** @@ -57,8 +57,8 @@ class Breaks { * } * }}} */ - def tryBreakable(op: => Unit) = new TryBlock { - def catchBreak(onBreak: => Unit) = try { + def tryBreakable[T](op: =>T) = new TryBlock[T] { + def catchBreak(onBreak: =>T) = try { op } catch { case ex: BreakControl => diff --git a/test/files/run/t4809.scala b/test/files/run/t4809.scala new file mode 100644 index 0000000000..b30d80562f --- /dev/null +++ b/test/files/run/t4809.scala @@ -0,0 +1,34 @@ + + +import scala.util.control.Breaks._ + + + +object Test { + + def main(args: Array[String]) { + val x = tryBreakable { + break + 2 + } catchBreak { + 3 + } + assert(x == 3, x) + + val y = tryBreakable { + 2 + } catchBreak { + 3 + } + assert(y == 2, y) + + val z = tryBreakable { + break + 1.0 + } catchBreak { + 2 + } + assert(z == 2.0, z) + } + +} -- cgit v1.2.3 From 7527979be7f3578b44997e0089f7734667cfd160 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Fri, 22 Jun 2012 12:08:29 +0200 Subject: Don't swallow `Throwables` while parsing bytecode. Print a warning and go on. This has caused hours of debugging, to find out that 'package X does not have a member Y' were caused by a `NullPointerException`. --- .../scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index d8bf23f4fe..31e1bf6389 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1013,9 +1013,16 @@ abstract class ClassfileParser { } catch { case f: FatalError => throw f // don't eat fatal errors, they mean a class was not found case ex: Throwable => - debuglog("dropping annotation on " + sym + ", an error occured during parsing (e.g. annotation class not found)") - - None // ignore malformed annotations ==> t1135 + // We want to be robust when annotations are unavailable, so the very least + // we can do is warn the user about the exception + // There was a reference to ticket 1135, but that is outdated: a reference to a class not on + // the classpath would *not* end up here. A class not found is signaled + // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), + // and that should never be swallowed silently. + warning("Caught: " + ex + " while parsing annotations in " + in.file) + if (settings.debug.value) ex.printStackTrace() + + None // ignore malformed annotations } /** -- cgit v1.2.3 From 4ec752e4c5ae914f0f50bcdeec12cd320f90ad94 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 20 Jun 2012 09:30:21 +0200 Subject: Exported new packages in the MANIFEST file. --- META-INF/MANIFEST.MF | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF index 9cbe99ab23..28a70d2879 100644 --- a/META-INF/MANIFEST.MF +++ b/META-INF/MANIFEST.MF @@ -40,9 +40,13 @@ Export-Package: scala.tools.nsc.util, scala.tools.util, scala.reflect.internal, + scala.reflect.internal.pickling, scala.reflect.internal.settings, + scala.reflect.internal.util, + scala.reflect.makro, scala.reflect.runtime, scala.reflect.internal.transform, + scala.reflect.api, ch.epfl.lamp.compiler.msil, ch.epfl.lamp.compiler.msil.emit, ch.epfl.lamp.compiler.msil.util, -- cgit v1.2.3 From 4736897ad2a6425c64cadfe80d5e6d7751ee0fec Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 23 Jun 2012 19:04:50 +0200 Subject: SI-5966 Fix eta expansion for repeated parameters with zero arguments. Reworks part of e33901 / SI-5610, which was inserting an tree as an argument in this case, which turns into a null in icode. --- src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala | 8 +++++--- test/files/run/t5966.check | 3 +++ test/files/run/t5966.scala | 9 +++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t5966.check create mode 100644 test/files/run/t5966.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index e1fb683aa9..177d1ddf19 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -88,9 +88,11 @@ trait EtaExpansion { self: Analyzer => defs ++= stats liftoutPrefix(fun) case Apply(fn, args) => - val byName = fn.tpe.params.map(p => definitions.isByNameParamType(p.tpe)) - // zipAll: with repeated params, there might be more args than params - val newArgs = args.zipAll(byName, EmptyTree, false) map { case (arg, byN) => liftout(arg, byN) } + val byName: Int => Option[Boolean] = fn.tpe.params.map(p => definitions.isByNameParamType(p.tpe)).lift + val newArgs = mapWithIndex(args) { (arg, i) => + // with repeated params, there might be more or fewer args than params + liftout(arg, byName(i).getOrElse(false)) + } treeCopy.Apply(tree, liftoutPrefix(fn), newArgs) setType null case TypeApply(fn, args) => treeCopy.TypeApply(tree, liftoutPrefix(fn), args) setType null diff --git a/test/files/run/t5966.check b/test/files/run/t5966.check new file mode 100644 index 0000000000..bfe8358a77 --- /dev/null +++ b/test/files/run/t5966.check @@ -0,0 +1,3 @@ +(o()_)("") = List() +(o("a1")_)("") = WrappedArray(a1) +(o("a1", "a2")_)("") = WrappedArray(a1, a2) diff --git a/test/files/run/t5966.scala b/test/files/run/t5966.scala new file mode 100644 index 0000000000..bbe1a6e874 --- /dev/null +++ b/test/files/run/t5966.scala @@ -0,0 +1,9 @@ +object o { def apply(i: AnyRef*)(j: String) = i } + +object Test { + def main(args: Array[String]) { + println("(o()_)(\"\") = " + (o()_)("")) + println("(o(\"a1\")_)(\"\") = " + (o("a1")_)("")) + println("(o(\"a1\", \"a2\")_)(\"\") = " + (o("a1", "a2")_)("")) + } +} -- cgit v1.2.3 From c27e5f0d60c853868b3676f1449e42dd351b0644 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Sat, 23 Jun 2012 22:38:23 +0200 Subject: SI-5968 Eliminate spurious exhaustiveness warning with singleton types. A singleton type is a type ripe for enumeration. --- src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala | 2 ++ test/files/pos/t5968.flags | 1 + test/files/pos/t5968.scala | 8 ++++++++ 3 files changed, 11 insertions(+) create mode 100644 test/files/pos/t5968.flags create mode 100644 test/files/pos/t5968.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 9b8ddffb49..f99d0e733b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -2362,6 +2362,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // patmatDebug("enum bool "+ tp) Some(List(ConstantType(Constant(true)), ConstantType(Constant(false)))) // TODO case _ if tp.isTupleType => // recurse into component types + case modSym: ModuleClassSymbol => + Some(List(tp)) case sym if !sym.isSealed || isPrimitiveValueClass(sym) => // patmatDebug("enum unsealed "+ (tp, sym, sym.isSealed, isPrimitiveValueClass(sym))) None diff --git a/test/files/pos/t5968.flags b/test/files/pos/t5968.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t5968.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/pos/t5968.scala b/test/files/pos/t5968.scala new file mode 100644 index 0000000000..0093f84fc0 --- /dev/null +++ b/test/files/pos/t5968.scala @@ -0,0 +1,8 @@ +object X { + def f(e: Either[Int, X.type]) = e match { + case Left(i) => i + case Right(X) => 0 + // SI-5986 spurious exhaustivity warning here + } +} + -- cgit v1.2.3 From bb0f5d9ca72905dfebdc543ebf0bb0aad30383b7 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 23 Jun 2012 14:12:11 -0700 Subject: Added a key comment. The commit message from f4d2678c42 works a lot better up close and personal with the change. --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index bce9f28847..3232bde3b4 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -253,7 +253,9 @@ self => final val InBlock = 1 final val InTemplate = 2 - lazy val ScalaValueClassNames = Seq(tpnme.AnyVal, + // These symbols may not yet be loaded (e.g. in the ide) so don't go + // through definitions to obtain the names. + lazy val ScalaValueClassNames = Seq(tpnme.AnyVal, tpnme.Unit, tpnme.Boolean, tpnme.Byte, -- cgit v1.2.3 From 58130c779dd2589f98bc96ae932df74f6eaff17a Mon Sep 17 00:00:00 2001 From: soc Date: Sun, 24 Jun 2012 02:58:13 +0300 Subject: Adds missing closing curly brace to ScalaDoc code example. --- src/library/scala/util/control/ControlThrowable.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/library/scala/util/control/ControlThrowable.scala b/src/library/scala/util/control/ControlThrowable.scala index 8cbe3064ef..64afb1f10f 100644 --- a/src/library/scala/util/control/ControlThrowable.scala +++ b/src/library/scala/util/control/ControlThrowable.scala @@ -24,8 +24,9 @@ package scala.util.control * try { * // Body might throw arbitrarily * } catch { - * case ce : ControlThrowable => throw ce // propagate - * case t : Exception => log(t) // log and suppress + * case c: ControlThrowable => throw c // propagate + * case t: Exception => log(t) // log and suppress + * } * }}} * * @author Miles Sabin -- cgit v1.2.3 From a64c305c91a4ae914807f9126300961dd395e885 Mon Sep 17 00:00:00 2001 From: soc Date: Sun, 24 Jun 2012 03:52:55 +0300 Subject: IntMap: Made life a bit less scary for those doing source archaeology. --- .../scala/collection/immutable/IntMap.scala | 289 +++++++++++---------- 1 file changed, 147 insertions(+), 142 deletions(-) diff --git a/src/library/scala/collection/immutable/IntMap.scala b/src/library/scala/collection/immutable/IntMap.scala index 039a57041c..e895c94599 100644 --- a/src/library/scala/collection/immutable/IntMap.scala +++ b/src/library/scala/collection/immutable/IntMap.scala @@ -18,17 +18,17 @@ import scala.collection.mutable.{ Builder, MapBuilder } private[immutable] object IntMapUtils extends BitOperations.Int { def branchMask(i: Int, j: Int) = highestOneBit(i ^ j) - def join[T](p1 : Int, t1 : IntMap[T], p2 : Int, t2 : IntMap[T]) : IntMap[T] = { - val m = branchMask(p1, p2); - val p = mask(p1, m); + def join[T](p1: Int, t1: IntMap[T], p2: Int, t2: IntMap[T]): IntMap[T] = { + val m = branchMask(p1, p2) + val p = mask(p1, m) if (zero(p1, m)) IntMap.Bin(p, m, t1, t2) - else IntMap.Bin(p, m, t2, t1); + else IntMap.Bin(p, m, t2, t1) } - def bin[T](prefix : Int, mask : Int, left : IntMap[T], right : IntMap[T]) : IntMap[T] = (left, right) match { - case (left, IntMap.Nil) => left; - case (IntMap.Nil, right) => right; - case (left, right) => IntMap.Bin(prefix, mask, left, right); + def bin[T](prefix: Int, mask: Int, left: IntMap[T], right: IntMap[T]): IntMap[T] = (left, right) match { + case (left, IntMap.Nil) => left + case (IntMap.Nil, right) => right + case (left, right) => IntMap.Bin(prefix, mask, left, right) } } @@ -50,9 +50,9 @@ object IntMap { } def empty[T] : IntMap[T] = IntMap.Nil; - def singleton[T](key : Int, value : T) : IntMap[T] = IntMap.Tip(key, value); - def apply[T](elems : (Int, T)*) : IntMap[T] = - elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)); + def singleton[T](key: Int, value: T): IntMap[T] = IntMap.Tip(key, value); + def apply[T](elems: (Int, T)*): IntMap[T] = + elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)) private[immutable] case object Nil extends IntMap[Nothing] { // Important! Without this equals method in place, an infinite @@ -66,15 +66,15 @@ object IntMap { } } - private[immutable] case class Tip[+T](key : Int, value : T) extends IntMap[T]{ + private[immutable] case class Tip[+T](key: Int, value: T) extends IntMap[T]{ def withValue[S](s: S) = - if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[IntMap.Tip[S]]; - else IntMap.Tip(key, s); + if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[IntMap.Tip[S]] + else IntMap.Tip(key, s) } - private[immutable] case class Bin[+T](prefix : Int, mask : Int, left : IntMap[T], right : IntMap[T]) extends IntMap[T]{ - def bin[S](left : IntMap[S], right : IntMap[S]) : IntMap[S] = { - if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[IntMap.Bin[S]]; - else IntMap.Bin[S](prefix, mask, left, right); + private[immutable] case class Bin[+T](prefix: Int, mask: Int, left: IntMap[T], right: IntMap[T]) extends IntMap[T] { + def bin[S](left: IntMap[S], right: IntMap[S]): IntMap[S] = { + if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[IntMap.Bin[S]] + else IntMap.Bin[S](prefix, mask, left, right) } } @@ -83,60 +83,60 @@ object IntMap { import IntMap._ // Iterator over a non-empty IntMap. -private[immutable] abstract class IntMapIterator[V, T](it : IntMap[V]) extends AbstractIterator[T] { +private[immutable] abstract class IntMapIterator[V, T](it: IntMap[V]) extends AbstractIterator[T] { // Basically this uses a simple stack to emulate conversion over the tree. However // because we know that Ints are at least 32 bits we can have at most 32 IntMap.Bins and // one IntMap.Tip sitting on the tree at any point. Therefore we know the maximum stack // depth is 33 and - var index = 0; - var buffer = new Array[AnyRef](33); + var index = 0 + var buffer = new Array[AnyRef](33) def pop = { - index -= 1; - buffer(index).asInstanceOf[IntMap[V]]; + index -= 1 + buffer(index).asInstanceOf[IntMap[V]] } - def push(x : IntMap[V]) { - buffer(index) = x.asInstanceOf[AnyRef]; - index += 1; + def push(x: IntMap[V]) { + buffer(index) = x.asInstanceOf[AnyRef] + index += 1 } - push(it); + push(it) /** * What value do we assign to a tip? */ - def valueOf(tip : IntMap.Tip[V]) : T; + def valueOf(tip: IntMap.Tip[V]): T - def hasNext = index != 0; - final def next : T = + def hasNext = index != 0 + final def next: T = pop match { case IntMap.Bin(_,_, t@IntMap.Tip(_, _), right) => { - push(right); - valueOf(t); + push(right) + valueOf(t) } case IntMap.Bin(_, _, left, right) => { - push(right); - push(left); - next; + push(right) + push(left) + next } - case t@IntMap.Tip(_, _) => valueOf(t); + case t@IntMap.Tip(_, _) => valueOf(t) // This should never happen. We don't allow IntMap.Nil in subtrees of the IntMap // and don't return an IntMapIterator for IntMap.Nil. - case IntMap.Nil => sys.error("Empty maps not allowed as subtrees"); + case IntMap.Nil => sys.error("Empty maps not allowed as subtrees") } } -private[immutable] class IntMapEntryIterator[V](it : IntMap[V]) extends IntMapIterator[V, (Int, V)](it){ - def valueOf(tip : IntMap.Tip[V]) = (tip.key, tip.value); +private[immutable] class IntMapEntryIterator[V](it: IntMap[V]) extends IntMapIterator[V, (Int, V)](it) { + def valueOf(tip: IntMap.Tip[V]) = (tip.key, tip.value) } -private[immutable] class IntMapValueIterator[V](it : IntMap[V]) extends IntMapIterator[V, V](it){ - def valueOf(tip : IntMap.Tip[V]) = tip.value +private[immutable] class IntMapValueIterator[V](it: IntMap[V]) extends IntMapIterator[V, V](it) { + def valueOf(tip: IntMap.Tip[V]) = tip.value } -private[immutable] class IntMapKeyIterator[V](it : IntMap[V]) extends IntMapIterator[V, Int](it){ - def valueOf(tip : IntMap.Tip[V]) = tip.key +private[immutable] class IntMapKeyIterator[V](it: IntMap[V]) extends IntMapIterator[V, Int](it) { + def valueOf(tip: IntMap.Tip[V]) = tip.key } import IntMap._ @@ -145,7 +145,7 @@ import IntMap._ * Fast Mergeable Integer Maps * by Okasaki and Gill. Essentially a trie based on binary digits of the integers. * - * Note: This class is as of 2.8 largely superseded by HashMap. + * '''Note:''' This class is as of 2.8 largely superseded by HashMap. * * @tparam T type of the values associated with integer keys. * @@ -155,17 +155,16 @@ import IntMap._ * @define mayNotTerminateInf * @define willNotTerminateInf */ -sealed abstract class IntMap[+T] -extends AbstractMap[Int, T] +sealed abstract class IntMap[+T] extends AbstractMap[Int, T] with Map[Int, T] with MapLike[Int, T, IntMap[T]] { - override def empty: IntMap[T] = IntMap.Nil; + override def empty: IntMap[T] = IntMap.Nil override def toList = { - val buffer = new scala.collection.mutable.ListBuffer[(Int, T)]; - foreach(buffer += _); - buffer.toList; + val buffer = new scala.collection.mutable.ListBuffer[(Int, T)] + foreach(buffer += _) + buffer.toList } /** @@ -173,109 +172,112 @@ extends AbstractMap[Int, T] * * @return an iterator over pairs of integer keys and corresponding values. */ - def iterator : Iterator[(Int, T)] = this match { - case IntMap.Nil => Iterator.empty; - case _ => new IntMapEntryIterator(this); + def iterator: Iterator[(Int, T)] = this match { + case IntMap.Nil => Iterator.empty + case _ => new IntMapEntryIterator(this) } /** * Loops over the key, value pairs of the map in unsigned order of the keys. */ - override final def foreach[U](f : ((Int, T)) => U) : Unit = this match { - case IntMap.Bin(_, _, left, right) => {left.foreach(f); right.foreach(f); } - case IntMap.Tip(key, value) => f((key, value)); - case IntMap.Nil => {}; + override final def foreach[U](f: ((Int, T)) => U): Unit = this match { + case IntMap.Bin(_, _, left, right) => { left.foreach(f); right.foreach(f) } + case IntMap.Tip(key, value) => f((key, value)) + case IntMap.Nil => } - override def keysIterator : Iterator[Int] = this match { - case IntMap.Nil => Iterator.empty; - case _ => new IntMapKeyIterator(this); + override def keysIterator: Iterator[Int] = this match { + case IntMap.Nil => Iterator.empty + case _ => new IntMapKeyIterator(this) } /** - * Loop over the keys of the map. The same as keys.foreach(f), but may + * Loop over the keys of the map. The same as `keys.foreach(f)`, but may * be more efficient. * * @param f The loop body */ - final def foreachKey(f : Int => Unit) : Unit = this match { - case IntMap.Bin(_, _, left, right) => {left.foreachKey(f); right.foreachKey(f); } - case IntMap.Tip(key, _) => f(key); - case IntMap.Nil => {} + final def foreachKey(f: Int => Unit): Unit = this match { + case IntMap.Bin(_, _, left, right) => { left.foreachKey(f); right.foreachKey(f) } + case IntMap.Tip(key, _) => f(key) + case IntMap.Nil => } - override def valuesIterator : Iterator[T] = this match { - case IntMap.Nil => Iterator.empty; - case _ => new IntMapValueIterator(this); + override def valuesIterator: Iterator[T] = this match { + case IntMap.Nil => Iterator.empty + case _ => new IntMapValueIterator(this) } /** - * Loop over the keys of the map. The same as keys.foreach(f), but may + * Loop over the keys of the map. The same as `keys.foreach(f)`, but may * be more efficient. * * @param f The loop body */ - final def foreachValue(f : T => Unit) : Unit = this match { - case IntMap.Bin(_, _, left, right) => {left.foreachValue(f); right.foreachValue(f); } - case IntMap.Tip(_, value) => f(value); - case IntMap.Nil => {}; + final def foreachValue(f: T => Unit): Unit = this match { + case IntMap.Bin(_, _, left, right) => { left.foreachValue(f); right.foreachValue(f) } + case IntMap.Tip(_, value) => f(value) + case IntMap.Nil => } override def stringPrefix = "IntMap" - override def isEmpty = this == IntMap.Nil; + override def isEmpty = this == IntMap.Nil - override def filter(f : ((Int, T)) => Boolean) : IntMap[T] = this match { + override def filter(f: ((Int, T)) => Boolean): IntMap[T] = this match { case IntMap.Bin(prefix, mask, left, right) => { - val (newleft, newright) = (left.filter(f), right.filter(f)); - if ((left eq newleft) && (right eq newright)) this; - else bin(prefix, mask, newleft, newright); + val (newleft, newright) = (left.filter(f), right.filter(f)) + if ((left eq newleft) && (right eq newright)) this + else bin(prefix, mask, newleft, newright) } case IntMap.Tip(key, value) => if (f((key, value))) this - else IntMap.Nil; - case IntMap.Nil => IntMap.Nil; + else IntMap.Nil + case IntMap.Nil => IntMap.Nil } - def transform[S](f : (Int, T) => S) : IntMap[S] = this match { - case b@IntMap.Bin(prefix, mask, left, right) => b.bin(left.transform(f), right.transform(f)); - case t@IntMap.Tip(key, value) => t.withValue(f(key, value)); - case IntMap.Nil => IntMap.Nil; + def transform[S](f: (Int, T) => S): IntMap[S] = this match { + case b@IntMap.Bin(prefix, mask, left, right) => b.bin(left.transform(f), right.transform(f)) + case t@IntMap.Tip(key, value) => t.withValue(f(key, value)) + case IntMap.Nil => IntMap.Nil } - final override def size : Int = this match { - case IntMap.Nil => 0; - case IntMap.Tip(_, _) => 1; - case IntMap.Bin(_, _, left, right) => left.size + right.size; + final override def size: Int = this match { + case IntMap.Nil => 0 + case IntMap.Tip(_, _) => 1 + case IntMap.Bin(_, _, left, right) => left.size + right.size } - final def get(key : Int) : Option[T] = this match { - case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.get(key) else right.get(key); - case IntMap.Tip(key2, value) => if (key == key2) Some(value) else None; - case IntMap.Nil => None; + final def get(key: Int): Option[T] = this match { + case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.get(key) else right.get(key) + case IntMap.Tip(key2, value) => if (key == key2) Some(value) else None + case IntMap.Nil => None } - final override def getOrElse[S >: T](key : Int, default : =>S) : S = this match { - case IntMap.Nil => default; - case IntMap.Tip(key2, value) => if (key == key2) value else default; - case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.getOrElse(key, default) else right.getOrElse(key, default); + final override def getOrElse[S >: T](key: Int, default: => S): S = this match { + case IntMap.Nil => default + case IntMap.Tip(key2, value) => if (key == key2) value else default + case IntMap.Bin(prefix, mask, left, right) => + if (zero(key, mask)) left.getOrElse(key, default) else right.getOrElse(key, default) } - final override def apply(key : Int) : T = this match { - case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key); - case IntMap.Tip(key2, value) => if (key == key2) value else sys.error("Key not found"); - case IntMap.Nil => sys.error("key not found"); + final override def apply(key: Int): T = this match { + case IntMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key) + case IntMap.Tip(key2, value) => if (key == key2) value else sys.error("Key not found") + case IntMap.Nil => sys.error("key not found") } def + [S >: T] (kv: (Int, S)): IntMap[S] = updated(kv._1, kv._2) - override def updated[S >: T](key : Int, value : S) : IntMap[S] = this match { - case IntMap.Bin(prefix, mask, left, right) => if (!hasMatch(key, prefix, mask)) join(key, IntMap.Tip(key, value), prefix, this); - else if (zero(key, mask)) IntMap.Bin(prefix, mask, left.updated(key, value), right) - else IntMap.Bin(prefix, mask, left, right.updated(key, value)); - case IntMap.Tip(key2, value2) => if (key == key2) IntMap.Tip(key, value); - else join(key, IntMap.Tip(key, value), key2, this); - case IntMap.Nil => IntMap.Tip(key, value); + override def updated[S >: T](key: Int, value: S): IntMap[S] = this match { + case IntMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, IntMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) IntMap.Bin(prefix, mask, left.updated(key, value), right) + else IntMap.Bin(prefix, mask, left, right.updated(key, value)) + case IntMap.Tip(key2, value2) => + if (key == key2) IntMap.Tip(key, value) + else join(key, IntMap.Tip(key, value), key2, this) + case IntMap.Nil => IntMap.Tip(key, value) } /** @@ -284,7 +286,7 @@ extends AbstractMap[Int, T] * Equivalent to: * {{{ * this.get(key) match { - * case None => this.update(key, value); + * case None => this.update(key, value) * case Some(oldvalue) => this.update(key, f(oldvalue, value) * } * }}} @@ -295,24 +297,26 @@ extends AbstractMap[Int, T] * @param f The function used to resolve conflicts. * @return The updated map. */ - def updateWith[S >: T](key : Int, value : S, f : (T, S) => S) : IntMap[S] = this match { - case IntMap.Bin(prefix, mask, left, right) => if (!hasMatch(key, prefix, mask)) join(key, IntMap.Tip(key, value), prefix, this); - else if (zero(key, mask)) IntMap.Bin(prefix, mask, left.updateWith(key, value, f), right) - else IntMap.Bin(prefix, mask, left, right.updateWith(key, value, f)); - case IntMap.Tip(key2, value2) => if (key == key2) IntMap.Tip(key, f(value2, value)); - else join(key, IntMap.Tip(key, value), key2, this); - case IntMap.Nil => IntMap.Tip(key, value); + def updateWith[S >: T](key: Int, value: S, f: (T, S) => S): IntMap[S] = this match { + case IntMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, IntMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) IntMap.Bin(prefix, mask, left.updateWith(key, value, f), right) + else IntMap.Bin(prefix, mask, left, right.updateWith(key, value, f)) + case IntMap.Tip(key2, value2) => + if (key == key2) IntMap.Tip(key, f(value2, value)) + else join(key, IntMap.Tip(key, value), key2, this) + case IntMap.Nil => IntMap.Tip(key, value) } - def - (key : Int) : IntMap[T] = this match { + def - (key: Int): IntMap[T] = this match { case IntMap.Bin(prefix, mask, left, right) => - if (!hasMatch(key, prefix, mask)) this; - else if (zero(key, mask)) bin(prefix, mask, left - key, right); - else bin(prefix, mask, left, right - key); + if (!hasMatch(key, prefix, mask)) this + else if (zero(key, mask)) bin(prefix, mask, left - key, right) + else bin(prefix, mask, left, right - key) case IntMap.Tip(key2, _) => - if (key == key2) IntMap.Nil; - else this; - case IntMap.Nil => IntMap.Nil; + if (key == key2) IntMap.Nil + else this + case IntMap.Nil => IntMap.Nil } /** @@ -324,7 +328,7 @@ extends AbstractMap[Int, T] * @param f The transforming function. * @return The modified map. */ - def modifyOrRemove[S](f : (Int, T) => Option[S]) : IntMap[S] = this match { + def modifyOrRemove[S](f: (Int, T) => Option[S]): IntMap[S] = this match { case IntMap.Bin(prefix, mask, left, right) => val newleft = left.modifyOrRemove(f) val newright = right.modifyOrRemove(f) @@ -350,25 +354,25 @@ extends AbstractMap[Int, T] * @param f The function used to resolve conflicts between two mappings. * @return Union of `this` and `that`, with identical key conflicts resolved using the function `f`. */ - def unionWith[S >: T](that : IntMap[S], f : (Int, S, S) => S) : IntMap[S] = (this, that) match{ + def unionWith[S >: T](that: IntMap[S], f: (Int, S, S) => S): IntMap[S] = (this, that) match{ case (IntMap.Bin(p1, m1, l1, r1), that@(IntMap.Bin(p2, m2, l2, r2))) => if (shorter(m1, m2)) { - if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed - else if (zero(p2, m1)) IntMap.Bin(p1, m1, l1.unionWith(that, f), r1); - else IntMap.Bin(p1, m1, l1, r1.unionWith(that, f)); + if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that) // TODO: remove [S] when SI-5548 is fixed + else if (zero(p2, m1)) IntMap.Bin(p1, m1, l1.unionWith(that, f), r1) + else IntMap.Bin(p1, m1, l1, r1.unionWith(that, f)) } else if (shorter(m2, m1)){ - if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed - else if (zero(p1, m2)) IntMap.Bin(p2, m2, this.unionWith(l2, f), r2); - else IntMap.Bin(p2, m2, l2, this.unionWith(r2, f)); + if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that) // TODO: remove [S] when SI-5548 is fixed + else if (zero(p1, m2)) IntMap.Bin(p2, m2, this.unionWith(l2, f), r2) + else IntMap.Bin(p2, m2, l2, this.unionWith(r2, f)) } else { - if (p1 == p2) IntMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)); - else join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed + if (p1 == p2) IntMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)) + else join[S](p1, this, p2, that) // TODO: remove [S] when SI-5548 is fixed } - case (IntMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x)); - case (x, IntMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)); - case (IntMap.Nil, x) => x; - case (x, IntMap.Nil) => x; + case (IntMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x)) + case (x, IntMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)) + case (IntMap.Nil, x) => x + case (x, IntMap.Nil) => x } /** @@ -382,13 +386,13 @@ extends AbstractMap[Int, T] * @param f The combining function. * @return Intersection of `this` and `that`, with values for identical keys produced by function `f`. */ - def intersectionWith[S, R](that : IntMap[S], f : (Int, T, S) => R) : IntMap[R] = (this, that) match { + def intersectionWith[S, R](that: IntMap[S], f: (Int, T, S) => R): IntMap[R] = (this, that) match { case (IntMap.Bin(p1, m1, l1, r1), that@IntMap.Bin(p2, m2, l2, r2)) => if (shorter(m1, m2)) { if (!hasMatch(p2, p1, m1)) IntMap.Nil else if (zero(p2, m1)) l1.intersectionWith(that, f) else r1.intersectionWith(that, f) - } else if (m1 == m2) bin(p1, m1, l1.intersectionWith(l2, f), r1.intersectionWith(r2, f)); + } else if (m1 == m2) bin(p1, m1, l1.intersectionWith(l2, f), r1.intersectionWith(r2, f)) else { if (!hasMatch(p1, p2, m2)) IntMap.Nil else if (zero(p1, m2)) this.intersectionWith(l2, f) @@ -413,15 +417,16 @@ extends AbstractMap[Int, T] * @param that The map to intersect with. * @return A map with all the keys both in `this` and `that`, mapped to corresponding values from `this`. */ - def intersection[R](that : IntMap[R]) : IntMap[T] = this.intersectionWith(that, (key : Int, value : T, value2 : R) => value); + def intersection[R](that: IntMap[R]): IntMap[T] = + this.intersectionWith(that, (key: Int, value: T, value2: R) => value) - def ++[S >: T](that : IntMap[S]) = + def ++[S >: T](that: IntMap[S]) = this.unionWith[S](that, (key, x, y) => y) /** * The entry with the lowest key value considered in unsigned order. */ - final def firstKey : Int = this match { + final def firstKey: Int = this match { case Bin(_, _, l, r) => l.firstKey case Tip(k, v) => k case IntMap.Nil => sys.error("Empty set") @@ -430,7 +435,7 @@ extends AbstractMap[Int, T] /** * The entry with the highest key value considered in unsigned order. */ - final def lastKey : Int = this match { + final def lastKey: Int = this match { case Bin(_, _, l, r) => r.lastKey case Tip(k, v) => k case IntMap.Nil => sys.error("Empty set") -- cgit v1.2.3 From f02e19ea98d4d280c61a9809f9d5c30a9ac0c03f Mon Sep 17 00:00:00 2001 From: soc Date: Sun, 24 Jun 2012 04:29:47 +0300 Subject: LongMap: Made life a bit less scary for those doing source archaeology. --- .../scala/collection/immutable/LongMap.scala | 318 +++++++++++---------- 1 file changed, 161 insertions(+), 157 deletions(-) diff --git a/src/library/scala/collection/immutable/LongMap.scala b/src/library/scala/collection/immutable/LongMap.scala index 8a316f37de..002027b162 100644 --- a/src/library/scala/collection/immutable/LongMap.scala +++ b/src/library/scala/collection/immutable/LongMap.scala @@ -18,17 +18,17 @@ import scala.collection.mutable.{ Builder, MapBuilder } private[immutable] object LongMapUtils extends BitOperations.Long { def branchMask(i: Long, j: Long) = highestOneBit(i ^ j) - def join[T](p1 : Long, t1 : LongMap[T], p2 : Long, t2 : LongMap[T]) : LongMap[T] = { - val m = branchMask(p1, p2); - val p = mask(p1, m); + def join[T](p1: Long, t1: LongMap[T], p2: Long, t2: LongMap[T]): LongMap[T] = { + val m = branchMask(p1, p2) + val p = mask(p1, m) if (zero(p1, m)) LongMap.Bin(p, m, t1, t2) - else LongMap.Bin(p, m, t2, t1); + else LongMap.Bin(p, m, t2, t1) } - def bin[T](prefix : Long, mask : Long, left : LongMap[T], right : LongMap[T]) : LongMap[T] = (left, right) match { - case (left, LongMap.Nil) => left; - case (LongMap.Nil, right) => right; - case (left, right) => LongMap.Bin(prefix, mask, left, right); + def bin[T](prefix: Long, mask: Long, left: LongMap[T], right: LongMap[T]): LongMap[T] = (left, right) match { + case (left, LongMap.Nil) => left + case (LongMap.Nil, right) => right + case (left, right) => LongMap.Bin(prefix, mask, left, right) } } @@ -49,29 +49,29 @@ object LongMap { def apply(): Builder[(Long, B), LongMap[B]] = new MapBuilder[Long, B, LongMap[B]](empty[B]) } - def empty[T] : LongMap[T] = LongMap.Nil; - def singleton[T](key : Long, value : T) : LongMap[T] = LongMap.Tip(key, value); - def apply[T](elems : (Long, T)*) : LongMap[T] = - elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)); + def empty[T]: LongMap[T] = LongMap.Nil + def singleton[T](key: Long, value: T): LongMap[T] = LongMap.Tip(key, value) + def apply[T](elems: (Long, T)*): LongMap[T] = + elems.foldLeft(empty[T])((x, y) => x.updated(y._1, y._2)) private[immutable] case object Nil extends LongMap[Nothing] { // Important, don't remove this! See IntMap for explanation. override def equals(that : Any) = that match { - case (that : AnyRef) if (this eq that) => true; - case (that : LongMap[_]) => false; // The only empty LongMaps are eq Nil - case that => super.equals(that); + case (that: AnyRef) if (this eq that) => true + case (that: LongMap[_]) => false // The only empty LongMaps are eq Nil + case that => super.equals(that) } - }; + } - private[immutable] case class Tip[+T](key : Long, value : T) extends LongMap[T]{ - def withValue[S](s : S) = - if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[LongMap.Tip[S]]; - else LongMap.Tip(key, s); + private[immutable] case class Tip[+T](key: Long, value: T) extends LongMap[T] { + def withValue[S](s: S) = + if (s.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) this.asInstanceOf[LongMap.Tip[S]] + else LongMap.Tip(key, s) } - private[immutable] case class Bin[+T](prefix : Long, mask : Long, left : LongMap[T], right : LongMap[T]) extends LongMap[T]{ - def bin[S](left : LongMap[S], right : LongMap[S]) : LongMap[S] = { - if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[LongMap.Bin[S]]; - else LongMap.Bin[S](prefix, mask, left, right); + private[immutable] case class Bin[+T](prefix: Long, mask: Long, left: LongMap[T], right: LongMap[T]) extends LongMap[T] { + def bin[S](left: LongMap[S], right: LongMap[S]): LongMap[S] = { + if ((this.left eq left) && (this.right eq right)) this.asInstanceOf[LongMap.Bin[S]] + else LongMap.Bin[S](prefix, mask, left, right) } } } @@ -79,64 +79,62 @@ object LongMap { import LongMap._ // Iterator over a non-empty LongMap. -private[immutable] abstract class LongMapIterator[V, T](it : LongMap[V]) extends AbstractIterator[T] { +private[immutable] abstract class LongMapIterator[V, T](it: LongMap[V]) extends AbstractIterator[T] { // Basically this uses a simple stack to emulate conversion over the tree. However // because we know that Longs are only 64 bits we can have at most 64 LongMap.Bins and // one LongMap.Tip sitting on the tree at any point. Therefore we know the maximum stack // depth is 65 - var index = 0; - var buffer = new Array[AnyRef](65); + var index = 0 + var buffer = new Array[AnyRef](65) def pop() = { - index -= 1; - buffer(index).asInstanceOf[LongMap[V]]; + index -= 1 + buffer(index).asInstanceOf[LongMap[V]] } - def push(x : LongMap[V]) { - buffer(index) = x.asInstanceOf[AnyRef]; - index += 1; + def push(x: LongMap[V]) { + buffer(index) = x.asInstanceOf[AnyRef] + index += 1 } push(it); /** * What value do we assign to a tip? */ - def valueOf(tip : LongMap.Tip[V]) : T; + def valueOf(tip: LongMap.Tip[V]): T - def hasNext = index != 0; - final def next : T = + def hasNext = index != 0 + final def next: T = pop() match { case LongMap.Bin(_,_, t@LongMap.Tip(_, _), right) => { - push(right); - valueOf(t); + push(right) + valueOf(t) } case LongMap.Bin(_, _, left, right) => { - push(right); - push(left); - next; + push(right) + push(left) + next } - case t@LongMap.Tip(_, _) => valueOf(t); + case t@LongMap.Tip(_, _) => valueOf(t) // This should never happen. We don't allow LongMap.Nil in subtrees of the LongMap // and don't return an LongMapIterator for LongMap.Nil. - case LongMap.Nil => sys.error("Empty maps not allowed as subtrees"); + case LongMap.Nil => sys.error("Empty maps not allowed as subtrees") } } -private[immutable] class LongMapEntryIterator[V](it : LongMap[V]) extends LongMapIterator[V, (Long, V)](it){ - def valueOf(tip : LongMap.Tip[V]) = (tip.key, tip.value); +private[immutable] class LongMapEntryIterator[V](it: LongMap[V]) extends LongMapIterator[V, (Long, V)](it){ + def valueOf(tip: LongMap.Tip[V]) = (tip.key, tip.value) } -private[immutable] class LongMapValueIterator[V](it : LongMap[V]) extends LongMapIterator[V, V](it){ - def valueOf(tip : LongMap.Tip[V]) = tip.value; +private[immutable] class LongMapValueIterator[V](it: LongMap[V]) extends LongMapIterator[V, V](it){ + def valueOf(tip: LongMap.Tip[V]) = tip.value } -private[immutable] class LongMapKeyIterator[V](it : LongMap[V]) extends LongMapIterator[V, Long](it){ - def valueOf(tip : LongMap.Tip[V]) = tip.key; +private[immutable] class LongMapKeyIterator[V](it: LongMap[V]) extends LongMapIterator[V, Long](it){ + def valueOf(tip: LongMap.Tip[V]) = tip.key } -import LongMap._; - /** * Specialised immutable map structure for long keys, based on * Fast Mergeable Long Maps @@ -157,12 +155,12 @@ extends AbstractMap[Long, T] with Map[Long, T] with MapLike[Long, T, LongMap[T]] { - override def empty: LongMap[T] = LongMap.Nil; + override def empty: LongMap[T] = LongMap.Nil override def toList = { - val buffer = new scala.collection.mutable.ListBuffer[(Long, T)]; - foreach(buffer += _); - buffer.toList; + val buffer = new scala.collection.mutable.ListBuffer[(Long, T)] + foreach(buffer += _) + buffer.toList } /** @@ -171,22 +169,22 @@ extends AbstractMap[Long, T] * @return an iterator over pairs of long keys and corresponding values. */ def iterator: Iterator[(Long, T)] = this match { - case LongMap.Nil => Iterator.empty; - case _ => new LongMapEntryIterator(this); + case LongMap.Nil => Iterator.empty + case _ => new LongMapEntryIterator(this) } /** * Loops over the key, value pairs of the map in unsigned order of the keys. */ - override final def foreach[U](f : ((Long, T)) => U) : Unit = this match { - case LongMap.Bin(_, _, left, right) => {left.foreach(f); right.foreach(f); } + override final def foreach[U](f: ((Long, T)) => U): Unit = this match { + case LongMap.Bin(_, _, left, right) => { left.foreach(f); right.foreach(f) } case LongMap.Tip(key, value) => f((key, value)); - case LongMap.Nil => {}; + case LongMap.Nil => } - override def keysIterator : Iterator[Long] = this match { - case LongMap.Nil => Iterator.empty; - case _ => new LongMapKeyIterator(this); + override def keysIterator: Iterator[Long] = this match { + case LongMap.Nil => Iterator.empty + case _ => new LongMapKeyIterator(this) } /** @@ -195,15 +193,15 @@ extends AbstractMap[Long, T] * * @param f The loop body */ - final def foreachKey(f : Long => Unit) : Unit = this match { - case LongMap.Bin(_, _, left, right) => {left.foreachKey(f); right.foreachKey(f); } - case LongMap.Tip(key, _) => f(key); - case LongMap.Nil => {} + final def foreachKey(f: Long => Unit): Unit = this match { + case LongMap.Bin(_, _, left, right) => { left.foreachKey(f); right.foreachKey(f) } + case LongMap.Tip(key, _) => f(key) + case LongMap.Nil => } - override def valuesIterator : Iterator[T] = this match { - case LongMap.Nil => Iterator.empty; - case _ => new LongMapValueIterator(this); + override def valuesIterator: Iterator[T] = this match { + case LongMap.Nil => Iterator.empty + case _ => new LongMapValueIterator(this) } /** @@ -212,67 +210,70 @@ extends AbstractMap[Long, T] * * @param f The loop body */ - final def foreachValue(f : T => Unit) : Unit = this match { - case LongMap.Bin(_, _, left, right) => {left.foreachValue(f); right.foreachValue(f); } - case LongMap.Tip(_, value) => f(value); - case LongMap.Nil => {}; + final def foreachValue(f: T => Unit): Unit = this match { + case LongMap.Bin(_, _, left, right) => { left.foreachValue(f); right.foreachValue(f) } + case LongMap.Tip(_, value) => f(value) + case LongMap.Nil => } override def stringPrefix = "LongMap" - override def isEmpty = this == LongMap.Nil; + override def isEmpty = this == LongMap.Nil - override def filter(f : ((Long, T)) => Boolean) : LongMap[T] = this match { + override def filter(f: ((Long, T)) => Boolean): LongMap[T] = this match { case LongMap.Bin(prefix, mask, left, right) => { - val (newleft, newright) = (left.filter(f), right.filter(f)); - if ((left eq newleft) && (right eq newright)) this; - else bin(prefix, mask, newleft, newright); + val (newleft, newright) = (left.filter(f), right.filter(f)) + if ((left eq newleft) && (right eq newright)) this + else bin(prefix, mask, newleft, newright) } case LongMap.Tip(key, value) => if (f((key, value))) this - else LongMap.Nil; - case LongMap.Nil => LongMap.Nil; + else LongMap.Nil + case LongMap.Nil => LongMap.Nil } - def transform[S](f : (Long, T) => S) : LongMap[S] = this match { - case b@LongMap.Bin(prefix, mask, left, right) => b.bin(left.transform(f), right.transform(f)); - case t@LongMap.Tip(key, value) => t.withValue(f(key, value)); - case LongMap.Nil => LongMap.Nil; + def transform[S](f: (Long, T) => S): LongMap[S] = this match { + case b@LongMap.Bin(prefix, mask, left, right) => b.bin(left.transform(f), right.transform(f)) + case t@LongMap.Tip(key, value) => t.withValue(f(key, value)) + case LongMap.Nil => LongMap.Nil } - final override def size : Int = this match { - case LongMap.Nil => 0; - case LongMap.Tip(_, _) => 1; - case LongMap.Bin(_, _, left, right) => left.size + right.size; + final override def size: Int = this match { + case LongMap.Nil => 0 + case LongMap.Tip(_, _) => 1 + case LongMap.Bin(_, _, left, right) => left.size + right.size } - final def get(key : Long) : Option[T] = this match { - case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.get(key) else right.get(key); - case LongMap.Tip(key2, value) => if (key == key2) Some(value) else None; - case LongMap.Nil => None; + final def get(key: Long): Option[T] = this match { + case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.get(key) else right.get(key) + case LongMap.Tip(key2, value) => if (key == key2) Some(value) else None + case LongMap.Nil => None } - final override def getOrElse[S >: T](key : Long, default : =>S) : S = this match { - case LongMap.Nil => default; - case LongMap.Tip(key2, value) => if (key == key2) value else default; - case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left.getOrElse(key, default) else right.getOrElse(key, default); + final override def getOrElse[S >: T](key: Long, default: => S): S = this match { + case LongMap.Nil => default + case LongMap.Tip(key2, value) => if (key == key2) value else default + case LongMap.Bin(prefix, mask, left, right) => + if (zero(key, mask)) left.getOrElse(key, default) else right.getOrElse(key, default) } - final override def apply(key : Long) : T = this match { - case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key); - case LongMap.Tip(key2, value) => if (key == key2) value else sys.error("Key not found"); - case LongMap.Nil => sys.error("key not found"); + final override def apply(key: Long): T = this match { + case LongMap.Bin(prefix, mask, left, right) => if (zero(key, mask)) left(key) else right(key) + case LongMap.Tip(key2, value) => if (key == key2) value else sys.error("Key not found") + case LongMap.Nil => sys.error("key not found") } def + [S >: T] (kv: (Long, S)): LongMap[S] = updated(kv._1, kv._2) - override def updated[S >: T](key : Long, value : S) : LongMap[S] = this match { - case LongMap.Bin(prefix, mask, left, right) => if (!hasMatch(key, prefix, mask)) join(key, LongMap.Tip(key, value), prefix, this); - else if (zero(key, mask)) LongMap.Bin(prefix, mask, left.updated(key, value), right) - else LongMap.Bin(prefix, mask, left, right.updated(key, value)); - case LongMap.Tip(key2, value2) => if (key == key2) LongMap.Tip(key, value); - else join(key, LongMap.Tip(key, value), key2, this); - case LongMap.Nil => LongMap.Tip(key, value); + override def updated[S >: T](key: Long, value: S): LongMap[S] = this match { + case LongMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, LongMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) LongMap.Bin(prefix, mask, left.updated(key, value), right) + else LongMap.Bin(prefix, mask, left, right.updated(key, value)) + case LongMap.Tip(key2, value2) => + if (key == key2) LongMap.Tip(key, value) + else join(key, LongMap.Tip(key, value), key2, this) + case LongMap.Nil => LongMap.Tip(key, value) } /** @@ -281,7 +282,7 @@ extends AbstractMap[Long, T] * Equivalent to * {{{ * this.get(key) match { - * case None => this.update(key, value); + * case None => this.update(key, value) * case Some(oldvalue) => this.update(key, f(oldvalue, value) * } * }}} @@ -292,24 +293,26 @@ extends AbstractMap[Long, T] * @param f The function used to resolve conflicts. * @return The updated map. */ - def updateWith[S >: T](key : Long, value : S, f : (T, S) => S) : LongMap[S] = this match { - case LongMap.Bin(prefix, mask, left, right) => if (!hasMatch(key, prefix, mask)) join(key, LongMap.Tip(key, value), prefix, this); - else if (zero(key, mask)) LongMap.Bin(prefix, mask, left.updateWith(key, value, f), right) - else LongMap.Bin(prefix, mask, left, right.updateWith(key, value, f)); - case LongMap.Tip(key2, value2) => if (key == key2) LongMap.Tip(key, f(value2, value)); - else join(key, LongMap.Tip(key, value), key2, this); - case LongMap.Nil => LongMap.Tip(key, value); + def updateWith[S >: T](key: Long, value: S, f: (T, S) => S): LongMap[S] = this match { + case LongMap.Bin(prefix, mask, left, right) => + if (!hasMatch(key, prefix, mask)) join(key, LongMap.Tip(key, value), prefix, this) + else if (zero(key, mask)) LongMap.Bin(prefix, mask, left.updateWith(key, value, f), right) + else LongMap.Bin(prefix, mask, left, right.updateWith(key, value, f)) + case LongMap.Tip(key2, value2) => + if (key == key2) LongMap.Tip(key, f(value2, value)) + else join(key, LongMap.Tip(key, value), key2, this) + case LongMap.Nil => LongMap.Tip(key, value) } - def -(key : Long) : LongMap[T] = this match { + def -(key: Long): LongMap[T] = this match { case LongMap.Bin(prefix, mask, left, right) => - if (!hasMatch(key, prefix, mask)) this; - else if (zero(key, mask)) bin(prefix, mask, left - key, right); - else bin(prefix, mask, left, right - key); + if (!hasMatch(key, prefix, mask)) this + else if (zero(key, mask)) bin(prefix, mask, left - key, right) + else bin(prefix, mask, left, right - key) case LongMap.Tip(key2, _) => - if (key == key2) LongMap.Nil; - else this; - case LongMap.Nil => LongMap.Nil; + if (key == key2) LongMap.Nil + else this + case LongMap.Nil => LongMap.Nil } /** @@ -321,21 +324,21 @@ extends AbstractMap[Long, T] * @param f The transforming function. * @return The modified map. */ - def modifyOrRemove[S](f : (Long, T) => Option[S]) : LongMap[S] = this match { + def modifyOrRemove[S](f: (Long, T) => Option[S]): LongMap[S] = this match { case LongMap.Bin(prefix, mask, left, right) => { - val newleft = left.modifyOrRemove(f); - val newright = right.modifyOrRemove(f); - if ((left eq newleft) && (right eq newright)) this.asInstanceOf[LongMap[S]]; + val newleft = left.modifyOrRemove(f) + val newright = right.modifyOrRemove(f) + if ((left eq newleft) && (right eq newright)) this.asInstanceOf[LongMap[S]] else bin(prefix, mask, newleft, newright) } case LongMap.Tip(key, value) => f(key, value) match { - case None => LongMap.Nil; + case None => LongMap.Nil case Some(value2) => //hack to preserve sharing if (value.asInstanceOf[AnyRef] eq value2.asInstanceOf[AnyRef]) this.asInstanceOf[LongMap[S]] - else LongMap.Tip(key, value2); + else LongMap.Tip(key, value2) } - case LongMap.Nil => LongMap.Nil; + case LongMap.Nil => LongMap.Nil } /** @@ -346,25 +349,25 @@ extends AbstractMap[Long, T] * @param f The function used to resolve conflicts between two mappings. * @return Union of `this` and `that`, with identical key conflicts resolved using the function `f`. */ - def unionWith[S >: T](that : LongMap[S], f : (Long, S, S) => S) : LongMap[S] = (this, that) match{ + def unionWith[S >: T](that: LongMap[S], f: (Long, S, S) => S): LongMap[S] = (this, that) match{ case (LongMap.Bin(p1, m1, l1, r1), that@(LongMap.Bin(p2, m2, l2, r2))) => if (shorter(m1, m2)) { - if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed - else if (zero(p2, m1)) LongMap.Bin(p1, m1, l1.unionWith(that, f), r1); - else LongMap.Bin(p1, m1, l1, r1.unionWith(that, f)); + if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that) // TODO: remove [S] when SI-5548 is fixed + else if (zero(p2, m1)) LongMap.Bin(p1, m1, l1.unionWith(that, f), r1) + else LongMap.Bin(p1, m1, l1, r1.unionWith(that, f)) } else if (shorter(m2, m1)){ - if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed - else if (zero(p1, m2)) LongMap.Bin(p2, m2, this.unionWith(l2, f), r2); - else LongMap.Bin(p2, m2, l2, this.unionWith(r2, f)); + if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that) // TODO: remove [S] when SI-5548 is fixed + else if (zero(p1, m2)) LongMap.Bin(p2, m2, this.unionWith(l2, f), r2) + else LongMap.Bin(p2, m2, l2, this.unionWith(r2, f)) } else { - if (p1 == p2) LongMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)); - else join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed + if (p1 == p2) LongMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)) + else join[S](p1, this, p2, that) // TODO: remove [S] when SI-5548 is fixed } - case (LongMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x)); // TODO: remove [S] when SI-5548 is fixed - case (x, LongMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)); - case (LongMap.Nil, x) => x; - case (x, LongMap.Nil) => x; + case (LongMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x)) // TODO: remove [S] when SI-5548 is fixed + case (x, LongMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)) + case (LongMap.Nil, x) => x + case (x, LongMap.Nil) => x } /** @@ -378,27 +381,27 @@ extends AbstractMap[Long, T] * @param f The combining function. * @return Intersection of `this` and `that`, with values for identical keys produced by function `f`. */ - def intersectionWith[S, R](that : LongMap[S], f : (Long, T, S) => R) : LongMap[R] = (this, that) match { + def intersectionWith[S, R](that: LongMap[S], f: (Long, T, S) => R): LongMap[R] = (this, that) match { case (LongMap.Bin(p1, m1, l1, r1), that@LongMap.Bin(p2, m2, l2, r2)) => if (shorter(m1, m2)) { - if (!hasMatch(p2, p1, m1)) LongMap.Nil; - else if (zero(p2, m1)) l1.intersectionWith(that, f); - else r1.intersectionWith(that, f); - } else if (m1 == m2) bin(p1, m1, l1.intersectionWith(l2, f), r1.intersectionWith(r2, f)); + if (!hasMatch(p2, p1, m1)) LongMap.Nil + else if (zero(p2, m1)) l1.intersectionWith(that, f) + else r1.intersectionWith(that, f) + } else if (m1 == m2) bin(p1, m1, l1.intersectionWith(l2, f), r1.intersectionWith(r2, f)) else { - if (!hasMatch(p1, p2, m2)) LongMap.Nil; - else if (zero(p1, m2)) this.intersectionWith(l2, f); - else this.intersectionWith(r2, f); + if (!hasMatch(p1, p2, m2)) LongMap.Nil + else if (zero(p1, m2)) this.intersectionWith(l2, f) + else this.intersectionWith(r2, f) } case (LongMap.Tip(key, value), that) => that.get(key) match { - case None => LongMap.Nil; - case Some(value2) => LongMap.Tip(key, f(key, value, value2)); + case None => LongMap.Nil + case Some(value2) => LongMap.Tip(key, f(key, value, value2)) } case (_, LongMap.Tip(key, value)) => this.get(key) match { - case None => LongMap.Nil; - case Some(value2) => LongMap.Tip(key, f(key, value2, value)); + case None => LongMap.Nil + case Some(value2) => LongMap.Tip(key, f(key, value2, value)) } - case (_, _) => LongMap.Nil; + case (_, _) => LongMap.Nil } /** @@ -409,9 +412,10 @@ extends AbstractMap[Long, T] * @param that The map to intersect with. * @return A map with all the keys both in `this` and `that`, mapped to corresponding values from `this`. */ - def intersection[R](that : LongMap[R]) : LongMap[T] = this.intersectionWith(that, (key : Long, value : T, value2 : R) => value); + def intersection[R](that: LongMap[R]): LongMap[T] = + this.intersectionWith(that, (key: Long, value: T, value2: R) => value) - def ++[S >: T](that : LongMap[S]) = + def ++[S >: T](that: LongMap[S]) = this.unionWith[S](that, (key, x, y) => y) } -- cgit v1.2.3 From 0db7dcb9ee31bc35343781715c92dd3bf14990e9 Mon Sep 17 00:00:00 2001 From: Miguel Garcia Date: Sun, 24 Jun 2012 18:24:03 +0200 Subject: emit JVM InnerClasses attribute for bean info classes --- .../scala/tools/nsc/backend/jvm/GenASM.scala | 172 ++++++++++----------- 1 file changed, 85 insertions(+), 87 deletions(-) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 0c527fbaf4..59adcc637a 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -458,6 +458,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters { val CLASS_CONSTRUCTOR_NAME = "" val INSTANCE_CONSTRUCTOR_NAME = "" + val INNER_CLASSES_FLAGS = + (asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED | + asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_INTERFACE | asm.Opcodes.ACC_ABSTRACT) + // ----------------------------------------------------------------------------------------- // factory methods // ----------------------------------------------------------------------------------------- @@ -644,6 +648,86 @@ abstract class GenASM extends SubComponent with BytecodeWriters { def isDeprecated(sym: Symbol): Boolean = { sym.annotations exists (_ matches definitions.DeprecatedAttr) } + def addInnerClasses(csym: Symbol, jclass: asm.ClassVisitor) { + /** The outer name for this inner class. Note that it returns null + * when the inner class should not get an index in the constant pool. + * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS. + */ + def outerName(innerSym: Symbol): String = { + if (innerSym.originalEnclosingMethod != NoSymbol) + null + else { + val outerName = javaName(innerSym.rawowner) + if (isTopLevelModule(innerSym.rawowner)) "" + nme.stripModuleSuffix(newTermName(outerName)) + else outerName + } + } + + def innerName(innerSym: Symbol): String = + if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction) + null + else + innerSym.rawname + innerSym.moduleSuffix + + // add inner classes which might not have been referenced yet + afterErasure { + for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) + innerClassBuffer += m + } + + val allInners: List[Symbol] = innerClassBuffer.toList + if (allInners.nonEmpty) { + debuglog(csym.fullName('.') + " contains " + allInners.size + " inner classes.") + + // entries ready to be serialized into the classfile, used to detect duplicates. + val entries = mutable.Map.empty[String, String] + + // sort them so inner classes succeed their enclosing class to satisfy the Eclipse Java compiler + for (innerSym <- allInners sortBy (_.name.length)) { // TODO why not sortBy (_.name.toString()) ?? + val flags = mkFlags( + if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0, + javaFlags(innerSym), + if(isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag + ) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED) + val jname = javaName(innerSym) // never null + val oname = outerName(innerSym) // null when method-enclosed + val iname = innerName(innerSym) // null for anonymous inner class + + // Mimicking javap inner class output + debuglog( + if (oname == null || iname == null) "//class " + jname + else "//%s=class %s of class %s".format(iname, jname, oname) + ) + + assert(jname != null, "javaName is broken.") // documentation + val doAdd = entries.get(jname) match { + // TODO is it ok for prevOName to be null? (Someone should really document the invariants of the InnerClasses bytecode attribute) + case Some(prevOName) => + // this occurs e.g. when innerClassBuffer contains both class Thread$State, object Thread$State, + // i.e. for them it must be the case that oname == java/lang/Thread + assert(prevOName == oname, "duplicate") + false + case None => true + } + + if(doAdd) { + entries += (jname -> oname) + jclass.visitInnerClass(jname, oname, iname, flags) + } + + /* + * TODO assert (JVMS 4.7.6 The InnerClasses attribute) + * If a class file has a version number that is greater than or equal to 51.0, and + * has an InnerClasses attribute in its attributes table, then for all entries in the + * classes array of the InnerClasses attribute, the value of the + * outer_class_info_index item must be zero if the value of the + * inner_name_index item is zero. + */ + + } + } + } + } // end of class JBuilder @@ -654,10 +738,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { // more constants // ----------------------------------------------------------------------------------------- - val INNER_CLASSES_FLAGS = - (asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED | - asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_INTERFACE | asm.Opcodes.ACC_ABSTRACT) - val PublicStatic = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC val PublicStaticFinal = asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL @@ -969,86 +1049,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters { } } - def addInnerClasses(csym: Symbol, jclass: asm.ClassVisitor) { - /** The outer name for this inner class. Note that it returns null - * when the inner class should not get an index in the constant pool. - * That means non-member classes (anonymous). See Section 4.7.5 in the JVMS. - */ - def outerName(innerSym: Symbol): String = { - if (innerSym.originalEnclosingMethod != NoSymbol) - null - else { - val outerName = javaName(innerSym.rawowner) - if (isTopLevelModule(innerSym.rawowner)) "" + nme.stripModuleSuffix(newTermName(outerName)) - else outerName - } - } - - def innerName(innerSym: Symbol): String = - if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction) - null - else - innerSym.rawname + innerSym.moduleSuffix - - // add inner classes which might not have been referenced yet - afterErasure { - for (sym <- List(csym, csym.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass) - innerClassBuffer += m - } - - val allInners: List[Symbol] = innerClassBuffer.toList - if (allInners.nonEmpty) { - debuglog(csym.fullName('.') + " contains " + allInners.size + " inner classes.") - - // entries ready to be serialized into the classfile, used to detect duplicates. - val entries = mutable.Map.empty[String, String] - - // sort them so inner classes succeed their enclosing class to satisfy the Eclipse Java compiler - for (innerSym <- allInners sortBy (_.name.length)) { // TODO why not sortBy (_.name.toString()) ?? - val flags = mkFlags( - if (innerSym.rawowner.hasModuleFlag) asm.Opcodes.ACC_STATIC else 0, - javaFlags(innerSym), - if(isDeprecated(innerSym)) asm.Opcodes.ACC_DEPRECATED else 0 // ASM pseudo-access flag - ) & (INNER_CLASSES_FLAGS | asm.Opcodes.ACC_DEPRECATED) - val jname = javaName(innerSym) // never null - val oname = outerName(innerSym) // null when method-enclosed - val iname = innerName(innerSym) // null for anonymous inner class - - // Mimicking javap inner class output - debuglog( - if (oname == null || iname == null) "//class " + jname - else "//%s=class %s of class %s".format(iname, jname, oname) - ) - - assert(jname != null, "javaName is broken.") // documentation - val doAdd = entries.get(jname) match { - // TODO is it ok for prevOName to be null? (Someone should really document the invariants of the InnerClasses bytecode attribute) - case Some(prevOName) => - // this occurs e.g. when innerClassBuffer contains both class Thread$State, object Thread$State, - // i.e. for them it must be the case that oname == java/lang/Thread - assert(prevOName == oname, "duplicate") - false - case None => true - } - - if(doAdd) { - entries += (jname -> oname) - jclass.visitInnerClass(jname, oname, iname, flags) - } - - /* - * TODO assert (JVMS 4.7.6 The InnerClasses attribute) - * If a class file has a version number that is greater than or equal to 51.0, and - * has an InnerClasses attribute in its attributes table, then for all entries in the - * classes array of the InnerClasses attribute, the value of the - * outer_class_info_index item must be zero if the value of the - * inner_name_index item is zero. - */ - - } - } - } - /** Adds a @remote annotation, actual use unknown. * * Invoked from genMethod() and addForwarder(). @@ -3033,9 +3033,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments constructor.visitEnd() - // TODO no inner classes attribute is written. Confirm intent. - assert(innerClassBuffer.isEmpty, innerClassBuffer) - + addInnerClasses(clasz.symbol, beanInfoClass) beanInfoClass.visitEnd() writeIfNotTooBig("BeanInfo ", beanInfoName, beanInfoClass, clasz.symbol) -- cgit v1.2.3 From e7feeaafe6ba83171714ca78b06afabb9320d20d Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Mon, 25 Jun 2012 09:55:31 +0200 Subject: Minor followup on SI-4842: remove awkward condition. Review by @retronym. --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 4 ++-- test/files/neg/t4842.check | 7 +++++++ test/files/neg/t4842.scala | 7 +++++++ test/files/neg/t4842a.check | 4 ---- test/files/neg/t4842a.scala | 3 --- test/files/neg/t4842b.check | 4 ---- test/files/neg/t4842b.scala | 3 --- 7 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 test/files/neg/t4842.check create mode 100644 test/files/neg/t4842.scala delete mode 100644 test/files/neg/t4842a.check delete mode 100644 test/files/neg/t4842a.scala delete mode 100644 test/files/neg/t4842b.check delete mode 100644 test/files/neg/t4842b.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 69d3fd7f47..6bb980260f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1833,7 +1833,7 @@ trait Typers extends Modes with Adaptations with Tags { val params = fn.tpe.params val args2 = if (params.isEmpty || !isRepeatedParamType(params.last.tpe)) args else args.take(params.length - 1) :+ EmptyTree - assert(sameLength(args2, params), "mismatch " + clazz + " " + (params map (_.tpe)) + " " + args2)//debug + assert(sameLength(args2, params) || call.isErrorTyped, "mismatch " + clazz + " " + (params map (_.tpe)) + " " + args2)//debug (superConstr, args1 ::: args2) case Block(stats, expr) if !stats.isEmpty => decompose(stats.last) @@ -2036,7 +2036,7 @@ trait Typers extends Modes with Adaptations with Tags { transformedOrTyped(ddef.rhs, EXPRmode, tpt1.tpe) } - if (meth.isClassConstructor && !isPastTyper && !reporter.hasErrors && !meth.owner.isSubClass(AnyValClass)) { + if (meth.isClassConstructor && !isPastTyper && !meth.owner.isSubClass(AnyValClass)) { // At this point in AnyVal there is no supercall, which will blow up // in computeParamAliases; there's nothing to be computed for Anyval anyway. if (meth.isPrimaryConstructor) diff --git a/test/files/neg/t4842.check b/test/files/neg/t4842.check new file mode 100644 index 0000000000..b53bbdbd15 --- /dev/null +++ b/test/files/neg/t4842.check @@ -0,0 +1,7 @@ +t4842.scala:2: error: self constructor arguments cannot reference unconstructed `this` + def this(x: Int) = this(new { println(Foo.this)}) // error + ^ +t4842.scala:6: error: self constructor arguments cannot reference unconstructed `this` + def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error + ^ +two errors found diff --git a/test/files/neg/t4842.scala b/test/files/neg/t4842.scala new file mode 100644 index 0000000000..c6244efda7 --- /dev/null +++ b/test/files/neg/t4842.scala @@ -0,0 +1,7 @@ +class Foo (x: AnyRef) { + def this(x: Int) = this(new { println(Foo.this)}) // error +} + +class TypeArg[X](val x: X)(a: AnyRef) { + def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error +} diff --git a/test/files/neg/t4842a.check b/test/files/neg/t4842a.check deleted file mode 100644 index 39d77bfc48..0000000000 --- a/test/files/neg/t4842a.check +++ /dev/null @@ -1,4 +0,0 @@ -t4842a.scala:2: error: self constructor arguments cannot reference unconstructed `this` - def this(x: Int) = this(new { println(Foo.this)}) // error - ^ -one error found diff --git a/test/files/neg/t4842a.scala b/test/files/neg/t4842a.scala deleted file mode 100644 index 78360effb4..0000000000 --- a/test/files/neg/t4842a.scala +++ /dev/null @@ -1,3 +0,0 @@ -class Foo (x: AnyRef) { - def this(x: Int) = this(new { println(Foo.this)}) // error -} diff --git a/test/files/neg/t4842b.check b/test/files/neg/t4842b.check deleted file mode 100644 index c7ccd5e059..0000000000 --- a/test/files/neg/t4842b.check +++ /dev/null @@ -1,4 +0,0 @@ -t4842b.scala:2: error: self constructor arguments cannot reference unconstructed `this` - def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error - ^ -one error found diff --git a/test/files/neg/t4842b.scala b/test/files/neg/t4842b.scala deleted file mode 100644 index a7996cc061..0000000000 --- a/test/files/neg/t4842b.scala +++ /dev/null @@ -1,3 +0,0 @@ -class TypeArg[X](val x: X)(a: AnyRef) { - def this() = { this(???)(new { println(TypeArg.this.x) } ); println("next") } // error -} -- cgit v1.2.3 From 1d67fe63b8c1e5537cccc60734cfbf3bc64d10ab Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 25 Jun 2012 22:46:49 +0200 Subject: Statistics improvements and bug fixes. Added compile-time option hotEnabled to Statistics that can be used to count very high frequency methods with 0 overhead for the disabled case. Fixed problem with weak maps that caused some timers to be discarded prematurely before they could be printed. Fixed problem with accouning for stacked timers. --- .../scala/tools/nsc/typechecker/Implicits.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 19 ++--- .../epfl/lamp/compiler/msil/emit/ILGenerator.scala | 1 - .../scala/reflect/internal/SymbolTable.scala | 14 +++- src/reflect/scala/reflect/internal/Symbols.scala | 39 ++++++--- src/reflect/scala/reflect/internal/Types.scala | 61 ++++++++------ .../scala/reflect/internal/util/Statistics.scala | 93 ++++++++++++++-------- 7 files changed, 149 insertions(+), 80 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index f7e00109ae..68782379a6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1125,7 +1125,7 @@ trait Implicits { * such that some part of `tp` has C as one of its superclasses. */ private def implicitsOfExpectedType: Infoss = { - Statistics.incCounter(implicitCacheHits) + Statistics.incCounter(implicitCacheAccs) implicitsCache get pt match { case Some(implicitInfoss) => Statistics.incCounter(implicitCacheHits) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 69d3fd7f47..a92a9aa82e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4025,7 +4025,7 @@ trait Typers extends Modes with Adaptations with Tags { } else { context.enclMethod.returnsSeen = true val expr1: Tree = typed(expr, EXPRmode | BYVALmode | RETmode, restpt.tpe) - + // Warn about returning a value if no value can be returned. if (restpt.tpe.typeSymbol == UnitClass) { // The typing in expr1 says expr is Unit (it has already been coerced if @@ -5131,7 +5131,7 @@ trait Typers extends Modes with Adaptations with Tags { indentTyping() var alreadyTyped = false - val startByType = Statistics.pushTimerClass(byTypeNanos, tree.getClass) + val startByType = Statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) Statistics.incCounter(visitsByType, tree.getClass) try { if (context.retyping && @@ -5187,7 +5187,7 @@ trait Typers extends Modes with Adaptations with Tags { } finally { deindentTyping() - Statistics.popTimerClass(byTypeNanos, startByType) + Statistics.popTimer(byTypeStack, startByType) } } @@ -5375,10 +5375,11 @@ object TypersStats { val compoundBaseTypeSeqCount = Statistics.newSubCounter(" of which for compound types", baseTypeSeqCount) val typerefBaseTypeSeqCount = Statistics.newSubCounter(" of which for typerefs", baseTypeSeqCount) val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) - val failedSilentNanos = Statistics.newSubTimer ("time spent in failed", typerNanos) - val failedApplyNanos = Statistics.newSubTimer (" failed apply", typerNanos) - val failedOpEqNanos = Statistics.newSubTimer (" failed op=", typerNanos) - val isReferencedNanos = Statistics.newSubTimer ("time spent ref scanning", typerNanos) - val visitsByType = Statistics.newByClass ("#visits by tree node", "typer")(Statistics.newCounter("")) - val byTypeNanos = Statistics.newByClassTimerStack("time spent by tree node", typerNanos) + val failedSilentNanos = Statistics.newSubTimer("time spent in failed", typerNanos) + val failedApplyNanos = Statistics.newSubTimer(" failed apply", typerNanos) + val failedOpEqNanos = Statistics.newSubTimer(" failed op=", typerNanos) + val isReferencedNanos = Statistics.newSubTimer("time spent ref scanning", typerNanos) + val visitsByType = Statistics.newByClass("#visits by tree node", "typer")(Statistics.newCounter("")) + val byTypeNanos = Statistics.newByClass("time spent by tree node", "typer")(Statistics.newStackableTimer("", typerNanos)) + val byTypeStack = Statistics.newTimerStack() } diff --git a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala index 2223a6db0f..2aa9a99054 100644 --- a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala +++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala @@ -336,7 +336,6 @@ import ILGenerator._ emitSpecialLabel(Label.Try) val endExc: Label = new Label.NormalLabel() // new Label(lastLabel) ??? excStack.push(Label.Try, endExc) - return endExc } /** Begins a catch block. */ diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 18adab7c68..5ae8f22c64 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -129,11 +129,15 @@ abstract class SymbolTable extends makro.Universe // sigh, this has to be public or atPhase doesn't inline. var phStack: List[Phase] = Nil - private var ph: Phase = NoPhase - private var per = NoPeriod + private[this] var ph: Phase = NoPhase + private[this] var per = NoPeriod final def atPhaseStack: List[Phase] = phStack - final def phase: Phase = ph + final def phase: Phase = { + if (Statistics.hotEnabled) + Statistics.incCounter(SymbolTableStats.phaseCounter) + ph + } def atPhaseStackMessage = atPhaseStack match { case Nil => "" @@ -330,3 +334,7 @@ abstract class SymbolTable extends makro.Universe */ def isCompilerUniverse = false } + +object SymbolTableStats { + val phaseCounter = Statistics.newCounter("#phase calls") +} diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 4b0ceeb86b..a3893a0236 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -645,6 +645,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => } final def flags: Long = { + if (Statistics.hotEnabled) Statistics.incCounter(flagsCount) val fs = _rawflags & phase.flagMask (fs | ((fs & LateFlags) >>> LateShift)) & ~(fs >>> AntiShift) } @@ -936,7 +937,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ owner attribute -------------------------------------------------------------- - def owner: Symbol = rawowner + def owner: Symbol = { + Statistics.incCounter(ownerCount) + rawowner + } + // TODO - don't allow the owner to be changed without checking invariants, at least // when under some flag. Define per-phase invariants for owner/owned relationships, // e.g. after flatten all classes are owned by package classes, there are lots and @@ -2324,7 +2329,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => private[this] var _rawname: TermName = initName def rawname = _rawname - def name = _rawname + def name = { + Statistics.incCounter(nameCount) + _rawname + } def name_=(name: Name) { if (name != rawname) { log("Renaming %s %s %s to %s".format(shortSymbolClass, debugFlagString, rawname, name)) @@ -2493,11 +2501,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def companionClass = flatOwnerInfo.decl(name.toTypeName).suchThat(_ isCoDefinedWith this) - override def owner = ( + override def owner = { + Statistics.incCounter(ownerCount) if (!isMethod && needsFlatClasses) rawowner.owner else rawowner - ) - override def name: TermName = ( + } + override def name: TermName = { + Statistics.incCounter(nameCount) if (!isMethod && needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname) @@ -2505,7 +2515,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => flatname } else rawname - ) + } } implicit val ModuleSymbolTag = ClassTag[ModuleSymbol](classOf[ModuleSymbol]) @@ -2576,7 +2586,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => // cloneSymbolImpl still abstract in TypeSymbol. def rawname = _rawname - def name = _rawname + def name = { + Statistics.incCounter(nameCount) + _rawname + } final def asNameType(n: Name) = n.toTypeName override def isNonClassType = true @@ -2888,10 +2901,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => thisTypeCache } - override def owner: Symbol = + override def owner: Symbol = { + Statistics.incCounter(ownerCount) if (needsFlatClasses) rawowner.owner else rawowner + } - override def name: TypeName = ( + override def name: TypeName = { + Statistics.incCounter(nameCount) if (needsFlatClasses) { if (flatname eq null) flatname = nme.flattenedName(rawowner.name, rawname).toTypeName @@ -2899,7 +2915,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => flatname } else rawname - ) + } /** A symbol carrying the self type of the class as its type */ override def thisSym: Symbol = thissym @@ -3194,4 +3210,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => object SymbolsStats { val typeSymbolCount = Statistics.newCounter("#type symbols") val classSymbolCount = Statistics.newCounter("#class symbols") + val flagsCount = Statistics.newCounter("#flags ops") + val ownerCount = Statistics.newCounter("#owner ops") + val nameCount = Statistics.newCounter("#name ops") } diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index d4b895bcb4..4cf2cceb81 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -679,7 +679,7 @@ trait Types extends api.Types { self: SymbolTable => else { // scala.tools.nsc.util.trace.when(pre.isInstanceOf[ExistentialType])("X "+this+".asSeenfrom("+pre+","+clazz+" = ") { Statistics.incCounter(asSeenFromCount) - val start = Statistics.startTimer(asSeenFromNanos) + val start = Statistics.pushTimer(typeOpsStack, asSeenFromNanos) val m = new AsSeenFromMap(pre.normalize, clazz) val tp = m apply this val tp1 = existentialAbstraction(m.capturedParams, tp) @@ -687,7 +687,7 @@ trait Types extends api.Types { self: SymbolTable => if (m.capturedSkolems.isEmpty) tp1 else deriveType(m.capturedSkolems, _.cloneSymbol setFlag CAPTURED)(tp1) - Statistics.stopTimer(asSeenFromNanos, start) + Statistics.popTimer(typeOpsStack, start) result } } @@ -826,12 +826,12 @@ trait Types extends api.Types { self: SymbolTable => def stat_<:<(that: Type): Boolean = { Statistics.incCounter(subtypeCount) - val start = Statistics.startTimer(subtypeNanos) + val start = Statistics.pushTimer(typeOpsStack, subtypeNanos) val result = (this eq that) || (if (explainSwitch) explain("<:", isSubType, this, that) else isSubType(this, that, AnyDepth)) - Statistics.stopTimer(subtypeNanos, start) + Statistics.popTimer(typeOpsStack, start) result } @@ -839,12 +839,12 @@ trait Types extends api.Types { self: SymbolTable => */ def weak_<:<(that: Type): Boolean = { Statistics.incCounter(subtypeCount) - val start = Statistics.startTimer(subtypeNanos) + val start = Statistics.pushTimer(typeOpsStack, subtypeNanos) val result = ((this eq that) || (if (explainSwitch) explain("weak_<:", isWeakSubType, this, that) else isWeakSubType(this, that))) - Statistics.stopTimer(subtypeNanos, start) + Statistics.popTimer(typeOpsStack, start) result } @@ -1018,7 +1018,7 @@ trait Types extends api.Types { self: SymbolTable => val suspension: List[TypeVar] = if (this.isGround) null else suspendTypeVarsInType(this) Statistics.incCounter(findMemberCount) - val start = Statistics.startTimer(findMemberNanos) + val start = Statistics.pushTimer(typeOpsStack, findMemberNanos) //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG var members: Scope = null @@ -1045,7 +1045,7 @@ trait Types extends api.Types { self: SymbolTable => !sym.isPrivateLocal || (bcs0.head.hasTransOwner(bcs.head)))) { if (name.isTypeName || stableOnly && sym.isStable) { - Statistics.stopTimer(findMemberNanos, start) + Statistics.popTimer(typeOpsStack, start) if (suspension ne null) suspension foreach (_.suspended = false) return sym } else if (member == NoSymbol) { @@ -1091,7 +1091,7 @@ trait Types extends api.Types { self: SymbolTable => } // while (!bcs.isEmpty) excluded = excludedFlags } // while (continue) - Statistics.stopTimer(findMemberNanos, start) + Statistics.popTimer(typeOpsStack, start) if (suspension ne null) suspension foreach (_.suspended = false) if (members eq null) { if (member == NoSymbol) Statistics.incCounter(noMemberCount) @@ -1534,11 +1534,17 @@ trait Types extends api.Types { self: SymbolTable => tpe.baseTypeSeqCache = bts lateMap paramToVar } else { Statistics.incCounter(compoundBaseTypeSeqCount) - tpe.baseTypeSeqCache = undetBaseTypeSeq - tpe.baseTypeSeqCache = if (tpe.typeSymbol.isRefinementClass) - tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe) - else - compoundBaseTypeSeq(tpe) + val start = Statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) + try { + tpe.baseTypeSeqCache = undetBaseTypeSeq + tpe.baseTypeSeqCache = + if (tpe.typeSymbol.isRefinementClass) + tpe.memo(compoundBaseTypeSeq(tpe))(_.baseTypeSeq updateHead tpe.typeSymbol.tpe) + else + compoundBaseTypeSeq(tpe) + } finally { + Statistics.popTimer(typeOpsStack, start) + } // [Martin] suppressing memo-ization solves the problem with "same type after erasure" errors // when compiling with // scalac scala.collection.IterableViewLike.scala scala.collection.IterableLike.scala @@ -2390,8 +2396,13 @@ trait Types extends api.Types { self: SymbolTable => tpe.baseTypeSeqPeriod = currentPeriod if (!isValidForBaseClasses(period)) { Statistics.incCounter(typerefBaseTypeSeqCount) - tpe.baseTypeSeqCache = undetBaseTypeSeq - tpe.baseTypeSeqCache = tpe.baseTypeSeqImpl + val start = Statistics.pushTimer(typeOpsStack, baseTypeSeqNanos) + try { + tpe.baseTypeSeqCache = undetBaseTypeSeq + tpe.baseTypeSeqCache = tpe.baseTypeSeqImpl + } finally { + Statistics.popTimer(typeOpsStack, start) + } } } if (tpe.baseTypeSeqCache == undetBaseTypeSeq) @@ -6306,13 +6317,13 @@ trait Types extends api.Types { self: SymbolTable => case List(t) => t case _ => Statistics.incCounter(lubCount) - val start = Statistics.startTimer(lubNanos) + val start = Statistics.pushTimer(typeOpsStack, lubNanos) try { lub(ts, lubDepth(ts)) } finally { lubResults.clear() glbResults.clear() - Statistics.stopTimer(lubNanos, start) + Statistics.popTimer(typeOpsStack, start) } } @@ -6454,13 +6465,13 @@ trait Types extends api.Types { self: SymbolTable => case List(t) => t case ts0 => Statistics.incCounter(lubCount) - val start = Statistics.startTimer(lubNanos) + val start = Statistics.pushTimer(typeOpsStack, lubNanos) try { glbNorm(ts0, lubDepth(ts0)) } finally { lubResults.clear() glbResults.clear() - Statistics.stopTimer(lubNanos, start) + Statistics.popTimer(typeOpsStack, start) } } @@ -6884,11 +6895,13 @@ object TypesStats { val noMemberCount = Statistics.newSubCounter(" of which not found", findMemberCount) val multMemberCount = Statistics.newSubCounter(" of which multiple overloaded", findMemberCount) val typerNanos = Statistics.newTimer ("time spent typechecking", "typer") - val lubNanos = Statistics.newSubTimer ("time spent in lubs", typerNanos) - val subtypeNanos = Statistics.newSubTimer ("time spent in <:<", typerNanos) - val findMemberNanos = Statistics.newSubTimer ("time spent in findmember", typerNanos) - val asSeenFromNanos = Statistics.newSubTimer ("time spent in asSeenFrom", typerNanos) + val lubNanos = Statistics.newStackableTimer("time spent in lubs", typerNanos) + val subtypeNanos = Statistics.newStackableTimer("time spent in <:<", typerNanos) + val findMemberNanos = Statistics.newStackableTimer("time spent in findmember", typerNanos) + val asSeenFromNanos = Statistics.newStackableTimer("time spent in asSeenFrom", typerNanos) + val baseTypeSeqNanos = Statistics.newStackableTimer("time spent in baseTypeSeq", typerNanos) val compoundBaseTypeSeqCount = Statistics.newSubCounter(" of which for compound types", baseTypeSeqCount) val typerefBaseTypeSeqCount = Statistics.newSubCounter(" of which for typerefs", baseTypeSeqCount) val singletonBaseTypeSeqCount = Statistics.newSubCounter(" of which for singletons", baseTypeSeqCount) + val typeOpsStack = Statistics.newTimerStack() } diff --git a/src/reflect/scala/reflect/internal/util/Statistics.scala b/src/reflect/scala/reflect/internal/util/Statistics.scala index 57c9e98174..f69530c40d 100644 --- a/src/reflect/scala/reflect/internal/util/Statistics.scala +++ b/src/reflect/scala/reflect/internal/util/Statistics.scala @@ -4,6 +4,8 @@ import collection.mutable object Statistics { + type TimerSnapshot = (Long, Long) + /** If enabled, increment counter by one */ @inline final def incCounter(c: Counter) { if (_enabled && c != null) c.value += 1 @@ -30,20 +32,20 @@ object Statistics { } /** If enabled, start timer */ - @inline final def startTimer(tm: Timer): (Long, Long) = + @inline final def startTimer(tm: Timer): TimerSnapshot = if (_enabled && tm != null) tm.start() else null /** If enabled, stop timer */ - @inline final def stopTimer(tm: Timer, start: (Long, Long)) { + @inline final def stopTimer(tm: Timer, start: TimerSnapshot) { if (_enabled && tm != null) tm.stop(start) } /** If enabled, push and start a new timer in timer stack */ - @inline final def pushTimerClass(timers: ByClassTimerStack, cls: Class[_]): (Long, Long) = - if (_enabled && timers != null) timers.push(cls) else null + @inline final def pushTimer(timers: TimerStack, timer: StackableTimer): TimerSnapshot = + if (_enabled && timers != null) timers.push(timer) else null /** If enabled, stop and pop timer from timer stack */ - @inline final def popTimerClass(timers: ByClassTimerStack, prev: (Long, Long)) { + @inline final def popTimer(timers: TimerStack, prev: TimerSnapshot) { if (_enabled && timers != null) timers.pop(prev) } @@ -73,6 +75,13 @@ object Statistics { */ def newSubTimer(prefix: String, timer: Timer): Timer = new SubTimer(prefix, timer) + /** Create a new stackable that shows as `prefix` and is active + * in the same phases as its base timer. Stackable timers are subtimers + * that can be stacked ina timerstack, and that print aggregate, as well as specific + * durations. + */ + def newStackableTimer(prefix: String, timer: Timer): StackableTimer = new StackableTimer(prefix, timer) + /** Create a new view that shows as `prefix` and is active in given phases. * The view always reflects the current value of `quant` as a quantity. */ @@ -86,20 +95,27 @@ quant) /** Same as newQuantMap, where the key type is fixed to be Class[_] */ def newByClass[V <% Ordered[V]](prefix: String, phases: String*)(initValue: => V): QuantMap[Class[_], V] = new QuantMap(prefix, phases, initValue) - /** Create a new timer stack map, indexed by Class[_]. */ - def newByClassTimerStack(prefix: String, underlying: Timer) = new ByClassTimerStack(prefix, underlying) + /** Create a new timer stack */ + def newTimerStack() = new TimerStack() def allQuantities: Iterable[Quantity] = - for ((q, _) <- qs if !q.isInstanceOf[SubQuantity]; + for ((_, q) <- qs if q.underlying == q; r <- q :: q.children.toList if r.prefix.nonEmpty) yield r private def showPercent(x: Double, base: Double) = if (base == 0) "" else f" (${x / base * 100}%2.1f%)" + /** The base trait for quantities. + * Quantities with non-empty prefix are printed in the statistics info. + */ trait Quantity { - qs += (this -> ()) + if (prefix.nonEmpty) { + val key = s"${if (underlying != this) underlying.prefix else ""}/$prefix" + qs(key) = this + } val prefix: String val phases: Seq[String] + def underlying: Quantity = this def showAt(phase: String) = phases.isEmpty || (phases contains phase) def line = f"$prefix%-30s: ${this}" val children = new mutable.ListBuffer[Quantity] @@ -123,7 +139,7 @@ quant) override def toString = quant.toString } - private class RelCounter(prefix: String, val underlying: Counter) extends Counter(prefix, underlying.phases) with SubQuantity { + private class RelCounter(prefix: String, override val underlying: Counter) extends Counter(prefix, underlying.phases) with SubQuantity { override def toString = if (value == 0) "0" else { @@ -142,26 +158,32 @@ quant) value + showPercent(value, underlying.value) } - class Timer(val prefix: String, val phases: Seq[String]) extends Quantity with Ordered[Timer] { + class Timer(val prefix: String, val phases: Seq[String]) extends Quantity { var nanos: Long = 0 var timings = 0 - def compare(that: Timer): Int = - if (this.nanos < that.nanos) -1 - else if (this.nanos > that.nanos) 1 - else 0 def start() = { (nanos, System.nanoTime()) } - def stop(prev: (Long, Long)) { + def stop(prev: TimerSnapshot) { val (nanos0, start) = prev nanos = nanos0 + System.nanoTime() - start timings += 1 } - override def toString = s"$timings spans, ${nanos/1000}ms" + protected def show(ns: Long) = s"${ns/1000}ms" + override def toString = s"$timings spans, ${show(nanos)}" } - private class SubTimer(prefix: String, override val underlying: Timer) extends Timer(prefix, underlying.phases) with SubQuantity { - override def toString: String = super.toString + showPercent(nanos, underlying.nanos) + class SubTimer(prefix: String, override val underlying: Timer) extends Timer(prefix, underlying.phases) with SubQuantity { + override protected def show(ns: Long) = super.show(ns) + showPercent(ns, underlying.nanos) + } + + class StackableTimer(prefix: String, underlying: Timer) extends SubTimer(prefix, underlying) with Ordered[StackableTimer] { + var specificNanos: Long = 0 + def compare(that: StackableTimer): Int = + if (this.specificNanos < that.specificNanos) -1 + else if (this.specificNanos > that.specificNanos) 1 + else 0 + override def toString = s"${super.toString} aggregate, ${show(specificNanos)} specific" } /** A mutable map quantity where missing elements are automatically inserted @@ -183,23 +205,25 @@ quant) }.mkString(", ") } - /** A mutable map quantity that takes class keys to subtimer values, relative to - * some `underlying` timer. In addition, class timers can be pushed and popped. - * Pushing the timer for a class means stopping the currently active timer. + /** A stack of timers, all active, where a timer's specific "clock" + * is stopped as long as it is buried by some other timer in the stack, but + * its aggregate clock keeps on ticking. */ - class ByClassTimerStack(prefix: String, val underlying: Timer) - extends QuantMap[Class[_], Timer](prefix, underlying.phases, new SubTimer("", underlying)) with SubQuantity { - private var elems: List[(Timer, Long)] = Nil - def push(cls: Class[_]): (Long, Long) = { - val topTimer = this(cls) - elems = (topTimer, 0L) :: elems - topTimer.start() + class TimerStack { + private var elems: List[(StackableTimer, Long)] = Nil + /** Start given timer and push it onto the stack */ + def push(t: StackableTimer): TimerSnapshot = { + elems = (t, 0L) :: elems + t.start() } - def pop(prev: (Long, Long)) = { + /** Stop and pop top timer in stack + */ + def pop(prev: TimerSnapshot) = { val (nanos0, start) = prev val duration = System.nanoTime() - start val (topTimer, nestedNanos) :: rest = elems - topTimer.nanos = nanos0 + duration - nestedNanos + topTimer.nanos = nanos0 + duration + topTimer.specificNanos += duration - nestedNanos topTimer.timings += 1 elems = rest match { case (outerTimer, outerNested) :: elems1 => @@ -211,7 +235,7 @@ quant) } private var _enabled = false - private val qs = new mutable.WeakHashMap[Quantity, Unit] + private val qs = new mutable.HashMap[String, Quantity] def enabled = _enabled def enabled_=(cond: Boolean) = { @@ -229,4 +253,9 @@ quant) _enabled = true } } + + /** replace rhs with enabled and rebuild to also count tiny but super-hot methods + * such as phase, flags, owner, name. + */ + final val hotEnabled = false } -- cgit v1.2.3 From dbd31fb845e4731dbe580911b6f44d2d9b1973a9 Mon Sep 17 00:00:00 2001 From: Miguel Garcia Date: Tue, 26 Jun 2012 11:06:50 +0200 Subject: fix for SI-4935 --- .../nsc/backend/opt/DeadCodeElimination.scala | 26 +++++++++++++++++++++- test/files/run/t4935.check | 1 + test/files/run/t4935.flags | 1 + test/files/run/t4935.scala | 9 ++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t4935.check create mode 100644 test/files/run/t4935.flags create mode 100644 test/files/run/t4935.scala diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index d4ee9b6b48..5cc6e78e9d 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -100,9 +100,29 @@ abstract class DeadCodeElimination extends SubComponent { var rd = rdef.in(bb); for (Pair(i, idx) <- bb.toList.zipWithIndex) { i match { + case LOAD_LOCAL(l) => defs = defs + Pair(((bb, idx)), rd.vars) -// Console.println(i + ": " + (bb, idx) + " rd: " + rd + " and having: " + defs) + + case STORE_LOCAL(_) => + /* SI-4935 Check whether a module is stack top, if so mark the instruction that loaded it + * (otherwise any side-effects of the module's constructor go lost). + * (a) The other two cases where a module's value is stored (STORE_FIELD and STORE_ARRAY_ITEM) + * are already marked (case clause below). + * (b) A CALL_METHOD targeting a method `m1` where the receiver is potentially a module (case clause below) + * will have the module's load marked provided `isSideEffecting(m1)`. + * TODO check for purity (the ICode?) of the module's constructor (besides m1's purity). + * See also https://github.com/paulp/scala/blob/topic/purity-analysis/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala + */ + val necessary = rdef.findDefs(bb, idx, 1) exists { p => + val (bb1, idx1) = p + bb1(idx1) match { + case LOAD_MODULE(module) => isLoadNeeded(module) + case _ => false + } + } + if (necessary) worklist += ((bb, idx)) + case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx)) @@ -129,6 +149,10 @@ abstract class DeadCodeElimination extends SubComponent { } } + private def isLoadNeeded(module: Symbol): Boolean = { + module.info.member(nme.CONSTRUCTOR).filter(isSideEffecting) != NoSymbol + } + /** Mark useful instructions. Instructions in the worklist are each inspected and their * dependencies are marked useful too, and added to the worklist. */ diff --git a/test/files/run/t4935.check b/test/files/run/t4935.check new file mode 100644 index 0000000000..ef0493b275 --- /dev/null +++ b/test/files/run/t4935.check @@ -0,0 +1 @@ +hello diff --git a/test/files/run/t4935.flags b/test/files/run/t4935.flags new file mode 100644 index 0000000000..ac14fe5dbd --- /dev/null +++ b/test/files/run/t4935.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/run/t4935.scala b/test/files/run/t4935.scala new file mode 100644 index 0000000000..18631e2041 --- /dev/null +++ b/test/files/run/t4935.scala @@ -0,0 +1,9 @@ +object Test extends App { + for (i <- 0 to 1) { + val a = Foo + } +} + +object Foo { + println("hello") +} -- cgit v1.2.3 From c9f29098c5e623894a6a2c2a9ddc1bb96add47b4 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 26 Jun 2012 11:19:17 +0200 Subject: Added test for SI-5761. Seems that my cleanup of SI-4842 also fixed that one. Review by @paulp or @retronym. --- test/files/neg/t5761.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/files/neg/t5761.scala diff --git a/test/files/neg/t5761.scala b/test/files/neg/t5761.scala new file mode 100644 index 0000000000..040c4eb75a --- /dev/null +++ b/test/files/neg/t5761.scala @@ -0,0 +1,16 @@ +class D[-A](x: A) { } + +object Test1 { + println(new D[Int]{}) // crash +} + +object Test2 { + println(new D[Int]()) // no crash + println(new D[Int]{}) // crash +} + +object Test3 { + new Tread("sth") { }.run() +} + + -- cgit v1.2.3 From fd6573a2ceba22a47ff657310630799c8fd5651b Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Fri, 22 Jun 2012 16:00:06 +0200 Subject: Added new project files for using the compiler and library inside Eclipse and removed the old ones. To use these project files you need to define the Eclipse 'path variable' SCALA_BASEDIR, in General/Workspace/Linked Resources. --- classpath.SAMPLE | 12 ------------ project.SAMPLE | 18 ------------------ src/eclipse/README | 23 +++++++++++++++++++++++ src/eclipse/asm/.classpath | 7 +++++++ src/eclipse/asm/.project | 29 +++++++++++++++++++++++++++++ src/eclipse/fjbg/.classpath | 7 +++++++ src/eclipse/fjbg/.project | 30 ++++++++++++++++++++++++++++++ src/eclipse/reflect/.classpath | 7 +++++++ src/eclipse/reflect/.project | 30 ++++++++++++++++++++++++++++++ src/eclipse/scala-compiler/.classpath | 14 ++++++++++++++ src/eclipse/scala-compiler/.project | 35 +++++++++++++++++++++++++++++++++++ src/eclipse/scala-library/.classpath | 7 +++++++ src/eclipse/scala-library/.project | 30 ++++++++++++++++++++++++++++++ 13 files changed, 219 insertions(+), 30 deletions(-) delete mode 100644 classpath.SAMPLE delete mode 100644 project.SAMPLE create mode 100644 src/eclipse/README create mode 100644 src/eclipse/asm/.classpath create mode 100644 src/eclipse/asm/.project create mode 100644 src/eclipse/fjbg/.classpath create mode 100644 src/eclipse/fjbg/.project create mode 100644 src/eclipse/reflect/.classpath create mode 100644 src/eclipse/reflect/.project create mode 100644 src/eclipse/scala-compiler/.classpath create mode 100644 src/eclipse/scala-compiler/.project create mode 100644 src/eclipse/scala-library/.classpath create mode 100644 src/eclipse/scala-library/.project diff --git a/classpath.SAMPLE b/classpath.SAMPLE deleted file mode 100644 index 9e607a41d9..0000000000 --- a/classpath.SAMPLE +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/project.SAMPLE b/project.SAMPLE deleted file mode 100644 index 0034c397ed..0000000000 --- a/project.SAMPLE +++ /dev/null @@ -1,18 +0,0 @@ - - - scala - - - - - - org.scala-ide.sdt.core.scalabuilder - - - - - - org.scala-ide.sdt.core.scalanature - org.eclipse.jdt.core.javanature - - diff --git a/src/eclipse/README b/src/eclipse/README new file mode 100644 index 0000000000..58dbd83815 --- /dev/null +++ b/src/eclipse/README @@ -0,0 +1,23 @@ +Eclipse project files +===================== + +Import all projects inside Eclipse by choosing File/Import Existing Projects +and navigate to src/eclipse. Check all projects and click ok. + +IMPORTANT +========= + +You need to define a `path variable` inside Eclipse. Define SCALA_BASEDIR in +Preferences/General/Workspace/Linked Resources. The value should be the absolute +path to your scala checkout. All paths in project files are relative to this one, +so nothing will work before you do so. + +DETAILS +======= + +The compiler project depends on the library, reflect, asm and fjbg projects. The +builder will take care of the correct ordering, and changes in one project will +be picked up by the dependent projects. + +The output directory is set to be build/quick, so the runner scripts in quick +work as they are (run an ant build to have the generated once) \ No newline at end of file diff --git a/src/eclipse/asm/.classpath b/src/eclipse/asm/.classpath new file mode 100644 index 0000000000..f88dea64f4 --- /dev/null +++ b/src/eclipse/asm/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/eclipse/asm/.project b/src/eclipse/asm/.project new file mode 100644 index 0000000000..2c88a0ab11 --- /dev/null +++ b/src/eclipse/asm/.project @@ -0,0 +1,29 @@ + + + asm + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + + + SCALA_BASEDIR + 2 + SCALA_BASEDIR/src/asm + + + asm-quick-bin + 2 + SCALA_BASEDIR/build/asm/classes + + + diff --git a/src/eclipse/fjbg/.classpath b/src/eclipse/fjbg/.classpath new file mode 100644 index 0000000000..3e2f55f48a --- /dev/null +++ b/src/eclipse/fjbg/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/eclipse/fjbg/.project b/src/eclipse/fjbg/.project new file mode 100644 index 0000000000..8acea9f5fe --- /dev/null +++ b/src/eclipse/fjbg/.project @@ -0,0 +1,30 @@ + + + fjbg + + + + + + org.scala-ide.sdt.core.scalabuilder + + + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + + + fjbg + 2 + SCALA_BASEDIR/src/fjbg + + + libs-classes-fjbg + 2 + SCALA_BASEDIR/build/libs/classes/fjbg + + + diff --git a/src/eclipse/reflect/.classpath b/src/eclipse/reflect/.classpath new file mode 100644 index 0000000000..2a764d5142 --- /dev/null +++ b/src/eclipse/reflect/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/eclipse/reflect/.project b/src/eclipse/reflect/.project new file mode 100644 index 0000000000..1e5cbb4ed9 --- /dev/null +++ b/src/eclipse/reflect/.project @@ -0,0 +1,30 @@ + + + reflect + + + + + + org.scala-ide.sdt.core.scalabuilder + + + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + + + build-quick-reflect + 2 + SCALA_BASEDIR/build/quick/classes/reflect + + + reflect + 2 + SCALA_BASEDIR/src/reflect + + + diff --git a/src/eclipse/scala-compiler/.classpath b/src/eclipse/scala-compiler/.classpath new file mode 100644 index 0000000000..ff3b63f3ca --- /dev/null +++ b/src/eclipse/scala-compiler/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/eclipse/scala-compiler/.project b/src/eclipse/scala-compiler/.project new file mode 100644 index 0000000000..cf8a68c8b6 --- /dev/null +++ b/src/eclipse/scala-compiler/.project @@ -0,0 +1,35 @@ + + + scala-compiler + + + + + + org.scala-ide.sdt.core.scalabuilder + + + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + + + build-quick-compiler + 2 + SCALA_BASEDIR/build/quick/classes/compiler + + + compiler + 2 + SCALA_BASEDIR/src/compiler + + + lib + 2 + SCALA_BASEDIR/lib + + + diff --git a/src/eclipse/scala-library/.classpath b/src/eclipse/scala-library/.classpath new file mode 100644 index 0000000000..a3a4933d34 --- /dev/null +++ b/src/eclipse/scala-library/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/eclipse/scala-library/.project b/src/eclipse/scala-library/.project new file mode 100644 index 0000000000..049cf75e0b --- /dev/null +++ b/src/eclipse/scala-library/.project @@ -0,0 +1,30 @@ + + + scala-library + + + + + + org.scala-ide.sdt.core.scalabuilder + + + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + + + build-quick-lib + 2 + SCALA_BASEDIR/build/quick/classes/library + + + library + 2 + SCALA_BASEDIR/src/library + + + -- cgit v1.2.3 From b7888cc356094138fa79e45bf101be4a893bfd50 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 26 Jun 2012 18:24:51 +0200 Subject: Fix range positions when applying anonymous classes. Review by @dragos or @odersky --- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- test/files/pos/rangepos-anonapply.flags | 1 + test/files/pos/rangepos-anonapply.scala | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/files/pos/rangepos-anonapply.flags create mode 100644 test/files/pos/rangepos-anonapply.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index acf1b3dc59..35127a7571 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4978,7 +4978,7 @@ trait Typers extends Modes with Adaptations with Tags { typedTypeApply(tree, mode, fun1, args1) case Apply(Block(stats, expr), args) => - typed1(atPos(tree.pos)(Block(stats, Apply(expr, args))), mode, pt) + typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt) case Apply(fun, args) => typedApply(fun, args) match { diff --git a/test/files/pos/rangepos-anonapply.flags b/test/files/pos/rangepos-anonapply.flags new file mode 100644 index 0000000000..281f0a10cd --- /dev/null +++ b/test/files/pos/rangepos-anonapply.flags @@ -0,0 +1 @@ +-Yrangepos diff --git a/test/files/pos/rangepos-anonapply.scala b/test/files/pos/rangepos-anonapply.scala new file mode 100644 index 0000000000..2f3e4ad6cd --- /dev/null +++ b/test/files/pos/rangepos-anonapply.scala @@ -0,0 +1,9 @@ +class Test { + trait PropTraverser { + def apply(x: Int): Unit = {} + } + + def gather(x: Int) { + (new PropTraverser {})(x) + } +} -- cgit v1.2.3 From 215a249bf020cf915136e11e04048f0144ab1ea1 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 26 Jun 2012 19:59:40 +0200 Subject: SI-5761: fix up #774 (missing check file) --- test/files/neg/t5761.check | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/files/neg/t5761.check diff --git a/test/files/neg/t5761.check b/test/files/neg/t5761.check new file mode 100644 index 0000000000..89d766fe34 --- /dev/null +++ b/test/files/neg/t5761.check @@ -0,0 +1,16 @@ +t5761.scala:4: error: not enough arguments for constructor D: (x: Int)D[Int]. +Unspecified value parameter x. + println(new D[Int]{}) // crash + ^ +t5761.scala:8: error: not enough arguments for constructor D: (x: Int)D[Int]. +Unspecified value parameter x. + println(new D[Int]()) // no crash + ^ +t5761.scala:9: error: not enough arguments for constructor D: (x: Int)D[Int]. +Unspecified value parameter x. + println(new D[Int]{}) // crash + ^ +t5761.scala:13: error: not found: type Tread + new Tread("sth") { }.run() + ^ +four errors found -- cgit v1.2.3 From 21814b53e963d0e762efb808a664dd2c8b6e67a1 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 11 Jun 2012 17:57:12 +0200 Subject: minor cleanup in patmat and typers patmat: - remove Var's unused 'checked' member; subsumed by 'domain' - updated docs: no longer using isSuccess in __match typers: clean up indentation, swap ifs to failure-first --- .../tools/nsc/typechecker/PatternMatching.scala | 36 ++++---- .../scala/tools/nsc/typechecker/Typers.scala | 102 ++++++++++----------- 2 files changed, 67 insertions(+), 71 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index f99d0e733b..7bac0dac3a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -123,7 +123,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def zero: M[Nothing] def one[T](x: P[T]): M[T] def guard[T](cond: P[Boolean], then: => P[T]): M[T] - def isSuccess[T, U](x: P[T])(f: P[T] => M[U]): P[Boolean] // used for isDefinedAt } * P and M are derived from one's signature (`def one[T](x: P[T]): M[T]`) @@ -137,7 +136,6 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // NOTE: guard's return type must be of the shape M[T], where M is the monad in which the pattern match should be interpreted def guard[T](cond: Boolean, then: => T): Option[T] = if(cond) Some(then) else None def runOrElse[T, U](x: T)(f: T => Option[U]): U = f(x) getOrElse (throw new MatchError(x)) - def isSuccess[T, U](x: T)(f: T => Option[U]): Boolean = !f(x).isEmpty } */ @@ -2006,7 +2004,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL private val uniques = new collection.mutable.HashMap[Tree, Var] def apply(x: Tree): Var = uniques getOrElseUpdate(x, new Var(x, x.tpe)) } - class Var(val path: Tree, fullTp: Type, checked: Boolean = true) extends AbsVar { + class Var(val path: Tree, fullTp: Type) extends AbsVar { private[this] val id: Int = Var.nextId // private[this] var canModify: Option[Array[StackTraceElement]] = None @@ -2028,26 +2026,24 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // we enumerate the subtypes of the full type, as that allows us to filter out more types statically, // once we go to run-time checks (on Const's), convert them to checkable types // TODO: there seems to be bug for singleton domains (variable does not show up in model) - lazy val domain: Option[Set[Const]] = - if (!checked) None - else { - val subConsts = enumerateSubtypes(fullTp).map{ tps => - tps.toSet[Type].map{ tp => - val domainC = TypeConst(tp) - registerEquality(domainC) - domainC - } + lazy val domain: Option[Set[Const]] = { + val subConsts = enumerateSubtypes(fullTp).map{ tps => + tps.toSet[Type].map{ tp => + val domainC = TypeConst(tp) + registerEquality(domainC) + domainC } + } - val allConsts = - if (! _considerNull) subConsts - else { - registerEquality(NullConst) - subConsts map (_ + NullConst) - } + val allConsts = + if (! _considerNull) subConsts + else { + registerEquality(NullConst) + subConsts map (_ + NullConst) + } - observed; allConsts - } + observed; allConsts + } // accessing after calling considerNull will result in inconsistencies lazy val domainSyms: Option[Set[Sym]] = domain map { _ map symForEqualsTo } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index acf1b3dc59..18726d3c86 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3103,66 +3103,67 @@ trait Typers extends Modes with Adaptations with Tags { val otpe = fun.tpe - if (args.length > MaxTupleArity) - return duplErrorTree(TooManyArgsPatternError(fun)) - - // - def freshArgType(tp: Type): (List[Symbol], Type) = tp match { - case MethodType(param :: _, _) => - (Nil, param.tpe) - case PolyType(tparams, restpe) => - createFromClonedSymbols(tparams, freshArgType(restpe)._2)((ps, t) => ((ps, t))) - // No longer used, see test case neg/t960.scala (#960 has nothing to do with it) - case OverloadedType(_, _) => - OverloadedUnapplyError(fun) - (Nil, ErrorType) - case _ => - UnapplyWithSingleArgError(fun) - (Nil, ErrorType) - } + if (args.length > MaxTupleArity) + return duplErrorTree(TooManyArgsPatternError(fun)) + + // + def freshArgType(tp: Type): (List[Symbol], Type) = tp match { + case MethodType(param :: _, _) => + (Nil, param.tpe) + case PolyType(tparams, restpe) => + createFromClonedSymbols(tparams, freshArgType(restpe)._2)((ps, t) => ((ps, t))) + // No longer used, see test case neg/t960.scala (#960 has nothing to do with it) + case OverloadedType(_, _) => + OverloadedUnapplyError(fun) + (Nil, ErrorType) + case _ => + UnapplyWithSingleArgError(fun) + (Nil, ErrorType) + } - val unapp = unapplyMember(otpe) - val unappType = otpe.memberType(unapp) - val argDummy = context.owner.newValue(nme.SELECTOR_DUMMY, fun.pos, SYNTHETIC) setInfo pt - val arg = Ident(argDummy) setType pt + val unapp = unapplyMember(otpe) + val unappType = otpe.memberType(unapp) + val argDummy = context.owner.newValue(nme.SELECTOR_DUMMY, fun.pos, SYNTHETIC) setInfo pt + val arg = Ident(argDummy) setType pt val uncheckedTypeExtractor = if (unappType.paramTypes.nonEmpty) extractorForUncheckedType(tree.pos, unappType.paramTypes.head) else None - if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) { - //Console.println("UNAPP: need to typetest, arg.tpe = "+arg.tpe+", unappType = "+unappType) - val (freeVars, unappFormal) = freshArgType(unappType.skolemizeExistential(context.owner, tree)) - val unapplyContext = context.makeNewScope(context.tree, context.owner) - freeVars foreach unapplyContext.scope.enter + if (!isApplicableSafe(Nil, unappType, List(pt), WildcardType)) { + //Console.println("UNAPP: need to typetest, arg.tpe = "+arg.tpe+", unappType = "+unappType) + val (freeVars, unappFormal) = freshArgType(unappType.skolemizeExistential(context.owner, tree)) + val unapplyContext = context.makeNewScope(context.tree, context.owner) + freeVars foreach unapplyContext.scope.enter - val typer1 = newTyper(unapplyContext) + val typer1 = newTyper(unapplyContext) val pattp = typer1.infer.inferTypedPattern(tree, unappFormal, arg.tpe, canRemedy = uncheckedTypeExtractor.nonEmpty) - // turn any unresolved type variables in freevars into existential skolems - val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) - arg.tpe = pattp.substSym(freeVars, skolems) - argDummy setInfo arg.tpe - } + // turn any unresolved type variables in freevars into existential skolems + val skolems = freeVars map (fv => unapplyContext.owner.newExistentialSkolem(fv, fv)) + arg.tpe = pattp.substSym(freeVars, skolems) + argDummy setInfo arg.tpe + } - // setType null is necessary so that ref will be stabilized; see bug 881 - val fun1 = typedPos(fun.pos)(Apply(Select(fun setType null, unapp), List(arg))) + // setType null is necessary so that ref will be stabilized; see bug 881 + val fun1 = typedPos(fun.pos)(Apply(Select(fun setType null, unapp), List(arg))) - if (fun1.tpe.isErroneous) { - duplErrTree - } else { - val formals0 = unapplyTypeList(fun1.symbol, fun1.tpe) - val formals1 = formalTypes(formals0, args.length) - if (sameLength(formals1, args)) { - val args1 = typedArgs(args, mode, formals0, formals1) - // This used to be the following (failing) assert: - // assert(isFullyDefined(pt), tree+" ==> "+UnApply(fun1, args1)+", pt = "+pt) - // I modified as follows. See SI-1048. - val pt1 = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) - - val itype = glb(List(pt1, arg.tpe)) - arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) + if (fun1.tpe.isErroneous) duplErrTree + else { + val formals0 = unapplyTypeList(fun1.symbol, fun1.tpe) + val formals1 = formalTypes(formals0, args.length) + + if (!sameLength(formals1, args)) duplErrorTree(WrongNumberArgsPatternError(tree, fun)) + else { + val args1 = typedArgs(args, mode, formals0, formals1) + // This used to be the following (failing) assert: + // assert(isFullyDefined(pt), tree+" ==> "+UnApply(fun1, args1)+", pt = "+pt) + // I modified as follows. See SI-1048. + val pt1 = if (isFullyDefined(pt)) pt else makeFullyDefined(pt) + + val itype = glb(List(pt1, arg.tpe)) + arg.tpe = pt1 // restore type (arg is a dummy tree, just needs to pass typechecking) val unapply = UnApply(fun1, args1) setPos tree.pos setType itype // if the type that the unapply method expects for its argument is uncheckable, wrap in classtag extractor @@ -3170,9 +3171,8 @@ trait Typers extends Modes with Adaptations with Tags { // also skip if we already wrapped a classtag extractor (so we don't keep doing that forever) if (uncheckedTypeExtractor.isEmpty || fun1.symbol.owner.isNonBottomSubClass(ClassTagClass)) unapply else wrapClassTagUnapply(unapply, uncheckedTypeExtractor.get, unappType.paramTypes.head) - } else - duplErrorTree(WrongNumberArgsPatternError(tree, fun)) - } + } + } } def wrapClassTagUnapply(uncheckedPattern: Tree, classTagExtractor: Tree, pt: Type): Tree = { -- cgit v1.2.3 From 552ee9cc54e7045c066c4b915874b9d4de3b0b24 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 12 Jun 2012 17:17:46 +0200 Subject: better fix for SI-5189 pt1 the fix in 0cffdf38d9 was brain dead (only looking at the first argument of the ctor, rather than the first argument list) the reasoning was that we should not look at the MethodType of the constructor, but at the argument types directly (since going through the method type would flip variance, though the ctor arguments are conceptually in covariant positions here) --- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 688dcd91ac..e99c31374e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1104,7 +1104,9 @@ trait Infer { try { // debuglog("TVARS "+ (tvars map (_.constr))) // look at the argument types of the primary constructor corresponding to the pattern - val variances = undetparams map varianceInType(ctorTp.paramTypes.headOption getOrElse ctorTp) + val variances = + if (ctorTp.paramTypes.isEmpty) undetparams map varianceInType(ctorTp) + else undetparams map varianceInTypes(ctorTp.paramTypes) val targs = solvedTypes(tvars, undetparams, variances, true, lubDepth(List(resTp, pt))) // checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") // no checkBounds here. If we enable it, test bug602 fails. -- cgit v1.2.3 From fc0b8644beb564d10ee7f4ea28209aa2053fa1cb Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 22 Jun 2012 09:59:23 +0200 Subject: SI-2442 fixed by virtpatmat -- test files only --- test/files/neg/t2442.check | 5 +++++ test/files/neg/t2442.flags | 1 + test/files/neg/t2442/MyEnum.java | 3 +++ test/files/neg/t2442/t2442.scala | 9 +++++++++ 4 files changed, 18 insertions(+) create mode 100644 test/files/neg/t2442.check create mode 100644 test/files/neg/t2442.flags create mode 100644 test/files/neg/t2442/MyEnum.java create mode 100644 test/files/neg/t2442/t2442.scala diff --git a/test/files/neg/t2442.check b/test/files/neg/t2442.check new file mode 100644 index 0000000000..b45ce3a2f5 --- /dev/null +++ b/test/files/neg/t2442.check @@ -0,0 +1,5 @@ +t2442.scala:4: error: match may not be exhaustive. +It would fail on the following input: THREE + def f(e: MyEnum) = e match { + ^ +one error found diff --git a/test/files/neg/t2442.flags b/test/files/neg/t2442.flags new file mode 100644 index 0000000000..32cf036c3d --- /dev/null +++ b/test/files/neg/t2442.flags @@ -0,0 +1 @@ +-Xexperimental -Xfatal-warnings \ No newline at end of file diff --git a/test/files/neg/t2442/MyEnum.java b/test/files/neg/t2442/MyEnum.java new file mode 100644 index 0000000000..3ffbbb31b8 --- /dev/null +++ b/test/files/neg/t2442/MyEnum.java @@ -0,0 +1,3 @@ +public enum MyEnum { + ONE, TWO, THREE; +} \ No newline at end of file diff --git a/test/files/neg/t2442/t2442.scala b/test/files/neg/t2442/t2442.scala new file mode 100644 index 0000000000..4ca0889400 --- /dev/null +++ b/test/files/neg/t2442/t2442.scala @@ -0,0 +1,9 @@ +class Test { + import MyEnum._ + + def f(e: MyEnum) = e match { + case ONE => println("one") + case TWO => println("two") + // missing case --> exhaustivity warning! + } +} \ No newline at end of file -- cgit v1.2.3 From 4da95609a92f0c685bb7a48688fe9c485f2bc328 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 25 Jun 2012 17:19:58 +0200 Subject: SI-5914 handle null in classtag extractor --- src/library/scala/reflect/ClassTag.scala | 2 +- test/files/run/t5914.check | 1 + test/files/run/t5914.scala | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/files/run/t5914.check create mode 100644 test/files/run/t5914.scala diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index f753dfbcbb..860e7bac28 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -52,7 +52,7 @@ trait ClassTag[T] extends Equals with Serializable { * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)` * is uncheckable, but we have an instance of `ClassTag[T]`. */ - def unapply(x: Any): Option[T] = if (runtimeClass.isAssignableFrom(x.getClass)) Some(x.asInstanceOf[T]) else None + def unapply(x: Any): Option[T] = if (x != null && runtimeClass.isAssignableFrom(x.getClass)) Some(x.asInstanceOf[T]) else None /** case class accessories */ override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] diff --git a/test/files/run/t5914.check b/test/files/run/t5914.check new file mode 100644 index 0000000000..818e321255 --- /dev/null +++ b/test/files/run/t5914.check @@ -0,0 +1 @@ +correct diff --git a/test/files/run/t5914.scala b/test/files/run/t5914.scala new file mode 100644 index 0000000000..53cae9be74 --- /dev/null +++ b/test/files/run/t5914.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ + +object Test extends App { + val tree: Tree = null + tree match { + case TypeTree() => println("lolwut") + case null => println("correct") + } +} \ No newline at end of file -- cgit v1.2.3 From 3601b34fb7bbd60f6f25d3bf44edc653aa7ba45b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Mon, 11 Jun 2012 17:56:29 +0200 Subject: exhaust unit: consider Unit as sealed one of the most boring sealed types imaginable, but a sealed type all the same --- src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 7bac0dac3a..2ee3570905 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -2350,16 +2350,19 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // exhaustivity - // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte - // TODO: domain of feasibly enumerable built-in types (enums, char?) + // TODO: domain of other feasibly enumerable built-in types (char?) def enumerateSubtypes(tp: Type): Option[List[Type]] = tp.typeSymbol match { + // TODO case _ if tp.isTupleType => // recurse into component types? + case UnitClass => + Some(List(UnitClass.tpe)) case BooleanClass => // patmatDebug("enum bool "+ tp) Some(List(ConstantType(Constant(true)), ConstantType(Constant(false)))) // TODO case _ if tp.isTupleType => // recurse into component types case modSym: ModuleClassSymbol => Some(List(tp)) + // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte case sym if !sym.isSealed || isPrimitiveValueClass(sym) => // patmatDebug("enum unsealed "+ (tp, sym, sym.isSealed, isPrimitiveValueClass(sym))) None -- cgit v1.2.3 From 966c9bbebec78b90a79f72c73b55d9de6db34798 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 26 Jun 2012 11:05:25 +0200 Subject: SI-5899 exhaustiveness for non-class types --- .../tools/nsc/typechecker/PatternMatching.scala | 22 +++++++++++++++++----- test/files/pos/t5899.flags | 1 + test/files/pos/t5899.scala | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 test/files/pos/t5899.flags create mode 100644 test/files/pos/t5899.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index 2ee3570905..c466206192 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -2153,6 +2153,21 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // the equals inherited from AnyRef does just this } + // find most precise super-type of tp that is a class + // we skip non-class types (singleton types, abstract types) so that we can + // correctly compute how types relate in terms of the values they rule out + // e.g., when we know some value must be of type T, can it still be of type S? (this is the positive formulation of what `excludes` on Const computes) + // since we're talking values, there must have been a class involved in creating it, so rephrase our types in terms of classes + // (At least conceptually: `true` is an instance of class `Boolean`) + private def widenToClass(tp: Type) = { + // getOrElse to err on the safe side -- all BTS should end in Any, right? + val wideTp = tp.widen + val clsTp = + if (wideTp.typeSymbol.isClass) wideTp + else wideTp.baseTypeSeq.toList.find(_.typeSymbol.isClass).getOrElse(AnyClass.tpe) + // patmatDebug("Widening to class: "+ (tp, clsTp, tp.widen, tp.widen.baseTypeSeq, tp.widen.baseTypeSeq.toList.find(_.typeSymbol.isClass))) + clsTp + } object TypeConst { def apply(tp: Type) = { @@ -2168,7 +2183,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL assert(!(tp =:= NullTp)) private[this] val id: Int = Const.nextTypeId - val wideTp = tp.widen + val wideTp = widenToClass(tp) override def toString = tp.toString //+"#"+ id } @@ -2187,10 +2202,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL val tp = p.tpe.normalize if (tp =:= NullTp) NullConst else { - val wideTp = { - if (p.hasSymbol && p.symbol.isStable) tp.asSeenFrom(tp.prefix, p.symbol.owner).widen - else tp.widen - } + val wideTp = widenToClass(tp) val narrowTp = if (tp.isInstanceOf[SingletonType]) tp diff --git a/test/files/pos/t5899.flags b/test/files/pos/t5899.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t5899.flags @@ -0,0 +1 @@ +-Xfatal-warnings \ No newline at end of file diff --git a/test/files/pos/t5899.scala b/test/files/pos/t5899.scala new file mode 100644 index 0000000000..b16f1f84fe --- /dev/null +++ b/test/files/pos/t5899.scala @@ -0,0 +1,19 @@ +import scala.tools.nsc._ + +trait Foo { + val global: Global + import global.{Name, Symbol, nme} + + case class Bippy(name: Name) + + def f(x: Bippy, sym: Symbol): Int = { + // no warning (!) for + // val Stable = sym.name.toTermName + + val Stable = sym.name + Bippy(Stable) match { + case Bippy(nme.WILDCARD) => 1 + case Bippy(Stable) => 2 // should not be considered unreachable + } + } +} \ No newline at end of file -- cgit v1.2.3 From 175d8714e31bee3146cfb6626b8b40e50852cb22 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Tue, 26 Jun 2012 16:01:48 +0200 Subject: Closes SI-5148. Unfortunately we have to wrap transform to catch all the MissingRequirementErrors exceptions (wrapped in TypeErrors). This is because we force the info of the symbol in a couple of places and we would have to catch all/some of them (and remove the duplicates as well which really becomes messy). Review by @axel22. --- .../nsc/symtab/classfile/ClassfileParser.scala | 4 +-- .../tools/nsc/transform/SpecializeTypes.scala | 30 ++++++++++------------ test/files/neg/t4541.check | 4 +-- test/files/neg/t4541b.check | 4 +-- test/files/neg/t5148.check | 2 ++ test/files/neg/t5148.scala | 4 +++ 6 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 test/files/neg/t5148.check create mode 100644 test/files/neg/t5148.scala diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index d8bf23f4fe..aad3cfb974 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -879,7 +879,7 @@ abstract class ClassfileParser { case tpnme.ScalaSignatureATTR => if (!isScalaAnnot) { debuglog("warning: symbol " + sym.fullName + " has pickled signature in attribute") - unpickler.unpickle(in.buf, in.bp, clazz, staticModule, in.file.toString) + unpickler.unpickle(in.buf, in.bp, clazz, staticModule, in.file.name) } in.skip(attrLen) case tpnme.ScalaATTR => @@ -897,7 +897,7 @@ abstract class ClassfileParser { case Some(san: AnnotationInfo) => val bytes = san.assocs.find({ _._1 == nme.bytes }).get._2.asInstanceOf[ScalaSigBytes].bytes - unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.toString) + unpickler.unpickle(bytes, 0, clazz, staticModule, in.file.name) case None => throw new RuntimeException("Scala class file does not contain Scala annotation") } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 1d820afe11..124d350385 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -1355,8 +1355,19 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } } } + + def reportError[T](body: =>T)(handler: TypeError => T): T = + try body + catch { + case te: TypeError => + reporter.error(te.pos, te.msg) + handler(te) + } - override def transform(tree: Tree): Tree = { + override def transform(tree: Tree): Tree = + reportError { transform1(tree) } {_ => tree} + + def transform1(tree: Tree) = { val symbol = tree.symbol /** The specialized symbol of 'tree.symbol' for tree.tpe, if there is one */ @@ -1382,14 +1393,6 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } else None } - def reportError[T](body: =>T)(handler: TypeError => T): T = - try body - catch { - case te: TypeError => - reporter.error(tree.pos, te.msg) - handler(te) - } - curTree = tree tree match { case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => @@ -1501,13 +1504,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { case ddef @ DefDef(_, _, _, vparamss, _, _) if info.isDefinedAt(symbol) => // log("--> method: " + ddef + " in " + ddef.symbol.owner + ", " + info(symbol)) - def reportTypeError(body: =>Tree) = - try body - catch { - case te: TypeError => - reporter.error(te.pos, te.toString) - ddef - } + def reportTypeError(body: =>Tree) = reportError(body)(_ => ddef) + if (symbol.isConstructor) { val t = atOwner(symbol)(forwardCtorCall(tree.pos, gen.mkSuperSelect, vparamss, symbol.owner)) diff --git a/test/files/neg/t4541.check b/test/files/neg/t4541.check index c01226685f..7bd8ff78f9 100644 --- a/test/files/neg/t4541.check +++ b/test/files/neg/t4541.check @@ -1,7 +1,7 @@ -t4541.scala:11: error: scala.reflect.internal.Types$TypeError: variable data in class Sparse cannot be accessed in Sparse[Int] +t4541.scala:11: error: variable data in class Sparse cannot be accessed in Sparse[Int] Access to protected method data not permitted because prefix type Sparse[Int] does not conform to class Sparse$mcI$sp where the access take place that.data ^ -one error found \ No newline at end of file +one error found diff --git a/test/files/neg/t4541b.check b/test/files/neg/t4541b.check index 54d9c3d1ee..8a52fd97f4 100644 --- a/test/files/neg/t4541b.check +++ b/test/files/neg/t4541b.check @@ -1,7 +1,7 @@ -t4541b.scala:13: error: scala.reflect.internal.Types$TypeError: variable data in class SparseArray cannot be accessed in SparseArray[Int] +t4541b.scala:13: error: variable data in class SparseArray cannot be accessed in SparseArray[Int] Access to protected method data not permitted because prefix type SparseArray[Int] does not conform to class SparseArray$mcI$sp where the access take place use(that.data.clone) ^ -one error found \ No newline at end of file +one error found diff --git a/test/files/neg/t5148.check b/test/files/neg/t5148.check new file mode 100644 index 0000000000..96eb1fd364 --- /dev/null +++ b/test/files/neg/t5148.check @@ -0,0 +1,2 @@ +error: bad reference while unpickling Imports.class: term memberHandlers not found in scala.tools.nsc.interpreter.IMain +one error found diff --git a/test/files/neg/t5148.scala b/test/files/neg/t5148.scala new file mode 100644 index 0000000000..fca64e57df --- /dev/null +++ b/test/files/neg/t5148.scala @@ -0,0 +1,4 @@ +package scala.tools.nsc +package interpreter + +class IMain extends Imports -- cgit v1.2.3 From ceb095362baeaea12983c1d0de3bd309fb7b0dab Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 27 Jun 2012 09:49:16 +0200 Subject: Updated Eclipse project files for `asm`, correcting a small typo in linked source folders. --- src/eclipse/asm/.classpath | 1 - src/eclipse/asm/.project | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/eclipse/asm/.classpath b/src/eclipse/asm/.classpath index f88dea64f4..03d9e9788d 100644 --- a/src/eclipse/asm/.classpath +++ b/src/eclipse/asm/.classpath @@ -1,7 +1,6 @@ - diff --git a/src/eclipse/asm/.project b/src/eclipse/asm/.project index 2c88a0ab11..c9051389af 100644 --- a/src/eclipse/asm/.project +++ b/src/eclipse/asm/.project @@ -16,7 +16,7 @@ - SCALA_BASEDIR + src 2 SCALA_BASEDIR/src/asm -- cgit v1.2.3 From 6ad6e3b6e9547caeebd66061a52294914c667219 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Wed, 27 Jun 2012 10:07:15 +0200 Subject: Test that closes SI-5839. Bug itself most probably fixed by #602 --- test/files/neg/t5839.check | 6 ++++++ test/files/neg/t5839.scala | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 test/files/neg/t5839.check create mode 100644 test/files/neg/t5839.scala diff --git a/test/files/neg/t5839.check b/test/files/neg/t5839.check new file mode 100644 index 0000000000..d4b125bd1e --- /dev/null +++ b/test/files/neg/t5839.check @@ -0,0 +1,6 @@ +t5839.scala:5: error: type mismatch; + found : (x: String => String)Int (x: Int)Int + required: Int => String + val x: String = goo(foo _) + ^ +one error found diff --git a/test/files/neg/t5839.scala b/test/files/neg/t5839.scala new file mode 100644 index 0000000000..d3a5d4b25f --- /dev/null +++ b/test/files/neg/t5839.scala @@ -0,0 +1,7 @@ +object Test { + def goo[T](x: Int => T): T = x(1) + implicit def f(x: Int): String = "" + def foo(x: Int): Int = x + 1 + val x: String = goo(foo _) + def foo(x: String => String) = 1 +} -- cgit v1.2.3 From 22834ee32e8471a8ab33e194d7469686d60ef7d5 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 27 Jun 2012 11:14:24 +0200 Subject: make tests independent of compiler api TODO: t5899 should also be refactored, but I couldn't figure out how I tried the obvious Cake Light pattern with abstract types etc, but that didn't trigger it there must be something with indirection through paths as well --- test/files/neg/t2442.check | 6 +++++- test/files/neg/t2442/MySecondEnum.java | 6 ++++++ test/files/neg/t2442/t2442.scala | 6 ++++++ test/files/run/t5914.scala | 26 ++++++++++++++++++++------ 4 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 test/files/neg/t2442/MySecondEnum.java diff --git a/test/files/neg/t2442.check b/test/files/neg/t2442.check index b45ce3a2f5..714816fd62 100644 --- a/test/files/neg/t2442.check +++ b/test/files/neg/t2442.check @@ -2,4 +2,8 @@ t2442.scala:4: error: match may not be exhaustive. It would fail on the following input: THREE def f(e: MyEnum) = e match { ^ -one error found +t2442.scala:11: error: match may not be exhaustive. +It would fail on the following input: BLUE + def g(e: MySecondEnum) = e match { + ^ +two errors found diff --git a/test/files/neg/t2442/MySecondEnum.java b/test/files/neg/t2442/MySecondEnum.java new file mode 100644 index 0000000000..0f841286de --- /dev/null +++ b/test/files/neg/t2442/MySecondEnum.java @@ -0,0 +1,6 @@ +public enum MySecondEnum { + RED(1), BLUE(2) { public void foo() {} }; + MySecondEnum(int i) {} + + public void foo() {} +} \ No newline at end of file diff --git a/test/files/neg/t2442/t2442.scala b/test/files/neg/t2442/t2442.scala index 4ca0889400..b0a0f3cd41 100644 --- a/test/files/neg/t2442/t2442.scala +++ b/test/files/neg/t2442/t2442.scala @@ -6,4 +6,10 @@ class Test { case TWO => println("two") // missing case --> exhaustivity warning! } + + import MySecondEnum._ + def g(e: MySecondEnum) = e match { + case RED => println("red") + // missing case --> exhaustivity warning! + } } \ No newline at end of file diff --git a/test/files/run/t5914.scala b/test/files/run/t5914.scala index 53cae9be74..45d8815738 100644 --- a/test/files/run/t5914.scala +++ b/test/files/run/t5914.scala @@ -1,9 +1,23 @@ -import scala.reflect.runtime.universe._ +import scala.reflect.ClassTag -object Test extends App { - val tree: Tree = null - tree match { - case TypeTree() => println("lolwut") - case null => println("correct") +trait Trees { + class Tree + implicit val ttTag: ClassTag[TypeTree] + type TypeTree <: Tree + val TypeTree: TypeTreeExtractor + abstract class TypeTreeExtractor { + def unapply(t: TypeTree): Option[String] } + def test(tree: Tree) = + tree match { + case TypeTree(_) => println("lolwut") + case null => println("correct") + } +} + +object Test extends App with Trees { + val ttTag = implicitly[ClassTag[TypeTree]] + case class TypeTree(meh: String) extends Tree + object TypeTree extends TypeTreeExtractor + test(null) // should not crash } \ No newline at end of file -- cgit v1.2.3 From 674f4db1cd9323cb4e637dca6f4641d76c09fc84 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 13:59:20 +0200 Subject: Parallelize convertTo in parallel collection. --- .../collection/parallel/ParIterableLike.scala | 12 ++++++------ test/files/run/collection-conversions.check | 22 ++++++++++++++++++++++ test/files/run/collection-conversions.scala | 6 +++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/library/scala/collection/parallel/ParIterableLike.scala b/src/library/scala/collection/parallel/ParIterableLike.scala index a7ec833193..815253b537 100644 --- a/src/library/scala/collection/parallel/ParIterableLike.scala +++ b/src/library/scala/collection/parallel/ParIterableLike.scala @@ -841,7 +841,7 @@ self: ParIterableLike[T, Repr, Sequential] => override def toBuffer[U >: T]: collection.mutable.Buffer[U] = seq.toBuffer // have additional, parallel buffers? - override def toTraversable: GenTraversable[T] = this.asInstanceOf[GenTraversable[T]] // TODO add ParTraversable[T] + override def toTraversable: GenTraversable[T] = this.asInstanceOf[GenTraversable[T]] override def toIterable: ParIterable[T] = this.asInstanceOf[ParIterable[T]] @@ -850,13 +850,13 @@ self: ParIterableLike[T, Repr, Sequential] => override def toSet[U >: T]: immutable.ParSet[U] = toParCollection[U, immutable.ParSet[U]](() => immutable.ParSet.newCombiner[U]) override def toMap[K, V](implicit ev: T <:< (K, V)): immutable.ParMap[K, V] = toParMap[K, V, immutable.ParMap[K, V]](() => immutable.ParMap.newCombiner[K, V]) - - // TODO(@alex22): make these better - override def toVector: Vector[T] = seq.toVector - - override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = seq.convertTo[Col] + override def toVector: Vector[T] = convertTo[Vector] + override def convertTo[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = if (cbf().isCombiner) { + toParCollection[T, Col[T]](() => cbf().asCombiner) + } else seq.convertTo(cbf) + /* tasks */ protected trait StrictSplitterCheckTask[R, Tp] extends Task[R, Tp] { diff --git a/test/files/run/collection-conversions.check b/test/files/run/collection-conversions.check index 08d0fa32c5..5e43d25f7e 100644 --- a/test/files/run/collection-conversions.check +++ b/test/files/run/collection-conversions.check @@ -11,6 +11,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing Vector --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -24,6 +25,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing List --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -37,6 +39,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing Buffer --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -50,6 +53,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing ParVector --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -63,6 +67,21 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK +-- Testing ParArray --- + :[Direct] Vector : OK + :[Copy] Vector : OK + :[Direct] Buffer : OK + :[Copy] Buffer : OK + :[Direct] GenSeq : OK + :[Copy] GenSeq : OK + :[Copy] Seq : OK + :[Direct] Stream : OK + :[Copy] Stream : OK + :[Direct] Array : OK + :[Copy] Array : OK + :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing Set --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -76,6 +95,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing SetView --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -89,6 +109,7 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK -- Testing BufferView --- :[Direct] Vector : OK :[Copy] Vector : OK @@ -102,3 +123,4 @@ :[Direct] Array : OK :[Copy] Array : OK :[Copy] ParVector: OK + :[Copy] ParArray : OK \ No newline at end of file diff --git a/test/files/run/collection-conversions.scala b/test/files/run/collection-conversions.scala index b5c4d8e261..6d90caee4b 100644 --- a/test/files/run/collection-conversions.scala +++ b/test/files/run/collection-conversions.scala @@ -1,6 +1,7 @@ import collection._ import mutable.Buffer import parallel.immutable.ParVector +import parallel.mutable.ParArray import reflect.ClassTag object Test { @@ -29,6 +30,7 @@ object Test { val testStream = Stream(1,2,3) val testArray = Array(1,2,3) val testParVector = ParVector(1,2,3) + val testParArray = ParArray(1,2,3) def testConversion[A: ClassTag](name: String, col: => GenTraversableOnce[A]): Unit = { val tmp = col @@ -45,14 +47,16 @@ object Test { printResult("[Direct] Array ", col.toArray, testArray) printResult("[Copy] Array ", col.convertTo[Array], testArray) printResult("[Copy] ParVector", col.convertTo[ParVector], testParVector) + printResult("[Copy] ParArray ", col.convertTo[ParArray], testParArray) } def main(args: Array[String]): Unit = { - testConversion("iterator", (1 to 3).iterator) + testConversion("iterator", (1 to 3).iterator) testConversion("Vector", Vector(1,2,3)) testConversion("List", List(1,2,3)) testConversion("Buffer", Buffer(1,2,3)) testConversion("ParVector", ParVector(1,2,3)) + testConversion("ParArray", ParArray(1,2,3)) testConversion("Set", Set(1,2,3)) testConversion("SetView", Set(1,2,3).view) testConversion("BufferView", Buffer(1,2,3).view) -- cgit v1.2.3 From 1a3976fc8276e6e3d7eeda12f645782ca93ea24b Mon Sep 17 00:00:00 2001 From: phaller Date: Tue, 26 Jun 2012 17:09:21 +0200 Subject: Revert pull request #720 (CPS: enable return expressions in CPS code if they are in tail position) Reverts commit 0ada0706746c9c603bf5bc8a0e6780e5783297cf. Reverts commit 51c92f02229098d0b402a65a72267f7a17984022. Reverts commit cdfbe8e39fbbec00c969cd74f117ae410b98b40b. Reverts commit 796024c7429a03e974a7d8e1dc5c80b84f82467d. --- .../scala/tools/nsc/typechecker/Modes.scala | 4 -- .../scala/tools/nsc/typechecker/Typers.scala | 3 +- .../tools/selectivecps/CPSAnnotationChecker.scala | 15 -------- .../plugin/scala/tools/selectivecps/CPSUtils.scala | 40 -------------------- .../tools/selectivecps/SelectiveANFTransform.scala | 15 +------- .../continuations-neg/ts-1681-nontail-return.check | 4 -- .../continuations-neg/ts-1681-nontail-return.scala | 18 --------- test/files/continuations-run/ts-1681-2.check | 5 --- test/files/continuations-run/ts-1681-2.scala | 44 ---------------------- test/files/continuations-run/ts-1681-3.check | 4 -- test/files/continuations-run/ts-1681-3.scala | 27 ------------- test/files/continuations-run/ts-1681.check | 3 -- test/files/continuations-run/ts-1681.scala | 29 -------------- 13 files changed, 3 insertions(+), 208 deletions(-) delete mode 100644 test/files/continuations-neg/ts-1681-nontail-return.check delete mode 100644 test/files/continuations-neg/ts-1681-nontail-return.scala delete mode 100644 test/files/continuations-run/ts-1681-2.check delete mode 100644 test/files/continuations-run/ts-1681-2.scala delete mode 100644 test/files/continuations-run/ts-1681-3.check delete mode 100644 test/files/continuations-run/ts-1681-3.scala delete mode 100644 test/files/continuations-run/ts-1681.check delete mode 100644 test/files/continuations-run/ts-1681.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Modes.scala b/src/compiler/scala/tools/nsc/typechecker/Modes.scala index bde3ad98c9..3eff5ef024 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Modes.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Modes.scala @@ -86,10 +86,6 @@ trait Modes { */ final val TYPEPATmode = 0x10000 - /** RETmode is set when we are typing a return expression. - */ - final val RETmode = 0x20000 - final private val StickyModes = EXPRmode | PATTERNmode | TYPEmode | ALTmode final def onlyStickyModes(mode: Int) = diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index acf1b3dc59..e6186bd41a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4024,8 +4024,7 @@ trait Typers extends Modes with Adaptations with Tags { ReturnWithoutTypeError(tree, enclMethod.owner) } else { context.enclMethod.returnsSeen = true - val expr1: Tree = typed(expr, EXPRmode | BYVALmode | RETmode, restpt.tpe) - + val expr1: Tree = typed(expr, EXPRmode | BYVALmode, restpt.tpe) // Warn about returning a value if no value can be returned. if (restpt.tpe.typeSymbol == UnitClass) { // The typing in expr1 says expr is Unit (it has already been coerced if diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 464ffc6fab..a20ff1667b 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -171,9 +171,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { vprintln("yes we can!! (byval)") return true } - } else if ((mode & global.analyzer.RETmode) != 0) { - vprintln("yes we can!! (return)") - return true } } false @@ -187,7 +184,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { val patMode = (mode & global.analyzer.PATTERNmode) != 0 val exprMode = (mode & global.analyzer.EXPRmode) != 0 val byValMode = (mode & global.analyzer.BYVALmode) != 0 - val retMode = (mode & global.analyzer.RETmode) != 0 val annotsTree = cpsParamAnnotation(tree.tpe) val annotsExpected = cpsParamAnnotation(pt) @@ -214,12 +210,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { val res = tree modifyType addMinusMarker vprintln("adapted annotations (by val) of " + tree + " to " + res.tpe) res - } else if (retMode && !hasPlusMarker(tree.tpe) && annotsTree.isEmpty && annotsExpected.nonEmpty) { - // add a marker annotation that will make tree.tpe behave as pt, subtyping wise - // tree will look like having no annotation - val res = tree modifyType (_ withAnnotations List(newPlusMarker())) - vprintln("adapted annotations (return) of " + tree + " to " + res.tpe) - res } else tree } @@ -476,11 +466,6 @@ abstract class CPSAnnotationChecker extends CPSUtils with Modes { } tpe - case ret @ Return(expr) => - if (hasPlusMarker(expr.tpe)) - ret setType expr.tpe - ret.tpe - case _ => tpe } diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 765cde5a81..3a1dc87a6a 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -3,7 +3,6 @@ package scala.tools.selectivecps import scala.tools.nsc.Global -import scala.collection.mutable.ListBuffer trait CPSUtils { val global: Global @@ -136,43 +135,4 @@ trait CPSUtils { case _ => None } } - - def isTailReturn(retExpr: Tree, body: Tree): Boolean = { - val removed = ListBuffer[Tree]() - removeTailReturn(body, removed) - removed contains retExpr - } - - def removeTailReturn(tree: Tree, removed: ListBuffer[Tree]): Tree = tree match { - case Block(stms, r @ Return(expr)) => - removed += r - treeCopy.Block(tree, stms, expr) - - case Block(stms, expr) => - treeCopy.Block(tree, stms, removeTailReturn(expr, removed)) - - case If(cond, r1 @ Return(thenExpr), r2 @ Return(elseExpr)) => - removed ++= Seq(r1, r2) - treeCopy.If(tree, cond, removeTailReturn(thenExpr, removed), removeTailReturn(elseExpr, removed)) - - case If(cond, thenExpr, elseExpr) => - treeCopy.If(tree, cond, removeTailReturn(thenExpr, removed), removeTailReturn(elseExpr, removed)) - - case Try(block, catches, finalizer) => - treeCopy.Try(tree, - removeTailReturn(block, removed), - (catches map (t => removeTailReturn(t, removed))).asInstanceOf[List[CaseDef]], - removeTailReturn(finalizer, removed)) - - case CaseDef(pat, guard, r @ Return(expr)) => - removed += r - treeCopy.CaseDef(tree, pat, guard, expr) - - case CaseDef(pat, guard, body) => - treeCopy.CaseDef(tree, pat, guard, removeTailReturn(body, removed)) - - case _ => - tree - } - } diff --git a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala index fe465aad0d..017c8d24fd 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/SelectiveANFTransform.scala @@ -9,8 +9,6 @@ import scala.tools.nsc.plugins._ import scala.tools.nsc.ast._ -import scala.collection.mutable.ListBuffer - /** * In methods marked @cps, explicitly name results of calls to other @cps methods */ @@ -48,20 +46,10 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with // this would cause infinite recursion. But we could remove the // ValDef case here. - case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs0) => + case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => debuglog("transforming " + dd.symbol) atOwner(dd.symbol) { - val tailReturns = ListBuffer[Tree]() - val rhs = removeTailReturn(rhs0, tailReturns) - // throw an error if there is a Return tree which is not in tail position - rhs0 foreach { - case r @ Return(_) => - if (!tailReturns.contains(r)) - unit.error(r.pos, "return expressions in CPS code must be in tail position") - case _ => /* do nothing */ - } - val rhs1 = transExpr(rhs, None, getExternalAnswerTypeAnn(tpt.tpe)) debuglog("result "+rhs1) @@ -165,6 +153,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with } } + def transExpr(tree: Tree, cpsA: CPSInfo, cpsR: CPSInfo): Tree = { transTailValue(tree, cpsA, cpsR) match { case (Nil, b) => b diff --git a/test/files/continuations-neg/ts-1681-nontail-return.check b/test/files/continuations-neg/ts-1681-nontail-return.check deleted file mode 100644 index 8fe15f154b..0000000000 --- a/test/files/continuations-neg/ts-1681-nontail-return.check +++ /dev/null @@ -1,4 +0,0 @@ -ts-1681-nontail-return.scala:10: error: return expressions in CPS code must be in tail position - return v - ^ -one error found diff --git a/test/files/continuations-neg/ts-1681-nontail-return.scala b/test/files/continuations-neg/ts-1681-nontail-return.scala deleted file mode 100644 index af86ad304f..0000000000 --- a/test/files/continuations-neg/ts-1681-nontail-return.scala +++ /dev/null @@ -1,18 +0,0 @@ -import scala.util.continuations._ - -class ReturnRepro { - def s1: Int @cpsParam[Any, Unit] = shift { k => k(5) } - def caller = reset { println(p(3)) } - - def p(i: Int): Int @cpsParam[Unit, Any] = { - val v= s1 + 3 - if (v == 8) - return v - v + 1 - } -} - -object Test extends App { - val repro = new ReturnRepro - repro.caller -} diff --git a/test/files/continuations-run/ts-1681-2.check b/test/files/continuations-run/ts-1681-2.check deleted file mode 100644 index 35b3c93780..0000000000 --- a/test/files/continuations-run/ts-1681-2.check +++ /dev/null @@ -1,5 +0,0 @@ -8 -hi -8 -from try -8 diff --git a/test/files/continuations-run/ts-1681-2.scala b/test/files/continuations-run/ts-1681-2.scala deleted file mode 100644 index 8a896dec2c..0000000000 --- a/test/files/continuations-run/ts-1681-2.scala +++ /dev/null @@ -1,44 +0,0 @@ -import scala.util.continuations._ - -class ReturnRepro { - def s1: Int @cps[Any] = shift { k => k(5) } - def caller = reset { println(p(3)) } - def caller2 = reset { println(p2(3)) } - def caller3 = reset { println(p3(3)) } - - def p(i: Int): Int @cps[Any] = { - val v= s1 + 3 - return v - } - - def p2(i: Int): Int @cps[Any] = { - val v = s1 + 3 - if (v > 0) { - println("hi") - return v - } else { - println("hi") - return 8 - } - } - - def p3(i: Int): Int @cps[Any] = { - val v = s1 + 3 - try { - println("from try") - return v - } catch { - case e: Exception => - println("from catch") - return 7 - } - } - -} - -object Test extends App { - val repro = new ReturnRepro - repro.caller - repro.caller2 - repro.caller3 -} diff --git a/test/files/continuations-run/ts-1681-3.check b/test/files/continuations-run/ts-1681-3.check deleted file mode 100644 index 71489f097c..0000000000 --- a/test/files/continuations-run/ts-1681-3.check +++ /dev/null @@ -1,4 +0,0 @@ -enter return expr -8 -hi -8 diff --git a/test/files/continuations-run/ts-1681-3.scala b/test/files/continuations-run/ts-1681-3.scala deleted file mode 100644 index 62c547f5a2..0000000000 --- a/test/files/continuations-run/ts-1681-3.scala +++ /dev/null @@ -1,27 +0,0 @@ -import scala.util.continuations._ - -class ReturnRepro { - def s1: Int @cpsParam[Any, Unit] = shift { k => k(5) } - def caller = reset { println(p(3)) } - def caller2 = reset { println(p2(3)) } - - def p(i: Int): Int @cpsParam[Unit, Any] = { - val v= s1 + 3 - return { println("enter return expr"); v } - } - - def p2(i: Int): Int @cpsParam[Unit, Any] = { - val v = s1 + 3 - if (v > 0) { - return { println("hi"); v } - } else { - return { println("hi"); 8 } - } - } -} - -object Test extends App { - val repro = new ReturnRepro - repro.caller - repro.caller2 -} diff --git a/test/files/continuations-run/ts-1681.check b/test/files/continuations-run/ts-1681.check deleted file mode 100644 index 85176d8e66..0000000000 --- a/test/files/continuations-run/ts-1681.check +++ /dev/null @@ -1,3 +0,0 @@ -8 -hi -8 diff --git a/test/files/continuations-run/ts-1681.scala b/test/files/continuations-run/ts-1681.scala deleted file mode 100644 index efb1abae15..0000000000 --- a/test/files/continuations-run/ts-1681.scala +++ /dev/null @@ -1,29 +0,0 @@ -import scala.util.continuations._ - -class ReturnRepro { - def s1: Int @cpsParam[Any, Unit] = shift { k => k(5) } - def caller = reset { println(p(3)) } - def caller2 = reset { println(p2(3)) } - - def p(i: Int): Int @cpsParam[Unit, Any] = { - val v= s1 + 3 - return v - } - - def p2(i: Int): Int @cpsParam[Unit, Any] = { - val v = s1 + 3 - if (v > 0) { - println("hi") - return v - } else { - println("hi") - return 8 - } - } -} - -object Test extends App { - val repro = new ReturnRepro - repro.caller - repro.caller2 -} -- cgit v1.2.3 From ba542ba60836cd89e89ac1e9efbc46be14d766f3 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 14:40:39 +0200 Subject: Fix SI-5971. When using `AbstractTransformed` abstract inner class in views in order to force generating bridges, one must take care to push the corresponding collection trait (such as `Iterable` or `Seq`) as far as possible to the left in the linearization order -- otherwise, overridden methods from these traits can override the already overridden methods in view. This was the case with `takeWhile`. --- .../scala/collection/IterableViewLike.scala | 2 +- src/library/scala/collection/SeqViewLike.scala | 2 +- .../scala/collection/TraversableViewLike.scala | 2 +- test/files/run/t5971.check | 4 ++++ test/files/run/t5971.scala | 23 ++++++++++++++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 test/files/run/t5971.check create mode 100644 test/files/run/t5971.scala diff --git a/src/library/scala/collection/IterableViewLike.scala b/src/library/scala/collection/IterableViewLike.scala index c842475590..e0c8b21d09 100644 --- a/src/library/scala/collection/IterableViewLike.scala +++ b/src/library/scala/collection/IterableViewLike.scala @@ -44,7 +44,7 @@ trait IterableViewLike[+A, } /** Explicit instantiation of the `Transformed` trait to reduce class file size in subclasses. */ - private[collection] abstract class AbstractTransformed[+B] extends super[TraversableViewLike].Transformed[B] with Transformed[B] + private[collection] abstract class AbstractTransformed[+B] extends Iterable[B] with super[TraversableViewLike].Transformed[B] with Transformed[B] trait EmptyView extends Transformed[Nothing] with super[TraversableViewLike].EmptyView with super[GenIterableViewLike].EmptyView diff --git a/src/library/scala/collection/SeqViewLike.scala b/src/library/scala/collection/SeqViewLike.scala index f64045c9f6..73f5dda11c 100644 --- a/src/library/scala/collection/SeqViewLike.scala +++ b/src/library/scala/collection/SeqViewLike.scala @@ -40,7 +40,7 @@ trait SeqViewLike[+A, } /** Explicit instantiation of the `Transformed` trait to reduce class file size in subclasses. */ - private[collection] abstract class AbstractTransformed[+B] extends super[IterableViewLike].AbstractTransformed[B] with Transformed[B] + private[collection] abstract class AbstractTransformed[+B] extends Seq[B] with super[IterableViewLike].Transformed[B] with Transformed[B] trait EmptyView extends Transformed[Nothing] with super[IterableViewLike].EmptyView with super[GenSeqViewLike].EmptyView diff --git a/src/library/scala/collection/TraversableViewLike.scala b/src/library/scala/collection/TraversableViewLike.scala index eb2091a5f3..bf4f8205d6 100644 --- a/src/library/scala/collection/TraversableViewLike.scala +++ b/src/library/scala/collection/TraversableViewLike.scala @@ -117,7 +117,7 @@ trait TraversableViewLike[+A, } /** Explicit instantiation of the `Transformed` trait to reduce class file size in subclasses. */ - private[collection] abstract class AbstractTransformed[+B] extends Transformed[B] + private[collection] abstract class AbstractTransformed[+B] extends Traversable[B] with Transformed[B] trait EmptyView extends Transformed[Nothing] with super.EmptyView diff --git a/test/files/run/t5971.check b/test/files/run/t5971.check new file mode 100644 index 0000000000..0c36a1ff02 --- /dev/null +++ b/test/files/run/t5971.check @@ -0,0 +1,4 @@ +r,b +r +a,b +r,a,b \ No newline at end of file diff --git a/test/files/run/t5971.scala b/test/files/run/t5971.scala new file mode 100644 index 0000000000..dbd9beebb3 --- /dev/null +++ b/test/files/run/t5971.scala @@ -0,0 +1,23 @@ + + + + + +/** When using `AbstractTransformed` abstract inner class in views in order + * to force generating bridges, one must take care to push the corresponding + * collection trait (such as `Iterable` or `Seq`) as far as possible to the + * left in the linearization order -- otherwise, overridden methods from these + * traits can override the already overridden methods in view. This was the + * case with `takeWhile`. + * Mind blowing, I know. + */ +object Test { + + def main(args: Array[String]) { + println("bar".view.reverse.filter(_ > 'a').mkString(",")) + println("bar".view.reverse.take(1).mkString(",")) + println("bar".view.reverse.dropWhile(_ > 'a').mkString(",")) + println("bar".view.reverse.takeWhile(_ => true).mkString(",")) + } + +} -- cgit v1.2.3 From 788ac7502154ca1329773ec869242df21015f5f3 Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 13:34:49 +0200 Subject: Fix SI-5986. Here we had an issue that RedBlack does not work the same way for sets - which are not supposed to replace an element if it is the same (wrt equals) and maps - which should replace the corresponding values. Adding an overwrite parameter which decides whether to overwrite added keys if they are the same in the ordering. Fix tests. --- .../scala/collection/immutable/RedBlackTree.scala | 33 ++++++++++---------- .../scala/collection/immutable/TreeMap.scala | 4 +-- .../scala/collection/immutable/TreeSet.scala | 4 +-- test/files/run/t5986.check | 15 +++++++++ test/files/run/t5986.scala | 36 ++++++++++++++++++++++ test/files/scalacheck/redblacktree.scala | 4 +-- 6 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 test/files/run/t5986.check create mode 100644 test/files/run/t5986.scala diff --git a/src/library/scala/collection/immutable/RedBlackTree.scala b/src/library/scala/collection/immutable/RedBlackTree.scala index 0f28c4997b..4b573511d1 100644 --- a/src/library/scala/collection/immutable/RedBlackTree.scala +++ b/src/library/scala/collection/immutable/RedBlackTree.scala @@ -43,7 +43,7 @@ object RedBlackTree { } def count(tree: Tree[_, _]) = if (tree eq null) 0 else tree.count - def update[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1)(implicit ordering: Ordering[A]): Tree[A, B1] = blacken(upd(tree, k, v)) + def update[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = blacken(upd(tree, k, v, overwrite)) def delete[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = blacken(del(tree, k)) def rangeImpl[A: Ordering, B](tree: Tree[A, B], from: Option[A], until: Option[A]): Tree[A, B] = (from, until) match { case (Some(from), Some(until)) => this.range(tree, from, until) @@ -122,17 +122,18 @@ object RedBlackTree { else mkTree(isBlack, x, xv, a, r) } - private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { + private[this] def upd[A, B, B1 >: B](tree: Tree[A, B], k: A, v: B1, overwrite: Boolean)(implicit ordering: Ordering[A]): Tree[A, B1] = if (tree eq null) { RedTree(k, v, null, null) } else { val cmp = ordering.compare(k, tree.key) - if (cmp < 0) balanceLeft(isBlackTree(tree), tree.key, tree.value, upd(tree.left, k, v), tree.right) - else if (cmp > 0) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, upd(tree.right, k, v)) - else mkTree(isBlackTree(tree), k, v, tree.left, tree.right) + if (cmp < 0) balanceLeft(isBlackTree(tree), tree.key, tree.value, upd(tree.left, k, v, overwrite), tree.right) + else if (cmp > 0) balanceRight(isBlackTree(tree), tree.key, tree.value, tree.left, upd(tree.right, k, v, overwrite)) + else if (overwrite || k != tree.key) mkTree(isBlackTree(tree), k, v, tree.left, tree.right) + else tree } - // Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees - // http://www.cse.unsw.edu.au/~dons/data/RedBlackTree.html + /* Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees + * http://www.cse.unsw.edu.au/~dons/data/RedBlackTree.html */ 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 balance(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tl)) { if (isRedTree(tr)) { @@ -216,7 +217,7 @@ object RedBlackTree { if (ordering.lt(tree.key, from)) return doFrom(tree.right, from) val newLeft = doFrom(tree.left, from) if (newLeft eq tree.left) tree - else if (newLeft eq null) upd(tree.right, tree.key, tree.value) + else if (newLeft eq null) upd(tree.right, tree.key, tree.value, false) else rebalance(tree, newLeft, tree.right) } private[this] def doTo[A, B](tree: Tree[A, B], to: A)(implicit ordering: Ordering[A]): Tree[A, B] = { @@ -224,7 +225,7 @@ object RedBlackTree { if (ordering.lt(to, tree.key)) return doTo(tree.left, to) val newRight = doTo(tree.right, to) if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value) + else if (newRight eq null) upd(tree.left, tree.key, tree.value, false) else rebalance(tree, tree.left, newRight) } private[this] def doUntil[A, B](tree: Tree[A, B], until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { @@ -232,7 +233,7 @@ object RedBlackTree { if (ordering.lteq(until, tree.key)) return doUntil(tree.left, until) val newRight = doUntil(tree.right, until) if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value) + else if (newRight eq null) upd(tree.left, tree.key, tree.value, false) else rebalance(tree, tree.left, newRight) } private[this] def doRange[A, B](tree: Tree[A, B], from: A, until: A)(implicit ordering: Ordering[A]): Tree[A, B] = { @@ -242,8 +243,8 @@ object RedBlackTree { val newLeft = doFrom(tree.left, from) val newRight = doUntil(tree.right, until) if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) upd(newRight, tree.key, tree.value); - else if (newRight eq null) upd(newLeft, tree.key, tree.value); + else if (newLeft eq null) upd(newRight, tree.key, tree.value, false); + else if (newRight eq null) upd(newLeft, tree.key, tree.value, false); else rebalance(tree, newLeft, newRight) } @@ -254,7 +255,7 @@ object RedBlackTree { if (n > count) return doDrop(tree.right, n - count - 1) val newLeft = doDrop(tree.left, n) if (newLeft eq tree.left) tree - else if (newLeft eq null) upd(tree.right, tree.key, tree.value) + else if (newLeft eq null) upd(tree.right, tree.key, tree.value, false) else rebalance(tree, newLeft, tree.right) } private[this] def doTake[A: Ordering, B](tree: Tree[A, B], n: Int): Tree[A, B] = { @@ -264,7 +265,7 @@ object RedBlackTree { if (n <= count) return doTake(tree.left, n) val newRight = doTake(tree.right, n - count - 1) if (newRight eq tree.right) tree - else if (newRight eq null) upd(tree.left, tree.key, tree.value) + else if (newRight eq null) upd(tree.left, tree.key, tree.value, false) else rebalance(tree, tree.left, newRight) } private[this] def doSlice[A: Ordering, B](tree: Tree[A, B], from: Int, until: Int): Tree[A, B] = { @@ -275,8 +276,8 @@ object RedBlackTree { val newLeft = doDrop(tree.left, from) val newRight = doTake(tree.right, until - count - 1) if ((newLeft eq tree.left) && (newRight eq tree.right)) tree - else if (newLeft eq null) upd(newRight, tree.key, tree.value) - else if (newRight eq null) upd(newLeft, tree.key, tree.value) + else if (newLeft eq null) upd(newRight, tree.key, tree.value, false) + else if (newRight eq null) upd(newLeft, tree.key, tree.value, false) else rebalance(tree, newLeft, newRight) } diff --git a/src/library/scala/collection/immutable/TreeMap.scala b/src/library/scala/collection/immutable/TreeMap.scala index 4c1a5f2e03..51bc76efc3 100644 --- a/src/library/scala/collection/immutable/TreeMap.scala +++ b/src/library/scala/collection/immutable/TreeMap.scala @@ -131,7 +131,7 @@ class TreeMap[A, +B] private (tree: RB.Tree[A, B])(implicit val ordering: Orderi * @param value the value to be associated with `key` * @return a new $coll with the updated binding */ - override def updated [B1 >: B](key: A, value: B1): TreeMap[A, B1] = new TreeMap(RB.update(tree, key, value)) + override def updated [B1 >: B](key: A, value: B1): TreeMap[A, B1] = new TreeMap(RB.update(tree, key, value, true)) /** Add a key/value pair to this map. * @tparam B1 type of the value of the new binding, a supertype of `B` @@ -171,7 +171,7 @@ class TreeMap[A, +B] private (tree: RB.Tree[A, B])(implicit val ordering: Orderi */ def insert [B1 >: B](key: A, value: B1): TreeMap[A, B1] = { assert(!RB.contains(tree, key)) - new TreeMap(RB.update(tree, key, value)) + new TreeMap(RB.update(tree, key, value, true)) } def - (key:A): TreeMap[A, B] = diff --git a/src/library/scala/collection/immutable/TreeSet.scala b/src/library/scala/collection/immutable/TreeSet.scala index 882e828c5b..697da2bc4b 100644 --- a/src/library/scala/collection/immutable/TreeSet.scala +++ b/src/library/scala/collection/immutable/TreeSet.scala @@ -112,7 +112,7 @@ class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: Orderin * @param elem a new element to add. * @return a new $coll containing `elem` and all the elements of this $coll. */ - def + (elem: A): TreeSet[A] = newSet(RB.update(tree, elem, ())) + def + (elem: A): TreeSet[A] = newSet(RB.update(tree, elem, (), false)) /** A new `TreeSet` with the entry added is returned, * assuming that elem is not in the TreeSet. @@ -122,7 +122,7 @@ class TreeSet[A] private (tree: RB.Tree[A, Unit])(implicit val ordering: Orderin */ def insert(elem: A): TreeSet[A] = { assert(!RB.contains(tree, elem)) - newSet(RB.update(tree, elem, ())) + newSet(RB.update(tree, elem, (), false)) } /** Creates a new `TreeSet` with the entry removed. diff --git a/test/files/run/t5986.check b/test/files/run/t5986.check new file mode 100644 index 0000000000..4101770c6d --- /dev/null +++ b/test/files/run/t5986.check @@ -0,0 +1,15 @@ +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) +Foo(bar, 1) +Foo(bar, 1) +Foo(bar, 1),Foo(baz, 3),Foo(bazz, 4) \ No newline at end of file diff --git a/test/files/run/t5986.scala b/test/files/run/t5986.scala new file mode 100644 index 0000000000..8cf7086f98 --- /dev/null +++ b/test/files/run/t5986.scala @@ -0,0 +1,36 @@ + + + +import scala.collection._ + + + +/** A sorted set should not replace elements when adding + * and the element already exists in the set. + */ +object Test { + + class Foo(val name: String, val n: Int) { + override def equals(obj: Any): Boolean = obj match { case other: Foo => name == other.name; case _ => false } + override def hashCode = name.## + override def toString = "Foo(" + name + ", " + n + ")" + } + + implicit val ordering: Ordering[Foo] = Ordering.fromLessThan[Foo] { (a, b) => a.name.compareTo(b.name) < 0 } + + def check[S <: Set[Foo]](set: S) { + def output(s: Set[Foo]) = println(s.toList.sorted.mkString(",")) + output(set + new Foo("bar", 2)) + output(set ++ List(new Foo("bar", 2), new Foo("bar", 3), new Foo("bar", 4))) + output(set union Set(new Foo("bar", 2), new Foo("baz", 3), new Foo("bazz", 4))) + } + + def main(args: Array[String]) { + check(Set(new Foo("bar", 1))) + check(immutable.Set(new Foo("bar", 1))) + check(mutable.Set(new Foo("bar", 1))) + check(immutable.SortedSet(new Foo("bar", 1))) + check(mutable.SortedSet(new Foo("bar", 1))) + } + +} diff --git a/test/files/scalacheck/redblacktree.scala b/test/files/scalacheck/redblacktree.scala index e4b356c889..e2609fa200 100644 --- a/test/files/scalacheck/redblacktree.scala +++ b/test/files/scalacheck/redblacktree.scala @@ -121,7 +121,7 @@ package scala.collection.immutable.redblacktree { override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, iterator(tree).size + 1) - override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = update(tree, generateKey(tree, parm), 0) + override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = update(tree, generateKey(tree, parm), 0, true) def generateKey(tree: Tree[String, Int], parm: ModifyParm): String = nodeAt(tree, parm) match { case Some((key, _)) => key.init.mkString + "MN" @@ -144,7 +144,7 @@ package scala.collection.immutable.redblacktree { override type ModifyParm = Int override def genParm(tree: Tree[String, Int]): Gen[ModifyParm] = choose(0, iterator(tree).size) override def modify(tree: Tree[String, Int], parm: ModifyParm): Tree[String, Int] = nodeAt(tree, parm) map { - case (key, _) => update(tree, key, newValue) + case (key, _) => update(tree, key, newValue, true) } getOrElse tree property("update modifies values") = forAll(genInput) { case (tree, parm, newTree) => -- cgit v1.2.3 From 5362f3df48a363308e41434b17fca60a0d4d84da Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 16:30:35 +0200 Subject: Fix SI-3326. The heart of the problem - we want to retain the ordering when using `++` on sorted maps. There are 2 `++` overloads - a generic one in traversables and a map-specific one in `MapLike` - which knows about the ordering. The problem here is that the expected return type for the expression in which `++` appears drives the decision of the overload that needs to be taken. The `collection.SortedMap` does not have `++` overridden to return `SortedMap`, but `immutable.Map` instead. This is why `collection.SortedMap` used to resort to the generic `TraversableLike.++` which knows nothing about the ordering. To avoid `collection.SortedMap`s resort to the more generic `TraverableLike.++`, we override the `MapLike.++` overload in `collection.SortedMap` to return the proper type `SortedMap`. --- src/library/scala/collection/SortedMapLike.scala | 8 +++ test/files/run/t3326.check | 8 +++ test/files/run/t3326.scala | 74 ++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 test/files/run/t3326.check create mode 100644 test/files/run/t3326.scala diff --git a/src/library/scala/collection/SortedMapLike.scala b/src/library/scala/collection/SortedMapLike.scala index 4dc0820a62..6b25151192 100644 --- a/src/library/scala/collection/SortedMapLike.scala +++ b/src/library/scala/collection/SortedMapLike.scala @@ -72,4 +72,12 @@ self => for (e <- elems) m = m + e m } + + /** Adds a number of elements provided by a traversable object + * and returns a new collection with the added elements. + * + * @param xs the traversable object. + */ + override def ++[B1 >: B](xs: GenTraversableOnce[(A, B1)]): SortedMap[A, B1] = + ((repr: SortedMap[A, B1]) /: xs.seq) (_ + _) } diff --git a/test/files/run/t3326.check b/test/files/run/t3326.check new file mode 100644 index 0000000000..d0e11cebf7 --- /dev/null +++ b/test/files/run/t3326.check @@ -0,0 +1,8 @@ +Map(2 -> Hello, 1 -> World) +Map(5 -> Foo, 4 -> Bar) +Map(5 -> Foo, 4 -> Bar, 2 -> Hello, 1 -> World) +Map(3 -> ?, 2 -> Hello, 1 -> World) +Map(2 -> Hello, 1 -> World) +Map(5 -> Foo, 4 -> Bar) +Map(5 -> Foo, 4 -> Bar, 2 -> Hello, 1 -> World) +Map(3 -> ?, 2 -> Hello, 1 -> World) \ No newline at end of file diff --git a/test/files/run/t3326.scala b/test/files/run/t3326.scala new file mode 100644 index 0000000000..f70cb01504 --- /dev/null +++ b/test/files/run/t3326.scala @@ -0,0 +1,74 @@ + + + +import scala.math.Ordering + + + +/** The heart of the problem - we want to retain the ordering when + * using `++` on sorted maps. + * + * There are 2 `++` overloads - a generic one in traversables and + * a map-specific one in `MapLike` - which knows about the ordering. + * + * The problem here is that the expected return type for the expression + * in which `++` appears drives the decision of the overload that needs + * to be taken. + * The `collection.SortedMap` does not have `++` overridden to return + * `SortedMap`, but `immutable.Map` instead. + * This is why `collection.SortedMap` used to resort to the generic + * `TraversableLike.++` which knows nothing about the ordering. + * + * To avoid `collection.SortedMap`s resort to the more generic `TraverableLike.++`, + * we override the `MapLike.++` overload in `collection.SortedMap` to return + * the proper type `SortedMap`. + */ +object Test { + + def main(args: Array[String]) { + testCollectionSorted() + testImmutableSorted() + } + + def testCollectionSorted() { + import collection._ + val order = implicitly[Ordering[Int]].reverse + var m1: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + var m2: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + + m1 += (1 -> "World") + m1 += (2 -> "Hello") + + m2 += (4 -> "Bar") + m2 += (5 -> "Foo") + + val m3: SortedMap[Int, String] = m1 ++ m2 + + println(m1) + println(m2) + println(m3) + + println(m1 + (3 -> "?")) + } + + def testImmutableSorted() { + import collection.immutable._ + val order = implicitly[Ordering[Int]].reverse + var m1: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + var m2: SortedMap[Int, String] = SortedMap.empty[Int, String](order) + + m1 += (1 -> "World") + m1 += (2 -> "Hello") + + m2 += (4 -> "Bar") + m2 += (5 -> "Foo") + + val m3: SortedMap[Int, String] = m1 ++ m2 + + println(m1) + println(m2) + println(m3) + + println(m1 + (3 -> "?")) + } +} -- cgit v1.2.3 From c4e059edcf3492a99fc6de5136ae59a7dcd878dc Mon Sep 17 00:00:00 2001 From: Aleksandar Prokopec Date: Wed, 27 Jun 2012 16:54:56 +0200 Subject: Fix SI-5336. --- src/library/scala/collection/parallel/immutable/ParIterable.scala | 1 + src/library/scala/collection/parallel/mutable/ParIterable.scala | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/library/scala/collection/parallel/immutable/ParIterable.scala b/src/library/scala/collection/parallel/immutable/ParIterable.scala index d8c42d74b0..349f4fa44c 100644 --- a/src/library/scala/collection/parallel/immutable/ParIterable.scala +++ b/src/library/scala/collection/parallel/immutable/ParIterable.scala @@ -34,6 +34,7 @@ extends collection/*.immutable*/.GenIterable[T] with collection.parallel.ParIterable[T] with GenericParTemplate[T, ParIterable] with ParIterableLike[T, ParIterable[T], collection.immutable.Iterable[T]] + with Immutable { override def companion: GenericCompanion[ParIterable] with GenericParCompanion[ParIterable] = ParIterable diff --git a/src/library/scala/collection/parallel/mutable/ParIterable.scala b/src/library/scala/collection/parallel/mutable/ParIterable.scala index 700d21d0bb..b5747a31cf 100644 --- a/src/library/scala/collection/parallel/mutable/ParIterable.scala +++ b/src/library/scala/collection/parallel/mutable/ParIterable.scala @@ -29,7 +29,8 @@ import scala.collection.GenIterable trait ParIterable[T] extends collection/*.mutable*/.GenIterable[T] with collection.parallel.ParIterable[T] with GenericParTemplate[T, ParIterable] - with ParIterableLike[T, ParIterable[T], Iterable[T]] { + with ParIterableLike[T, ParIterable[T], Iterable[T]] + with Mutable { override def companion: GenericCompanion[ParIterable] with GenericParCompanion[ParIterable] = ParIterable //protected[this] override def newBuilder = ParIterable.newBuilder[T] -- cgit v1.2.3