From 864580aae1c32041beccfcf61cb2b545b80c9579 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sat, 12 May 2012 23:12:29 -0400 Subject: [PATCH] approximate type parameters and references by name not as accurate, but simpler. --- compile/api/APIUtil.scala | 28 ----- compile/api/ClassToAPI.scala | 5 +- compile/api/HashAPI.scala | 15 +-- compile/api/SameAPI.scala | 23 ++-- compile/api/TagTypeVariables.scala | 164 ----------------------------- compile/interface/API.scala | 9 +- compile/persist/APIFormats.scala | 6 +- interface/other | 2 +- interface/type | 2 +- 9 files changed, 21 insertions(+), 233 deletions(-) delete mode 100644 compile/api/TagTypeVariables.scala diff --git a/compile/api/APIUtil.scala b/compile/api/APIUtil.scala index c05db8bfa..5f2d615d7 100644 --- a/compile/api/APIUtil.scala +++ b/compile/api/APIUtil.scala @@ -16,34 +16,6 @@ object APIUtil new Modifiers( x(0), x(1), x(2), x(3), x(4), x(5), x(6) ) } - def verifyTypeParameters(s: SourceAPI): Boolean = - { - val check = new CheckTypeParameters - val invalid = check(s) - if(!invalid.isEmpty) println("References to undefined type parameters: " + invalid.mkString(", ")) - invalid.isEmpty - } - private[this] class CheckTypeParameters extends Visit - { - private val defined = new HashSet[Int] - private val referenced = new HashSet[Int] - def apply(s: SourceAPI): List[Int] = - { - super.visitAPI(s) - (referenced filterNot defined).toList - } - override def visitTypeParameter(parameter: TypeParameter) - { - defined += parameter.id - super.visitTypeParameter(parameter) - } - override def visitParameterRef(ref: ParameterRef) - { - referenced += ref.id - super.visitParameterRef(ref) - } - } - def hasMacro(s: SourceAPI): Boolean = { val check = new HasMacro diff --git a/compile/api/ClassToAPI.scala b/compile/api/ClassToAPI.scala index 41f2ce61f..ccb6640b0 100644 --- a/compile/api/ClassToAPI.scala +++ b/compile/api/ClassToAPI.scala @@ -172,9 +172,8 @@ object ClassToAPI new api.TypeParameter(typeVariable(tp), emptyAnnotationArray, emptyTypeParameterArray, api.Variance.Invariant, NothingRef, upperBounds(tp.getBounds)) // needs to be stable across compilations - // preferably, it would be a proper unique id based on de Bruijn index - def typeVariable[T <: GenericDeclaration](tv: TypeVariable[T]): Int = - reduceHash((name(tv.getGenericDeclaration) + " " + tv.getName).getBytes) + def typeVariable[T <: GenericDeclaration](tv: TypeVariable[T]): String = + name(tv.getGenericDeclaration) + " " + tv.getName def reduceHash(in: Array[Byte]): Int = (0 /: in)( (acc, b) => (acc * 43) ^ b) diff --git a/compile/api/HashAPI.scala b/compile/api/HashAPI.scala index c47847161..5b5b7ad3a 100644 --- a/compile/api/HashAPI.scala +++ b/compile/api/HashAPI.scala @@ -6,21 +6,16 @@ package xsbt.api import scala.util import xsbti.api._ import util.MurmurHash -import TagTypeVariables.TypeVars import HashAPI.Hash object HashAPI { type Hash = Int def apply(a: SourceAPI): Hash = - { - /** de Bruijn levels for type parameters in source a and b*/ - val tags = TagTypeVariables(a) - (new HashAPI(tags, false, true)).hashAPI(a) - } + (new HashAPI(false, true)).hashAPI(a) } -final class HashAPI(tags: TypeVars, includePrivate: Boolean, includeParamNames: Boolean) +final class HashAPI(includePrivate: Boolean, includeParamNames: Boolean) { import scala.collection.mutable import MurmurHash._ @@ -229,6 +224,7 @@ final class HashAPI(tags: TypeVars, includePrivate: Boolean, includeParamNames: def hashTypeParameters(parameters: Seq[TypeParameter]) = hashSeq(parameters, hashTypeParameter) def hashTypeParameter(parameter: TypeParameter) { + hashString(parameter.id) extend(parameter.variance.ordinal) hashTypeParameters(parameter.typeParameters) hashType(parameter.lowerBound) @@ -267,10 +263,7 @@ final class HashAPI(tags: TypeVars, includePrivate: Boolean, includeParamNames: def hashParameterRef(p: ParameterRef) { extend(ParameterRefHash) - tags.get(p.id) match { - case Some((a,b)) => extend(a); extend(b) - case None => extend(-1) - } + hashString(p.id) } def hashSingleton(s: Singleton) { diff --git a/compile/api/SameAPI.scala b/compile/api/SameAPI.scala index 7ace3cfbd..c363eaeb9 100644 --- a/compile/api/SameAPI.scala +++ b/compile/api/SameAPI.scala @@ -39,7 +39,7 @@ object TopLevel def definitions(i: Iterable[SourceAPI]) = SameAPI.separateDefinitions(i.toSeq.flatMap( _.definitions )) def names(s: Iterable[Definition]): Set[String] = Set() ++ s.map(_.name) } - import TagTypeVariables.TypeVars + /** Checks the API of two source files for equality.*/ object SameAPI { @@ -56,11 +56,7 @@ object SameAPI println("\n=========== API #2 ================") println(ShowAPI.show(b))*/ - /** de Bruijn levels for type parameters in source a and b*/ - val tagsA = TagTypeVariables(a) - val tagsB = TagTypeVariables(b) - - val result = (new SameAPI(tagsA,tagsB, false, true)).check(a,b) + val result = (new SameAPI(false, true)).check(a,b) val end = System.currentTimeMillis //println(" API comparison took: " + (end - start) / 1000.0 + " s") result @@ -99,13 +95,11 @@ object SameAPI case _ => true } } -/** Used to implement API equality. All comparisons must be done between constructs in source files `a` and `b`. For example, when doing: -* `sameDefinitions(as, bs)`, `as` must be definitions from source file `a` and `bs` must be definitions from source file `b`. This is in order -* to properly handle type parameters, which must be computed for each source file and then referenced during comparison. +/** Used to implement API equality. * * If `includePrivate` is true, `private` and `private[this]` members are included in the comparison. Otherwise, those members are excluded. */ -class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, includeParamNames: Boolean) +class SameAPI(includePrivate: Boolean, includeParamNames: Boolean) { import SameAPI._ @@ -304,13 +298,8 @@ class SameAPI(tagsA: TypeVars, tagsB: TypeVars, includePrivate: Boolean, include debug(sameType(a.upperBound, b.upperBound), "Different upper bound") && sameTags(a.id, b.id) } - // until dangling type parameter references are straightened out in API phase, approximate - def sameTags(a: Int, b: Int): Boolean = - { - val ta = tagsA.get(a) - val tb = tagsB.get(b) - debug(ta == tb, "Different type parameter bindings: " + ta + "(" + a + "), " + tb + "(" + b + ")") - } + def sameTags(a: String, b: String): Boolean = + debug(a == b, "Different type parameter bindings: " + a + ", " + b) def sameType(a: Type, b: Type): Boolean = samePending(a,b)(sameTypeDirect) diff --git a/compile/api/TagTypeVariables.scala b/compile/api/TagTypeVariables.scala deleted file mode 100644 index 3b8f762e3..000000000 --- a/compile/api/TagTypeVariables.scala +++ /dev/null @@ -1,164 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2010 Mark Harrah - */ -package xsbt.api - - import xsbti.api._ - import scala.collection.mutable - -object TagTypeVariables -{ - type TypeVars = collection.Map[Int, (Int, Int)] - def apply(s: SourceAPI): TypeVars = (new TagTypeVariables).tag(s) -} -import TagTypeVariables.TypeVars -private class TagTypeVariables -{ - private val taggedStructures = new mutable.HashSet[Structure] - private val taggedClasses = new mutable.HashSet[ClassLike] - - private val tags = new mutable.HashMap[Int, (Int, Int)] - private var level = 0 - private var index = 0 - - def tag(s: SourceAPI): TypeVars = - { - tagDefinitions(s.definitions) - tags - } - def tagDefinitions(ds: Seq[Definition], eachFresh: Boolean = false): Unit = - for(d <- ds) if(eachFresh) freshScope(tagDefinition(d)) else tagDefinition(d) - - def tagDefinition(d: Definition) - { - d match - { - case c: ClassLike => tagClass(c) - case f: FieldLike => tagField(f) - case d: Def => tagDef(d) - case t: TypeDeclaration => tagTypeDeclaration(t) - case t: TypeAlias => tagTypeAlias(t) - } - } - def tagClass(c: ClassLike): Unit = if(taggedClasses add c) tagClass0(c) - def tagClass0(c: ClassLike): Unit = - tagParameterizedDefinition(c) { - tagType(c.selfType) - tagStructure(c.structure) - } - def tagField(f: FieldLike) - { - tagType(f.tpe) - tagAnnotations(f.annotations) - } - def tagDef(d: Def): Unit = - tagParameterizedDefinition(d) { - tagValueParameters(d.valueParameters) - tagType(d.returnType) - } - def tagValueParameters(valueParameters: Seq[ParameterList]) = valueParameters.foreach(tagValueParameterList) - def tagValueParameterList(list: ParameterList) = list.parameters.foreach(tagValueParameter) - def tagValueParameter(parameter: MethodParameter) = tagType(parameter.tpe) - - def tagParameterizedDefinition[T <: ParameterizedDefinition](d: T)(tagExtra: => Unit) - { - tagAnnotations(d.annotations) - scope { - tagTypeParameters(d.typeParameters) - tagExtra - } - } - def tagTypeDeclaration(d: TypeDeclaration): Unit = - tagParameterizedDefinition(d) { - tagType(d.lowerBound) - tagType(d.upperBound) - } - def tagTypeAlias(d: TypeAlias): Unit = - tagParameterizedDefinition(d) { - tagType(d.tpe) - } - - def tagTypeParameters(parameters: Seq[TypeParameter]) = parameters.foreach(tagTypeParameter) - def tagTypeParameter(parameter: TypeParameter) - { - recordTypeParameter(parameter.id) - scope { - tagTypeParameters(parameter.typeParameters) - tagType(parameter.lowerBound) - tagType(parameter.upperBound) - } - } - def tagAnnotations(annotations: Seq[Annotation]) = tagTypes(annotations.map(_.base)) - - def tagTypes(ts: Seq[Type]) = ts.foreach(tagType) - def tagType(t: Type) - { - t match - { - case s: Structure => tagStructure(s) - case e: Existential => tagExistential(e) - case c: Constant => tagConstant(c) - case p: Polymorphic => tagPolymorphic(p) - case a: Annotated => tagAnnotated(a) - case p: Parameterized => tagParameterized(p) - case p: Projection => tagProjection(p) - case _: EmptyType | _: Singleton | _: ParameterRef => () - } - } - - def tagConstant(c: Constant) = tagType(c.baseType) - def tagExistential(e: Existential) = tagParameters(e.clause, e.baseType) - def tagPolymorphic(p: Polymorphic) = tagParameters(p.parameters, p.baseType) - def tagProjection(p: Projection) = tagType(p.prefix) - def tagParameterized(p: Parameterized) - { - tagType(p.baseType) - tagTypes(p.typeArguments) - } - def tagAnnotated(a: Annotated) - { - tagType(a.baseType) - tagAnnotations(a.annotations) - } - def tagStructure(structure: Structure): Unit = if(taggedStructures add structure) tagStructure0(structure) - def tagStructure0(structure: Structure) - { - tagTypes(structure.parents) - tagDefinitions(structure.declared) - tagDefinitions(structure.inherited, eachFresh=true) - } - def tagParameters(parameters: Seq[TypeParameter], base: Type): Unit = - scope { - tagTypeParameters(parameters) - tagType(base) - } - - def scope(action: => Unit) - { - val saveIndex = index - index = 0 - level += 1 - - action - - level -= 1 - index = saveIndex - } - def freshScope(action: => Unit) - { - val saveIndex = index - val saveLevel = level - index = 0 - level = 0 - - action - - level = saveLevel - index = saveIndex - } - def recordTypeParameter(id: Int) - { - tags(id) = (level, index) - index += 1 - } -} \ No newline at end of file diff --git a/compile/interface/API.scala b/compile/interface/API.scala index e6cb036b8..3a5e9c2b1 100644 --- a/compile/interface/API.scala +++ b/compile/interface/API.scala @@ -126,9 +126,7 @@ final class API(val global: CallbackGlobal) extends Compat else if(sym.isRoot || sym.isRootPackage) Constants.emptyType else new xsbti.api.Projection(simpleType(in, pre), sym.nameString) } - - private def reference(sym: Symbol): xsbti.api.ParameterRef = new xsbti.api.ParameterRef(sym.id) - + private def reference(sym: Symbol): xsbti.api.ParameterRef = new xsbti.api.ParameterRef(tparamID(sym)) private def annotations(in: Symbol, as: List[AnnotationInfo]): Array[xsbti.api.Annotation] = as.toArray[AnnotationInfo].map(annotation(in,_)) private def annotation(in: Symbol, a: AnnotationInfo) = @@ -338,11 +336,12 @@ final class API(val global: CallbackGlobal) extends Compat val variance = if(varianceInt < 0) Contravariant else if(varianceInt > 0) Covariant else Invariant viewer(in).memberInfo(s) match { - case TypeBounds(low, high) => new xsbti.api.TypeParameter( s.id, annots, typeParameters(in, s), variance, processType(in, low), processType(in, high) ) - case PolyType(typeParams, base) => new xsbti.api.TypeParameter( s.id, annots, typeParameters(in, typeParams), variance, processType(in, base.bounds.lo), processType(in, base.bounds.hi)) + case TypeBounds(low, high) => new xsbti.api.TypeParameter( tparamID(s), annots, typeParameters(in, s), variance, processType(in, low), processType(in, high) ) + case PolyType(typeParams, base) => new xsbti.api.TypeParameter( tparamID(s), annots, typeParameters(in, typeParams), variance, processType(in, base.bounds.lo), processType(in, base.bounds.hi)) case x => error("Unknown type parameter info: " + x.getClass) } } + private def tparamID(s: Symbol) = s.fullName private def selfType(in: Symbol, s: Symbol): xsbti.api.Type = processType(in, s.thisSym.typeOfThis) private def classLike(in: Symbol, c: Symbol): ClassLike = classLikeCache.getOrElseUpdate( (in,c), mkClassLike(in, c)) diff --git a/compile/persist/APIFormats.scala b/compile/persist/APIFormats.scala index dfd276500..441b1b266 100644 --- a/compile/persist/APIFormats.scala +++ b/compile/persist/APIFormats.scala @@ -62,8 +62,8 @@ trait APIFormats extends FormatExtra implicit def formatExistential(implicit t: Format[Type], tps: Format[Array[TypeParameter]]): Format[Existential] = p2( (e: Existential) => (e.baseType, e.clause) )( new Existential(_,_) )(t,tps) - implicit def formatParameterRef(implicit i: Format[Int]): Format[ParameterRef] = - wrap[ParameterRef, Int](_.id, new ParameterRef(_))(i) + implicit def formatParameterRef(implicit i: Format[String]): Format[ParameterRef] = + wrap[ParameterRef, String](_.id, new ParameterRef(_))(i) // cyclic with many formats def formatType(implicit s: Format[SimpleType], a: Format[Annotated], st: Format[Structure], e: Format[Existential], po: Format[Polymorphic]): Format[Type] = @@ -172,7 +172,7 @@ trait APIFormats extends FormatExtra implicit def formatModifiers(implicit bf: Format[Byte]): Format[Modifiers] = wrap[Modifiers, Byte]( modifiersToByte, byteToModifiers ) - def formatTypeParameter(tps: Format[TypeParameter] => Format[Array[TypeParameter]])(implicit as: Format[Array[Annotation]], t: Format[Type], v: Format[Variance], i: Format[Int]): Format[TypeParameter] = + def formatTypeParameter(tps: Format[TypeParameter] => Format[Array[TypeParameter]])(implicit as: Format[Array[Annotation]], t: Format[Type], v: Format[Variance], i: Format[String]): Format[TypeParameter] = { lazy val ltps: Format[Array[TypeParameter]] = lazyFormat( tps(ltp) ) lazy val ltp = p6( (tp: TypeParameter) => (tp.id, tp.annotations, tp.typeParameters, tp.variance, tp.lowerBound, tp.upperBound))(new TypeParameter(_,_,_,_,_,_))(i, as, ltps, v, t, t) diff --git a/interface/other b/interface/other index aeec1ae5a..a8f5ba49c 100644 --- a/interface/other +++ b/interface/other @@ -39,7 +39,7 @@ MethodParameter modifier: ParameterModifier TypeParameter - id: Int + id: String annotations: Annotation* typeParameters : TypeParameter* variance: Variance diff --git a/interface/type b/interface/type index ae24f5cc1..ac0926e92 100644 --- a/interface/type +++ b/interface/type @@ -5,7 +5,7 @@ Type prefix : SimpleType id: String ParameterRef - id: Int + id: String Singleton path: Path EmptyType