summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2010-08-30 16:02:34 +0000
committerMartin Odersky <odersky@gmail.com>2010-08-30 16:02:34 +0000
commit8964f6f1bcc8500f1bc6a2808ef70d8852d208ec (patch)
treecc558460d2f7be3238af3bf80bc95f5422313641 /src
parent41e2c237dfdcaffaa53edc6ef30b044353bce5e4 (diff)
downloadscala-8964f6f1bcc8500f1bc6a2808ef70d8852d208ec.tar.gz
scala-8964f6f1bcc8500f1bc6a2808ef70d8852d208ec.tar.bz2
scala-8964f6f1bcc8500f1bc6a2808ef70d8852d208ec.zip
New wider interface of presentation compiler.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/Main.scala4
-rw-r--r--src/compiler/scala/tools/nsc/interactive/CompilerControl.scala32
-rw-r--r--src/compiler/scala/tools/nsc/interactive/ContextTrees.scala2
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Global.scala65
-rw-r--r--src/compiler/scala/tools/nsc/interactive/REPL.scala12
-rw-r--r--src/compiler/scala/tools/nsc/interactive/Response.scala68
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala2
7 files changed, 141 insertions, 44 deletions
diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala
index 948c5f6fb8..1e6699079d 100644
--- a/src/compiler/scala/tools/nsc/Main.scala
+++ b/src/compiler/scala/tools/nsc/Main.scala
@@ -8,8 +8,6 @@ package scala.tools.nsc
import java.io.File
import File.pathSeparator
-import scala.concurrent.SyncVar
-
import scala.tools.nsc.interactive.{ RefinedBuildManager, SimpleBuildManager }
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}
@@ -59,7 +57,7 @@ object Main extends AnyRef with EvalLoop {
import compiler.{ reporter => _, _ }
val sfs = command.files.map(getSourceFile(_))
- val reloaded = new SyncVar[Either[Unit, Throwable]]
+ val reloaded = new interactive.Response[Unit]
askReload(sfs, reloaded)
reloaded.get.right.toOption match {
case Some(ex) => reporter.cancelled = true // Causes exit code to be non-0
diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
index c6106a9865..b00da77b46 100644
--- a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
+++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala
@@ -1,7 +1,6 @@
package scala.tools.nsc
package interactive
-import scala.concurrent.SyncVar
import scala.util.control.ControlThrowable
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.{SourceFile, Position, WorkScheduler}
@@ -12,14 +11,6 @@ import scala.tools.nsc.ast._
*/
trait CompilerControl { self: Global =>
- /** Response {
- override def toString = "TypeMember("+sym+","+tpe+","+accessible+","+inherited+","+viaView+")"
- }{
- override def toString = "TypeMember("+sym+","+tpe+","+accessible+","+inherited+","+viaView+")"
- }wrapper to client
- */
- type Response[T] = SyncVar[Either[T, Throwable]]
-
abstract class WorkItem extends (() => Unit)
/** Info given for every member found by completion
@@ -30,8 +21,18 @@ trait CompilerControl { self: Global =>
val accessible: Boolean
}
- case class TypeMember(sym: Symbol, tpe: Type, accessible: Boolean, inherited: Boolean, viaView: Symbol) extends Member
- case class ScopeMember(sym: Symbol, tpe: Type, accessible: Boolean, viaImport: Tree) extends Member
+ case class TypeMember(
+ sym: Symbol,
+ tpe: Type,
+ accessible: Boolean,
+ inherited: Boolean,
+ viaView: Symbol) extends Member
+
+ case class ScopeMember(
+ sym: Symbol,
+ tpe: Type,
+ accessible: Boolean,
+ viaImport: Tree) extends Member
/** The scheduler by which client and compiler communicate
* Must be initialized before starting compilerRunner
@@ -82,7 +83,7 @@ trait CompilerControl { self: Global =>
override def toString = "reload "+sources
}
- /** Set sync var `result` to a fully attributed tree located at position `pos`
+ /** Set sync var `result` to the smallest fully attributed tree that encloses position `pos`.
*/
def askTypeAt(pos: Position, result: Response[Tree]) =
scheduler postWorkItem new WorkItem {
@@ -90,6 +91,8 @@ trait CompilerControl { self: Global =>
override def toString = "typeat "+pos.source+" "+pos.show
}
+ /** Set sync var `result` to the fully attributed & typechecked tree contained in `source`.
+ */
def askType(source: SourceFile, forceReload: Boolean, result: Response[Tree]) =
scheduler postWorkItem new WorkItem {
def apply() = self.getTypedTree(source, forceReload, result)
@@ -98,7 +101,6 @@ trait CompilerControl { self: Global =>
/** Set sync var `result' to list of members that are visible
* as members of the tree enclosing `pos`, possibly reachable by an implicit.
- * - if `selection` is false, as identifiers in the scope enclosing `pos`
*/
def askTypeCompletion(pos: Position, result: Response[List[Member]]) =
scheduler postWorkItem new WorkItem {
@@ -123,9 +125,6 @@ trait CompilerControl { self: Global =>
}
}
- /** Cancel currently pending high-priority jobs */
- def askCancel() = scheduler raise CancelActionReq
-
/** Cancel current compiler run and start a fresh one where everything will be re-typechecked
* (but not re-loaded).
*/
@@ -139,7 +138,6 @@ trait CompilerControl { self: Global =>
// ---------------- Interpreted exceptions -------------------
- object CancelActionReq extends ControlThrowable
object FreshRunReq extends ControlThrowable
object ShutdownReq extends ControlThrowable
}
diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
index 1fad3180ab..8fa4b86219 100644
--- a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
+++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala
@@ -17,7 +17,7 @@ trait ContextTrees { self: Global =>
* 3. The `pos` field of a context is the same as `context.tree.pos`, unless that
* position is transparent. In that case, `pos` equals the position of
* one of the solid descendants of `context.tree`.
- * 4. Children of a context have non-overlapping increasining positions.
+ * 4. Children of a context have non-overlapping increasing positions.
* 5. No context in the tree has a transparent position.
*/
class ContextTree(val pos: Position, val context: Context, val children: ArrayBuffer[ContextTree]) {
diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala
index de0d6ade13..bf901b321a 100644
--- a/src/compiler/scala/tools/nsc/interactive/Global.scala
+++ b/src/compiler/scala/tools/nsc/interactive/Global.scala
@@ -42,12 +42,17 @@ self =>
/** The currently active typer run */
private var currentTyperRun: TyperRun = _
- /** Is a background compiler run needed? */
+ /** Is a background compiler run needed?
+ * Note: outOfDate is true as long as there is a backgroud compile scheduled or going on.
+ */
private var outOfDate = false
/** Units compiled by a run with id >= minRunId are considered up-to-date */
private[interactive] var minRunId = 1
+ private val NoResponse: Response[_] = new Response[Any]
+ private var pendingResponse: Response[_] = NoResponse
+
/** Is a reload/background compiler currently running? */
private var acting = false
@@ -120,8 +125,9 @@ self =>
ir.execute(); pollForWork()
case _ =>
}
+ if (pendingResponse.isCancelled)
+ throw CancelException
scheduler.pollThrowable() match {
- case Some(ex @ CancelActionReq) => if (acting) throw ex
case Some(ex @ FreshRunReq) =>
currentTyperRun = newTyperRun
minRunId = currentRunId
@@ -137,9 +143,6 @@ self =>
if (debugIDE) println("picked up work item: "+action)
action()
if (debugIDE) println("done with work item: "+action)
- } catch {
- case CancelActionReq =>
- if (debugIDE) println("cancelled work item: "+action)
} finally {
if (debugIDE) println("quitting work item: "+action)
acting = false
@@ -226,10 +229,16 @@ self =>
private def backgroundCompile() {
if (debugIDE) inform("Starting new presentation compiler type checking pass")
reporter.reset
+
+ // remove any files in first that are no longer maintained by presentation compiler (i.e. closed)
firsts = firsts filter (s => unitOfFile contains (s.file))
+
val prefix = firsts map unitOf
+
val units = prefix ::: (unitOfFile.values.toList diff prefix) filter (!_.isUpToDate)
+
recompile(units)
+
if (debugIDE) inform("Everything is now up to date")
}
@@ -267,6 +276,8 @@ self =>
activeLocks = 0
currentTyperRun.typeCheck(unit)
unit.status = currentRunId
+ // todo: garbage collect any tyop-level symbols whose types are no longer valid for
+ // currentRunId
}
}
@@ -277,18 +288,24 @@ self =>
// ----------------- Implementations of client commands -----------------------
- def respond[T](result: Response[T])(op: => T): Unit =
+ def respond[T](result: Response[T])(op: => T): Unit = {
+ val prevResponse = pendingResponse
try {
- result set Left(op)
- return
+ pendingResponse = result
+ if (!result.isCancelled) result set op
} catch {
+ case CancelException =>
+ ;
case ex @ FreshRunReq =>
scheduler.postWorkItem(() => respond(result)(op))
throw ex
case ex =>
- result set Right(ex)
+ result raise ex
throw ex
+ } finally {
+ pendingResponse = prevResponse
}
+ }
/** Make sure a set of compilation units is loaded and parsed */
def reloadSources(sources: List[SourceFile]) {
@@ -304,8 +321,8 @@ self =>
/** Make sure a set of compilation units is loaded and parsed */
def reload(sources: List[SourceFile], result: Response[Unit]) {
respond(result)(reloadSources(sources))
- if (outOfDate) throw FreshRunReq
- else outOfDate = true
+ if (outOfDate) throw FreshRunReq // cancel background compile
+ else outOfDate = true // proceed normally and enable new background compile
}
/** A fully attributed tree located at position `pos` */
@@ -339,7 +356,7 @@ self =>
def stabilizedType(tree: Tree): Type = tree match {
case Ident(_) if tree.symbol.isStable => singleType(NoPrefix, tree.symbol)
- case Select(qual, _) if qual.tpe != null && tree.symbol.isStable => singleType(qual.tpe, tree.symbol)
+ case Select(qual, _) if qual.tpe != null && tree.symbol.isStable => singleType(qual.tpe, tree.symbol)
case Import(expr, selectors) =>
tree.symbol.info match {
case analyzer.ImportType(expr) => expr match {
@@ -405,11 +422,16 @@ self =>
def typeMembers(pos: Position): List[TypeMember] = {
var tree = typedTreeAt(pos)
+
+ // Let's say you have something like val x: List[Int] and ypu want to get completion after List
+ // Then the tree found at first is a TypeTree, ????
tree match {
- case tt : TypeTree => tree = tt.original
+ case tt : TypeTree if tt.original != null => tree = tt.original // ???
case _ =>
}
+ // if tree consists of just x. or x.fo where fo is not yet a full member name
+ // ignore the selection and look in just x.
tree match {
case Select(qual, name) if tree.tpe == ErrorType => tree = qual
case _ =>
@@ -425,6 +447,7 @@ self =>
val superAccess = tree.isInstanceOf[Super]
val scope = new Scope
val members = new LinkedHashMap[Symbol, TypeMember]
+
def addTypeMember(sym: Symbol, pre: Type, inherited: Boolean, viaView: Symbol) {
val symtpe = pre.memberType(sym)
if (scope.lookupAll(sym.name) forall (sym => !(members(sym).tpe matches symtpe))) {
@@ -437,22 +460,28 @@ self =>
viaView)
}
}
+
+ /** Create a fucntion application of a given view function to `tree` and typechecked it.
+ */
def viewApply(view: SearchResult): Tree = {
assert(view.tree != EmptyTree)
try {
- analyzer.newTyper(context.makeImplicit(false)).typed(Apply(view.tree, List(tree)) setPos tree.pos)
+ analyzer.newTyper(context.makeImplicit(reportAmbiguousErrors = false))
+ .typed(Apply(view.tree, List(tree)) setPos tree.pos)
} catch {
case ex: TypeError => EmptyTree
}
}
+
val pre = stabilizedType(tree)
val ownerTpe = if (tree.tpe != null) tree.tpe else pre
+
for (sym <- ownerTpe.decls)
addTypeMember(sym, pre, false, NoSymbol)
for (sym <- ownerTpe.members)
addTypeMember(sym, pre, true, NoSymbol)
val applicableViews: List[SearchResult] =
- new ImplicitSearch(tree, functionType(List(ownerTpe), AnyClass.tpe), true, context.makeImplicit(false))
+ new ImplicitSearch(tree, functionType(List(ownerTpe), AnyClass.tpe), isView = true, context.makeImplicit(reportAmbiguousErrors = false))
.allImplicits
for (view <- applicableViews) {
val vtree = viewApply(view)
@@ -505,7 +534,7 @@ self =>
tree
} else {
val unit = unitOf(pos)
- assert(unit.status >= JustParsed)
+ assert(unit.isParsed)
unit.targetPos = pos
try {
println("starting targeted type check")
@@ -521,7 +550,7 @@ self =>
}
def typedTree(unit: RichCompilationUnit): Tree = {
- assert(unit.status >= JustParsed)
+ assert(unit.isParsed)
unit.targetPos = NoPosition
typeCheck(unit)
unit.body
@@ -545,3 +574,5 @@ self =>
assert(globalPhase.id == 0)
}
+object CancelException extends Exception
+
diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala
index 5589ddb9b1..775b979851 100644
--- a/src/compiler/scala/tools/nsc/interactive/REPL.scala
+++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala
@@ -80,9 +80,9 @@ object REPL {
* complete file off1 off2?
*/
def run(comp: Global) {
- val reloadResult = new comp.Response[Unit]
- val typeatResult = new comp.Response[comp.Tree]
- val completeResult = new comp.Response[List[comp.Member]]
+ val reloadResult = new Response[Unit]
+ val typeatResult = new Response[comp.Tree]
+ val completeResult = new Response[List[comp.Member]]
def makePos(file: String, off1: String, off2: String) = {
val source = toSourceFile(file)
comp.rangePos(source, off1.toInt, off1.toInt, off2.toInt)
@@ -118,11 +118,11 @@ object REPL {
def toSourceFile(name: String) = new BatchSourceFile(new PlainFile(new java.io.File(name)))
- def show[T](svar: SyncVar[Either[T, Throwable]]) {
+ def show[T](svar: Response[T]) {
svar.get match {
case Left(result) => println("==> "+result)
- case Right(exc/*: Throwable ??*/) => exc.printStackTrace; println("ERROR: "+exc)
+ case Right(exc) => exc.printStackTrace; println("ERROR: "+exc)
}
- svar.unset()
+ svar.clear()
}
}
diff --git a/src/compiler/scala/tools/nsc/interactive/Response.scala b/src/compiler/scala/tools/nsc/interactive/Response.scala
new file mode 100644
index 0000000000..96e474a34a
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/interactive/Response.scala
@@ -0,0 +1,68 @@
+package scala.tools.nsc
+package interactive
+
+import scala.concurrent.SyncVar
+
+/** Typical interaction, given a predicate <user-input>, a function <display>,
+ * and an exception handler <handle>:
+ *
+ * val TIMEOUT = 100 // (milliseconds) or something like that
+ * val r = new Response()
+ * while (!r.isComplete && !r.isCancelled) {
+ * if (<user-input>) r.cancel()
+ * else r.get(TIMEOUT) match {
+ * case Some(Left(data)) => <display>(data)
+ * case Some(Right(exc)) => <handle>(exc)
+ * case None =>
+ * }
+ * }
+ */
+class Response[T] {
+
+ private val data = new SyncVar[Either[T, Throwable]]
+ private var complete = false
+ private var cancelled = false
+
+ /** Set provisional data, more to come
+ */
+ def setProvisionally(x: T) =
+ data.set(Left(x))
+
+ /** Set final data, and mark resposne as complete.
+ */
+ def set(x: T) = data.synchronized {
+ data.set(Left(x))
+ complete = true
+ }
+
+ def raise(exc: Throwable) = data.synchronized {
+ data.set(Right(exc))
+ complete = true
+ }
+
+ /** Get data, wait as long as necessary
+ */
+ def get: Either[T, Throwable] = data.get
+
+ /** Optionally get data within `timeout` milliseconds.
+ */
+ def get(timeout: Long): Option[Either[T, Throwable]] = data.get(timeout)
+
+ /** Final data set was stored
+ */
+ def isComplete = data.synchronized { complete }
+
+ /** Cancel action computing this response
+ */
+ def cancel() = data.synchronized { cancelled = true }
+
+ /** A cancel request for this response has been issued
+ */
+ def isCancelled = data.synchronized { cancelled }
+
+ def clear() = data.synchronized {
+ data.unset()
+ complete = false
+ cancelled = false
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index b389a4c033..a003a4fd7e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -2097,6 +2097,8 @@ trait Typers { self: Analyzer =>
EmptyTree
case _ =>
if (localTarget && !includesTargetPos(stat)) {
+ // skip typechecking of statements in a sequence where some other statement includes
+ // the targetposition
stat
} else {
val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this