From 22b60f2f2b6dbacbd528d10cb18da8e5afe750da Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 22 May 2009 15:13:22 +0000 Subject: some documentation; statistics wrt implicits; n... some documentation; statistics wrt implicits; new presentation compiler --- .../scala/tools/nsc/CompilationUnits.scala | 4 + src/compiler/scala/tools/nsc/CompileServer.scala | 2 +- src/compiler/scala/tools/nsc/Global.scala | 205 ++++++++++++++------- src/compiler/scala/tools/nsc/Interpreter.scala | 2 +- src/compiler/scala/tools/nsc/Main.scala | 4 +- src/compiler/scala/tools/nsc/ScalaDoc.scala | 2 +- .../scala/tools/nsc/ast/parser/Scanners.scala | 46 +++-- .../scala/tools/nsc/interactive/Global.scala | 41 +++-- src/compiler/scala/tools/nsc/symtab/Types.scala | 8 + .../scala/tools/nsc/typechecker/Analyzer.scala | 32 ++++ .../scala/tools/nsc/typechecker/Implicits.scala | 36 +++- .../scala/tools/nsc/typechecker/Typers.scala | 33 +++- .../scala/tools/nsc/util/WorkScheduler.scala | 43 +++++ 13 files changed, 339 insertions(+), 119 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/util/WorkScheduler.scala diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index b38ac01c8a..5385a5a54f 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -26,6 +26,7 @@ trait CompilationUnits { self: Global => * To get their sourcefiles, you need to dereference with .sourcefile */ val depends = new HashSet[Symbol] + /** so we can relink */ val defined = new HashSet[Symbol] @@ -40,6 +41,9 @@ 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 icode representation of classes in this compilation unit. diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index 5d14064728..010d183bc8 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -113,7 +113,7 @@ class StandardCompileServer extends SocketServer { compiler = newGlobal(command.settings, reporter) } val c = compiler - val run = new c.Run + val run = new c.Run() run compile command.files } catch { case ex @ FatalError(msg) => diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 5b23ce2c45..fff92aac64 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -38,10 +38,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable with PhaseAssembly { // alternate constructors ------------------------------------------ + def this(reporter: Reporter) = this(new Settings(err => reporter.error(null,err)), reporter) - def this(settings: Settings) = this(settings, new ConsoleReporter(settings)) @@ -49,6 +49,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // sub-components -------------------------------------------------- + /** Print tree in detailed form */ object nodePrinters extends { val global: Global.this.type = Global.this } with NodePrinters { @@ -56,6 +57,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } val nodeToString = nodePrinters.nodeToString + /** Generate ASTs */ object gen extends { val global: Global.this.type = Global.this } with TreeGen { @@ -63,38 +65,47 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable typer.typed(mkAttributedCastUntyped(tree, pt)) } + /** Fold constants */ object constfold extends { val global: Global.this.type = Global.this } with ConstantFolder + /** Tree checker (used for testing and debugging) */ object checker extends { val global: Global.this.type = Global.this } with TreeCheckers + /** ICode generator */ object icodes extends { val global: Global.this.type = Global.this } with ICodes + /** ICode analysis for optimization */ object analysis extends { val global: Global.this.type = Global.this } with TypeFlowAnalysis + /** Copy propagation for optimization */ object copyPropagation extends { val global: Global.this.type = Global.this } with CopyPropagation + /** Icode verification */ object checkers extends { val global: Global.this.type = Global.this } with Checkers + /** Some statistics (normally disabled) */ object statistics extends { val global: Global.this.type = Global.this } with Statistics + /** Computing pairs of overriding/overridden symbols */ object overridingPairs extends { val global: Global.this.type = Global.this } with OverridingPairs + /** Representing ASTs as graphs */ object treeBrowsers extends { val global: Global.this.type = Global.this } with TreeBrowsers @@ -104,22 +115,39 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // val copy = new LazyTreeCopier() + /** A map of all doc comments, indexed by symbols. + * Only active in onlyPresentation mode + */ val comments = if (onlyPresentation) new HashMap[Symbol,String] else null + + /** A map of argument names for methods + * !!! can be dropped once named method arguments are in !!! + */ val methodArgumentNames = if (onlyPresentation) new HashMap[Symbol,List[List[Symbol]]] else null -// reporting ------------------------------------------------------- + // ------------ Hooks for IDE ---------------------------------- + + /** 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 + */ + def pollForHighPriorityJob() {} + +// ------------------ Reporting ------------------------------------- + import nsc.util.NoPosition def error(msg: String) = reporter.error(NoPosition, msg) def warning(msg: String) = reporter.warning(NoPosition, msg) def inform(msg: String) = Console.err.println(msg) def inform[T](msg: String, value: T): T = { inform(msg+value); value } - def rangePos(source: SourceFile, start: Int, mid: Int, end: Int) = OffsetPosition(source, mid) - //reporter.info(null, msg, true) def informProgress(msg: String) = @@ -155,7 +183,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable def abort(msg: String) = throw new Error(msg) -// file interface ------------------------------------------------------- +// ------------ File interface ----------------------------------------- private val reader: SourceReader = { def stdCharset: Charset = { @@ -204,7 +232,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable set } - if (settings.verbose.value) { inform("[Classpath = " + classPath + "]") if (forMSIL) inform("[AssemRefs = " + settings.assemrefs.value + "]") @@ -235,7 +262,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable if (forMSIL) new loaders.NamespaceLoader(classPath.root) else new loaders.PackageLoader(classPath.root /* getRoot() */) -// Phases ------------------------------------------------------------} +// ------------ Phases -------------------------------------------} var globalPhase: Phase = NoPhase @@ -575,61 +602,85 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable messages.mkString("\n") } + // ----------- Runs --------------------------------------- private var curRun: Run = null - def currentRun: Run = curRun private var curRunId = 0 - override def currentRunId = curRunId - private var runCount = 0 + /** The currently active run + */ + def currentRun: Run = curRun - class Run { - curRunId += 1 - assert(curRunId > 0) - //Console.println("starting run: " + id) - var currentUnit: CompilationUnit = _ - curRun = this - // Can not take the phaseDescriptors.head even though its the syntaxAnalyser, this will implicitly - // call definitions.init which uses phase and needs it to be != NoPhase - private val firstPhase = syntaxAnalyzer.newPhase(NoPhase) - phase = firstPhase - definitions.init // needs phase to be defined != NoPhase, - // that's why it is placed here. - - /** Deprecation warnings occurred */ - var deprecationWarnings: Boolean = false - var uncheckedWarnings: Boolean = false + /** The id of the currently active run + */ + override def currentRunId = curRunId - // The first phase in the compiler phase chain - private var p: Phase = firstPhase + /** A Run is a single execution of the compiler on a sets of units + */ + class Run { - protected def stopPhase(name : String) = settings.stop.contains(name) + private val firstPhase = { + // ----------- Initialization code ------------------------- + curRunId += 1 + assert(curRunId > 0) + curRun = this + //Console.println("starting run: " + id) + + // Can not take the phaseDescriptors.head even though its the syntaxAnalyser, this will implicitly + // call definitions.init which uses phase and needs it to be != NoPhase + val phase1 = syntaxAnalyzer.newPhase(NoPhase) + phase = phase1 + definitions.init // needs phase to be defined != NoPhase, + // that's why it is placed here. + + // The first phase in the compiler phase chain + var p: Phase = phase1 + + // Reset the cache in terminal, the chain could have been build before where nobody used it + // This happens in the interpreter + terminal.reset + + // Each subcomponent is asked to deliver a newPhase that is chained together. If -Ystop:phasename is + // given at command-line, this will stop with that phasename + for (pd <- phaseDescriptors.tail.takeWhile(pd => !(stopPhase(pd.phaseName)))) + if (!(settings.skip contains pd.phaseName)) p = pd.newPhase(p) + + // Ensure there is a terminal phase at the end, Normally there will then be two terminal phases at the end + // if -Ystop:phasename was given, this makes sure that there is a terminal phase at the end + p = terminal.newPhase(p) + + phase1 + } - // Reset the cache in terminal, the chain could have been build before where nobody used it - // This happens in the interpreter - terminal.reset + // --------------- Miscellania ------------------------------- - // Each subcomponent is asked to deliver a newPhase that is chained together. If -Ystop:phasename is - // given at command-line, this will stop with that phasename - for (pd <- phaseDescriptors.tail.takeWhile(pd => !(stopPhase(pd.phaseName)))) - if (!(settings.skip contains pd.phaseName)) p = pd.newPhase(p) + /** The currently compiled unit; set from GlobalPhase */ + var currentUnit: CompilationUnit = _ - // Ensure there is a terminal phase at the end, Normally there will then be two terminal phases at the end - // if -Ystop:phasename was given, this makes sure that there is a terminal phase at the end - p = terminal.newPhase(p) + /** Flags indicating whether deprecation warnings occurred */ + var deprecationWarnings: Boolean = false + var uncheckedWarnings: Boolean = false def cancel { reporter.cancelled = true } - // progress tracking + // ------------------ Progress tracking ------------------------- + def progress(current: Int, total: Int) {} private var phasec: Int = 0 private var unitc: Int = 0 + + /** take note that phase is completed + * (for progress reporting) + */ def advancePhase { unitc = 0 phasec += 1 refreshProgress } + /** take note that a phase on a unit is completed + * (for progress reporting) + */ def advanceUnit { unitc += 1 refreshProgress @@ -639,6 +690,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable progress((phasec * fileset.size) + unitc, (phaseDescriptors.length-1) * fileset.size) // terminal phase not part of the progress display + // ----- finding phases -------------------------------------------- + def phaseNamed(name: String): Phase = { var p: Phase = firstPhase while (p.next != p && p.name != name) p = p.next @@ -656,14 +709,21 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val mixinPhase = phaseNamed("mixin") val icodePhase = phaseNamed("icode") + /** A test whether compilation should stop at phase with given name */ + protected def stopPhase(name : String) = settings.stop.contains(name) + + // ----------- Units and top-level classes and objects -------- + private var unitbuf = new ListBuffer[CompilationUnit] private var fileset = new HashSet[AbstractFile] + /** add unit to be compiled in this run */ private def addUnit(unit: CompilationUnit) { unitbuf += unit fileset += unit.source.file } + /* An iterator returning all the units being compiled in this run */ def units: Iterator[CompilationUnit] = unitbuf.elements /** A map from compiled top-level symbols to their source files */ @@ -680,6 +740,9 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable else if (sym.isModuleClass) compiles(sym.sourceModule) else false + // --------------- Compilation methods ---------------------------- + + /** Compile list of source files */ def compileSources(_sources: List[SourceFile]) { val sources = dependencyAnalysis.filter(_sources.removeDuplicates) // bug #1268, scalac confused by duplicated filenames if (reporter.hasErrors) @@ -715,7 +778,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable warning("It is not possible to check the result of the "+globalPhase.name+" phase") } } - if (settings.statistics.value) statistics.print(phase) advancePhase } @@ -745,29 +807,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable dependencyAnalysis.writeToFile(); } - def compileLate(file: AbstractFile) { - if (fileset eq null) { - val msg = "No class file for " + file + - " was found\n(This file cannot be loaded as a source file)" - inform(msg) - throw new FatalError(msg) - } - else if (!(fileset contains file)) { - val unit = new CompilationUnit(getSourceFile(file)) - addUnit(unit) - var localPhase = firstPhase.asInstanceOf[GlobalPhase] - while (localPhase != null && (localPhase.id < globalPhase.id || localPhase.id <= namerPhase.id) && !reporter.hasErrors) { - val oldSource = reporter.getSource - reporter.setSource(unit.source) - atPhase(localPhase)(localPhase.applyPhase(unit)) - val newLocalPhase = localPhase.next.asInstanceOf[GlobalPhase] - localPhase = if (localPhase == newLocalPhase) null else newLocalPhase - reporter.setSource(oldSource) - } - refreshProgress - } - } - + /** Compile list of abstract files */ def compileFiles(files: List[AbstractFile]) { try { compileSources(files map getSourceFile) @@ -776,6 +816,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } } + /** Compile list of files given by their names */ def compile(filenames: List[String]) { try { val scriptMain = settings.script.value @@ -795,6 +836,41 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } } + /** Compile abstract file until `globalPhase`, but at least + * to phase "namer". + */ + def compileLate(file: AbstractFile) { + if (fileset eq null) { + val msg = "No class file for " + file + + " was found\n(This file cannot be loaded as a source file)" + inform(msg) + throw new FatalError(msg) + } + else if (!(fileset contains file)) { + compileLate(new CompilationUnit(getSourceFile(file))) + } + } + + /** Compile abstract file until `globalPhase`, but at least + * to phase "namer". + */ + def compileLate(unit: CompilationUnit) { + addUnit(unit) + var localPhase = firstPhase.asInstanceOf[GlobalPhase] + while (localPhase != null && (localPhase.id < globalPhase.id || localPhase.id <= namerPhase.id) && !reporter.hasErrors) { + val oldSource = reporter.getSource + reporter.setSource(unit.source) + atPhase(localPhase)(localPhase.applyPhase(unit)) + val newLocalPhase = localPhase.next.asInstanceOf[GlobalPhase] + localPhase = if (localPhase == newLocalPhase) null else newLocalPhase + reporter.setSource(oldSource) + } + refreshProgress + } + + /** Reset package class to state at typer (not sure what this + * is needed for?) + */ private def resetPackageClass(pclazz: Symbol) { atPhase(firstPhase) { pclazz.setInfo(atPhase(typerPhase)(pclazz.info)) @@ -803,7 +879,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } } // class Run - def printAllUnits() { print("[[syntax trees at end of " + phase + "]]") atPhase(phase.next) { diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index bf48b2bbe6..0fbd115f4d 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -410,7 +410,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) */ def interpret(line: String): IR.Result = { // initialize the compiler - if (prevRequests.isEmpty) new compiler.Run + if (prevRequests.isEmpty) new compiler.Run() // parse val trees = parse(indentCode(line)) match { diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala index bfc6681b5b..36e0b67457 100644 --- a/src/compiler/scala/tools/nsc/Main.scala +++ b/src/compiler/scala/tools/nsc/Main.scala @@ -36,7 +36,7 @@ object Main extends AnyRef with EvalLoop { loop { line => val args = List.fromString(line, ' ') val command = new CompilerCommand(args, new Settings(error), error, true) - (new compiler.Run) compile command.files + new compiler.Run() compile command.files } } @@ -69,7 +69,7 @@ object Main extends AnyRef with EvalLoop { reporter.info(null, command.usageMsg, true) reporter.info(null, compiler.pluginOptionsHelp, true) } else { - val run = new compiler.Run + val run = new compiler.Run() run compile command.files reporter.printSummary() } diff --git a/src/compiler/scala/tools/nsc/ScalaDoc.scala b/src/compiler/scala/tools/nsc/ScalaDoc.scala index 092f02a259..564207c478 100644 --- a/src/compiler/scala/tools/nsc/ScalaDoc.scala +++ b/src/compiler/scala/tools/nsc/ScalaDoc.scala @@ -71,7 +71,7 @@ object ScalaDoc { else if (command.settings.showPhases.value) reporter.info(null, compiler.phaseDescriptions, true) else { - val run = new compiler.Run + val run = new compiler.Run() run compile command.files val generator = new DefaultDocDriver { lazy val global: compiler.type = compiler diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 82dcd74fef..bd32213837 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -811,31 +811,29 @@ trait Scanners { // ------------- character classification -------------------------------- - def isIdentifierStart(c: Char): Boolean = ( - ('A' <= c && c <= 'Z') || - ('a' <= c && c <= 'a') || - (c == '_') || (c == '$') || - Character.isUnicodeIdentifierStart(c) - ) - - def isIdentifierPart(c: Char) = ( - isIdentifierStart(c) || - ('0' <= c && c <= '9') || - Character.isUnicodeIdentifierPart(c) - ) - - def isSpecial(c: Char) = { - val chtp = Character.getType(c) - chtp == Character.MATH_SYMBOL || chtp == Character.OTHER_SYMBOL - } + def isIdentifierStart(c: Char): Boolean = + ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'a') || + (c == '_') || (c == '$') || + Character.isUnicodeIdentifierStart(c) + + def isIdentifierPart(c: Char) = + isIdentifierStart(c) || + ('0' <= c && c <= '9') || + Character.isUnicodeIdentifierPart(c) + + def isSpecial(c: Char) = { + val chtp = Character.getType(c) + chtp == Character.MATH_SYMBOL || chtp == Character.OTHER_SYMBOL + } - def isOperatorPart(c : Char) : Boolean = (c: @switch) match { - case '~' | '!' | '@' | '#' | '%' | - '^' | '*' | '+' | '-' | '<' | - '>' | '?' | ':' | '=' | '&' | - '|' | '/' | '\\' => true - case c => isSpecial(c) - } + def isOperatorPart(c : Char) : Boolean = (c: @switch) match { + case '~' | '!' | '@' | '#' | '%' | + '^' | '*' | '+' | '-' | '<' | + '>' | '?' | ':' | '=' | '&' | + '|' | '/' | '\\' => true + case c => isSpecial(c) + } // ------------- keyword configuration ----------------------------------- diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index a205d3636b..fae9b76520 100755 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -18,6 +18,7 @@ self => /** Called from typechecker */ override def pollForHighPriorityJob() { + // don';t do this if polling notnenabled unless cancel scheduler.nextWorkItem() match { case Some(action) => pollingEnabled = false @@ -98,10 +99,10 @@ self => currentTyperRun = new TyperRun while (outOfDate) { outOfDate = false - remainingPriUnits = priorityUnits for ((unit, id) <- unitsWithRunId.elements) { if (id != currentRunId) unitsToCompile += unit } + remainingPriUnits = priorityUnits while (unitsToCompile.nonEmpty) { if (change) { change = false @@ -123,19 +124,6 @@ self => case ex: FreshRunReq => outOfDate = true } - /** 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 - } - - /** The compilation unit corresponding to a position */ - def unitOf(pos: Position): CompilationUnit = unitOf(pos.source.get) - /** Make sure a set of compilation units is loaded and parsed */ def reload(units: Set[CompilationUnit]) = { for (unit <- units) { @@ -178,14 +166,27 @@ self => } } - /** Locate smallest tree that encloses position */ - def locateTree(pos: Position): Tree = - locate(pos, unitOf(pos).body) - // ----------------- 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 + } + + /** The compilation unit corresponding to a position */ + def unitOf(pos: Position): CompilationUnit = unitOf(pos.source.get) + + /** Locate smallest tree that encloses position */ + def locateTree(pos: Position): Tree = + locate(pos, unitOf(pos).body) + /** Make sure a set of compilation units is loaded and parsed */ def askReload(units: Set[CompilationUnit]) = scheduler.postWorkItem(() => reload(units)) @@ -194,7 +195,7 @@ self => def askTypeAt(pos: Position, result: SyncVar[Tree]) = scheduler.postWorkItem(() => self.typedTreeAt(pos, result)) - /** Ask to do unit first on subsequent type checking passes */ + /** 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 { () => @@ -203,7 +204,7 @@ self => } } - /** Cancel current high-priority job */ + /** Cancel currently pending high-priority jobs */ def askCancel() = scheduler.raise(new CancelActionReq) diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index f5e33a0ac9..3db4e372aa 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -1646,6 +1646,8 @@ A type's typeSymbol should never be inspected directly. override def kind = "MethodType" } + // Lukas: check whether we can eliminate this in favor of implicit flags on parameters + class ImplicitMethodType(pts: List[Type], rt: Type) extends MethodType(pts, rt) { override protected def paramPrefix = "(implicit " } @@ -2017,6 +2019,8 @@ A type's typeSymbol should never be inspected directly. new RefinementOfClass } + + /** the canonical creator for a refined type with a given scope */ def refinedType(parents: List[Type], owner: Symbol, decls: Scope, pos : Position): Type = { if (phase.erasedTypes) @@ -3226,6 +3230,10 @@ A type's typeSymbol should never be inspected directly. val restp1 = this(restp) if (restp1 eq restp) tp else PolyType(tparams, restp1) + + // Lukas: we need to check (together) whether we should also include parameter types + // of PolyType and MethodType in adaptToNewRun + case ClassInfoType(parents, decls, clazz) => if (clazz.isPackageClass) tp else { diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 31bb361d68..a2e567282f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -35,6 +35,8 @@ trait Analyzer extends AnyRef } } + var typerTime = 0L + object typerFactory extends SubComponent { val global: Analyzer.this.global.type = Analyzer.this.global val phaseName = "typer" @@ -42,6 +44,36 @@ trait Analyzer extends AnyRef val runsRightAfter = Some("namer") def newPhase(_prev: Phase): StdPhase = new StdPhase(_prev) { resetTyper() + override def run { + val start = System.nanoTime() + currentRun.units foreach applyPhase + /* + typerTime += System.nanoTime() - start + def show(time: Long) = "%2.1f".format(time.toDouble / typerTime * 100)+" / "+time+"ns" + println("time spent typechecking: "+show(typerTime)) + println("time spent in implicits: "+show(implicitTime)) + println(" successful in scope: "+show(inscopeSucceed)) + println(" failed in scope: "+show(inscopeFail)) + println(" successful of type: "+show(oftypeSucceed)) + println(" failed of type: "+show(oftypeFail)) + println(" successful manifest: "+show(manifSucceed)) + println(" failed manifest: "+show(manifFail)) + println("implicit cache hitratio: "+"%2.1f".format(hits.toDouble / (hits + misses) * 100)) + println("time spent in failed : "+show(failedSilent)) + println(" failed op= : "+show(failedOpEqs)) + println(" failed applu : "+show(failedApplies)) + */ + typerTime = 0L + implicitTime = 0L + inscopeSucceed = 0L + inscopeFail = 0L + oftypeSucceed = 0L + oftypeFail = 0L + manifSucceed = 0L + manifFail = 0L + hits = 0 + misses = 0 + } def apply(unit: CompilationUnit) { try { unit.body = newTyper(rootContext(unit)).typed(unit.body) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 6ca93116d6..667827dfa4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -30,6 +30,16 @@ self: Analyzer => final val traceImplicits = false + var implicitTime = 0L + var inscopeSucceed = 0L + var inscopeFail = 0L + var oftypeSucceed = 0L + var oftypeFail = 0L + var manifSucceed = 0L + var manifFail = 0L + var hits = 0 + var misses = 0 + /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch` * for more info how the search is conducted. * @param tree The tree for which the implicit needs to be inserted. @@ -52,6 +62,11 @@ self: Analyzer => search.result } + final val sizeLimit = 100 + val implicitsCache = new HashMap[Type, SearchResult] + + def resetImplicits() { implicitsCache.clear() } + /** If type `pt` an instance of Manifest or OptManifest, or an abstract type lower-bounded * by such an instance? */ @@ -633,16 +648,35 @@ self: Analyzer => * If that fails, and `pt` is an instance of Manifest, try to construct a manifest. * If all fails return SearchFailure */ + //val start = System.nanoTime() var result = searchImplicit(context.implicitss, true) + //val timer1 = System.nanoTime() + //if (result == SearchFailure) inscopeFail += timer1 - start else inscopeSucceed += timer1 - start if (result == SearchFailure) { - result = searchImplicit(implicitsOfExpectedType, false) + implicitsCache get pt match { + case Some(r) => + hits += 1 + result = r + case None => + misses += 1 + result = searchImplicit(implicitsOfExpectedType, false) +// println("new fact: search implicit of "+pt+" = "+result) +// if (implicitsCache.size >= sizeLimit) +// implicitsCache -= implicitsCache.values.next + implicitsCache(pt) = result + } } + //val timer2 = System.nanoTime() + //if (result == SearchFailure) oftypeFail += timer2 - timer1 else oftypeSucceed += timer2 - timer1 if (result == SearchFailure) { val resultTree = implicitManifest(pt) if (resultTree != EmptyTree) result = new SearchResult(resultTree, EmptyTreeTypeSubstituter) } + //val timer3 = System.nanoTime() + //if (result == SearchFailure) manifFail += timer3 - timer2 else manifSucceed += timer3 - timer2 if (result == SearchFailure && settings.debug.value) println("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+parts(pt)+implicitsOfExpectedType) + //implicitTime += System.nanoTime() - start if (util.Statistics.enabled) impltime += (currentTime - startTime) result diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index a272d7aa28..170e445458 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -36,6 +36,10 @@ trait Typers { self: Analyzer => var implcnt = 0 var impltime = 0l + var failedApplies = 0L + var failedOpEqs = 0L + var failedSilent = 0L + private val transformed = new HashMap[Tree, Tree] private val superDefs = new HashMap[Symbol, ListBuffer[Tree]] @@ -43,6 +47,7 @@ trait Typers { self: Analyzer => def resetTyper() { resetContexts resetNamer() + resetImplicits() transformed.clear superDefs.clear } @@ -660,7 +665,9 @@ trait Typers { self: Analyzer => else qual.tpe.nonLocalMember(name)(from) } - def silent(op: Typer => Tree): AnyRef /* in fact, TypeError or Tree */ = try { + def silent(op: Typer => Tree): AnyRef /* in fact, TypeError or Tree */ = { + val start = System.nanoTime() + try { if (context.reportGeneralErrors) { val context1 = context.makeSilent(context.reportAmbiguousErrors) context1.undetparams = context.undetparams @@ -675,8 +682,10 @@ trait Typers { self: Analyzer => } } catch { case ex: CyclicReference => throw ex - case ex: TypeError => ex - } + case ex: TypeError => + failedSilent += System.nanoTime() - start + ex + }} /** Perform the following adaptations of expression, pattern or type `tree' wrt to * given mode `mode' and given prototype `pt': @@ -1815,6 +1824,16 @@ trait Typers { self: Analyzer => fun.tpe match { case OverloadedType(pre, alts) => val undetparams = context.extractUndetparams() + + /* Lukas: + + var m: Map[Tree, Name] = Map() + val args1 = List.mapConserve(args) { + case Assign(name, rhs) => m += (rhs -> name) + case arg => arg + } + */ + val args1 = typedArgs(args, argMode(fun, mode)) context.undetparams = undetparams inferMethodAlternative(fun, undetparams, args1 map (_.tpe.deconst), pt) @@ -2667,11 +2686,13 @@ trait Typers { self: Analyzer => * @param args ... * @return ... */ - def tryTypedApply(fun: Tree, args: List[Tree]): Tree = + def tryTypedApply(fun: Tree, args: List[Tree]): Tree = { + val start = System.nanoTime() silent(_.doTypedApply(tree, fun, args, mode, pt)) match { case t: Tree => t case ex: TypeError => + failedApplies += System.nanoTime() - start def errorInResult(tree: Tree): Boolean = tree.pos == ex.pos || { tree match { case Block(_, r) => errorInResult(r) @@ -2701,6 +2722,7 @@ trait Typers { self: Analyzer => reportTypeError(tree.pos, ex) setError(tree) } + } def typedApply(fun: Tree, args: List[Tree]) = { val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable @@ -2709,6 +2731,7 @@ trait Typers { self: Analyzer => typed1(tree, mode & ~PATTERNmode | EXPRmode, pt) } else { val funpt = if ((mode & PATTERNmode) != 0) pt else WildcardType + val start = System.nanoTime() silent(_.typed(fun, funMode(mode), funpt)) match { case fun1: Tree => val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1 @@ -2736,6 +2759,7 @@ trait Typers { self: Analyzer => else res */ case ex: TypeError => + failedOpEqs += System.nanoTime() - start fun match { case Select(qual, name) if (mode & PATTERNmode) == 0 && nme.isOpAssignmentName(name.decode) => @@ -3425,6 +3449,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() result } catch { case ex: TypeError => diff --git a/src/compiler/scala/tools/nsc/util/WorkScheduler.scala b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala new file mode 100644 index 0000000000..7bcd3b7a8e --- /dev/null +++ b/src/compiler/scala/tools/nsc/util/WorkScheduler.scala @@ -0,0 +1,43 @@ +package scala.tools.nsc.util + +import scala.collection.mutable.Queue + +class WorkScheduler { + + type Action = () => Unit + + private var todo = new Queue[Action] + + /** Called from server */ + def waitForMoreWork() = synchronized { + do { wait() } while (todo.isEmpty) + } + + /** called from Server */ + def moreWork(): Boolean = synchronized { + todo.nonEmpty + } + + /** Called from server */ + def nextWorkItem(): Option[Action] = synchronized { + if (!todo.isEmpty) Some(todo.dequeue()) else None + } + + /** Called from client */ + def postWorkItem(action: Action) { + todo enqueue action + notify() + } + + /** Called from client */ + def cancel() = synchronized { + todo.clear() + } + + /** Called from client */ + def raise(exc: Exception) = synchronized { + todo.clear() + todo enqueue (() => throw exc) + } +} + -- cgit v1.2.3