diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index b112f5042e9b..9f2996ea0542 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -135,7 +135,7 @@ class Compiler { def reset()(implicit ctx: Context): Unit = { ctx.base.reset() - ctx.runInfo.clear() + if (ctx.run != null) ctx.run.reset() } def newRun(implicit ctx: Context): Run = { diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 7794d36c87e3..282975f86a0f 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -18,15 +18,16 @@ import transform.TreeChecker import rewrite.Rewrites import java.io.{BufferedWriter, OutputStreamWriter} import printing.XprintMode +import parsing.Parsers.Parser import typer.ImplicitRunInfo +import collection.mutable import scala.annotation.tailrec import dotty.tools.io.VirtualFile import scala.util.control.NonFatal /** A compiler run. Exports various methods to compile source files */ -class Run(comp: Compiler, ictx: Context) { - import Run._ +class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo { /** Produces the following contexts, from outermost to innermost * @@ -53,12 +54,49 @@ class Run(comp: Compiler, ictx: Context) { ctx.initialize()(start) // re-initialize the base context with start def addImport(ctx: Context, refFn: () => TermRef) = ctx.fresh.setImportInfo(ImportInfo.rootImport(refFn)(ctx)) - (start.setRunInfo(new RunInfo(start)) /: defn.RootImportFns)(addImport) + (start.setRun(this) /: defn.RootImportFns)(addImport) } - protected[this] implicit val ctx: Context = rootContext(ictx) + private[this] var myCtx = rootContext(ictx) + + /** The context created for this run */ + def runContext = myCtx + + protected[this] implicit def ctx: Context = myCtx assert(ctx.runId <= Periods.MaxPossibleRunId) + private[this] var myUnits: List[CompilationUnit] = _ + private[this] var myUnitsCached: List[CompilationUnit] = _ + private[this] var myFiles: Set[AbstractFile] = _ + private[this] val myLateUnits = mutable.ListBuffer[CompilationUnit]() + private[this] var myLateFiles = mutable.Set[AbstractFile]() + + /** The compilation units currently being compiled, this may return different + * results over time. + */ + def units: List[CompilationUnit] = myUnits + + private def units_=(us: List[CompilationUnit]): Unit = + myUnits = us + + /** The files currently being compiled, this may return different results over time. + * These files do not have to be source files since it's possible to compile + * from TASTY. + */ + def files: Set[AbstractFile] = { + if (myUnits ne myUnitsCached) { + myUnitsCached = myUnits + myFiles = myUnits.map(_.source.file).toSet + } + myFiles + } + + /** Units that are added from source completers but that are not compiled in current run. */ + def lateUnits: List[CompilationUnit] = myLateUnits.toList + + /** The source files of all late units, as a set */ + def lateFiles: collection.Set[AbstractFile] = myLateFiles + def getSource(fileName: String): SourceFile = { val f = new PlainFile(io.Path(fileName)) if (f.isDirectory) { @@ -78,7 +116,7 @@ class Run(comp: Compiler, ictx: Context) { compileSources(sources) } catch { case NonFatal(ex) => - ctx.echo(i"exception occurred while compiling ${ctx.runInfo.units}%, %") + ctx.echo(i"exception occurred while compiling $units%, %") throw ex } @@ -90,17 +128,17 @@ class Run(comp: Compiler, ictx: Context) { */ def compileSources(sources: List[SourceFile]) = if (sources forall (_.exists)) { - ctx.runInfo.units = sources map (new CompilationUnit(_)) + units = sources map (new CompilationUnit(_)) compileUnits() } def compileUnits(us: List[CompilationUnit]): Unit = { - ctx.runInfo.units = us + units = us compileUnits() } def compileUnits(us: List[CompilationUnit], ctx: Context): Unit = { - ctx.runInfo.units = us + units = us compileUnits()(ctx) } @@ -122,16 +160,16 @@ class Run(comp: Compiler, ictx: Context) { if (phase.isRunnable) Stats.trackTime(s"$phase ms ") { val start = System.currentTimeMillis - ctx.runInfo.units = phase.runOn(ctx.runInfo.units) + units = phase.runOn(units) if (ctx.settings.Xprint.value.containsPhase(phase)) { - for (unit <- ctx.runInfo.units) { + for (unit <- units) { lastPrintedTree = printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) } } ctx.informTime(s"$phase ", start) Stats.record(s"total trees at end of $phase", ast.Trees.ntrees) - for (unit <- ctx.runInfo.units) + for (unit <- units) Stats.record(s"retained typed trees at end of $phase", unit.tpdTree.treeSize) } } @@ -142,6 +180,22 @@ class Run(comp: Compiler, ictx: Context) { if (!ctx.reporter.hasErrors) Rewrites.writeBack() } + /** Enter top-level definitions of classes and objects contain in Scala source file `file`. + * The newly added symbols replace any previously entered symbols. + */ + def enterRoots(file: AbstractFile)(implicit ctx: Context): Unit = + if (!files.contains(file) && !lateFiles.contains(file)) { + val unit = new CompilationUnit(getSource(file.path)) + myLateUnits += unit + myLateFiles += file + enterRoots(unit)(runContext.fresh.setCompilationUnit(unit)) + } + + private def enterRoots(unit: CompilationUnit)(implicit ctx: Context): Unit = { + unit.untpdTree = new Parser(unit.source).parse() + ctx.typer.lateEnter(unit.untpdTree) + } + private sealed trait PrintedTree private /*final*/ case class SomePrintedTree(phase: String, tree: String) extends PrintedTree private object NoPrintedTree extends PrintedTree @@ -180,46 +234,19 @@ class Run(comp: Compiler, ictx: Context) { compileSources(List(new SourceFile(virtualFile, Codec.UTF8))) } - /** The context created for this run */ - def runContext = ctx - /** Print summary; return # of errors encountered */ def printSummary(): Reporter = { - ctx.runInfo.printMaxConstraint() + printMaxConstraint() val r = ctx.reporter r.printSummary r } -} - -object Run { - /** Info that changes on each compiler run */ - class RunInfo(initctx: Context) extends ImplicitRunInfo with ConstraintRunInfo { - implicit val ctx: Context = initctx - - private[this] var myUnits: List[CompilationUnit] = _ - private[this] var myUnitsCached: List[CompilationUnit] = _ - private[this] var myFiles: Set[AbstractFile] = _ - - /** The compilation units currently being compiled, this may return different - * results over time. - */ - def units: List[CompilationUnit] = myUnits - - private[Run] def units_=(us: List[CompilationUnit]): Unit = - myUnits = us - - - /** The files currently being compiled, this may return different results over time. - * These files do not have to be source files since it's possible to compile - * from TASTY. - */ - def files: Set[AbstractFile] = { - if (myUnits ne myUnitsCached) { - myUnitsCached = myUnits - myFiles = myUnits.map(_.source.file).toSet - } - myFiles - } + + override def reset() = { + super[ImplicitRunInfo].reset() + super[ConstraintRunInfo].reset() + myCtx = null + myUnits = null + myUnitsCached = null } -} +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/config/PathResolver.scala b/compiler/src/dotty/tools/dotc/config/PathResolver.scala index 572161a06647..48dc6deed65c 100644 --- a/compiler/src/dotty/tools/dotc/config/PathResolver.scala +++ b/compiler/src/dotty/tools/dotc/config/PathResolver.scala @@ -262,7 +262,7 @@ class PathResolver(implicit ctx: Context) { lazy val result: ClassPath = { val cp = AggregateClassPath(containers.toIndexedSeq) - if (settings.Ylogcp.value) { + if (settings.YlogClasspath.value) { Console.println("Classpath built from " + settings.toConciseString(ctx.settingsState)) Console.println("Defaults: " + PathResolver.Defaults) Console.println("Calculated: " + Calculated) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index e7da58fd5644..a9ed9642b7b8 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -16,7 +16,9 @@ class ScalaSettings extends Settings.SettingGroup { val extdirs = PathSetting("-extdirs", "Override location of installed extensions.", Defaults.scalaExtDirs) val javabootclasspath = PathSetting("-javabootclasspath", "Override java boot classpath.", Defaults.javaBootClassPath) val javaextdirs = PathSetting("-javaextdirs", "Override java extdirs classpath.", Defaults.javaExtDirs) - val sourcepath = PathSetting("-sourcepath", "Specify location(s) of source files.", "") // Defaults.scalaSourcePath + val sourcepath = PathSetting("-sourcepath", "Specify location(s) of source files.", Defaults.scalaSourcePath) + val scansource = BooleanSetting("-scansource", "Scan source files to locate classes for which class-name != file-name") + val classpath = PathSetting("-classpath", "Specify where to find user class files.", defaultClasspath) withAbbreviation "-cp" val outputDir = PathSetting("-d", "directory|jar", "destination for generated classfiles.", ".") val priorityclasspath = PathSetting("-priorityclasspath", "class path that takes precedence over all other paths (or testing only)", "") @@ -80,7 +82,7 @@ class ScalaSettings extends Settings.SettingGroup { val YtermConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val Ylog = PhasesSetting("-Ylog", "Log operations during") val YemitTasty = BooleanSetting("-Yemit-tasty", "Generate tasty in separate *.tasty file.") - val Ylogcp = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.") + val YlogClasspath = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.") val YdisableFlatCpCaching = BooleanSetting("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.") val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") @@ -131,6 +133,7 @@ class ScalaSettings extends Settings.SettingGroup { sys.props("user.dir") ) + val projectName = StringSetting ( "-project", "project title", diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala b/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala index c2b44dd189c6..55ce0292f115 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala @@ -2,10 +2,9 @@ package dotty.tools.dotc package core import Contexts._ -import Run.RunInfo import config.Printers.typr -trait ConstraintRunInfo { self: RunInfo => +trait ConstraintRunInfo { self: Run => private[this] var maxSize = 0 private[this] var maxConstraint: Constraint = _ def recordConstraintSize(c: Constraint, size: Int) = @@ -15,4 +14,6 @@ trait ConstraintRunInfo { self: RunInfo => } def printMaxConstraint()(implicit ctx: Context) = if (maxSize > 0) typr.println(s"max constraint = ${maxConstraint.show}") + + protected def reset() = maxConstraint = null } diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 3764dbd66d34..1a7b0e971a72 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -14,7 +14,6 @@ import NameOps._ import Uniques._ import SymDenotations._ import Comments._ -import Run.RunInfo import util.Positions._ import ast.Trees._ import ast.untpd @@ -43,7 +42,7 @@ object Contexts { private val (settingsStateLoc, store4) = store3.newLocation[SettingsState]() private val (freshNamesLoc, store5) = store4.newLocation[FreshNameCreator](new FreshNameCreator.Default) private val (compilationUnitLoc, store6) = store5.newLocation[CompilationUnit]() - private val (runInfoLoc, store7) = store6.newLocation[RunInfo]() + private val (runLoc, store7) = store6.newLocation[Run]() private val initialStore = store7 /** A context is passed basically everywhere in dotc. @@ -194,8 +193,8 @@ object Contexts { /** The current compilation unit */ def compilationUnit: CompilationUnit = store(compilationUnitLoc) - /** The current compiler-run specific Info */ - def runInfo: RunInfo = store(runInfoLoc) + /** The current compiler-run */ + def run: Run = store(runLoc) /** The new implicit references that are introduced by this scope */ protected var implicitsCache: ContextualImplicits = null @@ -460,7 +459,7 @@ object Contexts { def setPrinterFn(printer: Context => Printer): this.type = updateStore(printerFnLoc, printer) def setSettings(settingsState: SettingsState): this.type = updateStore(settingsStateLoc, settingsState) def setCompilationUnit(compilationUnit: CompilationUnit): this.type = updateStore(compilationUnitLoc, compilationUnit) - def setRunInfo(runInfo: RunInfo): this.type = updateStore(runInfoLoc, runInfo) + def setRun(run: Run): this.type = updateStore(runLoc, run) def setFreshNames(freshNames: FreshNameCreator): this.type = updateStore(freshNamesLoc, freshNames) def setProperty[T](key: Key[T], value: T): this.type = @@ -520,9 +519,7 @@ object Contexts { tree = untpd.EmptyTree typeAssigner = TypeAssigner moreProperties = Map.empty - store = initialStore - .updated(settingsStateLoc, settings.defaultState) - .updated(runInfoLoc, new RunInfo(this)) + store = initialStore.updated(settingsStateLoc, settings.defaultState) typeComparer = new TypeComparer(this) searchHistory = new SearchHistory(0, Map()) gadt = EmptyGADTMap diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index bf82ca9384b4..4614f5e325ca 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -929,7 +929,7 @@ object Denotations { // printPeriods(current) this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) if (current.validFor.firstPhaseId >= targetId) - insertInsteadOf(current) + current.replaceWith(this) else { current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) insertAfter(current) @@ -950,7 +950,7 @@ object Denotations { val current1: SingleDenotation = f(current.asSymDenotation) if (current1 ne current) { current1.validFor = current.validFor - current1.insertInsteadOf(current) + current.replaceWith(current1) } hasNext = current1.nextInRun.validFor.code > current1.validFor.code current = current1.nextInRun @@ -972,14 +972,14 @@ object Denotations { * The code to achieve this is subtle in that it works correctly * whether the replaced denotation is the only one in its cycle or not. */ - private def insertInsteadOf(old: SingleDenotation): Unit = { - var prev = old - while (prev.nextInRun ne old) prev = prev.nextInRun + private[dotc] def replaceWith(newd: SingleDenotation): Unit = { + var prev = this + while (prev.nextInRun ne this) prev = prev.nextInRun // order of next two assignments is important! - prev.nextInRun = this - this.nextInRun = old.nextInRun - old.validFor = Nowhere - old.nextInRun = this + prev.nextInRun = newd + newd.nextInRun = nextInRun + validFor = Nowhere + nextInRun = newd } def staleSymbolError(implicit ctx: Context) = diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 79f3b6e56304..af2eb3c04fe6 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -26,7 +26,7 @@ object OrderingConstraint { private def newConstraint(boundsMap: ParamBounds, lowerMap: ParamOrdering, upperMap: ParamOrdering)(implicit ctx: Context) : OrderingConstraint = { val result = new OrderingConstraint(boundsMap, lowerMap, upperMap) if (Config.checkConstraintsNonCyclic) result.checkNonCyclic() - ctx.runInfo.recordConstraintSize(result, result.boundsMap.size) + ctx.run.recordConstraintSize(result, result.boundsMap.size) result } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 128f884d53aa..5ee57d02a107 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -275,7 +275,7 @@ object SymDenotations { final def privateWithin(implicit ctx: Context): Symbol = { ensureCompleted(); myPrivateWithin } /** Set privateWithin. */ - protected[core] final def privateWithin_=(sym: Symbol): Unit = + protected[dotc] final def privateWithin_=(sym: Symbol): Unit = myPrivateWithin = sym /** The annotations of this denotation */ diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index da1716dd38d4..d2f548060a0e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -1,13 +1,8 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ - package dotty.tools package dotc package core -import java.io.IOException +import java.io.{IOException, File} import scala.compat.Platform.currentTime import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile } import classpath._ @@ -16,8 +11,10 @@ import StdNames._, NameOps._ import Decorators.{PreNamedString, StringInterpolators} import classfile.ClassfileParser import util.Stats +import Decorators._ import scala.util.control.NonFatal import ast.Trees._ +import parsing.Parsers.OutlineParser import reporting.trace object SymbolLoaders { @@ -29,11 +26,15 @@ object SymbolLoaders { /** A base class for Symbol loaders with some overridable behavior */ class SymbolLoaders { + import ast.untpd._ protected def enterNew( owner: Symbol, member: Symbol, completer: SymbolLoader, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = { - assert(scope.lookup(member.name) == NoSymbol, s"${owner.fullName}.${member.name} already has a symbol") + val comesFromScan = + completer.isInstanceOf[SourcefileLoader] && ctx.settings.scansource.value + assert(comesFromScan || scope.lookup(member.name) == NoSymbol, + s"${owner.fullName}.${member.name} already has a symbol") owner.asClass.enter(member, scope) member } @@ -103,16 +104,65 @@ class SymbolLoaders { scope = scope) } - /** In batch mode: Enter class and module with given `name` into scope of `owner` - * and give them a source completer for given `src` as type. - * In IDE mode: Find all toplevel definitions in `src` and enter then into scope of `owner` - * with source completer for given `src` as type. - * (overridden in interactive.Global). + /** If setting -scansource is set: + * Enter all toplevel classes and objects in file `src` into package `owner`, provided + * they are in the right package. Issue a warning if a class or object is in the wrong + * package, i.e. if the file path differs from the declared package clause. + * If -scansource is not set: + * Enter class and module with given `name` into scope of `owner`. + * + * All entered symbols are given a source completer of `src` as info. */ def enterToplevelsFromSource( owner: Symbol, name: PreName, src: AbstractFile, scope: Scope = EmptyScope)(implicit ctx: Context): Unit = { - enterClassAndModule(owner, name, new SourcefileLoader(src), scope = scope) + + val completer = new SourcefileLoader(src) + if (ctx.settings.scansource.value) { + if (src.exists && !src.isDirectory) { + val filePath = owner.ownersIterator.takeWhile(!_.isRoot).map(_.name.toTermName).toList + + def addPrefix(pid: RefTree, path: List[TermName]): List[TermName] = pid match { + case Ident(name: TermName) => name :: path + case Select(qual: RefTree, name: TermName) => name :: addPrefix(qual, path) + case _ => path + } + + def enterScanned(unit: CompilationUnit)(implicit ctx: Context) = { + + def checkPathMatches(path: List[TermName], what: String, tree: MemberDef): Boolean = { + val ok = filePath == path + if (!ok) + ctx.warning(i"""$what ${tree.name} is in the wrong directory. + |It was declared to be in package ${path.reverse.mkString(".")} + |But it is found in directory ${filePath.reverse.mkString(File.separator)}""", + tree.pos) + ok + } + + def traverse(tree: Tree, path: List[TermName]): Unit = tree match { + case PackageDef(pid, body) => + val path1 = addPrefix(pid, path) + for (stat <- body) traverse(stat, path1) + case tree: TypeDef if tree.isClassDef => + if (checkPathMatches(path, "class", tree)) + enterClassAndModule(owner, tree.name, completer, scope = scope) + // It might be a case class or implicit class, + // so enter class and module to be on the safe side + case tree: ModuleDef => + if (checkPathMatches(path, "object", tree)) + enterModule(owner, tree.name, completer, scope = scope) + case _ => + } + + traverse(new OutlineParser(unit.source).parse(), Nil) + } + + val unit = new CompilationUnit(ctx.run.getSource(src.path)) + enterScanned(unit)(ctx.run.runContext.fresh.setCompilationUnit(unit)) + } + } + else enterClassAndModule(owner, name, completer, scope = scope) } /** The package objects of scala and scala.reflect should always @@ -199,7 +249,7 @@ class SymbolLoaders { !root.unforcedDecls.lookup(classRep.name.toTypeName).exists if (!root.isRoot) { - val classReps = classPath.classes(packageName) + val classReps = classPath.list(packageName).classesAndSources for (classRep <- classReps) if (!maybeModuleClass(classRep) && isFlatName(classRep) == flat && @@ -343,5 +393,6 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader { class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader { def description(implicit ctx: Context) = "source file " + srcfile.toString override def sourceFileOrNull = srcfile - def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = unsupported("doComplete") + def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = + ctx.run.enterRoots(srcfile) } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 090dbaed0bf3..02b60fa7c78e 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -449,7 +449,7 @@ object Symbols { final def isDefinedInCurrentRun(implicit ctx: Context): Boolean = pos.exists && defRunId == ctx.runId && { val file = associatedFile - file != null && ctx.runInfo.files.contains(file) + file != null && ctx.run.files.contains(file) } /** Is symbol valid in current run? */ diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index b55b34298f5f..93165bde9640 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -191,8 +191,8 @@ class InteractiveDriver(settings: List[String]) extends Driver { if (!t.symbol.isCompleted) t.symbol.info = UnspecifiedErrorType t.symbol.annotations.foreach { annot => /* In some cases annotations are are used on themself (possibly larger cycles). - * This is the case with the java.lang.annotation.Target annotation, would end - * in an infinite loop while cleaning. The `seen` is added to ensure that those + * This is the case with the java.lang.annotation.Target annotation, would end + * in an infinite loop while cleaning. The `seen` is added to ensure that those * trees are not cleand twice. * TODO: Find a less expensive way to check for those cycles. */ @@ -226,7 +226,7 @@ class InteractiveDriver(settings: List[String]) extends Driver { run.compileSources(List(source)) run.printSummary() - val t = ctx.runInfo.units.head.tpdTree + val t = ctx.run.units.head.tpdTree cleanup(t) myOpenedTrees(uri) = topLevelClassTrees(t, source) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 0d8b373911e6..c49331f43883 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -9,7 +9,6 @@ import util.Stats.{track, record, monitored} import printing.{Showable, Printer} import printing.Texts._ import Contexts._ -import Run.RunInfo import Types._ import Flags._ import TypeErasure.{erasure, hasStableErasure} @@ -372,7 +371,7 @@ object Implicits { import Implicits._ /** Info relating to implicits that is kept for one run */ -trait ImplicitRunInfo { self: RunInfo => +trait ImplicitRunInfo { self: Run => private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]() @@ -502,7 +501,7 @@ trait ImplicitRunInfo { self: RunInfo => iscope(rootTp) } - def clear() = { + protected def reset() = { implicitScopeCache.clear() } } @@ -1059,7 +1058,7 @@ trait Implicits { self: Typer => } } - def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp, ctx) + def implicitScope(tp: Type): OfTypeImplicits = ctx.run.implicitScope(tp, ctx) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 07ca5c04deec..2bd8b586a833 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -204,6 +204,9 @@ class Namer { typer: Typer => */ val scope = newScope + /** We are entering symbols coming from a SourceLoader */ + private[this] var lateCompile = false + /** The symbol of the given expanded tree. */ def symbolOfTree(tree: Tree)(implicit ctx: Context): Symbol = { val xtree = expanded(tree) @@ -281,14 +284,32 @@ class Namer { typer: Typer => else name } + /** Create new symbol or redefine existing symbol under lateCompile. */ + def createOrRefine[S <: Symbol]( + tree: MemberDef, name: Name, flags: FlagSet, infoFn: S => Type, + symFn: (FlagSet, S => Type, Symbol) => S): Symbol = { + val prev = + if (lateCompile && ctx.owner.is(Package)) ctx.effectiveScope.lookup(name) + else NoSymbol + val sym = + if (prev.exists) { + prev.flags = flags + prev.info = infoFn(prev.asInstanceOf[S]) + prev.privateWithin = privateWithinClass(tree.mods) + prev + } + else symFn(flags, infoFn, privateWithinClass(tree.mods)) + recordSym(sym, tree) + } + tree match { case tree: TypeDef if tree.isClassDef => val name = checkNoConflict(tree.name).asTypeName val flags = checkFlags(tree.mods.flags &~ Implicit) - val cls = recordSym(ctx.newClassSymbol( - ctx.owner, name, flags, - cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree), - privateWithinClass(tree.mods), tree.namePos, ctx.source.file), tree) + val cls = + createOrRefine[ClassSymbol](tree, name, flags, + cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree), + ctx.newClassSymbol(ctx.owner, name, _, _, _, tree.namePos, ctx.source.file)) cls.completer.asInstanceOf[ClassCompleter].init() cls case tree: MemberDef => @@ -317,11 +338,10 @@ class Namer { typer: Typer => case tree: TypeDef => new TypeDefCompleter(tree)(cctx) case _ => new Completer(tree)(cctx) } - - recordSym(ctx.newSymbol( - ctx.owner, name, flags | deferred | method | higherKinded, - adjustIfModule(completer, tree), - privateWithinClass(tree.mods), tree.namePos), tree) + val info = adjustIfModule(completer, tree) + createOrRefine[Symbol](tree, name, flags | deferred | method | higherKinded, + _ => info, + (fs, _, pwithin) => ctx.newSymbol(ctx.owner, name, fs, info, pwithin, tree.namePos)) case tree: Import => recordSym(ctx.newImportSymbol(ctx.owner, new Completer(tree), tree.pos), tree) case _ => @@ -717,13 +737,23 @@ class Namer { typer: Typer => localCtx } + /** Index and annotate symbols in `tree` while asserting the `lateCompile` flag. + * This will cause any old top-level symbol with the same fully qualified + * name as a newly created symbol to be replaced. + */ + def lateEnter(tree: Tree)(implicit ctx: Context) = { + val saved = lateCompile + lateCompile = true + try indexAndAnnotate(tree :: Nil) finally lateCompile = saved + } + def missingType(sym: Symbol, modifier: String)(implicit ctx: Context) = { ctx.error(s"${modifier}type of implicit definition needs to be given explicitly", sym.pos) sym.resetFlag(Implicit) } /** The completer of a symbol defined by a member def or import (except ClassSymbols) */ - class Completer(val original: Tree)(implicit ctx: Context) extends LazyType { + class Completer(val original: Tree)(implicit ctx: Context) extends LazyType with SymbolLoaders.SecondCompleter { protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index a042477b3b26..480f1d3ee122 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -58,6 +58,9 @@ class CompilationTests extends ParallelTesting { compileFile("../tests/pos-special/i3323.scala", defaultOptions.and("-Xfatal-warnings")) + compileFile("../tests/pos-special/i3323b.scala", defaultOptions.and("-Xfatal-warnings")) + compileFile("../tests/pos-special/i3589-b.scala", defaultOptions.and("-Xfatal-warnings")) + + compileFile("../tests/pos-special/completeFromSource/Test.scala", defaultOptions.and("-sourcepath", "../tests/pos-special")) + + compileFile("../tests/pos-special/completeFromSource/Test2.scala", defaultOptions.and("-sourcepath", "../tests/pos-special")) + + compileFile("../tests/pos-special/completeFromSource/Test3.scala", defaultOptions.and("-sourcepath", "../tests/pos-special", "-scansource")) + compileList( "compileMixed", List( diff --git a/tests/pos-special/completeFromSource/Test.scala b/tests/pos-special/completeFromSource/Test.scala new file mode 100644 index 000000000000..bc56b52cf523 --- /dev/null +++ b/tests/pos-special/completeFromSource/Test.scala @@ -0,0 +1,13 @@ +package completeFromSource + +class Test extends nested.A(22) { + + val y: Int = this.x + + val a = nested.A(33) + + println(a.x) + +} + + diff --git a/tests/pos-special/completeFromSource/Test2.scala b/tests/pos-special/completeFromSource/Test2.scala new file mode 100644 index 000000000000..4678fe4d0e82 --- /dev/null +++ b/tests/pos-special/completeFromSource/Test2.scala @@ -0,0 +1,16 @@ +package completeFromSource +import nested._ + +class Test2 extends A(22) { + + val y: Int = this.x + + val a = A(33) + + println(a.x) + +} + + + + diff --git a/tests/pos-special/completeFromSource/Test3.scala b/tests/pos-special/completeFromSource/Test3.scala new file mode 100644 index 000000000000..f0a61769d200 --- /dev/null +++ b/tests/pos-special/completeFromSource/Test3.scala @@ -0,0 +1,16 @@ +package completeFromSource +import nested._ + +class Test3 { + + val x = if (true) new B(1) else new C("xx") + + x match { + case B(n) => println(s"B($n)") + case C(s) => println(s"C($s)") + } +} + + + + diff --git a/tests/pos-special/completeFromSource/nested/A.scala b/tests/pos-special/completeFromSource/nested/A.scala new file mode 100644 index 000000000000..77fa1a462136 --- /dev/null +++ b/tests/pos-special/completeFromSource/nested/A.scala @@ -0,0 +1,13 @@ +package completeFromSource.nested + +class A(y: Int) { + + val x: Int = y + +} + +object A { + + def apply(x: Int) = new A(22) + +} diff --git a/tests/pos-special/completeFromSource/nested/BC.scala b/tests/pos-special/completeFromSource/nested/BC.scala new file mode 100644 index 000000000000..af68b0fdb3bc --- /dev/null +++ b/tests/pos-special/completeFromSource/nested/BC.scala @@ -0,0 +1,6 @@ +package completeFromSource.nested + + +case class B(x: Int) extends A(x) + +case class C(s: String) extends A(s.length)