From f4b344d1b4605bd47456ba24b4788e9bbd49ebb7 Mon Sep 17 00:00:00 2001 From: dragos Date: Fri, 26 Jun 2009 16:35:41 +0000 Subject: [PATCH] Improved the refined build manager. git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@18124 5e8d7ff9-d8ef-0310-90f0-a4852d11357a --- .../tools/nsc/dependencies/Changes.scala | 138 ++++++++++++++++-- .../tools/nsc/dependencies/References.scala | 5 +- .../nsc/interactive/RefinedBuildManager.scala | 11 +- 3 files changed, 133 insertions(+), 21 deletions(-) diff --git a/src/compiler/scala/tools/nsc/dependencies/Changes.scala b/src/compiler/scala/tools/nsc/dependencies/Changes.scala index 7c648ee7f..6b315b53e 100644 --- a/src/compiler/scala/tools/nsc/dependencies/Changes.scala +++ b/src/compiler/scala/tools/nsc/dependencies/Changes.scala @@ -1,5 +1,7 @@ package scala.tools.nsc.dependencies +import symtab.Flags + import collection._ /** A component that describes the possible changes between successive @@ -34,18 +36,123 @@ abstract class Changes { case class Added(e: Entity) extends Change case class Removed(e: Entity) extends Change - case class Changed(e: Entity) extends Change + case class Changed(e: Entity)(implicit val reason: String) extends Change { + override def toString = "Changed(" + e + ")[" + reason + "]" + } + + private def sameSymbol(sym1: Symbol, sym2: Symbol): Boolean = + sym1.fullNameString == sym2.fullNameString + + private def sameType(tp1: Type, tp2: Type) = { + def typeOf(tp: Type): String = tp.toString + "[" + tp.getClass + "]" + val res = sameType0(tp1, tp2) +// if (!res) println("\t different types: " + typeOf(tp1) + " : " + typeOf(tp2)) + res + } + + private def sameType0(tp1: Type, tp2: Type): Boolean = ((tp1, tp2) match { + /*case (ErrorType, _) => false + case (WildcardType, _) => false + case (_, ErrorType) => false + case (_, WildcardType) => false + */ + case (NoType, _) => false + case (NoPrefix, NoPrefix) => true + case (_, NoType) => false + case (_, NoPrefix) => false + + case (ThisType(sym1), ThisType(sym2)) + if sameSymbol(sym1, sym2) => true + + case (SingleType(pre1, sym1), SingleType(pre2, sym2)) + if sameType(pre1, pre2) && sameSymbol(sym1, sym2) => true + case (ConstantType(value1), ConstantType(value2)) => + value1 == value2 + case (TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2)) => + sameType(pre1, pre2) && sameSymbol(sym1, sym2) && + ((tp1.isHigherKinded && tp2.isHigherKinded && tp1.normalize =:= tp2.normalize) || + sameTypes(args1, args2)) + // @M! normalize reduces higher-kinded case to PolyType's + + case (RefinedType(parents1, ref1), RefinedType(parents2, ref2)) => + def isSubScope(s1: Scope, s2: Scope): Boolean = s2.toList.forall { + sym2 => + var e1 = s1.lookupEntry(sym2.name) + (e1 ne null) && { + var isEqual = false + while (!isEqual && (e1 ne null)) { + isEqual = sameType(e1.sym.info, sym2.info) + e1 = s1.lookupNextEntry(e1) + } + isEqual + } + } + sameTypes(parents1, parents2) && isSubScope(ref1, ref2) && isSubScope(ref2, ref1) + + case (MethodType(params1, res1), MethodType(params2, res2)) => + // new dependent types: probably fix this, use substSym as done for PolyType + (sameTypes(tp1.paramTypes, tp2.paramTypes) && + sameType(res1, res2) && + tp1.isInstanceOf[ImplicitMethodType] == tp2.isInstanceOf[ImplicitMethodType]) + + case (PolyType(tparams1, res1), PolyType(tparams2, res2)) => + (tparams1.length == tparams2.length && + List.forall2(tparams1, tparams2) + ((p1, p2) => sameType(p1.info, p2.info)) && + sameType(res1, res2)) + case (ExistentialType(tparams1, res1), ExistentialType(tparams2, res2)) => + (tparams1.length == tparams2.length && + List.forall2(tparams1, tparams2) + ((p1, p2) => sameType(p1.info, p2.info)) && + sameType(res1, res2)) + case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => + sameType(lo1, lo2) && sameType(hi1, hi2) + case (BoundedWildcardType(bounds), _) => + bounds containsType tp2 + case (_, BoundedWildcardType(bounds)) => + bounds containsType tp1 + + case (AnnotatedType(_,_,_), _) => + annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations + case (_, AnnotatedType(_,_,_)) => + annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations + + case (_: SingletonType, _: SingletonType) => + var origin1 = tp1 + while (origin1.underlying.isInstanceOf[SingletonType]) { + assert(origin1 ne origin1.underlying, origin1) + origin1 = origin1.underlying + } + var origin2 = tp2 + while (origin2.underlying.isInstanceOf[SingletonType]) { + assert(origin2 ne origin2.underlying, origin2) + origin2 = origin2.underlying + } + ((origin1 ne tp1) || (origin2 ne tp2)) && sameType(origin1, origin2) + case _ => + false + }) || { + val tp1n = normalizePlus(tp1) + val tp2n = normalizePlus(tp2) + ((tp1n ne tp1) || (tp2n ne tp2)) && sameType(tp1n, tp2n) + } + + def sameTypes(tps1: List[Type], tps2: List[Type]): Boolean = + (tps1.length == tps2.length + && List.forall2(tps1, tps2)(sameType)) /** Return the list of changes between 'from' and 'to'. */ def changeSet(from: Symbol, to: Symbol): List[Change] = { + implicit val defaultReason = "types" // println("changeSet " + from + "(" + from.info + ")" // + " vs " + to + "(" + to.info + ")") val cs = new mutable.ListBuffer[Change] - if (((from.info.parents zip to.info.parents) exists { case (t1, t2) => !(t1 =:= t2) }) - || (from.typeParams != to.typeParams)) - cs += Changed(toEntity(from)) + if ((from.info.parents zip to.info.parents) exists { case (t1, t2) => !sameType(t1, t2) }) + cs += Changed(toEntity(from))(from.info.parents.zip(to.info.parents).toString) + if (from.typeParams != to.typeParams) + cs += Changed(toEntity(from))(" tparams: " + from.typeParams.zip(to.typeParams)) // new members not yet visited val newMembers = mutable.HashSet[Symbol]() @@ -55,17 +162,18 @@ abstract class Changes { val n = to.info.decl(o.name)) { newMembers -= n - if (o.isClass) - cs ++= changeSet(o, n) - else if (n == NoSymbol) - cs += Removed(toEntity(o)) - else { - val newSym = n.suchThat(_.tpe =:= o.tpe) - if (newSym == NoSymbol) { -// println(n + " changed from " + o.tpe + " to " + n.tpe) - cs += Changed(toEntity(o)) - } else - newMembers -= newSym + if (!o.hasFlag(Flags.PRIVATE | Flags.LOCAL | Flags.LIFTED)) { + if (o.isClass) + cs ++= changeSet(o, n) + else if (n == NoSymbol) + cs += Removed(toEntity(o)) + else { + val newSym = n.suchThat(ov => sameType(ov.tpe, o.tpe)) + if (newSym == NoSymbol) { + cs += Changed(toEntity(o))(n + " changed from " + o.tpe + " to " + n.tpe + " flags: " + Flags.flagsToString(o.flags)) + } else + newMembers -= newSym + } } } cs ++= (newMembers map (Added compose toEntity)) diff --git a/src/compiler/scala/tools/nsc/dependencies/References.scala b/src/compiler/scala/tools/nsc/dependencies/References.scala index 360c2474f..2dc4bc4c8 100644 --- a/src/compiler/scala/tools/nsc/dependencies/References.scala +++ b/src/compiler/scala/tools/nsc/dependencies/References.scala @@ -30,14 +30,15 @@ abstract class References extends SubComponent with Files { override def traverse(tree: Tree) { if ((tree.symbol ne null) && (tree.symbol != NoSymbol) + && (!tree.symbol.isPackage) && (!tree.symbol.hasFlag(Flags.JAVA)) && ((tree.symbol.sourceFile eq null) || (tree.symbol.sourceFile.path != file.path))) { references = references.updated(file, references(file) + tree.symbol.fullNameString) } tree match { - case cdef: ClassDef if !cdef.symbol.isModuleClass => - buf += cdef.symbol.cloneSymbol + case cdef: ClassDef if !cdef.symbol.isModuleClass && !cdef.symbol.hasFlag(Flags.PACKAGE) => + buf += cdef.symbol super.traverse(tree) case _ => diff --git a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala index c2146f44f..fe9ff4b81 100644 --- a/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala +++ b/src/compiler/scala/tools/nsc/interactive/RefinedBuildManager.scala @@ -20,7 +20,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana object referencesAnalysis extends { val global: BuilderGlobal.this.type = BuilderGlobal.this - val runsAfter = List("typer") + val runsAfter = List("icode") val runsRightAfter = None } with References @@ -59,7 +59,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana * them and all files that depend on them. Only files that * have been previously added as source files are recompiled. */ - def update(files: Set[AbstractFile]) { + def update(files: Set[AbstractFile]): Unit = if (!files.isEmpty) { val deps = compiler.dependencyAnalysis.dependencies val run = new compiler.Run() compiler.inform("compiling " + files) @@ -83,7 +83,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana } println("Changes: " + changesOf) updateDefinitions - invalidated(files, changesOf) + update(invalidated(files, changesOf)) } /** Return the set of source files that are invalidated by the given changes. */ @@ -92,6 +92,7 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana var directDeps = compiler.dependencyAnalysis.dependencies.dependentFiles(1, files) +// println("direct dependencies on " + files + " " + directDeps) def invalidate(file: AbstractFile, reason: String, change: Change) = { println("invalidate " + file + " because " + reason + " [" + change + "]") buf += file @@ -126,11 +127,13 @@ class RefinedBuildManager(val settings: Settings) extends Changes with BuildMana case Changed(Class(name)) => if (cls.info.typeSymbol.fullNameString == name) invalidate(file, "self type changed", change) + case _ => + () } } def checkReferences(file: AbstractFile) { - println(references) +// println(file + ":" + references(file)) val refs = references(file) change match { case Removed(Definition(name)) if refs(name) =>