summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-09-19 15:48:23 +0000
committerPaul Phillips <paulp@improving.org>2011-09-19 15:48:23 +0000
commite21d9b0a3907ee59b4d05489ecaf0fbf6467e27f (patch)
treed5d614e57cf207a8c43755ba7abeccbcc122725e /src
parent5637b22d21ccc1abe70e16e87c1607cd3acb9b3c (diff)
downloadscala-e21d9b0a3907ee59b4d05489ecaf0fbf6467e27f.tar.gz
scala-e21d9b0a3907ee59b4d05489ecaf0fbf6467e27f.tar.bz2
scala-e21d9b0a3907ee59b4d05489ecaf0fbf6467e27f.zip
Added tool for analyzing forwarder classes.
If you build and then run tools/proxy-report <outdir> you will be given files revealing interesting things like: /** With respect to trait SetLike, trait SetProxyLike does NOT wrap: */ trait Unwrapped { def +(elem1: A,elem2: A,elems: A*): This def ++(elems: scala.collection.GenTraversableOnce[A]): This def subsets(len: Int): Iterator[This] def subsets: Iterator[This] protected override def newBuilder: scala.collection.mutable.Builder[A,This] protected override def parCombiner: scala.collection.parallel.Combiner[A,scala.collection.parallel.ParSet[A]] } Lots more possible here, for now I just want to get a backstop against our worst transgressions (not just in the library -- look at SimpleTypeProxy, if you dare!) I will inquire about the results separately, so no review.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/reflect/internal/Symbols.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala56
-rw-r--r--src/compiler/scala/tools/nsc/util/ProxyReport.scala146
3 files changed, 179 insertions, 26 deletions
diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala
index 9757297e8f..7127e989fb 100644
--- a/src/compiler/scala/reflect/internal/Symbols.scala
+++ b/src/compiler/scala/reflect/internal/Symbols.scala
@@ -1615,6 +1615,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
else owner.ancestors map overriddenSymbol filter (_ != NoSymbol)
/** Equivalent to allOverriddenSymbols.nonEmpty, but more efficient. */
+ // !!! When if ever will this answer differ from .isOverride?
+ // How/where is the OVERRIDE flag managed, as compared to how checks
+ // based on type membership will evaluate?
def isOverridingSymbol = owner.isClass && (
owner.ancestors exists (cls => matchingSymbol(cls, owner.thisType) != NoSymbol)
)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 8828889231..68cc6db2fe 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -82,6 +82,28 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
if (sym.hasAccessBoundary) "" + sym.privateWithin.name else ""
)
+ def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.normalize, tp2.normalize) match {
+ case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) =>
+ rtp1 <:< rtp2
+ case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
+ rtp1 <:< rtp2
+ case (TypeRef(_, sym, _), _) if sym.isModuleClass =>
+ overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix)
+ case _ =>
+ def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner)
+
+ (tp1 <:< tp2) || ( // object override check
+ tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && {
+ val cb1 = classBoundAsSeen(tp1)
+ val cb2 = classBoundAsSeen(tp2)
+ (cb1 <:< cb2) && {
+ log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2))
+ true
+ }
+ }
+ )
+ }
+
class RefCheckTransformer(unit: CompilationUnit) extends Transformer {
var localTyper: analyzer.Typer = typer;
@@ -227,17 +249,6 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
}
- def isConformingObjectOverride(tp1: Type, tp2: Type) = {
- tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && {
- val cb1 = classBoundAsSeen(tp1)
- val cb2 = classBoundAsSeen(tp2)
-
- (cb1 <:< cb2) && {
- log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2))
- true
- }
- }
- }
def isAbstractTypeWithoutFBound(sym: Symbol) = // (part of DEVIRTUALIZE)
sym.isAbstractType && !sym.isFBounded
@@ -256,21 +267,12 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
else "")
}
- def overridesType(tp1: Type, tp2: Type): Boolean = (tp1.normalize, tp2.normalize) match {
- case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) =>
- rtp1 <:< rtp2
- case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
- rtp1 <:< rtp2
- case (TypeRef(_, sym, _), _) if sym.isModuleClass =>
- overridesType(NullaryMethodType(tp1), tp2)
- case _ =>
- (tp1 <:< tp2) || isConformingObjectOverride(tp1, tp2)
- }
-
/** Check that all conditions for overriding `other` by `member`
* of class `clazz` are met.
*/
- def checkOverride(clazz: Symbol, member: Symbol, other: Symbol) {
+ def checkOverride(member: Symbol, other: Symbol) {
+ def memberTp = self.memberType(member)
+ def otherTp = self.memberType(other)
def noErrorType = other.tpe != ErrorType && member.tpe != ErrorType
def isRootOrNone(sym: Symbol) = sym == RootClass || sym == NoSymbol
def objectOverrideErrorMsg = (
@@ -400,6 +402,8 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
}
+
+
def checkOverrideTypes() {
if (other.isAliasType) {
//if (!member.typeParams.isEmpty) (1.5) @MAT
@@ -449,13 +453,13 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
other.cookJavaRawInfo() // #2454
val memberTp = self.memberType(member)
val otherTp = self.memberType(other)
- if (!overridesType(memberTp, otherTp)) { // 8
+ if (!overridesTypeInPrefix(memberTp, otherTp, self)) { // 8
overrideTypeError()
explainTypes(memberTp, otherTp)
}
if (member.isStable && !otherTp.isVolatile) {
- if (memberTp.isVolatile)
+ if (memberTp.isVolatile)
overrideError("has a volatile type; cannot override a member with non-volatile type")
else memberTp.normalize.resultType match {
case rt: RefinedType if !(rt =:= otherTp) && !(checkedCombinations contains rt.parents) =>
@@ -474,7 +478,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
val opc = new overridingPairs.Cursor(clazz)
while (opc.hasNext) {
//Console.println(opc.overriding/* + ":" + opc.overriding.tpe*/ + " in "+opc.overriding.fullName + " overrides " + opc.overridden/* + ":" + opc.overridden.tpe*/ + " in "+opc.overridden.fullName + "/"+ opc.overridden.hasFlag(DEFERRED));//debug
- if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden);
+ if (!opc.overridden.isClass) checkOverride(opc.overriding, opc.overridden);
opc.next
}
diff --git a/src/compiler/scala/tools/nsc/util/ProxyReport.scala b/src/compiler/scala/tools/nsc/util/ProxyReport.scala
new file mode 100644
index 0000000000..86cf2006bb
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/util/ProxyReport.scala
@@ -0,0 +1,146 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools.nsc
+package util
+
+import scala.collection.{ mutable, immutable, generic }
+
+/** A class for analyzing forwarding/proxy relationships.
+ */
+trait ProxyReport {
+ val global: Global
+ import global._
+ import definitions.{ getClass => gc, _ }
+
+ private object classes {
+ def isIgnorable(sym: Symbol) = sym :: sym.allOverriddenSymbols exists { s =>
+ ObjectClass isSubClass s.owner
+ }
+ def nonPrivateMethods(sym: Symbol) = {
+ val methods = sym.initialize.tpe.nonPrivateMembers filter { x =>
+ x.isMethod && !x.isConstructor && !x.isPrivate && !isIgnorable(x)
+ }
+ methods foreach (m => m.initialize.info.paramss.flatten foreach (_.initialize))
+ methods
+ }
+ lazy val GlobalClass = gc(classOf[Global].getName)
+ lazy val GenericClass = getModule("scala.collection.generic").moduleClass
+ lazy val CollectionClass = getModule("scala.collection").moduleClass
+
+ def getType(name: String) = getMember(GlobalClass, name.toTypeName)
+ def getColl(name: String) = getMember(CollectionClass, name.toTypeName)
+ def getGeneric(name: String) = getMember(GenericClass, name.toTypeName)
+
+ // the following operations + those in RewrappingTypeProxy are all operations
+ // in class Type that are overridden in some subclass
+ // Important to keep this up-to-date when new operations are added!
+ def TypeClass = getType("Type")
+ def SimpleTypeProxy = getType("SimpleTypeProxy")
+ def RewrappingTypeProxy = getType("RewrappingTypeProxy")
+
+ def TraversableForwarder = getGeneric("TraversableForwarder")
+ def IterableForwarder = getGeneric("IterableForwarder")
+ def SeqForwarder = getGeneric("SeqForwarder")
+ def TraversableLike = getColl("TraversableLike")
+ def TraversableProxy = getColl("TraversableProxyLike")
+ def IterableLike = getColl("IterableLike")
+ def IterableProxy = getColl("IterableProxyLike")
+ def MapLike = getColl("MapLike")
+ def MapProxy = getColl("MapProxyLike")
+ def SeqLike = getColl("SeqLike")
+ def SeqProxy = getColl("SeqProxyLike")
+ def SetLike = getColl("SetLike")
+ def SetProxy = getColl("SetProxyLike")
+ }
+ import classes._
+
+ val wrappedHeader = """
+/** With respect to %s, %s wraps:
+ */
+trait Wrapped {
+ """.trim
+ val unwrappedHeader = """
+/** With respect to %s, %s does NOT wrap:
+ */
+trait Unwrapped {
+ """.trim
+
+ def wrapReport(underlying: Symbol, proxy: Symbol) = {
+ val underlyingMs = nonPrivateMethods(underlying)
+ val proxyMs = nonPrivateMethods(proxy) filterNot (_.owner == underlying)
+ val (wrapped, unwrapped) = underlyingMs partition (m =>
+ proxyMs exists (p =>
+ (p.name == m.name) && {
+ val self = proxy.thisType
+ val memberTp = self.memberType(p)
+ val parentTp = self.memberType(m)
+
+ refChecks.overridesTypeInPrefix(memberTp, parentTp, self)
+ // || {
+ // // if (p.paramss.flatten.length == m.paramss.flatten.length)
+ // // println("names equal, overridesType false:\n " + ((p, m, memberTp, parentTp, self)) + "\n")
+ //
+ // false
+ // }
+ }
+ )
+ )
+
+ def show(xs: List[Symbol], template: String) = {
+ val lines = xs.map(_.initialize.defString).sorted.map(" " + _ + "\n")
+ lines.mkString(template.format(underlying, proxy) + "\n", "", "}")
+ }
+
+ show(wrapped, wrappedHeader) + "\n\n" + show(unwrapped, unwrappedHeader)
+ }
+
+ lazy val wrappers = List(
+ TypeClass -> SimpleTypeProxy,
+ TypeClass -> RewrappingTypeProxy,
+ TraversableClass -> TraversableForwarder,
+ IterableClass -> IterableForwarder,
+ SeqClass -> SeqForwarder,
+ TraversableLike -> TraversableProxy,
+ IterableLike -> IterableProxy,
+ MapLike -> MapProxy,
+ SetLike -> SetProxy,
+ SeqLike -> SeqProxy
+ )
+
+ def generate(dir: io.Directory) = {
+ /** A proxy for a type (identified by field `underlying`) that forwards most
+ * operations to it (for exceptions, see WrappingProxy, which forwards even more operations).
+ * every operation that is overridden for some kind of types should be forwarded.
+ */
+ for ((clazz, proxy) <- wrappers) {
+ val text = wrapReport(clazz, proxy)
+ val file = dir / (proxy.fullName + ".scala") toFile;
+
+ file writeAll text
+ println("Created " + file)
+ }
+ }
+}
+
+object ProxyReportRunner {
+ class ProxyGlobal(s: Settings) extends Global(s) {
+ object proxyReport extends {
+ val global: ProxyGlobal.this.type = ProxyGlobal.this
+ } with util.ProxyReport
+ }
+
+ def main(args: Array[String]): Unit = {
+ if (args.isEmpty)
+ return println("Give an output directory as argument.")
+
+ val dir = io.Directory(args(0)).createDirectory()
+ val s = new Settings()
+ s.processArguments(args.toList.tail, true)
+ val g = new ProxyGlobal(s)
+ val run = new g.Run()
+ g.atPhase(run.typerPhase.next)(g.proxyReport.generate(dir))
+ }
+}