From 2039b7fec7b902e3cef0a9c31a94ea96c2e469c8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 27 May 2009 16:19:05 +0000 Subject: Fixed problem that spurious caused exhaustivene... Fixed problem that spurious caused exhaustiveness warning for RefChecks. Enriched positions and worked on the interactive compiler. --- .../scala/tools/nsc/ast/NodePrinters.scala | 2 - .../scala/tools/nsc/ast/TreePrinters.scala | 2 +- src/compiler/scala/tools/nsc/ast/Trees.scala | 12 ++-- .../scala/tools/nsc/ast/parser/Parsers.scala | 21 +++---- .../scala/tools/nsc/interactive/Global.scala | 41 ++++++++++--- .../scala/tools/nsc/interactive/REPL.scala | 70 ++++++++++++++++++---- .../scala/tools/nsc/symtab/Definitions.scala | 4 ++ src/compiler/scala/tools/nsc/symtab/StdNames.scala | 1 + .../scala/tools/nsc/typechecker/Typers.scala | 30 +++++++--- src/compiler/scala/tools/nsc/util/Position.scala | 3 + src/compiler/scala/tools/nsc/util/trace.scala | 4 ++ 11 files changed, 145 insertions(+), 45 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 57e60f9fba..4292cb24a0 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -341,8 +341,6 @@ abstract class NodePrinters { } printcln(")") } else printcln(p.productPrefix) - case _ => - printcln("***" + tree.getClass) } } } diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala index 4deccddde6..9e9b801d25 100644 --- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala @@ -389,7 +389,7 @@ abstract class TreePrinters { } def print(tree: Tree) { - if (settings.Xprintpos.value) print("[" + tree.pos + "]") + if (settings.Xprintpos.value) print(tree.pos.show) printRaw( if (tree.isDef && tree.symbol != NoSymbol && tree.symbol.isInitialized) { tree match { diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 5e930bb4fc..12c5c9c46d 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -96,7 +96,7 @@ trait Trees { // @M helper method for asserts that check consistency in kinding //def kindingIrrelevant(tp: Type) = (tp eq null) || phase.name == "erasure" || phase.erasedTypes - abstract class Tree { + abstract class Tree extends Product { { import util.Statistics if (Statistics.enabled) nodeCount += 1 @@ -159,6 +159,10 @@ trait Trees { /** Is there part of this tree which satisfies predicate `p'? */ def exists(p: Tree => Boolean): Boolean = !find(p).isEmpty + /** The direct children of this tree */ + def children(): Iterator[Tree] = + productElements filter (_.isInstanceOf[Tree]) map (_.asInstanceOf[Tree]) + override def toString(): String = { val buffer = new StringWriter() val printer = treePrinters.create(new PrintWriter(buffer)) @@ -190,10 +194,7 @@ trait Trees { i += 1 } } - this match { - case p : Product => g(p) - case _ => - } + g(this) hc } def equalsStructure(that : Tree) = equalsStructure0(that){case (t0,t1) => false} @@ -1788,6 +1789,7 @@ trait Trees { override def end: Int = original.pos.end override def underlying = original.pos.underlying override def focus = original.pos.focus + override def show = "["+underlying.show+"]" } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 0aa4907fc6..4254be02d5 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -114,6 +114,7 @@ self => def freshName(pos: Position, prefix: String): Name def o2p(offset: Int): Position def r2p(start: Int, mid: Int, end: Int): Position + def t2p(tree: Tree): Position = SyntheticPosition(tree) //private implicit def p2i(pos: Position) = pos.offset.get /** whether a non-continuable syntax error has been seen */ @@ -370,7 +371,7 @@ self => */ def joinComment(trees: => List[Tree]): List[Tree] = { val buf = in.flushDoc - if ((buf ne null) && buf.length > 0) trees map (t => DocDef(buf, t) setPos t.pos) + if ((buf ne null) && buf.length > 0) trees map (t => DocDef(buf, t) setPos t.pos) // !!! take true comment position else trees } @@ -403,13 +404,13 @@ self => tree match { case Ident(name) => removeAsPlaceholder(name) - ValDef(Modifiers(Flags.PARAM), name, TypeTree(), EmptyTree) + ValDef(Modifiers(Flags.PARAM), name, TypeTree() setPos o2p(tree.pos.end), EmptyTree) case Typed(tree @ Ident(name), tpe) if (tpe.isType) => // get the ident! removeAsPlaceholder(name) ValDef(Modifiers(Flags.PARAM), name, tpe, EmptyTree) case _ => syntaxError(tree.pos, "not a legal formal parameter", false) - ValDef(Modifiers(Flags.PARAM), nme.ERROR, errorTypeTree, EmptyTree) + ValDef(Modifiers(Flags.PARAM), nme.ERROR, errorTypeTree setPos o2p(tree.pos.end), EmptyTree) } } @@ -812,7 +813,7 @@ self => def wildcardType(start: Int) = { val pname = freshName(o2p(start), "_$").toTypeName val t = atPos(start) { Ident(pname) } - val param = atPos(start) { makeSyntheticTypeParam(pname, typeBounds()) } + val param = atPos(t2p(t)) { makeSyntheticTypeParam(pname, typeBounds()) } placeholderTypes = param :: placeholderTypes t } @@ -1130,12 +1131,12 @@ self => path(true, false) case USCORE => val start = in.offset - atPos(in.skipToken()) { - val pname = freshName(o2p(start), "x$") - val param = atPos(start){ makeSyntheticParam(pname) } - placeholderParams = param :: placeholderParams - Ident(pname) - } + val pname = freshName(o2p(start), "x$") + val id = atPos(start) (Ident(pname)) + in.nextToken() + val param = atPos(t2p(id)){ makeSyntheticParam(pname) } + placeholderParams = param :: placeholderParams + id case LPAREN => atPos(in.skipToken()) { val ts = if (in.token == RPAREN) List() else exprs() diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index 0ad7c1b5aa..44001b4b64 100755 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -28,8 +28,8 @@ self => /** The currently active typer run */ private var currentTyperRun: TyperRun = _ - /** Is a background compiler currently running? */ - private var compiling = false + /** Is a background compiler run needed? */ + private var outOfDate = false /** Is a reload/ background compiler currently running? */ private var acting = false @@ -84,7 +84,7 @@ self => def pollForWork() { scheduler.pollException() match { case Some(ex: CancelActionReq) => if (acting) throw ex - case Some(ex: FreshRunReq) => if (compiling) throw ex + case Some(ex: FreshRunReq) => if (outOfDate) throw ex case Some(ex: Throwable) => throw ex case _ => } @@ -115,16 +115,13 @@ self => while (true) { scheduler.waitForMoreWork() pollForWork() - var continue = true - while (continue) { + while (outOfDate) { try { - compiling = true backgroundCompile() - continue = false } catch { case ex: FreshRunReq => } finally { - compiling = false + outOfDate = false } } } @@ -190,6 +187,7 @@ self => val unit = new RichCompilationUnit(source) unitOfFile(source.file) = unit currentTyperRun.compileLate(unit) + validatePositions(unit.body) unit.status = JustParsed } moveToFront(sources) @@ -199,7 +197,8 @@ self => result set Right(ex) throw ex } - if (compiling) throw new FreshRunReq + if (outOfDate) throw new FreshRunReq + else outOfDate = true } /** Set sync var `result` to a fully attributed tree located at position `pos` */ @@ -218,6 +217,29 @@ self => // ---------------- Helper classes --------------------------- + + def validatePositions(tree: Tree) { + def check(condition: Boolean, msg: => String) { + if (!condition) { + println("**** bad positions:") + println(msg) + println("================= in =================") + println(tree) + } + } + def validate(tree: Tree, encltree: Tree, lefttree: Tree) { + if (encltree.pos.isSynthetic) check(tree.pos.isSynthetic, "synthetic "+encltree+" contains nonsynthetic" + tree) + check(encltree.pos includes tree.pos, encltree+" does not include "+tree) + if (lefttree != EmptyTree) check(lefttree.pos precedes tree.pos, lefttree+" does not not precede "+tree) + var newleft: Tree = EmptyTree + for (ct <- tree.children) { + validate(ct, tree, newleft) + newleft = ct + } + } + validate(tree, tree, EmptyTree) + } + /** 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 = { @@ -262,6 +284,7 @@ self => */ def typedTreeAt(pos: Position): Tree = { val tree = locateTree(pos) +// println("at pos "+pos+" was found: "+tree) if (tree.tpe ne null) tree else { val unit = unitOf(pos) diff --git a/src/compiler/scala/tools/nsc/interactive/REPL.scala b/src/compiler/scala/tools/nsc/interactive/REPL.scala index 58477dd693..e773f9f281 100644 --- a/src/compiler/scala/tools/nsc/interactive/REPL.scala +++ b/src/compiler/scala/tools/nsc/interactive/REPL.scala @@ -9,12 +9,66 @@ import scala.tools.nsc.io._ /** Interface of interactive compiler to a client such as an IDE */ -object REPL extends EvalLoop { +object REPL { - val settings = new Settings() - val comp = new Global(settings, new ConsoleReporter(settings)) + val versionMsg = "Scala compiler " + + Properties.versionString + " -- " + + Properties.copyrightString - def prompt = "> " + val prompt = "> " + + var reporter: ConsoleReporter = _ + + def error(msg: String) { + reporter.error(/*new Position */FakePos("scalac"), + msg + "\n scalac -help gives more information") + } + + def process(args: Array[String]) { + val settings = new Settings(error) + reporter = new ConsoleReporter(settings) + val command = new CompilerCommand(args.toList, settings, error, false) + if (command.settings.version.value) + reporter.info(null, versionMsg, true) + else { + 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 { + run(compiler) + } + } catch { + case ex @ FatalError(msg) => + if (true || command.settings.debug.value) // !!! + ex.printStackTrace(); + reporter.error(null, "fatal error: " + msg) + } + } + } + + def main(args: Array[String]) { + process(args) + exit(if (reporter.hasErrors) 1 else 0) + } + + def loop(action: (String) => Unit) { + Console.print(prompt) + try { + val line = Console.readLine + if (line.length() > 0) { + action(line) + } + loop(action) + } + catch { + case _: java.io.EOFException => //nop + } + } /** Commands: * @@ -23,12 +77,10 @@ object REPL extends EvalLoop { * * */ - def run() { + def run(comp: Global) { val reloadResult = new SyncVar[Either[Unit, Throwable]] val typeatResult = new SyncVar[Either[comp.Tree, Throwable]] loop { line => - println("["+line+"]") - println((line split " ").toList) (line split " ").toList match { case "reload" :: args => comp.askReload(args map toSourceFile, reloadResult) @@ -55,8 +107,4 @@ object REPL extends EvalLoop { case Right(exc/*: Throwable ??*/) => exc.printStackTrace; println("ERROR: "+exc) } } - - def main(args: Array[String]) { - run() - } } diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index ca8ad48224..03783d054c 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -37,6 +37,9 @@ trait Definitions { lazy val ScalaPackage: Symbol = getModule("scala") lazy val ScalaPackageClass: Symbol = ScalaPackage.tpe.typeSymbol + lazy val ScalaCollectionImmutablePackage: Symbol = getModule("scala.collection.immutable") + lazy val ScalaCollectionImmutablePackageClass: Symbol = ScalaCollectionImmutablePackage.tpe.typeSymbol + var AnyClass: Symbol = _ var AnyValClass: Symbol = _ var AnyRefClass: Symbol = _ @@ -111,6 +114,7 @@ trait Definitions { def Iterable_hasNext = getMember(IterableClass, nme.hasNext) lazy val IteratorClass: Symbol = getClass2("scala.Iterator", "scala.collection.Iterator") lazy val SeqClass: Symbol = getClass2("scala.Seq", "scala.collection.Sequence") + lazy val SeqModule: Symbol = getModule2("scala.Seq", "scala.collection.Sequence") lazy val TraversableClass: Symbol = getClass("scala.collection.Traversable") lazy val RandomAccessSeqMutableClass: Symbol = getMember( getModule2("scala.RandomAccessSeq", "scala.collection.Vector"), nme.Mutable) diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index bb04630985..f19a8e5138 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -234,6 +234,7 @@ trait StdNames { val Function = newTermName("Function") val Function1 = newTermName("Function1") val Int = newTermName("Int") + val List = newTermName("List") val Long = newTermName("Long") val Nil = newTermName("Nil") val Object = newTermName("Object") diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 89e2028ded..68ae453793 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -560,10 +560,25 @@ trait Typers { self: Analyzer => /** Make symbol accessible. This means: * If symbol refers to package object, insert `.package` as second to last selector. - * Call checkAccessible, which sets symbol's attributes. + * (exception for some symbols in scala package which are dealiased immediately) + * Call checkAccessible, which sets tree's attributes. + * @return modified tree and new prefix type */ - private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree = + private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): (Tree, Type) = if (isInPackageObject(sym, pre.typeSymbol)) { + if (pre.typeSymbol == ScalaPackageClass && sym.isTerm) { + // short cut some aliases. It seems that without that pattern matching + // fails to notice exhaustiveness and to generate good code when + // List extractors are mixed with :: patterns. See Test5 in lists.scala. + def dealias(sym: Symbol) = + (atPos(tree.pos) { gen.mkAttributedRef(sym) }, sym.owner.thisType) + sym.name match { + case nme.List => return dealias(ListModule) + case nme.Seq => return dealias(SeqModule) + case nme.Nil => return dealias(NilModule) + case _ => + } + } val qual = typedQualifier { tree match { case Ident(_) => Ident(nme.PACKAGEkw) @@ -578,10 +593,9 @@ trait Typers { self: Analyzer => case SelectFromTypeTree(_, name) => SelectFromTypeTree(qual, name) } } - val tree2 = checkAccessible(tree1, sym, qual.tpe, qual) - tree2 + (checkAccessible(tree1, sym, qual.tpe, qual), qual.tpe) } else { - checkAccessible(tree, sym, pre, site) + (checkAccessible(tree, sym, pre, site), pre) } private def isInPackageObject(sym: Symbol, pkg: Symbol) = @@ -2943,7 +2957,8 @@ trait Typers { self: Analyzer => case SelectFromTypeTree(_, _) => copy.SelectFromTypeTree(tree, qual, name) } //if (name.toString == "Elem") println("typedSelect "+qual+":"+qual.tpe+" "+sym+"/"+tree1+":"+tree1.tpe) - val result = stabilize(makeAccessible(tree1, sym, qual.tpe, qual), qual.tpe, mode, pt) + val (tree2, pre2) = makeAccessible(tree1, sym, qual.tpe, qual) + val result = stabilize(tree2, pre2, mode, pt) def isPotentialNullDeference() = { phase.id <= currentRun.typerPhase.id && !sym.isConstructor && @@ -3103,7 +3118,8 @@ trait Typers { self: Analyzer => val tree1 = if (qual == EmptyTree) tree else atPos(tree.pos)(Select(qual, name)) // atPos necessary because qualifier might come from startContext - stabilize(makeAccessible(tree1, defSym, pre, qual), pre, mode, pt) + val (tree2, pre2) = makeAccessible(tree1, defSym, pre, qual) + stabilize(tree2, pre2, mode, pt) } } diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index bd60035a5a..48343fd948 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -98,6 +98,7 @@ trait Position { }) } + def show: String = "["+toString+"]" } case object NoPosition extends Position @@ -114,6 +115,7 @@ case class OffsetPosition(source0: SourceFile, offset0: Int) extends Position { case that => false } override def hashCode = offset0 * 37 + source0.file.hashCode + override def show = "["+point+"]" } /** new for position ranges */ @@ -125,6 +127,7 @@ extends OffsetPosition(source0, point) { override def endOrElse(d: Int) = end override def focus = OffsetPosition(source0, point) override def toString = "RangePosition("+source0+", "+start+", "+point+", "+end+")" + override def show = "["+start+":"+end+"]" } diff --git a/src/compiler/scala/tools/nsc/util/trace.scala b/src/compiler/scala/tools/nsc/util/trace.scala index a24a18ec45..dc78756ec6 100644 --- a/src/compiler/scala/tools/nsc/util/trace.scala +++ b/src/compiler/scala/tools/nsc/util/trace.scala @@ -5,4 +5,8 @@ object trace { println(msg+value) value } + def withFun[T, U](msg: String)(value: T)(fun: T => U): T = { + println(msg+fun(value)) + value + } } -- cgit v1.2.3