From d73a2965746b3cc0bdeb3f96fed342143deae210 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 26 May 2009 14:19:52 +0000 Subject: added Synthetic Positions; refactored interacti... added Synthetic Positions; refactored interactive.Global --- src/compiler/scala/tools/nsc/ast/Trees.scala | 20 +++- .../scala/tools/nsc/ast/parser/Parsers.scala | 24 ++--- .../tools/nsc/interactive/CompilerControl.scala | 90 +++++++++++++++++ .../scala/tools/nsc/interactive/ContextTrees.scala | 3 +- .../scala/tools/nsc/interactive/Global.scala | 112 ++------------------- .../scala/tools/nsc/interactive/REPL.scala | 72 +++++++++++++ .../nsc/interactive/RichCompilationUnits.scala | 29 ++++++ src/compiler/scala/tools/nsc/symtab/Types.scala | 1 + .../scala/tools/nsc/typechecker/Typers.scala | 13 ++- src/compiler/scala/tools/nsc/util/Position.scala | 22 ++-- 10 files changed, 253 insertions(+), 133 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/interactive/CompilerControl.scala create mode 100644 src/compiler/scala/tools/nsc/interactive/REPL.scala create mode 100644 src/compiler/scala/tools/nsc/interactive/RichCompilationUnits.scala (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 0257faae88..0f08598157 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -812,15 +812,14 @@ trait Trees { * RefCheck, where the arbitrary type trees are all replaced by * TypeTree's. */ case class TypeTree() extends TypTree { - var original: Tree = _ - override def symbol = if (tpe == null) null else tpe.typeSymbol def setOriginal(tree: Tree): this.type = { - original = tree - setPos(tree.pos) + setPos(SyntheticPosition(tree)) } + def original: Tree = pos.asInstanceOf[SyntheticPosition].original + override def isEmpty = (tpe eq null) || tpe == NoType } @@ -1773,5 +1772,18 @@ trait Trees { def isTop : Boolean } + /** A position to be used for synthetic trees that correspond to some original tree + * @note Trees with synthetic positions may not contain trees with real positions inside them! + */ + case class SyntheticPosition(original: Tree) extends Position { + override def isDefined: Boolean = true + override def isSynthetic: Boolean = true + override def offset: Option[Int] = original.pos.offset + override def source: Option[SourceFile] = original.pos.source + override def start: Int = original.pos.start + override def point: Int = original.pos.point + override def end: Int = original.pos.end + override def underlying = original.pos.underlying + } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index abe7c1f951..0aa4907fc6 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -378,10 +378,10 @@ self => def atPos[T <: Tree](offset: Int)(t: T): T = posAssigner.atPos(r2p(offset, offset, in.lastOffset))(t) - def atPos[T <: Tree](start: Int, mid: Int)(t: T): T = - posAssigner.atPos(r2p(start, mid, in.lastOffset))(t) - def atPos[T <: Tree](start: Int, mid: Int, end: Int)(t: T): T = - posAssigner.atPos(r2p(start, mid, end))(t) + def atPos[T <: Tree](start: Int, point: Int)(t: T): T = + posAssigner.atPos(r2p(start, point, in.lastOffset))(t) + def atPos[T <: Tree](start: Int, point: Int, end: Int)(t: T): T = + posAssigner.atPos(r2p(start, point, end))(t) def atPos[T <: Tree](pos: Position)(t: T): T = posAssigner.atPos(pos)(t) @@ -498,8 +498,8 @@ self => } def selector(t: Tree): Tree = { - val mid = in.offset - Select(t, ident()) setPos r2p(t.pos.start, mid, in.lastOffset) + val point = in.offset + Select(t, ident()) setPos r2p(t.pos.start, point, in.lastOffset) } /** Path ::= StableId @@ -1281,16 +1281,16 @@ self => val start = in.offset if (in.token == VAL) in.nextToken() val pat = pattern1(false) - val mid = in.offset + val point = in.offset val tok = in.token if (tok == EQUALS && eqOK) in.nextToken() else accept(LARROW) val rhs = expr() - enums += makeGenerator(r2p(start, mid, in.lastOffset), pat, tok == EQUALS, rhs) + enums += makeGenerator(r2p(start, point, in.lastOffset), pat, tok == EQUALS, rhs) if (in.token == IF) enums += makeFilter(in.offset, guard()) } - def makeFilter(start: Int, tree: Tree) = Filter(r2p(start, tree.pos.mid, tree.pos.end), tree) + def makeFilter(start: Int, tree: Tree) = Filter(r2p(start, tree.pos.point, tree.pos.end), tree) /* -------- PATTERNS ------------------------------------------- */ @@ -1715,7 +1715,7 @@ self => val t = typ() if (isIdent && in.name == STAR) { in.nextToken() - atPos(t.pos.start, t.pos.mid) { + atPos(t.pos.start, t.pos.point) { AppliedTypeTree( rootScalaDot(nme.REPEATED_PARAM_CLASS_NAME.toTypeName), List(t)) } @@ -1947,7 +1947,7 @@ self => //Console.println("DEBUG: p = "+p.toString()); // DEBUG val trees = makePatDef(newmods, if (tp.isEmpty) p else Typed(p, tp), rhs) map - atPos(p.pos.start, p.pos.mid) + atPos(p.pos.start, p.pos.point) rhs = rhs.duplicate if (newmods hasFlag Flags.DEFERRED) { trees match { @@ -2283,7 +2283,7 @@ self => /** Create a tree representing a packaging */ def makePackaging(start: Int, pkg: Tree, stats: List[Tree]): PackageDef = - atPos(start, pkg.pos.mid) { + atPos(start, pkg.pos.point) { pkg match { case Ident(name) => PackageDef(name, stats) diff --git a/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala new file mode 100644 index 0000000000..f55195973e --- /dev/null +++ b/src/compiler/scala/tools/nsc/interactive/CompilerControl.scala @@ -0,0 +1,90 @@ +package scala.tools.nsc.interactive + +import scala.concurrent.SyncVar +import scala.tools.nsc.io.AbstractFile +import scala.tools.nsc.util.{SourceFile, Position, WorkScheduler} +import scala.tools.nsc.symtab._ +import scala.tools.nsc.ast._ + +/** Interface of interactive compiler to a client such as an IDE + */ +trait CompilerControl { self: Global => + + /* Must be initialized before starting compilerRunner */ + protected val scheduler = new WorkScheduler + + /** The compilation unit corresponding to a source file */ + 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): RichCompilationUnit = unitOf(pos.source.get) + + /** Locate smallest tree that encloses position */ + def locateTree(pos: Position): Tree = + 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(sources: List[SourceFile], result: SyncVar[Either[Unit, Throwable]]) = + scheduler.postWorkItem(() => reload(sources, result)) + + /** Set sync var `result` to a fully attributed tree located at position `pos` */ + def askTypeAt(pos: Position, result: SyncVar[Either[Tree, Throwable]]) = + scheduler.postWorkItem(() => self.typedTreeAt(pos, result)) + + /** Ask to do unit first on present and subsequent type checking passes */ + def askToDoFirst(f: SourceFile) = { + scheduler.postWorkItem { () => moveToFront(List(f)) } + } + + /** Cancel currently pending high-priority jobs */ + def askCancel() = + scheduler.raise(new CancelActionReq) + + /** Cancel current compiler run and start a fresh one where everything will be re-typechecked + * (but not re-loaded). + */ + def askReset() = + scheduler.raise(new FreshRunReq) + + /** Tell the compile server to shutdown, and do not restart again */ + def askShutdown() = + scheduler.raise(new ShutdownReq) + + // ---------------- Interpreted exeptions ------------------- + + class CancelActionReq extends Exception + class FreshRunReq extends Exception + class ShutdownReq extends Exception + + // ---------------- Helper class ----------------------------- + + /** A locator for trees with given positions. + * Given a position `pos`, locator.apply returns + * the smallest tree that encloses `pos`. + */ + private class Locator(pos: Position) extends Traverser { + var last: Tree = _ + def locateIn(root: Tree): Tree = { + this.last = EmptyTree + traverse(root) + this.last + } + override def traverse(t: Tree) { + if (!t.pos.isSynthetic && (t.pos includes pos)) { + last = t + super.traverse(t) + } + } + } +} diff --git a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala index ade9d7e792..8f8470ae2d 100755 --- a/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala +++ b/src/compiler/scala/tools/nsc/interactive/ContextTrees.scala @@ -39,7 +39,8 @@ trait ContextTrees { self: Global => def addContext(contexts: Contexts, context: Context) { val cpos = context.tree.pos - if (contexts.isEmpty) contexts += new ContextTree(context) + if (!cpos.isDefined || cpos.isSynthetic) {} + else if (contexts.isEmpty) contexts += new ContextTree(context) else { val hi = contexts.length - 1 if (contexts(hi).pos precedes cpos) diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index ad141175ff..16af234d48 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, SynchronizedMap} +import scala.collection.mutable.{LinkedHashMap, SynchronizedMap} import scala.concurrent.SyncVar import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.util.{SourceFile, Position, RangePosition, OffsetPosition, NoPosition, WorkScheduler} @@ -11,7 +11,7 @@ import scala.tools.nsc.ast._ /** 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 { + extends nsc.Global(settings, reporter) with CompilerControl with ContextTrees with RichCompilationUnits { self => /** A list indicating in which order some units should be typechecked. @@ -43,8 +43,8 @@ self => // ----------- Overriding hooks in nsc.Global ----------------------- /** Create a RangePosition */ - override def rangePos(source: SourceFile, start: Int, mid: Int, end: Int) = - new RangePosition(source, start, mid, end) + override def rangePos(source: SourceFile, start: Int, point: Int, end: Int) = + new RangePosition(source, start, point, end) /** Called from typechecker: signal that a node has been completely typechecked * @param context The context that typechecked the node @@ -85,7 +85,7 @@ self => scheduler.pollException() match { case Some(ex: CancelActionReq) => if (acting) throw ex case Some(ex: FreshRunReq) => if (compiling) throw ex - case Some(ex) => throw ex + case Some(ex: Throwable) => throw ex case _ => } scheduler.nextWorkItem() match { @@ -104,9 +104,6 @@ self => // ----------------- The Background Runner Thread ----------------------- - /* Must be initialized before starting compilerRunner */ - private val scheduler = new WorkScheduler - /** The current presentation compiler runner */ private var compileRunner = newRunnerThread @@ -220,25 +217,6 @@ self => // ---------------- Helper classes --------------------------- - /** A locator for trees with given positions. - * Given a position `pos`, locator.apply returns - * the smallest tree that encloses `pos`. - */ - class Locator(pos: Position) extends Traverser { - var last: Tree = _ - def locateIn(root: Tree): Tree = { - this.last = EmptyTree - traverse(root) - this.last - } - override def traverse(t: Tree) { - 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 = { @@ -254,6 +232,10 @@ self => object ResetAttrs extends Traverser { override def traverse(t: Tree) { if (t.hasSymbol) t.symbol = NoSymbol + t match { + case EmptyTree => ; + } + t.tpe = null super.traverse(t) } @@ -306,83 +288,7 @@ self => class TyperResult(val tree: Tree) extends Exception - class RichCompilationUnit(source: SourceFile) extends CompilationUnit(source) { - - /** The runid of the latest compiler run that typechecked this unit, - * or else @see NotLoaded, JustParsed - */ - 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 ------------------------------------ - - /** The compilation unit corresponding to a source file */ - 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): RichCompilationUnit = unitOf(pos.source.get) - - /** Locate smallest tree that encloses position */ - def locateTree(pos: Position): Tree = - 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(sources: List[SourceFile], result: SyncVar[Either[Unit, Throwable]]) = - scheduler.postWorkItem(() => reload(sources, result)) - - /** Set sync var `result` to a fully attributed tree located at position `pos` */ - def askTypeAt(pos: Position, result: SyncVar[Either[Tree, Throwable]]) = - scheduler.postWorkItem(() => self.typedTreeAt(pos, result)) - - /** Ask to do unit first on present and subsequent type checking passes */ - def askToDoFirst(f: SourceFile) = { - scheduler.postWorkItem { () => moveToFront(List(f)) } - } - - /** Cancel currently pending high-priority jobs */ - def askCancel() = - scheduler.raise(new CancelActionReq) - - /** Cancel current compiler run and start a fresh one where everything will be re-typechecked - * (but not re-loaded). - */ - def askReset() = - scheduler.raise(new FreshRunReq) - - /** Tell the compile server to shutdown, and do not restart again */ - def askShutdown() = - scheduler.raise(new ShutdownReq) - - class CancelActionReq extends Exception - class FreshRunReq extends Exception - class ShutdownReq extends Exception } diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala new file mode 100644 index 0000000000..cd83ddf082 --- /dev/null +++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala @@ -0,0 +1,72 @@ +package scala.tools.nsc.interactive + +import scala.concurrent.SyncVar +import scala.tools.nsc.io.AbstractFile +import scala.tools.nsc.util.{SourceFile, Position, WorkScheduler} +import scala.tools.nsc.symtab._ +import scala.tools.nsc.ast._ + +/** Interface of interactive compiler to a client such as an IDE + */ +object REPL /* extends EvalLoop */ { +} + + /** Commands: + * + * reload file1 ... fileN + * typeat file line col + * + * + def run() { + loop { line => + + + } + + def process(args: Array[String]) { + val settings = new Settings(error) + reporter = new ConsoleReporter(settings) + val command = new CompilerCommand(List.fromArray(args), settings, error, false) + if (command.settings.version.value) + reporter.info(null, versionMsg, true) + else { + if (command.settings.target.value == "msil") { + val libpath = System.getProperty("msil.libpath") + if (libpath != null) + command.settings.assemrefs.value = + command.settings.assemrefs.value + File.pathSeparator + libpath + } + try { + object compiler extends Global(command.settings, reporter) + if (reporter.hasErrors) { + reporter.flush() + return + } + + if (command.shouldStopWithInfo) { + reporter.info(null, command.getInfoMessage(compiler), true) + } else { + if (command.settings.resident.value) + resident(compiler) + else if (command.files.isEmpty) { + reporter.info(null, command.usageMsg, true) + reporter.info(null, compiler.pluginOptionsHelp, true) + } else { + val run = new compiler.Run() + run compile command.files + reporter.printSummary() + } + } + } catch { + case ex @ FatalError(msg) => + if (true || command.settings.debug.value) // !!! + ex.printStackTrace(); + reporter.error(null, "fatal error: " + msg) + } + } + } + val comp = new Global + + + def + */ diff --git a/src/compiler/scala/tools/nsc/interactive/RichCompilationUnits.scala b/src/compiler/scala/tools/nsc/interactive/RichCompilationUnits.scala new file mode 100644 index 0000000000..b8b59b67ae --- /dev/null +++ b/src/compiler/scala/tools/nsc/interactive/RichCompilationUnits.scala @@ -0,0 +1,29 @@ +package scala.tools.nsc.interactive + +import scala.tools.nsc.util.{SourceFile, Position, NoPosition} + +trait RichCompilationUnits { self: Global => + + class RichCompilationUnit(source: SourceFile) extends CompilationUnit(source) { + + /** The runid of the latest compiler run that typechecked this unit, + * or else @see NotLoaded, JustParsed + */ + 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 + + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 6bba3e74bf..6411ddf532 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -820,6 +820,7 @@ trait Types { trait UniqueType { override lazy val hashCode: Int = super.hashCode() } + /** A base class for types that defer some operations * to their immediate supertype. */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7dc60b91dc..89e2028ded 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3412,10 +3412,15 @@ trait Typers { self: Analyzer => newTyper(context.makeNewScope(tree, context.owner)).typedExistentialTypeTree(etpt, mode) case TypeTree() => - // we should get here only when something before failed - // and we try again (@see tryTypedApply). In that case we can assign - // whatever type to tree; we just have to survive until a real error message is issued. - tree setType AnyClass.tpe + tree.pos match { + case SyntheticPosition(original) => + tree setType typedType(original, mode).tpe + case _ => + // we should get here only when something before failed + // and we try again (@see tryTypedApply). In that case we can assign + // whatever type to tree; we just have to survive until a real error message is issued. + tree setType AnyClass.tpe + } case _ => throw new Error("unexpected tree: " + tree.getClass + "\n" + tree)//debug } diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index 6d494d5c3f..a62974c426 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -16,14 +16,17 @@ trait Position { def offset: Option[Int] = None def source: Option[SourceFile] = None def isDefined: Boolean = false + def isSynthetic: Boolean = false - def start: Int = mid - def mid: Int = offset.get - def end: Int = mid + def start: Int = point + def point: Int = offset.get + def end: Int = point - def startOrElse(d: Int) = offset.get//OrElse(d) - def midOrElse(d: Int) = offset.get//OrElse(d) - def endOrElse(d: Int) = offset.get//OrElse(d) + def startOrElse(d: Int) = offset.getOrElse(d) + def pointOrElse(d: Int) = offset.getOrElse(d) + def endOrElse(d: Int) = offset.getOrElse(d) + + def underlying = this def includes(pos: Position) = isDefined && pos.isDefined && start <= pos.start && pos.end <= end @@ -105,12 +108,13 @@ 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) { +class RangePosition(source0: SourceFile, override val start: Int, override val point: Int, override val end: Int) +extends OffsetPosition(source0, point) { override def isDefined = true override def startOrElse(d: Int) = start - override def midOrElse(d: Int) = mid + override def pointOrElse(d: Int) = point override def endOrElse(d: Int) = end } + -- cgit v1.2.3