summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2009-05-25 22:00:48 +0000
committerMartin Odersky <odersky@gmail.com>2009-05-25 22:00:48 +0000
commitc5aa57c2d573f0205615db6690139e0e4b555492 (patch)
tree3041ad7966799bcc4d69a78fd2a92beef76f7afc /src/compiler
parent213285991d73a80b322c55000c69633893dccb80 (diff)
downloadscala-c5aa57c2d573f0205615db6690139e0e4b555492.tar.gz
scala-c5aa57c2d573f0205615db6690139e0e4b555492.tar.bz2
scala-c5aa57c2d573f0205615db6690139e0e4b555492.zip
new presentation compiler design
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/CompilationUnits.scala13
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala10
-rwxr-xr-xsrc/compiler/scala/tools/nsc/interactive/ContextTrees.scala76
-rwxr-xr-xsrc/compiler/scala/tools/nsc/interactive/Global.scala378
-rw-r--r--src/compiler/scala/tools/nsc/symtab/SymbolWalker.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala51
-rw-r--r--src/compiler/scala/tools/nsc/util/Position.scala21
-rw-r--r--src/compiler/scala/tools/nsc/util/WorkScheduler.scala42
9 files changed, 421 insertions, 173 deletions
diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala
index 5385a5a54f..6b60d12d77 100644
--- a/src/compiler/scala/tools/nsc/CompilationUnits.scala
+++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala
@@ -6,7 +6,7 @@
package scala.tools.nsc
-import scala.tools.nsc.util.{FreshNameCreator,OffsetPosition,Position,SourceFile}
+import scala.tools.nsc.util.{FreshNameCreator,OffsetPosition,Position,NoPosition,SourceFile}
import scala.tools.nsc.io.AbstractFile
import scala.collection.mutable.{HashSet, HashMap, ListBuffer}
@@ -16,6 +16,7 @@ trait CompilationUnits { self: Global =>
* It typically corresponds to a single file of source code. It includes
* error-reporting hooks. */
class CompilationUnit(val source: SourceFile) extends CompilationUnitTrait {
+
/** the fresh name creator */
var fresh : FreshNameCreator = new FreshNameCreator.Default
@@ -41,11 +42,15 @@ trait CompilationUnits { self: Global =>
/** used to track changes in a signature */
var pickleHash : Long = 0
- /** the current edit point offset */
- var editPoint: Int = -1
-
def position(pos: Int) = source.position(pos)
+ /** The position of a targeted type check
+ * If this is different from NoPosition, the type checking
+ * will stop once a tree that contains this position range
+ * is fully attributed.
+ */
+ def targetPos: Position = NoPosition
+
/** The icode representation of classes in this compilation unit.
* It is empty up to phase 'icode'.
*/
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index fff92aac64..bb3e37e291 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -129,16 +129,20 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
if (onlyPresentation) new HashMap[Symbol,List[List[Symbol]]]
else null
- // ------------ Hooks for IDE ----------------------------------
+ // ------------ Hooks for interactive mode-------------------------
/** Return a position correponding to tree startaing at `start`, with tip
* at `mid`, and ending at `end`. ^ batch mode errors point at tip.
*/
def rangePos(source: SourceFile, start: Int, mid: Int, end: Int) = OffsetPosition(source, mid)
- /** Poll for a high-priority task
+ /** Called every time an AST node is succesfully typedchecked in typerPhase.
*/
- def pollForHighPriorityJob() {}
+ def signalDone(context: analyzer.Context, old: Tree, result: Tree) {}
+
+ /** Register new context; called for every created context
+ */
+ def registerContext(c: analyzer.Context) {}
// ------------------ Reporting -------------------------------------
diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
new file mode 100755
index 0000000000..ade9d7e792
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
@@ -0,0 +1,76 @@
+package scala.tools.nsc.interactive
+
+import collection.mutable.ArrayBuffer
+import nsc.util.Position
+
+trait ContextTrees { self: Global =>
+
+ type Context = analyzer.Context
+ type Contexts = ArrayBuffer[ContextTree]
+
+ class ContextTree(val context: Context, val children: ArrayBuffer[ContextTree]) {
+ def this(context: Context) = this(context, new ArrayBuffer[ContextTree])
+ def pos: Position = context.tree.pos
+ }
+
+ def locateContext(contexts: Contexts, pos: Position): Option[Context] = {
+ if (contexts.isEmpty) None
+ else {
+ val hi = contexts.length - 1
+ if ((contexts(hi).pos precedes pos) || (pos precedes contexts(0).pos)) None
+ else {
+ def loop(lo: Int, hi: Int): Option[Context] = {
+ assert(lo <= hi)
+ val mid = (lo + hi) / 2
+ val midpos = contexts(mid).pos
+ if (pos precedes midpos)
+ loop(lo, mid - 1)
+ else if (midpos precedes pos)
+ loop(mid + 1, hi)
+ else if (midpos includes pos)
+ locateContext(contexts(mid).children, pos) orElse Some(contexts(mid).context)
+ else
+ None
+ }
+ loop(0, hi)
+ }
+ }
+ }
+
+ def addContext(contexts: Contexts, context: Context) {
+ val cpos = context.tree.pos
+ if (contexts.isEmpty) contexts += new ContextTree(context)
+ else {
+ val hi = contexts.length - 1
+ if (contexts(hi).pos precedes cpos)
+ contexts += new ContextTree(context)
+ else if (contexts(hi).pos includes cpos) // fast path w/o search
+ addContext(contexts(hi).children, context)
+ else if (cpos precedes contexts(0).pos)
+ new ContextTree(context) +: contexts
+ else {
+ def loop(lo: Int, hi: Int) {
+ assert(lo <= hi)
+ val mid = (lo + hi) / 2
+ val midpos = contexts(mid).pos
+ if (cpos precedes midpos)
+ loop(lo, mid - 1)
+ else if (midpos precedes cpos)
+ loop(mid + 1, hi)
+ else if (midpos sameRange cpos)
+ contexts(mid) = new ContextTree(context, contexts(mid).children)
+ else if ((midpos includes cpos) && !(cpos includes midpos))
+ addContext(contexts(mid).children, context)
+ else if (cpos includes midpos)
+ contexts(mid) = new ContextTree(context, ArrayBuffer(contexts(mid)))
+ else {
+ inform("internal error? skewed positions: "+midpos+"/"+cpos)
+ contexts(mid) = new ContextTree(context)
+ }
+ }
+ loop(0, hi)
+ }
+ }
+ }
+}
+
diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala
index 215d6881e7..c912378385 100755
--- a/src/compiler/scala/tools/nsc/interactive/Global.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Global.scala
@@ -1,6 +1,6 @@
package scala.tools.nsc.interactive
-import scala.collection.mutable.{LinkedHashSet, LinkedHashMap}
+import scala.collection.mutable.{LinkedHashSet, LinkedHashMap, SynchronizedMap}
import scala.concurrent.SyncVar
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.{SourceFile, Position, RangePosition, OffsetPosition, NoPosition, WorkScheduler}
@@ -8,64 +8,94 @@ import scala.tools.nsc.reporters._
import scala.tools.nsc.symtab._
import scala.tools.nsc.ast._
-class Global(_settings: Settings, _reporter: Reporter) extends nsc.Global(_settings, _reporter) {
+/** The main class of the presentation compiler in an interactive environment such as an IDE
+ */
+class Global(_settings: Settings, _reporter: Reporter)
+ extends nsc.Global(_settings, _reporter)
+ with ContextTrees {
self =>
- /** Make rangePos return a RangePosition */
- override def rangePos(source: SourceFile, start: Int, mid: Int, end: Int) = new RangePosition(source, start, mid, end)
-
- private var pollingEnabled = true
-
- /** Called from typechecker */
- override def pollForHighPriorityJob() {
- // don';t do this if polling notnenabled unless cancel
- scheduler.nextWorkItem() match {
- case Some(action) =>
- pollingEnabled = false
- try {
- action()
- } catch {
- case ex: CancelActionReq =>
- }
- pollingEnabled = true
- case None =>
- }
- }
-
/** A list indicating in which order some units should be typechecked.
- * All units in priorityUnits are typechecked before any unit not in this list
+ * All units in firsts are typechecked before any unit not in this list
* Modified by askToDoFirst
*/
- var priorityUnits: List[CompilationUnit] = List()
-
- /** The list of high-proiority units that still needs to be typechecked in current iteration */
- var remainingPriUnits: List[CompilationUnit] = List()
+ var firsts: List[AbstractFile] = List()
/** Is there a loaded unit not up-to-date wrt typechecking?
*/
var outOfDate: Boolean = false
- /** Has a loaded unit changed recently, or has a new unit been added?
- */
- var change: Boolean = false
-
- /** The units that still need compiling in the current pass */
- val unitsToCompile = new LinkedHashSet[CompilationUnit]
-
/** A map of all loaded units to the id of the last compiler run that typechecked them
* @see NotLoaded, JustParsed
*/
- val unitsWithRunId = new LinkedHashMap[CompilationUnit, Int]
+ val unitOfFile = new LinkedHashMap[AbstractFile, RichCompilationUnit] with
+ SynchronizedMap[AbstractFile, RichCompilationUnit]
- /** The value associated in unitsWithRunId for units that have not yet been loaded */
+ /** The status value of a unit that has not yet been loaded */
final val NotLoaded = -1
- /** The value associated in unitsWithRunId for units that have not yet been typechecked */
+ /** The status value of a unit that has not yet been typechecked */
final val JustParsed = 0
/** The currently active typer run */
var currentTyperRun: TyperRun = _
+ /** Is a background compiler currently running? */
+ var compiling = false
+
+ // ----------- Overriding hooks in nsc.Global -----------------------
+
+ /** Make rangePos return a RangePosition */
+ override def rangePos(source: SourceFile, start: Int, mid: Int, end: Int) =
+ new RangePosition(source, start, mid, end)
+
+ /** Called from typechecker */
+ override def signalDone(context: Context, old: Tree, result: Tree) {
+ def integrateNew() {
+ context.unit.body = new TreeReplacer(old, result) transform context.unit.body
+ }
+ if (context.unit.targetPos includes result.pos) {
+ integrateNew()
+ throw new TyperResult(result)
+ }
+ pollForWork()
+ if (outOfDate) {
+ integrateNew()
+ throw new FreshRunReq
+ }
+ }
+
+ override def registerContext(c: Context) = c.unit match {
+ case u: RichCompilationUnit => addContext(u.contexts, c)
+ case _ =>
+ }
+
+ // ----------------- Polling ---------------------------------------
+
+ private var pollingEnabled = false
+
+ /** Called from runner thread and singnalDone */
+ def pollForWork() {
+ if (pollingEnabled)
+ scheduler.nextWorkItem() match {
+ case Some(action) =>
+ pollingEnabled = false
+ try {
+ action()
+ } catch {
+ case ex: CancelActionReq =>
+ } finally {
+ scheduler.doneWorkItem()
+ }
+ pollingEnabled = true
+ case None =>
+ }
+ else
+ scheduler.pollException()
+ }
+
+ // ----------------- The Background Runner Thread -----------------------
+
/** The current presentation compiler runner */
private var compileRunner = newRunnerThread
compileRunner.start()
@@ -74,7 +104,21 @@ self =>
def newRunnerThread: Thread = new Thread("Scala Presentation Compiler") {
override def run() {
try {
- while (true) typeCheckPass()
+ while (true) {
+ scheduler.waitForMoreWork()
+ pollForWork()
+ while (outOfDate) {
+ outOfDate = false
+ try {
+ compiling = true
+ backgroundCompile()
+ } catch {
+ case ex: FreshRunReq =>
+ } finally {
+ compiling = false
+ }
+ }
+ }
} catch {
case ex: ShutdownReq =>
;
@@ -87,121 +131,227 @@ self =>
}
}
- /** A pass that first waits for something to do, then typechecks all loaded units
- * until everything is up-to-date
+ /** Compile all given units
*/
- private def typeCheckPass() = try {
- while (!outOfDate) {
- scheduler.waitForMoreWork()
- pollForHighPriorityJob()
- }
+ private def backgroundCompile() {
inform("Starting new presentation compiler type checking pass")
- currentTyperRun = new TyperRun
- while (outOfDate) {
- outOfDate = false
- for ((unit, id) <- unitsWithRunId.elements) {
- if (id != currentRunId) unitsToCompile += unit
- }
- remainingPriUnits = priorityUnits
- while (unitsToCompile.nonEmpty) {
- if (change) {
- change = false
- outOfDate = true
- currentTyperRun = new TyperRun()
+ val unitsToCompile = new LinkedHashSet[RichCompilationUnit] ++= unitOfFile.values
+ var rest = firsts
+ def getUnit(): RichCompilationUnit = rest match {
+ case List() =>
+ unitsToCompile.head
+ case f :: fs =>
+ unitsToCompile.find(_.source.file == f) match {
+ case Some(unit) => unit
+ case None => rest = fs; getUnit()
}
- var unit = unitsToCompile.head
- if (!remainingPriUnits.isEmpty) {
- unit = remainingPriUnits.head
- remainingPriUnits = remainingPriUnits.tail
- }
- inform("type checking: "+unit)
- currentTyperRun.typeCheck(unit, NoPosition)
- unitsWithRunId(unit) = currentRunId
- unitsToCompile -= unit
- }
}
- } catch {
- case ex: FreshRunReq => outOfDate = true
+ while (unitsToCompile.nonEmpty) {
+ val unit = getUnit()
+ recompile(unit)
+ unitsToCompile -= unit
+ }
}
+ /** Reset unit to just-parsed state */
+ def reset(unit: RichCompilationUnit): Unit =
+ if (unit.status > JustParsed) {
+ unit.depends.clear()
+ unit.defined.clear()
+ unit.synthetics.clear()
+ unit.toCheck.clear()
+ unit.targetPos = NoPosition
+ unit.contexts.clear()
+ ResetAttrs.traverse(unit.body)
+ currentTyperRun.enterNames(unit)
+ unit.status = JustParsed
+ }
+
+ /** Make sure symbol and type attributes are reset and recompile unit.
+ */
+ def recompile(unit: RichCompilationUnit) {
+ assert(unit.status != NotLoaded)
+ reset(unit)
+ inform("type checking: "+unit)
+ currentTyperRun.typeCheck(unit)
+ unit.status = currentRunId
+ }
+
+ /** Move list of files to front of firsts */
+ def moveToFront(fs: List[AbstractFile]) {
+ firsts = fs ::: (firsts diff fs)
+ }
+
+ // ----------------- Implementations of client commmands -----------------------
+
/** Make sure a set of compilation units is loaded and parsed */
- def reload(units: Set[CompilationUnit]) = {
- for (unit <- units) {
+ def reload(sources: Set[SourceFile]) = {
+ currentTyperRun = new TyperRun()
+ for (source <- sources) {
+ val unit = new RichCompilationUnit(source)
+ unitOfFile(source.file) = unit
currentTyperRun.compileLate(unit)
- unitsWithRunId(unit) = JustParsed
- unitsToCompile += unit
+ unit.status = JustParsed
}
- change = true
outOfDate = true
+ moveToFront(sources.toList map (_.file))
+ if (compiling) throw new FreshRunReq
}
/** Set sync var `result` to a fully attributed tree located at position `pos` */
def typedTreeAt(pos: Position, result: SyncVar[Tree]) {
val unit = unitOf(pos)
- if (unitsWithRunId(unit) == NotLoaded)
- reload(Set(unit))
- if (unitsWithRunId(unit) != currentRunId)
- currentTyperRun.typeCheck(unit, pos)
- result set locateTree(pos)
+ assert(unit.status != NotLoaded)
+ moveToFront(List(unit.source.file))
+ result set currentTyperRun.typedTreeAt(pos)
}
+ // ---------------- Helper classes ---------------------------
+
/** A locator for trees with given positions.
* Given a position `pos`, locator.apply returns
* the smallest tree that encloses `pos`.
*/
- object locate extends Traverser {
- var pos: Position = _
+ class Locator(pos: Position) extends Traverser {
var last: Tree = _
- def apply(pos: Position, root: Tree): Tree = {
- this.pos = pos
+ def locateIn(root: Tree): Tree = {
this.last = EmptyTree
traverse(root)
this.last
}
override def traverse(t: Tree) {
- if (t.pos.start <= pos.start && pos.end <= t.pos.end) {
+ if (t.pos includes pos) {
last = t
super.traverse(t)
}
}
}
+ /** A transformer that replaces tree `from` with tree `to` in a given tree */
+ class TreeReplacer(from: Tree, to: Tree) extends Transformer {
+ override def transform(t: Tree): Tree = {
+ if (t.pos includes from.pos)
+ if (t == from) to
+ else super.transform(t)
+ else
+ t
+ }
+ }
+
+ /** A traverser that resets all type and symbol attributes in a tree */
+ object ResetAttrs extends Traverser {
+ override def traverse(t: Tree) {
+ if (t.hasSymbol) t.symbol = NoSymbol
+ t.tpe = null
+ super.traverse(t)
+ }
+ }
+
+ /** The typer run */
+ class TyperRun extends Run {
+ // units is always empty
+ // symSource, symData are ignored
+ override def compiles(sym: Symbol) = false
+
+ def typeCheck(unit: CompilationUnit) {
+ applyPhase(typerPhase, unit)
+ }
+
+ def enterNames(unit: CompilationUnit) {
+ applyPhase(namerPhase, unit)
+ }
+
+ /** Return fully attributed tree at given position
+ * (i.e. largest tree that's contained by position)
+ */
+ def typedTreeAt(pos: Position): Tree = {
+ val tree = locateTree(pos)
+ if (tree.tpe ne null) tree
+ else {
+ val unit = unitOf(pos)
+ assert(unit.status >= JustParsed)
+ unit.targetPos = pos
+ try {
+ typeCheck(unit)
+ throw new FatalError("tree not found")
+ } catch {
+ case ex: TyperResult => ex.tree
+ }
+ }
+ }
+
+ /** Apply a phase to a compilation unit */
+ private def applyPhase(phase: Phase, unit: CompilationUnit) {
+ val oldSource = reporter.getSource
+ try {
+ reporter.setSource(unit.source)
+ atPhase(phase) { phase.asInstanceOf[GlobalPhase] applyPhase unit }
+ } finally {
+ reporter setSource oldSource
+ }
+ }
+ }
+
+ class TyperResult(val tree: Tree) extends Exception
+
+ class RichCompilationUnit(source: SourceFile) extends CompilationUnit(source) {
+
+ var status: Int = NotLoaded
+
+ /** the current edit point offset */
+ var editPoint: Int = -1
+
+ /** The position of a targeted type check
+ * If this is different from NoPosition, the type checking
+ * will stop once a tree that contains this position range
+ * is fully attributed.
+ */
+ var _targetPos: Position = NoPosition
+ override def targetPos: Position = _targetPos
+ def targetPos_=(p: Position) { _targetPos = p }
+
+ var contexts: Contexts = new Contexts
+
+ }
+
+ assert(globalPhase.id == 0)
+
// ----------------- interface to IDE ------------------------------------
private val scheduler = new WorkScheduler
/** The compilation unit corresponding to a source file */
- def unitOf(s: SourceFile): CompilationUnit =
- unitsWithRunId.keys find (_.source == s) match {
- case Some(unit) => unit
- case None =>
- val unit = new CompilationUnit(s)
- unitsWithRunId(unit) = NotLoaded
- unit
- }
+ def unitOf(s: SourceFile): RichCompilationUnit = unitOfFile get s.file match {
+ case Some(unit) =>
+ unit
+ case None =>
+ val unit = new RichCompilationUnit(s)
+ unitOfFile(s.file) = unit
+ unit
+ }
/** The compilation unit corresponding to a position */
- def unitOf(pos: Position): CompilationUnit = unitOf(pos.source.get)
+ def unitOf(pos: Position): RichCompilationUnit = unitOf(pos.source.get)
/** Locate smallest tree that encloses position */
def locateTree(pos: Position): Tree =
- locate(pos, unitOf(pos).body)
+ new Locator(pos) locateIn unitOf(pos).body
+
+ /** Locate smallest context that encloses position */
+ def locateContext(pos: Position): Option[Context] =
+ locateContext(unitOf(pos).contexts, pos)
/** Make sure a set of compilation units is loaded and parsed */
- def askReload(units: Set[CompilationUnit]) =
- scheduler.postWorkItem(() => reload(units))
+ def askReload(sources: Set[SourceFile]) =
+ scheduler.postWorkItem(() => reload(sources))
/** Set sync var `result` to a fully attributed tree located at position `pos` */
def askTypeAt(pos: Position, result: SyncVar[Tree]) =
scheduler.postWorkItem(() => self.typedTreeAt(pos, result))
/** Ask to do unit first on present and subsequent type checking passes */
- def askToDoFirst(unit: CompilationUnit) = {
- def moveToFront(unit: CompilationUnit, units: List[CompilationUnit]) = unit :: (units filter (unit !=))
- scheduler.postWorkItem { () =>
- remainingPriUnits = moveToFront(unit, remainingPriUnits)
- priorityUnits = moveToFront(unit, priorityUnits)
- }
+ def askToDoFirst(f: AbstractFile) = {
+ scheduler.postWorkItem { () => moveToFront(List(f)) }
}
/** Cancel currently pending high-priority jobs */
@@ -221,25 +371,5 @@ self =>
class CancelActionReq extends Exception
class FreshRunReq extends Exception
class ShutdownReq extends Exception
-
- /** The typer run */
- class TyperRun extends Run {
- // units is always empty
- // symSource, symData are ignored
- override def compiles(sym: Symbol) = false
-
- def typeCheck(unit: CompilationUnit, pos: Position) {
- assert(unitsWithRunId(unit) >= JustParsed)
-/*
- unit.needOnly =
- if (pos == NoPosition) PackageDef(nme.EMPTY, List()) setPos(
- else
-
- typerPhase.applyPhase(unit)
-
-*/
- }
- }
-
- assert(globalPhase.id == 0)
}
+
diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolWalker.scala b/src/compiler/scala/tools/nsc/symtab/SymbolWalker.scala
index ba87b61f16..e663011d7c 100644
--- a/src/compiler/scala/tools/nsc/symtab/SymbolWalker.scala
+++ b/src/compiler/scala/tools/nsc/symtab/SymbolWalker.scala
@@ -22,7 +22,7 @@ trait SymbolWalker {
def walk(tree: Tree, visitor : Visitor)(fid : (util.Position) => Option[String]) : Unit = {
val visited = new LinkedHashSet[Tree]
def f(t : Tree) : Unit = {
- if (!visited.put(t)) return
+ if (visited.add(t)) return
def fs(l : List[Tree]) : Unit = {
val i = l.elements
while (i.hasNext) f(i.next)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 80b8189b5f..4a16da2b55 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -210,6 +210,7 @@ trait Contexts { self: Analyzer =>
c.checking = this.checking
c.retyping = this.retyping
c.openImplicits = this.openImplicits
+ registerContext(c.asInstanceOf[analyzer.Context])
c
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 170e445458..7dc60b91dc 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1653,29 +1653,38 @@ trait Typers { self: Analyzer =>
def typedStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
val inBlock = exprOwner == context.owner
+ val localTarget =
+ context.unit != null &&
+ context.unit.targetPos != NoPosition &&
+ (stats exists (context.unit.targetPos includes _.pos))
def typedStat(stat: Tree): Tree = {
if (context.owner.isRefinementClass && !treeInfo.isDeclaration(stat))
errorTree(stat, "only declarations allowed here")
- stat match {
- case imp @ Import(_, _) =>
- val imp0 = typedImport(imp)
- if (imp0 ne null) {
- context = context.makeNewImport(imp0)
- imp0.symbol.initialize
- }
- EmptyTree
- case _ =>
- val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this
- else newTyper(context.make(stat, exprOwner))
- val result = checkDead(localTyper.typed(stat))
- if (treeInfo.isSelfOrSuperConstrCall(result)) {
- context.inConstructorSuffix = true
- if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.offset.getOrElse(0) >= exprOwner.enclMethod.pos.offset.getOrElse(0))
- error(stat.pos, "called constructor's definition must precede calling constructor's definition")
- }
- result
- }
+ else
+ stat match {
+ case imp @ Import(_, _) =>
+ val imp0 = typedImport(imp)
+ if (imp0 ne null) {
+ context = context.makeNewImport(imp0)
+ imp0.symbol.initialize
+ }
+ EmptyTree
+ case _ =>
+ if (localTarget && !(context.unit.targetPos includes stat.pos)) {
+ stat
+ } else {
+ val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this
+ else newTyper(context.make(stat, exprOwner))
+ val result = checkDead(localTyper.typed(stat))
+ if (treeInfo.isSelfOrSuperConstrCall(result)) {
+ context.inConstructorSuffix = true
+ if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.offset.getOrElse(0) >= exprOwner.enclMethod.pos.offset.getOrElse(0))
+ error(stat.pos, "called constructor's definition must precede calling constructor's definition")
+ }
+ result
+ }
+ }
}
def accesses(accessor: Symbol, accessed: Symbol) =
@@ -3432,8 +3441,6 @@ trait Typers { self: Analyzer =>
}
try {
- if (settings.debug.value)
- assert(pt ne null, tree)//debug
if (context.retyping &&
(tree.tpe ne null) && (tree.tpe.isErroneous || !(tree.tpe <:< pt))) {
tree.tpe = null
@@ -3449,7 +3456,7 @@ trait Typers { self: Analyzer =>
val result = if (tree1.isEmpty) tree1 else adapt(tree1, mode, pt)
if (printTypings) println("adapted "+tree1+":"+tree1.tpe+" to "+pt+", "+context.undetparams); //DEBUG
// if ((mode & TYPEmode) != 0) println("type: "+tree1+" has type "+tree1.tpe)
- if (phase.id == currentRun.typerPhase.id) pollForHighPriorityJob()
+ if (phase.id == currentRun.typerPhase.id) signalDone(context.asInstanceOf[analyzer.Context], tree, result)
result
} catch {
case ex: TypeError =>
diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala
index b336e64c62..6d494d5c3f 100644
--- a/src/compiler/scala/tools/nsc/util/Position.scala
+++ b/src/compiler/scala/tools/nsc/util/Position.scala
@@ -15,6 +15,7 @@ trait Position {
import Position.tabInc
def offset: Option[Int] = None
def source: Option[SourceFile] = None
+ def isDefined: Boolean = false
def start: Int = mid
def mid: Int = offset.get
@@ -24,6 +25,15 @@ trait Position {
def midOrElse(d: Int) = offset.get//OrElse(d)
def endOrElse(d: Int) = offset.get//OrElse(d)
+ def includes(pos: Position) =
+ isDefined && pos.isDefined && start <= pos.start && pos.end <= end
+
+ def precedes(pos: Position) =
+ isDefined && pos.isDefined && end <= pos.start
+
+ def sameRange(pos: Position) =
+ isDefined && pos.isDefined && start == pos.start && end == pos.end
+
def line: Option[Int] =
if (offset.isEmpty || source.isEmpty) None
else Some(source.get.offsetToLine(offset.get) + 1)
@@ -83,18 +93,10 @@ case class FakePos(msg: String) extends Position {
override def toString=msg
}
-// ??? needed
-case class LinePosition(source0: SourceFile, line0: Int) extends Position {
- assert(line0 >= 1)
- override def offset = None
- override def column = None
- override def line = Some(line0)
- override def source = Some(source0)
-}
-
case class OffsetPosition(source0: SourceFile, offset0: Int) extends Position {
override def source = Some(source0)
override def offset = Some(offset0)
+ override def isDefined = true
override def equals(that : Any) = that match {
case that : OffsetPosition => offset0 == that.offset0 && source0.file == that.source0.file
case that => false
@@ -105,6 +107,7 @@ case class OffsetPosition(source0: SourceFile, offset0: Int) extends Position {
/** new for position ranges */
class RangePosition(source0: SourceFile, override val start: Int, override val mid: Int, override val end: Int)
extends OffsetPosition(source0, mid) {
+ override def isDefined = true
override def startOrElse(d: Int) = start
override def midOrElse(d: Int) = mid
override def endOrElse(d: Int) = end
diff --git a/src/compiler/scala/tools/nsc/util/WorkScheduler.scala b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala
index 7bcd3b7a8e..cd4d805701 100644
--- a/src/compiler/scala/tools/nsc/util/WorkScheduler.scala
+++ b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala
@@ -7,37 +7,59 @@ class WorkScheduler {
type Action = () => Unit
private var todo = new Queue[Action]
+ private var except: Option[Exception] = None
+ private var working = false
- /** Called from server */
+ /** Called from server: block until todo list is nonempty */
def waitForMoreWork() = synchronized {
do { wait() } while (todo.isEmpty)
}
- /** called from Server */
+ /** called from Server: test whether todo list is nonempty */
def moreWork(): Boolean = synchronized {
todo.nonEmpty
}
- /** Called from server */
+ /** Called from server: get first action in todo list, and pop it off */
def nextWorkItem(): Option[Action] = synchronized {
- if (!todo.isEmpty) Some(todo.dequeue()) else None
+ if (!todo.isEmpty) {
+ working = true
+ Some(todo.dequeue())
+ } else None
}
- /** Called from client */
+ /** Called from server: raise any exception posted by client */
+ def pollException() = synchronized {
+ except match {
+ case Some(exc) => throw exc
+ case None =>
+ }
+ }
+
+ /** Called from server: mark workitem as finished (influences
+ * meaning of raise)
+ */
+ def doneWorkItem() = synchronized {
+ working = false
+ }
+
+ /** Called from client: have action executed by server */
def postWorkItem(action: Action) {
todo enqueue action
notify()
}
- /** Called from client */
- def cancel() = synchronized {
+ /** Called from client: cancel all queued actions */
+ def cancelQueued() = synchronized {
todo.clear()
}
- /** Called from client */
+ /** Called from client:
+ * If work in progress, raise an exception in it next
+ * time pollException is called.
+ */
def raise(exc: Exception) = synchronized {
- todo.clear()
- todo enqueue (() => throw exc)
+ if (working) except = Some(exc)
}
}