mirror of https://github.com/sbt/sbt.git
approximate type parameters and references by name
not as accurate, but simpler.
This commit is contained in:
parent
00d0918714
commit
864580aae1
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ MethodParameter
|
|||
modifier: ParameterModifier
|
||||
|
||||
TypeParameter
|
||||
id: Int
|
||||
id: String
|
||||
annotations: Annotation*
|
||||
typeParameters : TypeParameter*
|
||||
variance: Variance
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Type
|
|||
prefix : SimpleType
|
||||
id: String
|
||||
ParameterRef
|
||||
id: Int
|
||||
id: String
|
||||
Singleton
|
||||
path: Path
|
||||
EmptyType
|
||||
|
|
|
|||
Loading…
Reference in New Issue