summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-03-14 10:00:30 -0700
committerPaul Phillips <paulp@improving.org>2012-03-14 10:08:36 -0700
commit3ab383ae01a66208df4955bf2117dd2ea8eb2afe (patch)
treeac83e7e89310955ccbc317f57f34165f9781117c /src/compiler/scala/tools/nsc
parentb6dde2b6cf5f66e96fc92c09c1fbe3b4a8dc348c (diff)
parent5dca64cefeed4bc3289e641949b103e5e806aa32 (diff)
downloadscala-3ab383ae01a66208df4955bf2117dd2ea8eb2afe.tar.gz
scala-3ab383ae01a66208df4955bf2117dd2ea8eb2afe.tar.bz2
scala-3ab383ae01a66208df4955bf2117dd2ea8eb2afe.zip
Merge branch 'master' into merge-inline
Conflicts: lib/scala-compiler.jar.desired.sha1 lib/scala-library-src.jar.desired.sha1 lib/scala-library.jar.desired.sha1 src/compiler/scala/reflect/internal/Definitions.scala src/compiler/scala/reflect/internal/Importers.scala src/compiler/scala/reflect/internal/Symbols.scala src/compiler/scala/reflect/internal/Trees.scala src/compiler/scala/reflect/internal/Types.scala src/compiler/scala/tools/nsc/Global.scala src/compiler/scala/tools/nsc/transform/Erasure.scala src/compiler/scala/tools/nsc/transform/LiftCode.scala src/compiler/scala/tools/nsc/transform/UnCurry.scala src/compiler/scala/tools/nsc/typechecker/RefChecks.scala src/compiler/scala/tools/nsc/typechecker/Typers.scala test/files/run/programmatic-main.check test/files/speclib/instrumented.jar.desired.sha1
Diffstat (limited to 'src/compiler/scala/tools/nsc')
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala234
-rw-r--r--src/compiler/scala/tools/nsc/MacroContext.scala4
-rw-r--r--src/compiler/scala/tools/nsc/SubComponent.scala3
-rw-r--r--src/compiler/scala/tools/nsc/ast/NodePrinters.scala55
-rw-r--r--src/compiler/scala/tools/nsc/ast/Reifiers.scala761
-rw-r--r--src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala75
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeDSL.scala8
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala115
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreePrinters.scala5
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala77
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala20
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala71
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Tokens.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala12
-rw-r--r--src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala21
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Members.scala20
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Primitives.scala53
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala50
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala19
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala8
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala639
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala14
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala1138
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala18
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/Inliners.scala329
-rw-r--r--src/compiler/scala/tools/nsc/dependencies/Changes.scala2
-rw-r--r--src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala21
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala4
-rw-r--r--src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt27
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala6
-rw-r--r--src/compiler/scala/tools/nsc/interactive/CompilerControl.scala23
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Global.scala10
-rw-r--r--src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala15
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala4
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ILoop.scala8
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala20
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Imports.scala4
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala8
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala11
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/Power.scala41
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala5
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/ReplVals.scala12
-rw-r--r--src/compiler/scala/tools/nsc/io/Pickler.scala2
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaParsers.scala10
-rw-r--r--src/compiler/scala/tools/nsc/matching/MatchSupport.scala4
-rw-r--r--src/compiler/scala/tools/nsc/matching/Matrix.scala9
-rw-r--r--src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala16
-rw-r--r--src/compiler/scala/tools/nsc/matching/ParallelMatching.scala2
-rw-r--r--src/compiler/scala/tools/nsc/matching/PatternBindings.scala5
-rw-r--r--src/compiler/scala/tools/nsc/matching/Patterns.scala59
-rw-r--r--src/compiler/scala/tools/nsc/reporters/Reporter.scala4
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala5
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala3
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolTable.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala32
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/AddInterfaces.scala23
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala31
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala43
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala98
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala26
-rw-r--r--src/compiler/scala/tools/nsc/transform/Flatten.scala40
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala76
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala45
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala97
-rw-r--r--src/compiler/scala/tools/nsc/transform/OverridingPairs.scala10
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala408
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala40
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala447
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala85
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala16
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala63
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala16
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala146
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala169
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala57
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala17
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala22
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala337
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala90
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala167
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala12
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala287
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Unapplies.scala2
-rwxr-xr-xsrc/compiler/scala/tools/nsc/util/DocStrings.scala24
-rw-r--r--src/compiler/scala/tools/nsc/util/ProxyReport.scala2
-rw-r--r--src/compiler/scala/tools/nsc/util/Statistics.scala2
-rw-r--r--src/compiler/scala/tools/nsc/util/WeakHashSet.scala60
98 files changed, 4458 insertions, 2653 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index 940d115b2f..d6f57801e7 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -74,7 +74,7 @@ trait CompilationUnits { self: Global =>
* It is empty up to phase 'icode'.
*/
val icode: LinkedHashSet[icodes.IClass] = new LinkedHashSet
-
+
def echo(pos: Position, msg: String) =
reporter.echo(pos, msg)
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 8e5ca2156a..248d5d675d 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -37,6 +37,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
with Plugins
with PhaseAssembly
with Trees
+ with Reifiers
with TreePrinters
with DocComments
with MacroContext
@@ -58,7 +59,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
type AbstractFileType = scala.tools.nsc.io.AbstractFile
def mkAttributedQualifier(tpe: Type, termSym: Symbol): Tree = gen.mkAttributedQualifier(tpe, termSym)
-
+
def picklerPhase: Phase = if (currentRun.isDefined) currentRun.picklerPhase else NoPhase
// platform specific elements
@@ -124,7 +125,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
/** Print tree in detailed form */
object nodePrinters extends {
val global: Global.this.type = Global.this
- } with NodePrinters {
+ } with NodePrinters with ReifyPrinters {
infolevel = InfoLevel.Verbose
}
@@ -134,6 +135,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
} with TreeBrowsers
val nodeToString = nodePrinters.nodeToString
+ val reifiedNodeToString = nodePrinters.reifiedNodeToString
val treeBrowser = treeBrowsers.create()
// ------------ Hooks for interactive mode-------------------------
@@ -152,7 +154,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
/** Register top level class (called on entering the class)
*/
def registerTopLevelSym(sym: Symbol) {}
-
+
// ------------------ Reporting -------------------------------------
// not deprecated yet, but a method called "error" imported into
@@ -191,10 +193,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
if (settings.debug.value)
body
}
- @inline final override def debuglog(msg: => String) {
- if (settings.debug.value && (settings.log containsPhase globalPhase))
- inform("[log " + phase + "] " + msg)
- }
// Warnings issued only under -Ydebug. For messages which should reach
// developer ears, but are not adequately actionable by users.
@inline final override def debugwarn(msg: => String) {
@@ -211,10 +209,29 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start))
def logError(msg: String, t: Throwable): Unit = ()
+
+ def logAfterEveryPhase[T](msg: String)(op: => T) {
+ log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op))
+ }
+
+ def shouldLogAtThisPhase = (
+ (settings.log.isSetByUser)
+ && ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase))
+ )
+ def atPhaseStackMessage = atPhaseStack match {
+ case Nil => ""
+ case ps => ps.reverseMap("->" + _).mkString("(", " ", ")")
+ }
// Over 200 closure objects are eliminated by inlining this.
- @inline final def log(msg: => AnyRef): Unit =
- if (settings.log containsPhase globalPhase)
- inform("[log " + phase + "] " + msg)
+ @inline final def log(msg: => AnyRef) {
+ if (shouldLogAtThisPhase)
+ inform("[log %s%s] %s".format(globalPhase, atPhaseStackMessage, msg))
+ }
+
+ @inline final override def debuglog(msg: => String) {
+ if (settings.debug.value)
+ log(msg)
+ }
def logThrowable(t: Throwable): Unit = globalError(throwableAsString(t))
def throwableAsString(t: Throwable): String =
@@ -463,17 +480,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
val runsRightAfter = None
} with RefChecks
- // phaseName = "liftcode"
- object liftcode extends {
- val global: Global.this.type = Global.this
- val runsAfter = List("refchecks")
- val runsRightAfter = None
- } with LiftCode
-
// phaseName = "uncurry"
override object uncurry extends {
val global: Global.this.type = Global.this
- val runsAfter = List("refchecks", "liftcode")
+ val runsAfter = List[String]("refchecks")
val runsRightAfter = None
} with UnCurry
@@ -659,7 +669,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
extensionMethods -> "add extension methods for inline classes",
pickler -> "serialize symbol tables",
refChecks -> "reference/override checking, translate nested objects",
- liftcode -> "reify trees",
uncurry -> "uncurry, translate function values to anonymous classes",
tailCalls -> "replace tail calls by jumps",
specializeTypes -> "@specialized-driven class and method specialization",
@@ -709,18 +718,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
private lazy val unitTimings = mutable.HashMap[CompilationUnit, Long]() withDefaultValue 0L // tracking time spent per unit
private def unitTimingsFormatted(): String = {
def toMillis(nanos: Long) = "%.3f" format nanos / 1000000d
-
+
val formatter = new util.TableDef[(String, String)] {
>> ("ms" -> (_._1)) >+ " "
<< ("path" -> (_._2))
}
"" + (
- new formatter.Table(unitTimings.toList sortBy (-_._2) map {
+ new formatter.Table(unitTimings.toList sortBy (-_._2) map {
case (unit, nanos) => (toMillis(nanos), unit.source.path)
})
)
}
-
+
protected def addToPhasesSet(sub: SubComponent, descr: String) {
phasesSet += sub
phasesDescMap(sub) = descr
@@ -767,6 +776,51 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
line1 :: line2 :: descs mkString
}
+ /** Returns List of (phase, value) pairs, including only those
+ * where the value compares unequal to the previous phase's value.
+ */
+ def afterEachPhase[T](op: => T): List[(Phase, T)] = {
+ phaseDescriptors.map(_.ownPhase).filterNot(_ eq NoPhase).foldLeft(List[(Phase, T)]()) { (res, ph) =>
+ val value = afterPhase(ph)(op)
+ if (res.nonEmpty && res.head._2 == value) res
+ else ((ph, value)) :: res
+ } reverse
+ }
+
+ /** Returns List of ChangeAfterPhase objects, encapsulating those
+ * phase transitions where the result of the operation gave a different
+ * list than it had when run during the previous phase.
+ */
+ def changesAfterEachPhase[T](op: => List[T]): List[ChangeAfterPhase[T]] = {
+ val ops = ((NoPhase, Nil)) :: afterEachPhase(op)
+
+ ops sliding 2 map {
+ case (_, before) :: (ph, after) :: Nil =>
+ val lost = before filterNot (after contains _)
+ val gained = after filterNot (before contains _)
+ ChangeAfterPhase(ph, lost, gained)
+ case _ => ???
+ } toList
+ }
+ private def numberedPhase(ph: Phase) = "%2d/%s".format(ph.id, ph.name)
+
+ case class ChangeAfterPhase[+T](ph: Phase, lost: List[T], gained: List[T]) {
+ private def mkStr(what: String, xs: List[_]) = (
+ if (xs.isEmpty) ""
+ else xs.mkString(what + " after " + numberedPhase(ph) + " {\n ", "\n ", "\n}\n")
+ )
+ override def toString = mkStr("Lost", lost) + mkStr("Gained", gained)
+ }
+
+ def describeAfterEachPhase[T](op: => T): List[String] =
+ afterEachPhase(op) map { case (ph, t) => "[after %-15s] %s".format(numberedPhase(ph), t) }
+
+ def describeAfterEveryPhase[T](op: => T): String =
+ describeAfterEachPhase(op) map (" " + _ + "\n") mkString
+
+ def printAfterEachPhase[T](op: => T): Unit =
+ describeAfterEachPhase(op) foreach (m => println(" " + m))
+
// ----------- Runs ---------------------------------------
private var curRun: Run = null
@@ -821,6 +875,28 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def currentUnit: CompilationUnit = if (currentRun eq null) NoCompilationUnit else currentRun.currentUnit
def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile
+ // TODO - trim these to the absolute minimum.
+ @inline final def afterErasure[T](op: => T): T = afterPhase(currentRun.erasurePhase)(op)
+ @inline final def afterExplicitOuter[T](op: => T): T = afterPhase(currentRun.explicitouterPhase)(op)
+ @inline final def afterFlatten[T](op: => T): T = afterPhase(currentRun.flattenPhase)(op)
+ @inline final def afterIcode[T](op: => T): T = afterPhase(currentRun.icodePhase)(op)
+ @inline final def afterMixin[T](op: => T): T = afterPhase(currentRun.mixinPhase)(op)
+ @inline final def afterPickler[T](op: => T): T = afterPhase(currentRun.picklerPhase)(op)
+ @inline final def afterRefchecks[T](op: => T): T = afterPhase(currentRun.refchecksPhase)(op)
+ @inline final def afterSpecialize[T](op: => T): T = afterPhase(currentRun.specializePhase)(op)
+ @inline final def afterTyper[T](op: => T): T = afterPhase(currentRun.typerPhase)(op)
+ @inline final def afterUncurry[T](op: => T): T = afterPhase(currentRun.uncurryPhase)(op)
+ @inline final def beforeErasure[T](op: => T): T = beforePhase(currentRun.erasurePhase)(op)
+ @inline final def beforeExplicitOuter[T](op: => T): T = beforePhase(currentRun.explicitouterPhase)(op)
+ @inline final def beforeFlatten[T](op: => T): T = beforePhase(currentRun.flattenPhase)(op)
+ @inline final def beforeIcode[T](op: => T): T = beforePhase(currentRun.icodePhase)(op)
+ @inline final def beforeMixin[T](op: => T): T = beforePhase(currentRun.mixinPhase)(op)
+ @inline final def beforePickler[T](op: => T): T = beforePhase(currentRun.picklerPhase)(op)
+ @inline final def beforeRefchecks[T](op: => T): T = beforePhase(currentRun.refchecksPhase)(op)
+ @inline final def beforeSpecialize[T](op: => T): T = beforePhase(currentRun.specializePhase)(op)
+ @inline final def beforeTyper[T](op: => T): T = beforePhase(currentRun.typerPhase)(op)
+ @inline final def beforeUncurry[T](op: => T): T = beforePhase(currentRun.uncurryPhase)(op)
+
/** Don't want to introduce new errors trying to report errors,
* so swallow exceptions.
*/
@@ -867,7 +943,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
/** Counts for certain classes of warnings during this run. */
var deprecationWarnings: List[(Position, String)] = Nil
var uncheckedWarnings: List[(Position, String)] = Nil
-
+
/** A flag whether macro expansions failed */
var macroExpansionFailed = false
@@ -928,16 +1004,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
// Each subcomponent supplies a phase, which are chained together.
// If -Ystop:phase is given, neither that phase nor any beyond it is added.
// If -Yskip:phase is given, that phase will be skipped.
- val lastPhase = phaseDescriptors.tail .
- takeWhile (pd => !stopPhase(pd.phaseName)) .
- filterNot (pd => skipPhase(pd.phaseName)) .
- foldLeft (parserPhase) ((chain, ph) => ph newPhase chain)
-
- // Ensure there is a terminal phase at the end, since -Ystop may have limited the phases.
- terminalPhase =
- if (lastPhase.name == "terminal") lastPhase
- else terminal newPhase lastPhase
-
+ val phaseLinks = {
+ val phs = (
+ phaseDescriptors.tail
+ takeWhile (pd => !stopPhase(pd.phaseName))
+ filterNot (pd => skipPhase(pd.phaseName))
+ )
+ // Ensure there is a terminal phase at the end, since -Ystop may have limited the phases.
+ if (phs.isEmpty || (phs.last ne terminal)) phs :+ terminal
+ else phs
+ }
+ // Link them together.
+ phaseLinks.foldLeft(parserPhase)((chain, ph) => ph newPhase chain)
parserPhase
}
@@ -1014,38 +1092,46 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
}
def cancel() { reporter.cancelled = true }
-
+
private def currentProgress = (phasec * size) + unitc
private def totalProgress = (phaseDescriptors.size - 1) * size // -1: drops terminal phase
private def refreshProgress() = if (size > 0) progress(currentProgress, totalProgress)
// ----- finding phases --------------------------------------------
- def phaseNamed(name: String): Phase = {
- var p: Phase = firstPhase
- while (p.next != p && p.name != name) p = p.next
- if (p.name != name) NoPhase else p
- }
-
- val parserPhase = phaseNamed("parser")
- val namerPhase = phaseNamed("namer")
- // packageobjects
- val typerPhase = phaseNamed("typer")
- val inlineclassesPhase = phaseNamed("inlineclasses")
- // superaccessors
- val picklerPhase = phaseNamed("pickler")
- val refchecksPhase = phaseNamed("refchecks")
- val uncurryPhase = phaseNamed("uncurry")
- // tailcalls, specialize
- val explicitouterPhase = phaseNamed("explicitouter")
- val erasurePhase = phaseNamed("erasure")
- // lazyvals, lambdalift, constructors
- val flattenPhase = phaseNamed("flatten")
- val mixinPhase = phaseNamed("mixin")
- val cleanupPhase = phaseNamed("cleanup")
- val icodePhase = phaseNamed("icode")
- // inliner, closelim, dce
- val jvmPhase = phaseNamed("jvm")
+ def phaseNamed(name: String): Phase =
+ findOrElse(firstPhase.iterator)(_.name == name)(NoPhase)
+
+ /** All phases as of 3/2012 here for handiness; the ones in
+ * active use uncommented.
+ */
+ val parserPhase = phaseNamed("parser")
+ val namerPhase = phaseNamed("namer")
+ // val packageobjectsPhase = phaseNamed("packageobjects")
+ val typerPhase = phaseNamed("typer")
+ val inlineclassesPhase = phaseNamed("inlineclasses")
+ // val superaccessorsPhase = phaseNamed("superaccessors")
+ val picklerPhase = phaseNamed("pickler")
+ val refchecksPhase = phaseNamed("refchecks")
+ // val selectiveanfPhase = phaseNamed("selectiveanf")
+ // val selectivecpsPhase = phaseNamed("selectivecps")
+ val uncurryPhase = phaseNamed("uncurry")
+ // val tailcallsPhase = phaseNamed("tailcalls")
+ val specializePhase = phaseNamed("specialize")
+ val explicitouterPhase = phaseNamed("explicitouter")
+ val erasurePhase = phaseNamed("erasure")
+ // val lazyvalsPhase = phaseNamed("lazyvals")
+ val lambdaliftPhase = phaseNamed("lambdalift")
+ // val constructorsPhase = phaseNamed("constructors")
+ val flattenPhase = phaseNamed("flatten")
+ val mixinPhase = phaseNamed("mixin")
+ val cleanupPhase = phaseNamed("cleanup")
+ val icodePhase = phaseNamed("icode")
+ // val inlinerPhase = phaseNamed("inliner")
+ // val inlineExceptionHandlersPhase = phaseNamed("inlineExceptionHandlers")
+ // val closelimPhase = phaseNamed("closelim")
+ // val dcePhase = phaseNamed("dce")
+ val jvmPhase = phaseNamed("jvm")
def runIsAt(ph: Phase) = globalPhase.id == ph.id
def runIsPast(ph: Phase) = globalPhase.id > ph.id
@@ -1090,7 +1176,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def compiles(sym: Symbol): Boolean =
if (sym == NoSymbol) false
else if (symSource.isDefinedAt(sym)) true
- else if (!sym.owner.isPackageClass) compiles(sym.toplevelClass)
+ else if (!sym.owner.isPackageClass) compiles(sym.enclosingTopLevelClass)
else if (sym.isModuleClass) compiles(sym.sourceModule)
else false
@@ -1128,7 +1214,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
lazy val trackers = currentRun.units.toList map (x => SymbolTracker(x))
def snapshot() = {
inform("\n[[symbol layout at end of " + phase + "]]")
- atPhase(phase.next) {
+ afterPhase(phase) {
trackers foreach { t =>
t.snapshot()
inform(t.show("Heading from " + phase.prev.name + " to " + phase.name))
@@ -1181,12 +1267,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
*/
def compileUnits(units: List[CompilationUnit], fromPhase: Phase) {
try compileUnitsInternal(units, fromPhase)
- catch { case ex =>
+ catch { case ex =>
globalError(supplementErrorMessage("uncaught exception during compilation: " + ex.getClass.getName))
throw ex
}
}
-
+
private def compileUnitsInternal(units: List[CompilationUnit], fromPhase: Phase) {
units foreach addUnit
if (opt.profileAll) {
@@ -1199,7 +1285,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
checkDeprecatedSettings(unitbuf.head)
globalPhase = fromPhase
- while (globalPhase != terminalPhase && !reporter.hasErrors) {
+ while (globalPhase.hasNext && !reporter.hasErrors) {
val startTime = currentTime
phase = globalPhase
@@ -1320,19 +1406,13 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
/** Compile abstract file until `globalPhase`, but at least to phase "namer".
*/
def compileLate(unit: CompilationUnit) {
- def stop(ph: Phase) = ph == null || ph.id >= (globalPhase.id max typerPhase.id)
- def loop(ph: Phase) {
- if (stop(ph)) refreshProgress
- else {
- atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit)
- loop(ph.next match {
- case `ph` => null // ph == ph.next implies terminal, and null ends processing
- case x => x
- })
- }
- }
+ val maxId = math.max(globalPhase.id, typerPhase.id)
addUnit(unit)
- loop(firstPhase)
+
+ firstPhase.iterator takeWhile (_.id < maxId) foreach (ph =>
+ atPhase(ph)(ph.asInstanceOf[GlobalPhase] applyPhase unit)
+ )
+ refreshProgress
}
/**
@@ -1402,7 +1482,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
def printAllUnits() {
print("[[syntax trees at end of " + phase + "]]")
- atPhase(phase.next) { currentRun.units foreach (treePrinter.print(_)) }
+ afterPhase(phase) { currentRun.units foreach (treePrinter.print(_)) }
}
private def findMemberFromRoot(fullName: Name): Symbol = {
diff --git a/src/compiler/scala/tools/nsc/MacroContext.scala b/src/compiler/scala/tools/nsc/MacroContext.scala
index 72662291f8..9ea1f87125 100644
--- a/src/compiler/scala/tools/nsc/MacroContext.scala
+++ b/src/compiler/scala/tools/nsc/MacroContext.scala
@@ -3,8 +3,8 @@ package scala.tools.nsc
import symtab.Flags._
trait MacroContext extends reflect.macro.Context { self: Global =>
-
+
def captureVariable(vble: Symbol): Unit = vble setFlag CAPTURED
-
+
def referenceCapturedVariable(id: Ident): Tree = ReferenceToBoxed(id)
}
diff --git a/src/compiler/scala/tools/nsc/SubComponent.scala b/src/compiler/scala/tools/nsc/SubComponent.scala
index cd9fef117f..a3e451f32f 100644
--- a/src/compiler/scala/tools/nsc/SubComponent.scala
+++ b/src/compiler/scala/tools/nsc/SubComponent.scala
@@ -47,6 +47,9 @@ abstract class SubComponent {
private var ownPhaseCache: WeakReference[Phase] = new WeakReference(null)
private var ownPhaseRunId = global.NoRunId
+ @inline final def beforeOwnPhase[T](op: => T) = global.beforePhase(ownPhase)(op)
+ @inline final def afterOwnPhase[T](op: => T) = global.afterPhase(ownPhase)(op)
+
/** The phase corresponding to this subcomponent in the current compiler run */
def ownPhase: Phase = {
ownPhaseCache.get match {
diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala
index ea51fc0141..9466d1c1f2 100644
--- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala
@@ -71,34 +71,39 @@ abstract class NodePrinters {
def nodeinfo(tree: Tree): String =
if (infolevel == InfoLevel.Quiet) ""
else {
- val buf = new StringBuilder(" // sym=" + tree.symbol)
- if (tree.hasSymbol) {
- if (tree.symbol.isPrimaryConstructor)
- buf.append(", isPrimaryConstructor")
- else if (tree.symbol.isConstructor)
- buf.append(", isConstructor")
- if (tree.symbol != NoSymbol)
- buf.append(", sym.owner=" + tree.symbol.owner)
- buf.append(", sym.tpe=" + tree.symbol.tpe)
- }
- buf.append(", tpe=" + tree.tpe)
- if (tree.tpe != null) {
- var sym = tree.tpe.termSymbol
- if (sym == NoSymbol) sym = tree.tpe.typeSymbol
- buf.append(", tpe.sym=" + sym)
- if (sym != NoSymbol) {
- buf.append(", tpe.sym.owner=" + sym.owner)
- if ((infolevel > InfoLevel.Normal) &&
- !(sym.owner eq definitions.ScalaPackageClass) &&
- !sym.isModuleClass && !sym.isPackageClass &&
- !sym.isJavaDefined) {
- val members = for (m <- tree.tpe.decls)
- yield m.toString() + ": " + m.tpe + ", "
- buf.append(", tpe.decls=" + members)
+ try {
+ val buf = new StringBuilder(" // sym=" + tree.symbol)
+ if (tree.hasSymbol) {
+ if (tree.symbol.isPrimaryConstructor)
+ buf.append(", isPrimaryConstructor")
+ else if (tree.symbol.isConstructor)
+ buf.append(", isConstructor")
+ if (tree.symbol != NoSymbol)
+ buf.append(", sym.owner=" + tree.symbol.owner)
+ buf.append(", sym.tpe=" + tree.symbol.tpe)
+ }
+ buf.append(", tpe=" + tree.tpe)
+ if (tree.tpe != null) {
+ var sym = tree.tpe.termSymbol
+ if (sym == NoSymbol) sym = tree.tpe.typeSymbol
+ buf.append(", tpe.sym=" + sym)
+ if (sym != NoSymbol) {
+ buf.append(", tpe.sym.owner=" + sym.owner)
+ if ((infolevel > InfoLevel.Normal) &&
+ !(sym.owner eq definitions.ScalaPackageClass) &&
+ !sym.isModuleClass && !sym.isPackageClass &&
+ !sym.isJavaDefined) {
+ val members = for (m <- tree.tpe.decls)
+ yield m.toString() + ": " + m.tpe + ", "
+ buf.append(", tpe.decls=" + members)
+ }
}
}
+ buf.toString
+ } catch {
+ case ex: Throwable =>
+ return " // sym= <error> " + ex.getMessage
}
- buf.toString
}
def nodeinfo2(tree: Tree): String =
(if (comma) "," else "") + nodeinfo(tree)
diff --git a/src/compiler/scala/tools/nsc/ast/Reifiers.scala b/src/compiler/scala/tools/nsc/ast/Reifiers.scala
new file mode 100644
index 0000000000..7ece8bbd31
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ast/Reifiers.scala
@@ -0,0 +1,761 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Gilles Dubochet
+ */
+
+package scala.tools.nsc
+package ast
+
+import symtab._
+import Flags._
+import scala.reflect.api.Modifier._
+import scala.collection.{ mutable, immutable }
+import scala.collection.mutable.ListBuffer
+import scala.tools.nsc.util.FreshNameCreator
+import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple }
+
+/** Given a tree or type, generate a tree that when executed at runtime produces the original tree or type.
+ * See more info in the comments to `reify' in scala.reflect.macro.Context.
+ *
+ * @author Martin Odersky
+ * @version 2.10
+ */
+trait Reifiers { self: Global =>
+
+ def reify(tree: Tree): Tree = {
+ class Reifier {
+ import definitions._
+ import Reifier._
+
+ final val scalaPrefix = "scala."
+ final val localPrefix = "$local"
+ final val memoizerName = "$memo"
+
+ val reifyDebug = settings.Yreifydebug.value
+
+ private val reifiableSyms = mutable.ArrayBuffer[Symbol]() // the symbols that are reified with the tree
+ private val symIndex = mutable.HashMap[Symbol, Int]() // the index of a reifiable symbol in `reifiableSyms`
+ private var boundSyms = Set[Symbol]() // set of all symbols that are bound in tree to be reified
+
+ private def definedInLiftedCode(tpe: Type) =
+ tpe exists (tp => boundSyms contains tp.typeSymbol)
+
+ private def definedInLiftedCode(sym: Symbol) =
+ boundSyms contains sym
+
+ /**
+ * Generate tree of the form
+ *
+ * { val $mr = scala.reflect.runtime.Mirror
+ * $local1 = new TypeSymbol(owner1, NoPosition, name1)
+ * ...
+ * $localN = new TermSymbol(ownerN, NoPositiion, nameN)
+ * $local1.setInfo(tpe1)
+ * ...
+ * $localN.setInfo(tpeN)
+ * $localN.setAnnotations(annotsN)
+ * rtree
+ * }
+ *
+ * where
+ *
+ * - `$localI` are free type symbols in the environment, as well as local symbols
+ * of refinement types.
+ * - `tpeI` are the info's of `symI`
+ * - `rtree` is code that generates `data` at runtime, maintaining all attributes.
+ * - `data` is typically a tree or a type.
+ */
+ def reifyTopLevel(data: Any): Tree = {
+ val rtree = reify(data)
+ Block(mirrorAlias :: reifySymbolTableSetup, rtree)
+ }
+
+ private def isLocatable(sym: Symbol) =
+ sym.isPackageClass || sym.owner.isClass || sym.isTypeParameter && sym.paramPos >= 0
+
+ private def registerReifiableSymbol(sym: Symbol): Unit =
+ if (!(symIndex contains sym)) {
+ sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol
+ symIndex(sym) = reifiableSyms.length
+ reifiableSyms += sym
+ }
+
+ // helper methods
+
+ private def localName(sym: Symbol): TermName =
+ newTermName(localPrefix + symIndex(sym))
+
+ private def call(fname: String, args: Tree*): Tree =
+ Apply(termPath(fname), args.toList)
+
+ private def mirrorSelect(name: String): Tree =
+ termPath(nme.MIRROR_PREFIX + name)
+
+ private def mirrorCall(name: TermName, args: Tree*): Tree =
+ call("" + (nme.MIRROR_PREFIX append name), args: _*)
+
+ private def mirrorCall(name: String, args: Tree*): Tree =
+ call(nme.MIRROR_PREFIX + name, args: _*)
+
+ private def mirrorFactoryCall(value: Product, args: Tree*): Tree =
+ mirrorFactoryCall(value.productPrefix, args: _*)
+
+ private def mirrorFactoryCall(prefix: String, args: Tree*): Tree =
+ mirrorCall(prefix, args: _*)
+
+ private def scalaFactoryCall(name: String, args: Tree*): Tree =
+ call(scalaPrefix + name + ".apply", args: _*)
+
+ private def mkList(args: List[Tree]): Tree =
+ scalaFactoryCall("collection.immutable.List", args: _*)
+
+ private def reifyModifiers(m: Modifiers) =
+ mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations))
+
+ private def reifyAggregate(name: String, args: Any*) =
+ scalaFactoryCall(name, (args map reify).toList: _*)
+
+ /**
+ * Reify a list
+ */
+ private def reifyList(xs: List[Any]): Tree =
+ mkList(xs map reify)
+
+ /**
+ * Reify an array
+ */
+ private def reifyArray(xs: Array[_]): Tree =
+ // @xeno.by: doesn't work for Array(LiteralAnnotArg(...))
+ // because we cannot generate manifests for path-dependent types
+ scalaFactoryCall(nme.Array, xs map reify: _*)
+
+ /** Reify a name */
+ private def reifyName(name: Name) =
+ mirrorCall(if (name.isTypeName) "newTypeName" else "newTermName", Literal(Constant(name.toString)))
+
+ private def isFree(sym: Symbol) =
+ !(symIndex contains sym)
+
+ /**
+ * Reify a reference to a symbol
+ */
+ private def reifySymRef(sym: Symbol): Tree = {
+ symIndex get sym match {
+ case Some(idx) =>
+ Ident(localName(sym))
+ case None =>
+ if (sym == NoSymbol)
+ mirrorSelect("NoSymbol")
+ else if (sym == RootPackage)
+ mirrorSelect("definitions.RootPackage")
+ else if (sym == RootClass)
+ mirrorSelect("definitions.RootClass")
+ else if (sym == EmptyPackage)
+ mirrorSelect("definitions.EmptyPackage")
+ else if (sym.isModuleClass)
+ Select(reifySymRef(sym.sourceModule), "moduleClass")
+ else if (sym.isStatic && sym.isClass)
+ mirrorCall("staticClass", reify(sym.fullName))
+ else if (sym.isStatic && sym.isModule)
+ mirrorCall("staticModule", reify(sym.fullName))
+ else if (isLocatable(sym))
+ if (sym.isTypeParameter)
+ mirrorCall("selectParam", reify(sym.owner), reify(sym.paramPos))
+ else {
+ if (reifyDebug) println("locatable: " + sym + " " + sym.isPackageClass + " " + sym.owner + " " + sym.isTypeParameter)
+ val rowner = reify(sym.owner)
+ val rname = reify(sym.name.toString)
+ if (sym.isType)
+ mirrorCall("selectType", rowner, rname)
+ else if (sym.isMethod && sym.owner.isClass && sym.owner.info.decl(sym.name).isOverloaded) {
+ val index = sym.owner.info.decl(sym.name).alternatives indexOf sym
+ assert(index >= 0, sym)
+ mirrorCall("selectOverloadedMethod", rowner, rname, reify(index))
+ } else
+ mirrorCall("selectTerm", rowner, rname)
+ }
+ else {
+ if (sym.isTerm) {
+ if (reifyDebug) println("Free: " + sym)
+ val symtpe = lambdaLift.boxIfCaptured(sym, sym.tpe, erasedTypes = false)
+ def markIfCaptured(arg: Ident): Tree =
+ if (sym.isCapturedVariable) referenceCapturedVariable(arg) else arg
+ mirrorCall("newFreeVar", reify(sym.name.toString), reify(symtpe), markIfCaptured(Ident(sym)))
+ } else {
+ if (reifyDebug) println("Late local: " + sym)
+ registerReifiableSymbol(sym)
+ reifySymRef(sym)
+ }
+ }
+ }
+ }
+
+ /**
+ * reify the creation of a symbol
+ */
+ private def reifySymbolDef(sym: Symbol): Tree = {
+ if (reifyDebug) println("reify sym def " + sym)
+
+ ValDef(NoMods, localName(sym), TypeTree(),
+ Apply(
+ Select(reify(sym.owner), "newNestedSymbol"),
+ List(reify(sym.name), reify(sym.pos), Literal(Constant(sym.flags)))
+ )
+ )
+ }
+
+ /**
+ * Generate code to add type and annotation info to a reified symbol
+ */
+ private def fillInSymbol(sym: Symbol): Tree = {
+ val rset = Apply(Select(reifySymRef(sym), nme.setTypeSignature), List(reifyType(sym.info)))
+ if (sym.annotations.isEmpty) rset
+ else Apply(Select(rset, nme.setAnnotations), List(reify(sym.annotations)))
+ }
+
+ /** Reify a scope */
+ private def reifyScope(scope: Scope): Tree = {
+ scope foreach registerReifiableSymbol
+ mirrorCall(nme.newScopeWith, scope.toList map reifySymRef: _*)
+ }
+
+ /** Reify a list of symbols that need to be created */
+ private def reifySymbols(syms: List[Symbol]): Tree = {
+ syms foreach registerReifiableSymbol
+ mkList(syms map reifySymRef)
+ }
+
+ /** Reify a type that defines some symbols */
+ private def reifyTypeBinder(value: Product, bound: List[Symbol], underlying: Type): Tree =
+ mirrorFactoryCall(value, reifySymbols(bound), reify(underlying))
+
+ /** Reify a type */
+ private def reifyType(tpe0: Type): Tree = {
+ val tpe = tpe0.normalize
+
+ if (tpe.isErroneous)
+ CannotReifyErroneousType(tpe)
+ if (definedInLiftedCode(tpe))
+ CannotReifyTypeInvolvingBoundType(tpe)
+
+ val tsym = tpe.typeSymbol
+ if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic)
+ Select(reifySymRef(tpe.typeSymbol), nme.asTypeConstructor)
+ else tpe match {
+ case t @ NoType =>
+ reifyMirrorObject(t)
+ case t @ NoPrefix =>
+ reifyMirrorObject(t)
+ case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic =>
+ mirrorCall(nme.thisModuleType, reify(clazz.fullName))
+ case t @ RefinedType(parents, decls) =>
+ registerReifiableSymbol(tpe.typeSymbol)
+ mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
+ case t @ ClassInfoType(parents, decls, clazz) =>
+ registerReifiableSymbol(clazz)
+ mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol))
+ case t @ ExistentialType(tparams, underlying) =>
+ reifyTypeBinder(t, tparams, underlying)
+ case t @ PolyType(tparams, underlying) =>
+ reifyTypeBinder(t, tparams, underlying)
+ case t @ MethodType(params, restpe) =>
+ reifyTypeBinder(t, params, restpe)
+ case t @ AnnotatedType(anns, underlying, selfsym) =>
+ val saved1 = reifySymbols
+ val saved2 = reifyTypes
+
+ try {
+ // one more quirk of reifying annotations
+ //
+ // when reifying AnnotatedTypes we need to reify all the types and symbols of inner ASTs
+ // that's because a lot of logic expects post-typer trees to have non-null tpes
+ //
+ // Q: reified trees are pre-typer, so there's shouldn't be a problem.
+ // reflective typechecker will fill in missing symbols and types, right?
+ // A: actually, no. annotation ASTs live inside AnnotatedTypes,
+ // and insides of the types is the place where typechecker doesn't look.
+ reifySymbols = true
+ reifyTypes = true
+ if (reifyDebug) println("reify AnnotatedType: " + tpe)
+ reifyProductUnsafe(tpe)
+ } finally {
+ reifySymbols = saved1
+ reifyTypes = saved2
+ }
+ case _ =>
+ reifyProductUnsafe(tpe)
+ }
+ }
+
+ var reifySymbols = false
+ var reifyTypes = false
+
+ /** Preprocess a tree before reification */
+ private def trimTree(tree: Tree): Tree = {
+ def trimSyntheticCaseClassMembers(deff: Tree, stats: List[Tree]) = {
+ var stats1 = stats filterNot (stat => stat.isDef && {
+ if (stat.symbol.isCaseAccessorMethod && reifyDebug) println("discarding case accessor method: " + stat)
+ stat.symbol.isCaseAccessorMethod
+ })
+ stats1 = stats1 filterNot (memberDef => memberDef.isDef && {
+ val isSynthetic = memberDef.symbol.isSynthetic
+ // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
+ // that's why I replace the check with an assumption that all synthetic members are, in fact, generated of case classes
+// val isCaseMember = deff.symbol.isCaseClass || deff.symbol.companionClass.isCaseClass
+ val isCaseMember = true
+ if (isSynthetic && isCaseMember && reifyDebug) println("discarding case class synthetic def: " + memberDef)
+ isSynthetic && isCaseMember
+ })
+ stats1 = stats1 map {
+ case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isCaseAccessor =>
+ if (reifyDebug) println("resetting visibility of case accessor field: " + valdef)
+ val Modifiers(flags, privateWithin, annotations) = mods
+ val flags1 = flags & ~Flags.LOCAL & ~Flags.PRIVATE
+ val mods1 = Modifiers(flags1, privateWithin, annotations)
+ ValDef(mods1, name, tpt, rhs).copyAttrs(valdef)
+ case stat =>
+ stat
+ }
+ stats1
+ }
+
+ def trimSyntheticCaseClassCompanions(stats: List[Tree]) =
+ stats diff (stats collect { case moddef: ModuleDef => moddef } filter (moddef => {
+ val isSynthetic = moddef.symbol.isSynthetic
+ // @xeno.by: this doesn't work for local classes, e.g. for ones that are top-level to a quasiquote (see comments to companionClass)
+ // that's why I replace the check with an assumption that all synthetic modules are, in fact, companions of case classes
+// val isCaseCompanion = moddef.symbol.companionClass.isCaseClass
+ val isCaseCompanion = true
+ // @xeno.by: we also have to do this ugly hack for the very same reason described above
+ // normally this sort of stuff is performed in reifyTree, which binds related symbols, however, local companions will be out of its reach
+ if (reifyDebug) println("boundSym: "+ moddef.symbol)
+ boundSyms += moddef.symbol
+ if (isSynthetic && isCaseCompanion && reifyDebug) println("discarding synthetic case class companion: " + moddef)
+ isSynthetic && isCaseCompanion
+ }))
+
+ tree match {
+ case tree if tree.isErroneous =>
+ tree
+ case ta @ TypeApply(hk, ts) =>
+ def isErased(tt: TypeTree) = tt.tpe != null && definedInLiftedCode(tt.tpe) && tt.original == null
+ val discard = ts collect { case tt: TypeTree => tt } exists isErased
+ if (reifyDebug && discard) println("discarding TypeApply: " + tree)
+ if (discard) hk else ta
+ case classDef @ ClassDef(mods, name, params, impl) =>
+ val Template(parents, self, body) = impl
+ val body1 = trimSyntheticCaseClassMembers(classDef, body)
+ var impl1 = Template(parents, self, body1).copyAttrs(impl)
+ ClassDef(mods, name, params, impl1).copyAttrs(classDef)
+ case moduledef @ ModuleDef(mods, name, impl) =>
+ val Template(parents, self, body) = impl
+ val body1 = trimSyntheticCaseClassMembers(moduledef, body)
+ var impl1 = Template(parents, self, body1).copyAttrs(impl)
+ ModuleDef(mods, name, impl1).copyAttrs(moduledef)
+ case template @ Template(parents, self, body) =>
+ val body1 = trimSyntheticCaseClassCompanions(body)
+ Template(parents, self, body1).copyAttrs(template)
+ case block @ Block(stats, expr) =>
+ val stats1 = trimSyntheticCaseClassCompanions(stats)
+ Block(stats1, expr).copyAttrs(block)
+ case valdef @ ValDef(mods, name, tpt, rhs) if valdef.symbol.isLazy =>
+ if (reifyDebug) println("dropping $lzy in lazy val's name: " + tree)
+ val name1 = if (name endsWith nme.LAZY_LOCAL) name dropRight nme.LAZY_LOCAL.length else name
+ ValDef(mods, name1, tpt, rhs).copyAttrs(valdef)
+ case unapply @ UnApply(fun, args) =>
+ def extractExtractor(tree: Tree): Tree = {
+ val Apply(fun, args) = tree
+ args match {
+ case List(Ident(special)) if special == nme.SELECTOR_DUMMY =>
+ val Select(extractor, flavor) = fun
+ assert(flavor == nme.unapply || flavor == nme.unapplySeq)
+ extractor
+ case _ =>
+ extractExtractor(fun)
+ }
+ }
+
+ if (reifyDebug) println("unapplying unapply: " + tree)
+ val fun1 = extractExtractor(fun)
+ Apply(fun1, args).copyAttrs(unapply)
+ case _ =>
+ tree
+ }
+ }
+
+ /** Reify a tree */
+ private def reifyTree(tree0: Tree): Tree = {
+ val tree = trimTree(tree0)
+
+ var rtree = tree match {
+ case tree if tree.isErroneous =>
+ CannotReifyErroneousTree(tree)
+ case self.EmptyTree =>
+ reifyMirrorObject(EmptyTree)
+ case self.emptyValDef =>
+ mirrorSelect(nme.emptyValDef)
+ case This(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
+ reifyFree(tree)
+ case Ident(_) if tree.symbol != NoSymbol && !(boundSyms contains tree.symbol) =>
+ if (tree.symbol.isVariable && tree.symbol.owner.isTerm) {
+ if (reifyDebug) println("captured variable: " + tree.symbol)
+ captureVariable(tree.symbol) // Note order dependency: captureVariable needs to come before reifyTree here.
+ mirrorCall("Select", reifyFree(tree), reifyName(nme.elem))
+ } else reifyFree(tree)
+ case tt: TypeTree if (tt.tpe != null) =>
+ reifyTypeTree(tt)
+ case Literal(constant @ Constant(tpe: Type)) if boundSyms exists (tpe contains _) =>
+ CannotReifyClassOfBoundType(tree, tpe)
+ case Literal(constant @ Constant(sym: Symbol)) if boundSyms contains sym =>
+ CannotReifyClassOfBoundEnum(tree, constant.tpe)
+ case tree if tree.isDef =>
+ if (reifyDebug) println("boundSym: %s of type %s".format(tree.symbol, (tree.productIterator.toList collect { case tt: TypeTree => tt } headOption).getOrElse(TypeTree(tree.tpe))))
+ boundSyms += tree.symbol
+
+ bindRelatedSymbol(tree.symbol.sourceModule, "sourceModule")
+ bindRelatedSymbol(tree.symbol.moduleClass, "moduleClass")
+ bindRelatedSymbol(tree.symbol.companionClass, "companionClass")
+ bindRelatedSymbol(tree.symbol.companionModule, "companionModule")
+ Some(tree.symbol) collect { case termSymbol: TermSymbol => bindRelatedSymbol(termSymbol.referenced, "referenced") }
+ def bindRelatedSymbol(related: Symbol, name: String): Unit =
+ if (related != null && related != NoSymbol) {
+ if (reifyDebug) println("boundSym (" + name + "): " + related)
+ boundSyms += related
+ }
+
+ val prefix = tree.productPrefix
+ val elements = (tree.productIterator map {
+ // annotations exist in two flavors:
+ // 1) pre-typer ones that populate: a) Modifiers, b) Annotated nodes (irrelevant in this context)
+ // 2) post-typer ones that dwell inside: a) sym.annotations, b) AnnotatedTypes (irrelevant in this context)
+ //
+ // here we process Modifiers that are involved in deftrees
+ // AnnotatedTypes get reified elsewhere (currently, in ``reifyTypeTree'')
+ case Modifiers(flags, privateWithin, annotations) =>
+ assert(annotations.isEmpty) // should've been eliminated by the typer
+ val postTyper = tree.symbol.annotations filter (_.original != EmptyTree)
+ if (reifyDebug && !postTyper.isEmpty) println("reify symbol annotations for %s: %s".format(tree.symbol, tree.symbol.annotations))
+ val preTyper = postTyper map toPreTyperAnnotation
+ Modifiers(flags, privateWithin, preTyper)
+ case x =>
+ x
+ }).toList
+ reifyProduct(prefix, elements)
+ case _ =>
+ reifyProduct(tree)
+ }
+
+ // usually we don't reify symbols/types, because they can be re-inferred during subsequent reflective compilation
+ // however, reification of AnnotatedTypes is special. see ``reifyType'' to find out why.
+ if (reifySymbols && tree.hasSymbol) {
+ if (reifyDebug) println("reifying symbol %s for tree %s".format(tree.symbol, tree))
+ rtree = Apply(Select(rtree, nme.setSymbol), List(reifySymRef(tree.symbol)))
+ }
+ if (reifyTypes && tree.tpe != null) {
+ if (reifyDebug) println("reifying type %s for tree %s".format(tree.tpe, tree))
+ rtree = Apply(Select(rtree, nme.setType), List(reifyType(tree.tpe)))
+ }
+
+ rtree
+ }
+
+ /** Reify pre-typer representation of a type.
+ *
+ * NB: This is the trickiest part of reification!
+ *
+ * In most cases, we're perfectly fine to reify a Type itself (see ``reifyType'').
+ * However if the type involves a symbol declared inside the quasiquote (i.e. registered in ``boundSyms''),
+ * then we cannot reify it, or otherwise subsequent reflective compilation will fail.
+ *
+ * Why will it fail? Because reified deftrees (e.g. ClassDef(...)) will generate fresh symbols during that compilation,
+ * so naively reified symbols will become out of sync, which brings really funny compilation errors and/or crashes, e.g.:
+ * https://issues.scala-lang.org/browse/SI-5230
+ *
+ * To deal with this unpleasant fact, we need to fall back from types to equivalent trees (after all, parser trees don't contain any types, just trees, so it should be possible).
+ * Luckily, these original trees get preserved for us in the ``original'' field when Trees get transformed into TypeTrees.
+ * And if an original of a type tree is empty, we can safely assume that this type is non-essential (e.g. was inferred/generated by the compiler).
+ * In that case the type can be omitted (e.g. reified as an empty TypeTree), since it will be inferred again later on.
+ *
+ * An important property of the original is that it isn't just a pre-typer tree.
+ * It's actually kind of a post-typer tree with symbols assigned to its Idents (e.g. Ident("List") will contain a symbol that points to immutable.this.List).
+ * This is very important, since subsequent reflective compilation won't have to resolve these symbols.
+ * In general case, such resolution cannot be performed, since reification doesn't preserve lexical context,
+ * which means that reflective compilation won't be aware of, say, imports that were provided when the reifee has been compiled.
+ *
+ * This workaround worked surprisingly well and allowed me to fix several important reification bugs, until the abstraction has leaked.
+ * Suddenly I found out that in certain contexts original trees do not contain symbols, but are just parser trees.
+ * To the moment I know only one such situation: typedAnnotations does not typecheck the annotation in-place, but rather creates new trees and typechecks them, so the original remains symless.
+ * This is laboriously worked around in the code below. I hope this will be the only workaround in this department.
+ */
+ private def reifyTypeTree(tt: TypeTree): Tree = {
+ if (definedInLiftedCode(tt.tpe)) {
+ if (reifyDebug) println("reifyTypeTree, defined in lifted code: " + tt.tpe)
+ if (tt.original != null) {
+ val annotations = tt.tpe filter { _.isInstanceOf[AnnotatedType] } collect { case atp: AnnotatedType => atp.annotations } flatten
+ val annmap = annotations map { ann => (ann.original, ann) } toMap
+
+ // annotations exist in two flavors:
+ // 1) pre-typer ones that populate: a) Modifiers (irrelevant in this context), b) Annotated nodes
+ // 2) post-typer ones that dwell inside: a) sym.annotations (irrelevant in this context), b) AnnotatedTypes
+ //
+ // here we process AnnotatedTypes, since only they can be involved in TypeTrees
+ // Modifiers get reified elsewhere (currently, in the "isDef" case of ``reifyTree'')
+ //
+ // the problem with annotations is that their originals don't preserve any symbols at all
+ // read the comment to this method to find out why it's bad
+ // that's why we transplant typechecked, i.e. symful, annotations onto original trees
+ class AnnotationFixup extends self.Transformer {
+ override def transform(tree: Tree) = tree match {
+ case Annotated(ann0, args) =>
+ assert(annmap contains ann0)
+ val ann1 = annmap(ann0)
+ val ann = toPreTyperAnnotation(ann1)
+ Annotated(ann, transform(args))
+ case _ =>
+ tree
+ }
+ }
+
+ if (reifyDebug) println("verdict: essential, reify as original")
+ val patchedOriginal = new AnnotationFixup().transform(tt.original)
+ reifyTree(patchedOriginal)
+ } else {
+ // type is deemed to be non-essential
+ // erase it and hope that subsequent reflective compilation will be able to recreate it again
+ if (reifyDebug) println("verdict: non-essential, discard")
+ mirrorCall("TypeTree")
+ }
+ } else {
+ var rtt = mirrorCall(nme.TypeTree, reifyType(tt.tpe))
+ // @xeno.by: temporarily disabling reification of originals
+ // subsequent reflective compilation will try to typecheck them
+ // and this means that the reifier has to do additional efforts to ensure that this will succeed
+ // additional efforts + no clear benefit = will be implemented later
+// if (tt.original != null) {
+// val setOriginal = Select(rtt, newTermName("setOriginal"))
+// val reifiedOriginal = reify(tt.original)
+// rtt = Apply(setOriginal, List(reifiedOriginal))
+// }
+ rtt
+ }
+ }
+
+ /** Reify post-typer representation of an annotation */
+ private def reifyAnnotation(ann: AnnotationInfo): Tree =
+ // @xeno.by: if you reify originals, you get SO when trying to reify AnnotatedTypes, so screw it - after all, it's not that important
+ mirrorFactoryCall("AnnotationInfo", reifyType(ann.atp), reifyList(ann.args), reify(ann.assocs))
+
+ /** Reify pre-typer representation of an annotation.
+ * The trick here is to retain the symbols that have been populated during typechecking of the annotation.
+ * If we do not do that, subsequent reflective compilation will fail.
+ */
+ private def toPreTyperAnnotation(ann: AnnotationInfo): Tree = {
+ if (definedInLiftedCode(ann.atp)) {
+ // todo. deconstruct reifiable tree from ann.original and ann.args+ann.assocs
+ //
+ // keep in mind that we can't simply use ann.original, because its args are symless
+ // which means that any imported symbol (e.g. List) will crash subsequent reflective compilation
+ // hint: if I had enough time, I'd try to extract reifiable annotation type from ann.original
+ // and to apply its constructor to ann.args (that are symful, i.e. suitable for reification)
+ //
+ // also, if we pursue the route of reifying annotations defined in lifted code
+ // we should think about how to provide types for all nodes of the return value
+ // this will be necessary for reifying AnnotatedTypes, since ASTs inside ATs must all have non-null tpes
+ // an alternative would be downgrading ATs to Annotated nodes, but this needs careful thinking
+ // for now I just leave this as an implementation restriction
+ CannotReifyAnnotationInvolvingBoundType(ann)
+ } else {
+ val args = if (ann.assocs.isEmpty) {
+ ann.args
+ } else {
+ def toScalaAnnotation(jann: ClassfileAnnotArg): Tree = jann match {
+ case LiteralAnnotArg(const) =>
+ Literal(const)
+ case ArrayAnnotArg(arr) =>
+ Apply(Ident(definitions.ArrayModule), arr.toList map toScalaAnnotation)
+ case NestedAnnotArg(ann) =>
+ toPreTyperAnnotation(ann)
+ }
+
+ ann.assocs map { case (nme, arg) => AssignOrNamedArg(Ident(nme), toScalaAnnotation(arg)) }
+ }
+
+ New(ann.atp, args: _*)
+ }
+ }
+
+ /**
+ * Reify a free reference. The result will be either a mirror reference
+ * to a global value, or else a mirror Literal.
+ */
+ private def reifyFree(tree: Tree): Tree = tree match {
+ case This(_) if tree.symbol.isClass && !tree.symbol.isModuleClass =>
+ val sym = tree.symbol
+ if (reifyDebug) println("This for %s, reified as freeVar".format(sym))
+ if (reifyDebug) println("Free: " + sym)
+ val freeVar = mirrorCall("newFreeVar", reify(sym.name.toString), reify(sym.tpe), This(sym))
+ mirrorCall(nme.Ident, freeVar)
+ case This(_) =>
+ if (reifyDebug) println("This for %s, reified as This".format(tree.symbol))
+ mirrorCall(nme.This, reifySymRef(tree.symbol))
+ case _ =>
+ mirrorCall(nme.Ident, reifySymRef(tree.symbol))
+ }
+
+ // todo: consider whether we should also reify positions
+ private def reifyPosition(pos: Position): Tree =
+ reifyMirrorObject(NoPosition)
+
+ // !!! we must eliminate these casts.
+ private def reifyProductUnsafe(x: Any): Tree =
+ if (x.isInstanceOf[Product]) reifyProduct(x.asInstanceOf[Product])
+ else throw new Exception("%s of type %s cannot be cast to Product".format(x, x.getClass))
+ private def reifyProduct(x: Product): Tree =
+ reifyProduct(x.productPrefix, x.productIterator.toList)
+ private def reifyProduct(prefix: String, elements: List[Any]): Tree = {
+ // @xeno.by: reflection would be more robust, but, hey, this is a hot path
+ if (prefix.startsWith("Tuple")) reifyAggregate(prefix, elements: _*)
+ else mirrorCall(prefix, (elements map reify): _*)
+ }
+
+ /**
+ * Reify a case object defined in Mirror
+ */
+ private def reifyMirrorObject(name: String): Tree = mirrorSelect(name)
+ private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix)
+
+ private def isReifiableConstant(value: Any) = value match {
+ case null => true // seems pretty reifable to me?
+ case _: String => true
+ case _ => isAnyVal(value)
+ }
+
+ /** Reify an arbitary value */
+ private def reify(value: Any): Tree = value match {
+ case tree: Tree => reifyTree(tree)
+ case sym: Symbol => reifySymRef(sym)
+ case tpe: Type => reifyType(tpe)
+ case xs: List[_] => reifyList(xs)
+ case xs: Array[_] => reifyArray(xs)
+ case scope: Scope => reifyScope(scope)
+ case x: Name => reifyName(x)
+ case x: Position => reifyPosition(x)
+ case x: Modifiers => reifyModifiers(x)
+ case x: AnnotationInfo => reifyAnnotation(x)
+ case _ =>
+ if (isReifiableConstant(value)) Literal(Constant(value))
+ else reifyProductUnsafe(value)
+ }
+
+ /**
+ * An (unreified) path that refers to definition with given fully qualified name
+ * @param mkName Creator for last portion of name (either TermName or TypeName)
+ */
+ private def path(fullname: String, mkName: String => Name): Tree = {
+ val parts = fullname split "\\."
+ val prefixParts = parts.init
+ val lastName = mkName(parts.last)
+ if (prefixParts.isEmpty) Ident(lastName)
+ else {
+ val prefixTree = ((Ident(prefixParts.head): Tree) /: prefixParts.tail)(Select(_, _))
+ Select(prefixTree, lastName)
+ }
+ }
+
+ /** An (unreified) path that refers to term definition with given fully qualified name */
+ private def termPath(fullname: String): Tree = path(fullname, newTermName)
+
+ /** An (unreified) path that refers to type definition with given fully qualified name */
+ private def typePath(fullname: String): Tree = path(fullname, newTypeName)
+
+ private def mirrorAlias =
+ ValDef(NoMods, nme.MIRROR_SHORT, SingletonTypeTree(termPath(fullnme.MirrorPackage)), termPath(fullnme.MirrorPackage))
+
+ /**
+ * Generate code that generates a symbol table of all symbols registered in `reifiableSyms`
+ */
+ private def reifySymbolTableSetup: List[Tree] = {
+ val symDefs, fillIns = new mutable.ArrayBuffer[Tree]
+ var i = 0
+ while (i < reifiableSyms.length) {
+ // fillInSymbol might create new reifiableSyms, that's why this is done iteratively
+ symDefs += reifySymbolDef(reifiableSyms(i))
+ fillIns += fillInSymbol(reifiableSyms(i))
+ i += 1
+ }
+
+ symDefs.toList ++ fillIns.toList
+ }
+ } // end of Reifier
+
+ object Reifier {
+ def CannotReifyPreTyperTree(tree: Tree) = {
+ val msg = "pre-typer trees are not supported, consider typechecking the tree before passing it to the reifier"
+ throw new ReifierError(tree.pos, msg)
+ }
+
+ def CannotReifyErroneousTree(tree: Tree) = {
+ val msg = "erroneous trees are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
+ throw new ReifierError(tree.pos, msg)
+ }
+
+ def CannotReifyErroneousType(tpe: Type) = {
+ val msg = "erroneous types are not supported, make sure that your tree typechecks successfully before passing it to the reifier"
+ throw new ReifierError(NoPosition, msg)
+ }
+
+ def CannotReifyClassOfBoundType(tree: Tree, tpe: Type) = {
+ val msg = "implementation restriction: cannot reify classOf[%s] which refers to a type declared inside the block being reified".format(tpe)
+ throw new ReifierError(tree.pos, msg)
+ }
+
+ def CannotReifyClassOfBoundEnum(tree: Tree, tpe: Type) = {
+ val msg = "implementation restriction: cannot reify classOf[%s] which refers to an enum declared inside the block being reified".format(tpe)
+ throw new ReifierError(tree.pos, msg)
+ }
+
+ def CannotReifyTypeInvolvingBoundType(tpe: Type) = {
+ val msg = "implementation restriction: cannot reify type %s which involves a symbol declared inside the block being reified".format(tpe)
+ throw new ReifierError(NoPosition, msg)
+ }
+
+ def CannotReifyAnnotationInvolvingBoundType(ann: AnnotationInfo) = {
+ val msg = "implementation restriction: cannot reify annotation @%s which involves a symbol declared inside the block being reified".format(ann)
+ throw new ReifierError(ann.original.pos, msg)
+ }
+ } // end of Reifier
+
+ // begin reify
+ import Reifier._
+ if (tree.tpe != null) {
+ val saved = printTypings
+ try {
+ val reifyDebug = settings.Yreifydebug.value
+ val debugTrace = util.trace when reifyDebug
+ debugTrace("transforming = ")(if (settings.Xshowtrees.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)
+ debugTrace("transformed = ") {
+ val reifier = new Reifier()
+ val untyped = reifier.reifyTopLevel(tree)
+
+ val reifyCopypaste = settings.Yreifycopypaste.value
+ if (reifyCopypaste) {
+ if (reifyDebug) println("=======================")
+ println(reifiedNodeToString(untyped))
+ if (reifyDebug) println("=======================")
+ }
+
+ untyped
+ }
+ } finally {
+ printTypings = saved
+ }
+ } else {
+ CannotReifyPreTyperTree(tree)
+ }
+ }
+
+ /** A throwable signalling a reification error */
+ class ReifierError(var pos: Position, val msg: String) extends Throwable(msg) {
+ def this(msg: String) = this(NoPosition, msg)
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala
new file mode 100644
index 0000000000..fce59bb099
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/ast/ReifyPrinters.scala
@@ -0,0 +1,75 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Martin Odersky
+ */
+
+package scala.tools.nsc
+package ast
+
+import compat.Platform.EOL
+import symtab._
+import Flags._
+
+trait ReifyPrinters { self: NodePrinters =>
+
+ val global: Global
+ import global._
+
+ object reifiedNodeToString extends Function1[Tree, String] {
+ def apply(tree: Tree): String = {
+ import scala.reflect.api.Modifier
+
+ // @PP: I fervently hope this is a test case or something, not anything being
+ // depended upon. Of more fragile code I cannot conceive.
+ // @eb: This stuff is only needed to debug-print out reifications in human-readable format
+ // Rolling a full-fledged, robust TreePrinter would be several times more code.
+ (for (line <- (tree.toString.split(EOL) drop 2 dropRight 1)) yield {
+ var s = line.trim
+ s = s.replace("$mr.", "")
+ s = s.replace(".apply", "")
+ s = s.replace("scala.collection.immutable.", "")
+ s = "List\\[List\\[.*?\\].*?\\]".r.replaceAllIn(s, "List")
+ s = "List\\[.*?\\]".r.replaceAllIn(s, "List")
+ s = s.replace("immutable.this.Nil", "List()")
+ s = s.replace("modifiersFromInternalFlags", "Modifiers")
+ s = s.replace("Modifiers(0L, newTypeName(\"\"), List())", "Modifiers()")
+ s = """Modifiers\((\d+)[lL], newTypeName\("(.*?)"\), List\((.*?)\)\)""".r.replaceAllIn(s, m => {
+ val buf = new collection.mutable.ListBuffer[String]
+
+ val annotations = m.group(3)
+ if (buf.nonEmpty || annotations.nonEmpty)
+ buf.append("List(" + annotations + ")")
+
+ val privateWithin = "" + m.group(2)
+ if (buf.nonEmpty || privateWithin != "")
+ buf.append("newTypeName(\"" + privateWithin + "\")")
+
+ val flags = m.group(1).toLong
+ val s_flags = Flags.modifiersOfFlags(flags) map (_.sourceString) mkString ", "
+ if (buf.nonEmpty || s_flags != "")
+ buf.append("Set(" + s_flags + ")")
+
+ "Modifiers(" + buf.reverse.mkString(", ") + ")"
+ })
+ s = """setInternalFlags\((\d+)L\)""".r.replaceAllIn(s, m => {
+ val flags = m.group(1).toLong
+ val mods = Flags.modifiersOfFlags(flags) map (_.sourceString)
+ "setInternalFlags(flagsOfModifiers(List(" + mods.mkString(", ") + ")))"
+ })
+
+ s
+ }) mkString EOL
+ }
+ }
+
+
+ def printReifyCopypaste(tree: Tree) {
+ val reifyDebug = settings.Yreifydebug.value
+ if (reifyDebug) println("=======================")
+ printReifyCopypaste1(tree)
+ if (reifyDebug) println("=======================")
+ }
+
+ def printReifyCopypaste1(tree: Tree) {
+ }
+} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala
index 2cfd21ecc8..0d19b781e2 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeDSL.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeDSL.scala
@@ -253,13 +253,11 @@ trait TreeDSL {
}
/** Top level accessible. */
- def MATCHERROR(arg: Tree) = Throw(New(TypeTree(MatchErrorClass.tpe), List(List(arg))))
- /** !!! should generalize null guard from match error here. */
- def THROW(sym: Symbol): Throw = Throw(New(TypeTree(sym.tpe), List(Nil)))
- def THROW(sym: Symbol, msg: Tree): Throw = Throw(New(TypeTree(sym.tpe), List(List(msg.TOSTRING()))))
+ def MATCHERROR(arg: Tree) = Throw(MatchErrorClass.tpe, arg)
+ def THROW(sym: Symbol, msg: Tree): Throw = Throw(sym.tpe, msg.TOSTRING())
def NEW(tpt: Tree, args: Tree*): Tree = New(tpt, List(args.toList))
- def NEW(sym: Symbol, args: Tree*): Tree = New(sym, args: _*)
+ def NEW(sym: Symbol, args: Tree*): Tree = New(sym.tpe, args: _*)
def DEF(name: Name, tp: Type): DefTreeStart = DEF(name) withType tp
def DEF(name: Name): DefTreeStart = new DefTreeStart(name)
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 265d017653..d7159c5fa8 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -13,7 +13,7 @@ import symtab.SymbolTable
/** XXX to resolve: TreeGen only assumes global is a SymbolTable, but
* TreeDSL at the moment expects a Global. Can we get by with SymbolTable?
*/
-abstract class TreeGen extends reflect.internal.TreeGen {
+abstract class TreeGen extends reflect.internal.TreeGen with TreeDSL {
val global: Global
import global._
@@ -30,7 +30,7 @@ abstract class TreeGen extends reflect.internal.TreeGen {
else
tree
}
-
+
/** Builds a fully attributed wildcard import node.
*/
def mkWildcardImport(pkg: Symbol): Import = {
@@ -51,13 +51,12 @@ abstract class TreeGen extends reflect.internal.TreeGen {
}
// wrap the given expression in a SoftReference so it can be gc-ed
- def mkSoftRef(expr: Tree): Tree = atPos(expr.pos) {
- New(SoftReferenceClass, expr)
- }
+ def mkSoftRef(expr: Tree): Tree = atPos(expr.pos)(New(SoftReferenceClass.tpe, expr))
+
// annotate the expression with @unchecked
def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) {
// This can't be "Annotated(New(UncheckedClass), expr)" because annotations
- // are very pick about things and it crashes the compiler with "unexpected new".
+ // are very picky about things and it crashes the compiler with "unexpected new".
Annotated(New(scalaDot(UncheckedClass.name), List(Nil)), expr)
}
// if it's a Match, mark the selector unchecked; otherwise nothing.
@@ -66,18 +65,81 @@ abstract class TreeGen extends reflect.internal.TreeGen {
case _ => tree
}
- def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = matchExpr match {
- case Match(scrutinee, cases) =>
- if (cases exists treeInfo.isDefaultCase) matchExpr
- else {
- val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(scrutinee))
- Match(scrutinee, cases :+ defaultCase)
+ // must be kept in synch with the codegen in PatMatVirtualiser
+ object VirtualCaseDef {
+ def unapply(b: Block): Option[(Assign, Tree, Tree)] = b match {
+ case Block(List(assign@Assign(keepGoingLhs, falseLit), matchRes), zero) => Some((assign, matchRes, zero)) // TODO: check tree annotation
+ case _ => None
+ }
+ }
+
+ // TODO: would be so much nicer if we would know during match-translation (i.e., type checking)
+ // whether we should emit missingCase-style apply (and isDefinedAt), instead of transforming trees post-factum
+ class MatchMatcher {
+ def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = unknownTree(orig)
+ def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = unknownTree(orig)
+ def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = unknownTree(orig)
+
+ def apply(matchExpr: Tree): Tree = (matchExpr: @unchecked) match {
+ // old-style match or virtpatmat switch
+ case Match(selector, cases) => // println("simple match: "+ (selector, cases) + "for:\n"+ matchExpr )
+ caseMatch(matchExpr, selector, cases, identity)
+ // old-style match or virtpatmat switch
+ case Block((vd: ValDef) :: Nil, orig@Match(selector, cases)) => // println("block match: "+ (selector, cases, vd) + "for:\n"+ matchExpr )
+ caseMatch(matchExpr, selector, cases, m => copyBlock(matchExpr, List(vd), m))
+ // virtpatmat
+ case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), List(scrut)), List(matcher)) if opt.virtPatmat => // println("virt match: "+ (tgt, targs, scrut, matcher) + "for:\n"+ matchExpr )
+ caseVirtualizedMatch(matchExpr, tgt, targs, scrut, matcher)
+ // optimized version of virtpatmat
+ case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue) if opt.virtPatmat => // TODO: check tree annotation // println("virtopt match: "+ (zero, x, matchRes, keepGoing, stats) + "for:\n"+ matchExpr )
+ caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, identity)
+ // optimized version of virtpatmat
+ case Block(outerStats, orig@Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, epilogue)) if opt.virtPatmat => // TODO: check tree annotation // println("virt opt block match: "+ (zero, x, matchRes, keepGoing, stats, outerStats) + "for:\n"+ matchExpr )
+ caseVirtualizedMatchOpt(matchExpr, zero, x, matchRes, keepGoing, stats, epilogue, m => copyBlock(matchExpr, outerStats, m))
+ case other =>
+ unknownTree(other)
+ }
+
+ def unknownTree(t: Tree): Tree = throw new MatchError(t)
+ def copyBlock(orig: Tree, stats: List[Tree], expr: Tree): Block = Block(stats, expr)
+
+ def dropSyntheticCatchAll(cases: List[CaseDef]): List[CaseDef] =
+ if (!opt.virtPatmat) cases
+ else cases filter {
+ case CaseDef(pat, EmptyTree, Throw(Apply(Select(New(exTpt), nme.CONSTRUCTOR), _))) if (treeInfo.isWildcardArg(pat) && (exTpt.tpe.typeSymbol eq MatchErrorClass)) => false
+ case CaseDef(pat, guard, body) => true
+ }
+ }
+
+ def withDefaultCase(matchExpr: Tree, defaultAction: Tree/*scrutinee*/ => Tree): Tree = {
+ object withDefaultTransformer extends MatchMatcher {
+ override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = {
+ val casesNoSynthCatchAll = dropSyntheticCatchAll(cases)
+ if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) orig
+ else {
+ val defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, defaultAction(selector.duplicate))
+ wrap(Match(selector, casesNoSynthCatchAll :+ defaultCase))
+ }
+ }
+ override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = { import CODE._
+ ((matcher APPLY (scrut)) DOT nme.getOrElse) APPLY (defaultAction(scrut.duplicate)) // TODO: pass targs
}
- case _ =>
- matchExpr
- // [Martin] Adriaan: please fill in virtpatmat transformation here
+ override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree): Tree = { import CODE._
+ wrap(Block(
+ zero ::
+ x ::
+ matchRes ::
+ keepGoing ::
+ stats,
+ // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `if (keepGoing) ${defaultAction(`x`)} else matchRes`
+ (IF (REF(keepGoing.symbol)) THEN defaultAction(x.rhs.duplicate) ELSE REF(matchRes.symbol))
+ ))
+ }
+ }
+ withDefaultTransformer(matchExpr)
}
+
def mkCached(cvar: Symbol, expr: Tree): Tree = {
val cvarRef = mkUnattributedRef(cvar)
Block(
@@ -98,7 +160,7 @@ abstract class TreeGen extends reflect.internal.TreeGen {
def mkModuleVarDef(accessor: Symbol) = {
val inClass = accessor.owner.isClass
val extraFlags = if (inClass) PrivateLocal | SYNTHETIC else 0
-
+
val mval = (
accessor.owner.newVariable(nme.moduleVarName(accessor.name), accessor.pos.focus, MODULEVAR | extraFlags)
setInfo accessor.tpe.finalResultType
@@ -118,10 +180,11 @@ abstract class TreeGen extends reflect.internal.TreeGen {
def mkModuleAccessDef(accessor: Symbol, msym: Symbol) =
DefDef(accessor, Select(This(msym.owner), msym))
- def newModule(accessor: Symbol, tpe: Type) =
- New(TypeTree(tpe),
- List(for (pt <- tpe.typeSymbol.primaryConstructor.info.paramTypes)
- yield This(accessor.owner.enclClass)))
+ def newModule(accessor: Symbol, tpe: Type) = {
+ val ps = tpe.typeSymbol.primaryConstructor.info.paramTypes
+ if (ps.isEmpty) New(tpe)
+ else New(tpe, This(accessor.owner.enclClass))
+ }
// def m: T;
def mkModuleAccessDcl(accessor: Symbol) =
@@ -156,6 +219,18 @@ abstract class TreeGen extends reflect.internal.TreeGen {
def mkSynchronized(monitor: Tree, body: Tree): Tree =
Apply(Select(monitor, Object_synchronized), List(body))
+ def mkAppliedTypeForCase(clazz: Symbol): Tree = {
+ val numParams = clazz.typeParams.size
+ if (clazz.typeParams.isEmpty) Ident(clazz)
+ else AppliedTypeTree(Ident(clazz), 1 to numParams map (_ => Bind(tpnme.WILDCARD, EmptyTree)) toList)
+ }
+ def mkBindForCase(patVar: Symbol, clazz: Symbol, targs: List[Type]): Tree = {
+ Bind(patVar, Typed(Ident(nme.WILDCARD),
+ if (targs.isEmpty) mkAppliedTypeForCase(clazz)
+ else AppliedTypeTree(Ident(clazz), targs map TypeTree)
+ ))
+ }
+
def wildcardStar(tree: Tree) =
atPos(tree.pos) { Typed(tree, Ident(tpnme.WILDCARD_STAR)) }
diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
index 5c3071739c..3371353f25 100644
--- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
@@ -51,14 +51,11 @@ trait TreePrinters extends reflect.internal.TreePrinters { this: Global =>
treePrinter.println()
treePrinter.print(definition)
- case AssignOrNamedArg(lhs, rhs) =>
- treePrinter.print(lhs, " = ", rhs)
-
case TypeTreeWithDeferredRefCheck() =>
treePrinter.print("<tree with deferred refcheck>")
case SelectFromArray(qualifier, name, _) =>
- treePrinter.print(qualifier, ".<arr>", treePrinter.symName(tree, name))
+ treePrinter.print(qualifier, ".<arr>", symName(tree, name))
case _ =>
super.xprintTree(treePrinter, tree)
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 83b6252b26..ad87889145 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -30,12 +30,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
override def isType = definition.isType
}
- /** Either an assignment or a named argument. Only appears in argument lists,
- * eliminated by typecheck (doTypedApply)
- */
- case class AssignOrNamedArg(lhs: Tree, rhs: Tree)
- extends TermTree
-
/** Array selection <qualifier> . <name> only used during erasure */
case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type)
extends TermTree with RefTree
@@ -85,16 +79,16 @@ trait Trees extends reflect.internal.Trees { self: Global =>
val (edefs, rest) = body span treeInfo.isEarlyDef
val (evdefs, etdefs) = edefs partition treeInfo.isEarlyValDef
val gvdefs = evdefs map {
- case vdef @ ValDef(mods, name, tpt, rhs) =>
- treeCopy.ValDef(
- vdef.duplicate, mods, name,
- atPos(focusPos(vdef.pos)) { TypeTree() setOriginal tpt setPos focusPos(tpt.pos) }, // atPos in case
- EmptyTree)
- }
- val lvdefs = evdefs map {
- case vdef @ ValDef(mods, name, tpt, rhs) =>
- treeCopy.ValDef(vdef, Modifiers(PRESUPER), name, tpt, rhs)
+ case vdef @ ValDef(_, _, tpt, _) => copyValDef(vdef)(
+ // !!! I know "atPos in case" wasn't intentionally planted to
+ // add an air of mystery to this file, but it is the sort of
+ // comment which only its author could love.
+ tpt = atPos(focusPos(vdef.pos))(TypeTree() setOriginal tpt setPos focusPos(tpt.pos)), // atPos in case
+ rhs = EmptyTree
+ )
}
+ val lvdefs = evdefs collect { case vdef: ValDef => copyValDef(vdef)(mods = Modifiers(PRESUPER)) }
+
val constrs = {
if (constrMods hasFlag TRAIT) {
if (body forall treeInfo.isInterfaceMember) List()
@@ -114,13 +108,11 @@ trait Trees extends reflect.internal.Trees { self: Global =>
DefDef(constrMods, nme.CONSTRUCTOR, List(), vparamss1, TypeTree(), Block(lvdefs ::: List(superCall), Literal(Constant())))))
}
}
- // println("typed template, gvdefs = "+gvdefs+", parents = "+parents+", constrs = "+constrs)
constrs foreach (ensureNonOverlapping(_, parents ::: gvdefs))
- // vparamss2 are used as field definitions for the class. remove defaults
- val vparamss2 = vparamss map (vps => vps map { vd =>
- treeCopy.ValDef(vd, vd.mods &~ DEFAULTPARAM, vd.name, vd.tpt, EmptyTree)
- })
- Template(parents, self, gvdefs ::: vparamss2.flatten ::: constrs ::: etdefs ::: rest)
+ // Field definitions for the class - remove defaults.
+ val fieldDefs = vparamss.flatten map (vd => copyValDef(vd)(mods = vd.mods &~ DEFAULTPARAM, rhs = EmptyTree))
+
+ Template(parents, self, gvdefs ::: fieldDefs ::: constrs ::: etdefs ::: rest)
}
/** Construct class definition with given class symbol, value parameters,
@@ -155,8 +147,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
traverser.traverseTrees(ts)
case DocDef(comment, definition) =>
traverser.traverse(definition)
- case AssignOrNamedArg(lhs, rhs) =>
- traverser.traverse(lhs); traverser.traverse(rhs)
case SelectFromArray(qualifier, selector, erasure) =>
traverser.traverse(qualifier)
case ReferenceToBoxed(idt) =>
@@ -168,7 +158,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
trait TreeCopier extends super.TreeCopierOps {
def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef
- def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree): AssignOrNamedArg
def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type): SelectFromArray
def ReferenceToBoxed(tree: Tree, idt: Ident): ReferenceToBoxed
def TypeTreeWithDeferredRefCheck(tree: Tree): TypeTreeWithDeferredRefCheck
@@ -180,8 +169,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
class StrictTreeCopier extends super.StrictTreeCopier with TreeCopier {
def DocDef(tree: Tree, comment: DocComment, definition: Tree) =
new DocDef(comment, definition).copyAttrs(tree)
- def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) =
- new AssignOrNamedArg(lhs, rhs).copyAttrs(tree)
def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) =
new SelectFromArray(qualifier, selector, erasure).copyAttrs(tree)
def ReferenceToBoxed(tree: Tree, idt: Ident) =
@@ -197,11 +184,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
if (comment0 == comment) && (definition0 == definition) => t
case _ => this.treeCopy.DocDef(tree, comment, definition)
}
- def AssignOrNamedArg(tree: Tree, lhs: Tree, rhs: Tree) = tree match {
- case t @ AssignOrNamedArg(lhs0, rhs0)
- if (lhs0 == lhs) && (rhs0 == rhs) => t
- case _ => this.treeCopy.AssignOrNamedArg(tree, lhs, rhs)
- }
def SelectFromArray(tree: Tree, qualifier: Tree, selector: Name, erasure: Type) = tree match {
case t @ SelectFromArray(qualifier0, selector0, _)
if (qualifier0 == qualifier) && (selector0 == selector) => t
@@ -232,8 +214,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
override protected def xtransform(transformer: super.Transformer, tree: Tree): Tree = tree match {
case DocDef(comment, definition) =>
transformer.treeCopy.DocDef(tree, comment, transformer.transform(definition))
- case AssignOrNamedArg(lhs, rhs) =>
- transformer.treeCopy.AssignOrNamedArg(tree, transformer.transform(lhs), transformer.transform(rhs))
case SelectFromArray(qualifier, selector, erasure) =>
transformer.treeCopy.SelectFromArray(
tree, transformer.transform(qualifier), selector, erasure)
@@ -269,12 +249,27 @@ trait Trees extends reflect.internal.Trees { self: Global =>
* (bq:) This transformer has mutable state and should be discarded after use
*/
private class ResetAttrs(localOnly: Boolean) {
+ val debug = settings.debug.value
+ val trace = scala.tools.nsc.util.trace when debug
+
val locals = util.HashSet[Symbol](8)
+ val orderedLocals = collection.mutable.ListBuffer[Symbol]()
+ def registerLocal(sym: Symbol) {
+ if (sym != null && sym != NoSymbol) {
+ if (debug && !(locals contains sym)) orderedLocals append sym
+ locals addEntry sym
+ }
+ }
class MarkLocals extends self.Traverser {
- def markLocal(tree: Tree) =
- if (tree.symbol != null && tree.symbol != NoSymbol)
- locals addEntry tree.symbol
+ def markLocal(tree: Tree) {
+ if (tree.symbol != null && tree.symbol != NoSymbol) {
+ val sym = tree.symbol
+ registerLocal(sym)
+ registerLocal(sym.sourceModule)
+ registerLocal(sym.moduleClass)
+ }
+ }
override def traverse(tree: Tree) = {
tree match {
@@ -319,9 +314,12 @@ trait Trees extends reflect.internal.Trees { self: Global =>
def transform[T <: Tree](x: T): T = {
new MarkLocals().traverse(x)
- val trace = scala.tools.nsc.util.trace when settings.debug.value
- val eoln = System.getProperty("line.separator")
- trace("locals (%d total): %n".format(locals.size))(locals.toList map {" " + _} mkString eoln)
+ if (debug) {
+ assert(locals.size == orderedLocals.size)
+ val eoln = System.getProperty("line.separator")
+ val msg = orderedLocals.toList filter {_ != NoSymbol} map {" " + _} mkString eoln
+ trace("locals (%d total): %n".format(orderedLocals.size))(msg)
+ }
val x1 = new Transformer().transform(x)
assert(x.getClass isInstance x1)
@@ -333,7 +331,6 @@ trait Trees extends reflect.internal.Trees { self: Global =>
case Parens(expr) (only used during parsing)
case DocDef(comment, defn) => (eliminated by typer)
- case AssignOrNamedArg(lhs, rhs) => (eliminated by typer)
case TypeTreeWithDeferredRefCheck() => (created and eliminated by typer)
case SelectFromArray(_, _, _) => (created and eliminated by erasure)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index c2b4dc32b6..cd19fca0b0 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -658,7 +658,8 @@ self =>
DocDef(doc, t) setPos {
if (t.pos.isDefined) {
val pos = doc.pos.withEnd(t.pos.endOrPoint)
- if (t.pos.isOpaqueRange) pos else pos.makeTransparent
+ // always make the position transparent
+ pos.makeTransparent
} else {
t.pos
}
@@ -1205,7 +1206,7 @@ self =>
*/
def wildcardType(start: Int) = {
val pname = freshTypeName("_$")
- val t = atPos(start) { Ident(pname) }
+ val t = atPos(start)(Ident(pname))
val bounds = typeBounds()
val param = atPos(t.pos union bounds.pos) { makeSyntheticTypeParam(pname, bounds) }
placeholderTypes = param :: placeholderTypes
@@ -1423,15 +1424,14 @@ self =>
def implicitClosure(start: Int, location: Int): Tree = {
val param0 = convertToParam {
atPos(in.offset) {
- var paramexpr: Tree = Ident(ident())
- if (in.token == COLON) {
- in.nextToken()
- paramexpr = Typed(paramexpr, typeOrInfixType(location))
+ Ident(ident()) match {
+ case expr if in.token == COLON =>
+ in.nextToken() ; Typed(expr, typeOrInfixType(location))
+ case expr => expr
}
- paramexpr
}
}
- val param = treeCopy.ValDef(param0, param0.mods | Flags.IMPLICIT, param0.name, param0.tpt, param0.rhs)
+ val param = copyValDef(param0)(mods = param0.mods | Flags.IMPLICIT)
atPos(start, in.offset) {
accept(ARROW)
Function(List(param), if (location != InBlock) expr() else block())
@@ -2689,8 +2689,8 @@ self =>
val (self, body) = templateBody(true)
if (in.token == WITH && self.isEmpty) {
val earlyDefs: List[Tree] = body flatMap {
- case vdef @ ValDef(mods, name, tpt, rhs) if !mods.isDeferred =>
- List(treeCopy.ValDef(vdef, mods | Flags.PRESUPER, name, tpt, rhs))
+ case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred =>
+ List(copyValDef(vdef)(mods = mods | Flags.PRESUPER))
case tdef @ TypeDef(mods, name, tparams, rhs) =>
List(treeCopy.TypeDef(tdef, mods | Flags.PRESUPER, name, tparams, rhs))
case stat if !stat.isEmpty =>
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index dae264fffe..2895d02dfe 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -84,7 +84,7 @@ trait Scanners extends ScannersCommon {
abstract class Scanner extends CharArrayReader with TokenData with ScannerCommon {
private def isDigit(c: Char) = java.lang.Character isDigit c
-
+
def isAtEnd = charOffset >= buf.length
def flush = { charOffset = offset; nextChar(); this }
@@ -164,7 +164,7 @@ trait Scanners extends ScannersCommon {
* RBRACE if region starts with '{'
* ARROW if region starts with `case'
* STRINGLIT if region is a string interpolation expression starting with '${'
- * (the STRINGLIT appears twice in succession on the stack iff the
+ * (the STRINGLIT appears twice in succession on the stack iff the
* expression is a multiline string literal).
*/
var sepRegions: List[Int] = List()
@@ -173,15 +173,15 @@ trait Scanners extends ScannersCommon {
/** Are we directly in a string interpolation expression?
*/
- @inline private def inStringInterpolation =
+ @inline private def inStringInterpolation =
sepRegions.nonEmpty && sepRegions.head == STRINGLIT
-
+
/** Are we directly in a multiline string interpolation expression?
* @pre: inStringInterpolation
*/
- @inline private def inMultiLineInterpolation =
- sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART
-
+ @inline private def inMultiLineInterpolation =
+ inStringInterpolation && sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART
+
/** read next token and return last offset
*/
def skipToken(): Offset = {
@@ -205,7 +205,7 @@ trait Scanners extends ScannersCommon {
case CASE =>
sepRegions = ARROW :: sepRegions
case RBRACE =>
- while (!sepRegions.isEmpty && sepRegions.head != RBRACE)
+ while (!sepRegions.isEmpty && sepRegions.head != RBRACE)
sepRegions = sepRegions.tail
if (!sepRegions.isEmpty) sepRegions = sepRegions.tail
docBuffer = null
@@ -217,11 +217,13 @@ trait Scanners extends ScannersCommon {
if (!sepRegions.isEmpty && sepRegions.head == lastToken)
sepRegions = sepRegions.tail
case STRINGLIT =>
- if (inStringInterpolation)
+ if (inMultiLineInterpolation)
+ sepRegions = sepRegions.tail.tail
+ else if (inStringInterpolation)
sepRegions = sepRegions.tail
case _ =>
}
-
+
// Read a token or copy it from `next` tokenData
if (next.token == EMPTY) {
lastOffset = charOffset - 1
@@ -325,8 +327,8 @@ trait Scanners extends ScannersCommon {
'z' =>
putChar(ch)
nextChar()
- getIdentRest()
- if (ch == '"' && token == IDENTIFIER && settings.Xexperimental.value)
+ getIdentRest()
+ if (ch == '"' && token == IDENTIFIER && settings.Xexperimental.value)
token = INTERPOLATIONID
case '<' => // is XMLSTART?
val last = if (charOffset >= 2) buf(charOffset - 2) else ' '
@@ -386,7 +388,7 @@ trait Scanners extends ScannersCommon {
if (ch == '\"') {
nextRawChar()
getStringPart(multiLine = true)
- sepRegions = STRINGLIT :: sepRegions // indicate string part
+ sepRegions = STRINGPART :: sepRegions // indicate string part
sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part
} else {
token = STRINGLIT
@@ -407,7 +409,7 @@ trait Scanners extends ScannersCommon {
token = STRINGLIT
strVal = ""
}
- } else {
+ } else {
getStringLit()
}
}
@@ -630,8 +632,8 @@ trait Scanners extends ScannersCommon {
else finishNamed()
}
}
-
-
+
+
// Literals -----------------------------------------------------------------
private def getStringLit() = {
@@ -659,21 +661,27 @@ trait Scanners extends ScannersCommon {
getRawStringLit()
}
}
-
+
@annotation.tailrec private def getStringPart(multiLine: Boolean): Unit = {
def finishStringPart() = {
setStrVal()
token = STRINGPART
next.lastOffset = charOffset - 1
next.offset = charOffset - 1
- }
+ }
if (ch == '"') {
- nextRawChar()
- if (!multiLine || isTripleQuote()) {
+ if (multiLine) {
+ nextRawChar()
+ if (isTripleQuote()) {
+ setStrVal()
+ token = STRINGLIT
+ } else
+ getStringPart(multiLine)
+ } else {
+ nextChar()
setStrVal()
token = STRINGLIT
- } else
- getStringPart(multiLine)
+ }
} else if (ch == '$') {
nextRawChar()
if (ch == '$') {
@@ -696,20 +704,23 @@ trait Scanners extends ScannersCommon {
} else {
syntaxError("invalid string interpolation")
}
- } else if ((ch == CR || ch == LF || ch == SU) && !isUnicodeEscape) {
- syntaxError("unclosed string literal")
} else {
- putChar(ch)
- nextRawChar()
- getStringPart(multiLine)
+ val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF)))
+ if (isUnclosedLiteral) {
+ syntaxError(if (!multiLine) "unclosed string literal" else "unclosed multi-line string literal")
+ } else {
+ putChar(ch)
+ nextRawChar()
+ getStringPart(multiLine)
+ }
}
}
-
+
private def fetchStringPart() = {
offset = charOffset - 1
getStringPart(multiLine = inMultiLineInterpolation)
}
-
+
private def isTripleQuote(): Boolean =
if (ch == '"') {
nextRawChar()
@@ -730,7 +741,7 @@ trait Scanners extends ScannersCommon {
false
}
- /** copy current character into cbuf, interpreting any escape sequences,
+ /** copy current character into cbuf, interpreting any escape sequences,
* and advance to next character.
*/
protected def getLitChar(): Unit =
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
index 091f333c27..fb4daefd57 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
@@ -58,7 +58,7 @@ object Tokens extends Tokens {
final val BACKQUOTED_IDENT = 11
def isIdentifier(code: Int) =
code >= IDENTIFIER && code <= BACKQUOTED_IDENT
-
+
@switch def canBeginExpression(code: Int) = code match {
case IDENTIFIER|BACKQUOTED_IDENT|USCORE => true
case LBRACE|LPAREN|LBRACKET|COMMENT => true
diff --git a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
index 40389466e2..0d2fbc5372 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/TreeBuilder.scala
@@ -471,15 +471,11 @@ abstract class TreeBuilder {
def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean): Tree =
makeVisitor(cases, checkExhaustive, "x$")
- private def makeUnchecked(expr: Tree): Tree = atPos(expr.pos) {
- Annotated(New(scalaDot(definitions.UncheckedClass.name), List(Nil)), expr)
- }
-
/** Create visitor <x => x match cases> */
def makeVisitor(cases: List[CaseDef], checkExhaustive: Boolean, prefix: String): Tree = {
- val x = freshTermName(prefix)
- val id = Ident(x)
- val sel = if (checkExhaustive) id else makeUnchecked(id)
+ val x = freshTermName(prefix)
+ val id = Ident(x)
+ val sel = if (checkExhaustive) id else gen.mkUnchecked(id)
Function(List(makeSyntheticParam(x)), Match(sel, cases))
}
@@ -564,7 +560,7 @@ abstract class TreeBuilder {
val vars = getVariables(pat1)
val matchExpr = atPos((pat1.pos union rhs.pos).makeTransparent) {
Match(
- makeUnchecked(rhs),
+ gen.mkUnchecked(rhs),
List(
atPos(pat1.pos) {
CaseDef(pat1, EmptyTree, makeTupleTerm(vars map (_._1) map Ident, true))
diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
index 05571b2424..aab944f65a 100644
--- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
+++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala
@@ -565,7 +565,7 @@ abstract class ScalaPrimitives {
import definitions._
val code = getPrimitive(fun)
- def elementType = atPhase(currentRun.typerPhase) {
+ def elementType = beforeTyper {
val arrayParent = tpe :: tpe.parents collectFirst {
case TypeRef(_, ArrayClass, elem :: Nil) => elem
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
index 4ab0eb0129..68c4ac03f6 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala
@@ -18,7 +18,7 @@ trait BasicBlocks {
import opcodes._
import global.{ ifDebug, settings, log, nme }
import nme.isExceptionResultName
-
+
object NoBasicBlock extends BasicBlock(-1, null)
/** This class represents a basic block. Each
@@ -182,7 +182,7 @@ trait BasicBlocks {
final def foreach[U](f: Instruction => U) = {
if (!closed) dumpMethodAndAbort(method, this)
else instrs foreach f
-
+
// !!! If I replace "instrs foreach f" with the following:
// var i = 0
// val len = instrs.length
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 3baff7da9e..9e801e3ea8 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -94,7 +94,7 @@ abstract class GenICode extends SubComponent {
// !! modules should be eliminated by refcheck... or not?
case ModuleDef(mods, name, impl) =>
- abort("Modules should not reach backend!")
+ abort("Modules should not reach backend! " + tree)
case ValDef(mods, name, tpt, rhs) =>
ctx // we use the symbol to add fields
@@ -133,7 +133,7 @@ abstract class GenICode extends SubComponent {
if (!ctx1.bb.closed) ctx1.bb.close
prune(ctx1.method)
} else
- ctx1.method.setCode(null)
+ ctx1.method.setCode(NoCode)
ctx1
case Template(_, _, body) =>
@@ -393,15 +393,15 @@ abstract class GenICode extends SubComponent {
for (CaseDef(pat, _, body) <- catches.reverse) yield {
def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) =
(sym, kind, ctx => {
- ctx.bb.emit(DROP(REFERENCE(sym)))
+ ctx.bb.emit(DROP(REFERENCE(sym))) // drop the loaded exception
genLoad(body, ctx, kind)
})
pat match {
case Typed(Ident(nme.WILDCARD), tpt) => genWildcardHandler(tpt.tpe.typeSymbol)
case Ident(nme.WILDCARD) => genWildcardHandler(ThrowableClass)
- case Bind(name, _) =>
- val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false)
+ case Bind(_, _) =>
+ val exception = ctx.method addLocal new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false) // the exception will be loaded and stored into this local
(pat.symbol.tpe.typeSymbol, kind, {
ctx: Context =>
@@ -704,7 +704,8 @@ abstract class GenICode extends SubComponent {
ctx1
case New(tpt) =>
- abort("Unexpected New")
+ abort("Unexpected New(" + tpt.summaryString + "/" + tpt + ") received in icode.\n" +
+ " Call was genLoad" + ((tree, ctx, expectedType)))
case Apply(TypeApply(fun, targs), _) =>
val sym = fun.symbol
@@ -1054,7 +1055,7 @@ abstract class GenICode extends SubComponent {
case Match(selector, cases) =>
debuglog("Generating SWITCH statement.");
- var ctx1 = genLoad(selector, ctx, INT)
+ var ctx1 = genLoad(selector, ctx, INT) // TODO: Java 7 allows strings in switches (so, don't assume INT and don't convert the literals using intValue)
val afterCtx = ctx1.newBlock
var caseCtx: Context = null
generatedType = toTypeKind(tree.tpe)
@@ -2086,12 +2087,12 @@ abstract class GenICode extends SubComponent {
exh
}) else None
- val exhs = handlers.map { handler =>
- val exh = this.newExceptionHandler(handler._1, handler._2, tree.pos)
+ val exhs = handlers.map { case (sym, kind, handler) => // def genWildcardHandler(sym: Symbol): (Symbol, TypeKind, Context => Context) =
+ val exh = this.newExceptionHandler(sym, kind, tree.pos)
var ctx1 = outerCtx.enterExceptionHandler(exh)
ctx1.addFinalizer(finalizer, finalizerCtx)
loadException(ctx1, exh, tree.pos)
- ctx1 = handler._3(ctx1)
+ ctx1 = handler(ctx1)
// emit finalizer
val ctx2 = emitFinalizer(ctx1)
ctx2.bb.closeWith(JUMP(afterCtx.bb))
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala
index 2668e7f29f..36651541b2 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala
@@ -21,7 +21,7 @@ trait Members {
self: ICodes =>
import global._
-
+
object NoCode extends Code(null, "NoCode") {
override def blocksList: List[BasicBlock] = Nil
}
@@ -138,7 +138,7 @@ trait Members {
/** Represent a field in ICode */
class IField(val symbol: Symbol) extends IMember { }
-
+
object NoIMethod extends IMethod(NoSymbol) { }
/**
@@ -177,19 +177,13 @@ trait Members {
/** method parameters */
var params: List[Local] = Nil
- // TODO - see how null is stil arriving here
- def hasCode = (code ne NoCode) && (code ne null)
+ def hasCode = code ne NoCode
def setCode(code: Code): IMethod = {
this.code = code;
this
}
- def addLocal(l: Local): Local =
- locals find (_ == l) getOrElse {
- locals ::= l
- l
- }
-
+ def addLocal(l: Local): Local = findOrElse(locals)(_ == l) { locals ::= l ; l }
def addParam(p: Local): Unit =
if (params contains p) ()
@@ -214,6 +208,12 @@ trait Members {
override def toString() = symbol.fullName
+ def matchesSignature(other: IMethod) = {
+ (symbol.name == other.symbol.name) &&
+ (params corresponds other.params)(_.kind == _.kind) &&
+ (returnType == other.returnType)
+ }
+
import opcodes._
def checkLocals(): Unit = {
def localsSet = (code.blocks flatMap { bb =>
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
index 2bcfb9d4a9..ec6c631bd1 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
@@ -350,6 +350,7 @@ trait Opcodes { self: ICodes =>
}
case class BOX(boxType: TypeKind) extends Instruction {
+ assert(boxType.isValueType && (boxType ne UNIT)) // documentation
override def toString(): String = "BOX " + boxType
override def consumed = 1
override def consumedTypes = boxType :: Nil
@@ -357,6 +358,7 @@ trait Opcodes { self: ICodes =>
}
case class UNBOX(boxType: TypeKind) extends Instruction {
+ assert(boxType.isValueType && (boxType ne UNIT)) // documentation
override def toString(): String = "UNBOX " + boxType
override def consumed = 1
override def consumedTypes = ObjectReference :: Nil
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala
index 37fff0e1e8..f99ac28e9d 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Primitives.scala
@@ -120,47 +120,50 @@ trait Primitives { self: ICodes =>
/** This class represents a test operation. */
- class TestOp {
+ sealed abstract class TestOp {
/** Returns the negation of this operation. */
- def negate(): TestOp = this match {
- case EQ => NE
- case NE => EQ
- case LT => GE
- case GE => LT
- case LE => GT
- case GT => LE
- case _ => throw new RuntimeException("TestOp unknown case")
- }
+ def negate(): TestOp
/** Returns a string representation of this operation. */
- override def toString(): String = this match {
- case EQ => "EQ"
- case NE => "NE"
- case LT => "LT"
- case GE => "GE"
- case LE => "LE"
- case GT => "GT"
- case _ => throw new RuntimeException("TestOp unknown case")
- }
+ override def toString(): String
}
+
/** An equality test */
- case object EQ extends TestOp
+ case object EQ extends TestOp {
+ def negate() = NE
+ override def toString() = "EQ"
+ }
/** A non-equality test */
- case object NE extends TestOp
+ case object NE extends TestOp {
+ def negate() = EQ
+ override def toString() = "NE"
+ }
/** A less-than test */
- case object LT extends TestOp
+ case object LT extends TestOp {
+ def negate() = GE
+ override def toString() = "LT"
+ }
/** A greater-than-or-equal test */
- case object GE extends TestOp
+ case object GE extends TestOp {
+ def negate() = LT
+ override def toString() = "GE"
+ }
/** A less-than-or-equal test */
- case object LE extends TestOp
+ case object LE extends TestOp {
+ def negate() = GT
+ override def toString() = "LE"
+ }
/** A greater-than test */
- case object GT extends TestOp
+ case object GT extends TestOp {
+ def negate() = LE
+ override def toString() = "GT"
+ }
/** This class represents an arithmetic operation. */
class ArithmeticOp {
diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
index a485272ca6..2ff0c1926c 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
@@ -74,22 +74,19 @@ trait TypeKinds { self: ICodes =>
case _ => false
}
- /** On the JVM, these types are like Ints for the
- * purposes of calculating the lub.
+ /** On the JVM,
+ * BOOL, BYTE, CHAR, SHORT, and INT
+ * are like Ints for the purposes of calculating the lub.
*/
- def isIntSizedType: Boolean = this match {
- case BOOL | CHAR | BYTE | SHORT | INT => true
- case _ => false
- }
- def isIntegralType: Boolean = this match {
- case BYTE | SHORT | INT | LONG | CHAR => true
- case _ => false
- }
- def isRealType: Boolean = this match {
- case FLOAT | DOUBLE => true
- case _ => false
- }
- def isNumericType: Boolean = isIntegralType | isRealType
+ def isIntSizedType: Boolean = false
+
+ /** On the JVM, similar to isIntSizedType except that BOOL isn't integral while LONG is. */
+ def isIntegralType: Boolean = false
+
+ /** On the JVM, FLOAT and DOUBLE. */
+ def isRealType: Boolean = false
+
+ final def isNumericType: Boolean = isIntegralType | isRealType
/** Simple subtyping check */
def <:<(other: TypeKind): Boolean = (this eq other) || (this match {
@@ -97,11 +94,8 @@ trait TypeKinds { self: ICodes =>
case _ => this eq other
})
- /** Is this type a category 2 type in JVM terms? */
- def isWideType: Boolean = this match {
- case DOUBLE | LONG => true
- case _ => false
- }
+ /** Is this type a category 2 type in JVM terms? (ie, is it LONG or DOUBLE?) */
+ def isWideType: Boolean = false
/** The number of dimensions for array types. */
def dimensions: Int = 0
@@ -145,7 +139,7 @@ trait TypeKinds { self: ICodes =>
* Here we make the adjustment by rewinding to a pre-erasure state and
* sifting through the parents for a class type.
*/
- def lub0(tk1: TypeKind, tk2: TypeKind): Type = atPhase(currentRun.uncurryPhase) {
+ def lub0(tk1: TypeKind, tk2: TypeKind): Type = beforeUncurry {
import definitions._
val tp = global.lub(List(tk1.toType, tk2.toType))
val (front, rest) = tp.parents span (_.typeSymbol.hasTraitFlag)
@@ -182,6 +176,7 @@ trait TypeKinds { self: ICodes =>
/** A boolean value */
case object BOOL extends ValueTypeKind {
+ override def isIntSizedType = true
def maxType(other: TypeKind) = other match {
case BOOL | REFERENCE(NothingClass) => BOOL
case _ => uncomparable(other)
@@ -195,6 +190,8 @@ trait TypeKinds { self: ICodes =>
/** A 1-byte signed integer */
case object BYTE extends ValueTypeKind {
+ override def isIntSizedType = true
+ override def isIntegralType = true
def maxType(other: TypeKind) = {
if (other == BYTE || other.isNothingType) BYTE
else if (other == CHAR) INT
@@ -205,6 +202,8 @@ trait TypeKinds { self: ICodes =>
/** A 2-byte signed integer */
case object SHORT extends ValueTypeKind {
+ override def isIntSizedType = true
+ override def isIntegralType = true
override def maxType(other: TypeKind) = other match {
case BYTE | SHORT | REFERENCE(NothingClass) => SHORT
case CHAR => INT
@@ -215,6 +214,8 @@ trait TypeKinds { self: ICodes =>
/** A 2-byte UNSIGNED integer */
case object CHAR extends ValueTypeKind {
+ override def isIntSizedType = true
+ override def isIntegralType = true
override def maxType(other: TypeKind) = other match {
case CHAR | REFERENCE(NothingClass) => CHAR
case BYTE | SHORT => INT
@@ -225,6 +226,8 @@ trait TypeKinds { self: ICodes =>
/** A 4-byte signed integer */
case object INT extends ValueTypeKind {
+ override def isIntSizedType = true
+ override def isIntegralType = true
override def maxType(other: TypeKind) = other match {
case BYTE | SHORT | CHAR | INT | REFERENCE(NothingClass) => INT
case LONG | FLOAT | DOUBLE => other
@@ -234,6 +237,8 @@ trait TypeKinds { self: ICodes =>
/** An 8-byte signed integer */
case object LONG extends ValueTypeKind {
+ override def isIntegralType = true
+ override def isWideType = true
override def maxType(other: TypeKind): TypeKind =
if (other.isIntegralType || other.isNothingType) LONG
else if (other.isRealType) DOUBLE
@@ -242,6 +247,7 @@ trait TypeKinds { self: ICodes =>
/** A 4-byte floating point number */
case object FLOAT extends ValueTypeKind {
+ override def isRealType = true
override def maxType(other: TypeKind): TypeKind =
if (other == DOUBLE) DOUBLE
else if (other.isNumericType || other.isNothingType) FLOAT
@@ -250,6 +256,8 @@ trait TypeKinds { self: ICodes =>
/** An 8-byte floating point number */
case object DOUBLE extends ValueTypeKind {
+ override def isRealType = true
+ override def isWideType = true
override def maxType(other: TypeKind): TypeKind =
if (other.isNumericType || other.isNothingType) DOUBLE
else uncomparable(other)
diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala
index ba4b250303..8a2ec9a191 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/TypeStacks.scala
@@ -21,7 +21,7 @@ trait TypeStacks {
* stack of the ICode.
*/
type Rep = List[TypeKind]
-
+
object NoTypeStack extends TypeStack(Nil) { }
class TypeStack(var types: Rep) {
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala
index 60cb679782..9f43e1b84c 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/DataFlowAnalysis.scala
@@ -60,20 +60,17 @@ trait DataFlowAnalysis[L <: SemiLattice] {
val output = f(point, in(point))
if ((lattice.bottom == out(point)) || output != out(point)) {
-// Console.println("Output changed at " + point
-// + " from: " + out(point) + " to: " + output
-// + " for input: " + in(point) + " and they are different: " + (output != out(point)))
+ // Console.println("Output changed at " + point
+ // + " from: " + out(point) + " to: " + output
+ // + " for input: " + in(point) + " and they are different: " + (output != out(point)))
out(point) = output
val succs = point.successors
succs foreach { p =>
- if (!worklist(p))
- worklist += p;
- if (!in.isDefinedAt(p))
- assert(false, "Invalid successor for: " + point + " successor " + p + " does not exist")
-// if (!p.exceptionHandlerHeader) {
-// println("lubbing " + p.predecessors + " outs: " + p.predecessors.map(out.apply).mkString("\n", "\n", ""))
- in(p) = lattice.lub(in(p) :: (p.predecessors map out.apply), p.exceptionHandlerStart)
-// }
+ val updated = lattice.lub(in(p) :: (p.predecessors map out.apply), p.exceptionHandlerStart)
+ if(updated != in(p)) {
+ in(p) = updated
+ if (!worklist(p)) { worklist += p; }
+ }
}
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala
index c06bd2e097..69de0dfa90 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala
@@ -105,11 +105,9 @@ abstract class ReachingDefinitions {
def genAndKill(b: BasicBlock): (ListSet[Definition], ListSet[Local]) = {
var genSet = ListSet[Definition]()
var killSet = ListSet[Local]()
- for ((i, idx) <- b.toList.zipWithIndex) i match {
- case STORE_LOCAL(local) =>
- killSet = killSet + local
- genSet = updateReachingDefinition(b, idx, genSet)
- case _ => ()
+ for ((STORE_LOCAL(local), idx) <- b.toList.zipWithIndex) {
+ killSet = killSet + local
+ genSet = updateReachingDefinition(b, idx, genSet)
}
(genSet, killSet)
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
index 6421d6c8ef..877c51ebc1 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala
@@ -127,34 +127,6 @@ abstract class TypeFlowAnalysis {
}
}
- /** reinitialize the analysis, keeping around solutions from a previous run. */
- def reinit(m: icodes.IMethod) {
- if (this.method == null || this.method.symbol != m.symbol)
- init(m)
- else reinit {
- m foreachBlock { b =>
- if (!in.contains(b)) {
- for (p <- b.predecessors) {
- if (out.isDefinedAt(p)) {
- in(b) = out(p)
- worklist += p
- }
- /* else
- in(b) = typeFlowLattice.bottom
- */ }
- out(b) = typeFlowLattice.bottom
- }
- }
- for (handler <- m.exh) {
- val start = handler.startBlock
- if (!in.contains(start)) {
- worklist += start
- in(start) = lattice.IState(in(start).vars, typeStackLattice.exceptionHandlerStack)
- }
- }
- }
- }
-
def this(m: icodes.IMethod) {
this()
init(m)
@@ -162,7 +134,7 @@ abstract class TypeFlowAnalysis {
def run = {
timer.start
-// icodes.lubs0 = 0
+ // icodes.lubs0 = 0
forwardAnalysis(blockTransfer)
val t = timer.stop
if (settings.debug.value) {
@@ -170,216 +142,35 @@ abstract class TypeFlowAnalysis {
assert(visited.contains(b),
"Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited));
}
-// log("" + method.symbol.fullName + " [" + method.code.blocks.size + " blocks] "
-// + "\n\t" + iterations + " iterations: " + t + " ms."
-// + "\n\tlubs: " + typeFlowLattice.lubs + " out of which " + icodes.lubs0 + " typer lubs")
+ // log("" + method.symbol.fullName + " [" + method.code.blocks.size + " blocks] "
+ // + "\n\t" + iterations + " iterations: " + t + " ms."
+ // + "\n\tlubs: " + typeFlowLattice.lubs + " out of which " + icodes.lubs0 + " typer lubs")
}
def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = {
- b.iterator.foldLeft(in)(interpret)
- }
- /** The flow function of a given basic block. */
- /* var flowFun: immutable.Map[BasicBlock, TransferFunction] = new immutable.HashMap */
-
- /** Fill flowFun with a transfer function per basic block. */
-/*
- private def buildFlowFunctions(blocks: List[BasicBlock]) {
- def transfer(b: BasicBlock): TransferFunction = {
- var gens: List[Gen] = Nil
- var consumed: Int = 0
- val stack = new SimulatedStack
-
- for (instr <- b) instr match {
- case THIS(clasz) =>
- stack push toTypeKind(clasz.tpe)
-
- case CONSTANT(const) =>
- stack push toTypeKind(const.tpe)
-
- case LOAD_ARRAY_ITEM(kind) =>
- stack.pop2
- stack.push(kind)
-
- case LOAD_LOCAL(local) =>
- val t = bindings(local)
- stack push (if (t == typeLattice.bottom) local.kind else t)
-
- case LOAD_FIELD(field, isStatic) =>
- if (!isStatic)
- stack.pop
- stack push toTypeKind(field.tpe)
-
- case LOAD_MODULE(module) =>
- stack push toTypeKind(module.tpe)
-
- case STORE_ARRAY_ITEM(kind) =>
- stack.pop3
-
- case STORE_LOCAL(local) =>
- val t = stack.pop
- bindings += (local -> t)
-
- case STORE_THIS(_) =>
- stack.pop
-
- case STORE_FIELD(field, isStatic) =>
- if (isStatic)
- stack.pop
- else
- stack.pop2
-
- case CALL_PRIMITIVE(primitive) =>
- primitive match {
- case Negation(kind) =>
- stack.pop; stack.push(kind)
- case Test(_, kind, zero) =>
- stack.pop
- if (!zero) stack.pop
- stack push BOOL;
- case Comparison(_, _) =>
- stack.pop2
- stack push INT
-
- case Arithmetic(op, kind) =>
- stack.pop
- if (op != NOT)
- stack.pop
- val k = kind match {
- case BYTE | SHORT | CHAR => INT
- case _ => kind
- }
- stack push k
-
- case Logical(op, kind) =>
- stack.pop2
- stack push kind
-
- case Shift(op, kind) =>
- stack.pop2
- stack push kind
-
- case Conversion(src, dst) =>
- stack.pop
- stack push dst
-
- case ArrayLength(kind) =>
- stack.pop
- stack push INT
-
- case StartConcat =>
- stack.push(ConcatClass)
-
- case EndConcat =>
- stack.pop
- stack.push(STRING)
-
- case StringConcat(el) =>
- stack.pop2
- stack push ConcatClass
- }
-
- case CALL_METHOD(method, style) => style match {
- case Dynamic =>
- stack.pop(1 + method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
-
- case Static(onInstance) =>
- if (onInstance) {
- stack.pop(1 + method.info.paramTypes.length)
- if (!method.isConstructor)
- stack.push(toTypeKind(method.info.resultType));
- } else {
- stack.pop(method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
- }
-
- case SuperCall(mix) =>
- stack.pop(1 + method.info.paramTypes.length)
- stack.push(toTypeKind(method.info.resultType))
- }
-
- case BOX(kind) =>
- stack.pop
- stack.push(BOXED(kind))
-
- case UNBOX(kind) =>
- stack.pop
- stack.push(kind)
-
- case NEW(kind) =>
- stack.push(kind)
-
- case CREATE_ARRAY(elem, dims) =>
- stack.pop(dims)
- stack.push(ARRAY(elem))
-
- case IS_INSTANCE(tpe) =>
- stack.pop
- stack.push(BOOL)
-
- case CHECK_CAST(tpe) =>
- stack.pop
- stack.push(tpe)
-
- case SWITCH(tags, labels) =>
- stack.pop
-
- case JUMP(whereto) =>
- ()
-
- case CJUMP(success, failure, cond, kind) =>
- stack.pop2
-
- case CZJUMP(success, failure, cond, kind) =>
- stack.pop
-
- case RETURN(kind) =>
- if (kind != UNIT)
- stack.pop;
-
- case THROW() =>
- stack.pop
-
- case DROP(kind) =>
- stack.pop
-
- case DUP(kind) =>
- stack.push(stack.head)
-
- case MONITOR_ENTER() =>
- stack.pop
-
- case MONITOR_EXIT() =>
- stack.pop
-
- case SCOPE_ENTER(_) | SCOPE_EXIT(_) =>
- ()
-
- case LOAD_EXCEPTION(_) =>
- stack.pop(stack.length)
- stack.push(typeLattice.Object)
-
- case _ =>
- dumpClassesAndAbort("Unknown instruction: " + i)
- }
-
- new TransferFunction(consumed, gens)
- }
-
- for (b <- blocks) {
- flowFun = flowFun + (b -> transfer(b))
+ var result = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack))
+ var instrs = b.toList
+ while(!instrs.isEmpty) {
+ val i = instrs.head
+ result = mutatingInterpret(result, i)
+ instrs = instrs.tail
}
+ result
}
-*/
+
/** Abstract interpretation for one instruction. */
def interpret(in: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = {
val out = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack))
+ mutatingInterpret(out, i)
+ }
+
+ def mutatingInterpret(out: typeFlowLattice.Elem, i: Instruction): typeFlowLattice.Elem = {
val bindings = out.vars
val stack = out.stack
if (settings.debug.value) {
-// Console.println("[before] Stack: " + stack);
-// Console.println(i);
+ // Console.println("[before] Stack: " + stack);
+ // Console.println(i);
}
i match {
@@ -619,11 +410,292 @@ abstract class TypeFlowAnalysis {
}
}
+ case class CallsiteInfo(bb: icodes.BasicBlock, receiver: Symbol, stackLength: Int, concreteMethod: Symbol)
+
+ /**
+
+ A full type-flow analysis on a method computes in- and out-flows for each basic block (that's what MethodTFA does).
+
+ For the purposes of Inliner, doing so guarantees that an abstract typestack-slot is available by the time an inlining candidate (a CALL_METHOD instruction) is visited.
+ This subclass (MTFAGrowable) of MethodTFA also aims at performing such analysis on CALL_METHOD instructions, with some differences:
+
+ (a) early screening is performed while the type-flow is being computed (in an override of `blockTransfer`) by testing a subset of the conditions that Inliner checks later.
+ The reasoning here is: if the early check fails at some iteration, there's no chance a follow-up iteration (with a yet more lub-ed typestack-slot) will succeed.
+ Failure is sufficient to remove that particular CALL_METHOD from the typeflow's `remainingCALLs`.
+ A forward note: in case inlining occurs at some basic block B, all blocks reachable from B get their CALL_METHOD instructions considered again as candidates
+ (because of the more precise types that -- perhaps -- can be computed).
+
+ (b) in case the early check does not fail, no conclusive decision can be made, thus the CALL_METHOD stays `isOnwatchlist`.
+
+ In other words, `remainingCALLs` tracks those callsites that still remain as candidates for inlining, so that Inliner can focus on those.
+ `remainingCALLs` also caches info about the typestack just before the callsite, so as to spare computing them again at inlining time.
+
+ Besides caching, a further optimization involves skipping those basic blocks whose in-flow and out-flow isn't needed anyway (as explained next).
+ A basic block lacking a callsite in `remainingCALLs`, when visisted by the standard algorithm, won't cause any inlining.
+ But as we know from the way type-flows are computed, computing the in- and out-flow for a basic block relies in general on those of other basic blocks.
+ In detail, we want to focus on that sub-graph of the CFG such that control flow may reach a remaining candidate callsite.
+ Those basic blocks not in that subgraph can be skipped altogether. That's why:
+ - `forwardAnalysis()` in `MTFAGrowable` now checks for inclusion of a basic block in `relevantBBs`
+ - same check is performed before adding a block to the worklist, and as part of choosing successors.
+ The bookkeeping supporting on-the-fly pruning of irrelevant blocks requires overridding most methods of the dataflow-analysis.
+
+ The rest of the story takes place in Inliner, which does not visit all of the method's basic blocks but only on those represented in `remainingCALLs`.
+
+ @author Miguel Garcia, http://lampwww.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/
+
+ */
class MTFAGrowable extends MethodTFA {
import icodes._
- /** discards what must be discarded, blanks what needs to be blanked out, and keeps the rest. */
+ val remainingCALLs = mutable.Map.empty[opcodes.CALL_METHOD, CallsiteInfo]
+
+ val preCandidates = mutable.Set.empty[BasicBlock]
+
+ var callerLin: Traversable[BasicBlock] = null
+
+ override def run {
+
+ timer.start
+ forwardAnalysis(blockTransfer)
+ val t = timer.stop
+
+ /* Now that `forwardAnalysis(blockTransfer)` has finished, all inlining candidates can be found in `remainingCALLs`,
+ whose keys are callsites and whose values are pieces of information about the typestack just before the callsite in question.
+ In order to keep `analyzeMethod()` simple, we collect in `preCandidates` those basic blocks containing at least one candidate. */
+ preCandidates.clear()
+ for(rc <- remainingCALLs) {
+ preCandidates += rc._2.bb
+ }
+
+ if (settings.debug.value) {
+ for(b <- callerLin; if (b != method.startBlock) && preCandidates(b)) {
+ assert(visited.contains(b),
+ "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited)
+ }
+ }
+
+ }
+
+ var shrinkedWatchlist = false
+
+ /*
+ This is the method where information cached elsewhere is put to use. References are given those other places that populate those caches.
+
+ The goal is avoiding computing type-flows for blocks we don't need (ie blocks not tracked in `relevantBBs`). The method used to add to `relevantBBs` is `putOnRadar`.
+
+ Moreover, it's often the case that the last CALL_METHOD of interest ("of interest" equates to "being tracked in `isOnWatchlist`) isn't the last instruction on the block.
+ There are cases where the typeflows computed past this `lastInstruction` are needed, and cases when they aren't.
+ The reasoning behind this decsision is described in `populatePerimeter()`. All `blockTransfer()` needs to do (in order to know at which instruction it can stop)
+ is querying `isOnPerimeter`.
+
+ Upon visiting a CALL_METHOD that's an inlining candidate, the relevant pieces of information about the pre-instruction typestack are collected for future use.
+ That is, unless the candidacy test fails. The reasoning here is: if such early check fails at some iteration, there's no chance a follow-up iteration
+ (with a yet more lub-ed typestack-slot) will succeed. In case of failure we can safely remove the CALL_METHOD from both `isOnWatchlist` and `remainingCALLs`.
+
+ */
+ override def blockTransfer(b: BasicBlock, in: lattice.Elem): lattice.Elem = {
+ var result = lattice.IState(new VarBinding(in.vars), new TypeStack(in.stack))
+
+ val stopAt = if(isOnPerimeter(b)) lastInstruction(b) else null;
+ var isPastLast = false
+
+ var instrs = b.toList
+ while(!isPastLast && !instrs.isEmpty) {
+ val i = instrs.head
+
+ if(isOnWatchlist(i)) {
+ val cm = i.asInstanceOf[opcodes.CALL_METHOD]
+ val msym = cm.method
+ val paramsLength = msym.info.paramTypes.size
+ val receiver = result.stack.types.drop(paramsLength).head match {
+ case REFERENCE(s) => s
+ case _ => NoSymbol // e.g. the scrutinee is BOX(s) or ARRAY
+ }
+ val concreteMethod = inliner.lookupImplFor(msym, receiver)
+ val isCandidate = {
+ ( inliner.isClosureClass(receiver) || concreteMethod.isEffectivelyFinal || receiver.isEffectivelyFinal ) &&
+ !blackballed(concreteMethod)
+ }
+ if(isCandidate) {
+ remainingCALLs += Pair(cm, CallsiteInfo(b, receiver, result.stack.length, concreteMethod))
+ } else {
+ remainingCALLs.remove(cm)
+ isOnWatchlist.remove(cm)
+ shrinkedWatchlist = true
+ }
+ }
+
+ isPastLast = (i eq stopAt)
+
+ if(!isPastLast) {
+ result = mutatingInterpret(result, i)
+ instrs = instrs.tail
+ }
+ }
+
+ result
+ } // end of method blockTransfer
+
+ val isOnWatchlist = mutable.Set.empty[Instruction]
+
+ /* Each time CallerCalleeInfo.isSafeToInline determines a concrete callee is unsafe to inline in the current caller,
+ the fact is recorded in this TFA instance for the purpose of avoiding devoting processing to that callsite next time.
+ The condition of "being unsafe to inline in the current caller" sticks across inlinings and TFA re-inits
+ because it depends on the instructions of the callee, which stay unchanged during the course of `analyzeInc(caller)`
+ (with the caveat of the side-effecting `makePublic` in `helperIsSafeToInline`).*/
+ val knownUnsafe = mutable.Set.empty[Symbol]
+ val knownSafe = mutable.Set.empty[Symbol]
+ val knownNever = mutable.Set.empty[Symbol] // `knownNever` needs be cleared only at the very end of the inlining phase (unlike `knownUnsafe` and `knownSafe`)
+ @inline final def blackballed(msym: Symbol): Boolean = { knownUnsafe(msym) || knownNever(msym) }
+
+ val relevantBBs = mutable.Set.empty[BasicBlock]
+
+ private def isPreCandidate(cm: opcodes.CALL_METHOD): Boolean = {
+ val msym = cm.method
+ val style = cm.style
+ // Dynamic == normal invocations
+ // Static(true) == calls to private members
+ !msym.isConstructor && !blackballed(msym) &&
+ (style.isDynamic || (style.hasInstance && style.isStatic))
+ // && !(msym hasAnnotation definitions.ScalaNoInlineClass)
+ }
+
+ override def init(m: icodes.IMethod) {
+ super.init(m)
+ remainingCALLs.clear()
+ knownUnsafe.clear()
+ knownSafe.clear()
+ // initially populate the watchlist with all callsites standing a chance of being inlined
+ isOnWatchlist.clear()
+ relevantBBs.clear()
+ /* TODO Do we want to perform inlining in non-finally exception handlers?
+ * Seems counterproductive (the larger the method the less likely it will be JITed.
+ * It's not that putting on radar only `linearizer linearizeAt (m, m.startBlock)` makes for much shorter inlining times (a minor speedup nonetheless)
+ * but the effect on method size could be explored. */
+ putOnRadar(m.linearizedBlocks(linearizer))
+ populatePerimeter()
+ assert(relevantBBs.isEmpty || relevantBBs.contains(m.startBlock), "you gave me dead code")
+ }
+
+ def conclusives(b: BasicBlock): List[opcodes.CALL_METHOD] = {
+ knownBeforehand(b) filter { cm => inliner.isMonadicMethod(cm.method) || inliner.hasInline(cm.method) }
+ }
+
+ def knownBeforehand(b: BasicBlock): List[opcodes.CALL_METHOD] = {
+ b.toList collect { case c : opcodes.CALL_METHOD => c } filter { cm => isPreCandidate(cm) && isReceiverKnown(cm) }
+ }
+
+ private def isReceiverKnown(cm: opcodes.CALL_METHOD): Boolean = {
+ cm.method.isEffectivelyFinal && cm.method.owner.isEffectivelyFinal
+ }
+
+ private def putOnRadar(blocks: Traversable[BasicBlock]) {
+ for(bb <- blocks) {
+ val preCands = bb.toList collect {
+ case cm : opcodes.CALL_METHOD
+ if isPreCandidate(cm) /* && !isReceiverKnown(cm) */
+ => cm
+ }
+ isOnWatchlist ++= preCands
+ }
+ relevantBBs ++= blocks
+ }
+
+ /* the argument is also included in the result */
+ private def transitivePreds(b: BasicBlock): Set[BasicBlock] = { transitivePreds(List(b)) }
+
+ /* those BBs in the argument are also included in the result */
+ private def transitivePreds(starters: Traversable[BasicBlock]): Set[BasicBlock] = {
+ val result = mutable.Set.empty[BasicBlock]
+ var toVisit: List[BasicBlock] = starters.toList.distinct
+ while(toVisit.nonEmpty) {
+ val h = toVisit.head
+ toVisit = toVisit.tail
+ result += h
+ for(p <- h.predecessors; if !result(p) && !toVisit.contains(p)) { toVisit = p :: toVisit }
+ }
+ result.toSet
+ }
+
+ /* those BBs in the argument are also included in the result */
+ private def transitiveSuccs(starters: Traversable[BasicBlock]): Set[BasicBlock] = {
+ val result = mutable.Set.empty[BasicBlock]
+ var toVisit: List[BasicBlock] = starters.toList.distinct
+ while(toVisit.nonEmpty) {
+ val h = toVisit.head
+ toVisit = toVisit.tail
+ result += h
+ for(p <- h.successors; if !result(p) && !toVisit.contains(p)) { toVisit = p :: toVisit }
+ }
+ result.toSet
+ }
+
+ /* A basic block B is "on the perimeter" of the current control-flow subgraph if none of its successors belongs to that subgraph.
+ * In that case, for the purposes of inlining, we're interested in the typestack right before the last inline candidate in B, not in those afterwards.
+ * In particular we can do without computing the outflow at B. */
+ private def populatePerimeter() {
+ isOnPerimeter.clear()
+ var done = true
+ do {
+ val (frontier, toPrune) = (relevantBBs filter hasNoRelevantSuccs) partition isWatching
+ isOnPerimeter ++= frontier
+ relevantBBs --= toPrune
+ done = toPrune.isEmpty
+ } while(!done)
+
+ lastInstruction.clear()
+ for(b <- isOnPerimeter; val lastIns = b.toList.reverse find isOnWatchlist) {
+ lastInstruction += (b -> lastIns.get.asInstanceOf[opcodes.CALL_METHOD])
+ }
+
+ // assertion: "no relevant block can have a predecessor that is on perimeter"
+ assert((for (b <- relevantBBs; if transitivePreds(b.predecessors) exists isOnPerimeter) yield b).isEmpty)
+ }
+
+ private val isOnPerimeter = mutable.Set.empty[BasicBlock]
+ private val lastInstruction = mutable.Map.empty[BasicBlock, opcodes.CALL_METHOD]
+
+ def hasNoRelevantSuccs(x: BasicBlock): Boolean = { !(x.successors exists relevantBBs) }
+
+ def isWatching(x: BasicBlock): Boolean = (x.toList exists isOnWatchlist)
+
+
+
+
+ /**
+
+ This method is invoked after one or more inlinings have been performed in basic blocks whose in-flow is non-bottom (this makes a difference later).
+ What we know about those inlinings is given by:
+
+ - `staleOut`: These are the blocks where a callsite was inlined.
+ For each callsite, all instructions in that block before the callsite were left in the block, and the rest moved to an `afterBlock`.
+ The out-flow of these basic blocks is thus in general stale, that's why we'll add them to the TFA worklist.
+
+ - `inlined` : These blocks were spliced into the method's CFG as part of inlining. Being new blocks, they haven't been visited yet by the typeflow analysis.
+
+ - `staleIn` : These blocks are what `doInline()` calls `afterBlock`s, ie the new home for instructions that previously appearead
+ after a callsite in a `staleOut` block.
+
+ Based on the above information, we have to bring up-to-date the caches that `forwardAnalysis` and `blockTransfer` use to skip blocks and instructions.
+ Those caches are `relevantBBs` and `isOnPerimeter` (for blocks) and `isOnWatchlist` and `lastInstruction` (for CALL_METHODs).
+ Please notice that all `inlined` and `staleIn` blocks are reachable from `staleOut` blocks.
+
+ The update takes place in two steps:
+
+ (1) `staleOut foreach { so => putOnRadar(linearizer linearizeAt (m, so)) }`
+ This results in initial populations for `relevantBBs` and `isOnWatchlist`.
+ Because of the way `isPreCandidate` reuses previous decision-outcomes that are still valid,
+ this already prunes some candidates standing no chance of being inlined.
+
+ (2) `populatePerimeter()`
+ Based on the CFG-subgraph determined in (1) as reflected in `relevantBBs`,
+ this method detects some blocks whose typeflows aren't needed past a certain CALL_METHOD
+ (not needed because none of its successors is relevant for the purposes of inlining, see `hasNoRelevantSuccs`).
+ The blocks thus chosen are said to be "on the perimeter" of the CFG-subgraph.
+ For each of them, its `lastInstruction` (after which no more typeflows are needed) is found.
+
+ */
def reinit(m: icodes.IMethod, staleOut: List[BasicBlock], inlined: collection.Set[BasicBlock], staleIn: collection.Set[BasicBlock]) {
if (this.method == null || this.method.symbol != m.symbol) {
init(m)
@@ -633,31 +705,102 @@ abstract class TypeFlowAnalysis {
return;
}
- reinit {
- // asserts conveying an idea what CFG shapes arrive here.
- // staleIn foreach (p => assert( !in.isDefinedAt(p), p))
- // staleIn foreach (p => assert(!out.isDefinedAt(p), p))
- // inlined foreach (p => assert( !in.isDefinedAt(p), p))
- // inlined foreach (p => assert(!out.isDefinedAt(p), p))
- // inlined foreach (p => assert(!p.successors.isEmpty || p.lastInstruction.isInstanceOf[icodes.opcodes.THROW], p))
- // staleOut foreach (p => assert( in.isDefinedAt(p), p))
-
- // never rewrite in(m.startBlock)
- staleOut foreach { b =>
- if(!inlined.contains(b)) { worklist += b }
- out(b) = typeFlowLattice.bottom
- }
- // nothing else is added to the worklist, bb's reachable via succs will be tfa'ed
- blankOut(inlined)
- blankOut(staleIn)
- // no need to add startBlocks from m.exh
+ worklist.clear // calling reinit(f: => Unit) would also clear visited, thus forgetting about blocks visited before reinit.
+
+ // asserts conveying an idea what CFG shapes arrive here:
+ // staleIn foreach (p => assert( !in.isDefinedAt(p), p))
+ // staleIn foreach (p => assert(!out.isDefinedAt(p), p))
+ // inlined foreach (p => assert( !in.isDefinedAt(p), p))
+ // inlined foreach (p => assert(!out.isDefinedAt(p), p))
+ // inlined foreach (p => assert(!p.successors.isEmpty || p.lastInstruction.isInstanceOf[icodes.opcodes.THROW], p))
+ // staleOut foreach (p => assert( in.isDefinedAt(p), p))
+
+ // remainingCALLs.clear()
+ isOnWatchlist.clear()
+ relevantBBs.clear()
+
+ // never rewrite in(m.startBlock)
+ staleOut foreach { b =>
+ enqueue(b)
+ out(b) = typeFlowLattice.bottom
}
+ // nothing else is added to the worklist, bb's reachable via succs will be tfa'ed
+ blankOut(inlined)
+ blankOut(staleIn)
+ // no need to add startBlocks from m.exh
+
+ staleOut foreach { so => putOnRadar(linearizer linearizeAt (m, so)) }
+ populatePerimeter()
+
+ } // end of method reinit
+
+ /* this is not a general purpose method to add to the worklist,
+ * because the assert is expected to hold only when called from MTFAGrowable.reinit() */
+ private def enqueue(b: BasicBlock) {
+ assert(in(b) ne typeFlowLattice.bottom)
+ if(!worklist.contains(b)) { worklist += b }
+ }
+
+ /* this is not a general purpose method to add to the worklist,
+ * because the assert is expected to hold only when called from MTFAGrowable.reinit() */
+ private def enqueue(bs: Traversable[BasicBlock]) {
+ bs foreach enqueue
}
private def blankOut(blocks: collection.Set[BasicBlock]) {
blocks foreach { b =>
- in(b) = typeFlowLattice.bottom
- out(b) = typeFlowLattice.bottom
+ in(b) = typeFlowLattice.bottom
+ out(b) = typeFlowLattice.bottom
+ }
+ }
+
+ /*
+ This is basically the plain-old forward-analysis part of a dataflow algorithm,
+ adapted to skip non-relevant blocks (as determined by `reinit()` via `populatePerimeter()`).
+
+ The adaptations are:
+
+ - only relevant blocks dequeued from the worklist move on to have the transfer function applied
+
+ - `visited` now means the transfer function was applied to the block,
+ but please notice that this does not imply anymore its out-flow to be different from bottom,
+ because a block on the perimeter will have per-instruction typeflows computed only up to its `lastInstruction`.
+ In case you need to know whether a visted block `v` has been "fully visited", evaluate `out(v) ne typeflowLattice.bottom`
+
+ - given that the transfer function may remove callsite-candidates from the watchlist (thus, they are not candidates anymore)
+ there's an opportunity to detect whether a previously relevant block has been left without candidates.
+ That's what `shrinkedWatchlist` detects. Provided the block was on the perimeter, we know we can skip it from now now,
+ and we can also constrain the CFG-subgraph by finding a new perimeter (thus the invocation to `populatePerimeter()`).
+ */
+ override def forwardAnalysis(f: (P, lattice.Elem) => lattice.Elem): Unit = {
+ while (!worklist.isEmpty && relevantBBs.nonEmpty) {
+ if (stat) iterations += 1
+ val point = worklist.iterator.next; worklist -= point;
+ if(relevantBBs(point)) {
+ shrinkedWatchlist = false
+ val output = f(point, in(point))
+ visited += point;
+ if(isOnPerimeter(point)) {
+ if(shrinkedWatchlist && !isWatching(point)) {
+ relevantBBs -= point;
+ populatePerimeter()
+ }
+ } else {
+ val propagate = ((lattice.bottom == out(point)) || output != out(point))
+ if (propagate) {
+ out(point) = output
+ val succs = point.successors filter relevantBBs
+ succs foreach { p =>
+ assert((p.predecessors filter isOnPerimeter).isEmpty)
+ val updated = lattice.lub(List(output, in(p)), p.exceptionHandlerStart)
+ if(updated != in(p)) {
+ in(p) = updated
+ enqueue(p)
+ }
+ }
+ }
+ }
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
index 865bacffaa..c217869a48 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala
@@ -23,9 +23,7 @@ trait BytecodeWriters {
import global._
private def outputDirectory(sym: Symbol): AbstractFile = (
- settings.outputDirs.outputDirFor {
- atPhase(currentRun.flattenPhase.prev)(sym.sourceFile)
- }
+ settings.outputDirs.outputDirFor(beforeFlatten(sym.sourceFile))
)
private def getFile(base: AbstractFile, cls: JClass, suffix: String): AbstractFile = {
var dir = base
@@ -85,7 +83,7 @@ trait BytecodeWriters {
emitJavap(bytes, javapFile)
}
}
-
+
trait ClassBytecodeWriter extends BytecodeWriter {
def writeClass(label: String, jclass: JClass, sym: Symbol) {
val outfile = getFile(sym, jclass, ".class")
@@ -96,18 +94,18 @@ trait BytecodeWriters {
informProgress("wrote '" + label + "' to " + outfile)
}
}
-
+
trait DumpBytecodeWriter extends BytecodeWriter {
val baseDir = Directory(settings.Ydumpclasses.value).createDirectory()
-
+
abstract override def writeClass(label: String, jclass: JClass, sym: Symbol) {
super.writeClass(label, jclass, sym)
-
+
val pathName = jclass.getName()
var dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _) changeExtension "class" toFile;
dumpFile.parent.createDirectory()
val outstream = new DataOutputStream(new FileOutputStream(dumpFile.path))
-
+
try jclass writeTo outstream
finally outstream.close()
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index b5232fff09..c609f126d3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -37,21 +37,19 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
/** Create a new phase */
override def newPhase(p: Phase): Phase = new JvmPhase(p)
- private def outputDirectory(sym: Symbol): AbstractFile = (
- settings.outputDirs.outputDirFor {
- atPhase(currentRun.flattenPhase.prev)(sym.sourceFile)
- }
- )
- private def getFile(base: AbstractFile, cls: JClass, suffix: String): AbstractFile = {
+ private def outputDirectory(sym: Symbol): AbstractFile =
+ settings.outputDirs outputDirFor beforeFlatten(sym.sourceFile)
+
+ private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = {
var dir = base
- val pathParts = cls.getName().split("[./]").toList
+ val pathParts = clsName.split("[./]").toList
for (part <- pathParts.init) {
dir = dir.subdirectoryNamed(part)
}
dir.fileNamed(pathParts.last + suffix)
}
- private def getFile(sym: Symbol, cls: JClass, suffix: String): AbstractFile =
- getFile(outputDirectory(sym), cls, suffix)
+ private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile =
+ getFile(outputDirectory(sym), clsName, suffix)
/** JVM code generation phase
*/
@@ -87,7 +85,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
// succeed or warn that it isn't.
hasApproximate && {
// Before erasure so we can identify generic mains.
- atPhase(currentRun.erasurePhase) {
+ beforeErasure {
val companion = sym.linkedClassOfClass
val companionMain = companion.tpe.member(nme.main)
@@ -154,14 +152,14 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
if (settings.Ygenjavap.isDefault) {
if(settings.Ydumpclasses.isDefault)
new ClassBytecodeWriter { }
- else
+ else
new ClassBytecodeWriter with DumpBytecodeWriter { }
}
else new ClassBytecodeWriter with JavapBytecodeWriter { }
}
val codeGenerator = new BytecodeGenerator(bytecodeWriter)
- log("Created new bytecode generator for " + classes.size + " classes.")
+ debuglog("Created new bytecode generator for " + classes.size + " classes.")
sortedClasses foreach { c =>
try codeGenerator.genClass(c)
@@ -197,8 +195,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
val StringBuilderClassName = javaName(definitions.StringBuilderClass)
val BoxesRunTime = "scala.runtime.BoxesRunTime"
- val StringBuilderType = new JObjectType(StringBuilderClassName)
- val toStringType = new JMethodType(JAVA_LANG_STRING, JType.EMPTY_ARRAY)
+ val StringBuilderType = new JObjectType(StringBuilderClassName) // TODO use ASMType.getObjectType
+ val toStringType = new JMethodType(JAVA_LANG_STRING, JType.EMPTY_ARRAY) // TODO use ASMType.getMethodType
val arrayCloneType = new JMethodType(JAVA_LANG_OBJECT, JType.EMPTY_ARRAY)
val MethodTypeType = new JObjectType("java.dyn.MethodType")
val JavaLangClassType = new JObjectType("java.lang.Class")
@@ -209,17 +207,18 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
val BeanInfoSkipAttr = definitions.getRequiredClass("scala.beans.BeanInfoSkip")
val BeanDisplayNameAttr = definitions.getRequiredClass("scala.beans.BeanDisplayName")
val BeanDescriptionAttr = definitions.getRequiredClass("scala.beans.BeanDescription")
-
+
final val ExcludedForwarderFlags = {
import Flags._
- ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | BridgeAndPrivateFlags )
+ // Should include DEFERRED but this breaks findMember.
+ ( CASE | SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags )
}
// Additional interface parents based on annotations and other cues
- def newParentForAttr(attr: Symbol): Option[Type] = attr match {
- case SerializableAttr => Some(SerializableClass.tpe)
- case CloneableAttr => Some(JavaCloneableClass.tpe)
- case RemoteAttr => Some(RemoteInterfaceClass.tpe)
+ def newParentForAttr(attr: Symbol): Option[Symbol] = attr match {
+ case SerializableAttr => Some(SerializableClass)
+ case CloneableAttr => Some(JavaCloneableClass)
+ case RemoteAttr => Some(RemoteInterfaceClass)
case _ => None
}
@@ -232,11 +231,47 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
vp
}
+ private def helperBoxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = {
+ val boxedType = definitions.boxedClass(kind.toType.typeSymbol)
+ val mtype = new JMethodType(javaType(boxedType), Array(javaType(kind)))
+
+ Pair("boxTo" + boxedType.decodedName, mtype)
+ }
+
+ private val jBoxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map(
+ BOOL -> helperBoxTo(BOOL) ,
+ BYTE -> helperBoxTo(BYTE) ,
+ CHAR -> helperBoxTo(CHAR) ,
+ SHORT -> helperBoxTo(SHORT) ,
+ INT -> helperBoxTo(INT) ,
+ LONG -> helperBoxTo(LONG) ,
+ FLOAT -> helperBoxTo(FLOAT) ,
+ DOUBLE -> helperBoxTo(DOUBLE)
+ )
+
+ private def helperUnboxTo(kind: ValueTypeKind): Tuple2[String, JMethodType] = {
+ val mtype = new JMethodType(javaType(kind), Array(JAVA_LANG_OBJECT))
+ val mname = "unboxTo" + kind.toType.typeSymbol.decodedName
+
+ Pair(mname, mtype)
+ }
+
+ private val jUnboxTo: Map[TypeKind, Tuple2[String, JMethodType]] = Map(
+ BOOL -> helperUnboxTo(BOOL) ,
+ BYTE -> helperUnboxTo(BYTE) ,
+ CHAR -> helperUnboxTo(CHAR) ,
+ SHORT -> helperUnboxTo(SHORT) ,
+ INT -> helperUnboxTo(INT) ,
+ LONG -> helperUnboxTo(LONG) ,
+ FLOAT -> helperUnboxTo(FLOAT) ,
+ DOUBLE -> helperUnboxTo(DOUBLE)
+ )
+
var clasz: IClass = _
var method: IMethod = _
var jclass: JClass = _
var jmethod: JMethod = _
-// var jcode: JExtendedCode = _
+ // var jcode: JExtendedCode = _
def isParcelableClass = isAndroidParcelableClass(clasz.symbol)
def isRemoteClass = clasz.symbol hasAnnotation RemoteAttr
@@ -264,7 +299,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
private def innerClassSymbolFor(s: Symbol): Symbol =
if (s.isClass) s else if (s.isModule) s.moduleClass else NoSymbol
- override def javaName(sym: Symbol): String = {
+ override def javaName(sym: Symbol): String = { // TODO Miguel says: check whether a single pass over `icodes.classes` can populate `innerClassBuffer` faster.
/**
* Checks if given symbol corresponds to inner class/object and add it to innerClassBuffer
*
@@ -272,13 +307,16 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
* of inner class all until root class.
*/
def collectInnerClass(s: Symbol): Unit = {
- // TODO: something atPhase(currentRun.flattenPhase.prev) which accounts for
+ // TODO: some beforeFlatten { ... } which accounts for
// being nested in parameterized classes (if we're going to selectively flatten.)
val x = innerClassSymbolFor(s)
- val isInner = x.isClass && !x.rawowner.isPackageClass
- if (isInner) {
- innerClassBuffer += x
- collectInnerClass(x.rawowner)
+ if(x ne NoSymbol) {
+ assert(x.isClass, "not an inner-class symbol")
+ val isInner = !x.rawowner.isPackageClass
+ if (isInner) {
+ innerClassBuffer += x
+ collectInnerClass(x.rawowner)
+ }
}
}
collectInnerClass(sym)
@@ -340,38 +378,44 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
private var innerClassBuffer = mutable.LinkedHashSet[Symbol]()
- /** Drop redundant interfaces (ones which are implemented by some
- * other parent) from the immediate parents. This is important on
- * android because there is otherwise an interface explosion.
+ /** Drop redundant interfaces (ones which are implemented by some other parent) from the immediate parents.
+ * This is important on Android because there is otherwise an interface explosion.
*/
- private def minimizeInterfaces(interfaces: List[Symbol]): List[Symbol] = (
- interfaces filterNot (int1 =>
- interfaces exists (int2 =>
- (int1 ne int2) && (int2 isSubClass int1)
- )
- )
- )
+ private def minimizeInterfaces(interfaces: List[Symbol]): List[Symbol] = {
+ var rest = interfaces
+ var leaves = List.empty[Symbol]
+ while(!rest.isEmpty) {
+ val candidate = rest.head
+ val nonLeaf = leaves exists { lsym => lsym isSubClass candidate }
+ if(!nonLeaf) {
+ leaves = candidate :: (leaves filterNot { lsym => candidate isSubClass lsym })
+ }
+ rest = rest.tail
+ }
+
+ leaves
+ }
def genClass(c: IClass) {
clasz = c
innerClassBuffer.clear()
val name = javaName(c.symbol)
- val superClass :: superInterfaces = {
- val parents0 = c.symbol.info.parents match {
- case Nil => List(ObjectClass.tpe)
- case ps => ps
- }
- parents0 ++ c.symbol.annotations.flatMap(ann => newParentForAttr(ann.symbol)) distinct
- }
- val ifaces = superInterfaces match {
- case Nil => JClass.NO_INTERFACES
- case _ => mkArray(minimizeInterfaces(superInterfaces map (_.typeSymbol)) map javaName)
- }
+
+ val ps = c.symbol.info.parents
+
+ val superClass: Symbol = if(ps.isEmpty) ObjectClass else ps.head.typeSymbol;
+
+ val superInterfaces0: List[Symbol] = if(ps.isEmpty) Nil else c.symbol.mixinClasses;
+ val superInterfaces = superInterfaces0 ++ c.symbol.annotations.flatMap(ann => newParentForAttr(ann.symbol)) distinct
+
+ val ifaces =
+ if(superInterfaces.isEmpty) JClass.NO_INTERFACES
+ else mkArray(minimizeInterfaces(superInterfaces) map javaName)
jclass = fjbgContext.JClass(javaFlags(c.symbol),
name,
- javaName(superClass.typeSymbol),
+ javaName(superClass),
ifaces,
c.cunit.source.toString)
@@ -393,7 +437,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
// it must be a top level class (name contains no $s)
def isCandidateForForwarders(sym: Symbol): Boolean =
- atPhase(currentRun.picklerPhase.next) {
+ afterPickler {
!(sym.name.toString contains '$') && sym.hasModuleFlag && !sym.isImplClass && !sym.isNestedClass
}
@@ -433,7 +477,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
private def addEnclosingMethodAttribute(jclass: JClass, clazz: Symbol) {
val sym = clazz.originalEnclosingMethod
if (sym.isMethod) {
- log("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass))
+ debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass))
jclass addAttribute fjbgContext.JEnclosingMethodAttribute(
jclass,
javaName(sym.enclClass),
@@ -449,7 +493,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
enclClass, clazz)
)
else {
- log("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass))
+ debuglog("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass))
jclass addAttribute fjbgContext.JEnclosingMethodAttribute(
jclass,
javaName(enclClass),
@@ -681,7 +725,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
)
def addGenericSignature(jmember: JMember, sym: Symbol, owner: Symbol) {
if (needsGenericSignature(sym)) {
- val memberTpe = atPhase(currentRun.erasurePhase)(owner.thisType.memberInfo(sym))
+ val memberTpe = beforeErasure(owner.thisType.memberInfo(sym))
// println("addGenericSignature sym: " + sym.fullName + " : " + memberTpe + " sym.info: " + sym.info)
// println("addGenericSignature: "+ (sym.ownerChain map (x => (x.name, x.isImplClass))))
erasure.javaSig(sym, memberTpe) foreach { sig =>
@@ -700,7 +744,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
return
}
if ((settings.check.value contains "genjvm")) {
- val normalizedTpe = atPhase(currentRun.erasurePhase)(erasure.prepareSigMap(memberTpe))
+ val normalizedTpe = beforeErasure(erasure.prepareSigMap(memberTpe))
val bytecodeTpe = owner.thisType.memberInfo(sym)
if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym, normalizedTpe) =:= bytecodeTpe)) {
clasz.cunit.warning(sym.pos,
@@ -716,9 +760,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
}
val index = jmember.getConstantPool.addUtf8(sig).toShort
if (opt.verboseDebug)
- atPhase(currentRun.erasurePhase) {
- println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index)
- }
+ beforeErasure(println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index))
+
val buf = ByteBuffer.allocate(2)
buf putShort index
addAttribute(jmember, tpnme.SignatureATTR, buf)
@@ -793,14 +836,14 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
innerSym.rawname + innerSym.moduleSuffix
// add inner classes which might not have been referenced yet
- atPhase(currentRun.erasurePhase.next) {
+ afterErasure {
for (sym <- List(clasz.symbol, clasz.symbol.linkedClassOfClass); m <- sym.info.decls.map(innerClassSymbolFor) if m.isClass)
innerClassBuffer += m
}
val allInners = innerClassBuffer.toList
if (allInners.nonEmpty) {
- log(clasz.symbol.fullName('.') + " contains " + allInners.size + " inner classes.")
+ debuglog(clasz.symbol.fullName('.') + " contains " + allInners.size + " inner classes.")
val innerClassesAttr = jclass.getInnerClasses()
// sort them so inner classes succeed their enclosing class
// to satisfy the Eclipse Java compiler
@@ -826,6 +869,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
def genField(f: IField) {
debuglog("Adding field: " + f.symbol.fullName)
+
val jfield = jclass.addNewField(
javaFlags(f.symbol) | javaFieldFlags(f.symbol),
javaName(f.symbol),
@@ -1128,8 +1172,6 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
linearization = linearizer.linearize(m)
val labels = makeLabels(linearization)
- /** local variables whose scope appears in this block. */
- val varsInBlock: mutable.Set[Local] = new mutable.HashSet
var nextBlock: BasicBlock = linearization.head
@@ -1139,302 +1181,298 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys)
}
- /** Generate exception handlers for the current method. */
- def genExceptionHandlers() {
+ /** Generate exception handlers for the current method. */
+ def genExceptionHandlers() {
- /** Return a list of pairs of intervals where the handler is active.
- * The intervals in the list have to be inclusive in the beginning and
- * exclusive in the end: [start, end).
- */
- def ranges(e: ExceptionHandler): List[(Int, Int)] = {
- var covered = e.covered
- var ranges: List[(Int, Int)] = Nil
- var start = -1
- var end = -1
-
- linearization foreach { b =>
- if (! (covered contains b) ) {
- if (start >= 0) { // we're inside a handler range
- end = labels(b).getAnchor()
- ranges ::= ((start, end))
- start = -1
+ /** Return a list of pairs of intervals where the handler is active.
+ * The intervals in the list have to be inclusive in the beginning and
+ * exclusive in the end: [start, end).
+ */
+ def ranges(e: ExceptionHandler): List[(Int, Int)] = {
+ var covered = e.covered
+ var ranges: List[(Int, Int)] = Nil
+ var start = -1
+ var end = -1
+
+ linearization foreach { b =>
+ if (! (covered contains b) ) {
+ if (start >= 0) { // we're inside a handler range
+ end = labels(b).getAnchor()
+ ranges ::= ((start, end))
+ start = -1
+ }
+ } else {
+ if (start < 0) // we're not inside a handler range
+ start = labels(b).getAnchor()
+
+ end = endPC(b)
+ covered -= b
}
- } else {
- if (start < 0) // we're not inside a handler range
- start = labels(b).getAnchor()
+ }
- end = endPC(b)
- covered -= b
+ /* Add the last interval. Note that since the intervals are
+ * open-ended to the right, we have to give a number past the actual
+ * code!
+ */
+ if (start >= 0) {
+ ranges ::= ((start, jcode.getPC()))
}
- }
- /* Add the last interval. Note that since the intervals are
- * open-ended to the right, we have to give a number past the actual
- * code!
- */
- if (start >= 0) {
- ranges ::= ((start, jcode.getPC()))
+ if (!covered.isEmpty)
+ debuglog("Some covered blocks were not found in method: " + method +
+ " covered: " + covered + " not in " + linearization)
+ ranges
}
- if (!covered.isEmpty)
- debuglog("Some covered blocks were not found in method: " + method +
- " covered: " + covered + " not in " + linearization)
- ranges
+ for (e <- this.method.exh ; p <- ranges(e).sortBy(_._1)) {
+ if (p._1 < p._2) {
+ debuglog("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method +
+ " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls);
+ val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null
+ else javaName(e.cls)
+ jcode.addExceptionHandler(p._1, p._2,
+ labels(e.startBlock).getAnchor(),
+ cls)
+ } else
+ log("Empty exception range: " + p)
+ }
}
- for (e <- this.method.exh ; p <- ranges(e).sortBy(_._1)) {
- if (p._1 < p._2) {
- debuglog("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method +
- " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls);
- val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null
- else javaName(e.cls)
- jcode.addExceptionHandler(p._1, p._2,
- labels(e.startBlock).getAnchor(),
- cls)
- } else
- log("Empty exception range: " + p)
+ def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = {
+ target.isPublic || target.isProtected && {
+ (site.enclClass isSubClass target.enclClass) ||
+ (site.enclosingPackage == target.privateWithin)
+ }
}
- }
- def isAccessibleFrom(target: Symbol, site: Symbol): Boolean = {
- target.isPublic || target.isProtected && {
- (site.enclClass isSubClass target.enclClass) ||
- (site.enclosingPackage == target.privateWithin)
- }
- }
+ def genCallMethod(call: CALL_METHOD) {
+ val CALL_METHOD(method, style) = call
+ val siteSymbol = clasz.symbol
+ val hostSymbol = call.hostClass
+ val methodOwner = method.owner
+ // info calls so that types are up to date; erasure may add lateINTERFACE to traits
+ hostSymbol.info ; methodOwner.info
+
+ def isInterfaceCall(sym: Symbol) = (
+ sym.isInterface && methodOwner != ObjectClass
+ || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass)
+ )
+ // whether to reference the type of the receiver or
+ // the type of the method owner (if not an interface!)
+ val useMethodOwner = (
+ style != Dynamic
+ || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol)
+ || hostSymbol.isBottomClass
+ )
+ val receiver = if (useMethodOwner) methodOwner else hostSymbol
+ val jowner = javaName(receiver)
+ val jname = javaName(method)
+ val jtype = javaType(method).asInstanceOf[JMethodType]
- def genCallMethod(call: CALL_METHOD) {
- val CALL_METHOD(method, style) = call
- val siteSymbol = clasz.symbol
- val hostSymbol = call.hostClass
- val methodOwner = method.owner
- // info calls so that types are up to date; erasure may add lateINTERFACE to traits
- hostSymbol.info ; methodOwner.info
-
- def isInterfaceCall(sym: Symbol) = (
- sym.isInterface && methodOwner != ObjectClass
- || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass)
- )
- // whether to reference the type of the receiver or
- // the type of the method owner (if not an interface!)
- val useMethodOwner = (
- style != Dynamic
- || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol)
- || hostSymbol.isBottomClass
- )
- val receiver = if (useMethodOwner) methodOwner else hostSymbol
- val jowner = javaName(receiver)
- val jname = javaName(method)
- val jtype = javaType(method).asInstanceOf[JMethodType]
-
- def emit(invoke: String) {
- log("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype))
- invoke match {
- case "invokeinterface" => jcode.emitINVOKEINTERFACE(jowner, jname, jtype)
- case "invokevirtual" => jcode.emitINVOKEVIRTUAL(jowner, jname, jtype)
- case "invokespecial" => jcode.emitINVOKESPECIAL(jowner, jname, jtype)
- case "invokestatic" => jcode.emitINVOKESTATIC(jowner, jname, jtype)
+ def debugMsg(invoke: String) {
+ debuglog("%s %s %s.%s:%s".format(invoke, receiver.accessString, jowner, jname, jtype))
}
- }
- def initModule() {
- // we initialize the MODULE$ field immediately after the super ctor
- if (isStaticModule(siteSymbol) && !isModuleInitialized &&
- jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME &&
- jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) {
- isModuleInitialized = true
- jcode.emitALOAD_0()
- jcode.emitPUTSTATIC(jclass.getName(),
- nme.MODULE_INSTANCE_FIELD.toString,
- jclass.getType())
+
+ def initModule() {
+ // we initialize the MODULE$ field immediately after the super ctor
+ if (isStaticModule(siteSymbol) && !isModuleInitialized &&
+ jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME &&
+ jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) {
+ isModuleInitialized = true
+ jcode.emitALOAD_0()
+ jcode.emitPUTSTATIC(jclass.getName(),
+ nme.MODULE_INSTANCE_FIELD.toString,
+ jclass.getType())
+ }
}
- }
- style match {
- case Static(true) => emit("invokespecial")
- case Static(false) => emit("invokestatic")
- case Dynamic if isInterfaceCall(receiver) => emit("invokeinterface")
- case Dynamic => emit("invokevirtual")
- case SuperCall(_) => emit("invokespecial") ; initModule()
+ style match {
+ case Static(true) => jcode.emitINVOKESPECIAL (jowner, jname, jtype) ; debugMsg("invokespecial")
+ case Static(false) => jcode.emitINVOKESTATIC (jowner, jname, jtype) ; debugMsg("invokestatic")
+ case Dynamic if isInterfaceCall(receiver) => jcode.emitINVOKEINTERFACE(jowner, jname, jtype) ; debugMsg("invokinterface")
+ case Dynamic => jcode.emitINVOKEVIRTUAL (jowner, jname, jtype) ; debugMsg("invokevirtual")
+ case SuperCall(_) =>
+ jcode.emitINVOKESPECIAL(jowner, jname, jtype)
+ initModule()
+ debugMsg("invokespecial")
+ }
}
- }
- def genBlock(b: BasicBlock) {
- labels(b).anchorToNext()
+ def genBlock(b: BasicBlock) {
+ labels(b).anchorToNext()
- debuglog("Generating code for block: " + b + " at pc: " + labels(b).getAnchor())
- var lastMappedPC = 0
- var lastLineNr = 0
- var crtPC = 0
- varsInBlock.clear()
+ debuglog("Generating code for block: " + b + " at pc: " + labels(b).getAnchor())
+ var lastMappedPC = 0
+ var lastLineNr = 0
+ var crtPC = 0
- for (instr <- b) {
+ /** local variables whose scope appears in this block. */
+ val varsInBlock: mutable.Set[Local] = new mutable.HashSet
+ val lastInstr = b.lastInstruction
- instr match {
- case THIS(clasz) =>
- jcode.emitALOAD_0()
+ for (instr <- b) {
- case CONSTANT(const) =>
- genConstant(jcode, const)
+ instr match {
+ case THIS(clasz) => jcode.emitALOAD_0()
- case LOAD_ARRAY_ITEM(kind) =>
- jcode.emitALOAD(javaType(kind))
+ case CONSTANT(const) => genConstant(jcode, const)
- case LOAD_LOCAL(local) =>
- jcode.emitLOAD(indexOf(local), javaType(local.kind))
+ case LOAD_ARRAY_ITEM(kind) =>
+ if(kind.isRefOrArrayType) { jcode.emitAALOAD() }
+ else {
+ (kind: @unchecked) match {
+ case UNIT => throw new IllegalArgumentException("invalid type for aload " + kind)
+ case BOOL | BYTE => jcode.emitBALOAD()
+ case SHORT => jcode.emitSALOAD()
+ case CHAR => jcode.emitCALOAD()
+ case INT => jcode.emitIALOAD()
+ case LONG => jcode.emitLALOAD()
+ case FLOAT => jcode.emitFALOAD()
+ case DOUBLE => jcode.emitDALOAD()
+ }
+ }
- case lf @ LOAD_FIELD(field, isStatic) =>
- var owner = javaName(lf.hostClass)
- debuglog("LOAD_FIELD with owner: " + owner +
- " flags: " + Flags.flagsToString(field.owner.flags))
- if (isStatic)
- jcode.emitGETSTATIC(owner,
- javaName(field),
- javaType(field))
- else
- jcode.emitGETFIELD(owner,
- javaName(field),
- javaType(field))
-
- case LOAD_MODULE(module) =>
-// assert(module.isModule, "Expected module: " + module)
- debuglog("generating LOAD_MODULE for: " + module + " flags: " +
- Flags.flagsToString(module.flags));
- if (clasz.symbol == module.moduleClass && jmethod.getName() != nme.readResolve.toString)
- jcode.emitALOAD_0()
- else
- jcode.emitGETSTATIC(javaName(module) /* + "$" */ ,
- nme.MODULE_INSTANCE_FIELD.toString,
- javaType(module))
-
- case STORE_ARRAY_ITEM(kind) =>
- jcode emitASTORE javaType(kind)
-
- case STORE_LOCAL(local) =>
- jcode.emitSTORE(indexOf(local), javaType(local.kind))
-
- case STORE_THIS(_) =>
- // this only works for impl classes because the self parameter comes first
- // in the method signature. If that changes, this code has to be revisited.
- jcode.emitASTORE_0()
-
- case STORE_FIELD(field, isStatic) =>
- val owner = javaName(field.owner)
- if (isStatic)
- jcode.emitPUTSTATIC(owner,
- javaName(field),
- javaType(field))
- else
- jcode.emitPUTFIELD(owner,
- javaName(field),
- javaType(field))
-
- case CALL_PRIMITIVE(primitive) =>
- genPrimitive(primitive, instr.pos)
-
- /** Special handling to access native Array.clone() */
- case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
- val target: String = javaType(call.targetTypeKind).getSignature()
- jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType)
-
- case call @ CALL_METHOD(method, style) =>
- genCallMethod(call)
-
- case BOX(kind) =>
- val boxedType = definitions.boxedClass(kind.toType.typeSymbol)
- val mtype = new JMethodType(javaType(boxedType), Array(javaType(kind)))
- jcode.emitINVOKESTATIC(BoxesRunTime, "boxTo" + boxedType.decodedName, mtype)
-
- case UNBOX(kind) =>
- val mtype = new JMethodType(javaType(kind), Array(JAVA_LANG_OBJECT))
- jcode.emitINVOKESTATIC(BoxesRunTime, "unboxTo" + kind.toType.typeSymbol.decodedName, mtype)
-
- case NEW(REFERENCE(cls)) =>
- val className = javaName(cls)
- jcode emitNEW className
-
- case CREATE_ARRAY(elem, 1) => elem match {
- case REFERENCE(_) | ARRAY(_) =>
- jcode emitANEWARRAY javaType(elem).asInstanceOf[JReferenceType]
- case _ =>
- jcode emitNEWARRAY javaType(elem)
- }
+ case LOAD_LOCAL(local) => jcode.emitLOAD(indexOf(local), javaType(local.kind))
+
+ case lf @ LOAD_FIELD(field, isStatic) =>
+ var owner = javaName(lf.hostClass)
+ debuglog("LOAD_FIELD with owner: " + owner +
+ " flags: " + Flags.flagsToString(field.owner.flags))
+ val fieldJName = javaName(field)
+ val fieldJType = javaType(field)
+ if (isStatic) jcode.emitGETSTATIC(owner, fieldJName, fieldJType)
+ else jcode.emitGETFIELD( owner, fieldJName, fieldJType)
+
+ case LOAD_MODULE(module) =>
+ // assert(module.isModule, "Expected module: " + module)
+ debuglog("generating LOAD_MODULE for: " + module + " flags: " + Flags.flagsToString(module.flags));
+ if (clasz.symbol == module.moduleClass && jmethod.getName() != nme.readResolve.toString)
+ jcode.emitALOAD_0()
+ else
+ jcode.emitGETSTATIC(javaName(module) /* + "$" */ ,
+ nme.MODULE_INSTANCE_FIELD.toString,
+ javaType(module))
+
+ case STORE_ARRAY_ITEM(kind) =>
+ if(kind.isRefOrArrayType) { jcode.emitAASTORE() }
+ else {
+ (kind: @unchecked) match {
+ case UNIT => throw new IllegalArgumentException("invalid type for astore " + kind)
+ case BOOL | BYTE => jcode.emitBASTORE()
+ case SHORT => jcode.emitSASTORE()
+ case CHAR => jcode.emitCASTORE()
+ case INT => jcode.emitIASTORE()
+ case LONG => jcode.emitLASTORE()
+ case FLOAT => jcode.emitFASTORE()
+ case DOUBLE => jcode.emitDASTORE()
+ }
+ }
- case CREATE_ARRAY(elem, dims) =>
- jcode.emitMULTIANEWARRAY(javaType(ArrayN(elem, dims)).asInstanceOf[JReferenceType], dims)
+ case STORE_LOCAL(local) =>
+ jcode.emitSTORE(indexOf(local), javaType(local.kind))
- case IS_INSTANCE(tpe) =>
- tpe match {
- case REFERENCE(cls) =>
- jcode emitINSTANCEOF new JObjectType(javaName(cls))
- case ARRAY(elem) =>
- jcode emitINSTANCEOF new JArrayType(javaType(elem))
- case _ =>
- abort("Unknown reference type in IS_INSTANCE: " + tpe)
- }
+ case STORE_THIS(_) =>
+ // this only works for impl classes because the self parameter comes first
+ // in the method signature. If that changes, this code has to be revisited.
+ jcode.emitASTORE_0()
- case CHECK_CAST(tpe) =>
- tpe match {
- case REFERENCE(cls) =>
- // No need to checkcast for Objects
- if (cls != ObjectClass)
- jcode emitCHECKCAST new JObjectType(javaName(cls))
- case ARRAY(elem) =>
- jcode emitCHECKCAST new JArrayType(javaType(elem))
- case _ =>
- abort("Unknown reference type in IS_INSTANCE: " + tpe)
- }
+ case STORE_FIELD(field, isStatic) =>
+ val owner = javaName(field.owner)
+ val fieldJName = javaName(field)
+ val fieldJType = javaType(field)
+ if (isStatic) jcode.emitPUTSTATIC(owner, fieldJName, fieldJType)
+ else jcode.emitPUTFIELD( owner, fieldJName, fieldJType)
- case SWITCH(tags, branches) =>
- val tagArray = new Array[Array[Int]](tags.length)
- var caze = tags
- var i = 0
+ case CALL_PRIMITIVE(primitive) => genPrimitive(primitive, instr.pos)
- while (i < tagArray.length) {
- tagArray(i) = new Array[Int](caze.head.length)
- caze.head.copyToArray(tagArray(i), 0)
- i += 1
- caze = caze.tail
- }
- val branchArray = jcode.newLabels(tagArray.length)
- i = 0
- while (i < branchArray.length) {
- branchArray(i) = labels(branches(i))
- i += 1
- }
- debuglog("Emitting SWITCH:\ntags: " + tags + "\nbranches: " + branches)
- jcode.emitSWITCH(tagArray,
- branchArray,
- labels(branches.last),
- MIN_SWITCH_DENSITY)
- ()
-
- case JUMP(whereto) =>
- if (nextBlock != whereto)
- jcode.emitGOTO_maybe_W(labels(whereto), false) // default to short jumps
-
- case CJUMP(success, failure, cond, kind) =>
- kind match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
+ /** Special handling to access native Array.clone() */
+ case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
+ val target: String = javaType(call.targetTypeKind).getSignature()
+ jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType)
+
+ case call @ CALL_METHOD(method, style) => genCallMethod(call)
+
+ case BOX(kind) =>
+ val Pair(mname, mtype) = jBoxTo(kind)
+ jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype)
+
+ case UNBOX(kind) =>
+ val Pair(mname, mtype) = jUnboxTo(kind)
+ jcode.emitINVOKESTATIC(BoxesRunTime, mname, mtype)
+
+ case NEW(REFERENCE(cls)) =>
+ val className = javaName(cls)
+ jcode emitNEW className
+
+ case CREATE_ARRAY(elem, 1) =>
+ if(elem.isRefOrArrayType) { jcode emitANEWARRAY javaType(elem).asInstanceOf[JReferenceType] }
+ else { jcode emitNEWARRAY javaType(elem) }
+
+ case CREATE_ARRAY(elem, dims) =>
+ jcode.emitMULTIANEWARRAY(javaType(ArrayN(elem, dims)).asInstanceOf[JReferenceType], dims)
+
+ case IS_INSTANCE(tpe) =>
+ tpe match {
+ case REFERENCE(cls) => jcode emitINSTANCEOF new JObjectType(javaName(cls))
+ case ARRAY(elem) => jcode emitINSTANCEOF new JArrayType(javaType(elem))
+ case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe)
+ }
+
+ case CHECK_CAST(tpe) =>
+ tpe match {
+ case REFERENCE(cls) => if (cls != ObjectClass) { jcode emitCHECKCAST new JObjectType(javaName(cls)) } // No need to checkcast for Objects
+ case ARRAY(elem) => jcode emitCHECKCAST new JArrayType(javaType(elem))
+ case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe)
+ }
+
+ case SWITCH(tags, branches) =>
+ val tagArray = new Array[Array[Int]](tags.length)
+ var caze = tags
+ var i = 0
+
+ while (i < tagArray.length) {
+ tagArray(i) = new Array[Int](caze.head.length)
+ caze.head.copyToArray(tagArray(i), 0)
+ i += 1
+ caze = caze.tail
+ }
+ val branchArray = jcode.newLabels(tagArray.length)
+ i = 0
+ while (i < branchArray.length) {
+ branchArray(i) = labels(branches(i))
+ i += 1
+ }
+ debuglog("Emitting SWITCH:\ntags: " + tags + "\nbranches: " + branches)
+ jcode.emitSWITCH(tagArray,
+ branchArray,
+ labels(branches.last),
+ MIN_SWITCH_DENSITY)
+ ()
+
+ case JUMP(whereto) =>
+ if (nextBlock != whereto)
+ jcode.emitGOTO_maybe_W(labels(whereto), false) // default to short jumps
+
+ case CJUMP(success, failure, cond, kind) =>
+ if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
if (nextBlock == success) {
- jcode.emitIF_ICMP(conds(negate(cond)), labels(failure))
+ jcode.emitIF_ICMP(conds(cond.negate()), labels(failure))
// .. and fall through to success label
} else {
jcode.emitIF_ICMP(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
-
- case REFERENCE(_) | ARRAY(_) =>
+ } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
if (nextBlock == success) {
- jcode.emitIF_ACMP(conds(negate(cond)), labels(failure))
+ jcode.emitIF_ACMP(conds(cond.negate()), labels(failure))
// .. and fall through to success label
} else {
jcode.emitIF_ACMP(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
-
- case _ =>
+ } else {
(kind: @unchecked) match {
case LONG => jcode.emitLCMP()
case FLOAT =>
@@ -1445,38 +1483,32 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
else jcode.emitDCMPL()
}
if (nextBlock == success) {
- jcode.emitIF(conds(negate(cond)), labels(failure))
+ jcode.emitIF(conds(cond.negate()), labels(failure))
// .. and fall through to success label
} else {
jcode.emitIF(conds(cond), labels(success));
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
- }
+ }
- case CZJUMP(success, failure, cond, kind) =>
- kind match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
+ case CZJUMP(success, failure, cond, kind) =>
+ if(kind.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
if (nextBlock == success) {
- jcode.emitIF(conds(negate(cond)), labels(failure))
+ jcode.emitIF(conds(cond.negate()), labels(failure))
} else {
jcode.emitIF(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
-
- case REFERENCE(_) | ARRAY(_) =>
+ } else if(kind.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_)
val Success = success
val Failure = failure
(cond, nextBlock) match {
- case (EQ, Success) =>
- jcode emitIFNONNULL labels(failure)
- case (NE, Failure) =>
- jcode emitIFNONNULL labels(success)
- case (EQ, Failure) =>
- jcode emitIFNULL labels(success)
- case (NE, Success) =>
- jcode emitIFNULL labels(failure)
+ case (EQ, Success) => jcode emitIFNONNULL labels(failure)
+ case (NE, Failure) => jcode emitIFNONNULL labels(success)
+ case (EQ, Failure) => jcode emitIFNULL labels(success)
+ case (NE, Success) => jcode emitIFNULL labels(failure)
case (EQ, _) =>
jcode emitIFNULL labels(success)
jcode.emitGOTO_maybe_W(labels(failure), false)
@@ -1484,11 +1516,11 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
jcode emitIFNONNULL labels(success)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
-
- case _ =>
+ } else {
(kind: @unchecked) match {
case LONG =>
- jcode.emitLCONST_0(); jcode.emitLCMP()
+ jcode.emitLCONST_0()
+ jcode.emitLCMP()
case FLOAT =>
jcode.emitFCONST_0()
if (cond == LT || cond == LE) jcode.emitFCMPG()
@@ -1499,263 +1531,254 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
else jcode.emitDCMPL()
}
if (nextBlock == success) {
- jcode.emitIF(conds(negate(cond)), labels(failure))
+ jcode.emitIF(conds(cond.negate()), labels(failure))
} else {
jcode.emitIF(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
- }
+ }
- case RETURN(kind) =>
- jcode emitRETURN javaType(kind)
+ case RETURN(kind) => jcode emitRETURN javaType(kind)
- case THROW(_) =>
- jcode.emitATHROW()
+ case THROW(_) => jcode.emitATHROW()
- case DROP(kind) =>
- kind match {
- case LONG | DOUBLE => jcode.emitPOP2()
- case _ => jcode.emitPOP()
- }
+ case DROP(kind) =>
+ if(kind.isWideType) jcode.emitPOP2()
+ else jcode.emitPOP()
- case DUP(kind) =>
- kind match {
- case LONG | DOUBLE => jcode.emitDUP2()
- case _ => jcode.emitDUP()
- }
+ case DUP(kind) =>
+ if(kind.isWideType) jcode.emitDUP2()
+ else jcode.emitDUP()
- case MONITOR_ENTER() =>
- jcode.emitMONITORENTER()
+ case MONITOR_ENTER() => jcode.emitMONITORENTER()
- case MONITOR_EXIT() =>
- jcode.emitMONITOREXIT()
+ case MONITOR_EXIT() => jcode.emitMONITOREXIT()
- case SCOPE_ENTER(lv) =>
- varsInBlock += lv
- lv.start = jcode.getPC()
+ case SCOPE_ENTER(lv) =>
+ varsInBlock += lv
+ lv.start = jcode.getPC()
- case SCOPE_EXIT(lv) =>
- if (varsInBlock(lv)) {
- lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges
- varsInBlock -= lv
- }
- else if (b.varsInScope(lv)) {
- lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges
- b.varsInScope -= lv
- }
- else dumpMethodAndAbort(method, "Illegal local var nesting")
+ case SCOPE_EXIT(lv) =>
+ if (varsInBlock(lv)) {
+ lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges
+ varsInBlock -= lv
+ }
+ else if (b.varsInScope(lv)) {
+ lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges
+ b.varsInScope -= lv
+ }
+ else dumpMethodAndAbort(method, "Illegal local var nesting")
- case LOAD_EXCEPTION(_) =>
- ()
- }
+ case LOAD_EXCEPTION(_) =>
+ ()
+ }
- crtPC = jcode.getPC()
+ crtPC = jcode.getPC()
-// assert(instr.pos.source.isEmpty || instr.pos.source.get == (clasz.cunit.source), "sources don't match")
-// val crtLine = instr.pos.line.get(lastLineNr);
+ // assert(instr.pos.source.isEmpty || instr.pos.source.get == (clasz.cunit.source), "sources don't match")
+ // val crtLine = instr.pos.line.get(lastLineNr);
- val crtLine = try {
- if (instr.pos == NoPosition) lastLineNr else (instr.pos).line // check NoPosition to avoid costly exception
- } catch {
- case _: UnsupportedOperationException =>
- log("Warning: wrong position in: " + method)
- lastLineNr
- }
+ val crtLine = try {
+ if (instr.pos == NoPosition) lastLineNr else (instr.pos).line // check NoPosition to avoid costly exception
+ } catch {
+ case _: UnsupportedOperationException =>
+ log("Warning: wrong position in: " + method)
+ lastLineNr
+ }
- if (b.lastInstruction == instr)
- endPC(b) = jcode.getPC()
+ if (instr eq lastInstr) { endPC(b) = jcode.getPC() }
- //System.err.println("CRTLINE: " + instr.pos + " " +
- // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine);
+ //System.err.println("CRTLINE: " + instr.pos + " " +
+ // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine);
- if (crtPC > lastMappedPC) {
- jcode.completeLineNumber(lastMappedPC, crtPC, crtLine)
- lastMappedPC = crtPC
- lastLineNr = crtLine
+ if (crtPC > lastMappedPC) {
+ jcode.completeLineNumber(lastMappedPC, crtPC, crtLine)
+ lastMappedPC = crtPC
+ lastLineNr = crtLine
+ }
}
- }
- // local vars that survived this basic block
- for (lv <- varsInBlock) {
- lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges
- }
- for (lv <- b.varsInScope) {
- lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges
+ // local vars that survived this basic block
+ for (lv <- varsInBlock) {
+ lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges
+ }
+ for (lv <- b.varsInScope) {
+ lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges
+ }
}
- }
- /**
- * @param primitive ...
- * @param pos ...
- */
- def genPrimitive(primitive: Primitive, pos: Position) {
- primitive match {
- case Negation(kind) =>
- kind match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
- jcode.emitINEG()
- case LONG => jcode.emitLNEG()
- case FLOAT => jcode.emitFNEG()
- case DOUBLE => jcode.emitDNEG()
- case _ => abort("Impossible to negate a " + kind)
- }
-
- case Arithmetic(op, kind) =>
- op match {
- case ADD => jcode.emitADD(javaType(kind))
- case SUB =>
- (kind: @unchecked) match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
- jcode.emitISUB()
- case LONG => jcode.emitLSUB()
- case FLOAT => jcode.emitFSUB()
- case DOUBLE => jcode.emitDSUB()
+ /**
+ * @param primitive ...
+ * @param pos ...
+ */
+ def genPrimitive(primitive: Primitive, pos: Position) {
+ primitive match {
+ case Negation(kind) =>
+ if(kind.isIntSizedType) { jcode.emitINEG() }
+ else {
+ kind match {
+ case LONG => jcode.emitLNEG()
+ case FLOAT => jcode.emitFNEG()
+ case DOUBLE => jcode.emitDNEG()
+ case _ => abort("Impossible to negate a " + kind)
}
+ }
- case MUL =>
- (kind: @unchecked) match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
- jcode.emitIMUL()
- case LONG => jcode.emitLMUL()
- case FLOAT => jcode.emitFMUL()
- case DOUBLE => jcode.emitDMUL()
- }
+ case Arithmetic(op, kind) =>
+ op match {
+ case ADD =>
+ if(kind.isIntSizedType) { jcode.emitIADD() }
+ else {
+ (kind: @unchecked) match {
+ case LONG => jcode.emitLADD()
+ case FLOAT => jcode.emitFADD()
+ case DOUBLE => jcode.emitDADD()
+ }
+ }
- case DIV =>
- (kind: @unchecked) match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
- jcode.emitIDIV()
- case LONG => jcode.emitLDIV()
- case FLOAT => jcode.emitFDIV()
- case DOUBLE => jcode.emitDDIV()
- }
+ case SUB =>
+ if(kind.isIntSizedType) { jcode.emitISUB() }
+ else {
+ (kind: @unchecked) match {
+ case LONG => jcode.emitLSUB()
+ case FLOAT => jcode.emitFSUB()
+ case DOUBLE => jcode.emitDSUB()
+ }
+ }
- case REM =>
- (kind: @unchecked) match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
- jcode.emitIREM()
- case LONG => jcode.emitLREM()
- case FLOAT => jcode.emitFREM()
- case DOUBLE => jcode.emitDREM()
- }
+ case MUL =>
+ if(kind.isIntSizedType) { jcode.emitIMUL() }
+ else {
+ (kind: @unchecked) match {
+ case LONG => jcode.emitLMUL()
+ case FLOAT => jcode.emitFMUL()
+ case DOUBLE => jcode.emitDMUL()
+ }
+ }
- case NOT =>
- kind match {
- case BOOL | BYTE | CHAR | SHORT | INT =>
+ case DIV =>
+ if(kind.isIntSizedType) { jcode.emitIDIV() }
+ else {
+ (kind: @unchecked) match {
+ case LONG => jcode.emitLDIV()
+ case FLOAT => jcode.emitFDIV()
+ case DOUBLE => jcode.emitDDIV()
+ }
+ }
+
+ case REM =>
+ if(kind.isIntSizedType) { jcode.emitIREM() }
+ else {
+ (kind: @unchecked) match {
+ case LONG => jcode.emitLREM()
+ case FLOAT => jcode.emitFREM()
+ case DOUBLE => jcode.emitDREM()
+ }
+ }
+
+ case NOT =>
+ if(kind.isIntSizedType) {
jcode.emitPUSH(-1)
jcode.emitIXOR()
- case LONG =>
+ } else if(kind == LONG) {
jcode.emitPUSH(-1l)
jcode.emitLXOR()
- case _ =>
+ } else {
abort("Impossible to negate an " + kind)
- }
-
- case _ =>
- abort("Unknown arithmetic primitive " + primitive)
- }
-
- case Logical(op, kind) => (op, kind) match {
- case (AND, LONG) =>
- jcode.emitLAND()
- case (AND, INT) =>
- jcode.emitIAND()
- case (AND, _) =>
- jcode.emitIAND()
- if (kind != BOOL)
- jcode.emitT2T(javaType(INT), javaType(kind));
-
- case (OR, LONG) =>
- jcode.emitLOR()
- case (OR, INT) =>
- jcode.emitIOR()
- case (OR, _) =>
- jcode.emitIOR()
- if (kind != BOOL)
- jcode.emitT2T(javaType(INT), javaType(kind));
-
- case (XOR, LONG) =>
- jcode.emitLXOR()
- case (XOR, INT) =>
- jcode.emitIXOR()
- case (XOR, _) =>
- jcode.emitIXOR()
- if (kind != BOOL)
- jcode.emitT2T(javaType(INT), javaType(kind));
- }
-
- case Shift(op, kind) => (op, kind) match {
- case (LSL, LONG) =>
- jcode.emitLSHL()
- case (LSL, INT) =>
- jcode.emitISHL()
- case (LSL, _) =>
- jcode.emitISHL()
- jcode.emitT2T(javaType(INT), javaType(kind))
-
- case (ASR, LONG) =>
- jcode.emitLSHR()
- case (ASR, INT) =>
- jcode.emitISHR()
- case (ASR, _) =>
- jcode.emitISHR()
- jcode.emitT2T(javaType(INT), javaType(kind))
-
- case (LSR, LONG) =>
- jcode.emitLUSHR()
- case (LSR, INT) =>
- jcode.emitIUSHR()
- case (LSR, _) =>
- jcode.emitIUSHR()
- jcode.emitT2T(javaType(INT), javaType(kind))
- }
+ }
- case Comparison(op, kind) => ((op, kind): @unchecked) match {
- case (CMP, LONG) => jcode.emitLCMP()
- case (CMPL, FLOAT) => jcode.emitFCMPL()
- case (CMPG, FLOAT) => jcode.emitFCMPG()
- case (CMPL, DOUBLE) => jcode.emitDCMPL()
- case (CMPG, DOUBLE) => jcode.emitDCMPL()
- }
+ case _ =>
+ abort("Unknown arithmetic primitive " + primitive)
+ }
- case Conversion(src, dst) =>
- debuglog("Converting from: " + src + " to: " + dst)
- if (dst == BOOL) {
- println("Illegal conversion at: " + clasz +
- " at: " + pos.source + ":" + pos.line)
- } else
- jcode.emitT2T(javaType(src), javaType(dst))
+ case Logical(op, kind) => (op, kind) match {
+ case (AND, LONG) => jcode.emitLAND()
+ case (AND, INT) => jcode.emitIAND()
+ case (AND, _) =>
+ jcode.emitIAND()
+ if (kind != BOOL)
+ jcode.emitT2T(javaType(INT), javaType(kind));
+
+ case (OR, LONG) => jcode.emitLOR()
+ case (OR, INT) => jcode.emitIOR()
+ case (OR, _) =>
+ jcode.emitIOR()
+ if (kind != BOOL)
+ jcode.emitT2T(javaType(INT), javaType(kind));
+
+ case (XOR, LONG) => jcode.emitLXOR()
+ case (XOR, INT) => jcode.emitIXOR()
+ case (XOR, _) =>
+ jcode.emitIXOR()
+ if (kind != BOOL)
+ jcode.emitT2T(javaType(INT), javaType(kind));
+ }
- case ArrayLength(_) =>
- jcode.emitARRAYLENGTH()
+ case Shift(op, kind) => (op, kind) match {
+ case (LSL, LONG) => jcode.emitLSHL()
+ case (LSL, INT) => jcode.emitISHL()
+ case (LSL, _) =>
+ jcode.emitISHL()
+ jcode.emitT2T(javaType(INT), javaType(kind))
+
+ case (ASR, LONG) => jcode.emitLSHR()
+ case (ASR, INT) => jcode.emitISHR()
+ case (ASR, _) =>
+ jcode.emitISHR()
+ jcode.emitT2T(javaType(INT), javaType(kind))
+
+ case (LSR, LONG) => jcode.emitLUSHR()
+ case (LSR, INT) => jcode.emitIUSHR()
+ case (LSR, _) =>
+ jcode.emitIUSHR()
+ jcode.emitT2T(javaType(INT), javaType(kind))
+ }
- case StartConcat =>
- jcode emitNEW StringBuilderClassName
- jcode.emitDUP()
- jcode.emitINVOKESPECIAL(StringBuilderClassName,
- JMethod.INSTANCE_CONSTRUCTOR_NAME,
- JMethodType.ARGLESS_VOID_FUNCTION)
-
- case StringConcat(el) =>
- val jtype = el match {
- case REFERENCE(_) | ARRAY(_) => JAVA_LANG_OBJECT
- case _ => javaType(el)
+ case Comparison(op, kind) => ((op, kind): @unchecked) match {
+ case (CMP, LONG) => jcode.emitLCMP()
+ case (CMPL, FLOAT) => jcode.emitFCMPL()
+ case (CMPG, FLOAT) => jcode.emitFCMPG()
+ case (CMPL, DOUBLE) => jcode.emitDCMPL()
+ case (CMPG, DOUBLE) => jcode.emitDCMPL()
}
- jcode.emitINVOKEVIRTUAL(StringBuilderClassName,
- "append",
- new JMethodType(StringBuilderType,
- Array(jtype)))
- case EndConcat =>
- jcode.emitINVOKEVIRTUAL(StringBuilderClassName,
- "toString",
- toStringType)
- case _ =>
- abort("Unimplemented primitive " + primitive)
+ case Conversion(src, dst) =>
+ debuglog("Converting from: " + src + " to: " + dst)
+ if (dst == BOOL) {
+ println("Illegal conversion at: " + clasz + " at: " + pos.source + ":" + pos.line)
+ } else
+ jcode.emitT2T(javaType(src), javaType(dst))
+
+ case ArrayLength(_) =>
+ jcode.emitARRAYLENGTH()
+
+ case StartConcat =>
+ jcode emitNEW StringBuilderClassName
+ jcode.emitDUP()
+ jcode.emitINVOKESPECIAL(StringBuilderClassName,
+ JMethod.INSTANCE_CONSTRUCTOR_NAME,
+ JMethodType.ARGLESS_VOID_FUNCTION)
+
+ case StringConcat(el) =>
+ val jtype = el match {
+ case REFERENCE(_) | ARRAY(_) => JAVA_LANG_OBJECT
+ case _ => javaType(el)
+ }
+ jcode.emitINVOKEVIRTUAL(StringBuilderClassName,
+ "append",
+ new JMethodType(StringBuilderType,
+ Array(jtype)))
+ case EndConcat =>
+ jcode.emitINVOKEVIRTUAL(StringBuilderClassName,
+ "toString",
+ toStringType)
+
+ case _ =>
+ abort("Unimplemented primitive " + primitive)
+ }
}
- }
// genCode starts here
genBlocks(linearization)
@@ -1825,10 +1848,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe))
- def sizeOf(k: TypeKind): Int = k match {
- case DOUBLE | LONG => 2
- case _ => 1
- }
+ def sizeOf(k: TypeKind): Int = if(k.isWideType) 2 else 1
def indexOf(m: IMethod, sym: Symbol): Int = {
val Some(local) = m lookupLocal sym
@@ -1845,9 +1865,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
* method. *Does not assume the parameters come first!*
*/
def computeLocalVarsIndex(m: IMethod) {
- var idx = 1
- if (m.symbol.isStaticMember)
- idx = 0;
+ var idx = if (m.symbol.isStaticMember) 0 else 1;
for (l <- m.params) {
debuglog("Index value for " + l + "{" + l.## + "}: " + idx)
@@ -1906,6 +1924,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
((sym.rawflags & (Flags.FINAL | Flags.MODULE)) != 0)
&& !sym.enclClass.isInterface
&& !sym.isClassConstructor
+ && !sym.isMutable // fix for SI-3569, it is too broad?
)
mkFlags(
@@ -1914,9 +1933,10 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
if (sym.isInterface) ACC_INTERFACE else 0,
if (finalFlag) ACC_FINAL else 0,
if (sym.isStaticMember) ACC_STATIC else 0,
- if (sym.isBridge) ACC_BRIDGE else 0,
+ if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
- if (sym.isVarargsMethod) ACC_VARARGS else 0
+ if (sym.isVarargsMethod) ACC_VARARGS else 0,
+ if (sym.hasFlag(Flags.SYNCHRONIZED)) JAVA_ACC_SYNCHRONIZED else 0
)
}
def javaFieldFlags(sym: Symbol) = {
@@ -1928,9 +1948,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
}
def isTopLevelModule(sym: Symbol): Boolean =
- atPhase (currentRun.picklerPhase.next) {
- sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
- }
+ afterPickler { sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass }
def isStaticModule(sym: Symbol): Boolean = {
sym.isModuleClass && !sym.isImplClass && !sym.isLifted
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala
index 93d3d19ac8..b74981b999 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMUtil.scala
@@ -54,14 +54,6 @@ trait GenJVMUtil {
LE -> JExtendedCode.COND_LE,
GE -> JExtendedCode.COND_GE
)
- val negate = immutable.Map[TestOp, TestOp](
- EQ -> NE,
- NE -> EQ,
- LT -> GE,
- GT -> LE,
- LE -> GT,
- GE -> LT
- )
/** Specialized array conversion to prevent calling
* java.lang.reflect.Array.newInstance via TraversableOnce.toArray
@@ -85,12 +77,10 @@ trait GenJVMUtil {
*/
def javaName(sym: Symbol): String =
javaNameCache.getOrElseUpdate(sym, {
- sym.name.newName(
- if (sym.isClass || (sym.isModule && !sym.isMethod))
- sym.javaBinaryName
- else
- sym.javaSimpleName
- )
+ if (sym.isClass || (sym.isModule && !sym.isMethod))
+ sym.javaBinaryName
+ else
+ sym.javaSimpleName
}).toString
def javaType(t: TypeKind): JType = (t: @unchecked) match {
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
index d2e54ff3f1..2fb615f893 100644
--- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
+++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
@@ -1125,7 +1125,7 @@ abstract class GenMSIL extends SubComponent {
}
// method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType
- val (isDelegateView, paramType, resType) = atPhase(currentRun.typerPhase) {
+ val (isDelegateView, paramType, resType) = beforeTyper {
msym.tpe match {
case MethodType(params, resultType)
if (params.length == 1 && msym.name == nme.view_) =>
@@ -1954,7 +1954,7 @@ abstract class GenMSIL extends SubComponent {
} // createClassMembers0
private def isTopLevelModule(sym: Symbol): Boolean =
- atPhase (currentRun.refchecksPhase) {
+ beforeRefchecks {
sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
index e8abee7d06..ff45bb8fd1 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
@@ -108,7 +108,7 @@ abstract class ClosureElimination extends SubComponent {
case LOAD_LOCAL(l) if info.bindings isDefinedAt LocalVar(l) =>
val t = info.getBinding(l)
t match {
- case Deref(LocalVar(_)) | Deref(This) | Const(_) =>
+ case Deref(This) | Const(_) =>
bb.replaceInstruction(i, valueToInstruction(t));
log("replaced " + i + " with " + t)
diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
index 5fc7329955..95c371fa8b 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
@@ -225,9 +225,9 @@ abstract class DeadCodeElimination extends SubComponent {
m foreachBlock { bb =>
assert(bb.closed, "Open block in computeCompensations")
- for ((i, idx) <- bb.toList.zipWithIndex) {
+ foreachWithIndex(bb.toList) { (i, idx) =>
if (!useful(bb)(idx)) {
- for ((consumedType, depth) <- i.consumedTypes.reverse.zipWithIndex) {
+ foreachWithIndex(i.consumedTypes.reverse) { (consumedType, depth) =>
log("Finding definitions of: " + i + "\n\t" + consumedType + " at depth: " + depth)
val defs = rdef.findDefs(bb, idx, 1, depth)
for (d <- defs) {
diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
index 66f802f74f..a734b2b92b 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
@@ -38,6 +38,33 @@ abstract class Inliners extends SubComponent {
res
}
+ /** Look up implementation of method 'sym in 'clazz'.
+ */
+ def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = {
+ // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info)
+ def needsLookup = (
+ (clazz != NoSymbol)
+ && (clazz != sym.owner)
+ && !sym.isEffectivelyFinal
+ && clazz.isEffectivelyFinal
+ )
+ def lookup(clazz: Symbol): Symbol = {
+ // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner)
+ if (sym.owner == clazz || isBottomType(clazz)) sym
+ else sym.overridingSymbol(clazz) match {
+ case NoSymbol => if (sym.owner.isTrait) sym else lookup(clazz.superClass)
+ case imp => imp
+ }
+ }
+ if (needsLookup) {
+ val concreteMethod = lookup(clazz)
+ debuglog("\tlooked up method: " + concreteMethod.fullName)
+
+ concreteMethod
+ }
+ else sym
+ }
+
/* A warning threshold */
private final val MAX_INLINE_MILLIS = 2000
@@ -67,8 +94,7 @@ abstract class Inliners extends SubComponent {
try {
super.run()
} finally {
- inliner.NonPublicRefs.usesNonPublics.clear()
- inliner.recentTFAs.clear
+ inliner.clearCaches()
}
}
}
@@ -80,6 +106,21 @@ abstract class Inliners extends SubComponent {
def isClosureClass(cls: Symbol): Boolean =
cls.isFinal && cls.isSynthetic && !cls.isModuleClass && cls.isAnonymousFunction
+ /*
+ TODO now that Inliner runs faster we could consider additional "monadic methods" (in the limit, all those taking a closure as last arg)
+ Any "monadic method" occurring in a given caller C that is not `isMonadicMethod()` will prevent CloseElim from eliminating
+ any anonymous-closure-class any whose instances are given as argument to C invocations.
+ */
+ def isMonadicMethod(sym: Symbol) = {
+ nme.unspecializedName(sym.name) match {
+ case nme.foreach | nme.filter | nme.withFilter | nme.map | nme.flatMap => true
+ case _ => false
+ }
+ }
+
+ def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass
+ def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass
+
/**
* Simple inliner.
*/
@@ -92,9 +133,6 @@ abstract class Inliners extends SubComponent {
}
import NonPublicRefs._
- private def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass
- private def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass
-
/** The current iclass */
private var currentIClazz: IClass = _
private def warn(pos: Position, msg: String) = currentIClazz.cunit.warning(pos, msg)
@@ -121,6 +159,21 @@ abstract class Inliners extends SubComponent {
(hasRETURN, a)
}
+ def clearCaches() {
+ // methods
+ NonPublicRefs.usesNonPublics.clear()
+ recentTFAs.clear
+ tfa.knownUnsafe.clear()
+ tfa.knownSafe.clear()
+ tfa.knownNever.clear()
+ // basic blocks
+ tfa.preCandidates.clear()
+ tfa.relevantBBs.clear()
+ // callsites
+ tfa.remainingCALLs.clear()
+ tfa.isOnWatchlist.clear()
+ }
+
def analyzeClass(cls: IClass): Unit =
if (settings.inline.value) {
debuglog("Analyzing " + cls)
@@ -142,7 +195,38 @@ abstract class Inliners extends SubComponent {
val splicedBlocks = mutable.Set.empty[BasicBlock]
val staleIn = mutable.Set.empty[BasicBlock]
+ /**
+ * A transformation local to the body of the argument.
+ * An linining decision consists in replacing a callsite with the body of the callee.
+ * Please notice that, because `analyzeMethod()` itself may modify a method body,
+ * the particular callee bodies that end up being inlined depend on the particular order in which methods are visited
+ * (no topological ordering over the call-graph is attempted).
+ *
+ * Making an inlining decision requires type-flow information for both caller and callee.
+ * Regarding the caller, such information is needed only for basic blocks containing inlining candidates
+ * (and their transitive predecessors). This observation leads to using a custom type-flow analysis (MTFAGrowable)
+ * that can be re-inited, i.e. that reuses lattice elements (type-flow information) computed in a previous iteration
+ * as starting point for faster convergence in a new iteration.
+ *
+ * The mechanics of inlining are iterative for a given invocation of `analyzeMethod(m)`,
+ * thus considering the basic blocks that successful inlining added in a previous iteration:
+ *
+ * (1) before the iterations proper start, so-called preinlining is performed.
+ * Those callsites whose (receiver, concreteMethod) are both known statically
+ * can be analyzed for inlining before computing a type-flow. Details in `preInline()`
+ *
+ * (2) the first iteration computes type-flow information for basic blocks containing inlining candidates
+ * (and their transitive predecessors), so called `relevantBBs`.
+ * The ensuing analysis of each candidate (performed by `analyzeInc()`)
+ * may result in a CFG isomorphic to that of the callee being inserted where the callsite was
+ * (i.e. a CALL_METHOD instruction is replaced with a single-entry single-exit CFG, which we call "successful inlining").
+ *
+ * (3) following iterations have their relevant basic blocks updated to focus
+ * on the inlined basic blocks and their successors only. Details in `MTFAGrowable.reinit()`
+ * */
def analyzeMethod(m: IMethod): Unit = {
+ // m.normalize
+
var sizeBeforeInlining = m.code.blockCount
var instrBeforeInlining = m.code.instructionCount
var retry = false
@@ -154,17 +238,53 @@ abstract class Inliners extends SubComponent {
val inlinedMethodCount = mutable.HashMap.empty[Symbol, Int] withDefaultValue 0
val caller = new IMethodInfo(m)
- var info: tfa.lattice.Elem = null
- def analyzeInc(msym: Symbol, i: Instruction, bb: BasicBlock): Boolean = {
- var inlined = false
- def paramTypes = msym.info.paramTypes
- val receiver = (info.stack.types drop paramTypes.length) match {
- case Nil => log("analyzeInc(" + msym + "), no type on the stack!") ; NoSymbol
- case REFERENCE(s) :: _ => s
- case _ => NoSymbol
+ def preInline(isFirstRound: Boolean): Int = {
+ val inputBlocks = caller.m.linearizedBlocks()
+ val callsites: Function1[BasicBlock, List[opcodes.CALL_METHOD]] = {
+ if(isFirstRound) tfa.conclusives else tfa.knownBeforehand
}
- val concreteMethod = lookupImplFor(msym, receiver)
+ inlineWithoutTFA(inputBlocks, callsites)
+ }
+
+ /**
+ * Inline straightforward callsites (those that can be inlined without a TFA).
+ *
+ * To perform inlining, all we need to know is listed as formal params in `analyzeInc()`:
+ * - callsite and block containing it
+ * - actual (ie runtime) class of the receiver
+ * - actual (ie runtime) method being invoked
+ * - stack length just before the callsite (to check whether enough arguments have been pushed).
+ * The assert below lists the conditions under which "no TFA is needed"
+ * (the statically known receiver and method are both final, thus, at runtime they can't be any others than those).
+ *
+ */
+ def inlineWithoutTFA(inputBlocks: Traversable[BasicBlock], callsites: Function1[BasicBlock, List[opcodes.CALL_METHOD]]): Int = {
+ var inlineCount = 0
+ import scala.util.control.Breaks._
+ for(x <- inputBlocks; val easyCake = callsites(x); if easyCake.nonEmpty) {
+ breakable {
+ for(ocm <- easyCake) {
+ assert(ocm.method.isEffectivelyFinal && ocm.method.owner.isEffectivelyFinal)
+ if(analyzeInc(ocm, x, ocm.method.owner, -1, ocm.method)) {
+ inlineCount += 1
+ break
+ }
+ }
+ }
+ }
+
+ inlineCount
+ }
+
+ /**
+ Decides whether it's feasible and desirable to inline the body of the method given by `concreteMethod`
+ at the program point given by `i` (a callsite). The boolean result indicates whether inlining was performed.
+
+ */
+ def analyzeInc(i: CALL_METHOD, bb: BasicBlock, receiver: Symbol, stackLength: Int, concreteMethod: Symbol): Boolean = {
+ var inlined = false
+ val msym = i.method
def warnNoInline(reason: String) = {
if (hasInline(msym) && !caller.isBridge)
@@ -209,7 +329,7 @@ abstract class Inliners extends SubComponent {
val inc = new IMethodInfo(callee)
val pair = new CallerCalleeInfo(caller, inc, fresh, inlinedMethodCount)
- if (pair isStampedForInlining info.stack) {
+ if (pair isStampedForInlining stackLength) {
retry = true
inlined = true
if (isCountable)
@@ -228,9 +348,9 @@ abstract class Inliners extends SubComponent {
}
else {
if (settings.debug.value)
- pair logFailure info.stack
+ pair logFailure stackLength
- warnNoInline(pair failureReason info.stack)
+ warnNoInline(pair failureReason stackLength)
}
case None =>
warnNoInline("bytecode was not available")
@@ -241,38 +361,96 @@ abstract class Inliners extends SubComponent {
if (!isAvailable) "bytecode was not available"
else "it can be overridden"
)
+
inlined
}
- import scala.util.control.Breaks._
+ /* Pre-inlining consists in invoking the usual inlining subroutine with (receiver class, concrete method) pairs as input
+ * where both method and receiver are final, which implies that the receiver computed via TFA will always match `concreteMethod.owner`.
+ *
+ * As with any invocation of `analyzeInc()` the inlining outcome is based on heuristics which favor inlining an isMonadicMethod before other methods.
+ * That's why preInline() is invoked twice: any inlinings downplayed by the heuristics during the first round get an opportunity to rank higher during the second.
+ *
+ * As a whole, both `preInline()` invocations amount to priming the inlining process,
+ * so that the first TFA run afterwards is able to gain more information as compared to a cold-start.
+ */
+ val totalPreInlines = {
+ val firstRound = preInline(true)
+ if(firstRound == 0) 0 else (firstRound + preInline(false))
+ }
+ staleOut.clear()
+ splicedBlocks.clear()
+ staleIn.clear()
+
do {
retry = false
log("Analyzing " + m + " count " + count + " with " + caller.length + " blocks")
+
+ /* it's important not to inline in unreachable basic blocks. linearizedBlocks() returns only reachable ones. */
+ tfa.callerLin = caller.m.linearizedBlocks()
+ /* TODO Do we want to perform inlining in non-finally exception handlers?
+ * Seems counterproductive (the larger the method the less likely it will be JITed).
+ * The alternative above would be `linearizer.linearizeAt(caller.m, caller.m.startBlock)`.
+ * See also comment on the same topic in TypeFlowAnalysis. */
+
tfa.reinit(m, staleOut.toList, splicedBlocks, staleIn)
tfa.run
staleOut.clear()
splicedBlocks.clear()
staleIn.clear()
- caller.m.linearizedBlocks() foreach { bb =>
- info = tfa in bb
-
+ import scala.util.control.Breaks._
+ for(bb <- tfa.callerLin; if tfa.preCandidates(bb)) {
+ val cms = bb.toList collect { case cm : CALL_METHOD => cm }
breakable {
- for (i <- bb) {
- i match {
- // Dynamic == normal invocations
- // Static(true) == calls to private members
- case CALL_METHOD(msym, Dynamic | Static(true)) if !msym.isConstructor =>
- if (analyzeInc(msym, i, bb)) {
- break
- }
- case _ => ()
+ for (cm <- cms; if tfa.remainingCALLs.isDefinedAt(cm)) {
+ val analysis.CallsiteInfo(_, receiver, stackLength, concreteMethod) = tfa.remainingCALLs(cm)
+ if (analyzeInc(cm, bb, receiver, stackLength, concreteMethod)) {
+ break
}
- info = tfa.interpret(info, i)
}
}
+ }
+
+ /* As part of inlining, some instructions are moved to a new block.
+ * In detail: the instructions moved to a new block originally appeared after a (by now inlined) callsite.
+ * Their new home is an `afterBlock` created by `doInline()` to that effect.
+ * Each block in staleIn is one such `afterBlock`.
+ *
+ * Some of those instructions may be CALL_METHOD possibly tracked in `remainingCALLs`
+ * (with an entry still noting the old containing block). However, that causes no problem:
+ *
+ * (1) such callsites won't be analyzed for inlining by `analyzeInc()` (*in this iteration*)
+ * because of the `break` that abandons the original basic block where it was contained.
+ *
+ * (2) Additionally, its new containing block won't be visited either (*in this iteration*)
+ * because the new blocks don't show up in the linearization computed before inlinings started:
+ * `for(bb <- tfa.callerLin; if tfa.preCandidates(bb)) {`
+ *
+ * For a next iteration, the new home of any instructions that have moved
+ * will be tracked properly in `remainingCALLs` after `MTFAGrowable.reinit()` puts on radar their new homes.
+ *
+ */
+ if(retry) {
+ for(afterBlock <- staleIn) {
+ val justCALLsAfter = afterBlock.toList collect { case c : opcodes.CALL_METHOD => c }
+ for(ia <- justCALLsAfter) { tfa.remainingCALLs.remove(ia) }
+ }
+ }
+ /*
+ if(splicedBlocks.nonEmpty) { // TODO explore (saves time but leads to slightly different inlining decisions)
+ // opportunistically perform straightforward inlinings before the next typeflow round
+ val savedRetry = retry
+ val savedStaleOut = staleOut.toSet; staleOut.clear()
+ val savedStaleIn = staleIn.toSet ; staleIn.clear()
+ val howmany = inlineWithoutTFA(splicedBlocks, tfa.knownBeforehand)
+ splicedBlocks ++= staleIn
+ staleOut.clear(); staleOut ++= savedStaleOut;
+ staleIn.clear(); staleIn ++= savedStaleIn;
+ retry = savedRetry
}
+ */
if (tfa.stat)
log(m.symbol.fullName + " iterations: " + tfa.iterations + " (size: " + caller.length + ")")
@@ -288,15 +466,10 @@ abstract class Inliners extends SubComponent {
}
}
- private def isMonadicMethod(sym: Symbol) = {
- nme.unspecializedName(sym.name) match {
- case nme.foreach | nme.filter | nme.withFilter | nme.map | nme.flatMap => true
- case _ => false
- }
- }
-
- private def isHigherOrderMethod(sym: Symbol) =
- sym.isMethod && atPhase(currentRun.erasurePhase.prev)(sym.info.paramTypes exists isFunctionType)
+ private def isHigherOrderMethod(sym: Symbol) = (
+ sym.isMethod
+ && beforeExplicitOuter(sym.info.paramTypes exists isFunctionType) // was "at erasurePhase.prev"
+ )
/** Should method 'sym' being called in 'receiver' be loaded from disk? */
def shouldLoadImplFor(sym: Symbol, receiver: Symbol): Boolean = {
@@ -308,33 +481,6 @@ abstract class Inliners extends SubComponent {
res
}
- /** Look up implementation of method 'sym in 'clazz'.
- */
- def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = {
- // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info)
- def needsLookup = (
- (clazz != NoSymbol)
- && (clazz != sym.owner)
- && !sym.isEffectivelyFinal
- && clazz.isEffectivelyFinal
- )
- def lookup(clazz: Symbol): Symbol = {
- // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner)
- if (sym.owner == clazz || isBottomType(clazz)) sym
- else sym.overridingSymbol(clazz) match {
- case NoSymbol => if (sym.owner.isTrait) sym else lookup(clazz.superClass)
- case imp => imp
- }
- }
- if (needsLookup) {
- val concreteMethod = lookup(clazz)
- debuglog("\tlooked up method: " + concreteMethod.fullName)
-
- concreteMethod
- }
- else sym
- }
-
class IMethodInfo(val m: IMethod) {
val sym = m.symbol
val name = sym.name
@@ -386,10 +532,13 @@ abstract class Inliners extends SubComponent {
/** Inline 'inc' into 'caller' at the given block and instruction.
* The instruction must be a CALL_METHOD.
*/
- def doInline(block: BasicBlock, instr: Instruction) {
+ def doInline(block: BasicBlock, instr: CALL_METHOD) {
staleOut += block
+ tfa.remainingCALLs.remove(instr) // this bookkpeeping is done here and not in MTFAGrowable.reinit due to (1st) convenience and (2nd) necessity.
+ tfa.isOnWatchlist.remove(instr) // ditto
+
val targetPos = instr.pos
log("Inlining " + inc.m + " in " + caller.m + " at pos: " + posToStr(targetPos))
@@ -403,9 +552,9 @@ abstract class Inliners extends SubComponent {
val activeHandlers = caller.handlers filter (_ covered block)
/* Map 'original' blocks to the ones inlined in the caller. */
- val inlinedBlock: mutable.Map[BasicBlock, BasicBlock] = new mutable.HashMap
+ val inlinedBlock = mutable.Map[BasicBlock, BasicBlock]()
- val varsInScope: mutable.Set[Local] = mutable.HashSet() ++= block.varsInScope
+ val varsInScope = mutable.HashSet[Local]() ++= block.varsInScope
/** Side effects varsInScope when it sees SCOPE_ENTERs. */
def instrBeforeFilter(i: Instruction): Boolean = {
@@ -557,10 +706,11 @@ abstract class Inliners extends SubComponent {
if (settings.debug.value) icodes.checkValid(caller.m)
}
- def isStampedForInlining(stack: TypeStack) =
- !sameSymbols && inc.m.hasCode && shouldInline && isSafeToInline(stack)
+ def isStampedForInlining(stackLength: Int) =
+ !sameSymbols && inc.m.hasCode && shouldInline &&
+ isSafeToInline(stackLength) // `isSafeToInline()` must be invoked last in this AND expr bc it mutates the `knownSafe` and `knownUnsafe` maps for good.
- def logFailure(stack: TypeStack) = log(
+ def logFailure(stackLength: Int) = log(
"""|inline failed for %s:
| pair.sameSymbols: %s
| inc.numInlined < 2: %s
@@ -569,13 +719,14 @@ abstract class Inliners extends SubComponent {
| shouldInline: %s
""".stripMargin.format(
inc.m, sameSymbols, inlinedMethodCount(inc.sym) < 2,
- inc.m.hasCode, isSafeToInline(stack), shouldInline
+ inc.m.hasCode, isSafeToInline(stackLength), shouldInline
)
)
- def failureReason(stack: TypeStack) =
+ def failureReason(stackLength: Int) =
if (!inc.m.hasCode) "bytecode was unavailable"
- else if (!isSafeToInline(stack)) "it is unsafe (target may reference private fields)"
+ else if (inc.m.symbol.hasFlag(Flags.SYNCHRONIZED)) "method is synchronized"
+ else if (!isSafeToInline(stackLength)) "it is unsafe (target may reference private fields)"
else "of a bug (run with -Ylog:inline -Ydebug for more information)"
def canAccess(level: NonPublicRefs.Value) = level match {
@@ -587,15 +738,26 @@ abstract class Inliners extends SubComponent {
private def sameOwner = caller.owner == inc.owner
/** A method is safe to inline when:
- * - it does not contain calls to private methods when
- * called from another class
+ * - it does not contain calls to private methods when called from another class
* - it is not inlined into a position with non-empty stack,
* while having a top-level finalizer (see liftedTry problem)
* - it is not recursive
* Note:
* - synthetic private members are made public in this pass.
*/
- def isSafeToInline(stack: TypeStack): Boolean = {
+ def isSafeToInline(stackLength: Int): Boolean = {
+
+ if(tfa.blackballed(inc.sym)) { return false }
+ if(tfa.knownSafe(inc.sym)) { return true }
+
+ if(helperIsSafeToInline(stackLength)) {
+ tfa.knownSafe += inc.sym; true
+ } else {
+ tfa.knownUnsafe += inc.sym; false
+ }
+ }
+
+ private def helperIsSafeToInline(stackLength: Int): Boolean = {
def makePublic(f: Symbol): Boolean =
(inc.m.sourceFile ne NoSourceFile) && (f.isSynthetic || f.isParamAccessor) && {
debuglog("Making not-private symbol out of synthetic: " + f)
@@ -604,8 +766,8 @@ abstract class Inliners extends SubComponent {
true
}
- if (!inc.m.hasCode || inc.isRecursive)
- return false
+ if (!inc.m.hasCode || inc.isRecursive) { return false }
+ if (inc.m.symbol.hasFlag(Flags.SYNCHRONIZED)) { return false }
val accessNeeded = usesNonPublics.getOrElseUpdate(inc.m, {
// Avoiding crashing the compiler if there are open blocks.
@@ -642,9 +804,10 @@ abstract class Inliners extends SubComponent {
})
canAccess(accessNeeded) && {
- val isIllegalStack = (stack.length > inc.minimumStack && inc.hasNonFinalizerHandler)
+ val isIllegalStack = (stackLength > inc.minimumStack && inc.hasNonFinalizerHandler)
+
!isIllegalStack || {
- debuglog("method " + inc.sym + " is used on a non-empty stack with finalizer. Stack: " + stack)
+ debuglog("method " + inc.sym + " is used on a non-empty stack with finalizer.")
false
}
}
diff --git a/src/compiler/scala/tools/nsc/dependencies/Changes.scala b/src/compiler/scala/tools/nsc/dependencies/Changes.scala
index 089ef9cf35..176c00c025 100644
--- a/src/compiler/scala/tools/nsc/dependencies/Changes.scala
+++ b/src/compiler/scala/tools/nsc/dependencies/Changes.scala
@@ -18,7 +18,7 @@ abstract class Changes {
abstract class Change
- private lazy val annotationsChecked =
+ private lazy val annotationsChecked =
List(definitions.SpecializedClass) // Any others that should be checked?
private val flagsToCheck = IMPLICIT | FINAL | PRIVATE | PROTECTED | SEALED |
diff --git a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala
index bd890b7194..02be916f59 100644
--- a/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/dependencies/DependencyAnalysis.scala
@@ -145,10 +145,8 @@ trait DependencyAnalysis extends SubComponent with Files {
val name = d.toString
d.symbol match {
case s : ModuleClassSymbol =>
- val isTopLevelModule =
- atPhase (currentRun.picklerPhase.next) {
- !s.isImplClass && !s.isNestedClass
- }
+ val isTopLevelModule = afterPickler { !s.isImplClass && !s.isNestedClass }
+
if (isTopLevelModule && (s.companionModule != NoSymbol)) {
dependencies.emits(source, nameToFile(unit.source.file, name))
}
@@ -182,16 +180,18 @@ trait DependencyAnalysis extends SubComponent with Files {
|| (tree.symbol.sourceFile.path != file.path))
&& (!tree.symbol.isClassConstructor)) {
updateReferences(tree.symbol.fullName)
- atPhase(currentRun.uncurryPhase.prev) {
- checkType(tree.symbol.tpe)
- }
+ // was "at uncurryPhase.prev", which is actually non-deterministic
+ // because the continuations plugin may or may not supply uncurry's
+ // immediately preceding phase.
+ beforeRefchecks(checkType(tree.symbol.tpe))
}
tree match {
case cdef: ClassDef if !cdef.symbol.hasPackageFlag &&
!cdef.symbol.isAnonymousFunction =>
if (cdef.symbol != NoSymbol) buf += cdef.symbol
- atPhase(currentRun.erasurePhase.prev) {
+ // was "at erasurePhase.prev"
+ beforeExplicitOuter {
for (s <- cdef.symbol.info.decls)
s match {
case ts: TypeSymbol if !ts.isClass =>
@@ -202,9 +202,8 @@ trait DependencyAnalysis extends SubComponent with Files {
super.traverse(tree)
case ddef: DefDef =>
- atPhase(currentRun.typerPhase.prev) {
- checkType(ddef.symbol.tpe)
- }
+ // was "at typer.prev"
+ beforeTyper { checkType(ddef.symbol.tpe) }
super.traverse(tree)
case a @ Select(q, n) if ((a.symbol != NoSymbol) && (q.symbol != null)) => // #2556
if (!a.symbol.isConstructor &&
diff --git a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala
index c21507ef45..4f05678d85 100644
--- a/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/html/HtmlFactory.scala
@@ -80,9 +80,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) {
"selected.png",
"selected2-right.png",
"selected2.png",
- "unselected.png",
-
- "rootdoc.txt"
+ "unselected.png"
)
/** Generates the Scaladoc site for a model into the site root.
diff --git a/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt b/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt
deleted file mode 100644
index 6145429f1e..0000000000
--- a/src/compiler/scala/tools/nsc/doc/html/resource/lib/rootdoc.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-This is the documentation for the Scala standard library.
-
-== Package structure ==
-
-The [[scala]] package contains core types.
-
-scala.[[scala.collection]] and its subpackages contain a collections framework with higher-order functions for manipulation. Both [[scala.collection.immutable]] and [[scala.collection.mutable]] data structures are available, with immutable as the default. The [[scala.collection.parallel]] collections provide automatic parallel operation.
-
-Other important packages include:
-
- - scala.[[scala.actors]] - Concurrency framework inspired by Erlang.
- - scala.[[scala.io]] - Input and output.
- - scala.[[scala.math]] - Basic math functions and additional numeric types.
- - scala.[[scala.sys]] - Interaction with other processes and the operating system.
- - scala.util.[[scala.util.matching]] - Pattern matching in text using regular expressions.
- - scala.util.parsing.[[scala.util.parsing.combinator]] - Composable combinators for parsing.
- - scala.[[scala.xml]] - XML parsing, manipulation, and serialization.
-
-Many other packages exist. See the complete list on the left.
-
-== Automatic imports ==
-
-Identifiers in the scala package and the [[scala.Predef]] object are always in scope by default.
-
-Some of these identifiers are type aliases provided as shortcuts to commonly used classes. For example, List is an alias for scala.collection.immutable.[[scala.collection.immutable.List]].
-
-Other aliases refer to classes providing by the underlying platform. For example, on the JVM, String is an alias for java.lang.String.
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
index ec9f705f5a..496d004fd8 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala
@@ -104,7 +104,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
case mb: NonTemplateMemberEntity if (mb.useCaseOf.isDefined) =>
mb.useCaseOf.get.inDefinitionTemplates
case _ =>
- if (inTpl == null)
+ if (inTpl == null)
makeRootPackage.toList
else
makeTemplate(sym.owner) :: (sym.allOverriddenSymbols map { inhSym => makeTemplate(inhSym.owner) })
@@ -123,14 +123,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) {
else Public()
}
}
- def flags = {
+ def flags = {
val fgs = mutable.ListBuffer.empty[Paragraph]
if (sym.isImplicit) fgs += Paragraph(Text("implicit"))
if (sym.isSealed) fgs += Paragraph(Text("sealed"))
if (!sym.isTrait && (sym hasFlag Flags.ABSTRACT)) fgs += Paragraph(Text("abstract"))
if (!sym.isTrait && (sym hasFlag Flags.DEFERRED)) fgs += Paragraph(Text("abstract"))
if (!sym.isModule && (sym hasFlag Flags.FINAL)) fgs += Paragraph(Text("final"))
- fgs.toList
+ fgs.toList
}
def deprecation =
if (sym.isDeprecated)
diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
index f2d59206e0..1b91b06942 100644
--- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
+++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
@@ -10,6 +10,8 @@ import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.{SourceFile, Position, WorkScheduler}
import scala.tools.nsc.symtab._
import scala.tools.nsc.ast._
+import scala.tools.nsc.util.FailedInterrupt
+import scala.tools.nsc.util.EmptyAction
/** Interface of interactive compiler to a client such as an IDE
* The model the presentation compiler consists of the following parts:
@@ -48,7 +50,7 @@ trait CompilerControl { self: Global =>
/** The scheduler by which client and compiler communicate
* Must be initialized before starting compilerRunner
*/
- protected[interactive] val scheduler = new WorkScheduler
+ @volatile protected[interactive] var scheduler = new WorkScheduler
/** Return the compilation unit attached to a source file, or None
* if source is not loaded.
@@ -374,6 +376,25 @@ trait CompilerControl { self: Global =>
response raise new MissingResponse
}
+ /** A do-nothing work scheduler that responds immediately with MissingResponse.
+ *
+ * Used during compiler shutdown.
+ */
+ class NoWorkScheduler extends WorkScheduler {
+
+ override def postWorkItem(action: Action) = synchronized {
+ action match {
+ case w: WorkItem => w.raiseMissing()
+ case e: EmptyAction => // do nothing
+ case _ => println("don't know what to do with this " + action.getClass)
+ }
+ }
+
+ override def doQuickly[A](op: () => A): A = {
+ throw new FailedInterrupt(new Exception("Posted a work item to a compiler that's shutting down"))
+ }
+ }
+
}
// ---------------- Interpreted exceptions -------------------
diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala
index 477cec8c8e..166b38f503 100644
--- a/src/compiler/scala/tools/nsc/interactive/Global.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Global.scala
@@ -357,6 +357,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
checkNoResponsesOutstanding()
log.flush();
+ scheduler = new NoWorkScheduler
throw ShutdownReq
}
@@ -609,6 +610,15 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "")
}
response raise ex
throw ex
+
+ case ex @ ShutdownReq =>
+ if (debugIDE) {
+ println("ShutdownReq thrown during response")
+ ex.printStackTrace()
+ }
+ response raise ex
+ throw ex
+
case ex =>
if (debugIDE) {
println("exception thrown during response: "+ex)
diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala
index f251fd83fb..bad181eb76 100644
--- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala
+++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala
@@ -22,6 +22,7 @@ import scala.tools.util.PathResolver
* changes require a compilation. It repeats this process until
* a fixpoint is reached.
*/
+@deprecated("Use sbt incremental compilation mechanism", "2.10.0")
class RefinedBuildManager(val settings: Settings) extends Changes with BuildManager {
class BuilderGlobal(settings: Settings, reporter : Reporter) extends scala.tools.nsc.Global(settings, reporter) {
@@ -47,7 +48,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana
protected def newCompiler(settings: Settings) = new BuilderGlobal(settings)
val compiler = newCompiler(settings)
- import compiler.{Symbol, Type, atPhase, currentRun}
+ import compiler.{ Symbol, Type, beforeErasure }
import compiler.dependencyAnalysis.Inherited
private case class SymWithHistory(sym: Symbol, befErasure: Type)
@@ -159,10 +160,8 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana
isCorrespondingSym(s.sym, sym)) match {
case Some(SymWithHistory(oldSym, info)) =>
val changes = changeSet(oldSym.info, sym)
- val changesErasure =
- atPhase(currentRun.erasurePhase.prev) {
- changeSet(info, sym)
- }
+ val changesErasure = beforeErasure(changeSet(info, sym))
+
changesOf(oldSym) = (changes ++ changesErasure).distinct
case _ =>
// a new top level definition
@@ -332,11 +331,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana
for (src <- files; localDefs = compiler.dependencyAnalysis.definitions(src)) {
definitions(src) = (localDefs map (s => {
this.classes += s.fullName -> src
- SymWithHistory(
- s.cloneSymbol,
- atPhase(currentRun.erasurePhase.prev) {
- s.info.cloneInfo(s)
- })
+ SymWithHistory(s.cloneSymbol, beforeErasure(s.info.cloneInfo(s)))
}))
}
this.references = compiler.dependencyAnalysis.references
diff --git a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala
index 9f5fde70d8..68c8f2fdb8 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ExprTyper.scala
@@ -62,7 +62,7 @@ trait ExprTyper {
else Some(trees)
}
}
- def tokens(line: String) = beSilentDuring(codeParser.tokens(line))
+ def tokens(line: String) = beQuietDuring(codeParser.tokens(line))
// TODO: integrate these into a CodeHandler[Type].
@@ -92,7 +92,7 @@ trait ExprTyper {
case _ => NoType
}
}
-
+
def evaluate(): Type = {
typeOfExpressionDepth += 1
try typeOfTerm(expr) orElse asModule orElse asExpr orElse asQualifiedImport
diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
index 7c71438b98..e1ea69842f 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala
@@ -324,7 +324,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
private def implicitsCommand(line: String): Result = {
val intp = ILoop.this.intp
import intp._
- import global.Symbol
+ import global.{ Symbol, afterTyper }
def p(x: Any) = intp.reporter.printMessage("" + x)
@@ -348,7 +348,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
// This groups the members by where the symbol is defined
val byOwner = syms groupBy (_.owner)
- val sortedOwners = byOwner.toList sortBy { case (owner, _) => intp.afterTyper(source.info.baseClasses indexOf owner) }
+ val sortedOwners = byOwner.toList sortBy { case (owner, _) => afterTyper(source.info.baseClasses indexOf owner) }
sortedOwners foreach {
case (owner, members) =>
@@ -382,7 +382,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
private def findToolsJar() = {
val jdkPath = Directory(jdkHome)
val jar = jdkPath / "lib" / "tools.jar" toFile;
-
+
if (jar isFile)
Some(jar)
else if (jdkPath.isDirectory)
@@ -440,7 +440,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
else {
val tp = intp.typeOfExpression(line, false)
if (tp == NoType) "" // the error message was already printed
- else intp.afterTyper(tp.toString)
+ else intp.global.afterTyper(tp.toString)
}
}
private def warningsCommand(): Result = {
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 6ae8d0e7d0..9a12bc1471 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -230,9 +230,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
} with MemberHandlers
import memberHandlers._
- def atPickler[T](op: => T): T = atPhase(currentRun.picklerPhase)(op)
- def afterTyper[T](op: => T): T = atPhase(currentRun.typerPhase.next)(op)
-
/** Temporarily be quiet */
def beQuietDuring[T](body: => T): T = {
val saved = printResults
@@ -787,10 +784,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
}
def compile(source: String): Boolean = compileAndSaveRun("<console>", source)
- def lineAfterTyper[T](op: => T): T = {
- assert(lastRun != null, "Internal error: trying to use atPhase, but Run is null." + this)
- atPhase(lastRun.typerPhase.next)(op)
- }
/** The innermost object inside the wrapper, found by
* following accessPath into the outer one.
@@ -799,7 +792,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
val readRoot = getRequiredModule(readPath) // the outermost wrapper
(accessPath split '.').foldLeft(readRoot) { (sym, name) =>
if (name == "") sym else
- lineAfterTyper(sym.info member newTermName(name))
+ afterTyper(sym.info member newTermName(name))
}
}
/** We get a bunch of repeated warnings for reasons I haven't
@@ -842,7 +835,6 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
// private
class Request(val line: String, val trees: List[Tree]) {
val lineRep = new ReadEvalPrint()
- import lineRep.lineAfterTyper
private var _originalLine: String = null
def withOriginalLine(s: String): this.type = { _originalLine = s ; this }
@@ -906,11 +898,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
if (!handlers.last.definesValue) ""
else handlers.last.definesTerm match {
case Some(vname) if typeOf contains vname =>
- """
- |lazy val %s = {
- | %s
- | %s
- |}""".stripMargin.format(lineRep.resultName, lineRep.printName, fullPath(vname))
+ "lazy val %s = %s".format(lineRep.resultName, fullPath(vname))
case _ => ""
}
// first line evaluates object to make sure constructor is run
@@ -956,7 +944,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
typesOfDefinedTerms
// compile the result-extraction object
- beSilentDuring {
+ beQuietDuring {
savingSettings(_.nowarn.value = true) {
lineRep compile ResultObjectSourceCode(handlers)
}
@@ -965,7 +953,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
}
lazy val resultSymbol = lineRep.resolvePathToSymbol(accessPath)
- def applyToResultMember[T](name: Name, f: Symbol => T) = lineAfterTyper(f(resultSymbol.info.nonPrivateDecl(name)))
+ def applyToResultMember[T](name: Name, f: Symbol => T) = afterTyper(f(resultSymbol.info.nonPrivateDecl(name)))
/* typeOf lookup with encoding */
def lookupTypeOf(name: Name) = typeOf.getOrElse(name, typeOf(global.encode(name.toString)))
diff --git a/src/compiler/scala/tools/nsc/interpreter/Imports.scala b/src/compiler/scala/tools/nsc/interpreter/Imports.scala
index d34ca8bbca..d579e0369e 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Imports.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Imports.scala
@@ -61,7 +61,7 @@ trait Imports {
def importedTypeSymbols = importedSymbols collect { case x: TypeSymbol => x }
def implicitSymbols = importedSymbols filter (_.isImplicit)
- def importedTermNamed(name: String): Symbol =
+ def importedTermNamed(name: String): Symbol =
importedTermSymbols find (_.name.toString == name) getOrElse NoSymbol
/** Tuples of (source, imported symbols) in the order they were imported.
@@ -191,5 +191,5 @@ trait Imports {
prevRequestList flatMap (req => req.handlers map (req -> _))
private def membersAtPickler(sym: Symbol): List[Symbol] =
- atPickler(sym.info.nonPrivateMembers)
+ beforePickler(sym.info.nonPrivateMembers)
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala
index d96e8b07fc..f9c1907696 100644
--- a/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/JLineCompletion.scala
@@ -18,7 +18,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
import global._
import definitions.{ PredefModule, RootClass, AnyClass, AnyRefClass, ScalaPackage, JavaLangPackage, getModuleIfDefined }
type ExecResult = Any
- import intp.{ debugging, afterTyper }
+ import intp.{ debugging }
// verbosity goes up with consecutive tabs
private var verbosity: Int = 0
@@ -61,7 +61,7 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
def packageNames = packages map tos
def aliasNames = aliases map tos
}
-
+
object NoTypeCompletion extends TypeMemberCompletion(NoType) {
override def memberNamed(s: String) = NoSymbol
override def members = Nil
@@ -165,11 +165,11 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput
override def follow(id: String): Option[CompletionAware] = {
if (!completions(0).contains(id))
return None
-
+
val tpe = intp typeOfExpression id
if (tpe == NoType)
return None
-
+
def default = Some(TypeMemberCompletion(tpe))
// only rebinding vals in power mode for now.
diff --git a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
index 37dd032135..68bfeafbc6 100644
--- a/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/MemberHandlers.scala
@@ -13,7 +13,7 @@ import scala.reflect.internal.Chars
trait MemberHandlers {
val intp: IMain
- import intp.{ Request, global, naming, atPickler }
+ import intp.{ Request, global, naming }
import global._
import naming._
@@ -118,8 +118,9 @@ trait MemberHandlers {
class DefHandler(member: DefDef) extends MemberDefHandler(member) {
private def vparamss = member.vparamss
- // true if 0-arity
- override def definesValue = vparamss.isEmpty || vparamss.head.isEmpty
+ private def isMacro = member.mods.hasFlag(scala.reflect.internal.Flags.MACRO)
+ // true if not a macro and 0-arity
+ override def definesValue = !isMacro && (vparamss.isEmpty || vparamss.head.isEmpty)
override def resultExtractionCode(req: Request) =
if (mods.isPublic) codegenln(name, ": ", req.typeOf(name)) else ""
}
@@ -199,10 +200,10 @@ trait MemberHandlers {
def importedSymbols = individualSymbols ++ wildcardSymbols
lazy val individualSymbols: List[Symbol] =
- atPickler(individualNames map (targetType nonPrivateMember _))
+ beforePickler(individualNames map (targetType nonPrivateMember _))
lazy val wildcardSymbols: List[Symbol] =
- if (importsWildcard) atPickler(targetType.nonPrivateMembers)
+ if (importsWildcard) beforePickler(targetType.nonPrivateMembers)
else Nil
/** Complete list of names imported by a wildcard */
diff --git a/src/compiler/scala/tools/nsc/interpreter/Power.scala b/src/compiler/scala/tools/nsc/interpreter/Power.scala
index 835fbb5638..14876425f4 100644
--- a/src/compiler/scala/tools/nsc/interpreter/Power.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/Power.scala
@@ -15,6 +15,31 @@ import scala.io.Codec
import java.net.{ URL, MalformedURLException }
import io.{ Path }
+/** Collecting some power mode examples.
+
+scala> trait F[@specialized(Int) T] { def f: T = ??? }
+defined trait F
+
+scala> trait G[@specialized(Long, Int) T] extends F[T] { override def f: T = super.f }
+defined trait G
+
+scala> changesAfterEachPhase(intp("G").info.members filter (_.name.toString contains "super")) >
+Gained after 1/parser {
+ method super$f
+}
+
+Gained after 12/specialize {
+ method super$f$mcJ$sp
+ method super$f$mcI$sp
+}
+
+Lost after 18/flatten {
+ method super$f$mcJ$sp
+ method super$f$mcI$sp
+ method super$f
+}
+*/
+
/** A class for methods to be injected into the intp in power mode.
*/
class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: ReplValsImpl) {
@@ -130,7 +155,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl
( rutil.info[ReplValsImpl].membersDeclared
filter (m => m.isPublic && !m.hasModuleFlag && !m.isConstructor)
- sortBy (_.decodedName)
+ sortBy (_.decodedName)
map to_str
mkString ("Name and type of values imported into the repl in power mode.\n\n", "\n", "")
)
@@ -140,7 +165,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl
implicit def apply[T: Manifest] : InternalInfo[T] = new InternalInfo[T](None)
}
object InternalInfo extends LowPriorityInternalInfo { }
-
+
/** Now dealing with the problem of acidentally calling a method on Type
* when you're holding a Symbol and seeing the Symbol converted to the
* type of Symbol rather than the type of the thing represented by the
@@ -151,7 +176,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl
implicit def apply[T: Manifest] : InternalInfoWrapper[T] = new InternalInfoWrapper[T](None)
}
object InternalInfoWrapper extends LowPriorityInternalInfoWrapper {
-
+
}
class InternalInfoWrapper[T: Manifest](value: Option[T] = None) {
def ? : InternalInfo[T] = new InternalInfo[T](value)
@@ -165,7 +190,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl
private def newInfo[U: Manifest](value: U): InternalInfo[U] = new InternalInfo[U](Some(value))
private def isSpecialized(s: Symbol) = s.name.toString contains "$mc"
private def isImplClass(s: Symbol) = s.name.toString endsWith "$class"
-
+
/** Standard noise reduction filter. */
def excludeMember(s: Symbol) = (
isSpecialized(s)
@@ -193,7 +218,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl
def membersInherited = members filterNot (membersDeclared contains _)
def memberTypes = members filter (_.name.isTypeName)
def memberMethods = members filter (_.isMethod)
-
+
def pkg = symbol.enclosingPackage
def pkgName = pkg.fullName
def pkgClass = symbol.enclosingPackageClass
@@ -318,12 +343,12 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl
def sigs = syms map (_.defString)
def infos = syms map (_.info)
}
-
+
trait Implicits1 {
// fallback
implicit def replPrinting[T](x: T)(implicit pretty: Prettifier[T] = Prettifier.default[T]) =
new SinglePrettifierClass[T](x)
-
+
implicit def liftToTypeName(s: String): TypeName = newTypeName(s)
}
trait Implicits2 extends Implicits1 {
@@ -350,7 +375,7 @@ class Power[ReplValsImpl <: ReplVals : Manifest](val intp: IMain, replVals: Repl
implicit def replInputStream(in: InputStream)(implicit codec: Codec) = new RichInputStream(in)
implicit def replEnhancedURLs(url: URL)(implicit codec: Codec): RichReplURL = new RichReplURL(url)(codec)
-
+
implicit def liftToTermName(s: String): TermName = newTermName(s)
implicit def replListOfSymbols(xs: List[Symbol]) = new RichSymbolList(xs)
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
index dac20ad348..130af990ad 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplReporter.scala
@@ -14,7 +14,10 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i
// Avoiding deadlock if the compiler starts logging before
// the lazy val is complete.
if (intp.isInitializeComplete) {
- if (intp.totalSilence) ()
+ if (intp.totalSilence) {
+ if (isReplTrace)
+ super.printMessage("[silent] " + msg)
+ }
else super.printMessage(msg)
}
else Console.println("[init] " + msg)
diff --git a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala
index 6e5dec4205..a68392f0fb 100644
--- a/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/ReplVals.scala
@@ -50,7 +50,7 @@ object ReplVals {
def mkManifestToType[T <: Global](global: T) = {
import global._
import definitions._
-
+
/** We can't use definitions.manifestToType directly because we're passing
* it to map and the compiler refuses to perform eta expansion on a method
* with a dependent return type. (Can this be relaxed?) To get around this
@@ -59,15 +59,17 @@ object ReplVals {
*/
def manifestToType(m: OptManifest[_]): Global#Type =
definitions.manifestToType(m)
-
+
class AppliedTypeFromManifests(sym: Symbol) {
def apply[M](implicit m1: Manifest[M]): Type =
- appliedType(sym.typeConstructor, List(m1) map (x => manifestToType(x).asInstanceOf[Type]))
+ if (sym eq NoSymbol) NoType
+ else appliedType(sym.typeConstructor, List(m1) map (x => manifestToType(x).asInstanceOf[Type]))
def apply[M1, M2](implicit m1: Manifest[M1], m2: Manifest[M2]): Type =
- appliedType(sym.typeConstructor, List(m1, m2) map (x => manifestToType(x).asInstanceOf[Type]))
+ if (sym eq NoSymbol) NoType
+ else appliedType(sym.typeConstructor, List(m1, m2) map (x => manifestToType(x).asInstanceOf[Type]))
}
-
+
(sym: Symbol) => new AppliedTypeFromManifests(sym)
}
}
diff --git a/src/compiler/scala/tools/nsc/io/Pickler.scala b/src/compiler/scala/tools/nsc/io/Pickler.scala
index 5bb8bdda35..80b6e086da 100644
--- a/src/compiler/scala/tools/nsc/io/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/io/Pickler.scala
@@ -165,7 +165,7 @@ object Pickler {
def pkl[T: Pickler] = implicitly[Pickler[T]]
/** A class represenenting `~`-pairs */
- case class ~[S, T](fst: S, snd: T)
+ case class ~[+S, +T](fst: S, snd: T)
/** A wrapper class to be able to use `~` s an infix method */
class TildeDecorator[S](x: S) {
diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
index 0c94e40d68..06b06c50a6 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
@@ -393,8 +393,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
// assumed true unless we see public/private/protected
var isPackageAccess = true
var annots: List[Tree] = Nil
- def addAnnot(sym: Symbol) =
- annots :+= New(TypeTree(sym.tpe), List(Nil))
+ def addAnnot(sym: Symbol) = annots :+= New(sym.tpe)
while (true) {
in.token match {
@@ -654,15 +653,12 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
// leaves auxiliary constructors unable to access members of the companion object
// as unqualified identifiers.
def addCompanionObject(statics: List[Tree], cdef: ClassDef): List[Tree] = {
- def implWithImport(importStmt: Tree) = {
- import cdef.impl._
- treeCopy.Template(cdef.impl, parents, self, importStmt :: body)
- }
+ def implWithImport(importStmt: Tree) = deriveTemplate(cdef.impl)(importStmt :: _)
// if there are no statics we can use the original cdef, but we always
// create the companion so import A._ is not an error (see ticket #1700)
val cdefNew =
if (statics.isEmpty) cdef
- else treeCopy.ClassDef(cdef, cdef.mods, cdef.name, cdef.tparams, implWithImport(importCompanionObject(cdef)))
+ else deriveClassDef(cdef)(_ => implWithImport(importCompanionObject(cdef)))
List(makeCompanionObject(cdefNew, statics), cdefNew)
}
diff --git a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala
index 5e46960d04..371f4bc4d8 100644
--- a/src/compiler/scala/tools/nsc/matching/MatchSupport.scala
+++ b/src/compiler/scala/tools/nsc/matching/MatchSupport.scala
@@ -115,6 +115,10 @@ trait MatchSupport extends ast.TreeDSL { self: ParallelMatching =>
println(fmt.format(xs: _*) + " == " + x)
x
}
+ private[nsc] def debugging[T](fmt: String, xs: Any*)(x: T): T = {
+ if (settings.debug.value) printing(fmt, xs: _*)(x)
+ else x
+ }
def indent(s: Any) = s.toString() split "\n" map (" " + _) mkString "\n"
def indentAll(s: Seq[Any]) = s map (" " + _.toString() + "\n") mkString
diff --git a/src/compiler/scala/tools/nsc/matching/Matrix.scala b/src/compiler/scala/tools/nsc/matching/Matrix.scala
index d81f05cd51..e1ff88557e 100644
--- a/src/compiler/scala/tools/nsc/matching/Matrix.scala
+++ b/src/compiler/scala/tools/nsc/matching/Matrix.scala
@@ -198,6 +198,10 @@ trait Matrix extends MatrixAdditions {
class PatternVar(val lhs: Symbol, val rhs: Tree, val checked: Boolean) {
def sym = lhs
def tpe = lhs.tpe
+ if (checked)
+ lhs resetFlag NO_EXHAUSTIVE
+ else
+ lhs setFlag NO_EXHAUSTIVE
// See #1427 for an example of a crash which occurs unless we retype:
// in that instance there is an existential in the pattern.
@@ -207,11 +211,6 @@ trait Matrix extends MatrixAdditions {
override def toString() = "%s: %s = %s".format(lhs, tpe, rhs)
}
- /** Sets the rhs to EmptyTree, which makes the valDef ignored in Scrutinee.
- */
- def specialVar(lhs: Symbol, checked: Boolean) =
- new PatternVar(lhs, EmptyTree, checked)
-
/** Given a tree, creates a new synthetic variable of the same type
* and assigns the tree to it.
*/
diff --git a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala
index 24d3c38e74..e72a0007a0 100644
--- a/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala
+++ b/src/compiler/scala/tools/nsc/matching/MatrixAdditions.scala
@@ -131,23 +131,11 @@ trait MatrixAdditions extends ast.TreeDSL {
import Flags.{ MUTABLE, ABSTRACT, SEALED }
- private case class Combo(index: Int, sym: Symbol) {
- val isBaseClass = sym.tpe.baseClasses.toSet
-
- // is this combination covered by the given pattern?
- def isCovered(p: Pattern) = {
- def coversSym = isBaseClass(decodedEqualsType(p.tpe).typeSymbol)
-
- cond(p.tree) {
- case _: UnApply | _: ArrayValue => true
- case x => p.isDefault || coversSym
- }
- }
- }
+ private case class Combo(index: Int, sym: Symbol) { }
/* True if the patterns in 'row' cover the given type symbol combination, and has no guard. */
private def rowCoversCombo(row: Row, combos: List[Combo]) =
- row.guard.isEmpty && (combos forall (c => c isCovered row.pats(c.index)))
+ row.guard.isEmpty && combos.forall(c => row.pats(c.index) covers c.sym)
private def requiresExhaustive(sym: Symbol) = {
(sym.isMutable) && // indicates that have not yet checked exhaustivity
diff --git a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
index 9d4c9b4411..1285e29d4a 100644
--- a/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
+++ b/src/compiler/scala/tools/nsc/matching/ParallelMatching.scala
@@ -745,7 +745,7 @@ trait ParallelMatching extends ast.TreeDSL
(others.head :: _column.tail, make(_tvars, _rows))
def mix() = {
- val newScrut = new Scrutinee(specialVar(_pv.sym, _pv.checked))
+ val newScrut = new Scrutinee(new PatternVar(_pv.sym, EmptyTree, _pv.checked))
PatternMatch(newScrut, _ncol) mkRule _nrep
}
}
diff --git a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala b/src/compiler/scala/tools/nsc/matching/PatternBindings.scala
index 5dd7d8f3ee..56297f0195 100644
--- a/src/compiler/scala/tools/nsc/matching/PatternBindings.scala
+++ b/src/compiler/scala/tools/nsc/matching/PatternBindings.scala
@@ -19,9 +19,10 @@ trait PatternBindings extends ast.TreeDSL
import Debug._
/** EqualsPattern **/
- def isEquals(tpe: Type) = cond(tpe) { case TypeRef(_, EqualsPatternClass, _) => true }
+ def isEquals(tpe: Type) = tpe.typeSymbol == EqualsPatternClass
def mkEqualsRef(tpe: Type) = typeRef(NoPrefix, EqualsPatternClass, List(tpe))
- def decodedEqualsType(tpe: Type) = condOpt(tpe) { case TypeRef(_, EqualsPatternClass, List(arg)) => arg } getOrElse (tpe)
+ def decodedEqualsType(tpe: Type) =
+ if (tpe.typeSymbol == EqualsPatternClass) tpe.typeArgs.head else tpe
// A subtype test which creates fresh existentials for type
// parameters on the right hand side.
diff --git a/src/compiler/scala/tools/nsc/matching/Patterns.scala b/src/compiler/scala/tools/nsc/matching/Patterns.scala
index 18409cfffe..8bdf83fda4 100644
--- a/src/compiler/scala/tools/nsc/matching/Patterns.scala
+++ b/src/compiler/scala/tools/nsc/matching/Patterns.scala
@@ -26,19 +26,6 @@ trait Patterns extends ast.TreeDSL {
type PatternMatch = MatchMatrix#PatternMatch
private type PatternVar = MatrixContext#PatternVar
- // private def unapplyArgs(x: Any) = x match {
- // case UnApply(Apply(TypeApply(_, targs), args), _) => (targs map (_.symbol), args map (_.symbol))
- // case _ => (Nil, Nil)
- // }
- //
- // private def unapplyCall(x: Any) = x match {
- // case UnApply(t, _) => treeInfo.methPart(t).symbol
- // case _ => NoSymbol
- // }
-
- private lazy val dummyMethod =
- NoSymbol.newTermSymbol(newTermName("matching$dummy"))
-
// Fresh patterns
def emptyPatterns(i: Int): List[Pattern] = List.fill(i)(NoPattern)
def emptyTrees(i: Int): List[Tree] = List.fill(i)(EmptyTree)
@@ -56,13 +43,14 @@ trait Patterns extends ast.TreeDSL {
case class VariablePattern(tree: Ident) extends NamePattern {
lazy val Ident(name) = tree
require(isVarPattern(tree) && name != nme.WILDCARD)
-
+ override def covers(sym: Symbol) = true
override def description = "%s".format(name)
}
// 8.1.1 (b)
case class WildcardPattern() extends Pattern {
def tree = EmptyTree
+ override def covers(sym: Symbol) = true
override def isDefault = true
override def description = "_"
}
@@ -71,6 +59,8 @@ trait Patterns extends ast.TreeDSL {
case class TypedPattern(tree: Typed) extends Pattern {
lazy val Typed(expr, tpt) = tree
+ override def covers(sym: Symbol) = newMatchesPattern(sym, tpt.tpe)
+ override def sufficientType = tpt.tpe
override def subpatternsForVars: List[Pattern] = List(Pattern(expr))
override def simplify(pv: PatternVar) = Pattern(expr) match {
case ExtractorPattern(ua) if pv.sym.tpe <:< tpt.tpe => this rebindTo expr
@@ -115,6 +105,7 @@ trait Patterns extends ast.TreeDSL {
}
}
+ override def covers(sym: Symbol) = newMatchesPattern(sym, sufficientType)
override def simplify(pv: PatternVar) = this.rebindToObjectCheck()
override def description = backticked match {
case Some(s) => "this." + s
@@ -133,13 +124,15 @@ trait Patterns extends ast.TreeDSL {
case class ObjectPattern(tree: Apply) extends ApplyPattern { // NamePattern?
require(!fn.isType && isModule)
+ override def covers(sym: Symbol) = newMatchesPattern(sym, sufficientType)
override def sufficientType = tpe.narrow
override def simplify(pv: PatternVar) = this.rebindToObjectCheck()
override def description = "Obj(%s)".format(fn)
}
// 8.1.4 (e)
case class SimpleIdPattern(tree: Ident) extends NamePattern {
- lazy val Ident(name) = tree
+ val Ident(name) = tree
+ override def covers(sym: Symbol) = newMatchesPattern(sym, tpe.narrow)
override def description = "Id(%s)".format(name)
}
@@ -163,6 +156,11 @@ trait Patterns extends ast.TreeDSL {
if (args.isEmpty) this rebindToEmpty tree.tpe
else this
+ override def covers(sym: Symbol) = {
+ debugging("[constructor] Does " + this + " cover " + sym + " ? ") {
+ sym.tpe.typeSymbol == this.tpe.typeSymbol
+ }
+ }
override def description = {
if (isColonColon) "%s :: %s".format(Pattern(args(0)), Pattern(args(1)))
else "%s(%s)".format(name, toPats(args).mkString(", "))
@@ -175,17 +173,12 @@ trait Patterns extends ast.TreeDSL {
// 8.1.7 / 8.1.8 (unapply and unapplySeq calls)
case class ExtractorPattern(tree: UnApply) extends UnapplyPattern {
- override def simplify(pv: PatternVar) = {
- if (pv.sym hasFlag NO_EXHAUSTIVE) ()
- else {
- TRACE("Setting NO_EXHAUSTIVE on " + pv.sym + " due to extractor " + tree)
- pv.sym setFlag NO_EXHAUSTIVE
- }
+ private def uaTyped = Typed(tree, TypeTree(arg.tpe)) setType arg.tpe
+ override def simplify(pv: PatternVar) = {
if (pv.tpe <:< arg.tpe) this
else this rebindTo uaTyped
}
-
override def description = "Unapply(%s => %s)".format(necessaryType, resTypesString)
}
@@ -208,6 +201,7 @@ trait Patterns extends ast.TreeDSL {
private def listFolder(hd: Tree, tl: Tree): Tree = unbind(hd) match {
case t @ Star(_) => moveBindings(hd, WILD(t.tpe))
case _ =>
+ val dummyMethod = NoSymbol.newTermSymbol(newTermName("matching$dummy"))
val consType = MethodType(dummyMethod newSyntheticValueParams List(packedType, listRef), consRef)
Apply(TypeTree(consType), List(hd, tl)) setType consRef
@@ -376,7 +370,7 @@ trait Patterns extends ast.TreeDSL {
case _: This if isVariableName(name) => Some("`%s`".format(name))
case _ => None
}
-
+ override def covers(sym: Symbol) = newMatchesPattern(sym, tree.tpe)
protected def getPathSegments(t: Tree): List[Name] = t match {
case Select(q, name) => name :: getPathSegments(q)
case Apply(f, Nil) => getPathSegments(f)
@@ -395,7 +389,13 @@ trait Patterns extends ast.TreeDSL {
lazy val UnApply(unfn, args) = tree
lazy val Apply(fn, _) = unfn
lazy val MethodType(List(arg, _*), _) = fn.tpe
- protected def uaTyped = Typed(tree, TypeTree(arg.tpe)) setType arg.tpe
+
+ // Covers if the symbol matches the unapply method's argument type,
+ // and the return type of the unapply is Some.
+ override def covers(sym: Symbol) = newMatchesPattern(sym, arg.tpe)
+
+ // TODO: for alwaysCovers:
+ // fn.tpe.finalResultType.typeSymbol == SomeClass
override def necessaryType = arg.tpe
override def subpatternsForVars = args match {
@@ -419,6 +419,7 @@ trait Patterns extends ast.TreeDSL {
else emptyPatterns(sufficientType.typeSymbol.caseFieldAccessors.size)
def isConstructorPattern = fn.isType
+ override def covers(sym: Symbol) = newMatchesPattern(sym, fn.tpe)
}
sealed abstract class Pattern extends PatternBindingLogic {
@@ -443,6 +444,15 @@ trait Patterns extends ast.TreeDSL {
// the subpatterns for this pattern (at the moment, that means constructor arguments)
def subpatterns(pm: MatchMatrix#PatternMatch): List[Pattern] = pm.dummies
+ // if this pattern should be considered to cover the given symbol
+ def covers(sym: Symbol): Boolean = newMatchesPattern(sym, sufficientType)
+ def newMatchesPattern(sym: Symbol, pattp: Type) = {
+ debugging("[" + kindString + "] Does " + pattp + " cover " + sym + " ? ") {
+ (sym.isModuleClass && (sym.tpe.typeSymbol eq pattp.typeSymbol)) ||
+ (sym.tpe.baseTypeSeq exists (_ matchesPattern pattp))
+ }
+ }
+
def sym = tree.symbol
def tpe = tree.tpe
def isEmpty = tree.isEmpty
@@ -475,6 +485,7 @@ trait Patterns extends ast.TreeDSL {
final override def toString = description
def toTypeString() = "%s <: x <: %s".format(necessaryType, sufficientType)
+ def kindString = ""
}
/*** Extractors ***/
diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
index f19a285d7c..309fc5733f 100644
--- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala
@@ -56,10 +56,10 @@ abstract class Reporter {
*/
def echo(msg: String): Unit = info(NoPosition, msg, true)
def echo(pos: Position, msg: String): Unit = info(pos, msg, true)
-
+
/** Informational messages, suppressed unless -verbose or force=true. */
def info(pos: Position, msg: String, force: Boolean): Unit = info0(pos, msg, INFO, force)
-
+
/** Warnings and errors. */
def warning(pos: Position, msg: String): Unit = withoutTruncating(info0(pos, msg, WARNING, false))
def error(pos: Position, msg: String): Unit = withoutTruncating(info0(pos, msg, ERROR, false))
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index b468e7c0af..e7959f36b2 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -95,8 +95,7 @@ class MutableSettings(val errorFn: String => Unit)
*/
def copy(): Settings = {
val s = new Settings()
- val xs = userSetSettings flatMap (_.unparse)
- s.processArguments(xs.toList, true)
+ s.processArguments(recreateArgs, true)
s
}
@@ -534,7 +533,7 @@ class MutableSettings(val errorFn: String => Unit)
Some(rest)
}
override def tryToSetColon(args: List[String]) = tryToSet(args)
- override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(" +").toList)
+ override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList)
def unparse: List[String] = value map { name + ":" + _ }
withHelpSyntax(name + ":<" + arg + ">")
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index d1ce460eb9..e949cb3eb2 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -167,8 +167,7 @@ trait ScalaSettings extends AbsScalaSettings
val Yreifycopypaste =
BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
val Yreifydebug = BooleanSetting ("-Yreify-debug", "Trace reification.")
- val Yreifytyperdebug
- = BooleanSetting ("-Yreifytyper-debug", "Trace typings of reified trees.")
+ val Ymacrodebug = BooleanSetting ("-Ymacro-debug", "Trace macro-related activities: generation of synthetics, expansion, exceptions.")
val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup")
val Yrepldebug = BooleanSetting ("-Yrepl-debug", "Trace all repl activity.") .
withPostSetHook(_ => interpreter.replProps.debug setValue true)
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala
index a47bfda8c1..fb85ebeeb0 100644
--- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala
@@ -9,4 +9,4 @@ package symtab
import ast.{Trees, TreePrinters, DocComments}
import util._
-abstract class SymbolTable extends reflect.internal.SymbolTable \ No newline at end of file
+abstract class SymbolTable extends reflect.internal.SymbolTable \ No newline at end of file
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index d8db62a408..1cd4ab21ea 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -368,7 +368,7 @@ abstract class ClassfileParser {
case arr: Type => Constant(arr)
}
}
-
+
private def getSubArray(bytes: Array[Byte]): Array[Byte] = {
val decodedLength = ByteCodecs.decode(bytes)
val arr = new Array[Byte](decodedLength)
@@ -424,19 +424,21 @@ abstract class ClassfileParser {
def forceMangledName(name: Name, module: Boolean): Symbol = {
val parts = name.decode.toString.split(Array('.', '$'))
var sym: Symbol = definitions.RootClass
- atPhase(currentRun.flattenPhase.prev) {
+
+ // was "at flatten.prev"
+ beforeFlatten {
for (part0 <- parts; if !(part0 == ""); part = newTermName(part0)) {
- val sym1 = atPhase(currentRun.icodePhase) {
+ val sym1 = beforeIcode {
sym.linkedClassOfClass.info
sym.info.decl(part.encode)
}//.suchThat(module == _.isModule)
- if (sym1 == NoSymbol)
- sym = sym.info.decl(part.encode.toTypeName)
- else
- sym = sym1
+
+ sym = (
+ if (sym1 ne NoSymbol) sym1
+ else sym.info.decl(part.encode.toTypeName)
+ )
}
}
-// println("found: " + sym)
sym
}
@@ -719,7 +721,7 @@ abstract class ClassfileParser {
index += 1
val bounds = variance match {
case '+' => TypeBounds.upper(objToAny(sig2type(tparams, skiptvs)))
- case '-' =>
+ case '-' =>
val tp = sig2type(tparams, skiptvs)
// sig2type seems to return AnyClass regardless of the situation:
// we don't want Any as a LOWER bound.
@@ -1205,11 +1207,11 @@ abstract class ClassfileParser {
// if loading during initialization of `definitions` typerPhase is not yet set.
// in that case we simply load the member at the current phase
if (currentRun.typerPhase != null)
- atPhase(currentRun.typerPhase)(getMember(sym, innerName.toTypeName))
+ beforeTyper(getMember(sym, innerName.toTypeName))
else
getMember(sym, innerName.toTypeName)
- assert(s ne NoSymbol,
+ assert(s ne NoSymbol,
"" + ((externalName, outerName, innerName, sym.fullLocationString)) + " / " +
" while parsing " + ((in.file, busy)) +
sym + "." + innerName + " linkedModule: " + sym.companionModule + sym.companionModule.info.members
@@ -1269,13 +1271,13 @@ abstract class ClassfileParser {
if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0)
// See ticket #1687 for an example of when topLevelClass is NoSymbol: it
// apparently occurs when processing v45.3 bytecode.
- if (sym.toplevelClass != NoSymbol)
- sym.privateWithin = sym.toplevelClass.owner
+ if (sym.enclosingTopLevelClass != NoSymbol)
+ sym.privateWithin = sym.enclosingTopLevelClass.owner
// protected in java means package protected. #3946
if ((jflags & JAVA_ACC_PROTECTED) != 0)
- if (sym.toplevelClass != NoSymbol)
- sym.privateWithin = sym.toplevelClass.owner
+ if (sym.enclosingTopLevelClass != NoSymbol)
+ sym.privateWithin = sym.enclosingTopLevelClass.owner
}
@inline private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
index 7d42dabc08..68af518d3a 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
@@ -179,7 +179,7 @@ abstract class ICodeReader extends ClassfileParser {
}
else {
forceMangledName(name, false)
- atPhase(currentRun.flattenPhase.next)(definitions.getClass(name))
+ afterFlatten(definitions.getClass(name.toTypeName))
}
if (sym.isModule)
sym.moduleClass
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
index 25ae6f33d2..758f870d6b 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
@@ -77,7 +77,7 @@ abstract class Pickler extends SubComponent {
private var entries = new Array[AnyRef](256)
private var ep = 0
private val index = new LinkedHashMap[AnyRef, Int]
- private lazy val nonClassRoot = root.ownersIterator.find(! _.isClass) getOrElse NoSymbol
+ private lazy val nonClassRoot = findOrElse(root.ownersIterator)(!_.isClass)(NoSymbol)
private def isRootSym(sym: Symbol) =
sym.name.toTermName == rootName && sym.owner == rootOwner
diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
index e7759f1d7e..71d595c9c4 100644
--- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
+++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
@@ -82,7 +82,9 @@ abstract class AddInterfaces extends InfoTransform {
implClassMap.getOrElse(iface, {
atPhase(implClassPhase) {
- log("%s.implClass == %s".format(iface, iface.implClass))
+ if (iface.implClass ne NoSymbol)
+ log("%s.implClass == %s".format(iface, iface.implClass))
+
val implName = nme.implClassName(iface.name)
var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol
@@ -94,7 +96,6 @@ abstract class AddInterfaces extends InfoTransform {
// error: java.lang.AssertionError: assertion failed: (scala.tools.nsc.typechecker.Contexts$NoContext$,scala.tools.nsc.typechecker.Contexts,NoContext$,trait Contexts in package typechecker) / while parsing (/scala/trunk/build/pack/lib/scala-compiler.jar(scala/tools/nsc/interactive/ContextTrees$class.class),Some(class ContextTrees$class))trait Contexts.NoContext$ linkedModule: <none>List()
val originalImpl = impl
- val originalImplString = originalImpl.hasFlagsToString(-1L)
if (impl != NoSymbol) {
// Unlink a pre-existing symbol only if the implementation class is
// visible on the compilation classpath. In general this is true under
@@ -120,8 +121,8 @@ abstract class AddInterfaces extends InfoTransform {
impl setInfo new LazyImplClassType(iface)
implClassMap(iface) = impl
debuglog(
- "generating impl class " + impl + " " + impl.hasFlagsToString(-1L) + " in " + iface.owner + (
- if (originalImpl == NoSymbol) "" else " (cloned from " + originalImpl.fullLocationString + " " + originalImplString + ")"
+ "generating impl class " + impl.debugLocationString + " in " + iface.owner + (
+ if (originalImpl == NoSymbol) "" else " (cloned from " + originalImpl.debugLocationString + ")"
)
)
impl
@@ -194,7 +195,7 @@ abstract class AddInterfaces extends InfoTransform {
case PolyType(_, restpe) =>
implType(restpe)
}
- sym setInfo implType(atPhase(currentRun.erasurePhase)(iface.info))
+ sym setInfo implType(beforeErasure(iface.info))
}
override def load(clazz: Symbol) { complete(clazz) }
@@ -327,13 +328,11 @@ abstract class AddInterfaces extends InfoTransform {
override def transform(tree: Tree): Tree = {
val sym = tree.symbol
val tree1 = tree match {
- case ClassDef(mods, name, tparams, impl) if (sym.needsImplClass) =>
+ case ClassDef(mods, _, _, impl) if sym.needsImplClass =>
implClass(sym).initialize // to force lateDEFERRED flags
- treeCopy.ClassDef(tree, mods | INTERFACE, name, tparams, ifaceTemplate(impl))
- case DefDef(mods, name, tparams, vparamss, tpt, rhs)
- if (sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass) =>
- treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt,
- addMixinConstructorCalls(rhs, sym.owner)) // (3)
+ copyClassDef(tree)(mods = mods | INTERFACE, impl = ifaceTemplate(impl))
+ case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass =>
+ deriveDefDef(tree)(addMixinConstructorCalls(_, sym.owner)) // (3)
case Template(parents, self, body) =>
val parents1 = sym.owner.info.parents map (t => TypeTree(t) setPos tree.pos)
treeCopy.Template(tree, parents1, emptyValDef, body)
@@ -350,7 +349,7 @@ abstract class AddInterfaces extends InfoTransform {
val mix1 = mix
if (mix == tpnme.EMPTY) mix
else {
- val ps = atPhase(currentRun.erasurePhase) {
+ val ps = beforeErasure {
sym.info.parents dropWhile (p => p.symbol.name != mix)
}
assert(!ps.isEmpty, tree);
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index 50e6139e65..d04c6115ca 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -33,21 +33,21 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
private def savingStatics[T](body: => T): T = {
val savedNewStaticMembers : mutable.Buffer[Tree] = newStaticMembers.clone()
val savedNewStaticInits : mutable.Buffer[Tree] = newStaticInits.clone()
- val savedSymbolsStoredAsStatic : mutable.Map[String, Symbol] = symbolsStoredAsStatic.clone()
+ val savedSymbolsStoredAsStatic : mutable.Map[String, Symbol] = symbolsStoredAsStatic.clone()
val result = body
clearStatics()
newStaticMembers ++= savedNewStaticMembers
newStaticInits ++= savedNewStaticInits
symbolsStoredAsStatic ++= savedSymbolsStoredAsStatic
-
+
result
}
private def transformTemplate(tree: Tree) = {
val Template(parents, self, body) = tree
clearStatics()
val newBody = transformTrees(body)
- val templ = treeCopy.Template(tree, parents, self, transformTrees(newStaticMembers.toList) ::: newBody)
+ val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody)
try addStaticInits(templ) // postprocess to include static ctors
finally clearStatics()
}
@@ -85,6 +85,11 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
case "poly-cache" => POLY_CACHE
}
+ def shouldRewriteTry(tree: Try) = {
+ val sym = tree.tpe.typeSymbol
+ forMSIL && (sym != UnitClass) && (sym != NothingClass)
+ }
+
private def typedWithPos(pos: Position)(tree: Tree) =
localTyper.typedPos(pos)(tree)
@@ -97,7 +102,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
/** The boxed type if it's a primitive; identity otherwise.
*/
def toBoxedType(tp: Type) = if (isJavaValueType(tp)) boxedClass(tp.typeSymbol).tpe else tp
-
+
override def transform(tree: Tree): Tree = tree match {
/* Transforms dynamic calls (i.e. calls to methods that are undefined
@@ -134,7 +139,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
case ad@ApplyDynamic(qual0, params) =>
if (settings.logReflectiveCalls.value)
unit.echo(ad.pos, "method invocation uses reflection")
-
+
val typedPos = typedWithPos(ad.pos) _
assert(ad.symbol.isPublic)
@@ -146,7 +151,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
val flags = PRIVATE | STATIC | SYNTHETIC | (
if (isFinal) FINAL else 0
)
-
+
val varSym = currentClass.newVariable(mkTerm("" + forName), ad.pos, flags) setInfoAndEnter forType
if (!isFinal)
varSym.addAnnotation(VolatileAttr)
@@ -488,7 +493,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
val t: Tree = ad.symbol.tpe match {
case MethodType(mparams, resType) =>
assert(params.length == mparams.length, mparams)
-
+
typedPos {
val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe
qual = safeREF(sym)
@@ -560,8 +565,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
* Hence, we here rewrite all try blocks with a result != {Unit, All} such that they
* store their result in a local variable. The catch blocks are adjusted as well.
* The try tree is subsituted by a block whose result expression is read of that variable. */
- case theTry @ Try(block, catches, finalizer)
- if theTry.tpe.typeSymbol != definitions.UnitClass && theTry.tpe.typeSymbol != definitions.NothingClass =>
+ case theTry @ Try(block, catches, finalizer) if shouldRewriteTry(theTry) =>
val tpe = theTry.tpe.widen
val tempVar = currentOwner.newVariable(mkTerm(nme.EXCEPTION_RESULT_PREFIX), theTry.pos).setInfo(tpe)
def assignBlock(rhs: Tree) = super.transform(BLOCK(Ident(tempVar) === transform(rhs)))
@@ -669,9 +673,9 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
val newCtor = findStaticCtor(template) match {
// in case there already were static ctors - augment existing ones
// currently, however, static ctors aren't being generated anywhere else
- case Some(ctor @ DefDef(mods, name, tparams, vparamss, tpt, rhs)) =>
+ case Some(ctor @ DefDef(_,_,_,_,_,_)) =>
// modify existing static ctor
- val newBlock = rhs match {
+ deriveDefDef(ctor) {
case block @ Block(stats, expr) =>
// need to add inits to existing block
treeCopy.Block(block, newStaticInits.toList ::: stats, expr)
@@ -679,15 +683,14 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
// need to create a new block with inits and the old term
treeCopy.Block(term, newStaticInits.toList, term)
}
- treeCopy.DefDef(ctor, mods, name, tparams, vparamss, tpt, newBlock)
case None =>
// create new static ctor
val staticCtorSym = currentClass.newStaticConstructor(template.pos)
- val rhs = Block(newStaticInits.toList, Literal(Constant()))
+ val rhs = Block(newStaticInits.toList, Literal(Constant(())))
localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs))
}
- treeCopy.Template(template, template.parents, template.self, newCtor :: template.body)
+ deriveTemplate(template)(newCtor :: _)
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index b60b411f47..445b21c7ad 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -24,8 +24,8 @@ abstract class Constructors extends Transform with ast.TreeDSL {
protected def newTransformer(unit: CompilationUnit): Transformer =
new ConstructorTransformer(unit)
- private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = new mutable.HashMap[Symbol, List[Tree]]
- private val ctorParams: mutable.Map[Symbol, List[Symbol]] = new mutable.HashMap[Symbol, List[Symbol]]
+ private val guardedCtorStats: mutable.Map[Symbol, List[Tree]] = perRunCaches.newMap[Symbol, List[Tree]]
+ private val ctorParams: mutable.Map[Symbol, List[Symbol]] = perRunCaches.newMap[Symbol, List[Symbol]]
class ConstructorTransformer(unit: CompilationUnit) extends Transformer {
@@ -129,7 +129,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
if (from.name != nme.OUTER) result
else localTyper.typedPos(to.pos) {
- IF (from OBJ_EQ NULL) THEN THROW(NullPointerExceptionClass) ELSE result
+ IF (from OBJ_EQ NULL) THEN Throw(NullPointerExceptionClass.tpe) ELSE result
}
}
@@ -167,20 +167,18 @@ abstract class Constructors extends Transform with ast.TreeDSL {
// Triage all template definitions to go into defBuf/auxConstructorBuf, constrStatBuf, or constrPrefixBuf.
for (stat <- stats) stat match {
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case DefDef(_,_,_,_,_,rhs) =>
// methods with constant result type get literals as their body
// all methods except the primary constructor go into template
stat.symbol.tpe match {
case MethodType(List(), tp @ ConstantType(c)) =>
- defBuf += treeCopy.DefDef(
- stat, mods, name, tparams, vparamss, tpt,
- Literal(c) setPos rhs.pos setType tp)
+ defBuf += deriveDefDef(stat)(Literal(c) setPos _.pos setType tp)
case _ =>
if (stat.symbol.isPrimaryConstructor) ()
else if (stat.symbol.isConstructor) auxConstructorBuf += stat
else defBuf += stat
}
- case ValDef(mods, name, tpt, rhs) =>
+ case ValDef(_, _, _, rhs) =>
// val defs with constant right-hand sides are eliminated.
// for all other val defs, an empty valdef goes into the template and
// the initializer goes as an assignment into the constructor
@@ -193,7 +191,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
(if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign(
stat.symbol, rhs1)
}
- defBuf += treeCopy.ValDef(stat, mods, name, tpt, EmptyTree)
+ defBuf += deriveValDef(stat)(_ => EmptyTree)
}
case ClassDef(_, _, _, _) =>
// classes are treated recursively, and left in the template
@@ -231,11 +229,11 @@ abstract class Constructors extends Transform with ast.TreeDSL {
tree match {
case DefDef(_, _, _, _, _, body)
if (tree.symbol.isOuterAccessor && tree.symbol.owner == clazz && clazz.isEffectivelyFinal) =>
- log("outerAccessors += " + tree.symbol.fullName)
+ debuglog("outerAccessors += " + tree.symbol.fullName)
outerAccessors ::= ((tree.symbol, body))
case Select(_, _) =>
if (!mustbeKept(tree.symbol)) {
- log("accessedSyms += " + tree.symbol.fullName)
+ debuglog("accessedSyms += " + tree.symbol.fullName)
accessedSyms addEntry tree.symbol
}
super.traverse(tree)
@@ -519,14 +517,9 @@ abstract class Constructors extends Transform with ast.TreeDSL {
}
}
- def delayedInitCall(closure: Tree) =
- localTyper.typed {
- atPos(impl.pos) {
- Apply(
- Select(This(clazz), delayedInitMethod),
- List(New(TypeTree(closure.symbol.tpe), List(List(This(clazz))))))
- }
- }
+ def delayedInitCall(closure: Tree) = localTyper.typedPos(impl.pos) {
+ gen.mkMethodCall(This(clazz), delayedInitMethod, Nil, List(New(closure.symbol.tpe, This(clazz))))
+ }
/** Return a pair consisting of (all statements up to and including superclass and trait constr calls, rest) */
def splitAtSuper(stats: List[Tree]) = {
@@ -555,13 +548,12 @@ abstract class Constructors extends Transform with ast.TreeDSL {
}
// Assemble final constructor
- defBuf += treeCopy.DefDef(
- constr, constr.mods, constr.name, constr.tparams, constr.vparamss, constr.tpt,
+ defBuf += deriveDefDef(constr)(_ =>
treeCopy.Block(
constrBody,
paramInits ::: constrPrefixBuf.toList ::: uptoSuperStats :::
guardSpecializedInitializer(remainingConstrStats),
- constrBody.expr));
+ constrBody.expr))
// Followed by any auxiliary constructors
defBuf ++= auxConstructorBuf
@@ -571,14 +563,13 @@ abstract class Constructors extends Transform with ast.TreeDSL {
clazz.info.decls unlink sym
// Eliminate all field definitions that can be dropped from template
- treeCopy.Template(impl, impl.parents, impl.self,
- defBuf.toList filter (stat => mustbeKept(stat.symbol)))
+ deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustbeKept(stat.symbol)))
} // transformClassTemplate
override def transform(tree: Tree): Tree =
tree match {
- case ClassDef(mods, name, tparams, impl) if !tree.symbol.isInterface && !isValueClass(tree.symbol) =>
- treeCopy.ClassDef(tree, mods, name, tparams, transformClassTemplate(impl))
+ case ClassDef(_,_,_,_) if !tree.symbol.isInterface && !isValueClass(tree.symbol) =>
+ deriveClassDef(tree)(transformClassTemplate)
case _ =>
super.transform(tree)
}
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index efbfe4da41..70364070ff 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -31,6 +31,33 @@ abstract class Erasure extends AddInterfaces
// -------- erasure on types --------------------------------------------------------
+ // A type function from T => Class[U], used to determine the return
+ // type of getClass calls. The returned type is:
+ //
+ // 1. If T is a value type, Class[T].
+ // 2. If T is a phantom type (Any or AnyVal), Class[_].
+ // 3. If T is a local class, Class[_ <: |T|].
+ // 4. Otherwise, Class[_ <: T].
+ //
+ // Note: AnyVal cannot be Class[_ <: AnyVal] because if the static type of the
+ // receiver is AnyVal, it implies the receiver is boxed, so the correct
+ // class object is that of java.lang.Integer, not Int.
+ //
+ // TODO: If T is final, return type could be Class[T]. Should it?
+ def getClassReturnType(tpe: Type): Type = {
+ if (phase.erasedTypes) ClassClass.tpe else {
+ val tp = tpe.widen.normalize
+ val sym = tp.typeSymbol
+
+ if (isValueClass(sym)) ClassType(tp)
+ else boundedClassType(
+ if (isPhantomClass(sym)) ObjectClass.tpe
+ else if (sym.isLocalClass) intersectionDominator(tp.parents)
+ else tp
+ )
+ }
+ }
+
// convert a numeric with a toXXX method
def numericConversion(tree: Tree, numericSym: Symbol): Tree = {
val mname = newTermName("to" + numericSym.name)
@@ -196,7 +223,7 @@ abstract class Erasure extends AddInterfaces
/** The Java signature of type 'info', for symbol sym. The symbol is used to give the right return
* type for constructors.
*/
- def javaSig(sym0: Symbol, info: Type): Option[String] = atPhase(currentRun.erasurePhase) {
+ def javaSig(sym0: Symbol, info: Type): Option[String] = beforeErasure {
val isTraitSignature = sym0.enclClass.isTrait
def superSig(parents: List[Type]) = traceSig("superSig", parents) {
@@ -230,7 +257,7 @@ abstract class Erasure extends AddInterfaces
// Anything which could conceivably be a module (i.e. isn't known to be
// a type parameter or similar) must go through here or the signature is
// likely to end up with Foo<T>.Empty where it needs Foo<T>.Empty$.
- def fullNameInSig(sym: Symbol) = "L" + atPhase(currentRun.icodePhase)(sym.javaBinaryName)
+ def fullNameInSig(sym: Symbol) = "L" + beforeIcode(sym.javaBinaryName.toString)
def jsig(tp0: Type, existentiallyBound: List[Symbol] = Nil, toplevel: Boolean = false, primitiveOK: Boolean = true): String = {
val tp = tp0.dealias
@@ -394,9 +421,9 @@ abstract class Erasure extends AddInterfaces
/** Box `tree` of unboxed type */
private def box(tree: Tree): Tree = tree match {
- case LabelDef(name, params, rhs) =>
- val rhs1 = box(rhs)
- treeCopy.LabelDef(tree, name, params, rhs1) setType rhs1.tpe
+ case LabelDef(_, _, _) =>
+ val ldef = deriveLabelDef(tree)(box)
+ ldef setType ldef.rhs.tpe
case _ =>
typedPos(tree.pos)(tree.tpe.typeSymbol match {
case UnitClass =>
@@ -413,7 +440,7 @@ abstract class Erasure extends AddInterfaces
* fields (see TupleX). (ID)
*/
case Apply(boxFun, List(arg)) if isUnbox(tree.symbol) && safeToRemoveUnbox(arg.tpe.typeSymbol) =>
- log("boxing an unbox: " + tree + " and replying with " + arg)
+ log("boxing an unbox: " + tree + "/" + tree.symbol + " and replying with " + arg + " of type " + arg.tpe)
arg
case _ =>
(REF(boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectClass.tpe
@@ -433,9 +460,9 @@ abstract class Erasure extends AddInterfaces
println("unbox shorten: "+tree) // this never seems to kick in during build and test; therefore disabled.
adaptToType(unboxed, pt)
*/
- case LabelDef(name, params, rhs) =>
- val rhs1 = unbox(rhs, pt)
- treeCopy.LabelDef(tree, name, params, rhs1) setType rhs1.tpe
+ case LabelDef(_, _, _) =>
+ val ldef = deriveLabelDef(tree)(unbox(_, pt))
+ ldef setType ldef.rhs.tpe
case _ =>
typedPos(tree.pos)(pt.typeSymbol match {
case UnitClass =>
@@ -577,8 +604,8 @@ abstract class Erasure extends AddInterfaces
throw ex
}
def adaptCase(cdef: CaseDef): CaseDef = {
- val body1 = adaptToType(cdef.body, tree1.tpe)
- treeCopy.CaseDef(cdef, cdef.pat, cdef.guard, body1) setType body1.tpe
+ val newCdef = deriveCaseDef(cdef)(adaptToType(_, tree1.tpe))
+ newCdef setType newCdef.body.tpe
}
def adaptBranch(branch: Tree): Tree =
if (branch == EmptyTree) branch else adaptToType(branch, tree1.tpe);
@@ -621,21 +648,20 @@ abstract class Erasure extends AddInterfaces
private def checkNoDoubleDefs(root: Symbol) {
def doubleDefError(sym1: Symbol, sym2: Symbol) {
// the .toString must also be computed at the earlier phase
- def atRefc[T](op: => T) = atPhase[T](currentRun.refchecksPhase.next)(op)
- val tpe1 = atRefc(root.thisType.memberType(sym1))
- val tpe2 = atRefc(root.thisType.memberType(sym2))
+ val tpe1 = afterRefchecks(root.thisType.memberType(sym1))
+ val tpe2 = afterRefchecks(root.thisType.memberType(sym2))
if (!tpe1.isErroneous && !tpe2.isErroneous)
unit.error(
if (sym1.owner == root) sym1.pos else root.pos,
(if (sym1.owner == sym2.owner) "double definition:\n"
else if (sym1.owner == root) "name clash between defined and inherited member:\n"
else "name clash between inherited members:\n") +
- sym1 + ":" + atRefc(tpe1.toString) +
+ sym1 + ":" + afterRefchecks(tpe1.toString) +
(if (sym1.owner == root) "" else sym1.locationString) + " and\n" +
- sym2 + ":" + atRefc(tpe2.toString) +
+ sym2 + ":" + afterRefchecks(tpe2.toString) +
(if (sym2.owner == root) " at line " + (sym2.pos).line else sym2.locationString) +
"\nhave same type" +
- (if (atRefc(tpe1 =:= tpe2)) "" else " after erasure: " + atPhase(phase.next)(sym1.tpe)))
+ (if (afterRefchecks(tpe1 =:= tpe2)) "" else " after erasure: " + afterErasure(sym1.tpe)))
sym1.setInfo(ErrorType)
}
@@ -645,7 +671,7 @@ abstract class Erasure extends AddInterfaces
if (e.sym.isTerm) {
var e1 = decls.lookupNextEntry(e)
while (e1 ne null) {
- if (atPhase(phase.next)(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym)
+ if (afterErasure(e1.sym.info =:= e.sym.info)) doubleDefError(e.sym, e1.sym)
e1 = decls.lookupNextEntry(e1)
}
}
@@ -659,10 +685,10 @@ abstract class Erasure extends AddInterfaces
|| !sym.hasTypeAt(currentRun.refchecksPhase.id))
override def matches(sym1: Symbol, sym2: Symbol): Boolean =
- atPhase(phase.next)(sym1.tpe =:= sym2.tpe)
+ afterErasure(sym1.tpe =:= sym2.tpe)
}
while (opc.hasNext) {
- if (!atPhase(currentRun.refchecksPhase.next)(
+ if (!afterRefchecks(
root.thisType.memberType(opc.overriding) matches
root.thisType.memberType(opc.overridden))) {
debuglog("" + opc.overriding.locationString + " " +
@@ -681,8 +707,8 @@ abstract class Erasure extends AddInterfaces
for (member <- root.info.nonPrivateMember(other.name).alternatives) {
if (member != other &&
!(member hasFlag BRIDGE) &&
- atPhase(phase.next)(member.tpe =:= other.tpe) &&
- !atPhase(refchecksPhase.next)(
+ afterErasure(member.tpe =:= other.tpe) &&
+ !afterRefchecks(
root.thisType.memberType(member) matches root.thisType.memberType(other))) {
debuglog("" + member.locationString + " " + member.infosString + other.locationString + " " + other.infosString);
doubleDefError(member, other)
@@ -706,13 +732,13 @@ abstract class Erasure extends AddInterfaces
*/
private def bridgeDefs(owner: Symbol): (List[Tree], immutable.Set[Symbol]) = {
var toBeRemoved: immutable.Set[Symbol] = immutable.Set()
- //println("computing bridges for " + owner)//DEBUG
- assert(phase == currentRun.erasurePhase)
+ debuglog("computing bridges for " + owner)//DEBUG
+ assert(phase == currentRun.erasurePhase, phase)
val site = owner.thisType
val bridgesScope = newScope
val bridgeTarget = new mutable.HashMap[Symbol, Symbol]
var bridges: List[Tree] = List()
- val opc = atPhase(currentRun.explicitouterPhase) {
+ val opc = beforeExplicitOuter {
new overridingPairs.Cursor(owner) {
override def parents: List[Type] = List(owner.info.firstParent)
override def exclude(sym: Symbol): Boolean =
@@ -723,9 +749,9 @@ abstract class Erasure extends AddInterfaces
val member = opc.overriding
val other = opc.overridden
//println("bridge? " + member + ":" + member.tpe + member.locationString + " to " + other + ":" + other.tpe + other.locationString)//DEBUG
- if (atPhase(currentRun.explicitouterPhase)(!member.isDeferred)) {
+ if (beforeExplicitOuter(!member.isDeferred)) {
val otpe = erasure(owner, other.tpe)
- val bridgeNeeded = atPhase(phase.next) (
+ val bridgeNeeded = afterErasure (
!(other.tpe =:= member.tpe) &&
!(deconstMap(other.tpe) =:= deconstMap(member.tpe)) &&
{ var e = bridgesScope.lookupEntry(member.name)
@@ -740,15 +766,15 @@ abstract class Erasure extends AddInterfaces
// the parameter symbols need to have the new owner
bridge.setInfo(otpe.cloneInfo(bridge))
bridgeTarget(bridge) = member
- atPhase(phase.next) { owner.info.decls.enter(bridge) }
+ afterErasure { owner.info.decls.enter(bridge) }
if (other.owner == owner) {
//println("bridge to same: "+other+other.locationString)//DEBUG
- atPhase(phase.next) { owner.info.decls.unlink(other) }
+ afterErasure { owner.info.decls.unlink(other) }
toBeRemoved += other
}
bridgesScope enter bridge
bridges =
- atPhase(phase.next) {
+ afterErasure {
atPos(bridge.pos) {
val bridgeDef =
DefDef(bridge,
@@ -762,7 +788,7 @@ abstract class Erasure extends AddInterfaces
if ( member.isSynthetic // TODO: should we do this for user-defined unapplies as well?
&& ((member.name == nme.unapply) || (member.name == nme.unapplySeq))
// && (bridge.paramss.nonEmpty && bridge.paramss.head.nonEmpty && bridge.paramss.head.tail.isEmpty) // does the first argument list has exactly one argument -- for user-defined unapplies we can't be sure
- && !(atPhase(phase.next)(member.tpe <:< other.tpe))) { // no static guarantees (TODO: is the subtype test ever true?)
+ && !(afterErasure(member.tpe <:< other.tpe))) { // no static guarantees (TODO: is the subtype test ever true?)
import CODE._
val typeTest = gen.mkIsInstanceOf(REF(bridge.firstParam), member.tpe.params.head.tpe, any = true, wrapInApply = true) // any = true since we're before erasure (?), wrapInapply is true since we're after uncurry
// println("unapp type test: "+ typeTest)
@@ -819,11 +845,11 @@ abstract class Erasure extends AddInterfaces
*/
private val preTransformer = new TypingTransformer(unit) {
def preErase(tree: Tree): Tree = tree match {
- case ClassDef(mods, name, tparams, impl) =>
+ case ClassDef(_,_,_,_) =>
debuglog("defs of " + tree.symbol + " = " + tree.symbol.info.decls)
- treeCopy.ClassDef(tree, mods, name, List(), impl)
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
- treeCopy.DefDef(tree, mods, name, List(), vparamss, tpt, rhs)
+ copyClassDef(tree)(tparams = Nil)
+ case DefDef(_,_,_,_,_,_) =>
+ copyDefDef(tree)(tparams = Nil)
case TypeDef(_, _, _, _) =>
EmptyTree
case Apply(instanceOf @ TypeApply(fun @ Select(qual, name), args @ List(arg)), List()) // !!! todo: simplify by having GenericArray also extract trees
@@ -1025,7 +1051,7 @@ abstract class Erasure extends AddInterfaces
*/
override def transform(tree: Tree): Tree = {
val tree1 = preTransformer.transform(tree)
- atPhase(phase.next) {
+ afterErasure {
val tree2 = mixinTransformer.transform(tree1)
debuglog("tree after addinterfaces: \n" + tree2)
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index 7f7f7e7b65..595c1486b6 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -68,7 +68,7 @@ abstract class ExplicitOuter extends InfoTransform
result
}
-
+
private val innerClassConstructorParamName: TermName = newTermName("arg" + nme.OUTER)
class RemoveBindingsTransformer(toRemove: Set[Symbol]) extends Transformer {
@@ -89,13 +89,13 @@ abstract class ExplicitOuter extends InfoTransform
def outerAccessor(clazz: Symbol): Symbol = {
val firstTry = clazz.info.decl(nme.expandedName(nme.OUTER, clazz))
if (firstTry != NoSymbol && firstTry.outerSource == clazz) firstTry
- else clazz.info.decls find (_.outerSource == clazz) getOrElse NoSymbol
- }
+ else findOrElse(clazz.info.decls)(_.outerSource == clazz)(NoSymbol)
+ }
def newOuterAccessor(clazz: Symbol) = {
val accFlags = SYNTHETIC | METHOD | STABLE | ( if (clazz.isTrait) DEFERRED else 0 )
val sym = clazz.newMethodSymbol(nme.OUTER, clazz.pos, accFlags)
val restpe = if (clazz.isTrait) clazz.outerClass.tpe else clazz.outerClass.thisType
-
+
sym expandName clazz
sym.referenced = clazz
sym setInfo MethodType(Nil, restpe)
@@ -163,14 +163,14 @@ abstract class ExplicitOuter extends InfoTransform
decls1 = decls.cloneScope
val outerAcc = clazz.newMethod(nme.OUTER, clazz.pos) // 3
outerAcc expandName clazz
-
+
decls1 enter newOuterAccessor(clazz)
if (hasOuterField(clazz)) //2
decls1 enter newOuterField(clazz)
}
if (!clazz.isTrait && !parents.isEmpty) {
for (mc <- clazz.mixinClasses) {
- val mixinOuterAcc: Symbol = atPhase(phase.next)(outerAccessor(mc))
+ val mixinOuterAcc: Symbol = afterExplicitOuter(outerAccessor(mc))
if (mixinOuterAcc != NoSymbol) {
if (decls1 eq decls) decls1 = decls.cloneScope
val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED)
@@ -468,10 +468,12 @@ abstract class ExplicitOuter extends InfoTransform
}
}
super.transform(
- treeCopy.Template(tree, parents, self,
- if (newDefs.isEmpty) decls else decls ::: newDefs.toList)
+ deriveTemplate(tree)(decls =>
+ if (newDefs.isEmpty) decls
+ else decls ::: newDefs.toList
+ )
)
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case DefDef(_, _, _, vparamss, _, rhs) =>
if (sym.isClassConstructor) {
rhs match {
case Literal(_) =>
@@ -484,7 +486,7 @@ abstract class ExplicitOuter extends InfoTransform
sym.newValueParameter(nme.OUTER, sym.pos) setInfo outerField(clazz).info
((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail
} else vparamss
- super.transform(treeCopy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs))
+ super.transform(copyDefDef(tree)(vparamss = vparamss1))
}
} else
super.transform(tree)
@@ -517,7 +519,7 @@ abstract class ExplicitOuter extends InfoTransform
super.transform(treeCopy.Apply(tree, sel, outerVal :: args))
// entry point for pattern matcher translation
- case mch: Match =>
+ case mch: Match if (!opt.virtPatmat) => // don't use old pattern matcher as fallback when the user wants the virtualizing one
matchTranslation(mch)
case _ =>
@@ -559,7 +561,7 @@ abstract class ExplicitOuter extends InfoTransform
/** The transformation method for whole compilation units */
override def transformUnit(unit: CompilationUnit) {
- atPhase(phase.next)(super.transformUnit(unit))
+ afterExplicitOuter(super.transformUnit(unit))
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala
index 4fa5b52de3..8856024a30 100644
--- a/src/compiler/scala/tools/nsc/transform/Flatten.scala
+++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala
@@ -20,16 +20,14 @@ abstract class Flatten extends InfoTransform {
/** Updates the owning scope with the given symbol; returns the old symbol.
*/
- private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = {
- atPhase(phase.next) {
- val scope = sym.owner.info.decls
- val old = scope lookup sym.name
- if (old ne NoSymbol)
- scope unlink old
+ private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = afterFlatten {
+ val scope = sym.owner.info.decls
+ val old = scope lookup sym.name
+ if (old ne NoSymbol)
+ scope unlink old
- scope enter sym
- old
- }
+ scope enter sym
+ old
}
private def liftClass(sym: Symbol) {
@@ -53,24 +51,26 @@ abstract class Flatten extends InfoTransform {
val clazz = pre.typeSymbol
clazz.isClass && !clazz.isPackageClass && {
// Cannot flatten here: class A[T] { object B }
- atPhase(currentRun.erasurePhase.prev)(clazz.typeParams.isEmpty)
+ // was "at erasurePhase.prev"
+ beforeErasure(clazz.typeParams.isEmpty)
}
}
private val flattened = new TypeMap {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, sym, args) if isFlattenablePrefix(pre) =>
- assert(args.isEmpty && sym.toplevelClass != NoSymbol, sym.ownerChain)
- typeRef(sym.toplevelClass.owner.thisType, sym, Nil)
+ assert(args.isEmpty && sym.enclosingTopLevelClass != NoSymbol, sym.ownerChain)
+ typeRef(sym.enclosingTopLevelClass.owner.thisType, sym, Nil)
case ClassInfoType(parents, decls, clazz) =>
var parents1 = parents
val decls1 = scopeTransform(clazz) {
val decls1 = newScope
if (clazz.isPackageClass) {
- atPhase(phase.next)(decls foreach (decls1 enter _))
- } else {
+ afterFlatten { decls foreach (decls1 enter _) }
+ }
+ else {
val oldowner = clazz.owner
- atPhase(phase.next)(oldowner.info)
+ afterFlatten { oldowner.info }
parents1 = parents mapConserve (this)
for (sym <- decls) {
@@ -102,7 +102,7 @@ abstract class Flatten extends InfoTransform {
class Flattener extends Transformer {
/** Buffers for lifted out classes */
- private val liftedDefs = new mutable.HashMap[Symbol, ListBuffer[Tree]]
+ private val liftedDefs = perRunCaches.newMap[Symbol, ListBuffer[Tree]]()
override def transform(tree: Tree): Tree = {
tree match {
@@ -119,14 +119,10 @@ abstract class Flatten extends InfoTransform {
val sym = tree.symbol
val tree1 = tree match {
case ClassDef(_, _, _, _) if sym.isNestedClass =>
- liftedDefs(sym.toplevelClass.owner) += tree
+ liftedDefs(sym.enclosingTopLevelClass.owner) += tree
EmptyTree
case Select(qual, name) if (sym.isStaticModule && !sym.owner.isPackageClass) =>
- atPhase(phase.next) {
- atPos(tree.pos) {
- gen.mkAttributedRef(sym)
- }
- }
+ afterFlatten(atPos(tree.pos)(gen.mkAttributedRef(sym)))
case _ =>
tree
}
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index 712298bd89..570eaba3a9 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -18,7 +18,7 @@ abstract class LambdaLift extends InfoTransform {
/** the following two members override abstract members in Transform */
val phaseName: String = "lambdalift"
-
+
/** Converts types of captured variables to *Ref types.
*/
def boxIfCaptured(sym: Symbol, tpe: Type, erasedTypes: Boolean) =
@@ -65,17 +65,20 @@ abstract class LambdaLift extends InfoTransform {
/** The set of symbols that need to be renamed. */
private val renamable = newSymSet
- private val renamableImplClasses = mutable.HashMap[Name, Symbol]() withDefaultValue NoSymbol
+ // (trait, name) -> owner
+ private val localTraits = mutable.HashMap[(Symbol, Name), Symbol]()
+ // (owner, name) -> implClass
+ private val localImplClasses = mutable.HashMap[(Symbol, Name), Symbol]()
/** A flag to indicate whether new free variables have been found */
private var changedFreeVars: Boolean = _
/** Buffers for lifted out classes and methods */
private val liftedDefs = new LinkedHashMap[Symbol, List[Tree]]
-
+
/** True if we are transforming under a ReferenceToBoxed node */
private var isBoxedRef = false
-
+
private type SymSet = TreeSet[Symbol]
private def newSymSet = new TreeSet[Symbol](_ isLess _)
@@ -125,7 +128,7 @@ abstract class LambdaLift extends InfoTransform {
if (!ss(sym)) {
ss addEntry sym
renamable addEntry sym
- atPhase(currentRun.picklerPhase) {
+ beforePickler {
// The param symbol in the MethodType should not be renamed, only the symbol in scope. This way,
// parameter names for named arguments are not changed. Example: without cloning the MethodType,
// def closure(x: Int) = { () => x }
@@ -167,8 +170,13 @@ abstract class LambdaLift extends InfoTransform {
// arrangements, and then have separate methods which attempt to compensate
// for that failure. There should be exactly one method for any given
// entity which always gives the right answer.
- if (sym.isImplClass) renamableImplClasses(nme.interfaceName(sym.name)) = sym
- else renamable addEntry sym
+ if (sym.isImplClass)
+ localImplClasses((sym.owner, nme.interfaceName(sym.name))) = sym
+ else {
+ renamable addEntry sym
+ if (sym.isTrait)
+ localTraits((sym, sym.name)) = sym.owner
+ }
}
case DefDef(_, _, _, _, _, _) =>
if (sym.isLocal) {
@@ -213,7 +221,7 @@ abstract class LambdaLift extends InfoTransform {
for (caller <- called.keys ; callee <- called(caller) ; fvs <- free get callee ; fv <- fvs)
markFree(fv, caller)
} while (changedFreeVars)
-
+
def renameSym(sym: Symbol) {
val originalName = sym.name
val base = sym.name + nme.NAME_JOIN_STRING + (
@@ -241,13 +249,20 @@ abstract class LambdaLift extends InfoTransform {
for (sym <- renamable) {
// If we renamed a trait from Foo to Foo$1, we must rename the implementation
// class from Foo$class to Foo$1$class. (Without special consideration it would
- // become Foo$class$1 instead.)
- val implClass = if (sym.isTrait) renamableImplClasses(sym.name) else NoSymbol
- if ((implClass ne NoSymbol) && (sym.owner == implClass.owner)) renameTrait(sym, implClass)
- else renameSym(sym)
+ // become Foo$class$1 instead.) Since the symbols are being renamed out from
+ // under us, and there's no reliable link between trait symbol and impl symbol,
+ // we have maps from ((trait, name)) -> owner and ((owner, name)) -> impl.
+ localTraits remove ((sym, sym.name)) match {
+ case None => renameSym(sym)
+ case Some(owner) =>
+ localImplClasses remove ((owner, sym.name)) match {
+ case Some(implSym) => renameTrait(sym, implSym)
+ case _ => renameSym(sym) // pure interface, no impl class
+ }
+ }
}
- atPhase(phase.next) {
+ afterOwnPhase {
for ((owner, freeValues) <- free.toList) {
val newFlags = SYNTHETIC | ( if (owner.isClass) PARAMACCESSOR | PrivateLocal else PARAM )
debuglog("free var proxy: %s, %s".format(owner.fullLocationString, freeValues.toList.mkString(", ")))
@@ -305,12 +320,13 @@ abstract class LambdaLift extends InfoTransform {
case Some(ps) =>
val freeParams = ps map (p => ValDef(p) setPos tree.pos setType NoType)
tree match {
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case DefDef(_, _, _, vparams :: _, _, _) =>
val addParams = cloneSymbols(ps).map(_.setFlag(PARAM))
sym.updateInfo(
lifted(MethodType(sym.info.params ::: addParams, sym.info.resultType)))
- treeCopy.DefDef(tree, mods, name, tparams, List(vparamss.head ++ freeParams), tpt, rhs)
- case ClassDef(mods, name, tparams, impl @ Template(parents, self, body)) =>
+
+ copyDefDef(tree)(vparamss = List(vparams ++ freeParams))
+ case ClassDef(_, _, _, _) =>
// Disabled attempt to to add getters to freeParams
// this does not work yet. Problem is that local symbols need local names
// and references to local symbols need to be transformed into
@@ -322,8 +338,7 @@ abstract class LambdaLift extends InfoTransform {
// DefDef(getter, rhs) setPos tree.pos setType NoType
// }
// val newDefs = if (sym.isTrait) freeParams ::: (ps map paramGetter) else freeParams
- treeCopy.ClassDef(tree, mods, name, tparams,
- treeCopy.Template(impl, parents, self, body ::: freeParams))
+ deriveClassDef(tree)(impl => deriveTemplate(impl)(_ ::: freeParams))
}
case None =>
tree
@@ -404,10 +419,10 @@ abstract class LambdaLift extends InfoTransform {
def refConstr(expr: Tree): Tree = expr match {
case Try(block, catches, finalizer) =>
Try(refConstr(block), catches map refConstrCase, finalizer)
- case _ =>
- Apply(Select(New(TypeTree(sym.tpe)), nme.CONSTRUCTOR), List(expr))
+ case _ =>
+ New(sym.tpe, expr)
}
- def refConstrCase(cdef: CaseDef): CaseDef =
+ def refConstrCase(cdef: CaseDef): CaseDef =
CaseDef(cdef.pat, cdef.guard, refConstr(cdef.body))
treeCopy.ValDef(tree, mods, name, tpt1, typer.typedPos(rhs.pos) {
refConstr(constructorArg)
@@ -452,7 +467,7 @@ abstract class LambdaLift extends InfoTransform {
tree
}
}
-
+
private def preTransform(tree: Tree) = super.transform(tree) setType lifted(tree.tpe)
override def transform(tree: Tree): Tree = tree match {
@@ -461,21 +476,20 @@ abstract class LambdaLift extends InfoTransform {
case _ =>
postTransform(preTransform(tree))
}
-
+
/** Transform statements and add lifted definitions to them. */
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
def addLifted(stat: Tree): Tree = stat match {
- case ClassDef(mods, name, tparams, impl @ Template(parents, self, body)) =>
+ case ClassDef(_, _, _, _) =>
val lifted = liftedDefs get stat.symbol match {
case Some(xs) => xs reverseMap addLifted
case _ => log("unexpectedly no lifted defs for " + stat.symbol) ; Nil
}
- val result = treeCopy.ClassDef(
- stat, mods, name, tparams, treeCopy.Template(impl, parents, self, body ::: lifted))
- liftedDefs -= stat.symbol
- result
- case DefDef(mods, name, tp, vp, tpt, Block(Nil, expr)) if !stat.symbol.isConstructor =>
- treeCopy.DefDef(stat, mods, name, tp, vp, tpt, expr)
+ try deriveClassDef(stat)(impl => deriveTemplate(impl)(_ ::: lifted))
+ finally liftedDefs -= stat.symbol
+
+ case DefDef(_, _, _, _, _, Block(Nil, expr)) if !stat.symbol.isConstructor =>
+ deriveDefDef(stat)(_ => expr)
case _ =>
stat
}
@@ -484,7 +498,7 @@ abstract class LambdaLift extends InfoTransform {
override def transformUnit(unit: CompilationUnit) {
computeFreeVars
- atPhase(phase.next)(super.transformUnit(unit))
+ afterOwnPhase(super.transformUnit(unit))
assert(liftedDefs.isEmpty, liftedDefs.keys mkString ", ")
}
} // class LambdaLifter
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index f8c5f5bfc6..85ba539993 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -50,9 +50,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
*/
class LazyValues(unit: CompilationUnit) extends TypingTransformer(unit) {
/** map from method symbols to the number of lazy values it defines. */
- private val lazyVals = new mutable.HashMap[Symbol, Int] {
- override def default(meth: Symbol) = 0
- }
+ private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0
import symtab.Flags._
import lazyVals._
@@ -70,7 +68,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
curTree = tree
tree match {
- case DefDef(mods, name, tparams, vparams, tpt, rhs) => atOwner(tree.symbol) {
+ case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) {
val res = if (!sym.owner.isClass && sym.isLazy) {
val enclosingClassOrDummyOrMethod = {
val enclMethod = sym.enclMethod
@@ -92,11 +90,10 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
} else
super.transform(rhs)
- treeCopy.DefDef(tree, mods, name, tparams, vparams, tpt,
- if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res)
+ deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res)
}
- case Template(parents, self, body) => atOwner(currentOwner) {
+ case Template(_, _, body) => atOwner(currentOwner) {
val body1 = super.transformTrees(body)
var added = false
val stats =
@@ -108,8 +105,8 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
added = true
typed(addBitmapDefs(sym, stat))
} else stat
- case ValDef(mods, name, tpt, rhs) =>
- typed(treeCopy.ValDef(stat, mods, name, tpt, addBitmapDefs(stat.symbol, rhs)))
+ case ValDef(_, _, _, _) =>
+ typed(deriveValDef(stat)(addBitmapDefs(stat.symbol, _)))
case _ =>
stat
}
@@ -124,29 +121,29 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
})
toAdd0
} else List()
- treeCopy.Template(tree, parents, self, innerClassBitmaps ++ stats)
+ deriveTemplate(tree)(_ => innerClassBitmaps ++ stats)
}
- case ValDef(mods, name, tpt, rhs0) if (!sym.owner.isModule && !sym.owner.isClass) =>
- val rhs = super.transform(rhs0)
- treeCopy.ValDef(tree, mods, name, tpt,
- if (LocalLazyValFinder.find(rhs)) typed(addBitmapDefs(sym, rhs)) else rhs)
+ case ValDef(_, _, _, _) if !sym.owner.isModule && !sym.owner.isClass =>
+ deriveValDef(tree) { rhs0 =>
+ val rhs = super.transform(rhs0)
+ if (LocalLazyValFinder.find(rhs)) typed(addBitmapDefs(sym, rhs)) else rhs
+ }
case l@LabelDef(name0, params0, ifp0@If(_, _, _)) if name0.startsWith(nme.WHILE_PREFIX) =>
val ifp1 = super.transform(ifp0)
val If(cond0, thenp0, elsep0) = ifp1
+
if (LocalLazyValFinder.find(thenp0))
- treeCopy.LabelDef(l, name0, params0,
- treeCopy.If(ifp1, cond0, typed(addBitmapDefs(sym.owner, thenp0)), elsep0))
+ deriveLabelDef(l)(_ => treeCopy.If(ifp1, cond0, typed(addBitmapDefs(sym.owner, thenp0)), elsep0))
else
l
- case l@LabelDef(name0, params0, block@Block(stats0, _))
+ case l@LabelDef(name0, params0, block@Block(stats0, expr))
if name0.startsWith(nme.WHILE_PREFIX) || name0.startsWith(nme.DO_WHILE_PREFIX) =>
val stats1 = super.transformTrees(stats0)
if (LocalLazyValFinder.find(stats1))
- treeCopy.LabelDef(l, name0, params0,
- treeCopy.Block(block, typed(addBitmapDefs(sym.owner, stats1.head))::stats1.tail, block.expr))
+ deriveLabelDef(l)(_ => treeCopy.Block(block, typed(addBitmapDefs(sym.owner, stats1.head))::stats1.tail, expr))
else
l
@@ -171,9 +168,9 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
def isMatch(params: List[Ident]) = (params.tail corresponds methSym.tpe.params)(_.tpe == _.tpe)
if (bmps.isEmpty) rhs else rhs match {
- case Block(assign, l @ LabelDef(name, params, rhs1))
+ case Block(assign, l @ LabelDef(name, params, _))
if name.toString == ("_" + methSym.name) && isMatch(params) =>
- Block(assign, treeCopy.LabelDef(l, name, params, typed(prependStats(bmps, rhs1))))
+ Block(assign, deriveLabelDef(l)(rhs => typed(prependStats(bmps, rhs))))
case _ => prependStats(bmps, rhs)
}
@@ -233,9 +230,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
private def mkSetFlag(bmp: Symbol, mask: Tree, bmpRef: Tree): Tree =
bmpRef === (bmpRef INT_| mask)
- val bitmaps = new mutable.HashMap[Symbol, List[Symbol]] {
- override def default(meth: Symbol) = Nil
- }
+ val bitmaps = mutable.Map[Symbol, List[Symbol]]() withDefaultValue Nil
/** Return the symbol corresponding of the right bitmap int inside meth,
* given offset.
@@ -247,7 +242,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
bmps(n)
else {
val sym = meth.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), meth.pos).setInfo(IntClass.tpe)
- atPhase(currentRun.typerPhase) {
+ beforeTyper {
sym addAnnotation VolatileAttr
}
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index b3b7596f9a..c9794cc20f 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -71,7 +71,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* maps all other types to themselves.
*/
private def toInterface(tp: Type): Type =
- atPhase(currentRun.mixinPhase)(tp.typeSymbol.toInterface).tpe
+ beforeMixin(tp.typeSymbol.toInterface).tpe
private def isFieldWithBitmap(field: Symbol) = {
field.info // ensure that nested objects are transformed
@@ -103,7 +103,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
private val toInterfaceMap = new TypeMap {
def apply(tp: Type): Type = mapOver( tp match {
case TypeRef(pre, sym, args) if (sym.isImplClass) =>
- typeRef(pre, atPhase(currentRun.mixinPhase)(sym.toInterface), args)
+ typeRef(pre, beforeMixin(sym.toInterface), args)
case _ => tp
})
}
@@ -123,7 +123,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* @param mixinClass The mixin class that produced the superaccessor
*/
private def rebindSuper(base: Symbol, member: Symbol, mixinClass: Symbol): Symbol =
- atPhase(currentRun.picklerPhase.next) {
+ afterPickler {
var bcs = base.info.baseClasses.dropWhile(mixinClass !=).tail
var sym: Symbol = NoSymbol
debuglog("starting rebindsuper " + base + " " + member + ":" + member.tpe +
@@ -131,7 +131,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
while (!bcs.isEmpty && sym == NoSymbol) {
if (settings.debug.value) {
val other = bcs.head.info.nonPrivateDecl(member.name);
- log("rebindsuper " + bcs.head + " " + other + " " + other.tpe +
+ debuglog("rebindsuper " + bcs.head + " " + other + " " + other.tpe +
" " + other.isDeferred)
}
sym = member.matchingSymbol(bcs.head, base.thisType).suchThat(sym => !sym.hasFlag(DEFERRED | BRIDGE))
@@ -147,7 +147,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
member.hasAccessorFlag && (!member.isDeferred || (member hasFlag lateDEFERRED))
/** Is member overridden (either directly or via a bridge) in base class sequence `bcs`? */
- def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = atPhase(ownPhase) {
+ def isOverriddenAccessor(member: Symbol, bcs: List[Symbol]): Boolean = beforeOwnPhase {
def hasOverridingAccessor(clazz: Symbol) = {
clazz.info.nonPrivateDecl(member.name).alternatives.exists(
sym =>
@@ -155,8 +155,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
!sym.hasFlag(MIXEDIN) &&
matchesType(sym.tpe, member.tpe, true))
}
- bcs.head != member.owner &&
- (hasOverridingAccessor(bcs.head) || isOverriddenAccessor(member, bcs.tail))
+ ( bcs.head != member.owner
+ && (hasOverridingAccessor(bcs.head) || isOverriddenAccessor(member, bcs.tail))
+ )
}
/** Add given member to given class, and mark member as mixed-in.
@@ -202,7 +203,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
setter setInfo MethodType(setter.newSyntheticValueParams(List(field.info)), UnitClass.tpe)
if (needsExpandedSetterName(field))
setter.name = nme.expandedSetterName(setter.name, clazz)
-
+
setter
}
@@ -241,7 +242,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
*/
def addMixedinMembers(clazz: Symbol, unit : CompilationUnit) {
def cloneBeforeErasure(iface: Symbol, clazz: Symbol, imember: Symbol): Symbol = {
- val newSym = atPhase(currentRun.erasurePhase) {
+ val newSym = beforeErasure {
val res = imember.cloneSymbol(clazz)
// since we used the member (imember) from the interface that represents the trait that's being mixed in,
// have to instantiate the interface type params (that may occur in imember's info) as they are seen from the class
@@ -337,8 +338,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
case _ => // otherwise mixin a field as well
// atPhase: the private field is moved to the implementation class by erasure,
// so it can no longer be found in the member's owner (the trait)
- val accessed = atPhase(currentRun.picklerPhase)(member.accessed)
- val sym = atPhase(currentRun.erasurePhase){ // #3857, need to retain info before erasure when cloning (since cloning only carries over the current entry in the type history)
+ val accessed = beforePickler(member.accessed)
+ val sym = beforeErasure { // #3857, need to retain info before erasure when cloning (since cloning only carries over the current entry in the type history)
clazz.newValue(nme.getterToLocal(member.name), member.pos).setInfo(member.tpe.resultType) // so we have a type history entry before erasure
}
sym.updateInfo(member.tpe.resultType) // info at current phase
@@ -349,13 +350,15 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
setAnnotations accessed.annotations)
}
}
- } else if (member.isSuperAccessor) { // mixin super accessors
+ }
+ else if (member.isSuperAccessor) { // mixin super accessors
val member1 = addMember(clazz, member.cloneSymbol(clazz)) setPos clazz.pos
assert(member1.alias != NoSymbol, member1)
val alias1 = rebindSuper(clazz, member.alias, mixinClass)
member1.asInstanceOf[TermSymbol] setAlias alias1
- } else if (member.isMethod && member.isModule && member.hasNoFlags(LIFTED | BRIDGE)) {
+ }
+ else if (member.isMethod && member.isModule && member.hasNoFlags(LIFTED | BRIDGE)) {
// mixin objects: todo what happens with abstract objects?
addMember(clazz, member.cloneSymbol(clazz, member.flags & ~(DEFERRED | lateDEFERRED)) setPos clazz.pos)
}
@@ -383,7 +386,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
var parents1 = parents
var decls1 = decls
if (!clazz.isPackageClass) {
- atPhase(phase.next)(clazz.owner.info)
+ afterMixin(clazz.owner.info)
if (clazz.isImplClass) {
clazz setFlag lateMODULE
var sourceModule = clazz.owner.info.decls.lookup(sym.name.toTermName)
@@ -449,7 +452,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
&& sym.owner == templ.symbol.owner
&& !sym.isLazy
&& !tree.isDef) {
- log("added use in: " + currentOwner + " -- " + tree)
+ debuglog("added use in: " + currentOwner + " -- " + tree)
usedIn(sym) ::= currentOwner
}
@@ -459,7 +462,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}
}
SingleUseTraverser(templ)
- log("usedIn: " + usedIn)
+ debuglog("usedIn: " + usedIn)
usedIn filter {
case (_, member :: Nil) => member.isValue && member.isLazy
case _ => false
@@ -515,7 +518,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
tree match {
case Template(parents, self, body) =>
localTyper = erasure.newTyper(rootContext.make(tree, currentOwner))
- atPhase(phase.next)(currentOwner.owner.info)//todo: needed?
+ afterMixin(currentOwner.owner.info)//todo: needed?
if (!currentOwner.isTrait && !isValueClass(currentOwner))
addMixedinMembers(currentOwner, unit)
@@ -523,18 +526,18 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
addLateInterfaceMembers(currentOwner)
tree
- case DefDef(mods, name, tparams, List(vparams), tpt, rhs) =>
+ case DefDef(_, _, _, vparams :: Nil, _, _) =>
if (currentOwner.isImplClass) {
if (isImplementedStatically(sym)) {
sym setFlag notOVERRIDE
self = sym.newValueParameter(nme.SELF, sym.pos) setInfo toInterface(currentOwner.typeOfThis)
val selfdef = ValDef(self) setType NoType
- treeCopy.DefDef(tree, mods, name, tparams, List(selfdef :: vparams), tpt, rhs)
- } else {
- EmptyTree
+ copyDefDef(tree)(vparamss = List(selfdef :: vparams))
}
- } else {
- if (currentOwner.isTrait && sym.isSetter && !atPhase(currentRun.picklerPhase)(sym.isDeferred)) {
+ else EmptyTree
+ }
+ else {
+ if (currentOwner.isTrait && sym.isSetter && !beforePickler(sym.isDeferred)) {
sym.addAnnotation(TraitSetterAnnotationClass)
}
tree
@@ -699,15 +702,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* This rhs is typed and then mixin transformed.
*/
def completeSuperAccessor(stat: Tree) = stat match {
- case DefDef(mods, name, tparams, List(vparams), tpt, EmptyTree) if stat.symbol.isSuperAccessor =>
+ case DefDef(_, _, _, vparams :: Nil, _, EmptyTree) if stat.symbol.isSuperAccessor =>
val rhs0 = (Super(clazz, tpnme.EMPTY) DOT stat.symbol.alias)(vparams map (v => Ident(v.symbol)): _*)
val rhs1 = localTyped(stat.pos, rhs0, stat.symbol.tpe.resultType)
- val rhs2 = atPhase(currentRun.mixinPhase)(transform(rhs1))
- debuglog("complete super acc " + stat.symbol.fullLocationString +
- " " + rhs1 + " " + stat.symbol.alias.fullLocationString +
- "/" + stat.symbol.alias.owner.hasFlag(lateINTERFACE))//debug
- treeCopy.DefDef(stat, mods, name, tparams, List(vparams), tpt, rhs2)
+ deriveDefDef(stat)(_ => beforeMixin(transform(rhs1)))
case _ =>
stat
}
@@ -738,7 +737,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
def createBitmap: Symbol = {
val sym = clazz0.newVariable(bitmapName, clazz0.pos) setInfo IntClass.tpe
- atPhase(currentRun.typerPhase)(sym addAnnotation VolatileAttr)
+ beforeTyper(sym addAnnotation VolatileAttr)
category match {
case nme.BITMAP_TRANSIENT | nme.BITMAP_CHECKINIT_TRANSIENT => sym addAnnotation TransientAttr
@@ -846,7 +845,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
val nulls = lazyValNullables(lzyVal).toList sortBy (_.id) map nullify
def syncBody = init ::: List(mkSetFlag(clazz, offset, lzyVal), UNIT)
- log("nulling fields inside " + lzyVal + ": " + nulls)
+ if (nulls.nonEmpty)
+ log("nulling fields inside " + lzyVal + ": " + nulls)
+
val result = gen.mkDoubleCheckedLocking(clazz, cond, syncBody, nulls)
typedPos(init.head.pos)(BLOCK(result, retVal))
}
@@ -883,14 +884,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
*/
def addCheckedGetters(clazz: Symbol, stats: List[Tree]): List[Tree] = {
def dd(stat: DefDef) = {
- val DefDef(mods, name, tp, vp, tpt, rhs) = stat
- val sym = stat.symbol
- def isUnit = sym.tpe.resultType.typeSymbol == UnitClass
- def isEmpty = rhs == EmptyTree
+ val sym = stat.symbol
+ def isUnit = sym.tpe.resultType.typeSymbol == UnitClass
+ def isEmpty = stat.rhs == EmptyTree
if (sym.isLazy && !isEmpty && !clazz.isImplClass) {
assert(fieldOffset contains sym, sym)
- treeCopy.DefDef(stat, mods, name, tp, vp, tpt,
+ deriveDefDef(stat)(rhs =>
if (isUnit)
mkLazyDef(clazz, sym, List(rhs), UNIT, fieldOffset(sym))
else {
@@ -901,7 +901,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}
else if (needsInitFlag(sym) && !isEmpty && !clazz.hasFlag(IMPLCLASS | TRAIT)) {
assert(fieldOffset contains sym, sym)
- treeCopy.DefDef(stat, mods, name, tp, vp, tpt,
+ deriveDefDef(stat)(rhs =>
(mkCheckedAccessor(clazz, _: Tree, fieldOffset(sym), stat.pos, sym))(
if (sym.tpe.resultType.typeSymbol == UnitClass) UNIT
else rhs
@@ -909,26 +909,24 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
)
}
else if (sym.isConstructor) {
- treeCopy.DefDef(stat, mods, name, tp, vp, tpt, addInitBits(clazz, rhs))
+ deriveDefDef(stat)(addInitBits(clazz, _))
}
else if (settings.checkInit.value && !clazz.isTrait && sym.isSetter) {
val getter = sym.getter(clazz)
if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter))
- treeCopy.DefDef(stat, mods, name, tp, vp, tpt,
- Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter))), UNIT)
- )
+ deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter))), UNIT))
else stat
}
else if (sym.isModule && (!clazz.isTrait || clazz.isImplClass) && !sym.isBridge) {
- treeCopy.DefDef(stat, mods, name, tp, vp, tpt,
- typedPos(stat.pos) {
+ deriveDefDef(stat)(rhs =>
+ typedPos(stat.pos)(
mkInnerClassAccessorDoubleChecked(
// Martin to Hubert: I think this can be replaced by selfRef(tree.pos)
// @PP: It does not seem so, it crashes for me trying to bootstrap.
- if (clazz.isImplClass) gen.mkAttributedIdent(vp.head.head.symbol) else gen.mkAttributedThis(clazz),
+ if (clazz.isImplClass) gen.mkAttributedIdent(stat.vparamss.head.head.symbol) else gen.mkAttributedThis(clazz),
rhs
)
- }
+ )
)
}
else stat
@@ -943,7 +941,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
private def checkedGetter(lhs: Tree) = {
val sym = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter)
if (needsInitAndHasOffset(sym)) {
- log("adding checked getter for: " + sym + " " + lhs.symbol.defaultFlagString)
+ debuglog("adding checked getter for: " + sym + " " + lhs.symbol.defaultFlagString)
List(localTyper typed mkSetFlag(clazz, fieldOffset(sym), sym))
}
else Nil
@@ -1130,7 +1128,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
*/
private def postTransform(tree: Tree): Tree = {
val sym = tree.symbol
- // assert(tree.tpe ne null, tree.getClass +" : "+ tree +" in "+ localTyper.context.tree)
// change every node type that refers to an implementation class to its
// corresponding interface, unless the node's symbol is an implementation class.
if (tree.tpe.typeSymbol.isImplClass && ((sym eq null) || !sym.isImplClass))
@@ -1163,7 +1160,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
def implSym = implClass(sym.owner).info.member(sym.name)
assert(target ne NoSymbol,
List(sym + ":", sym.tpe, sym.owner, implClass(sym.owner), implSym,
- atPhase(phase.prev)(implSym.tpe), phase) mkString " "
+ beforePrevPhase(implSym.tpe), phase) mkString " "
)
typedPos(tree.pos)(Apply(staticRef(target), transformSuper(qual) :: args))
}
@@ -1188,7 +1185,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
val sym1 = sym.overridingSymbol(currentOwner.enclClass)
typedPos(tree.pos)((transformSuper(qual) DOT sym1)())
} else {
- staticCall(atPhase(phase.prev)(sym.overridingSymbol(implClass(sym.owner))))
+ staticCall(beforePrevPhase(sym.overridingSymbol(implClass(sym.owner))))
}
} else {
assert(!currentOwner.enclClass.isImplClass, currentOwner.enclClass)
@@ -1237,7 +1234,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
val tree1 = super.transform(preTransform(tree))
// localTyper needed when not flattening inner classes. parts after an
// inner class will otherwise be typechecked with a wrong scope
- try atPhase(phase.next)(postTransform(tree1))
+ try afterMixin(postTransform(tree1))
finally localTyper = saved
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala
index 1200e973c5..d8c18c2d50 100644
--- a/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala
+++ b/src/compiler/scala/tools/nsc/transform/OverridingPairs.scala
@@ -45,8 +45,14 @@ abstract class OverridingPairs {
* Types always match. Term symbols match if their membertypes
* relative to <base>.this do
*/
- protected def matches(sym1: Symbol, sym2: Symbol): Boolean =
- sym1.isType || (self.memberType(sym1) matches self.memberType(sym2))
+ protected def matches(sym1: Symbol, sym2: Symbol): Boolean = {
+ def tp_s(s: Symbol) = self.memberType(s) + "/" + self.memberType(s).getClass
+ val result = sym1.isType || (self.memberType(sym1) matches self.memberType(sym2))
+ debuglog("overriding-pairs? %s matches %s (%s vs. %s) == %s".format(
+ sym1.fullLocationString, sym2.fullLocationString, tp_s(sym1), tp_s(sym2), result))
+
+ result
+ }
/** An implementation of BitSets as arrays (maybe consider collection.BitSet
* for that?) The main purpose of this is to implement
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index c1265b39d7..323fecfd0a 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -6,12 +6,9 @@
package scala.tools.nsc
package transform
-
import scala.tools.nsc.symtab.Flags
import scala.collection.{ mutable, immutable }
-
-
/** Specialize code on types.
*
* Make sure you've read the thesis:
@@ -71,10 +68,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
RootClass, BooleanClass, UnitClass, ArrayClass,
ScalaValueClasses, isValueClass, isScalaValueType,
SpecializedClass, RepeatedParamClass, JavaRepeatedParamClass,
- AnyRefClass, ObjectClass, Predef_AnyRef,
- uncheckedVarianceClass
+ AnyRefClass, ObjectClass, AnyRefModule,
+ GroupOfSpecializable, uncheckedVarianceClass, ScalaInlineClass
}
-
+
/** TODO - this is a lot of maps.
*/
@@ -82,17 +79,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val specializedClass: mutable.Map[(Symbol, TypeEnv), Symbol] = new mutable.LinkedHashMap
/** Map a method symbol to a list of its specialized overloads in the same class. */
- private val overloads: mutable.Map[Symbol, List[Overload]] = mutable.HashMap[Symbol, List[Overload]]() withDefaultValue Nil
+ private val overloads = perRunCaches.newMap[Symbol, List[Overload]]() withDefaultValue Nil
/** Map a symbol to additional information on specialization. */
- private val info: mutable.Map[Symbol, SpecializedInfo] = perRunCaches.newMap[Symbol, SpecializedInfo]()
+ private val info = perRunCaches.newMap[Symbol, SpecializedInfo]()
/** Map class symbols to the type environments where they were created. */
- private val typeEnv = mutable.HashMap[Symbol, TypeEnv]() withDefaultValue emptyEnv
+ private val typeEnv = perRunCaches.newMap[Symbol, TypeEnv]() withDefaultValue emptyEnv
- // holds mappings from regular type parameter symbols to symbols of
- // specialized type parameters which are subtypes of AnyRef
- private val anyrefSpecCache = perRunCaches.newMap[Symbol, Symbol]()
+ // Key: a specialized class or method
+ // Value: a map from tparams in the original class to tparams in the specialized class.
+ private val anyrefSpecCache = perRunCaches.newMap[Symbol, mutable.Map[Symbol, Symbol]]()
// holds mappings from members to the type variables in the class
// that they were already specialized for, so that they don't get
@@ -100,30 +97,53 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
private val wasSpecializedForTypeVars = perRunCaches.newMap[Symbol, Set[Symbol]]() withDefaultValue Set()
/** Concrete methods that use a specialized type, or override such methods. */
- private val concreteSpecMethods = new mutable.HashSet[Symbol]()
+ private val concreteSpecMethods = perRunCaches.newWeakSet[Symbol]()
private def isSpecialized(sym: Symbol) = sym hasAnnotation SpecializedClass
private def hasSpecializedFlag(sym: Symbol) = sym hasFlag SPECIALIZED
private def specializedTypes(tps: List[Symbol]) = tps filter isSpecialized
- private def specializedOn(sym: Symbol) = sym getAnnotation SpecializedClass match {
- case Some(AnnotationInfo(_, args, _)) => args
- case _ => Nil
+ private def specializedOn(sym: Symbol): List[Symbol] = {
+ sym getAnnotation SpecializedClass match {
+ case Some(AnnotationInfo(_, Nil, _)) => specializableTypes.map(_.typeSymbol)
+ case Some(ann @ AnnotationInfo(_, args, _)) => {
+ args map (_.tpe) flatMap { tp =>
+ tp baseType GroupOfSpecializable match {
+ case TypeRef(_, GroupOfSpecializable, arg :: Nil) =>
+ arg.typeArgs map (_.typeSymbol)
+ case _ =>
+ List(tp.typeSymbol)
+ }
+ }
+ }
+ case _ => Nil
+ }
}
// If we replace `isBoundedGeneric` with (tp <:< AnyRefClass.tpe),
// then pos/spec-List.scala fails - why? Does this kind of check fail
// for similar reasons? Does `sym.isAbstractType` make a difference?
- private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = (
- specializedOn(sym).exists(_.symbol == Predef_AnyRef) // specialized on AnyRef
- && !isValueClass(tp.typeSymbol)
- && isBoundedGeneric(tp)
- )
+ private def isSpecializedAnyRefSubtype(tp: Type, sym: Symbol) = {
+ specializedOn(sym).exists(s => !isValueClass(s)) &&
+ !isValueClass(tp.typeSymbol) &&
+ isBoundedGeneric(tp)
+ //(tp <:< AnyRefClass.tpe)
+ }
private def isBoundedGeneric(tp: Type) = tp match {
case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefClass.tpe)
case TypeRef(_, sym, _) => !isValueClass(sym)
case _ => false
}
+ def unspecializedSymbol(sym: Symbol): Symbol = {
+ if (sym hasFlag SPECIALIZED) {
+ // add initialization from its generic class constructor
+ val genericName = nme.unspecializedName(sym.name)
+ val member = sym.owner.info.decl(genericName.toTypeName)
+ member
+ }
+ else NoSymbol
+ }
+
object TypeEnv {
/** Return a new type environment binding specialized type parameters of sym to
* the given args. Expects the lists to have the same length.
@@ -241,7 +261,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val stvTypeParams = specializedTypeVars(target.info.typeParams map (_.info))
val stvResult = specializedTypeVars(target.info.resultType)
- log("degenerate: " + target + " stv tparams: " + stvTypeParams + " stv info: " + stvResult)
+ debuglog("degenerate: " + target + " stv tparams: " + stvTypeParams + " stv info: " + stvResult)
(stvTypeParams -- stvResult).nonEmpty
}
@@ -272,10 +292,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val pre1 = this(pre)
// when searching for a specialized class, take care to map all
// type parameters that are subtypes of AnyRef to AnyRef
- val args1 = map2(args, sym.typeParams) {
- case (tp, orig) if isSpecializedAnyRefSubtype(tp, orig) => AnyRefClass.tpe
- case (tp, _) => tp
- }
+ val args1 = map2(args, sym.info.typeParams)((tp, orig) =>
+ if (isSpecializedAnyRefSubtype(tp, orig)) AnyRefClass.tpe
+ else tp
+ )
specializedClass.get((sym, TypeEnv.fromSpecialization(sym, args1))) match {
case Some(sym1) => typeRef(pre1, sym1, survivingArgs(sym, args))
case None => typeRef(pre1, sym, args)
@@ -315,35 +335,38 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
nme.getterToLocal(specializedName(nme.localToGetter(name), types1, types2))
else {
val (base, cs, ms) = nme.splitSpecializedName(name)
- val abbrevs = definitions.abbrvTag withDefaultValue definitions.abbrvTag(ObjectClass)
newTermName(base.toString + "$"
- + "m" + ms + types1.map(t => abbrevs(t.typeSymbol)).mkString("", "", "")
- + "c" + cs + types2.map(t => abbrevs(t.typeSymbol)).mkString("", "", "$sp"))
+ + "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "")
+ + "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp"))
}
}
- lazy val primitiveTypes = ScalaValueClasses map (_.tpe)
+ lazy val specializableTypes = (ScalaValueClasses :+ AnyRefClass) map (_.tpe) sorted
+
+ /** If the symbol is the companion of a value class, the value class.
+ * Otherwise, AnyRef.
+ */
+ def specializesClass(sym: Symbol): Symbol = {
+ val c = sym.companionClass
+ if (isValueClass(c)) c else AnyRefClass
+ }
/** Return the types `sym` should be specialized at. This may be some of the primitive types
* or AnyRef. AnyRef means that a new type parameter T will be generated later, known to be a
* subtype of AnyRef (T <: AnyRef).
* These are in a meaningful order for stability purposes.
*/
- def concreteTypes(sym: Symbol): List[Type] = (
- if (!isSpecialized(sym)) Nil // no @specialized Annotation
- else specializedOn(sym) match {
- case Nil => primitiveTypes // specialized on everything
- case args => // specialized on args
- (args map { tp =>
- if (tp.symbol == Predef_AnyRef) {
- if (isBoundedGeneric(sym.tpe))
- reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefClass.tpe + ".")
- AnyRefClass.tpe
- }
- else tp.symbol.companionClass.tpe
- }).sorted
- }
- )
+ def concreteTypes(sym: Symbol): List[Type] = {
+ val types = if (!isSpecialized(sym))
+ Nil // no @specialized Annotation
+ else
+ specializedOn(sym) map (s => specializesClass(s).tpe) sorted
+
+ if (isBoundedGeneric(sym.tpe) && (types contains AnyRefClass))
+ reporter.warning(sym.pos, sym + " is always a subtype of " + AnyRefClass.tpe + ".")
+
+ types
+ }
/** Return a list of all type environments for all specializations
* of @specialized types in `tps`.
@@ -357,8 +380,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case set :: Nil => set map (x => List(x))
case set :: sets => for (x <- set ; xs <- loop(sets)) yield x :: xs
}
- // zip the keys with each permutation to create a TypeEnv
- loop(keys map concreteTypes) map (xss => Map(keys zip xss: _*))
+ // zip the keys with each permutation to create a TypeEnv.
+ // If we don't exclude the "all AnyRef" specialization, we will
+ // incur duplicate members and crash during mixin.
+ loop(keys map concreteTypes) filterNot (_ forall (_ <:< AnyRefClass.tpe)) map (xss => Map(keys zip xss: _*))
}
/** Does the given 'sym' need to be specialized in the environment 'env'?
@@ -382,8 +407,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
tpes foreach (tp => buf ++= specializedTypeVars(tp))
buf.result
}
- def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] =
- atPhase(currentRun.typerPhase)(specializedTypeVars(sym.info))
+ def specializedTypeVars(sym: Symbol): immutable.Set[Symbol] = beforeTyper(specializedTypeVars(sym.info))
/** Return the set of @specialized type variables mentioned by the given type.
* It only counts type variables that appear:
@@ -415,27 +439,24 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case _ => Set()
}
- /** Returns the type parameter in the specialized class `clazz` that corresponds to type parameter
- * `sym` in the original class. It will create it if needed or use the one from the cache.
+ /** Returns the type parameter in the specialized class `sClass` that corresponds to type parameter
+ * `tparam` in the original class. It will create it if needed or use the one from the cache.
*/
- private def typeParamSubAnyRef(sym: Symbol, clazz: Symbol) = (
- anyrefSpecCache.getOrElseUpdate(sym,
- clazz.newTypeParameter(sym.name append nme.SPECIALIZED_SUFFIX_NAME toTypeName, sym.pos)
- setInfo TypeBounds(sym.info.bounds.lo, AnyRefClass.tpe)
+ private def typeParamSubAnyRef(tparam: Symbol, sClass: Symbol): Type = {
+ val sClassMap = anyrefSpecCache.getOrElseUpdate(sClass, mutable.Map[Symbol, Symbol]())
+
+ sClassMap.getOrElseUpdate(tparam,
+ tparam.cloneSymbol(sClass, tparam.flags, tparam.name append tpnme.SPECIALIZED_SUFFIX)
+ modifyInfo (info => TypeBounds(info.bounds.lo, AnyRefClass.tpe))
).tpe
- )
+ }
/** Cleans the anyrefSpecCache of all type parameter symbols of a class.
*/
- private def cleanAnyRefSpecCache(clazz: Symbol, decls: List[Symbol]) = (
+ private def cleanAnyRefSpecCache(clazz: Symbol, decls: List[Symbol]) {
// remove class type parameters and those of normalized members.
- clazz :: decls foreach {
- _.tpe match {
- case PolyType(tparams, _) => anyrefSpecCache --= tparams
- case _ => ()
- }
- }
- )
+ clazz :: decls foreach (anyrefSpecCache remove _)
+ }
/** Type parameters that survive when specializing in the specified environment. */
def survivingParams(params: List[Symbol], env: TypeEnv) =
@@ -490,11 +511,21 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* was both already used for a map and mucho long. So "sClass" is the
* specialized subclass of "clazz" throughout this file.
*/
- val sClass = clazz.owner.newClass(specializedName(clazz, env0).toTypeName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE)
+
+ // SI-5545: Eliminate classes with the same name loaded from the bytecode already present - all we need to do is
+ // to force .info on them, as their lazy type will be evaluated and the symbols will be eliminated. Unfortunately
+ // evaluating the info after creating the specialized class will mess the specialized class signature, so we'd
+ // better evaluate it before creating the new class symbol
+ val clazzName = specializedName(clazz, env0).toTypeName
+ val bytecodeClazz = clazz.owner.info.decl(clazzName)
+ debuglog("Specializing " + clazz + " found " + bytecodeClazz + " already there")
+ bytecodeClazz.info
+
+ val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE)
def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long) =
member.cloneSymbol(sClass, flagFn(member.flags | SPECIALIZED))
-
+
sClass.sourceFile = clazz.sourceFile
currentRun.symSource(sClass) = clazz.sourceFile // needed later on by mixin
@@ -527,7 +558,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
var res: List[Type] = Nil
// log(specializedClass + ": seeking specialized parents of class with parents: " + parents.map(_.typeSymbol))
for (p <- parents) {
- val stp = atPhase(phase.next)(specializedType(p))
+ val stp = afterSpecialize(specializedType(p))
if (stp != p)
if (p.typeSymbol.isTrait) res ::= stp
else if (currentRun.compiles(clazz))
@@ -537,7 +568,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
res
}
- var parents = List(applyContext(atPhase(currentRun.typerPhase)(clazz.tpe)))
+ var parents = List(applyContext(beforeTyper(clazz.tpe)))
// log("!!! Parents: " + parents + ", sym: " + parents.map(_.typeSymbol))
if (parents.head.typeSymbol.isTrait)
parents = parents.head.parents.head :: parents
@@ -551,7 +582,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
// as with the parents and assign it to typeOfThis.
if (clazz.typeOfThis.typeConstructor ne clazz.typeConstructor) {
sClass.typeOfThis = applyContext(clazz.typeOfThis)
- log("Rewriting self-type for specialized class:\n" +
+ debuglog("Rewriting self-type for specialized class:\n" +
" " + clazz.defStringSeenAs(clazz.typeOfThis) + "\n" +
" => " + sClass.defStringSeenAs(sClass.typeOfThis)
)
@@ -559,7 +590,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
GenPolyType(newClassTParams, ClassInfoType(parents ::: extraSpecializedMixins, decls1, sClass))
}
- atPhase(phase.next)(sClass setInfo specializedInfoType)
+ afterSpecialize(sClass setInfo specializedInfoType)
val fullEnv = outerEnv ++ env
/** Enter 'sym' in the scope of the current specialized class. It's type is
@@ -633,7 +664,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
})
}
else
- log("conflicting env for " + m + " env: " + env)
+ debuglog("conflicting env for " + m + " env: " + env)
}
else if (m.isDeferred) { // abstract methods
val specMember = enterMember(cloneInSpecializedClass(m, _ | DEFERRED))
@@ -702,7 +733,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
typeEnv(specClass) = fullEnv
specClass.name = specializedName(specClass, fullEnv).toTypeName
enterMember(specClass)
- log("entered specialized class " + specClass.fullName)
+ debuglog("entered specialized class " + specClass.fullName)
info(specClass) = SpecializedInnerClass(m, fullEnv)
}
}
@@ -739,7 +770,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if (existing != NoSymbol)
clazz.owner.info.decls.unlink(existing)
- atPhase(phase.next)(clazz.owner.info.decls enter spc) //!!! assumes fully specialized classes
+ afterSpecialize(clazz.owner.info.decls enter spc) //!!! assumes fully specialized classes
}
if (subclasses.nonEmpty) clazz.resetFlag(FINAL)
cleanAnyRefSpecCache(clazz, decls1)
@@ -758,7 +789,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
private def normalizeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv): List[Symbol] = {
debuglog("normalizeMember: " + sym.fullName)
sym :: (
- if (!sym.isMethod || atPhase(currentRun.typerPhase)(sym.typeParams.isEmpty)) Nil
+ if (!sym.isMethod || beforeTyper(sym.typeParams.isEmpty)) Nil
else {
var specializingOn = specializedParams(sym)
val unusedStvars = specializingOn filterNot specializedTypeVars(sym.info)
@@ -816,10 +847,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if (sym.isPrivate/* || sym.isProtected*/) {
//sym.privateWithin = sym.enclosingPackage
sym.resetFlag(PRIVATE).setFlag(PROTECTED)
- log("-->d SETTING PRIVATE WITHIN TO " + sym.enclosingPackage + " for " + sym)
+ debuglog("-->d SETTING PRIVATE WITHIN TO " + sym.enclosingPackage + " for " + sym)
}
- sym.resetFlag(FINAL)
val specMember = subst(outerEnv)(specializedOverload(owner, sym, spec))
typeEnv(specMember) = typeEnv(sym) ++ outerEnv ++ spec
wasSpecializedForTypeVars(specMember) ++= spec collect { case (s, tp) if s.tpe == tp => s }
@@ -900,7 +930,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
checkOverriddenTParams(overridden)
val env = unify(overridden.info, overriding.info, emptyEnv, false)
- def atNext = atPhase(phase.next)(overridden.owner.info.decl(specializedName(overridden, env)))
+ def atNext = afterSpecialize(overridden.owner.info.decl(specializedName(overridden, env)))
debuglog("\t\tenv: " + env + "isValid: " + TypeEnv.isValid(env, overridden) + "found: " + atNext)
if (TypeEnv.restrict(env, stvars).nonEmpty && TypeEnv.isValid(env, overridden) && atNext != NoSymbol)
@@ -915,7 +945,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case (NoSymbol, _) => None
case (overridden, env) =>
val om = specializedOverload(clazz, overridden, env)
- log("Added specialized overload %s for %s in env: %s with type: %s".format(om, overriding.fullName, env, om.info))
+ debuglog("Added specialized overload %s for %s in env: %s with type: %s".format(om, overriding.fullName, env, om.info))
typeEnv(om) = env
addConcreteSpecMethod(overriding)
info(om) = (
@@ -940,7 +970,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
)
overloads(overriding) ::= Overload(om, env)
- ifDebug(atPhase(phase.next)(assert(
+ ifDebug(afterSpecialize(assert(
overridden.owner.info.decl(om.name) != NoSymbol,
"Could not find " + om.name + " in " + overridden.owner.info.decls))
)
@@ -950,6 +980,10 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
case object UnifyError extends scala.util.control.ControlThrowable
+ private[this] def unifyError(tp1: Any, tp2: Any): Nothing = {
+ log("unifyError" + ((tp1, tp2)))
+ throw UnifyError
+ }
/** Return the most general type environment that specializes tp1 to tp2.
* It only allows binding of type parameters annotated with @specialized.
@@ -960,29 +994,34 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean): TypeEnv = (tp1, tp2) match {
case (TypeRef(_, sym1, _), _) if isSpecialized(sym1) =>
debuglog("Unify - basic case: " + tp1 + ", " + tp2)
- if (isValueClass(tp2.typeSymbol) || isSpecializedAnyRefSubtype(tp2, sym1))
+ if (isValueClass(tp2.typeSymbol))
env + ((sym1, tp2))
+ else if (isSpecializedAnyRefSubtype(tp2, sym1))
+ env + ((sym1, tp2)) // env + ((sym1, AnyRefClass.tpe))
+ else if (strict)
+ unifyError(tp1, tp2)
else
- if (strict) throw UnifyError else env
+ env
case (TypeRef(_, sym1, args1), TypeRef(_, sym2, args2)) =>
debuglog("Unify TypeRefs: " + tp1 + " and " + tp2 + " with args " + (args1, args2) + " - ")
- if (strict && args1.length != args2.length) throw UnifyError
+ if (strict && args1.length != args2.length) unifyError(tp1, tp2)
val e = unify(args1, args2, env, strict)
debuglog("unified to: " + e)
e
case (TypeRef(_, sym1, _), _) if sym1.isTypeParameterOrSkolem =>
env
case (MethodType(params1, res1), MethodType(params2, res2)) =>
- if (strict && params1.length != params2.length) throw UnifyError
+ if (strict && params1.length != params2.length) unifyError(tp1, tp2)
debuglog("Unify MethodTypes: " + tp1 + " and " + tp2)
unify(res1 :: (params1 map (_.tpe)), res2 :: (params2 map (_.tpe)), env, strict)
case (PolyType(tparams1, res1), PolyType(tparams2, res2)) =>
- if (strict && tparams1.length != tparams2.length) throw UnifyError
debuglog("Unify PolyTypes: " + tp1 + " and " + tp2)
- unify(res1, res2, env, strict)
- case (PolyType(_, res), other) =>
- unify(res, other, env, strict)
- case (ThisType(_), ThisType(_)) => env
+ if (strict && tparams1.length != tparams2.length)
+ unifyError(tp1, tp2)
+ else
+ unify(res1, res2, env, strict)
+ case (PolyType(_, res), other) => unify(res, other, env, strict)
+ case (ThisType(_), ThisType(_)) => env
case (_, SingleType(_, _)) => unify(tp1, tp2.underlying, env, strict)
case (SingleType(_, _), _) => unify(tp1.underlying, tp2, env, strict)
case (ThisType(_), _) => unify(tp1.widen, tp2, env, strict)
@@ -1004,7 +1043,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if (env.keySet intersect nenv.keySet isEmpty) env ++ nenv
else {
debuglog("could not unify: u(" + args._1 + ", " + args._2 + ") yields " + nenv + ", env: " + env)
- throw UnifyError
+ unifyError(tp1, tp2)
}
}
}
@@ -1082,7 +1121,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case cinfo @ ClassInfoType(parents, decls, clazz) if !unspecializableClass(cinfo) =>
val tparams = tpe.typeParams
if (tparams.isEmpty)
- atPhase(phase.next)(parents map (_.typeSymbol.info))
+ afterSpecialize(parents map (_.typeSymbol.info))
val parents1 = parents map specializedType
debuglog("transformInfo %s %s with parents1 %s ph: %s".format(
@@ -1128,7 +1167,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if (warnings)
reporter.warning(tvar.pos, "Bounds prevent specialization of " + tvar)
- log("specvars: " +
+ debuglog("specvars: " +
tvar.info.bounds.lo + ": " +
specializedTypeVars(tvar.info.bounds.lo) + " " +
subst(env, tvar.info.bounds.hi) + ": " +
@@ -1197,27 +1236,27 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
} else NoSymbol
def illegalSpecializedInheritance(clazz: Symbol): Boolean = (
- hasSpecializedFlag(clazz)
+ hasSpecializedFlag(clazz)
&& originalClass(clazz).parentSymbols.exists(p => hasSpecializedParams(p) && !p.isTrait)
)
def specializeCalls(unit: CompilationUnit) = new TypingTransformer(unit) {
/** Map a specializable method to it's rhs, when not deferred. */
- val body: mutable.Map[Symbol, Tree] = new mutable.HashMap
+ val body = perRunCaches.newMap[Symbol, Tree]()
/** Map a specializable method to its value parameter symbols. */
- val parameters: mutable.Map[Symbol, List[List[Symbol]]] = new mutable.HashMap
+ val parameters = perRunCaches.newMap[Symbol, List[Symbol]]()
/** Collect method bodies that are concrete specialized methods.
*/
class CollectMethodBodies extends Traverser {
override def traverse(tree: Tree) = tree match {
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case DefDef(_, _, _, vparams :: Nil, _, rhs) =>
if (concreteSpecMethods(tree.symbol) || tree.symbol.isConstructor) {
debuglog("!!! adding body of a defdef %s, symbol %s: %s".format(tree, tree.symbol, rhs))
body(tree.symbol) = rhs
// body(tree.symbol) = tree // whole method
- parameters(tree.symbol) = mmap(vparamss)(_.symbol)
+ parameters(tree.symbol) = vparams.map(_.symbol)
concreteSpecMethods -= tree.symbol
} // no need to descend further down inside method bodies
@@ -1230,7 +1269,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
- def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) =
+ def doesConform(origSymbol: Symbol, treeType: Type, memberType: Type, env: TypeEnv) = {
(treeType =:= memberType) || { // anyref specialization
memberType match {
case PolyType(_, resTpe) =>
@@ -1247,6 +1286,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case _ => false
}
}
+ }
override def transform(tree: Tree): Tree = {
val symbol = tree.symbol
@@ -1254,20 +1294,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
/** The specialized symbol of 'tree.symbol' for tree.tpe, if there is one */
def specSym(qual: Tree): Option[Symbol] = {
val env = unify(symbol.tpe, tree.tpe, emptyEnv, false)
- log("[specSym] checking for rerouting: %s with \n\tsym.tpe: %s, \n\ttree.tpe: %s \n\tenv: %s \n\tname: %s"
+ debuglog("[specSym] checking for rerouting: %s with \n\tsym.tpe: %s, \n\ttree.tpe: %s \n\tenv: %s \n\tname: %s"
.format(tree, symbol.tpe, tree.tpe, env, specializedName(symbol, env)))
if (!env.isEmpty) { // a method?
val specCandidates = qual.tpe.member(specializedName(symbol, env))
val specMember = specCandidates suchThat { s =>
doesConform(symbol, tree.tpe, qual.tpe.memberType(s), env)
}
-
- log("[specSym] found: " + specCandidates.tpe + ", instantiated as: " + tree.tpe)
- log("[specSym] found specMember: " + specMember)
+
+ debuglog("[specSym] found: " + specCandidates.tpe + ", instantiated as: " + tree.tpe)
+ debuglog("[specSym] found specMember: " + specMember)
if (specMember ne NoSymbol)
if (TypeEnv.includes(typeEnv(specMember), env)) Some(specMember)
else {
- log("wrong environments for specialized member: \n\ttypeEnv(%s) = %s\n\tenv = %s".format(specMember, typeEnv(specMember), env))
+ debuglog("wrong environments for specialized member: \n\ttypeEnv(%s) = %s\n\tenv = %s".format(specMember, typeEnv(specMember), env))
None
}
else None
@@ -1277,16 +1317,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
curTree = tree
tree match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
- if (findSpec(tpt.tpe).typeSymbol ne tpt.tpe.typeSymbol) {
+ debuglog("Attempting to specialize new %s(%s)".format(tpt, args.mkString(", ")))
+ val found = findSpec(tpt.tpe)
+ if (found.typeSymbol ne tpt.tpe.typeSymbol) {
// the ctor can be specialized
- log("** instantiated specialized type: " + findSpec(tpt.tpe))
- try {
- atPos(tree.pos)(
- localTyper.typed(
- Apply(
- Select(New(TypeTree(findSpec(tpt.tpe))), nme.CONSTRUCTOR),
- transformTrees(args))))
- } catch {
+ debuglog("** instantiated specialized type: " + found)
+ try localTyper.typedPos(tree.pos)(New(found, transformTrees(args): _*))
+ catch {
case te: TypeError =>
reporter.error(tree.pos, te.msg)
super.transform(tree)
@@ -1313,7 +1350,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
)
val tree1 = gen.mkTypeApply(Select(qual1, specMember), residualTargs)
- log("rewrote " + tree + " to " + tree1)
+ debuglog("rewrote " + tree + " to " + tree1)
localTyper.typedOperator(atPos(tree.pos)(tree1)) // being polymorphic, it must be a method
case None => super.transform(tree)
@@ -1321,8 +1358,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
case Select(Super(_, _), name) if illegalSpecializedInheritance(currentClass) =>
val pos = tree.pos
- log(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.")
- log(pos.lineContent)
+ debuglog(pos.source.file.name+":"+pos.line+": not specializing call to super inside illegal specialized inheritance class.")
+ debuglog(pos.lineContent)
tree
case Select(qual, name) =>
@@ -1378,13 +1415,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
(new CollectMethodBodies)(tree)
val parents1 = map2(currentOwner.info.parents, parents)((tpe, parent) =>
TypeTree(tpe) setPos parent.pos)
-
+
treeCopy.Template(tree,
parents1 /*currentOwner.info.parents.map(tpe => TypeTree(tpe) setPos parents.head.pos)*/ ,
self,
atOwner(currentOwner)(transformTrees(body ::: specMembers)))
- case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) if info.isDefinedAt(symbol) =>
+ case ddef @ DefDef(_, _, _, vparamss, _, _) if info.isDefinedAt(symbol) =>
// log("--> method: " + ddef + " in " + ddef.symbol.owner + ", " + info(symbol))
if (symbol.isConstructor) {
@@ -1392,116 +1429,96 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val superRef: Tree = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR)
forwardCtorCall(tree.pos, superRef, vparamss, symbol.owner)
}
- if (symbol.isPrimaryConstructor) localTyper typed {
- atPos(symbol.pos)(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, Block(List(t), Literal(Constant()))))
- } else {
- // duplicate the original constructor
+ 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 info(symbol) match {
-
+ }
+ 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)
debuglog("implementation: " + tree1)
- val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree1
- treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs))
+ deriveDefDef(tree1)(transform)
case NormalizedMember(target) =>
- log("Normalized member: " + symbol + ", target: " + target)
+ debuglog("Normalized member: " + symbol + ", target: " + target)
if (target.isDeferred || conflicting(typeEnv(symbol))) {
- treeCopy.DefDef(
- tree, mods, name, tparams, vparamss, tpt,
- localTyper typed gen.mkSysErrorCall("boom! you stepped on a bug. This method should never be called.")
- )
+ deriveDefDef(tree)(_ => localTyper typed gen.mkSysErrorCall("Fatal error in code generation: this should never be called."))
}
else {
// we have an rhs, specialize it
val tree1 = duplicateBody(ddef, target)
debuglog("implementation: " + tree1)
- val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree1
- treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs))
+ deriveDefDef(tree1)(transform)
}
case SpecialOverride(target) =>
assert(body.isDefinedAt(target), "sym: " + symbol.fullName + " target: " + target.fullName)
//debuglog("moving implementation, body of target " + target + ": " + body(target))
- log("%s is param accessor? %b".format(ddef.symbol, ddef.symbol.isParamAccessor))
+ debuglog("%s is param accessor? %b".format(ddef.symbol, ddef.symbol.isParamAccessor))
// we have an rhs, specialize it
val tree1 = addBody(ddef, target)
(new ChangeOwnerTraverser(target, tree1.symbol))(tree1.rhs)
debuglog("changed owners, now: " + tree1)
- val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree1
- treeCopy.DefDef(tree1, mods, name, tparams, vparamss, tpt, transform(rhs))
-
+ deriveDefDef(tree1)(transform)
case SpecialOverload(original, env) =>
debuglog("completing specialized " + symbol.fullName + " calling " + original)
- log("special overload " + original + " -> " + env)
+ debuglog("special overload " + original + " -> " + env)
val t = DefDef(symbol, { vparamss =>
val fun = Apply(Select(This(symbol.owner), original),
makeArguments(original, vparamss.head))
- log("inside defdef: " + symbol + "; type: " + symbol.tpe + "; owner: " + symbol.owner)
+ debuglog("inside defdef: " + symbol + "; type: " + symbol.tpe + "; owner: " + symbol.owner)
gen.maybeMkAsInstanceOf(fun,
symbol.owner.thisType.memberType(symbol).finalResultType,
symbol.owner.thisType.memberType(original).finalResultType)
})
- log("created special overload tree " + t)
+ debuglog("created special overload tree " + t)
debuglog("created " + t)
localTyper.typed(t)
case fwd @ Forward(_) =>
- log("forward: " + fwd + ", " + ddef)
+ debuglog("forward: " + fwd + ", " + ddef)
val rhs1 = forwardCall(tree.pos, gen.mkAttributedRef(symbol.owner.thisType, fwd.target), vparamss)
- log("-->d completed forwarder to specialized overload: " + fwd.target + ": " + rhs1)
- localTyper.typed(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1))
+ debuglog("-->d completed forwarder to specialized overload: " + fwd.target + ": " + rhs1)
+ localTyper.typed(deriveDefDef(tree)(_ => rhs1))
case SpecializedAccessor(target) =>
val rhs1 = if (symbol.isGetter)
gen.mkAttributedRef(target)
else
Assign(gen.mkAttributedRef(target), Ident(vparamss.head.head.symbol))
- log("specialized accessor: " + target + " -> " + rhs1)
- localTyper.typed(treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1))
+ debuglog("specialized accessor: " + target + " -> " + rhs1)
+ localTyper.typed(deriveDefDef(tree)(_ => rhs1))
case Abstract(targ) =>
- log("abstract: " + targ)
- val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree
- val t = treeCopy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs)
- localTyper.typed(t)
+ debuglog("abstract: " + targ)
+ localTyper.typed(deriveDefDef(tree)(rhs => rhs))
}
- case ValDef(mods, name, tpt, rhs) if symbol.hasFlag(SPECIALIZED) && !symbol.isParamAccessor =>
+ case ValDef(_, _, _, _) if symbol.hasFlag(SPECIALIZED) && !symbol.isParamAccessor =>
assert(body.isDefinedAt(symbol.alias), body)
- val tree1 = treeCopy.ValDef(tree, mods, name, tpt, body(symbol.alias).duplicate)
+ val tree1 = deriveValDef(tree)(_ => body(symbol.alias).duplicate)
debuglog("now typing: " + tree1 + " in " + tree.symbol.owner.fullName)
+
val d = new Duplicator
- val ValDef(mods1, name1, tpt1, rhs1) = d.retyped(
+ val newValDef = d.retyped(
localTyper.context1.asInstanceOf[d.Context],
tree1,
symbol.alias.enclClass,
symbol.enclClass,
typeEnv(symbol.alias) ++ typeEnv(tree.symbol)
)
- val t = treeCopy.ValDef(tree1, mods1, name1, tpt1, transform(rhs1))
- log("valdef " + tree + " -> " + t)
- t
-
-// val tree1 =
-// treeCopy.ValDef(tree, mods, name, tpt,
-// localTyper.typed(
-// Apply(Select(Super(currentClass, nme.EMPTY), symbol.alias.getter(symbol.alias.owner)),
-// List())))
-// debuglog("replaced ValDef: " + tree1 + " in " + tree.symbol.owner.fullName)
-// tree1
+ deriveValDef(newValDef)(transform)
case Apply(sel @ Select(sup @ Super(qual, name), name1), args)
- if (sup.symbol.info.parents != atPhase(phase.prev)(sup.symbol.info.parents)) =>
+ if (sup.symbol.info.parents != beforePrevPhase(sup.symbol.info.parents)) =>
def parents = sup.symbol.info.parents
- debuglog(tree + " parents changed from: " + atPhase(phase.prev)(parents) + " to: " + parents)
+ debuglog(tree + " parents changed from: " + beforePrevPhase(parents) + " to: " + parents)
val res = localTyper.typed(
Apply(Select(Super(qual, name) setPos sup.pos, name1) setPos sel.pos, transformTrees(args)) setPos tree.pos)
@@ -1513,18 +1530,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
- private def reskolemize(tparams: List[TypeDef]): (List[Symbol], List[Symbol]) = {
- val saved = tparams map (_.symbol)
- localTyper skolemizeTypeParams tparams
- (saved, tparams map (_.symbol))
- }
-
private def duplicateBody(tree: DefDef, source: Symbol) = {
val symbol = tree.symbol
val meth = addBody(tree, source)
val d = new Duplicator
- log("-->d DUPLICATING: " + meth)
+ debuglog("-->d DUPLICATING: " + meth)
d.retyped(
localTyper.context1.asInstanceOf[d.Context],
meth,
@@ -1542,8 +1553,8 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
*/
private def addBody(tree: DefDef, source: Symbol): DefDef = {
val symbol = tree.symbol
- debuglog("specializing body of" + symbol.fullName + ": " + symbol.info)
- val DefDef(mods, name, tparams, vparamss, tpt, _) = tree
+ debuglog("specializing body of" + symbol.defString)
+ val DefDef(_, _, tparams, vparams :: Nil, tpt, _) = tree
// val (_, origtparams) = splitParams(source.typeParams)
val env = typeEnv(symbol)
val boundTvars = env.keySet
@@ -1551,12 +1562,12 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
debuglog("substituting " + origtparams + " for " + symbol.typeParams)
// skolemize type parameters
- val (oldtparams, newtparams) = reskolemize(tparams)
+ val oldtparams = tparams map (_.symbol)
+ val newtparams = deriveFreshSkolems(oldtparams)
+ map2(tparams, newtparams)(_ setSymbol _)
// create fresh symbols for value parameters to hold the skolem types
- val vparamss1 = List(for (vdef <- vparamss.head; param = vdef.symbol) yield {
- ValDef(param cloneSymbol symbol substInfo (oldtparams, newtparams))
- })
+ val newSyms = cloneSymbolsAtOwnerAndModify(vparams map (_.symbol), symbol, _.substSym(oldtparams, newtparams))
// replace value and type parameters of the old method with the new ones
// log("Adding body for " + tree.symbol + " - origtparams: " + origtparams + "; tparams: " + tparams)
@@ -1564,14 +1575,15 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
// log("Type env of: " + tree.symbol + ": " + boundTvars)
// log("newtparams: " + newtparams)
val symSubstituter = new ImplementationAdapter(
- parameters(source).flatten ::: origtparams,
- vparamss1.flatten.map(_.symbol) ::: newtparams,
+ parameters(source) ::: origtparams,
+ newSyms ::: newtparams,
source.enclClass,
false) // don't make private fields public
- val tmp = symSubstituter(body(source).duplicate)
+
+ val newBody = symSubstituter(body(source).duplicate)
tpt.tpe = tpt.tpe.substSym(oldtparams, newtparams)
- treeCopy.DefDef(tree, mods, name, tparams, vparamss1, tpt, tmp)
+ copyDefDef(tree)(vparamss = List(newSyms map ValDef), rhs = newBody)
}
/** Create trees for specialized members of 'sClass', based on the
@@ -1588,13 +1600,13 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if m.hasFlag(SPECIALIZED)
&& (m.sourceFile ne null)
&& satisfiable(typeEnv(m), !sClass.hasFlag(SPECIALIZED))) {
- log("creating tree for " + m.fullName)
+ debuglog("creating tree for " + m.fullName)
if (m.isMethod) {
if (info(m).target.hasAccessorFlag) hasSpecializedFields = true
if (m.isClassConstructor) {
- val origParamss = parameters(info(m).target)
+ val origParams = parameters(info(m).target)
val vparams = (
- map2(m.info.paramTypes, origParamss(0))((tp, sym) =>
+ map2(m.info.paramTypes, origParams)((tp, sym) =>
m.newValue(specializedName(sym, typeEnv(sClass)), sym.pos, sym.flags) setInfo tp
)
)
@@ -1644,7 +1656,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
buf +=
ClassDef(specCls, atPos(impl.pos)(Template(parents, emptyValDef, List()))
.setSymbol(specCls.newLocalDummy(sym1.pos))) setPos tree.pos
- log("created synthetic class: " + specCls + " of " + sym1 + " in env: " + env)
+ debuglog("created synthetic class: " + specCls + " of " + sym1 + " in env: " + env)
}
case _ =>
}
@@ -1685,7 +1697,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* - there is a getter for the specialized field in the same class
*/
def initializesSpecializedField(f: Symbol) = (
- (f.name endsWith nme.SPECIALIZED_SUFFIX_NAME)
+ (f.name endsWith nme.SPECIALIZED_SUFFIX)
&& clazz.info.member(nme.originalName(f.name)).isPublic
&& clazz.info.decl(f.name).suchThat(_.isGetter) != NoSymbol
)
@@ -1720,9 +1732,27 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
class SpecializationTransformer(unit: CompilationUnit) extends Transformer {
informProgress("specializing " + unit)
- override def transform(tree: Tree) =
- if (settings.nospecialization.value) tree
- else atPhase(phase.next)(specializeCalls(unit).transform(tree))
+ override def transform(tree: Tree) = {
+ val resultTree = if (settings.nospecialization.value) tree
+ else afterSpecialize(specializeCalls(unit).transform(tree))
+
+ // Remove the final modifier and @inline annotation from anything in the
+ // original class (since it's being overridden in at least onesubclass).
+ //
+ // We do this here so that the specialized subclasses will correctly copy
+ // final and @inline.
+ info.foreach {
+ case (sym, SpecialOverload(target, _)) => {
+ sym.resetFlag(FINAL)
+ target.resetFlag(FINAL)
+ sym.removeAnnotation(ScalaInlineClass)
+ target.removeAnnotation(ScalaInlineClass)
+ }
+ case _ => {}
+ }
+
+ resultTree
+ }
}
def printSpecStats() {
diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
index 1655ad09c4..fdb5c7e52e 100644
--- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala
+++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
@@ -204,7 +204,7 @@ abstract class TailCalls extends Transform {
fail(reason)
}
def rewriteTailCall(recv: Tree): Tree = {
- log("Rewriting tail recursive call: " + fun.pos.lineContent.trim)
+ debuglog("Rewriting tail recursive call: " + fun.pos.lineContent.trim)
ctx.accessed = true
typedPos(fun.pos)(Apply(Ident(ctx.label), recv :: transformArgs))
@@ -223,13 +223,29 @@ abstract class TailCalls extends Transform {
}
tree match {
- case dd @ DefDef(mods, name, tparams, vparams, tpt, rhs) =>
- val newCtx = new Context(dd)
+ case ValDef(_, _, _, _) =>
+ if (tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass))
+ unit.error(tree.pos, "lazy vals are not tailcall transformed")
+
+ super.transform(tree)
- debuglog("Considering " + name + " for tailcalls")
- val newRHS = transform(rhs, newCtx)
+ case dd @ DefDef(_, _, _, vparamss0, _, rhs0) if !dd.symbol.hasAccessorFlag =>
+ val newCtx = new Context(dd)
+ def isRecursiveCall(t: Tree) = {
+ val sym = t.symbol
+ (sym != null) && {
+ sym.isMethod && (dd.symbol.name == sym.name) && (dd.symbol.enclClass isSubClass sym.enclClass)
+ }
+ }
+ if (newCtx.isMandatory) {
+ if (!rhs0.exists(isRecursiveCall)) {
+ unit.error(tree.pos, "@tailrec annotated method contains no recursive calls")
+ }
+ }
+ debuglog("Considering " + dd.name + " for tailcalls")
+ val newRHS = transform(rhs0, newCtx)
- treeCopy.DefDef(tree, mods, name, tparams, vparams, tpt, {
+ deriveDefDef(tree)(rhs =>
if (newCtx.isTransformed) {
/** We have rewritten the tree, but there may be nested recursive calls remaining.
* If @tailrec is given we need to fail those now.
@@ -241,7 +257,7 @@ abstract class TailCalls extends Transform {
}
}
val newThis = newCtx.newThis(tree.pos)
- val vpSyms = vparams.flatten map (_.symbol)
+ val vpSyms = vparamss0.flatten map (_.symbol)
typedPos(tree.pos)(Block(
List(ValDef(newThis, This(currentClass))),
@@ -249,12 +265,12 @@ abstract class TailCalls extends Transform {
))
}
else {
- if (newCtx.isMandatory)
+ if (newCtx.isMandatory && newRHS.exists(isRecursiveCall))
newCtx.tailrecFailure()
newRHS
}
- })
+ )
case Block(stats, expr) =>
treeCopy.Block(tree,
@@ -263,11 +279,7 @@ abstract class TailCalls extends Transform {
)
case CaseDef(pat, guard, body) =>
- treeCopy.CaseDef(tree,
- pat,
- guard,
- transform(body)
- )
+ deriveCaseDef(tree)(transform)
case If(cond, thenp, elsep) =>
treeCopy.If(tree,
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index adbb7bc7f1..ee565530b7 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -129,7 +129,7 @@ abstract class UnCurry extends InfoTransform
appliedType(NonLocalReturnControlClass.typeConstructor, List(argtype))
/** A hashmap from method symbols to non-local return keys */
- private val nonLocalReturnKeys = new mutable.HashMap[Symbol, Symbol]
+ private val nonLocalReturnKeys = perRunCaches.newMap[Symbol, Symbol]()
/** Return non-local return key for given method */
private def nonLocalReturnKey(meth: Symbol) =
@@ -144,13 +144,13 @@ abstract class UnCurry extends InfoTransform
* todo: maybe clone a pre-existing exception instead?
* (but what to do about exceptions that miss their targets?)
*/
- private def nonLocalReturnThrow(expr: Tree, meth: Symbol) =
- localTyper.typed {
- Throw(
- New(
- TypeTree(nonLocalReturnExceptionType(expr.tpe)),
- List(List(Ident(nonLocalReturnKey(meth)), expr))))
- }
+ private def nonLocalReturnThrow(expr: Tree, meth: Symbol) = localTyper typed {
+ Throw(
+ nonLocalReturnExceptionType(expr.tpe.widen),
+ Ident(nonLocalReturnKey(meth)),
+ expr
+ )
+ }
/** Transform (body, key) to:
*
@@ -166,31 +166,18 @@ abstract class UnCurry extends InfoTransform
* }
*/
private def nonLocalReturnTry(body: Tree, key: Symbol, meth: Symbol) = {
- localTyper.typed {
- val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType)
- val ex = meth.newValue(nme.ex, body.pos) setInfo extpe
- val pat = Bind(ex,
- Typed(Ident(nme.WILDCARD),
- AppliedTypeTree(Ident(NonLocalReturnControlClass),
- List(Bind(tpnme.WILDCARD,
- EmptyTree)))))
- val rhs =
- If(
- Apply(
- Select(
- Apply(Select(Ident(ex), "key"), List()),
- Object_eq),
- List(Ident(key))),
- Apply(
- TypeApply(
- Select(
- Apply(Select(Ident(ex), "value"), List()),
- Any_asInstanceOf),
- List(TypeTree(meth.tpe.finalResultType))),
- List()),
- Throw(Ident(ex)))
- val keyDef = ValDef(key, New(TypeTree(ObjectClass.tpe), List(List())))
- val tryCatch = Try(body, List(CaseDef(pat, EmptyTree, rhs)), EmptyTree)
+ localTyper typed {
+ val extpe = nonLocalReturnExceptionType(meth.tpe.finalResultType)
+ val ex = meth.newValue(body.pos, nme.ex) setInfo extpe
+ val pat = gen.mkBindForCase(ex, NonLocalReturnControlClass, List(meth.tpe.finalResultType))
+ val rhs = (
+ IF ((ex DOT nme.key)() OBJ_EQ Ident(key))
+ THEN ((ex DOT nme.value)())
+ ELSE (Throw(Ident(ex)))
+ )
+ val keyDef = ValDef(key, New(ObjectClass.tpe))
+ val tryCatch = Try(body, pat -> rhs)
+
Block(List(keyDef), tryCatch)
}
}
@@ -228,9 +215,9 @@ abstract class UnCurry extends InfoTransform
* case P_1 if G_1 => E_1
* ...
* case P_n if G_n => true
- * case _ => this.missingCase(x)
+ * case _ => this.missingCase(expr)
* }
- * def isDefinedAtCurrent(x: T): boolean = (x: @unchecked) match {
+ * def _isDefinedAt(x: T): boolean = (x: @unchecked) match {
* case P_1 if G_1 => true
* ...
* case P_n if G_n => true
@@ -240,7 +227,7 @@ abstract class UnCurry extends InfoTransform
* new $anon()
*
* However, if one of the patterns P_i if G_i is a default pattern,
- * drop the last default clause in tghe definition of `apply` and generate for `isDefinedAtCurrent` instead
+ * drop the last default clause in the definition of `apply` and generate for `_isDefinedAt` instead
*
* def isDefinedAtCurrent(x: T): boolean = true
*/
@@ -260,7 +247,7 @@ abstract class UnCurry extends InfoTransform
else List(ObjectClass.tpe, fun.tpe, SerializableClass.tpe)
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
- val applyMethod = anonClass.newMethod(nme.apply, fun.pos, FINAL)
+ val applyMethod = anonClass.newMethod(nme.apply, fun.pos, FINAL)
applyMethod setInfoAndEnter MethodType(applyMethod newSyntheticValueParams formals, restpe)
anonClass addAnnotation serialVersionUIDAnnotation
@@ -291,73 +278,26 @@ abstract class UnCurry extends InfoTransform
val substParam = new TreeSymSubstituter(fun.vparams map (_.symbol), params)
def substTree[T <: Tree](t: T): T = substParam(resetLocalAttrs(t))
- // waiting here until we can mix case classes and extractors reliably (i.e., when virtpatmat becomes the default)
- // object VirtPatmatOpt {
- // object Last {
- // def unapply[T](xs: List[T]) = xs.lastOption
- // }
- // // keep this in synch by what's generated by combineCases/runOrElse
- // object MatcherBlock {
- // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree])] = matcher match { // TODO: BUG the unapplySeq version of the case below does not seem to work in virtpatmat??
- // case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) => Some(zero, x, matchRes, keepGoing, stats)
- // case _ => None
- // }
- // }
- // // TODO: virtpatmat use case: would be nice if could abstract over the repeated pattern more easily
- // // case Block(Last(P)) =>
- // // case P =>
- // def unapply(matcher: Tree): Option[(ValDef, ValDef, ValDef, ValDef, List[Tree], Tree => Tree)] = matcher match {
- // case MatcherBlock(zero, x, matchRes, keepGoing, stats) => Some(zero, x, matchRes, keepGoing, stats, identity[Tree])
- // case Block(outerStats, MatcherBlock(zero, x, matchRes, keepGoing, stats)) => Some(zero, x, matchRes, keepGoing, stats, inner => Block(outerStats, inner))
- // case b => treeBrowser browse b; None
- // }
- // }
-
- // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly
- def dupMatch(selector: Tree, cases: List[CaseDef], wrap: Match => Tree = identity) = {
- def transformCase(cdef: CaseDef): CaseDef =
- CaseDef(cdef.pat, cdef.guard, Literal(Constant(true)))
- def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
-
- gen.mkUncheckedMatch(
- if (cases exists treeInfo.isDefaultCase) Literal(Constant(true))
- else substTree(wrap(Match(selector, (cases map transformCase) :+ defaultCase)).duplicate)
- )
- }
+ object isDefinedAtTransformer extends gen.MatchMatcher {
+ // TODO: optimize duplication, but make sure ValDef's introduced by wrap are treated correctly
+ override def caseMatch(orig: Tree, selector: Tree, cases: List[CaseDef], wrap: Tree => Tree): Tree = {
+ def transformCase(cdef: CaseDef): CaseDef =
+ CaseDef(cdef.pat, cdef.guard, Literal(Constant(true)))
- def dupVirtMatch(zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], wrap: Block => Tree = identity) = {
- object dropMatchResAssign extends Transformer {
- // override val treeCopy = newStrictTreeCopier // will duplicate below
- override def transform(tree: Tree): Tree = tree match {
- // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing
- case Block(List(matchRes, ass@Assign(keepGoingLhs, falseLit)), zero) if keepGoingLhs.symbol eq keepGoing.symbol =>
- Block(List(ass), zero)
- case _ =>
- super.transform(tree)
- }
+ def defaultCase = CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false)))
+
+ val casesNoSynthCatchAll = dropSyntheticCatchAll(cases)
+
+ gen.mkUncheckedMatch(
+ if (casesNoSynthCatchAll exists treeInfo.isDefaultCase) Literal(Constant(true))
+ else substTree(wrap(Match(selector, (casesNoSynthCatchAll map transformCase) :+ defaultCase)).duplicate)
+ )
}
- val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList
- val idaBlock = wrap(Block(
- zero ::
- x ::
- /* drop matchRes def */
- keepGoing ::
- statsNoMatchRes,
- NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` by `!keepGoing`
- ))
- substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed
- }
- DefDef(m, (fun.body: @unchecked) match {
- case Match(selector, cases) =>
- dupMatch(selector, cases)
- case Block((vd: ValDef) :: Nil, Match(selector, cases)) => // can't factor this out using an extractor due to bugs in the old pattern matcher
- dupMatch(selector, cases, m => Block(List(vd), m))
- // virtpatmat -- TODO: find a better way to keep this in synch with the code generated by patmatvirtualizer
- case Apply(Apply(TypeApply(Select(tgt, nme.runOrElse), targs), args_scrut), args_pm) if opt.virtPatmat =>
+ override def caseVirtualizedMatch(orig: Tree, _match: Tree, targs: List[Tree], scrut: Tree, matcher: Tree): Tree = {
object noOne extends Transformer {
override val treeCopy = newStrictTreeCopier // must duplicate everything
- val one = tgt.tpe member newTermName("one")
+ val one = _match.tpe member newTermName("one")
override def transform(tree: Tree): Tree = tree match {
case Apply(fun, List(a)) if fun.symbol == one =>
// blow one's argument away since all we want to know is whether the match succeeds or not
@@ -367,15 +307,34 @@ abstract class UnCurry extends InfoTransform
super.transform(tree)
}
}
- substTree(Apply(Apply(TypeApply(Select(tgt.duplicate, tgt.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), args_scrut map (_.duplicate)), args_pm map (noOne.transform)))
- // for the optimized version of virtpatmat
- case Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _) if opt.virtPatmat =>
- dupVirtMatch(zero, x, matchRes, keepGoing, stats)
- case Block(outerStats, Block((zero: ValDef) :: (x: ValDef) :: (matchRes: ValDef) :: (keepGoing: ValDef) :: stats, _)) if opt.virtPatmat => // can't factor this out using an extractor due to bugs in the old pattern matcher
- dupVirtMatch(zero, x, matchRes, keepGoing, stats, m => Block(outerStats, m))
- // case other =>
- // treeBrowser browse other
- })
+ substTree(Apply(Apply(TypeApply(Select(_match.duplicate, _match.tpe.member(newTermName("isSuccess"))), targs map (_.duplicate)), List(scrut.duplicate)), List(noOne.transform(matcher))))
+ }
+
+ override def caseVirtualizedMatchOpt(orig: Tree, zero: ValDef, x: ValDef, matchRes: ValDef, keepGoing: ValDef, stats: List[Tree], epilogue: Tree, wrap: Tree => Tree) = {
+ object dropMatchResAssign extends Transformer {
+ // override val treeCopy = newStrictTreeCopier // will duplicate below
+ override def transform(tree: Tree): Tree = tree match {
+ // don't compute the result of the match -- remove the block for the RHS (emitted by pmgen.one), except for the assignment to keepGoing
+ case gen.VirtualCaseDef(assignKeepGoing, matchRes, zero) if assignKeepGoing.lhs.symbol eq keepGoing.symbol =>
+ Block(List(assignKeepGoing), zero)
+ case _ =>
+ super.transform(tree)
+ }
+ }
+ val statsNoMatchRes: List[Tree] = stats map (dropMatchResAssign.transform) toList
+ val idaBlock = wrap(Block(
+ zero ::
+ x ::
+ /* drop matchRes def */
+ keepGoing ::
+ statsNoMatchRes,
+ NOT(REF(keepGoing.symbol)) // replace `if (keepGoing) throw new MatchError(...) else matchRes` epilogue by `!keepGoing`
+ ))
+ substTree(idaBlock.duplicate) // duplicate on block as a whole to ensure valdefs are properly cloned and substed
+ }
+ }
+
+ DefDef(m, isDefinedAtTransformer(fun.body))
}
val members =
@@ -385,9 +344,7 @@ abstract class UnCurry extends InfoTransform
localTyper.typedPos(fun.pos) {
Block(
List(ClassDef(anonClass, NoMods, List(List()), List(List()), members, fun.pos)),
- Typed(
- New(TypeTree(anonClass.tpe), List(List())),
- TypeTree(fun.tpe)))
+ Typed(New(anonClass.tpe), TypeTree(fun.tpe)))
}
}
}
@@ -400,7 +357,7 @@ abstract class UnCurry extends InfoTransform
// when calling into scala varargs, make sure it's a sequence.
def arrayToSequence(tree: Tree, elemtp: Type) = {
- atPhase(phase.next) {
+ afterUncurry {
localTyper.typedPos(pos) {
val pt = arrayType(elemtp)
val adaptedTree = // might need to cast to Array[elemtp], as arrays are not covariant
@@ -424,7 +381,7 @@ abstract class UnCurry extends InfoTransform
else if (tp.bounds.hi ne tp) getManifest(tp.bounds.hi)
else localTyper.getManifestTree(tree, tp, false)
}
- atPhase(phase.next) {
+ afterUncurry {
localTyper.typedPos(pos) {
Apply(gen.mkAttributedSelect(tree, toArraySym),
List(getManifest(tree.tpe.baseType(TraversableClass).typeArgs.head)))
@@ -449,7 +406,7 @@ abstract class UnCurry extends InfoTransform
else arrayToSequence(mkArray, varargsElemType)
}
- atPhase(phase.next) {
+ afterUncurry {
if (isJava && isPrimitiveArray(suffix.tpe) && isArrayOfSymbol(fun.tpe.params.last.tpe, ObjectClass)) {
suffix = localTyper.typedPos(pos) {
gen.mkRuntimeCall(nme.toObjectArray, List(suffix))
@@ -482,19 +439,38 @@ abstract class UnCurry extends InfoTransform
}
}
- /** For removing calls to specially designated methods.
+ /** Called if a tree's symbol is elidable. If it's a DefDef,
+ * replace only the body/rhs with 0/false/()/null; otherwise replace
+ * the whole tree with it.
*/
- def elideIntoUnit(tree: Tree): Tree = Literal(Constant()) setPos tree.pos setType UnitClass.tpe
- def isElidable(tree: Tree) = {
- val sym = treeInfo.methPart(tree).symbol
- // XXX settings.noassertions.value temporarily retained to avoid
- // breakage until a reasonable interface is settled upon.
- sym != null && sym.elisionLevel.exists(x => x < settings.elidebelow.value || settings.noassertions.value) && {
- log("Eliding call from " + tree.symbol.owner + " to " + sym + " based on its elision threshold of " + sym.elisionLevel.get)
- true
+ private def replaceElidableTree(tree: Tree): Tree = {
+ tree match {
+ case DefDef(_,_,_,_,_,_) =>
+ deriveDefDef(tree)(rhs => Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe) setSymbol tree.symbol setType tree.tpe
+ case _ =>
+ gen.mkZero(tree.tpe) setType tree.tpe
}
}
+ private def isSelfSynchronized(ddef: DefDef) = ddef.rhs match {
+ case Apply(fn @ TypeApply(Select(sel, _), _), _) =>
+ fn.symbol == Object_synchronized && sel.symbol == ddef.symbol.enclClass && !ddef.symbol.enclClass.isTrait
+ case _ => false
+ }
+
+ /** If an eligible method is entirely wrapped in a call to synchronized
+ * locked on the same instance, remove the synchronized scaffolding and
+ * mark the method symbol SYNCHRONIZED for bytecode generation.
+ */
+ private def translateSynchronized(tree: Tree) = tree match {
+ case dd @ DefDef(_, _, _, _, _, Apply(fn, body :: Nil)) if isSelfSynchronized(dd) =>
+ log("Translating " + dd.symbol.defString + " into synchronized method")
+ dd.symbol setFlag SYNCHRONIZED
+ deriveDefDef(dd)(_ => body)
+ case _ => tree
+ }
+ def isNonLocalReturn(ret: Return) = ret.symbol != currentOwner.enclMethod || currentOwner.isLazy
+
// ------ The tree transformers --------------------------------------------------------
def mainTransform(tree: Tree): Tree = {
@@ -532,112 +508,103 @@ abstract class UnCurry extends InfoTransform
finally this.inConstructorFlag = saved
}
- if (isElidable(tree)) elideIntoUnit(tree)
- else tree match {
- case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
- if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd)
- withNeedLift(false) {
- if (tree.symbol.isClassConstructor) {
- atOwner(tree.symbol) {
- val rhs1 = (rhs: @unchecked) match {
- case Block(stats, expr) =>
- def transformInConstructor(stat: Tree) =
- withInConstructorFlag(INCONSTRUCTOR) { transform(stat) }
- val presupers = treeInfo.preSuperFields(stats) map transformInConstructor
- val rest = stats drop presupers.length
- val supercalls = rest take 1 map transformInConstructor
- val others = rest drop 1 map transform
- treeCopy.Block(rhs, presupers ::: supercalls ::: others, transform(expr))
+ val sym = tree.symbol
+ val result = (
+ // TODO - settings.noassertions.value temporarily retained to avoid
+ // breakage until a reasonable interface is settled upon.
+ if ((sym ne null) && (sym.elisionLevel.exists (_ < settings.elidebelow.value || settings.noassertions.value)))
+ replaceElidableTree(tree)
+ else translateSynchronized(tree) match {
+ case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ if (dd.symbol hasAnnotation VarargsClass) saveRepeatedParams(dd)
+
+ withNeedLift(false) {
+ if (dd.symbol.isClassConstructor) {
+ atOwner(sym) {
+ val rhs1 = (rhs: @unchecked) match {
+ case Block(stats, expr) =>
+ def transformInConstructor(stat: Tree) =
+ withInConstructorFlag(INCONSTRUCTOR) { transform(stat) }
+ val presupers = treeInfo.preSuperFields(stats) map transformInConstructor
+ val rest = stats drop presupers.length
+ val supercalls = rest take 1 map transformInConstructor
+ val others = rest drop 1 map transform
+ treeCopy.Block(rhs, presupers ::: supercalls ::: others, transform(expr))
+ }
+ treeCopy.DefDef(
+ dd, mods, name, transformTypeDefs(tparams),
+ transformValDefss(vparamss), transform(tpt), rhs1)
}
- treeCopy.DefDef(
- tree, mods, name, transformTypeDefs(tparams),
- transformValDefss(vparamss), transform(tpt), rhs1)
+ } else {
+ super.transform(dd)
}
- } else {
- super.transform(tree)
}
- }
- case ValDef(_, _, _, rhs) =>
- val sym = tree.symbol
- if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit)
- // a local variable that is mutable and free somewhere later should be lifted
- // as lambda lifting (coming later) will wrap 'rhs' in an Ref object.
- if (!sym.owner.isSourceMethod)
+ case ValDef(_, _, _, rhs) =>
+ if (sym eq NoSymbol) throw new IllegalStateException("Encountered Valdef without symbol: "+ tree + " in "+ unit)
+ // a local variable that is mutable and free somewhere later should be lifted
+ // as lambda lifting (coming later) will wrap 'rhs' in an Ref object.
+ if (!sym.owner.isSourceMethod)
+ withNeedLift(true) { super.transform(tree) }
+ else
+ super.transform(tree)
+ case UnApply(fn, args) =>
+ val fn1 = withInPattern(false)(transform(fn))
+ val args1 = transformTrees(fn.symbol.name match {
+ case nme.unapply => args
+ case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeListFromReturnTypeSeq(fn.tpe))
+ case _ => sys.error("internal error: UnApply node has wrong symbol")
+ })
+ treeCopy.UnApply(tree, fn1, args1)
+
+ case Apply(fn, args) =>
+ if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head))
+ transform(treeCopy.Apply(tree, fn, List(liftTree(args.head))))
+ else
+ withNeedLift(true) {
+ val formals = fn.tpe.paramTypes
+ treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals)))
+ }
+
+ case Assign(Select(_, _), _) =>
withNeedLift(true) { super.transform(tree) }
- else
- super.transform(tree)
-/*
- case Apply(Select(Block(List(), Function(vparams, body)), nme.apply), args) =>
- // perform beta-reduction; this helps keep view applications small
- println("beta-reduce1: "+tree)
- withNeedLift(true) {
- mainTransform(new TreeSubstituter(vparams map (_.symbol), args).transform(body))
- }
- case Apply(Select(Function(vparams, body), nme.apply), args) =>
-// if (List.forall2(vparams, args)((vparam, arg) => treeInfo.isAffineIn(body) ||
-// treeInfo.isExprSafeToInline(arg))) =>
- // perform beta-reduction; this helps keep view applications small
- println("beta-reduce2: "+tree)
- withNeedLift(true) {
- mainTransform(new TreeSubstituter(vparams map (_.symbol), args).transform(body))
- }
-*/
- case UnApply(fn, args) =>
- val fn1 = withInPattern(false)(transform(fn))
- val args1 = transformTrees(fn.symbol.name match {
- case nme.unapply => args
- case nme.unapplySeq => transformArgs(tree.pos, fn.symbol, args, analyzer.unapplyTypeListFromReturnTypeSeq(fn.tpe))
- case _ => sys.error("internal error: UnApply node has wrong symbol")
- })
- treeCopy.UnApply(tree, fn1, args1)
-
- case Apply(fn, args) =>
- if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head))
- transform(treeCopy.Apply(tree, fn, List(liftTree(args.head))))
- else
- withNeedLift(true) {
- val formals = fn.tpe.paramTypes
- treeCopy.Apply(tree, transform(fn), transformTrees(transformArgs(tree.pos, fn.symbol, args, formals)))
- }
+ case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) =>
+ withNeedLift(true) { super.transform(tree) }
- case Assign(Select(_, _), _) =>
- withNeedLift(true) { super.transform(tree) }
+ case ret @ Return(_) if (isNonLocalReturn(ret)) =>
+ withNeedLift(true) { super.transform(ret) }
- case Assign(lhs, _) if lhs.symbol.owner != currentMethod || lhs.symbol.hasFlag(LAZY | ACCESSOR) =>
- withNeedLift(true) { super.transform(tree) }
+ case Try(block, catches, finalizer) =>
+ if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree))
+ else super.transform(tree)
- case Try(block, catches, finalizer) =>
- if (needTryLift || shouldBeLiftedAnyway(tree)) transform(liftTree(tree))
- else super.transform(tree)
+ case CaseDef(pat, guard, body) =>
+ val pat1 = withInPattern(true)(transform(pat))
+ treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))
- case CaseDef(pat, guard, body) =>
- val pat1 = withInPattern(true)(transform(pat))
- treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))
+ case fun @ Function(_, _) =>
+ mainTransform(transformFunction(fun))
- case fun @ Function(_, _) =>
- mainTransform(transformFunction(fun))
+ case Template(_, _, _) =>
+ withInConstructorFlag(0) { super.transform(tree) }
- case Template(_, _, _) =>
- withInConstructorFlag(0) { super.transform(tree) }
-
- case _ =>
- val tree1 = super.transform(tree)
- if (isByNameRef(tree1)) {
- val tree2 = tree1 setType functionType(Nil, tree1.tpe)
- return {
- if (noApply contains tree2) tree2
- else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil))
+ case _ =>
+ val tree1 = super.transform(tree)
+ if (isByNameRef(tree1)) {
+ val tree2 = tree1 setType functionType(Nil, tree1.tpe)
+ return {
+ if (noApply contains tree2) tree2
+ else localTyper.typedPos(tree1.pos)(Apply(Select(tree2, nme.apply), Nil))
+ }
}
- }
- tree1
- }
- } setType {
- assert(tree.tpe != null, "tpe is null at " + tree.pos + " for " + tree.summaryString + " / " + tree)
- uncurryTreeType(tree.tpe)
+ tree1
+ }
+ )
+ assert(result.tpe != null, result + " tpe is null")
+ result setType uncurryTreeType(result.tpe)
}
- def postTransform(tree: Tree): Tree = atPhase(phase.next) {
+ def postTransform(tree: Tree): Tree = afterUncurry {
def applyUnary(): Tree = {
// TODO_NMT: verify that the inner tree of a type-apply also gets parens if the
// whole tree is a polymorphic nullary method application
@@ -663,23 +630,24 @@ abstract class UnCurry extends InfoTransform
* In particular, this case will add:
* - synthetic Java varargs forwarders for repeated parameters
*/
- case Template(parents, self, body) =>
+ case Template(_, _, _) =>
localTyper = typer.atOwner(tree, currentClass)
- val tmpl = if (!forMSIL || forMSIL) {
- treeCopy.Template(tree, parents, self, transformTrees(newMembers.toList) ::: body)
- } else super.transform(tree).asInstanceOf[Template]
- newMembers.clear
- tmpl
- case dd @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
- val rhs1 = nonLocalReturnKeys.get(tree.symbol) match {
- case None => rhs
- case Some(k) => atPos(rhs.pos)(nonLocalReturnTry(rhs, k, tree.symbol))
- }
- val flatdd = treeCopy.DefDef(tree, mods, name, tparams, List(vparamss.flatten), tpt, rhs1)
- if (dd.symbol hasAnnotation VarargsClass) addJavaVarargsForwarders(dd, flatdd, tree)
- flatdd
+ try deriveTemplate(tree)(transformTrees(newMembers.toList) ::: _)
+ finally newMembers.clear()
+
+ case dd @ DefDef(_, _, _, vparamss0, _, rhs0) =>
+ val flatdd = copyDefDef(dd)(
+ vparamss = List(vparamss0.flatten),
+ rhs = nonLocalReturnKeys get dd.symbol match {
+ case Some(k) => atPos(rhs0.pos)(nonLocalReturnTry(rhs0, k, dd.symbol))
+ case None => rhs0
+ }
+ )
+ addJavaVarargsForwarders(dd, flatdd)
+
case Try(body, catches, finalizer) =>
- if (catches forall treeInfo.isCatchCase) tree
+ if (opt.virtPatmat) { if(catches exists (cd => !treeInfo.isCatchCase(cd))) debugwarn("VPM BUG! illegal try/catch "+ catches); tree }
+ else if (catches forall treeInfo.isCatchCase) tree
else {
val exname = unit.freshTermName("ex$")
val cases =
@@ -701,7 +669,7 @@ abstract class UnCurry extends InfoTransform
}
debuglog("rewrote try: " + catches + " ==> " + catchall);
val catches1 = localTyper.typedCases(
- tree, List(catchall), ThrowableClass.tpe, WildcardType)
+ List(catchall), ThrowableClass.tpe, WildcardType)
treeCopy.Try(tree, body, catches1, finalizer)
}
case Apply(Apply(fn, args), args1) =>
@@ -711,9 +679,9 @@ abstract class UnCurry extends InfoTransform
applyUnary()
case Select(_, _) | TypeApply(_, _) =>
applyUnary()
- case Return(expr) if (tree.symbol != currentOwner.enclMethod || currentOwner.isLazy) =>
- debuglog("non local return in "+tree.symbol+" from "+currentOwner.enclMethod)
- atPos(tree.pos)(nonLocalReturnThrow(expr, tree.symbol))
+ case ret @ Return(expr) if (isNonLocalReturn(ret)) =>
+ debuglog("non local return in "+ret.symbol+" from "+currentOwner.enclMethod)
+ atPos(ret.pos)(nonLocalReturnThrow(expr, ret.symbol))
case TypeTree() =>
tree
case _ =>
@@ -739,9 +707,9 @@ abstract class UnCurry extends InfoTransform
* It looks for the method in the `repeatedParams` map, and generates a Java-style
* varargs forwarder. It then adds the forwarder to the `newMembers` sequence.
*/
- private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef, tree: Tree): Unit = {
- if (!repeatedParams.contains(dd.symbol))
- return
+ private def addJavaVarargsForwarders(dd: DefDef, flatdd: DefDef): DefDef = {
+ if (!dd.symbol.hasAnnotation(VarargsClass) || !repeatedParams.contains(dd.symbol))
+ return flatdd
def toSeqType(tp: Type): Type = {
val arg = elementType(ArrayClass, tp)
@@ -762,7 +730,7 @@ abstract class UnCurry extends InfoTransform
val reps = repeatedParams(dd.symbol)
val rpsymbols = reps.map(_.symbol).toSet
- val theTyper = typer.atOwner(tree, currentClass)
+ val theTyper = typer.atOwner(dd, currentClass)
val flatparams = flatdd.vparamss.head
// create the type
@@ -814,10 +782,11 @@ abstract class UnCurry extends InfoTransform
case None =>
// enter symbol into scope
currentClass.info.decls enter forwsym
-
// add the method to `newMembers`
newMembers += forwtree
}
+
+ flatdd
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 68bc80ffc4..b7a22c6ac1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -31,14 +31,14 @@ trait ContextErrors {
case class NormalTypeError(underlyingTree: Tree, errMsg: String, kind: ErrorKind = ErrorKinds.Normal)
extends AbsTypeError {
-
+
def errPos:Position = underlyingTree.pos
override def toString() = "[Type error at:" + underlyingTree.pos + "] " + errMsg
}
case class SymbolTypeError(underlyingSym: Symbol, errMsg: String, kind: ErrorKind = ErrorKinds.Normal)
extends AbsTypeError {
-
+
def errPos = underlyingSym.pos
}
@@ -76,7 +76,7 @@ trait ContextErrors {
}
def issueTypeError(err: AbsTypeError)(implicit context: Context) { context.issue(err) }
-
+
def typeErrorMsg(found: Type, req: Type, possiblyMissingArgs: Boolean) = {
def missingArgsMsg = if (possiblyMissingArgs) "\n possible cause: missing arguments for method or constructor" else ""
"type mismatch" + foundReqMsg(found, req) + missingArgsMsg
@@ -143,12 +143,12 @@ trait ContextErrors {
found
}
assert(!found.isErroneous && !req.isErroneous, (found, req))
-
+
issueNormalTypeError(tree, withAddendum(tree.pos)(typeErrorMsg(found, req, infer.isPossiblyMissingArgs(found, req))) )
if (settings.explaintypes.value)
explainTypes(found, req)
}
-
+
def WithFilterError(tree: Tree, ex: AbsTypeError) = {
issueTypeError(ex)
setError(tree)
@@ -177,13 +177,13 @@ trait ContextErrors {
val calcSimilar = (
name.length > 2 && (
startingIdentCx.reportErrors
- || startingIdentCx.enclClassOrMethod.reportErrors
+ || startingIdentCx.enclClassOrMethod.reportErrors
)
)
- // avoid calculating if we're in "silent" mode.
- // name length check to limit unhelpful suggestions for e.g. "x" and "b1"
+ // avoid calculating if we're in "silent" mode.
+ // name length check to limit unhelpful suggestions for e.g. "x" and "b1"
val similar = {
- if (!calcSimilar) ""
+ if (!calcSimilar) ""
else {
val allowed = (
startingIdentCx.enclosingContextChain
@@ -624,11 +624,21 @@ trait ContextErrors {
setError(tree)
}
- // checkNoDoubleDefs...
- def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) =
- issueSymbolTypeError(sym0, sym1+" is defined twice"+
- {if(!settings.debug.value) "" else " in "+context0.unit}+
- {if (sym0.isMacro && sym1.isMacro) " \n(note that macros cannot be overloaded)" else ""})
+ // checkNoDoubleDefs...
+ // @PP: I hacked the filename in (context0.unit) to work around SI-4893. It would be
+ // much better if every symbol could offer some idea of where it came from, else
+ // the obviously untrue claim that something has been defined twice can only frustrate.
+ // There's no direct test because partest doesn't work, but to reproduce, separately
+ // compile the next two lines:
+ // package object foo { val x: Class[_] = null }
+ // package foo
+ def DefDefinedTwiceError(sym0: Symbol, sym1: Symbol) = {
+ val isBug = sym0.isAbstractType && sym1.isAbstractType && (sym0.name startsWith "_$")
+ issueSymbolTypeError(sym0, sym1+" is defined twice in " + context0.unit
+ + ( if (sym0.isMacro && sym1.isMacro) "\n(note that macros cannot be overloaded)" else "" )
+ + ( if (isBug) "\n(this error is likely due to a bug in the scala compiler involving wildcards in package objects)" else "" )
+ )
+ }
// cyclic errors
def CyclicAliasingOrSubtypingError(errPos: Position, sym0: Symbol) =
@@ -636,11 +646,6 @@ trait ContextErrors {
def CyclicReferenceError(errPos: Position, lockedSym: Symbol) =
issueTypeError(PosAndMsgTypeError(errPos, "illegal cyclic reference involving " + lockedSym))
-
- def MacroExpandError(tree: Tree, t: Any) = {
- issueNormalTypeError(tree, "macros must return a compiler-specific tree; returned class is: " + t.getClass)
- setError(tree)
- }
}
}
@@ -667,7 +672,7 @@ trait ContextErrors {
type ErrorType = Value
val WrongNumber, NoParams, ArgsDoNotConform = Value
}
-
+
private def ambiguousErrorMsgPos(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String) =
if (sym1.hasDefaultFlag && sym2.hasDefaultFlag && sym1.enclClass == sym2.enclClass) {
val methodName = nme.defaultGetterToMethod(sym1.name)
@@ -714,9 +719,15 @@ trait ContextErrors {
setError(tree)
}
- def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) =
+ def NoBestMethodAlternativeError(tree: Tree, argtpes: List[Type], pt: Type) = {
issueNormalTypeError(tree,
applyErrorMsg(tree, " cannot be applied to ", argtpes, pt))
+ // since inferMethodAlternative modifies the state of the tree
+ // we have to set the type of tree to ErrorType only in the very last
+ // fallback action that is done in the inference (tracking it manually is error prone).
+ // This avoids entering infinite loop in doTypeApply.
+ if (implicitly[Context].reportErrors) setError(tree)
+ }
def AmbiguousMethodAlternativeError(tree: Tree, pre: Type, best: Symbol,
firstCompeting: Symbol, argtpes: List[Type], pt: Type) = {
@@ -724,6 +735,8 @@ trait ContextErrors {
"argument types " + argtpes.mkString("(", ",", ")") +
(if (pt == WildcardType) "" else " and expected result type " + pt)
val (pos, msg) = ambiguousErrorMsgPos(tree.pos, pre, best, firstCompeting, msg0)
+ // discover last attempt in a similar way as for NoBestMethodAlternativeError
+ if (implicitly[Context].ambiguousErrors) setError(tree)
issueAmbiguousTypeError(pre, best, firstCompeting, AmbiguousTypeError(tree, pos, msg))
}
@@ -830,14 +843,14 @@ trait ContextErrors {
object NamerErrorGen {
implicit val context0 = context
-
+
object SymValidateErrors extends Enumeration {
val ImplicitConstr, ImplicitNotTerm, ImplicitTopObject,
OverrideClass, SealedNonClass, AbstractNonClass,
OverrideConstr, AbstractOverride, LazyAndEarlyInit,
ByNameParameter, AbstractVar = Value
}
-
+
object DuplicatesErrorKinds extends Enumeration {
val RenamedTwice, AppearsTwice = Value
}
@@ -845,7 +858,7 @@ trait ContextErrors {
import SymValidateErrors._
import DuplicatesErrorKinds._
import symtab.Flags
-
+
def TypeSigError(tree: Tree, ex: TypeError) = {
ex match {
case CyclicReference(sym, info: TypeCompleter) =>
@@ -854,7 +867,7 @@ trait ContextErrors {
context0.issue(TypeErrorWithUnderlyingTree(tree, ex))
}
}
-
+
def GetterDefinedTwiceError(getter: Symbol) =
issueSymbolTypeError(getter, getter+" is defined twice")
@@ -897,37 +910,37 @@ trait ContextErrors {
val msg = errKind match {
case ImplicitConstr =>
"`implicit' modifier not allowed for constructors"
-
+
case ImplicitNotTerm =>
"`implicit' modifier can be used only for values, variables and methods"
-
+
case ImplicitTopObject =>
"`implicit' modifier cannot be used for top-level objects"
-
+
case OverrideClass =>
"`override' modifier not allowed for classes"
-
+
case SealedNonClass =>
"`sealed' modifier can be used only for classes"
-
+
case AbstractNonClass =>
"`abstract' modifier can be used only for classes; it should be omitted for abstract members"
-
+
case OverrideConstr =>
"`override' modifier not allowed for constructors"
-
+
case AbstractOverride =>
"`abstract override' modifier only allowed for members of traits"
-
+
case LazyAndEarlyInit =>
"`lazy' definitions may not be initialized early"
-
+
case ByNameParameter =>
"pass-by-name arguments not allowed for case class parameters"
-
+
case AbstractVar =>
"only classes can have declared but undefined members" + abstractVarMessage(sym)
-
+
}
issueSymbolTypeError(sym, msg)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index b5afd681d2..a1ba8a2982 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -21,7 +21,7 @@ trait Contexts { self: Analyzer =>
outer = this
enclClass = this
enclMethod = this
-
+
override def nextEnclosing(p: Context => Boolean): Context = this
override def enclosingContextChain: List[Context] = Nil
override def implicitss: List[List[ImplicitInfo]] = Nil
@@ -128,6 +128,8 @@ trait Contexts { self: Analyzer =>
var typingIndentLevel: Int = 0
def typingIndent = " " * typingIndentLevel
+ var buffer: Set[AbsTypeError] = _
+
def enclClassOrMethod: Context =
if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this
else outer.enclClassOrMethod
@@ -145,7 +147,6 @@ trait Contexts { self: Analyzer =>
}
private[this] var mode = 0
- private[this] val buffer = LinkedHashSet[AbsTypeError]()
def errBuffer = buffer
def hasErrors = buffer.nonEmpty
@@ -160,7 +161,7 @@ trait Contexts { self: Analyzer =>
def setReportErrors() = mode = (ReportErrors | AmbiguousErrors)
def setBufferErrors() = {
- assert(bufferErrors || !hasErrors, "When entering the buffer state, context has to be clean. Current buffer: " + buffer)
+ //assert(bufferErrors || !hasErrors, "When entering the buffer state, context has to be clean. Current buffer: " + buffer)
mode = BufferErrors
}
def setThrowErrors() = mode &= (~AllMask)
@@ -177,7 +178,7 @@ trait Contexts { self: Analyzer =>
buffer.clear()
current
}
-
+
def logError(err: AbsTypeError) = buffer += err
def withImplicitsDisabled[T](op: => T): T = {
@@ -225,6 +226,7 @@ trait Contexts { self: Analyzer =>
c.checking = this.checking
c.retyping = this.retyping
c.openImplicits = this.openImplicits
+ c.buffer = if (this.buffer == null) LinkedHashSet[AbsTypeError]() else this.buffer // need to initialize
registerContext(c.asInstanceOf[analyzer.Context])
debuglog("[context] ++ " + c.unit + " / " + tree.summaryString)
c
@@ -237,7 +239,7 @@ trait Contexts { self: Analyzer =>
c.implicitsEnabled = true
c
}
-
+
def makeNewImport(sym: Symbol): Context =
makeNewImport(gen.mkWildcardImport(sym))
@@ -265,6 +267,7 @@ trait Contexts { self: Analyzer =>
val c = make(newtree)
c.setBufferErrors()
c.setAmbiguousErrors(reportAmbiguousErrors)
+ c.buffer = new LinkedHashSet[AbsTypeError]()
c
}
@@ -308,12 +311,14 @@ trait Contexts { self: Analyzer =>
unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg)
def issue(err: AbsTypeError) {
+ debugwarn("issue error: " + err.errMsg)
if (reportErrors) unitError(err.errPos, addDiagString(err.errMsg))
else if (bufferErrors) { buffer += err }
else throw new TypeError(err.errPos, err.errMsg)
}
def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) {
+ debugwarn("issue ambiguous error: " + err.errMsg)
if (ambiguousErrors) {
if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous)
unitError(err.errPos, err.errMsg)
@@ -322,6 +327,7 @@ trait Contexts { self: Analyzer =>
}
def issueAmbiguousError(err: AbsTypeError) {
+ debugwarn("issue ambiguous error: " + err.errMsg)
if (ambiguousErrors)
unitError(err.errPos, addDiagString(err.errMsg))
else if (bufferErrors) { buffer += err }
diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
index 3536608efd..29831c8469 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
@@ -7,7 +7,6 @@ package scala.tools.nsc
package typechecker
import scala.tools.nsc.symtab.Flags
-
import scala.collection.{ mutable, immutable }
/** Duplicate trees and re-type check them, taking care to replace
@@ -18,6 +17,7 @@ import scala.collection.{ mutable, immutable }
*/
abstract class Duplicators extends Analyzer {
import global._
+ import definitions.{ AnyRefClass, AnyValClass }
def retyped(context: Context, tree: Tree): Tree = {
resetClassOwners
@@ -36,7 +36,7 @@ abstract class Duplicators extends Analyzer {
} else resetClassOwners
envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList)
- log("retyped with env: " + env)
+ debuglog("retyped with env: " + env)
(new BodyDuplicator(context)).typed(tree)
}
@@ -82,14 +82,14 @@ abstract class Duplicators extends Analyzer {
val sym1 = context.scope.lookup(sym.name)
// assert(sym1 ne NoSymbol, tpe)
if ((sym1 ne NoSymbol) && (sym1 ne sym)) {
- log("fixing " + sym + " -> " + sym1)
+ debuglog("fixing " + sym + " -> " + sym1)
typeRef(NoPrefix, sym1, mapOverArgs(args, sym1.typeParams))
} else super.mapOver(tpe)
case TypeRef(pre, sym, args) =>
val newsym = updateSym(sym)
if (newsym ne sym) {
- log("fixing " + sym + " -> " + newsym)
+ debuglog("fixing " + sym + " -> " + newsym)
typeRef(mapOver(pre), newsym, mapOverArgs(args, newsym.typeParams))
} else
super.mapOver(tpe)
@@ -97,7 +97,7 @@ abstract class Duplicators extends Analyzer {
case SingleType(pre, sym) =>
val sym1 = updateSym(sym)
if (sym1 ne sym) {
- log("fixing " + sym + " -> " + sym1)
+ debuglog("fixing " + sym + " -> " + sym1)
singleType(mapOver(pre), sym1)
} else
super.mapOver(tpe)
@@ -105,7 +105,7 @@ abstract class Duplicators extends Analyzer {
case ThisType(sym) =>
val sym1 = updateSym(sym)
if (sym1 ne sym) {
- log("fixing " + sym + " -> " + sym1)
+ debuglog("fixing " + sym + " -> " + sym1)
ThisType(sym1)
} else
super.mapOver(tpe)
@@ -136,26 +136,26 @@ abstract class Duplicators extends Analyzer {
private def invalidate(tree: Tree) {
debuglog("attempting to invalidate " + tree.symbol + ", owner - " + (if (tree.symbol ne null) tree.symbol.owner else "<NULL>"))
if (tree.isDef && tree.symbol != NoSymbol) {
- log("invalid " + tree.symbol)
+ debuglog("invalid " + tree.symbol)
invalidSyms(tree.symbol) = tree
tree match {
case ldef @ LabelDef(name, params, rhs) =>
- log("LabelDef " + name + " sym.info: " + ldef.symbol.info)
+ debuglog("LabelDef " + name + " sym.info: " + ldef.symbol.info)
invalidSyms(ldef.symbol) = ldef
// breakIf(true, this, ldef, context)
val newsym = ldef.symbol.cloneSymbol(context.owner)
newsym.setInfo(fixType(ldef.symbol.info))
ldef.symbol = newsym
- log("newsym: " + newsym + " info: " + newsym.info)
+ debuglog("newsym: " + newsym + " info: " + newsym.info)
case vdef @ ValDef(mods, name, _, rhs) if mods.hasFlag(Flags.LAZY) =>
- log("ValDef " + name + " sym.info: " + vdef.symbol.info)
+ debuglog("ValDef " + name + " sym.info: " + vdef.symbol.info)
invalidSyms(vdef.symbol) = vdef
val newsym = vdef.symbol.cloneSymbol(context.owner)
newsym.setInfo(fixType(vdef.symbol.info))
vdef.symbol = newsym
- log("newsym: " + newsym + " info: " + newsym.info)
+ debuglog("newsym: " + newsym + " info: " + newsym.info)
case DefDef(_, name, tparams, vparamss, _, rhs) =>
// invalidate parameters
@@ -182,7 +182,7 @@ abstract class Duplicators extends Analyzer {
}
ddef.symbol = NoSymbol
enterSym(context, ddef)
- log("remapping this of " + oldClassOwner + " to " + newClassOwner)
+ debuglog("remapping this of " + oldClassOwner + " to " + newClassOwner)
typed(ddef)
}
@@ -228,7 +228,7 @@ abstract class Duplicators extends Analyzer {
ttree
case Block(stats, res) =>
- log("invalidating block")
+ debuglog("invalidating block")
invalidate(stats)
invalidate(res)
tree.tpe = null
@@ -256,7 +256,7 @@ abstract class Duplicators extends Analyzer {
case ldef @ LabelDef(name, params, rhs) =>
// log("label def: " + ldef)
ldef.tpe = null
- val params1 = params map { p => Ident(updateSym(p.symbol)) }
+ val params1 = params map (p => Ident(updateSym(p.symbol)))
super.typed(treeCopy.LabelDef(tree, name, params1, rhs), mode, pt)
case Bind(name, _) =>
@@ -266,13 +266,13 @@ abstract class Duplicators extends Analyzer {
super.typed(tree, mode, pt)
case Ident(_) if tree.symbol.isLabel =>
- log("Ident to labeldef " + tree + " switched to ")
+ debuglog("Ident to labeldef " + tree + " switched to ")
tree.symbol = updateSym(tree.symbol)
tree.tpe = null
super.typed(tree, mode, pt)
case Ident(_) if (origtreesym ne null) && origtreesym.isLazy =>
- log("Ident to a lazy val " + tree + ", " + tree.symbol + " updated to " + origtreesym)
+ debuglog("Ident to a lazy val " + tree + ", " + tree.symbol + " updated to " + origtreesym)
tree.symbol = updateSym(origtreesym)
tree.tpe = null
super.typed(tree, mode, pt)
@@ -308,17 +308,26 @@ abstract class Duplicators extends Analyzer {
super.typed(atPos(tree.pos)(tree1))
*/
case Match(scrut, cases) =>
- val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType)
+ val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType)
val scrutTpe = scrut1.tpe.widen
- val cases1 = if (scrutTpe.isFinalType) cases filter {
- case CaseDef(Bind(_, pat @ Typed(_, tpt)), EmptyTree, body) =>
- // the typed pattern is not incompatible with the scrutinee type
- scrutTpe.matchesPattern(fixType(tpt.tpe))
- case CaseDef(Typed(_, tpt), EmptyTree, body) =>
- // the typed pattern is not incompatible with the scrutinee type
- scrutTpe.matchesPattern(fixType(tpt.tpe))
- case _ => true
- } else cases
+ val cases1 = {
+ if (scrutTpe.isFinalType) cases filter {
+ case CaseDef(Bind(_, pat @ Typed(_, tpt)), EmptyTree, body) =>
+ // the typed pattern is not incompatible with the scrutinee type
+ scrutTpe matchesPattern fixType(tpt.tpe)
+ case CaseDef(Typed(_, tpt), EmptyTree, body) =>
+ // the typed pattern is not incompatible with the scrutinee type
+ scrutTpe matchesPattern fixType(tpt.tpe)
+ case _ => true
+ }
+ // Without this, AnyRef specializations crash on patterns like
+ // case _: Boolean => ...
+ // Not at all sure this is safe.
+ else if (scrutTpe <:< AnyRefClass.tpe)
+ cases filterNot (_.pat.tpe <:< AnyValClass.tpe)
+ else
+ cases
+ }
super.typed(atPos(tree.pos)(Match(scrut, cases1)), mode, pt)
@@ -327,7 +336,7 @@ abstract class Duplicators extends Analyzer {
tree
case _ =>
- log("default: " + tree)
+ debuglog("Duplicators default case: " + tree.summaryString)
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/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 036e7fc750..0ddacf7d36 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -215,7 +215,7 @@ trait Implicits {
object HasMethodMatching {
val dummyMethod = NoSymbol.newTermSymbol(newTermName("typer$dummy"))
def templateArgType(argtpe: Type) = new BoundedWildcardType(TypeBounds.lower(argtpe))
-
+
def apply(name: Name, argtpes: List[Type], restpe: Type): Type = {
val mtpe = MethodType(dummyMethod.newSyntheticValueParams(argtpes map templateArgType), restpe)
memberWildcardType(name, mtpe)
@@ -571,7 +571,7 @@ trait Implicits {
else {
val tvars = undetParams map freshVar
def ptInstantiated = pt.instantiateTypeParams(undetParams, tvars)
-
+
printInference("[search] considering %s (pt contains %s) trying %s against pt=%s".format(
if (undetParams.isEmpty) "no tparams" else undetParams.map(_.name).mkString(", "),
typeVarsInType(ptInstantiated) filterNot (_.isGround) match { case Nil => "no tvars" ; case tvs => tvs.mkString(", ") },
@@ -594,7 +594,7 @@ trait Implicits {
// we must be conservative in leaving type params in undetparams
// prototype == WildcardType: want to remove all inferred Nothings
val AdjustedTypeArgs(okParams, okArgs) = adjustTypeArgs(undetParams, tvars, targs)
-
+
val subst: TreeTypeSubstituter =
if (okParams.isEmpty) EmptyTreeTypeSubstituter
else {
@@ -621,7 +621,7 @@ trait Implicits {
case Apply(TypeApply(fun, args), _) => typedTypeApply(itree2, EXPRmode, fun, args) // t2421c
case t => t
}
-
+
if (context.hasErrors)
fail("typing TypeApply reported errors for the implicit tree")
else {
@@ -780,13 +780,13 @@ trait Implicits {
val newPending = undoLog undo {
is filterNot (alt => alt == i || {
try improves(i, alt)
- catch {
- case e: CyclicReference =>
+ catch {
+ case e: CyclicReference =>
if (printInfers) {
println(i+" discarded because cyclic reference occurred")
e.printStackTrace()
}
- true
+ true
}
})
}
@@ -1163,7 +1163,7 @@ trait Implicits {
/* !!! the following is almost right, but we have to splice nested manifest
* !!! types into this type. This requires a substantial extension of
* !!! reifiers.
- val reifier = new liftcode.Reifier()
+ val reifier = new Reifier()
val rtree = reifier.reifyTopLevel(tp1)
manifestFactoryCall("apply", tp, rtree)
*/
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index eac657da19..c0c801910c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -210,9 +210,9 @@ trait Infer {
def getContext = context
def issue(err: AbsTypeError): Unit = context.issue(err)
-
- def isPossiblyMissingArgs(found: Type, req: Type) = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req)
-
+
+ def isPossiblyMissingArgs(found: Type, req: Type) = (found.resultApprox ne found) && isWeaklyCompatible(found.resultApprox, req)
+
def explainTypes(tp1: Type, tp2: Type) =
withDisambiguation(List(), tp1, tp2)(global.explainTypes(tp1, tp2))
@@ -228,9 +228,9 @@ trait Infer {
if (sym.isError) {
tree setSymbol sym setType ErrorType
} else {
- val topClass = context.owner.toplevelClass
+ val topClass = context.owner.enclosingTopLevelClass
if (context.unit.exists)
- context.unit.depends += sym.toplevelClass
+ context.unit.depends += sym.enclosingTopLevelClass
var sym1 = sym filter (alt => context.isAccessible(alt, pre, site.isInstanceOf[Super]))
// Console.println("check acc " + (sym, sym1) + ":" + (sym.tpe, sym1.tpe) + " from " + pre);//DEBUG
@@ -465,7 +465,7 @@ trait Infer {
*/
def adjustTypeArgs(tparams: List[Symbol], tvars: List[TypeVar], targs: List[Type], restpe: Type = WildcardType): AdjustedTypeArgs.Result = {
val buf = AdjustedTypeArgs.Result.newBuilder[Symbol, Option[Type]]
-
+
foreach3(tparams, tvars, targs) { (tparam, tvar, targ) =>
val retract = (
targ.typeSymbol == NothingClass // only retract Nothings
@@ -640,13 +640,7 @@ trait Infer {
case ExistentialType(tparams, qtpe) =>
isApplicable(undetparams, qtpe, argtpes0, pt)
case MethodType(params, _) =>
- val formals0 = params map { param =>
- param.tpe match {
- case TypeRef(_, sym, List(tpe)) if sym isNonBottomSubClass CodeClass => tpe
- case tpe => tpe
- }
- }
- val formals = formalTypes(formals0, argtpes0.length)
+ val formals = formalTypes(params map { _.tpe }, argtpes0.length)
def tryTupleApply: Boolean = {
// if 1 formal, 1 argtpe (a tuple), otherwise unmodified argtpes0
@@ -1057,49 +1051,74 @@ trait Infer {
* @param pt the expected result type of the instance
*/
def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) {
- val pt = widen(pt0)
- //println("infer constr inst "+tree+"/"+undetparams+"/"+pt0)
- var restpe = tree.tpe.finalResultType
- var tvars = undetparams map freshVar
+ val pt = widen(pt0)
+ val ptparams = freeTypeParamsOfTerms.collect(pt)
+ val ctorTp = tree.tpe
+ val resTp = ctorTp.finalResultType
- /** Compute type arguments for undetermined params and substitute them in given tree.
+ debuglog("infer constr inst "+ tree +"/"+ undetparams +"/ pt= "+ pt +" pt0= "+ pt0 +" resTp: "+ resTp)
+
+ /** Compute type arguments for undetermined params
*/
- def computeArgs =
- try {
- val targs = solvedTypes(tvars, undetparams, undetparams map varianceInType(restpe),
- true, lubDepth(List(restpe, pt)))
-// checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ")
-// no checkBounds here. If we enable it, test bug602 fails.
- new TreeTypeSubstituter(undetparams, targs).traverse(tree)
- } catch ifNoInstance{ msg =>
- NoConstructorInstanceError(tree, restpe, pt, msg)
+ def inferFor(pt: Type): Option[List[Type]] = {
+ val tvars = undetparams map freshVar
+ val resTpV = resTp.instantiateTypeParams(undetparams, tvars)
+
+ if (resTpV <:< pt) {
+ 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 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.
+ // TODO: reinstate checkBounds, return params that fail to meet their bounds to undetparams
+ Some(targs)
+ } catch ifNoInstance { msg =>
+ debuglog("NO INST "+ (tvars, tvars map (_.constr)))
+ NoConstructorInstanceError(tree, resTp, pt, msg)
+ None
+ }
+ } else {
+ debuglog("not a subtype: "+ resTpV +" </:< "+ pt)
+ None
}
- def instError = {
- if (settings.debug.value) Console.println("ici " + tree + " " + undetparams + " " + pt)
- if (settings.explaintypes.value) explainTypes(restpe.instantiateTypeParams(undetparams, tvars), pt)
- ConstrInstantiationError(tree, restpe, pt)
}
- if (restpe.instantiateTypeParams(undetparams, tvars) <:< pt) {
- computeArgs
- } else if (isFullyDefined(pt)) {
- debuglog("infer constr " + tree + ":" + restpe + ", pt = " + pt)
- var ptparams = freeTypeParamsOfTerms.collect(pt)
- debuglog("free type params = " + ptparams)
- val ptWithWildcards = pt.instantiateTypeParams(ptparams, ptparams map (ptparam => WildcardType))
- tvars = undetparams map freshVar
- if (restpe.instantiateTypeParams(undetparams, tvars) <:< ptWithWildcards) {
- computeArgs
- restpe = skipImplicit(tree.tpe.resultType)
- debuglog("new tree = " + tree + ":" + restpe)
- val ptvars = ptparams map freshVar
- val pt1 = pt.instantiateTypeParams(ptparams, ptvars)
- if (isPopulated(restpe, pt1)) {
- ptvars foreach instantiateTypeVar
- } else { if (settings.debug.value) Console.println("no instance: "); instError }
- } else { if (settings.debug.value) Console.println("not a subtype " + restpe.instantiateTypeParams(undetparams, tvars) + " of " + ptWithWildcards); instError }
- } else { if (settings.debug.value) Console.println("not fully defined: " + pt); instError }
+
+ def inferForApproxPt =
+ if (isFullyDefined(pt)) {
+ inferFor(pt.instantiateTypeParams(ptparams, ptparams map (x => WildcardType))) flatMap { targs =>
+ val ctorTpInst = tree.tpe.instantiateTypeParams(undetparams, targs)
+ val resTpInst = skipImplicit(ctorTpInst.finalResultType)
+ val ptvars =
+ ptparams map {
+ // since instantiateTypeVar wants to modify the skolem that corresponds to the method's type parameter,
+ // and it uses the TypeVar's origin to locate it, deskolemize the existential skolem to the method tparam skolem
+ // (the existential skolem was created by adaptConstrPattern to introduce the type slack necessary to soundly deal with variant type parameters)
+ case skolem if skolem.isExistentialSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol])
+ case p => freshVar(p)
+ }
+
+ val ptV = pt.instantiateTypeParams(ptparams, ptvars)
+
+ if (isPopulated(resTpInst, ptV)) {
+ ptvars foreach instantiateTypeVar
+ debuglog("isPopulated "+ resTpInst +", "+ ptV +" vars= "+ ptvars)
+ Some(targs)
+ } else None
+ }
+ } else None
+
+ (inferFor(pt) orElse inferForApproxPt) map { targs =>
+ new TreeTypeSubstituter(undetparams, targs).traverse(tree)
+ } getOrElse {
+ debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)"))
+ // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt)
+ ConstrInstantiationError(tree, resTp, pt)
+ }
}
+
def instBounds(tvar: TypeVar): (Type, Type) = {
val tparam = tvar.origin.typeSymbol
val instType = toOrigin(tvar.constr.inst)
@@ -1391,10 +1410,11 @@ trait Infer {
case _ =>
}
}
+ // todo: missing test case
NoBestExprAlternativeError(tree, pt)
} else if (!competing.isEmpty) {
- if (secondTry) NoBestExprAlternativeError(tree, pt)
- else { if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt) }
+ if (secondTry) { NoBestExprAlternativeError(tree, pt); setError(tree) }
+ else if (!pt.isErroneous) AmbiguousExprAlternativeError(tree, pre, best, competing.head, pt)
} else {
// val applicable = alts1 filter (alt =>
// global.typer.infer.isWeaklyCompatible(pre.memberType(alt), pt))
@@ -1404,10 +1424,14 @@ trait Infer {
}
}
- @inline private def inSilentMode(expr: Typer => Boolean): Boolean = {
- val silentContext = context.makeSilent(context.ambiguousErrors)
- val res = expr(newTyper(silentContext))
- if (silentContext.hasErrors) false else res
+ @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = {
+ val oldState = context.state
+ context.setBufferErrors()
+ val res = expr
+ val contextWithErrors = context.hasErrors
+ context.flushBuffer()
+ context.restoreState(oldState)
+ res && !contextWithErrors
}
// Checks against the name of the parameter and also any @deprecatedName.
@@ -1478,7 +1502,7 @@ trait Infer {
val applicable = resolveOverloadedMethod(argtpes, {
alts filter { alt =>
- inSilentMode(typer0 => typer0.infer.isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) &&
+ inSilentMode(context)(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) &&
(!varArgsOnly || isVarArgsList(alt.tpe.params))
}
})
@@ -1494,14 +1518,12 @@ trait Infer {
if (improves(alt, best)) alt else best)
val competing = applicable.dropWhile(alt => best == alt || improves(best, alt))
if (best == NoSymbol) {
- if (pt == WildcardType)
- NoBestMethodAlternativeError(tree, argtpes, pt)
- else
- inferMethodAlternative(tree, undetparams, argtpes, WildcardType)
+ if (pt == WildcardType) NoBestMethodAlternativeError(tree, argtpes, pt)
+ else inferMethodAlternative(tree, undetparams, argtpes, WildcardType)
} else if (!competing.isEmpty) {
if (!(argtpes exists (_.isErroneous)) && !pt.isErroneous)
AmbiguousMethodAlternativeError(tree, pre, best, competing.head, argtpes, pt)
- setError(tree)
+ else setError(tree)
()
} else {
// checkNotShadowed(tree.pos, pre, best, applicable)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index c63ae90ef6..e43b1fab0b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -20,50 +20,50 @@ trait Macros { self: Analyzer =>
macroArgs(fn) :+ args
case TypeApply(fn, args) =>
macroArgs(fn) :+ args
- case Select(qual, name) if !isStaticMacro(tree.symbol) =>
+ case Select(qual, name) =>
List(List(qual))
case _ =>
List(List())
}
- private def isStaticMacro(mac: Symbol): Boolean =
- mac.owner.isModuleClass
-
/**
- * The definition of the method implementing a macro. Example:
+ * The definition of the method implementing a macro. Example:
* Say we have in a class C
*
* def macro foo[T](xs: List[T]): T = expr
*
* Then the following macro method is generated for `foo`:
*
- * def defmacro$foo(glob: scala.reflect.api.Universe)
- * (_this: glob.Tree)
- * (T: glob.Type)
- * (xs: glob.Tree): glob.Tree = {
- * implicit val $glob = glob
+ * def defmacro$foo
+ * (_context: scala.reflect.macro.Context)
+ * (_this: _context.Tree)
+ * (T: _context.TypeTree)
+ * (xs: _context.Tree): _context.Tree = {
+ * import _context._ // this means that all methods of Context can be used unqualified in macro's body
* expr
* }
*
- * If `foo` is declared in an object, the second parameter list is () instead of (_this: glob.Tree).
+ * If macro has no type arguments, the third parameter list is omitted (it's not empty, but omitted altogether).
+ *
+ * To find out the desugared representation of your particular macro, compile it with -Ymacro-debug.
*/
def macroMethDef(mdef: DefDef): Tree = {
def paramDef(name: Name, tpt: Tree) = ValDef(Modifiers(PARAM), name, tpt, EmptyTree)
val contextType = TypeTree(ReflectMacroContext.tpe)
- val globParamSec = List(paramDef(nme.context, contextType))
- def globSelect(name: Name) = Select(Ident(nme.context), name)
- def globTree = globSelect(newTypeName("Tree"))
- def globType = globSelect(newTypeName("Type"))
- val thisParamSec = if (isStaticMacro(mdef.symbol)) List() else List(paramDef(newTermName("_this"), globTree))
- def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globType)
+ val globParamSec = List(paramDef(nme.macroContext, contextType))
+ def globSelect(name: Name) = Select(Ident(nme.macroContext), name)
+ def globTree = globSelect(tpnme.Tree)
+ def globTypeTree = globSelect(tpnme.TypeTree)
+ val thisParamSec = List(paramDef(newTermName(nme.macroThis), globTree))
+ def tparamInMacro(tdef: TypeDef) = paramDef(tdef.name.toTermName, globTypeTree)
def vparamInMacro(vdef: ValDef): ValDef = paramDef(vdef.name, vdef.tpt match {
case tpt @ AppliedTypeTree(hk, _) if treeInfo.isRepeatedParamType(tpt) => AppliedTypeTree(hk, List(globTree))
case _ => globTree
})
def wrapImplicit(tree: Tree) = atPos(tree.pos) {
// implicit hasn't proven useful so far, so I'm disabling it
- //val implicitDecl = ValDef(Modifiers(IMPLICIT), nme.contextImplicit, SingletonTypeTree(Ident(nme.context)), Ident(nme.context))
- val importGlob = Import(Ident(nme.context), List(ImportSelector(nme.WILDCARD, -1, null, -1)))
+ //val implicitDecl = ValDef(Modifiers(IMPLICIT), nme.macroContextImplicit, SingletonTypeTree(Ident(nme.macroContext)), Ident(nme.macroContext))
+ val importGlob = Import(Ident(nme.macroContext), List(ImportSelector(nme.WILDCARD, -1, null, -1)))
Block(List(importGlob), tree)
}
var formals = (mdef.vparamss map (_ map vparamInMacro))
@@ -82,7 +82,7 @@ trait Macros { self: Analyzer =>
def addMacroMethods(templ: Template, namer: Namer): Unit = {
for (ddef @ DefDef(mods, _, _, _, _, _) <- templ.body if mods hasFlag MACRO) {
- val trace = scala.tools.nsc.util.trace when settings.debug.value
+ val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value
val sym = namer.enterSyntheticSym(trace("macro def: ")(macroMethDef(ddef)))
trace("added to "+namer.context.owner.enclClass+": ")(sym)
}
@@ -90,33 +90,102 @@ trait Macros { self: Analyzer =>
lazy val mirror = new scala.reflect.runtime.Mirror {
lazy val libraryClassLoader = {
+ // todo. this is more or less okay, but not completely correct
+ // see https://issues.scala-lang.org/browse/SI-5433 for more info
val classpath = global.classPath.asURLs
- ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+ var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader)
+
+ // an heuristic to detect REPL
+ if (global.settings.exposeEmptyPackage.value) {
+ import scala.tools.nsc.interpreter._
+ val virtualDirectory = global.settings.outputDirs.getSingleOutput.get
+ loader = new AbstractFileClassLoader(virtualDirectory, loader) {}
+ }
+
+ loader
}
override def defaultReflectiveClassLoader() = libraryClassLoader
}
/** Return optionally address of companion object and implementation method symbol
- * of given macro; or None if implementation classfile cannot be loaded or does
+ * of given macro; or None if implementation classfile cannot be loaded or does
* not contain the macro implementation.
*/
def macroImpl(mac: Symbol): Option[(AnyRef, mirror.Symbol)] = {
+ val debug = settings.Ymacrodebug.value
+ val trace = scala.tools.nsc.util.trace when debug
+ trace("looking for macro implementation: ")(mac.fullNameString)
+
try {
val mmeth = macroMeth(mac)
+ trace("found implementation at: ")(mmeth.fullNameString)
+
if (mmeth == NoSymbol) None
else {
- val receiverClass: mirror.Symbol = mirror.classWithName(mmeth.owner.fullName)
+ trace("loading implementation class: ")(mmeth.owner.fullName)
+ trace("classloader is: ")("%s of type %s".format(mirror.libraryClassLoader, mirror.libraryClassLoader.getClass))
+ def inferClasspath(cl: ClassLoader) = cl match {
+ case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]"
+ case _ => "<unknown>"
+ }
+ trace("classpath is: ")(inferClasspath(mirror.libraryClassLoader))
+
+ // @xeno.by: relies on the fact that macros can only be defined in static classes
+ def classfile(sym: Symbol): String = {
+ def recur(sym: Symbol): String = sym match {
+ case sym if sym.owner.isPackageClass =>
+ val suffix = if (sym.isModuleClass) "$" else ""
+ sym.fullName + suffix
+ case sym =>
+ val separator = if (sym.owner.isModuleClass) "" else "$"
+ recur(sym.owner) + separator + sym.javaSimpleName.toString
+ }
+
+ if (sym.isClass || sym.isModule) recur(sym)
+ else recur(sym.enclClass)
+ }
+
+ // @xeno.by: this doesn't work for inner classes
+ // neither does mmeth.owner.javaClassName, so I had to roll my own implementation
+ //val receiverName = mmeth.owner.fullName
+ val receiverName = classfile(mmeth.owner)
+ val receiverClass: mirror.Symbol = mirror.symbolForName(receiverName)
+
+ if (debug) {
+ println("receiverClass is: " + receiverClass.fullNameString)
+
+ val jreceiverClass = mirror.classToJava(receiverClass)
+ val jreceiverSource = jreceiverClass.getProtectionDomain.getCodeSource
+ println("jreceiverClass is %s from %s".format(jreceiverClass, jreceiverSource))
+ println("jreceiverClassLoader is %s with classpath %s".format(jreceiverClass.getClassLoader, inferClasspath(jreceiverClass.getClassLoader)))
+ }
+
val receiverObj = receiverClass.companionModule
- if (receiverObj == NoSymbol) None
+ trace("receiverObj is: ")(receiverObj.fullNameString)
+
+ if (receiverObj == mirror.NoSymbol) None
else {
- val receiver = mirror.getCompanionObject(receiverClass)
+ // @xeno.by: yet another reflection method that doesn't work for inner classes
+ //val receiver = mirror.companionInstance(receiverClass)
+ val clazz = java.lang.Class.forName(receiverName, true, mirror.libraryClassLoader)
+ val receiver = clazz getField "MODULE$" get null
+
val rmeth = receiverObj.info.member(mirror.newTermName(mmeth.name.toString))
- Some((receiver, rmeth))
+ if (debug) {
+ println("rmeth is: " + rmeth.fullNameString)
+ println("jrmeth is: " + mirror.methodToJava(rmeth))
+ }
+
+ if (rmeth == mirror.NoSymbol) None
+ else {
+ Some((receiver, rmeth))
+ }
}
}
} catch {
case ex: ClassNotFoundException =>
+ trace("implementation class failed to load: ")(ex.toString)
None
}
}
@@ -125,32 +194,62 @@ trait Macros { self: Analyzer =>
* Or, if that fails, and the macro overrides a method return
* tree that calls this method instead of the macro.
*/
- def macroExpand(tree: Tree, context: Context): Option[Any] = {
+ def macroExpand(tree: Tree, typer: Typer): Option[Any] = {
+ val trace = scala.tools.nsc.util.trace when settings.Ymacrodebug.value
+ trace("macroExpand: ")(tree)
+
val macroDef = tree.symbol
macroImpl(macroDef) match {
case Some((receiver, rmeth)) =>
val argss = List(global) :: macroArgs(tree)
val paramss = macroMeth(macroDef).paramss
+ trace("paramss: ")(paramss)
val rawArgss = for ((as, ps) <- argss zip paramss) yield {
if (isVarArgsList(ps)) as.take(ps.length - 1) :+ as.drop(ps.length - 1)
else as
}
val rawArgs: Seq[Any] = rawArgss.flatten
+ trace("rawArgs: ")(rawArgs)
+ val savedInfolevel = nodePrinters.infolevel
try {
- Some(mirror.invoke(receiver, rmeth, rawArgs: _*))
+ // @xeno.by: InfoLevel.Verbose examines and prints out infos of symbols
+ // by the means of this'es these symbols can climb up the lexical scope
+ // when these symbols will be examined by a node printer
+ // they will enumerate and analyze their children (ask for infos and tpes)
+ // if one of those children involves macro expansion, things might get nasty
+ // that's why I'm temporarily turning this behavior off
+ nodePrinters.infolevel = nodePrinters.InfoLevel.Quiet
+ val expanded = mirror.invoke(receiver, rmeth)(rawArgs: _*)
+ expanded match {
+ case expanded: Tree =>
+ val expectedTpe = tree.tpe
+ val typed = typer.typed(expanded, EXPRmode, expectedTpe)
+ Some(typed)
+ case expanded if expanded.isInstanceOf[Tree] =>
+ typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is Tree, but it doesn't belong to this compiler's universe")
+ None
+ case expanded =>
+ typer.context.unit.error(tree.pos, "macro must return a compiler-specific tree; returned value is of class: " + expanded.getClass)
+ None
+ }
} catch {
case ex =>
val realex = ReflectionUtils.unwrapThrowable(ex)
- val stacktrace = new java.io.StringWriter()
- realex.printStackTrace(new java.io.PrintWriter(stacktrace))
- val msg = System.getProperty("line.separator") + stacktrace
- context.unit.error(tree.pos, "exception during macro expansion: " + msg)
+ val msg = if (settings.Ymacrodebug.value) {
+ val stacktrace = new java.io.StringWriter()
+ realex.printStackTrace(new java.io.PrintWriter(stacktrace))
+ System.getProperty("line.separator") + stacktrace
+ } else {
+ realex.getMessage
+ }
+ typer.context.unit.error(tree.pos, "exception during macro expansion: " + msg)
None
+ } finally {
+ nodePrinters.infolevel = savedInfolevel
}
case None =>
- val trace = scala.tools.nsc.util.trace when settings.debug.value
def notFound() = {
- context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name)
+ typer.context.unit.error(tree.pos, "macro implementation not found: " + macroDef.name)
None
}
def fallBackToOverridden(tree: Tree): Option[Tree] = {
@@ -159,7 +258,7 @@ trait Macros { self: Analyzer =>
macroDef.allOverriddenSymbols match {
case first :: _ =>
Some(Select(qual, name) setPos tree.pos setSymbol first)
- case _ =>
+ case _ =>
trace("macro is not overridden: ")(tree)
notFound()
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 0c32ff32c0..088a56cd7b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -18,7 +18,7 @@ trait MethodSynthesis {
import global._
import definitions._
import CODE._
-
+
object synthesisUtil {
type M[T] = Manifest[T]
type CM[T] = ClassManifest[T]
@@ -39,7 +39,7 @@ trait MethodSynthesis {
typeRef(container.typeConstructor.prefix, container, args map (_.tpe))
}
-
+
def companionType[T](implicit m: M[T]) =
getRequiredModule(m.erasure.getName).tpe
@@ -71,7 +71,7 @@ trait MethodSynthesis {
class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) {
private def isOverride(name: TermName) =
clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz))
-
+
def newMethodFlags(name: TermName) = {
val overrideFlag = if (isOverride(name)) OVERRIDE else 0L
overrideFlag | SYNTHETIC
@@ -82,7 +82,7 @@ trait MethodSynthesis {
}
private def finishMethod(method: Symbol, f: Symbol => Tree): Tree =
- logResult("finishMethod")(localTyper typed ValOrDefDef(method, f(method)))
+ localTyper typed ValOrDefDef(method, f(method))
private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = {
val m = clazz.newMethod(name.toTermName, clazz.pos.focus, newMethodFlags(name))
@@ -200,7 +200,7 @@ trait MethodSynthesis {
map (acc => atPos(vd.pos.focus)(acc derive annotations))
filterNot (_ eq EmptyTree)
)
- log(trees.mkString("Accessor trees:\n ", "\n ", "\n"))
+ // log(trees.mkString("Accessor trees:\n ", "\n ", "\n"))
if (vd.symbol.isLazy) List(stat)
else trees
case _ =>
@@ -282,7 +282,7 @@ trait MethodSynthesis {
}
}
private def logDerived(result: Tree): Tree = {
- log("[+derived] " + ojoin(mods.defaultFlagString, basisSym.accurateKindString, basisSym.getterName.decode)
+ debuglog("[+derived] " + ojoin(mods.defaultFlagString, basisSym.accurateKindString, basisSym.getterName.decode)
+ " (" + derivedSym + ")\n " + result)
result
@@ -326,22 +326,35 @@ trait MethodSynthesis {
super.validate()
}
- // keep type tree of original abstract field
- private def fixTypeTree(dd: DefDef): DefDef = {
- dd.tpt match {
- case tt: TypeTree if dd.rhs == EmptyTree =>
- tt setOriginal tree.tpt
- case tpt =>
- tpt setPos tree.tpt.pos.focus
- }
- dd
- }
override def derivedTree: DefDef = {
- fixTypeTree {
- DefDef(derivedSym,
- if (mods.isDeferred) EmptyTree
- else gen.mkCheckInit(fieldSelection)
- )
+ // For existentials, don't specify a type for the getter, even one derived
+ // from the symbol! This leads to incompatible existentials for the field and
+ // the getter. Let the typer do all the work. You might think "why only for
+ // existentials, why not always," and you would be right, except: a single test
+ // fails, but it looked like some work to deal with it. Test neg/t0606.scala
+ // starts compiling (instead of failing like it's supposed to) because the typer
+ // expects to be able to identify escaping locals in typedDefDef, and fails to
+ // spot that brand of them. In other words it's an artifact of the implementation.
+ val tpt = derivedSym.tpe.finalResultType match {
+ case ExistentialType(_, _) => TypeTree()
+ case tp => TypeTree(tp)
+ }
+ tpt setPos focusPos(derivedSym.pos)
+ // keep type tree of original abstract field
+ if (mods.isDeferred)
+ tpt setOriginal tree.tpt
+
+ // TODO - reconcile this with the DefDef creator in Trees (which
+ // at this writing presented no way to pass a tree in for tpt.)
+ atPos(derivedSym.pos) {
+ DefDef(
+ Modifiers(derivedSym.flags),
+ derivedSym.name.toTermName,
+ Nil,
+ Nil,
+ tpt,
+ if (mods.isDeferred) EmptyTree else gen.mkCheckInit(fieldSelection)
+ ) setSymbol derivedSym
}
}
}
@@ -363,7 +376,7 @@ trait MethodSynthesis {
override def keepClean = !mods.isParamAccessor
override def derivedTree = (
if (mods.isDeferred) EmptyTree
- else treeCopy.ValDef(tree, mods | flagsExtra, name, tree.tpt, tree.rhs)
+ else copyValDef(tree)(mods = mods | flagsExtra, name = this.name)
)
}
case class Param(tree: ValDef) extends DerivedFromValDef {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 35ee46363c..1566897dab 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -277,12 +277,16 @@ trait Namers extends MethodSynthesis {
def assignAndEnterFinishedSymbol(tree: MemberDef): Symbol = {
val sym = assignAndEnterSymbol(tree)
sym setInfo completerOf(tree)
- log("[+info] " + sym.fullLocationString)
+ // log("[+info] " + sym.fullLocationString)
sym
}
private def logAssignSymbol(tree: Tree, sym: Symbol): Symbol = {
- log("[+symbol] " + sym.hasFlagsToString(-1L) + " " + sym)
+ sym.name.toTermName match {
+ case nme.IMPORT | nme.OUTER | nme.ANON_CLASS_NAME | nme.ANON_FUN_NAME | nme.CONSTRUCTOR => ()
+ case _ =>
+ log("[+symbol] " + sym.debugLocationString)
+ }
tree.symbol = sym
sym
}
@@ -1315,7 +1319,7 @@ trait Namers extends MethodSynthesis {
catch typeErrorHandler(tree, ErrorType)
result match {
- case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => typer.deskolemizeTypeParams(tparams)(result)
+ case PolyType(tparams @ (tp :: _), _) if tp.owner.isTerm => deskolemizeTypeParams(tparams)(result)
case _ => result
}
}
@@ -1478,8 +1482,11 @@ trait Namers extends MethodSynthesis {
private val ownerSym = owner.symbol
override val typeParams = tparams map (_.symbol) //@M
override val tree = restp.tree
- if (ownerSym.isTerm)
- typer skolemizeTypeParams tparams
+
+ if (ownerSym.isTerm) {
+ val skolems = deriveFreshSkolems(tparams map (_.symbol))
+ map2(tparams, skolems)(_ setSymbol _)
+ }
def completeImpl(sym: Symbol) = {
// @M an abstract type's type parameters are entered.
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index 359e72e3e4..c621497618 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -37,21 +37,17 @@ trait NamesDefaults { self: Analyzer =>
}
def isNamed(arg: Tree) = nameOf(arg).isDefined
- /** @param pos maps indicies from old to new */
+ /** @param pos maps indices from old to new */
def reorderArgs[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = {
val res = new Array[T](args.length)
- // (hopefully) faster than zipWithIndex
- (0 /: args) { case (index, arg) => res(pos(index)) = arg; index + 1 }
+ foreachWithIndex(args)((arg, index) => res(pos(index)) = arg)
res.toList
}
- /** @param pos maps indicies from new to old (!) */
+ /** @param pos maps indices from new to old (!) */
def reorderArgsInv[T: ClassManifest](args: List[T], pos: Int => Int): List[T] = {
val argsArray = args.toArray
- val res = new mutable.ListBuffer[T]
- for (i <- 0 until argsArray.length)
- res += argsArray(pos(i))
- res.toList
+ argsArray.indices map (i => argsArray(pos(i))) toList
}
/** returns `true` if every element is equal to its index */
@@ -432,11 +428,11 @@ trait NamesDefaults { self: Analyzer =>
}
} else NoSymbol
}
-
+
private def savingUndeterminedTParams[T](context: Context)(fn: List[Symbol] => T): T = {
val savedParams = context.extractUndetparams()
val savedReporting = context.ambiguousErrors
-
+
context.setAmbiguousErrors(false)
try fn(savedParams)
finally {
@@ -455,7 +451,7 @@ trait NamesDefaults { self: Analyzer =>
|| (ctx.owner.rawInfo.member(name) != NoSymbol)
)
)
-
+
/** A full type check is very expensive; let's make sure there's a name
* somewhere which could potentially be ambiguous before we go that route.
*/
@@ -507,7 +503,7 @@ trait NamesDefaults { self: Analyzer =>
/**
* Removes name assignments from args. Additionally, returns an array mapping
- * argument indicies from call-site-order to definition-site-order.
+ * argument indices from call-site-order to definition-site-order.
*
* Verifies that names are not specified twice, positional args don't appear
* after named ones.
@@ -523,7 +519,7 @@ trait NamesDefaults { self: Analyzer =>
def matchesName(param: Symbol) = !param.isSynthetic && (
(param.name == name) || (param.deprecatedParamName match {
case Some(`name`) =>
- context0.unit.deprecationWarning(arg.pos,
+ context0.unit.deprecationWarning(arg.pos,
"the parameter name "+ name +" has been deprecated. Use "+ param.name +" instead.")
true
case _ => false
diff --git a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
index 6d31243fd0..b060fd7121 100644
--- a/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/PatMatVirtualiser.scala
@@ -43,7 +43,7 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
val outer = newTermName("<outer>")
val runOrElse = newTermName("runOrElse")
val zero = newTermName("zero")
- val __match = newTermName("__match")
+ val _match = newTermName("__match") // don't call it __match, since that will trigger virtual pattern matching...
def counted(str: String, i: Int) = newTermName(str+i)
}
@@ -51,8 +51,8 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
object MatchTranslator {
def apply(typer: Typer): MatchTranslation = {
import typer._
- // typing `__match` to decide which MatchTranslator to create adds 4% to quick.comp.timer
- newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName.__match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match {
+ // typing `_match` to decide which MatchTranslator to create adds 4% to quick.comp.timer
+ newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) match {
case SilentResultValue(ms) => new PureMatchTranslator(typer, ms)
case _ => new OptimizingMatchTranslator(typer)
}
@@ -116,6 +116,10 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
trait MatchTranslation extends MatchMonadInterface { self: TreeMakers with CodegenCore =>
import typer.{typed, context, silent, reallyExists}
+ private def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
+ case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args)
+ case _ => tp
+ }
/** Implement a pattern match by turning its cases (including the implicit failure case)
* into the corresponding (monadic) extractors, and combining them with the `orElse` combinator.
@@ -133,11 +137,6 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
// and the only place that emits Matches after typers is for exception handling anyway)
assert(phase.id <= currentRun.typerPhase.id, phase)
- def repeatedToSeq(tp: Type): Type = (tp baseType RepeatedParamClass) match {
- case TypeRef(_, RepeatedParamClass, args) => appliedType(SeqClass.typeConstructor, args)
- case _ => tp
- }
-
val scrutType = repeatedToSeq(elimAnonymousClass(scrut.tpe.widen))
val scrutSym = freshSym(scrut.pos, pureType(scrutType))
@@ -146,6 +145,47 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
combineCases(scrut, scrutSym, cases map translateCase(scrutSym, okPt), okPt, matchOwner)
}
+ // return list of typed CaseDefs that are supported by the backend (typed/bind/wildcard)
+ // we don't have a global scrutinee -- the caught exception must be bound in each of the casedefs
+ // there's no need to check the scrutinee for null -- "throw null" becomes "throw new NullPointerException"
+ // try to simplify to a type-based switch, or fall back to a catch-all case that runs a normal pattern match
+ // unlike translateMatch, we type our result before returning it
+ def translateTry(caseDefs: List[CaseDef], pt: Type, pos: Position): List[CaseDef] =
+ // if they're already simple enough to be handled by the back-end, we're done
+ if (caseDefs forall treeInfo.isCatchCase) caseDefs
+ else {
+ val okPt = repeatedToSeq(pt)
+ val switch = {
+ val bindersAndCases = caseDefs map { caseDef =>
+ // generate a fresh symbol for each case, hoping we'll end up emitting a type-switch (we don't have a global scrut there)
+ // if we fail to emit a fine-grained switch, have to do translateCase again with a single scrutSym (TODO: uniformize substitution on treemakers so we can avoid this)
+ val caseScrutSym = freshSym(pos, pureType(ThrowableClass.tpe))
+ (caseScrutSym, propagateSubstitution(translateCase(caseScrutSym, okPt)(caseDef), EmptySubstitution))
+ }
+
+ (emitTypeSwitch(bindersAndCases, pt) map (_.map(fixerUpper(matchOwner, pos).apply(_).asInstanceOf[CaseDef])))
+ }
+
+ val catches = switch getOrElse {
+ val scrutSym = freshSym(pos, pureType(ThrowableClass.tpe))
+ val casesNoSubstOnly = caseDefs map { caseDef => (propagateSubstitution(translateCase(scrutSym, okPt)(caseDef), EmptySubstitution))}
+
+ val exSym = freshSym(pos, pureType(ThrowableClass.tpe), "ex")
+
+ List(
+ atPos(pos) {
+ CaseDef(
+ Bind(exSym, Ident(nme.WILDCARD)), // TODO: does this need fixing upping?
+ EmptyTree,
+ combineCasesNoSubstOnly(CODE.REF(exSym), scrutSym, casesNoSubstOnly, pt, matchOwner, scrut => Throw(CODE.REF(exSym)))
+ )
+ })
+ }
+
+ typer.typedCases(catches, ThrowableClass.tpe, WildcardType)
+ }
+
+
/** The translation of `pat if guard => body` has two aspects:
* 1) the substitution due to the variables bound by patterns
@@ -213,13 +253,14 @@ trait PatMatVirtualiser extends ast.TreeDSL { self: Analyzer =>
withSubPats(typeTestTreeMaker :+ extractor.treeMaker(patBinderOrCasted, pos), extractor.subBindersAndPatterns: _*)
}
- /** Decompose the pattern in `tree`, of shape C(p_1, ..., p_N), into a list of N symbols, and a list of its N sub-trees
- * The list of N symbols contains symbols for every bound name as well as the un-named sub-patterns (fresh symbols are generated here for these)
- *
- * @arg patBinder symbol used to refer to the result of the previous pattern's extractor (will later be replaced by the outer tree with the correct tree to refer to that patterns result)
- */
+
object MaybeBoundTyped {
- // the returned type is the one inferred by inferTypedPattern (`owntype`)
+ /** Decompose the pattern in `tree`, of shape C(p_1, ..., p_N), into a list of N symbols, and a list of its N sub-trees
+ * The list of N symbols contains symbols for every bound name as well as the un-named sub-patterns (fresh symbols are generated here for these).
+ * The returned type is the one inferred by inferTypedPattern (`owntype`)
+ *
+ * @arg patBinder symbol used to refer to the result of the previous pattern's extractor (will later be replaced by the outer tree with the correct tree to refer to that patterns result)
+ */
def unapply(tree: Tree): Option[(Symbol, Type)] = tree match {
case Bound(subpatBinder, typed@Typed(expr, tpt)) => Some((subpatBinder, typed.tpe))
case Bind(_, typed@Typed(expr, tpt)) => Some((patBinder, typed.tpe))
@@ -668,6 +709,10 @@ class Foo(x: Other) { x._1 } // no error in this order
def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] =
None
+ // for catch
+ def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] =
+ None
+
abstract class TreeMaker {
/** captures the scope and the value of the bindings in patterns
* important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed)
@@ -788,6 +833,7 @@ class Foo(x: Other) { x._1 } // no error in this order
}
// implements the run-time aspects of (§8.2) (typedPattern has already done the necessary type transformations)
+ // TODO: normalize construction, which yields a combination of a EqualityTestTreeMaker (when necessary) and a TypeTestTreeMaker
case class TypeAndEqualityTestTreeMaker(prevBinder: Symbol, patBinder: Symbol, pt: Type, pos: Position) extends CondTreeMaker {
val nextBinderTp = glb(List(patBinder.info.widen, pt))
@@ -843,6 +889,10 @@ class Foo(x: Other) { x._1 } // no error in this order
val cond = typeAndEqualityTest(patBinder, pt)
val res = codegen._asInstanceOf(patBinder, nextBinderTp)
+
+ // TODO: remove this
+ def isStraightTypeTest = cond match { case TypeApply(_, _) => cond.symbol == Any_isInstanceOf case _ => false }
+
override def toString = "TET"+(patBinder, pt)
}
@@ -926,25 +976,30 @@ class Foo(x: Other) { x._1 } // no error in this order
}
// calls propagateSubstitution on the treemakers
- def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = fixerUpper(owner, scrut.pos){
- val casesUnOpt = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
+ def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol): Tree = {
+ val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution)) // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
+ combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, CODE.MATCHERROR(_))
+ }
- emitSwitch(scrut, scrutSym, casesUnOpt, pt).getOrElse{
+ def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFail: Tree => Tree): Tree = fixerUpper(owner, scrut.pos){
+ emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt).getOrElse{
val (matcher, hasDefault, toHoist) =
- if (casesUnOpt nonEmpty) {
+ if (casesNoSubstOnly nonEmpty) {
// when specified, need to propagate pt explicitly (type inferencer can't handle it)
val optPt =
if (isFullyDefined(pt)) inMatchMonad(pt)
else NoType
- // do this check on casesUnOpt, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
+ // do this check on casesNoSubstOnly, since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
// exhaustivity and reachability must be checked before optimization as well
- val hasDefault = casesUnOpt.nonEmpty && {
- val nonTrivLast = casesUnOpt.last
+ // TODO: improve, a trivial type test before the body still makes for a default case
+ // ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op)
+ val hasDefault = casesNoSubstOnly.nonEmpty && {
+ val nonTrivLast = casesNoSubstOnly.last
nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker]
}
- val (cases, toHoist) = optimizeCases(scrutSym, casesUnOpt, pt)
+ val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt)
val combinedCases =
cases.map(combineExtractors(_, pt)).reduceLeft(codegen.typedOrElse(optPt))
@@ -952,7 +1007,11 @@ class Foo(x: Other) { x._1 } // no error in this order
(combinedCases, hasDefault, toHoist)
} else (codegen.zero, false, Nil)
- val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, hasDefault)
+ // catch-all
+ val catchAll =
+ if (hasDefault) None // no need for a catch-all when there's already a default
+ else Some(matchFail)
+ val expr = codegen.runOrElse(scrut, scrutSym, matcher, if (isFullyDefined(pt)) pt else NoType, catchAll)
if (toHoist isEmpty) expr
else Block(toHoist, expr)
}
@@ -966,7 +1025,7 @@ class Foo(x: Other) { x._1 } // no error in this order
// TODO: do this during tree construction, but that will require tracking the current owner in treemakers
// TODO: assign more fine-grained positions
// fixes symbol nesting, assigns positions
- private def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
+ protected def fixerUpper(origOwner: Symbol, pos: Position) = new Traverser {
currentOwner = origOwner
override def traverse(t: Tree) {
@@ -1019,7 +1078,7 @@ class Foo(x: Other) { x._1 } // no error in this order
// codegen relevant to the structure of the translation (how extractors are combined)
trait AbsCodegen {
- def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree
def one(res: Tree, bodyPt: Type, matchPt: Type): Tree
def zero: Tree
def flatMap(prev: Tree, b: Symbol, next: Tree): Tree
@@ -1098,10 +1157,10 @@ class Foo(x: Other) { x._1 } // no error in this order
protected def matchMonadSym = oneSig.finalResultType.typeSymbol
import CODE._
- def __match(n: Name): SelectStart = matchStrategy DOT n
+ def _match(n: Name): SelectStart = matchStrategy DOT n
private lazy val oneSig: Type =
- typer.typed(__match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message
+ typer.typed(_match(vpmName.one), EXPRmode | POLYmode | TAPPmode | FUNmode, WildcardType).tpe // TODO: error message
}
trait PureCodegen extends CodegenCore with PureMatchMonadInterface {
@@ -1110,14 +1169,15 @@ class Foo(x: Other) { x._1 } // no error in this order
object pureCodegen extends CommonCodegen { import CODE._
//// methods in MatchingStrategy (the monad companion) -- used directly in translation
// __match.runOrElse(`scrut`)(`scrutSym` => `matcher`)
- def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean): Tree
- = __match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher))
+ // TODO: consider catchAll, or virtualized matching will break in exception handlers
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]): Tree
+ = _match(vpmName.runOrElse) APPLY (scrut) APPLY (fun(scrutSym, matcher))
// __match.one(`res`)
- def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (__match(vpmName.one)) (res)
+ def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = (_match(vpmName.one)) (res)
// __match.zero
- def zero: Tree = __match(vpmName.zero)
+ def zero: Tree = _match(vpmName.zero)
// __match.guard(`c`, `then`)
- def guard(c: Tree, then: Tree, tp: Type): Tree = __match(vpmName.guard) APPLY (c, then)
+ def guard(c: Tree, then: Tree, tp: Type): Tree = _match(vpmName.guard) APPLY (c, then)
//// methods in the monad instance -- used directly in translation
// `prev`.flatMap(`b` => `next`)
@@ -1437,94 +1497,145 @@ class Foo(x: Other) { x._1 } // no error in this order
}
}
- //// SWITCHES
+ //// SWITCHES -- TODO: operate on Tests rather than TreeMakers
trait SwitchEmission extends TreeMakers with OptimizedMatchMonadInterface { self: CodegenCore =>
- object SwitchablePattern { def unapply(pat: Tree) = pat match {
- case Literal(Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) => true // TODO: Java 7 allows strings in switches
- case _ => false
- }}
-
- // def isSwitchable(cases: List[(List[TreeMaker], Tree)]): Boolean = {
- // def isSwitchableTreeMaker(tm: TreeMaker) = tm match {
- // case tm@EqualityTestTreeMaker(_, SwitchablePattern(), _) => true
- // case SubstOnlyTreeMaker(_) => true
- // case AlternativesTreeMaker(_, altss, _) => altss forall (_.forall(isSwitchableTreeMaker))
- // case _ => false
- // }
- // }
+ abstract class SwitchMaker {
+ abstract class SwitchableTreeMakerExtractor { def unapply(x: TreeMaker): Option[Tree] }
+ val SwitchableTreeMaker: SwitchableTreeMakerExtractor
- private val switchableTpes = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe)
+ def alternativesSupported: Boolean
- override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = {
- def sequence[T](xs: List[Option[T]]): Option[List[T]] =
+ def isDefault(x: CaseDef): Boolean
+ def defaultSym: Symbol
+ def defaultBody: Tree
+ def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef
+
+ private def sequence[T](xs: List[Option[T]]): Option[List[T]] =
if (xs exists (_.isEmpty)) None else Some(xs.flatten)
- def isSwitchableTpe(tpe: Type): Boolean =
- switchableTpes contains tpe
- def switchableConstToInt(x: Tree): Tree = {
- val Literal(const) = x
- const.tag match {
- case IntTag => x
- case ByteTag | ShortTag | CharTag => Literal(Constant(const.intValue))
+ // empty list ==> failure
+ def apply(cases: List[(Symbol, List[TreeMaker])], pt: Type): List[CaseDef] = {
+ val caseDefs = cases map { case (scrutSym, makers) =>
+ makers match {
+ // default case
+ case (btm@BodyTreeMaker(body, _)) :: Nil =>
+ Some(defaultCase(scrutSym, btm.substitution(body)))
+ // constant (or typetest for typeSwitch)
+ case SwitchableTreeMaker(pattern) :: (btm@BodyTreeMaker(body, _)) :: Nil =>
+ Some(CaseDef(pattern, EmptyTree, btm.substitution(body)))
+ // alternatives
+ case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil if alternativesSupported =>
+ val casePatterns = altss map {
+ case SwitchableTreeMaker(pattern) :: Nil =>
+ Some(pattern)
+ case _ =>
+ None
+ }
+
+ sequence(casePatterns) map { patterns =>
+ val substedBody = btm.substitution(body)
+ CaseDef(Alternative(patterns), EmptyTree, substedBody)
+ }
+ case _ => //println("can't emit switch for "+ makers)
+ None //failure (can't translate pattern to a switch)
+ }
}
- }
- val caseDefs = cases map { makers =>
- removeSubstOnly(makers) match {
- // default case (don't move this to unfold, as it may only occur on the top level, not as an alternative -- well, except in degenerate matches)
- case (btm@BodyTreeMaker(body, _)) :: Nil =>
- Some(CaseDef(Ident(nme.WILDCARD), EmptyTree, btm.substitution(body)))
- // constant
- case (EqualityTestTreeMaker(_, const@SwitchablePattern(), _)) :: (btm@BodyTreeMaker(body, _)) :: Nil =>
- Some(CaseDef(switchableConstToInt(const), EmptyTree, btm.substitution(body)))
- // alternatives
- case AlternativesTreeMaker(_, altss, _) :: (btm@BodyTreeMaker(body, _)) :: Nil => // assert(currLabel.isEmpty && nextLabel.isEmpty)
- val caseConstants = altss map {
- case EqualityTestTreeMaker(_, const@SwitchablePattern(), _) :: Nil =>
- Some(switchableConstToInt(const))
- case _ =>
- None
+ (for(
+ caseDefs <- sequence(caseDefs)) yield
+ if (caseDefs exists isDefault) caseDefs
+ else {
+ caseDefs :+ defaultCase()
}
+ ) getOrElse Nil
+ }
+ }
- sequence(caseConstants) map { contants =>
- val substedBody = btm.substitution(body)
- CaseDef(Alternative(contants), EmptyTree, substedBody)
- }
- case _ =>
- None //failure (can't translate pattern to a switch)
+ class RegularSwitchMaker(scrutSym: Symbol) extends SwitchMaker {
+ val switchableTpe = Set(ByteClass.tpe, ShortClass.tpe, IntClass.tpe, CharClass.tpe)
+ val alternativesSupported = true
+
+ object SwitchablePattern { def unapply(pat: Tree): Option[Tree] = pat match {
+ case Literal(const@Constant((_: Byte ) | (_: Short) | (_: Int ) | (_: Char ))) =>
+ Some(Literal(Constant(const.intValue))) // TODO: Java 7 allows strings in switches
+ case _ => None
+ }}
+
+ object SwitchableTreeMaker extends SwitchableTreeMakerExtractor {
+ def unapply(x: TreeMaker): Option[Tree] = x match {
+ case EqualityTestTreeMaker(_, SwitchablePattern(const), _) => Some(const)
+ case _ => None
}
}
- if (!isSwitchableTpe(scrut.tpe))
- None // TODO: emit a cast of the scrutinee and a switch on the cast scrutinee if patterns allow switch but the type of the scrutinee doesn't
- else {
- sequence(caseDefs) map { caseDefs =>
- import CODE._
- val caseDefsWithDefault = {
- def isDefault(x: CaseDef): Boolean = x match {
- case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true
- case _ => false
- }
- val hasDefault = caseDefs exists isDefault
- if (hasDefault) caseDefs else {
- val default = atPos(scrut.pos) { DEFAULT ==> MATCHERROR(REF(scrutSym)) }
- caseDefs :+ default
- }
- }
- val matcher = BLOCK(
- if (scrut.tpe != IntClass.tpe) {
- scrutSym setInfo IntClass.tpe
- VAL(scrutSym) === (scrut DOT nme.toInt)
- } else {
- VAL(scrutSym) === scrut
- },
- Match(REF(scrutSym), caseDefsWithDefault) // match on scrutSym, not scrut to avoid duplicating scrut
- )
- // matcher filter (tree => tree.tpe == null) foreach println
- // treeBrowser browse matcher
- matcher // set type to avoid recursion in typedMatch
+ def isDefault(x: CaseDef): Boolean = x match {
+ case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true
+ case _ => false
+ }
+
+ def defaultSym: Symbol = scrutSym
+ def defaultBody: Tree = { import CODE._; MATCHERROR(REF(scrutSym)) }
+ def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) {
+ DEFAULT ==> body
+ }}
+ }
+
+ override def emitSwitch(scrut: Tree, scrutSym: Symbol, cases: List[List[TreeMaker]], pt: Type): Option[Tree] = { import CODE._
+ val regularSwitchMaker = new RegularSwitchMaker(scrutSym)
+ // TODO: if patterns allow switch but the type of the scrutinee doesn't, cast (type-test) the scrutinee to the corresponding switchable type and switch on the result
+ if (regularSwitchMaker.switchableTpe(scrutSym.tpe)) {
+ val caseDefsWithDefault = regularSwitchMaker(cases map {c => (scrutSym, c)}, pt)
+ if (caseDefsWithDefault isEmpty) None
+ else {
+ // match on scrutSym -- converted to an int if necessary -- not on scrut directly (to avoid duplicating scrut)
+ val scrutToInt: Tree =
+ if(scrutSym.tpe =:= IntClass.tpe) REF(scrutSym)
+ else (REF(scrutSym) DOT (nme.toInt))
+ Some(BLOCK(
+ VAL(scrutSym) === scrut,
+ Match(scrutToInt, caseDefsWithDefault)
+ ))
}
+ } else None
+ }
+
+ // for the catch-cases in a try/catch
+ private object typeSwitchMaker extends SwitchMaker {
+ def switchableTpe(tp: Type) = true
+ val alternativesSupported = false // TODO: needs either back-end support of flattening of alternatives during typers
+
+ // TODO: there are more treemaker-sequences that can be handled by type tests
+ // analyze the result of approximateTreeMaker rather than the TreeMaker itself
+ object SwitchableTreeMaker extends SwitchableTreeMakerExtractor {
+ def unapply(x: TreeMaker): Option[Tree] = x match {
+ case tm@TypeTestTreeMaker(_, _, _) =>
+ Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */)) // -- TODO: use this if binder does not occur in the body
+ case tm@TypeAndEqualityTestTreeMaker(_, patBinder, pt, _) if tm.isStraightTypeTest =>
+ Some(Bind(tm.nextBinder, Typed(Ident(nme.WILDCARD), TypeTree(tm.nextBinderTp)) /* not used by back-end */))
+ case _ =>
+ None
+ }
+ }
+
+ def isDefault(x: CaseDef): Boolean = x match {
+ case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true
+ case CaseDef(Bind(_, Typed(Ident(nme.WILDCARD), tpt)), EmptyTree, _) if (tpt.tpe =:= ThrowableClass.tpe) => true
+ case CaseDef(Ident(nme.WILDCARD), EmptyTree, _) => true
+ case _ => false
}
+
+ lazy val defaultSym: Symbol = freshSym(NoPosition, ThrowableClass.tpe)
+ def defaultBody: Tree = Throw(CODE.REF(defaultSym))
+ def defaultCase(scrutSym: Symbol = defaultSym, body: Tree = defaultBody): CaseDef = { import CODE._; atPos(body.pos) {
+ CASE (Bind(scrutSym, Typed(Ident(nme.WILDCARD), TypeTree(ThrowableClass.tpe)))) ==> body
+ }}
+ }
+
+ // TODO: drop null checks
+ override def emitTypeSwitch(bindersAndCases: List[(Symbol, List[TreeMaker])], pt: Type): Option[List[CaseDef]] = {
+ val caseDefsWithDefault = typeSwitchMaker(bindersAndCases, pt)
+ if (caseDefsWithDefault isEmpty) None
+ else Some(caseDefsWithDefault)
}
}
@@ -1551,33 +1662,31 @@ class Foo(x: Other) { x._1 } // no error in this order
/** Inline runOrElse and get rid of Option allocations
*
- * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse (throw new MatchError(x))
+ * runOrElse(scrut: scrutTp)(matcher): resTp = matcher(scrut) getOrElse ${catchAll(`scrut`)}
* the matcher's optional result is encoded as a flag, keepGoing, where keepGoing == true encodes result.isEmpty,
* if keepGoing is false, the result Some(x) of the naive translation is encoded as matchRes == x
*/
@inline private def dontStore(tp: Type) = (tp.typeSymbol eq UnitClass) || (tp.typeSymbol eq NothingClass)
lazy val keepGoing = freshSym(NoPosition, BooleanClass.tpe, "keepGoing") setFlag MUTABLE
lazy val matchRes = freshSym(NoPosition, AnyClass.tpe, "matchRes") setFlag MUTABLE
- def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, hasDefault: Boolean) = {
+ def runOrElse(scrut: Tree, scrutSym: Symbol, matcher: Tree, resTp: Type, catchAll: Option[Tree => Tree]) = {
matchRes.info = if (resTp ne NoType) resTp.widen else AnyClass.tpe // we don't always know resTp, and it might be AnyVal, in which case we can't assign NULL
if (dontStore(resTp)) matchRes resetFlag MUTABLE // don't assign to Unit-typed var's, in fact, make it a val -- conveniently also works around SI-5245
BLOCK(
VAL(zeroSym) === REF(NoneModule), // TODO: can we just get rid of explicitly emitted zero? don't know how to do that as a local rewrite...
- VAL(scrutSym) === scrut, // reuse the symbol of the function's argument to avoid creating a fresh one and substituting it for scrutSym in `matcher` -- the owner structure is repaired by fixerUpper
+ VAL(scrutSym) === scrut,
VAL(matchRes) === mkZero(matchRes.info), // must cast to deal with GADT typing, hence the private mkZero above
VAL(keepGoing) === TRUE,
matcher,
- if(hasDefault) REF(matchRes)
- else (IF (REF(keepGoing)) THEN MATCHERROR(REF(scrutSym)) ELSE REF(matchRes))
+ catchAll map { catchAllGen => (IF (REF(keepGoing)) THEN catchAllGen(REF(scrutSym)) ELSE REF(matchRes)) } getOrElse REF(matchRes)
)
}
// only used to wrap the RHS of a body
def one(res: Tree, bodyPt: Type, matchPt: Type): Tree = {
BLOCK(
- if (dontStore(matchPt)) res // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse...
- else (REF(matchRes) === res), // _asInstanceOf(res, tp.widen, force = true)
- REF(keepGoing) === FALSE,
+ REF(keepGoing) === FALSE, // comes before assignment to matchRes, so the latter is in tail positions (can ignore the trailing zero -- will disappear when we flatten blocks, which is TODO)
+ if (dontStore(matchPt)) res else (REF(matchRes) === res), // runOrElse hasn't been called yet, so matchRes.isMutable is irrelevant, also, tp may be a subtype of resTp used in runOrElse...
zero // to have a nice lub for lubs -- otherwise we'll get a boxed unit here -- TODO: get rid of all those dangling else zero's
)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 04213cfda7..1e17cb2e3f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -150,7 +150,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
// Override checking ------------------------------------------------------------
-
+
def isJavaVarargsAncestor(clazz: Symbol) = (
clazz.isClass
&& clazz.isJavaDefined
@@ -167,14 +167,14 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
log("Found java varargs ancestor in " + clazz.fullLocationString + ".")
val self = clazz.thisType
val bridges = new ListBuffer[Tree]
-
+
def varargBridge(member: Symbol, bridgetpe: Type): Tree = {
log("Generating varargs bridge for " + member.fullLocationString + " of type " + bridgetpe)
-
+
val bridge = member.cloneSymbolImpl(clazz, member.flags | VBRIDGE) setPos clazz.pos
bridge.setInfo(bridgetpe.cloneInfo(bridge))
clazz.info.decls enter bridge
-
+
val params = bridge.paramss.head
val elemtp = params.last.tpe.typeArgs.head
val idents = params map Ident
@@ -183,7 +183,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
localTyper typed DefDef(bridge, body)
}
-
+
// For all concrete non-private members that have a (Scala) repeated parameter:
// compute the corresponding method type `jtpe` with a Java repeated parameter
// if a method with type `jtpe` exists and that method is not a varargs bridge
@@ -203,7 +203,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
}
}
-
+
bridges.toList
}
else Nil
@@ -276,10 +276,13 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
* of class `clazz` are met.
*/
def checkOverride(member: Symbol, other: Symbol) {
+ debuglog("Checking validity of %s overriding %s".format(member.fullLocationString, other.fullLocationString))
+
def memberTp = self.memberType(member)
def otherTp = self.memberType(other)
def noErrorType = other.tpe != ErrorType && member.tpe != ErrorType
def isRootOrNone(sym: Symbol) = sym == RootClass || sym == NoSymbol
+ def isNeitherInClass = (member.owner != clazz) && (other.owner != clazz)
def objectOverrideErrorMsg = (
"overriding " + other.fullLocationString + " with " + member.fullLocationString + ":\n" +
"an overriding object must conform to the overridden object's class bound" +
@@ -334,7 +337,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
def deferredCheck = member.isDeferred || !other.isDeferred
def subOther(s: Symbol) = s isSubClass other.owner
def subMember(s: Symbol) = s isSubClass member.owner
-
+
if (subOther(member.owner) && deferredCheck) {
//Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG
return
@@ -381,7 +384,14 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
overrideError("cannot override final member");
// synthetic exclusion needed for (at least) default getters.
} else if (!other.isDeferred && !member.isAnyOverride && !member.isSynthetic) {
- overrideError("needs `override' modifier");
+ if (isNeitherInClass && !(other.owner isSubClass member.owner))
+ emitOverrideError(
+ clazz + " inherits conflicting members:\n "
+ + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member)
+ + "\n(Note: this can be resolved by declaring an override in " + clazz + ".)"
+ )
+ else
+ overrideError("needs `override' modifier")
} else if (other.isAbstractOverride && other.isIncompleteIn(clazz) && !member.isAbstractOverride) {
overrideError("needs `abstract override' modifiers")
} else if (member.isAnyOverride && (other hasFlag ACCESSOR) && other.accessed.isVariable && !other.accessed.isLazy) {
@@ -420,12 +430,12 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
if( !(sameLength(member.typeParams, other.typeParams) && (memberTp.substSym(member.typeParams, other.typeParams) =:= otherTp)) ) // (1.6)
overrideTypeError();
- }
+ }
else if (other.isAbstractType) {
//if (!member.typeParams.isEmpty) // (1.7) @MAT
// overrideError("may not be parameterized");
val otherTp = self.memberInfo(other)
-
+
if (!(otherTp.bounds containsType memberTp)) { // (1.7.1)
overrideTypeError(); // todo: do an explaintypes with bounds here
explainTypes(_.bounds containsType _, otherTp, memberTp)
@@ -515,16 +525,17 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
!other.isDeferred && other.isJavaDefined && {
// #3622: erasure operates on uncurried types --
// note on passing sym in both cases: only sym.isType is relevant for uncurry.transformInfo
- def uncurryAndErase(tp: Type) = erasure.erasure(sym, uncurry.transformInfo(sym, tp))
+ // !!! erasure.erasure(sym, uncurry.transformInfo(sym, tp)) gives erreneous of inaccessible type - check whether that's still the case!
+ def uncurryAndErase(tp: Type) = erasure.erasure(sym)(uncurry.transformInfo(sym, tp))
val tp1 = uncurryAndErase(clazz.thisType.memberType(sym))
val tp2 = uncurryAndErase(clazz.thisType.memberType(other))
- atPhase(currentRun.erasurePhase.next)(tp1 matches tp2)
+ afterErasure(tp1 matches tp2)
})
def ignoreDeferred(member: Symbol) = (
(member.isAbstractType && !member.isFBounded) || (
member.isJavaDefined &&
- // the test requires atPhase(erasurePhase.next) so shouldn't be
+ // the test requires afterErasure so shouldn't be
// done if the compiler has no erasure phase available
(currentRun.erasurePhase == NoPhase || javaErasedOverridingSym(member) != NoSymbol)
)
@@ -1053,7 +1064,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass
def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass
def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass)
- def isSpecial(s: Symbol) = isValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass) || isMaybeValue(s)
+ def isSpecial(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || (s isSubClass ScalaNumberClass) || isMaybeValue(s)
def possibleNumericCount = onSyms(_ filter (x => isNumeric(x) || isMaybeValue(x)) size)
val nullCount = onSyms(_ filter (_ == NullClass) size)
@@ -1074,7 +1085,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
if (nullCount == 2)
nonSensible("", true) // null == null
else if (nullCount == 1) {
- if (onSyms(_ exists isValueClass)) // null == 5
+ if (onSyms(_ exists isPrimitiveValueClass)) // null == 5
nonSensible("", false)
else if (onTrees( _ exists isNew)) // null == new AnyRef
nonSensibleWarning("a fresh object", false)
@@ -1167,7 +1178,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
case vsym => ValDef(vsym)
}
}
- def createStaticModuleAccessor() = atPhase(phase.next) {
+ def createStaticModuleAccessor() = afterRefchecks {
val method = (
sym.owner.newMethod(sym.name.toTermName, sym.pos, (sym.flags | STABLE) & ~MODULE)
setInfoAndEnter NullaryMethodType(sym.moduleClass.tpe)
@@ -1178,7 +1189,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
vdef,
localTyper.typedPos(tree.pos) {
val vsym = vdef.symbol
- atPhase(phase.next) {
+ afterRefchecks {
val rhs = gen.newModule(sym, vsym.tpe)
val body = if (sym.owner.isTrait) rhs else gen.mkAssignAndReturn(vsym, rhs)
DefDef(sym, body.changeOwner(vsym -> sym))
@@ -1214,12 +1225,12 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
else gen.mkAssignAndReturn(vsym, rhs)
)
val lazyDef = atPos(tree.pos)(DefDef(lazySym, body.changeOwner(vsym -> lazySym)))
- log("Made lazy def: " + lazyDef)
+ debuglog("Created lazy accessor: " + lazyDef)
if (hasUnitType) List(typed(lazyDef))
else List(
typed(ValDef(vsym)),
- atPhase(phase.next)(typed(lazyDef))
+ afterRefchecks(typed(lazyDef))
)
}
@@ -1443,26 +1454,6 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
transform(qual)
- case Apply(Select(New(tpt), name), args)
- if (tpt.tpe.typeSymbol == ArrayClass && args.length >= 2) =>
- unit.deprecationWarning(tree.pos,
- "new Array(...) with multiple dimensions has been deprecated; use Array.ofDim(...) instead")
- val manif = {
- var etpe = tpt.tpe
- for (_ <- args) { etpe = etpe.typeArgs.headOption.getOrElse(NoType) }
- if (etpe == NoType) {
- unit.error(tree.pos, "too many dimensions for array creation")
- Literal(Constant(null))
- } else {
- localTyper.getManifestTree(tree, etpe, false)
- }
- }
- val newResult = localTyper.typedPos(tree.pos) {
- new ApplyToImplicitArgs(gen.mkMethodCall(ArrayModule, nme.ofDim, args), List(manif))
- }
- currentApplication = tree
- newResult
-
case Apply(fn, args) =>
checkSensible(tree.pos, fn, args)
currentApplication = tree
@@ -1531,17 +1522,17 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
)
case _ => ()
}
-
+
// verify classes extending AnyVal meet the requirements
// (whatever those are to be, but at least: @inline annotation)
private def checkAnyValSubclass(clazz: Symbol) = {
if ((clazz isSubClass AnyValClass) && (clazz ne AnyValClass) && !isPrimitiveValueClass(clazz)) {
- if (!clazz.hasAnnotation(ScalaInlineClass))
- unit.error(clazz.pos, "Only @inline classes are allowed to extend AnyVal")
if (clazz.isTrait)
- unit.error(clazz.pos, "Only @inline classes (not traits) are allowed to extend AnyVal")
+ unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal")
+ /* [Martin] That one is already taken care of by Typers
if (clazz.tpe <:< AnyRefClass.tpe)
unit.error(clazz.pos, "Classes which extend AnyVal may not have an ancestor which inherits AnyRef")
+ */
}
}
@@ -1556,12 +1547,9 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
// inside annotations.
applyRefchecksToAnnotations(tree)
var result: Tree = tree match {
- case DefDef(mods, name, tparams, vparams, tpt, EmptyTree) if tree.symbol.hasAnnotation(NativeAttr) =>
- tree.symbol.resetFlag(DEFERRED)
- transform(treeCopy.DefDef(
- tree, mods, name, tparams, vparams, tpt,
- typed(gen.mkSysErrorCall("native method stub"))
- ))
+ case DefDef(_, _, _, _, _, EmptyTree) if sym hasAnnotation NativeAttr =>
+ sym resetFlag DEFERRED
+ transform(deriveDefDef(tree)(_ => typed(gen.mkSysErrorCall("native method stub"))))
case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) =>
checkDeprecatedOvers(tree)
@@ -1580,9 +1568,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
val bridges = addVarargBridges(currentOwner)
checkAllOverrides(currentOwner)
checkAnyValSubclass(currentOwner)
-
- if (bridges.nonEmpty) treeCopy.Template(tree, parents, self, body ::: bridges)
- else tree
+ if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree
case dc@TypeTreeWithDeferredRefCheck() => assert(false, "adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc"); dc
case tpt@TypeTree() =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index 0ab09b4fec..243e685b13 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -24,7 +24,7 @@ import symtab.Flags._
*/
abstract class SuperAccessors extends transform.Transform with transform.TypingTransformers {
import global._
- import definitions.{ UnitClass, isRepeatedParamType, isByNameParamType, Any_asInstanceOf }
+ import definitions.{ UnitClass, ObjectClass, isRepeatedParamType, isByNameParamType, Any_asInstanceOf }
import analyzer.{ restrictionError }
/** the following two members override abstract members in Transform */
@@ -34,6 +34,12 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
new SuperAccTransformer(unit)
class SuperAccTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
+ /** validCurrentOwner arrives undocumented, but I reverse engineer it to be
+ * a flag for needsProtectedAccessor which is false while transforming either
+ * a by-name argument block or a closure. This excludes them from being
+ * considered able to access protected members via subclassing (why?) which in turn
+ * increases the frequency with which needsProtectedAccessor will be true.
+ */
private var validCurrentOwner = true
private val accDefs = mutable.Map[Symbol, ListBuffer[Tree]]()
@@ -41,6 +47,25 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val buf = accDefs.getOrElse(clazz, sys.error("no acc def buf for "+clazz))
buf += typers(clazz) typed tree
}
+ private def ensureAccessor(sel: Select) = {
+ val Select(qual, name) = sel
+ val sym = sel.symbol
+ val clazz = qual.symbol
+ val supername = nme.superName(name)
+ val superAcc = clazz.info.decl(supername).suchThat(_.alias == sym) orElse {
+ debuglog("add super acc " + sym + sym.locationString + " to `" + clazz);//debug
+ val acc = clazz.newMethod(supername, sel.pos, SUPERACCESSOR | PRIVATE) setAlias sym
+ val tpe = clazz.thisType memberType sym match {
+ case t if sym.isModule && !sym.isMethod => NullaryMethodType(t)
+ case t => t
+ }
+ acc setInfoAndEnter (tpe cloneInfo acc)
+ storeAccessorDefinition(clazz, DefDef(acc, EmptyTree))
+ acc
+ }
+
+ atPos(sel.pos)(Select(gen.mkAttributedThis(clazz), superAcc) setType sel.tpe)
+ }
private def transformArgs(params: List[Symbol], args: List[Tree]) = {
treeInfo.mapMethodParamsAndArgs(params, args) { (param, arg) =>
@@ -88,42 +113,21 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
}
}
- private def transformSuperSelect(tree: Tree): Tree = tree match {
- case Select(sup @ Super(_, mix), name) =>
- val sym = tree.symbol
- val clazz = sup.symbol
-
- if (sym.isDeferred) {
- val member = sym.overridingSymbol(clazz);
- if (mix != tpnme.EMPTY || member == NoSymbol ||
- !((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)))
- unit.error(tree.pos, ""+sym+sym.locationString+" is accessed from super. It may not be abstract "+
- "unless it is overridden by a member declared `abstract' and `override'");
- }
- if (tree.isTerm && mix == tpnme.EMPTY &&
- (clazz.isTrait || clazz != currentOwner.enclClass || !validCurrentOwner)) {
- val supername = nme.superName(sym.name)
- var superAcc = clazz.info.decl(supername).suchThat(_.alias == sym)
- if (superAcc == NoSymbol) {
- debuglog("add super acc " + sym + sym.locationString + " to `" + clazz);//debug
- superAcc = clazz.newMethod(supername, tree.pos, SUPERACCESSOR | PRIVATE) setAlias sym
- var superAccTpe = clazz.thisType.memberType(sym)
- if (sym.isModule && !sym.isMethod) {
- // the super accessor always needs to be a method. See #231
- superAccTpe = NullaryMethodType(superAccTpe)
- }
- superAcc setInfoAndEnter (superAccTpe cloneInfo superAcc)
- storeAccessorDefinition(clazz, DefDef(superAcc, EmptyTree))
- }
- atPos(sup.pos) {
- Select(gen.mkAttributedThis(clazz), superAcc) setType tree.tpe;
- }
- } else {
- tree
- }
- case _ =>
- assert(tree.tpe.isError, tree)
- tree
+ private def transformSuperSelect(sel: Select): Tree = {
+ val Select(sup @ Super(_, mix), name) = sel
+ val sym = sel.symbol
+ val clazz = sup.symbol
+
+ if (sym.isDeferred) {
+ val member = sym.overridingSymbol(clazz);
+ if (mix != tpnme.EMPTY || member == NoSymbol ||
+ !((member hasFlag ABSOVERRIDE) && 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'");
+ }
+ if (name.isTermName && mix == tpnme.EMPTY && (clazz.isTrait || clazz != currentClass || !validCurrentOwner))
+ ensureAccessor(sel)
+ else sel
}
// Disallow some super.XX calls targeting Any methods which would
@@ -156,9 +160,11 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
for (s <- decls) {
if (s.privateWithin.isClass && !s.isProtected && !s.privateWithin.isModuleClass &&
!s.hasFlag(EXPANDEDNAME) && !s.isConstructor) {
+ val savedName = s.name
decls.unlink(s)
s.expandName(s.privateWithin)
decls.enter(s)
+ log("Expanded '%s' to '%s' in %s".format(savedName, s.name, sym))
}
}
if (settings.verbose.value && forScaladoc && !sym.isAnonymousClass) {
@@ -177,7 +183,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
case ModuleDef(_, _, _) =>
checkCompanionNameClashes(sym)
super.transform(tree)
- case Template(parents, self, body) =>
+ case Template(_, _, body) =>
val ownAccDefs = new ListBuffer[Tree]
accDefs(currentOwner) = ownAccDefs
@@ -189,7 +195,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val body1 = atOwner(currentOwner)(transformTrees(body))
accDefs -= currentOwner
ownAccDefs ++= body1
- treeCopy.Template(tree, parents, self, ownAccDefs.toList)
+ deriveTemplate(tree)(_ => ownAccDefs.toList)
case TypeApply(sel @ Select(This(_), name), args) =>
mayNeedProtectedAccessor(sel, args, false)
@@ -218,24 +224,47 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
// direct calls to aliases of param accessors to the superclass in order to avoid
// duplicating fields.
if (sym.isParamAccessor && sym.alias != NoSymbol) {
- val result = localTyper.typed {
- Select(
- Super(qual, tpnme.EMPTY/*qual.symbol.info.parents.head.symbol.name*/) setPos qual.pos,
- sym.alias) setPos tree.pos
- }
+ val result = (localTyper.typedPos(tree.pos) {
+ Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias)
+ }).asInstanceOf[Select]
debuglog("alias replacement: " + tree + " ==> " + result);//debug
localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, true))
}
- else mayNeedProtectedAccessor(sel, List(EmptyTree), false)
+ else {
+ /** A trait which extends a class and accesses a protected member
+ * of that class cannot implement the necessary accessor method
+ * because its implementation is in an implementation class (e.g.
+ * Foo$class) which inherits nothing, and jvm access restrictions
+ * require the call site to be in an actual subclass. So non-trait
+ * classes inspect their ancestors for any such situations and
+ * generate the accessors. See SI-2296.
+ */
+ // FIXME - this should be unified with needsProtectedAccessor, but some
+ // subtlety which presently eludes me is foiling my attempts.
+ val shouldEnsureAccessor = (
+ currentClass.isTrait
+ && sym.isProtected
+ && sym.enclClass != currentClass
+ && !sym.owner.isTrait
+ && (sym.owner.enclosingPackageClass != currentPackage)
+ && (qual.symbol.info.member(sym.name) ne NoSymbol)
+ )
+ if (shouldEnsureAccessor) {
+ log("Ensuring accessor for call to protected " + sym.fullLocationString + " from " + currentClass)
+ ensureAccessor(sel)
+ }
+ else
+ mayNeedProtectedAccessor(sel, List(EmptyTree), false)
+ }
- case Select(Super(_, mix), name) =>
+ case sel @ Select(Super(_, mix), name) =>
if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) {
unit.error(tree.pos, "super may be not be used on "+ sym.accessedOrSelf)
}
else if (isDisallowed(sym)) {
unit.error(tree.pos, "super not allowed here: use this." + name.decode + " instead")
}
- transformSuperSelect(tree)
+ transformSuperSelect(sel)
case TypeApply(sel @ Select(qual, name), args) =>
mayNeedProtectedAccessor(sel, args, true)
@@ -280,11 +309,10 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
}
private def withInvalidOwner[A](trans: => A): A = {
- val prevValidCurrentOwner = validCurrentOwner
+ val saved = validCurrentOwner
validCurrentOwner = false
- val result = trans
- validCurrentOwner = prevValidCurrentOwner
- result
+ try trans
+ finally validCurrentOwner = saved
}
/** Add a protected accessor, if needed, and return a tree that calls
@@ -294,7 +322,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
private def makeAccessor(tree: Select, targs: List[Tree]): Tree = {
val Select(qual, name) = tree
val sym = tree.symbol
- val clazz = hostForAccessorOf(sym, currentOwner.enclClass)
+ val clazz = hostForAccessorOf(sym, currentClass)
assert(clazz != NoSymbol, sym)
debuglog("Decided for host class: " + clazz)
@@ -334,7 +362,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
}
val selection = Select(This(clazz), protAcc)
def mkApply(fn: Tree) = Apply(fn, qual :: Nil)
- val res = atPos(tree.pos) {
+ val res = atPos(tree.pos) {
targs.head match {
case EmptyTree => mkApply(selection)
case _ => mkApply(TypeApply(selection, targs))
@@ -373,27 +401,27 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
*/
private def makeSetter(tree: Select): Tree = {
val field = tree.symbol
- val clazz = hostForAccessorOf(field, currentOwner.enclClass)
+ val clazz = hostForAccessorOf(field, currentClass)
assert(clazz != NoSymbol, field)
debuglog("Decided for host class: " + clazz)
-
+
val accName = nme.protSetterName(field.originalName)
val protectedAccessor = clazz.info decl accName orElse {
val protAcc = clazz.newMethod(accName, field.pos)
val paramTypes = List(clazz.typeOfThis, field.tpe)
val params = protAcc newSyntheticValueParams paramTypes
val accessorType = MethodType(params, UnitClass.tpe)
-
+
protAcc setInfoAndEnter accessorType
val obj :: value :: Nil = params
storeAccessorDefinition(clazz, DefDef(protAcc, Assign(Select(Ident(obj), field.name), Ident(value))))
-
+
protAcc
}
atPos(tree.pos)(Select(This(clazz), protectedAccessor))
}
- /** Does `sym` need an accessor when accessed from `currentOwner`?
+ /** Does `sym` need an accessor when accessed from `currentClass`?
* A special case arises for classes with explicit self-types. If the
* self type is a Java class, and a protected accessor is needed, we issue
* an error. If the self type is a Scala class, we don't add an accessor.
@@ -407,23 +435,20 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
* classes, this has to be signaled as error.
*/
private def needsProtectedAccessor(sym: Symbol, pos: Position): Boolean = {
- val clazz = currentOwner.enclClass
+ val clazz = currentClass
def accessibleThroughSubclassing =
validCurrentOwner && clazz.thisSym.isSubClass(sym.owner) && !clazz.isTrait
- def packageAccessBoundry(sym: Symbol) = {
- val b = sym.accessBoundary(sym.owner)
- if (b.isPackageClass) b
- else b.enclosingPackageClass
- }
+ def packageAccessBoundry(sym: Symbol) =
+ sym.accessBoundary(sym.enclosingPackageClass)
val isCandidate = (
sym.isProtected
&& sym.isJavaDefined
&& !sym.isDefinedInPackage
&& !accessibleThroughSubclassing
- && (sym.owner.enclosingPackageClass != currentOwner.enclosingPackageClass)
- && (sym.owner.enclosingPackageClass == packageAccessBoundry(sym))
+ && (sym.enclosingPackageClass != currentPackage)
+ && (sym.enclosingPackageClass == sym.accessBoundary(sym.enclosingPackageClass))
)
val host = hostForAccessorOf(sym, clazz)
def isSelfType = !(host.tpe <:< host.typeOfThis) && {
@@ -433,15 +458,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
)
true
}
- def isJavaProtected = host.isTrait && sym.isJavaDefined && {
- restrictionError(pos, unit,
- """|%s accesses protected %s inside a concrete trait method.
- |Add an accessor in a class extending %s as a workaround.""".stripMargin.format(
- clazz, sym, sym.enclClass)
- )
- true
- }
- isCandidate && !host.isPackageClass && !isSelfType && !isJavaProtected
+ isCandidate && !host.isPackageClass && !isSelfType
}
/** Return the innermost enclosing class C of referencingClass for which either
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index 3ee5bf601d..def6475221 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -126,8 +126,8 @@ trait SyntheticMethods extends ast.TreeDSL {
* def canEqual(that: Any) = that.isInstanceOf[This]
*/
def canEqualMethod: Tree = (
- createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m =>
- Ident(m.firstParam) IS_OBJ typeCaseType(clazz))
+ createMethod(nme.canEqual_, List(AnyClass.tpe), BooleanClass.tpe)(m =>
+ Ident(m.firstParam) IS_OBJ classExistentialType(clazz))
)
/** The equality method for case classes.
@@ -143,7 +143,7 @@ trait SyntheticMethods extends ast.TreeDSL {
*/
def equalsClassMethod: Tree = createMethod(nme.equals_, List(AnyClass.tpe), BooleanClass.tpe) { m =>
val arg0 = Ident(m.firstParam)
- val thatTest = gen.mkIsInstanceOf(arg0, typeCaseType(clazz), true, false)
+ val thatTest = gen.mkIsInstanceOf(arg0, classExistentialType(clazz), true, false)
val thatCast = gen.mkCast(arg0, clazz.tpe)
def argsBody: Tree = {
@@ -259,11 +259,11 @@ trait SyntheticMethods extends ast.TreeDSL {
}
if (phase.id > currentRun.typerPhase.id) templ
- else treeCopy.Template(templ, templ.parents, templ.self,
+ else deriveTemplate(templ)(body =>
if (clazz.isCase) caseTemplateBody()
else synthesize() match {
- case Nil => templ.body // avoiding unnecessary copy
- case ms => templ.body ++ ms
+ case Nil => body // avoiding unnecessary copy
+ case ms => body ++ ms
}
)
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index 4f4087a953..1434002121 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -415,7 +415,7 @@ trait TypeDiagnostics {
"\nIf applicable, you may wish to try moving some members into another object."
)
}
-
+
/** Report a type error.
*
* @param pos0 The position where to report the error
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 66330d4321..24ec0c8028 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -604,6 +604,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
* 1. Check that non-function pattern expressions are stable
* 2. Check that packages and static modules are not used as values
* 3. Turn tree type into stable type if possible and required by context.
+ * 4. Give getClass calls a more precise type based on the type of the target of the call.
*/
private def stabilize(tree: Tree, pre: Type, mode: Int, pt: Type): Tree = {
if (tree.symbol.isOverloaded && !inFunMode(mode))
@@ -614,7 +615,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (tree.isErrorTyped) tree
else if ((mode & (PATTERNmode | FUNmode)) == PATTERNmode && tree.isTerm) { // (1)
- if (sym.isValue) checkStable(tree)
+ if (sym.isValue) {
+ val tree1 = checkStable(tree)
+ // A module reference in a pattern has type Foo.type, not "object Foo"
+ if (sym.isModule && !sym.isMethod) tree1 setType singleType(pre, sym)
+ else tree1
+ }
else fail()
} else if ((mode & (EXPRmode | QUALmode)) == EXPRmode && !sym.isValue && !phase.erasedTypes) { // (2)
fail()
@@ -622,7 +628,18 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (sym.isStable && pre.isStable && !isByNameParamType(tree.tpe) &&
(isStableContext(tree, mode, pt) || sym.isModule && !sym.isMethod))
tree.setType(singleType(pre, sym))
- else tree
+ // To fully benefit from special casing the return type of
+ // getClass, we have to catch it immediately so expressions
+ // like x.getClass().newInstance() are typed with the type of x.
+ else if ( tree.symbol.name == nme.getClass_
+ && tree.tpe.params.isEmpty
+ // TODO: If the type of the qualifier is inaccessible, we can cause private types
+ // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this
+ // so for now it requires the type symbol be public.
+ && pre.typeSymbol.isPublic)
+ tree setType MethodType(Nil, erasure.getClassReturnType(pre))
+ else
+ tree
}
}
@@ -708,8 +725,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}
- def isCodeType(tpe: Type) = tpe.typeSymbol isNonBottomSubClass CodeClass
-
/** Perform the following adaptations of expression, pattern or type `tree` wrt to
* given mode `mode` and given prototype `pt`:
* (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
@@ -849,6 +864,33 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}
+ /**
+ * To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T,
+ * reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant).
+ *
+ * Consider the following example:
+ *
+ * class AbsWrapperCov[+A]
+ * case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B]
+ *
+ * def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match {
+ * case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it
+ * wrapped // : Wrapped[_ <: T]
+ * }
+ *
+ * this method should type check if and only if Wrapped is covariant in its type parameter
+ *
+ * when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T],
+ * we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T}
+ * as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound
+ *
+ * since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?),
+ * we can simply replace skolems that represent method type parameters as seen from the method's body
+ * by other skolems that are (upper/lower)-bounded by that type-parameter skolem
+ * (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt)
+ *
+ * see test/files/../t5189*.scala
+ */
def adaptConstrPattern(): Tree = { // (5)
val extractor = tree.symbol.filter(sym => reallyExists(unapplyMember(sym.tpe)))
if (extractor != NoSymbol) {
@@ -862,7 +904,32 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner))
.setOriginal(tree)
- inferConstructorInstance(tree1, clazz.typeParams, pt)
+ val skolems = new mutable.ListBuffer[TypeSymbol]
+ object variantToSkolem extends VariantTypeMap {
+ def apply(tp: Type) = mapOver(tp) match {
+ case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm =>
+ val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe)
+ val skolem = context.owner.newExistentialSkolem(tpSym, tpSym, unit.freshTypeName("?"+tpSym.name), bounds)
+ // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt)
+ skolems += skolem
+ skolem.tpe
+ case tp1 => tp1
+ }
+ }
+
+ // have to open up the existential and put the skolems in scope
+ // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance)
+ val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ?
+ val freeVars = skolems.toList
+
+ // use "tree" for the context, not context.tree: don't make another CaseDef context,
+ // as instantiateTypeVar's bounds would end up there
+ val ctorContext = context.makeNewScope(tree, context.owner)
+ freeVars foreach ctorContext.scope.enter
+ newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe)
+
+ // tree1's type-slack skolems will be deskolemized (to the method type parameter skolems)
+ // once the containing CaseDef has been type checked (see typedCase)
tree1
} else {
tree
@@ -943,10 +1010,14 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
if (tree.isType)
adaptType()
- else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef) {
- val tree1 = expandMacro(tree)
- if (tree1.isErroneous) tree1 else typed(tree1, mode, pt)
- } else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode))
+ else if (inExprModeButNot(mode, FUNmode) && tree.symbol != null && tree.symbol.isMacro && !tree.isDef && !(tree exists (_.isErroneous)))
+ macroExpand(tree, this) match {
+ case Some(expanded: Tree) =>
+ typed(expanded, mode, pt)
+ case None =>
+ setError(tree) // error already reported
+ }
+ else if ((mode & (PATTERNmode | FUNmode)) == (PATTERNmode | FUNmode))
adaptConstrPattern()
else if (inAllModes(mode, EXPRmode | FUNmode) &&
!tree.tpe.isInstanceOf[MethodType] &&
@@ -1410,7 +1481,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def typedClassDef(cdef: ClassDef): Tree = {
// attributes(cdef)
val clazz = cdef.symbol
- val typedMods = removeAnnotations(cdef.mods)
+ val typedMods = typedModifiers(cdef.mods)
assert(clazz != NoSymbol, cdef)
reenterTypeParams(cdef.tparams)
val tparams1 = cdef.tparams mapConserve (typedTypeDef)
@@ -1451,7 +1522,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
linkedClass.info.decl(nme.CONSTRUCTOR).alternatives foreach (_.initialize)
val clazz = mdef.symbol.moduleClass
- val typedMods = removeAnnotations(mdef.mods)
+ val typedMods = typedModifiers(mdef.mods)
assert(clazz != NoSymbol, mdef)
val impl1 = typerReportAnyContextErrors(context.make(mdef.impl, clazz, newScope)) {
_.typedTemplate(mdef.impl, {
@@ -1509,12 +1580,12 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (templ.symbol == NoSymbol)
templ setSymbol clazz.newLocalDummy(templ.pos)
val self1 = templ.self match {
- case vd @ ValDef(mods, name, tpt, EmptyTree) =>
+ case vd @ ValDef(_, _, tpt, EmptyTree) =>
val tpt1 = checkNoEscaping.privates(
clazz.thisSym,
treeCopy.TypeTree(tpt).setOriginal(tpt) setType vd.symbol.tpe
)
- treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType
+ copyValDef(vd)(tpt = tpt1, rhs = EmptyTree) setType NoType
}
// was:
// val tpt1 = checkNoEscaping.privates(clazz.thisSym, typedType(tpt))
@@ -1551,8 +1622,17 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
/** Remove definition annotations from modifiers (they have been saved
* into the symbol's ``annotations'' in the type completer / namer)
+ *
+ * However reification does need annotation definitions to proceed.
+ * Unfortunately, AnnotationInfo doesn't provide enough info to reify it in general case.
+ * The biggest problem is with the "atp: Type" field, which cannot be reified in some situations
+ * that involve locally defined annotations. See more about that in Reifiers.scala.
+ *
+ * That's why the original tree gets saved into ``original'' field of AnnotationInfo (happens elsewhere).
+ * The field doesn't get pickled/unpickled and exists only during a single compilation run.
+ * This simultaneously allows us to reify annotations and to preserve backward compatibility.
*/
- def removeAnnotations(mods: Modifiers): Modifiers =
+ def typedModifiers(mods: Modifiers): Modifiers =
mods.copy(annotations = Nil) setPositions mods.positions
/**
@@ -1563,7 +1643,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// attributes(vdef)
val sym = vdef.symbol.initialize
val typer1 = constrTyperIf(sym.isParameter && sym.owner.isConstructor)
- val typedMods = removeAnnotations(vdef.mods)
+ val typedMods = typedModifiers(vdef.mods)
// complete lazy annotations
val annots = sym.annotations
@@ -1774,7 +1854,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
var tpt1 = checkNoEscaping.privates(meth, typedType(ddef.tpt))
checkNonCyclic(ddef, tpt1)
ddef.tpt.setType(tpt1.tpe)
- val typedMods = removeAnnotations(ddef.mods)
+ val typedMods = typedModifiers(ddef.mods)
var rhs1 =
if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasStaticFlag) { // need this to make it possible to generate static ctors
if (!meth.isPrimaryConstructor &&
@@ -1827,7 +1907,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
tdef.symbol.initialize
reenterTypeParams(tdef.tparams)
val tparams1 = tdef.tparams mapConserve typedTypeDef
- val typedMods = removeAnnotations(tdef.mods)
+ val typedMods = typedModifiers(tdef.mods)
// complete lazy annotations
val annots = tdef.symbol.annotations
@@ -1862,8 +1942,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val restpe = ldef.symbol.tpe.resultType
val rhs1 = typed(ldef.rhs, restpe)
ldef.params foreach (param => param.tpe = param.symbol.tpe)
- treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe
- } else {
+ deriveLabelDef(ldef)(_ => rhs1) setType restpe
+ }
+ else {
val initpe = ldef.symbol.tpe.resultType
val rhs1 = typed(ldef.rhs)
val restpe = rhs1.tpe
@@ -1876,7 +1957,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(List(), restpe))
val rhs2 = typed(resetAllAttrs(ldef.rhs), restpe)
ldef.params foreach (param => param.tpe = param.symbol.tpe)
- treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs2) setSymbol sym2 setType restpe
+ deriveLabelDef(ldef)(_ => rhs2) setSymbol sym2 setType restpe
}
}
}
@@ -1981,18 +2062,38 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree
else typed(cdef.guard, BooleanClass.tpe)
var body1: Tree = typed(cdef.body, pt)
- if (!context.savedTypeBounds.isEmpty) {
- body1.tpe = context.restoreTypeBounds(body1.tpe)
- if (isFullyDefined(pt) && !(body1.tpe <:< pt)) {
- // @M no need for pt.normalize here, is done in erasure
+
+ val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef])
+ if (contextWithTypeBounds.savedTypeBounds nonEmpty) {
+ body1.tpe = contextWithTypeBounds restoreTypeBounds body1.tpe
+
+ // insert a cast if something typechecked under the GADT constraints,
+ // but not in real life (i.e., now that's we've reset the method's type skolems'
+ // infos back to their pre-GADT-constraint state)
+ if (isFullyDefined(pt) && !(body1.tpe <:< pt))
body1 = typedPos(body1.pos)(gen.mkCast(body1, pt))
- }
+
}
+
// body1 = checkNoEscaping.locals(context.scope, pt, body1)
- treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
+ val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
+
+ // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher
+ // TODO: Paul, can we do the deskolemization lazily in the old pattern matcher
+ object deskolemizeOnce extends TypeMap {
+ def apply(tp: Type): Type = mapOver(tp) match {
+ case TypeRef(pre, sym, args) if sym.isExistentialSkolem && sym.deSkolemize.isSkolem && sym.deSkolemize.owner.isTerm =>
+ typeRef(NoPrefix, sym.deSkolemize, args)
+ case tp1 => tp1
+ }
+ }
+
+ new TypeMapTreeSubstituter(deskolemizeOnce).traverse(treeWithSkolems)
+
+ treeWithSkolems // now without skolems, actually
}
- def typedCases(tree: Tree, cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] =
+ def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] =
cases mapConserve { cdef =>
newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt)
}
@@ -2005,8 +2106,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
*/
def typedFunction(fun: Function, mode: Int, pt: Type): Tree = {
val numVparams = fun.vparams.length
- val codeExpected = !forMSIL && (pt.typeSymbol isNonBottomSubClass CodeClass)
-
if (numVparams > definitions.MaxFunctionArity)
return MaxFunctionArityError(fun)
@@ -2023,7 +2122,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
else
(FunctionClass(numVparams), fun.vparams map (x => NoType), WildcardType)
- val (clazz, argpts, respt) = decompose(if (codeExpected) pt.normalize.typeArgs.head else pt)
+ val (clazz, argpts, respt) = decompose(pt)
if (argpts.lengthCompare(numVparams) != 0)
WrongNumberOfParametersError(fun, argpts)
else {
@@ -2033,7 +2132,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (isFullyDefined(argpt)) argpt
else {
fun match {
- case etaExpansion(vparams, fn, args) if !codeExpected =>
+ case etaExpansion(vparams, fn, args) =>
silent(_.typed(fn, forFunMode(mode), pt)) match {
case SilentResultValue(fn1) if context.undetparams.isEmpty =>
// if context,undetparams is not empty, the function was polymorphic,
@@ -2065,13 +2164,8 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
val restpe = packedType(body1, fun.symbol).deconst.resultType
val funtpe = typeRef(clazz.tpe.prefix, clazz, formals :+ restpe)
// body = checkNoEscaping.locals(context.scope, restpe, body)
- val fun1 = treeCopy.Function(fun, vparams, body1).setType(funtpe)
- if (codeExpected) lifted(fun1) else fun1
- }
+ treeCopy.Function(fun, vparams, body1).setType(funtpe)
}
-
- def lifted(tree: Tree): Tree = typedPos(tree.pos) {
- Apply(Select(Ident(CodeModule), nme.lift_), List(tree))
}
def typedRefinement(stats: List[Tree]) {
@@ -2181,6 +2275,10 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
// error for this is issued in RefChecks.checkDefaultsInOverloaded
if (!e.sym.isErroneous && !e1.sym.isErroneous && !e.sym.hasDefaultFlag &&
!e.sym.hasAnnotation(BridgeClass) && !e1.sym.hasAnnotation(BridgeClass)) {
+ log("Double definition detected:\n " +
+ ((e.sym.getClass, e.sym.info, e.sym.ownerChain)) + "\n " +
+ ((e1.sym.getClass, e1.sym.info, e1.sym.ownerChain)))
+
DefDefinedTwiceError(e.sym, e1.sym)
scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779
}
@@ -2794,7 +2892,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
if (hasError) annotationError
- else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setPos(ann.pos)
+ else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(ann).setPos(ann.pos)
}
} else if (requireJava) {
reportAnnotationError(NestedAnnotationError(ann, annType))
@@ -2834,7 +2932,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def annInfo(t: Tree): AnnotationInfo = t match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
- AnnotationInfo(annType, args, List()).setPos(t.pos)
+ AnnotationInfo(annType, args, List()).setOriginal(ann).setPos(t.pos)
case Block(stats, expr) =>
context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+
@@ -2869,6 +2967,33 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type?
sym.isTypeParameter && sym.owner.isJavaDefined
+ /** If we map a set of hidden symbols to their existential bounds, we
+ * have a problem: the bounds may themselves contain references to the
+ * hidden symbols. So this recursively calls existentialBound until
+ * the typeSymbol is not amongst the symbols being hidden.
+ */
+ def existentialBoundsExcludingHidden(hidden: List[Symbol]): Map[Symbol, Type] = {
+ def safeBound(t: Type): Type =
+ if (hidden contains t.typeSymbol) safeBound(t.typeSymbol.existentialBound.bounds.hi) else t
+
+ def hiBound(s: Symbol): Type = safeBound(s.existentialBound.bounds.hi) match {
+ case tp @ RefinedType(parents, decls) =>
+ val parents1 = parents mapConserve safeBound
+ if (parents eq parents1) tp
+ else copyRefinedType(tp, parents1, decls)
+ case tp => tp
+ }
+
+ (hidden map { s =>
+ // Hanging onto lower bound in case anything interesting
+ // happens with it.
+ (s, s.existentialBound match {
+ case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s))
+ case _ => hiBound(s)
+ })
+ }).toMap
+ }
+
/** Given a set `rawSyms` of term- and type-symbols, and a type
* `tp`, produce a set of fresh type parameters and a type so that
* it can be abstracted to an existential type. Every type symbol
@@ -2886,12 +3011,13 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
* only the type of the Ident is changed.
*/
protected def existentialTransform[T](rawSyms: List[Symbol], tp: Type)(creator: (List[Symbol], Type) => T): T = {
+ val allBounds = existentialBoundsExcludingHidden(rawSyms)
val typeParams: List[Symbol] = rawSyms map { sym =>
val name = sym.name match {
case x: TypeName => x
- case x => newTypeName(x + ".type")
+ case x => nme.singletonName(x)
}
- val bound = sym.existentialBound
+ val bound = allBounds(sym)
val sowner = if (isRawParameter(sym)) context.owner else sym.owner
val quantified = sowner.newExistential(name, sym.pos)
@@ -3015,40 +3141,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
packSymbols(localSyms.toList, normalizedTpe)
}
- /** Replace type parameters with their TypeSkolems, which can later
- * be deskolemized to the original type param. (A skolem is a
- * representation of a bound variable when viewed inside its scope)
- * !!!Adriaan: this does not work for hk types.
- */
- def skolemizeTypeParams(tparams: List[TypeDef]): List[TypeDef] = {
- class Deskolemizer extends LazyType {
- override val typeParams = tparams map (_.symbol)
- val typeSkolems = typeParams map (_.newTypeSkolem setInfo this)
- // Replace the symbols
- def substitute() = map2(tparams, typeSkolems)(_ setSymbol _)
- override def complete(sym: Symbol) {
- // The info of a skolem is the skolemized info of the
- // actual type parameter of the skolem
- sym setInfo sym.deSkolemize.info.substSym(typeParams, typeSkolems)
- }
- }
- (new Deskolemizer).substitute()
- }
- /** Convert to corresponding type parameters all skolems of method
- * parameters which appear in `tparams`.
- */
- def deskolemizeTypeParams(tparams: List[Symbol])(tp: Type): Type = {
- class DeSkolemizeMap extends TypeMap {
- def apply(tp: Type): Type = tp match {
- case TypeRef(pre, sym, args) if sym.isTypeSkolem && (tparams contains sym.deSkolemize) =>
- mapOver(typeRef(NoPrefix, sym.deSkolemize, args))
- case _ =>
- mapOver(tp)
- }
- }
- new DeSkolemizeMap mapOver tp
- }
-
def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) =
if (!checkClassType(tpt, true, false) && noGen) tpt
else atPos(tree.pos)(gen.mkClassOf(tpt.tpe))
@@ -3305,7 +3397,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
typed1(atPos(tree.pos) { Function(params, body) }, mode, pt)
} else {
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
- var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt)
+ var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
if (isPastTyper || !opt.virtPatmat) {
val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
@@ -3321,7 +3413,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
(MatchTranslator(this)).translateMatch(selector1, cases1, owntype) match {
case Block(vd :: Nil, tree@Match(selector, cases)) =>
val selector1 = checkDead(typed(selector, EXPRmode | BYVALmode, WildcardType))
- var cases1 = typedCases(tree, cases, packCaptured(selector1.tpe.widen), pt)
+ var cases1 = typedCases(cases, packCaptured(selector1.tpe.widen), pt)
val (owntype, needAdapt) = ptOrLub(cases1 map (_.tpe))
if (needAdapt)
cases1 = cases1 map (adaptCase(_, owntype))
@@ -3657,8 +3749,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (ps.isEmpty)
ps = site.parents filter (_.typeSymbol.toInterface.name == mix)
if (ps.isEmpty) {
- if (settings.debug.value)
- Console.println(site.parents map (_.typeSymbol.name))//debug
+ debuglog("Fatal: couldn't find site " + site + " in " + site.parents.map(_.typeSymbol.name))
if (phase.erasedTypes && context.enclClass.owner.isImplClass) {
// println(qual1)
// println(clazz)
@@ -3733,7 +3824,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
return typed(treeCopy.Select(tree, qual1, name), mode, pt)
}
if (!reallyExists(sym)) {
- if (context.owner.toplevelClass.isJavaDefined && name.isTypeName) {
+ if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) {
val tree1 = atPos(tree.pos) { gen.convertToSelectFromType(qual, name) }
if (tree1 != EmptyTree) return typed1(tree1, mode, pt)
}
@@ -3805,7 +3896,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
if (settings.warnSelectNullable.value && isPotentialNullDeference && unit != null)
unit.warning(tree.pos, "potential null pointer dereference: "+tree)
- val selection = result match {
+ result match {
// could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual?
case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs nonEmpty => // TODO: somehow the new qual is not checked in refchecks
treeCopy.SelectFromTypeTree(
@@ -3827,22 +3918,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case _ =>
result
}
- // To fully benefit from special casing the return type of
- // getClass, we have to catch it immediately so expressions
- // like x.getClass().newInstance() are typed with the type of x.
- val isRefinableGetClass = (
- !selection.isErrorTyped
- && selection.symbol.name == nme.getClass_
- && selection.tpe.params.isEmpty
- // TODO: If the type of the qualifier is inaccessible, we can cause private types
- // to escape scope here, e.g. pos/t1107. I'm not sure how to properly handle this
- // so for now it requires the type symbol be public.
- && qual.tpe.typeSymbol.isPublic
- )
- if (isRefinableGetClass)
- selection setType MethodType(Nil, getClassReturnType(qual.tpe))
- else
- selection
}
}
@@ -4120,7 +4195,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
def adaptCase(cdef: CaseDef, tpe: Type): CaseDef =
- treeCopy.CaseDef(cdef, cdef.pat, cdef.guard, adapt(cdef.body, mode, tpe))
+ deriveCaseDef(cdef)(adapt(_, mode, tpe))
// begin typed1
val sym: Symbol = tree.symbol
@@ -4224,7 +4299,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case Try(block, catches, finalizer) =>
var block1 = typed(block, pt)
- var catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt)
+ var catches1 = typedCases(catches, ThrowableClass.tpe, pt)
val finalizer1 = if (finalizer.isEmpty) finalizer
else typed(finalizer, UnitClass.tpe)
val (owntype, needAdapt) = ptOrLub(block1.tpe :: (catches1 map (_.tpe)))
@@ -4232,6 +4307,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
block1 = adapt(block1, mode, owntype)
catches1 = catches1 map (adaptCase(_, owntype))
}
+
+ if(!isPastTyper && opt.virtPatmat) {
+ catches1 = (MatchTranslator(this)).translateTry(catches1, owntype, tree.pos)
+ }
+
treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype
case Throw(expr) =>
@@ -4518,13 +4598,6 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
}
}
- def expandMacro(tree: Tree): Tree =
- macroExpand(tree, context) match {
- case Some(t: Tree) => t
- case Some(t) => MacroExpandError(tree, t)
- case None => setError(tree) // error already reported
- }
-
def atOwner(owner: Symbol): Typer =
newTyper(context.make(context.tree, owner))
@@ -4641,7 +4714,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
case None => typed(tree, mode, pt)
}
- def findManifest(tp: Type, full: Boolean) = atPhase(currentRun.typerPhase) {
+ def findManifest(tp: Type, full: Boolean) = beforeTyper {
inferImplicit(
EmptyTree,
appliedType((if (full) FullManifestClass else PartialManifestClass).typeConstructor, List(tp)),
diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
index b8bc0946c1..cc272b7b8d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala
@@ -22,7 +22,7 @@ trait Unapplies extends ast.TreeDSL
import treeInfo.{ isRepeatedParamType, isByNameParamType }
private val unapplyParamName = nme.x_0
-
+
/** returns type list for return type of the extraction */
def unapplyTypeList(ufn: Symbol, ufntpe: Type) = {
assert(ufn.isMethod, ufn)
diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala
index 1db6c38b4d..fbe92e5d84 100755
--- a/src/compiler/scala/tools/nsc/util/DocStrings.scala
+++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala
@@ -71,13 +71,35 @@ object DocStrings {
* Every section starts with a `@` and extends to the next `@`, or
* to the end of the comment string, but excluding the final two
* characters which terminate the comment.
+ *
+ * Also take usecases into account - they need to expand until the next
+ * usecase or the end of the string, as they might include other sections
+ * of their own
*/
def tagIndex(str: String, p: Int => Boolean = (idx => true)): List[(Int, Int)] =
findAll(str, 0) (idx => str(idx) == '@' && p(idx)) match {
case List() => List()
- case idxs => idxs zip (idxs.tail ::: List(str.length - 2))
+ case idxs => {
+ val idxs2 = mergeUsecaseSections(str, idxs)
+ idxs2 zip (idxs2.tail ::: List(str.length - 2))
+ }
}
+ /**
+ * Merge sections following an usecase into the usecase comment, so they
+ * can override the parent symbol's sections
+ */
+ def mergeUsecaseSections(str: String, idxs: List[Int]): List[Int] = {
+ idxs.find(str.substring(_).startsWith("@usecase")) match {
+ case Some(firstUC) =>
+ val commentSections = idxs.take(idxs.indexOf(firstUC))
+ val usecaseSections = idxs.drop(idxs.indexOf(firstUC)).filter(str.substring(_).startsWith("@usecase"))
+ commentSections ::: usecaseSections
+ case None =>
+ idxs
+ }
+ }
+
/** Does interval `iv` start with given `tag`?
*/
def startsWithTag(str: String, section: (Int, Int), tag: String): Boolean =
diff --git a/src/compiler/scala/tools/nsc/util/ProxyReport.scala b/src/compiler/scala/tools/nsc/util/ProxyReport.scala
index 2f4f029308..4fc86c3a32 100644
--- a/src/compiler/scala/tools/nsc/util/ProxyReport.scala
+++ b/src/compiler/scala/tools/nsc/util/ProxyReport.scala
@@ -141,6 +141,6 @@ object ProxyReportRunner {
s.processArguments(args.toList.tail, true)
val g = new ProxyGlobal(s)
val run = new g.Run()
- g.atPhase(run.typerPhase.next)(g.proxyReport.generate(dir))
+ g.afterTyper(g.proxyReport.generate(dir))
}
}
diff --git a/src/compiler/scala/tools/nsc/util/Statistics.scala b/src/compiler/scala/tools/nsc/util/Statistics.scala
index f7c27dceb5..d1cdd30dd8 100644
--- a/src/compiler/scala/tools/nsc/util/Statistics.scala
+++ b/src/compiler/scala/tools/nsc/util/Statistics.scala
@@ -20,7 +20,7 @@ class Statistics extends scala.reflect.internal.util.Statistics {
val typedSelectCount = new Counter
val typerNanos = new Timer
val classReadNanos = new Timer
-
+
val failedApplyNanos = new Timer
val failedOpEqNanos = new Timer
val failedSilentNanos = new Timer
diff --git a/src/compiler/scala/tools/nsc/util/WeakHashSet.scala b/src/compiler/scala/tools/nsc/util/WeakHashSet.scala
new file mode 100644
index 0000000000..6a10422b00
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/util/WeakHashSet.scala
@@ -0,0 +1,60 @@
+package scala.tools.nsc.util
+
+import scala.collection.mutable
+import scala.collection.mutable.ArrayBuffer
+import scala.collection.mutable.Builder
+import scala.collection.mutable.SetBuilder
+import scala.runtime.AbstractFunction1
+
+/** A bare-bones implementation of a mutable `Set` that uses weak references
+ * to hold the elements.
+ *
+ * This implementation offers only add/remove/test operations,
+ * therefore it does not fulfill the contract of Scala collection sets.
+ */
+class WeakHashSet[T <: AnyRef] extends AbstractFunction1[T, Boolean] {
+ private val underlying = mutable.HashSet[WeakReferenceWithEquals[T]]()
+
+ /** Add the given element to this set. */
+ def +=(elem: T): this.type = {
+ underlying += new WeakReferenceWithEquals(elem)
+ this
+ }
+
+ /** Remove the given element from this set. */
+ def -=(elem: T): this.type = {
+ underlying -= new WeakReferenceWithEquals(elem)
+ this
+ }
+
+ /** Does the given element belong to this set? */
+ def contains(elem: T): Boolean =
+ underlying.contains(new WeakReferenceWithEquals(elem))
+
+ /** Does the given element belong to this set? */
+ def apply(elem: T): Boolean = contains(elem)
+
+ /** Return the number of elements in this set, including reclaimed elements. */
+ def size = underlying.size
+
+ /** Remove all elements in this set. */
+ def clear() = underlying.clear()
+}
+
+/** A WeakReference implementation that implements equals and hashCode by
+ * delegating to the referent.
+ */
+class WeakReferenceWithEquals[T <: AnyRef](ref: T) {
+ def get(): T = underlying.get()
+
+ override val hashCode = ref.hashCode
+
+ override def equals(other: Any): Boolean = other match {
+ case wf: WeakReferenceWithEquals[_] =>
+ underlying.get() == wf.get()
+ case _ =>
+ false
+ }
+
+ private val underlying = new java.lang.ref.WeakReference(ref)
+}