refactoring of TypeVar to set the stage for tcpoly inference (also touched UndoLog, isHigherKinded logic in TypeRef)

added <:< implicit, should bootstrap

selection from squashed commit messages:
    commented out stuff so that this can be used to bootstrap and build a new starr
    merged/cherry picked refactorings unrelated to #2261
    (undoLog, cloneInternal, NoImplicitInfo)
    made conforms implicit, identity explicit
    replaced the implicit `identity` coercion by `conforms`, which can be used to encode generalised constraints
    the introduction of `conforms` revealed a bug in adaptToMember, which was inferring views while already inferring one, which gave rise to diverging implicits. Predef.identity is no longer special as far as the compiler is concerned.
    cleaned up isHigherKinded logic in TypeRef, and implemented it in TypeVar along with normalize
    added <:< to Predef: use as evidence for encoding generalized constraints
    (BTW: extractUndetparams clears undetparams: don't use in debug output -- I learned the hard way...)
    added todo about ticket 2066 -- branching from master to explicitkinds for fix
    refactoring: moved bounds tracking logic to TypeVar
    introduced typeConstructor in Type because we can't use appliedType(tp, List())) to strip a type's type arguments (appliedType is a no-op for empty args) -- don't want to pattern match on type either
    removed unused overrides in TypeVar (TODO double check)
    making appliedType more robust since it is now used more liberally -- neg/t0226 should no longer fail now
    merged in appliedType refactoring and added TypeVar logic to appliedType

git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@19204 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
moors 2009-10-21 18:33:45 +00:00
parent d2b4cfa170
commit fad15cfbce
8 changed files with 261 additions and 154 deletions

View File

@ -721,7 +721,10 @@ trait Symbols {
private[Symbols] var infos: TypeHistory = null
/** Get type. The type of a symbol is:
* for a type symbol, the type corresponding to the symbol itself
* for a type symbol, the type corresponding to the symbol itself,
* @M you should use tpeHK for a type symbol with type parameters if
* the kind of the type need not be *, as tpe introduces dummy arguments
* to generate a type of kind *
* for a term symbol, its usual type
*/
def tpe: Type = info
@ -879,6 +882,13 @@ trait Symbols {
def typeConstructor: Type =
throw new Error("typeConstructor inapplicable for " + this)
/** @M -- tpe vs tpeHK:
* Symbol::tpe creates a TypeRef that has dummy type arguments to get a type of kind *
* Symbol::tpeHK creates a TypeRef without type arguments, but with type params --> higher-kinded if non-empty list of tpars
* calling tpe may hide errors or introduce spurious ones
* (e.g., when deriving a type from the symbol of a type argument that must be higher-kinded)
* as far as I can tell, it only makes sense to call tpe in conjunction with a substitution that replaces the generated dummy type arguments by their actual types
*/
def tpeHK = if (isType) typeConstructor else tpe // @M! used in memberType
/** The type parameters of this symbol, without ensuring type completion.

View File

@ -88,7 +88,7 @@ trait Types {
/** Decrement depth unless it is a don't care */
private final def decr(depth: Int) = if (depth == AnyDepth) AnyDepth else depth - 1
private final val printLubs = false
private final val printLubs = false //@MDEBUG
/** The current skolemization level, needed for the algorithms
* in isSameType, isSubType that do constraint solving under a prefix
@ -98,8 +98,41 @@ trait Types {
/** A log of type variable with their original constraints. Used in order
* to undo constraints in the case of isSubType/isSameType failure.
*/
type UndoLog = List[(TypeVar, TypeConstraint)]
var undoLog: UndoLog = List()
object undoLog {
private type UndoLog = List[(TypeVar, TypeConstraint)]
private var log: UndoLog = List()
/** Undo all changes to constraints to type variables upto `limit'
*/
private def undoTo(limit: UndoLog) {
while (log ne limit) {
val (tv, constr) = log.head
tv.constr = constr
log = log.tail
}
}
private[Types] def record(tv: TypeVar) = {log = (tv, tv.constr.cloneInternal) :: log}
private[Types] def clear {log = List()}
// `block` should not affect constraints on typevars
def undo[T](block: => T): T = {
val before = log
val result = block
undoTo(before)
result
}
// if `block` evaluates to false, it should not affect constraints on typevars
def undoUnless(block: => Boolean): Boolean = {
val before = log
val result = block
if(!result) undoTo(before)
result
}
}
/** A map from lists to compound types that have the given list as parents.
* This is used to avoid duplication in the computation of base type sequences and baseClasses.
@ -116,15 +149,15 @@ trait Types {
// @M toString that is safe during debugging (does not normalize, ...)
def debugString(tp: Type): String = tp match {
case TypeRef(pre, sym, args) => "TypeRef"+(debugString(pre), sym, args map debugString)
case ThisType(sym) => "ThisType("+sym+")"
case SingleType(pre, sym) => "SingleType"+(debugString(pre), sym)
case RefinedType(parents, defs) => "RefinedType"+(parents map debugString, defs.toList)
case ClassInfoType(parents, defs, clazz) => "ClassInfoType"+(parents map debugString, defs.toList, clazz)
case PolyType(tparams, result) => "PolyType"+(tparams, debugString(result))
case TypeBounds(lo, hi) => "TypeBounds "+debugString(lo)+","+debugString(hi)
case TypeVar(origin, constr) => "TypeVar "+origin+","+constr
case ExistentialType(tparams, qtpe) => "ExistentialType("+(tparams map (_.defString))+","+debugString(qtpe)+")"
case TypeRef(pre, sym, args) => debugString(pre) +"."+ sym.nameString + (args map debugString).mkString("[",", ","]")
case ThisType(sym) => sym.nameString+".this"
case SingleType(pre, sym) => debugString(pre) +"."+ sym.nameString +".type"
case RefinedType(parents, defs) => (parents map debugString).mkString("", " with ", "") + defs.toList.mkString(" {", " ;\n ", "}")
case ClassInfoType(parents, defs, clazz) => "class "+ clazz.nameString + (parents map debugString).mkString("", " with ", "") + defs.toList.mkString("{", " ;\n ", "}")
case PolyType(tparams, result) => tparams.mkString("[", ", ", "] ") + debugString(result)
case TypeBounds(lo, hi) => ">: "+ debugString(lo) +" <: "+ debugString(hi)
case tv : TypeVar => tv.toString
case ExistentialType(tparams, qtpe) => "forsome "+ tparams.mkString("[", ", ", "] ") + debugString(qtpe)
case _ => tp.toString
}
@ -140,6 +173,7 @@ trait Types {
// Important to keep this up-to-date when new operations are added!
override def isTrivial = underlying.isTrivial
override def isHigherKinded: Boolean = underlying.isHigherKinded
override def typeConstructor: Type = underlying.typeConstructor
override def isNotNull = underlying.isNotNull
override def isError = underlying.isError
override def isErroneous = underlying.isErroneous
@ -302,6 +336,9 @@ trait Types {
case _ => List()
}
/** This type, without its type arguments @M */
def typeConstructor: Type = this
/** For a typeref, its arguments. The empty list for all other types */
def typeArgs: List[Type] = List()
@ -640,7 +677,7 @@ trait Types {
/** A test whether a type contains any unification type variables */
def isGround: Boolean = this match {
case TypeVar(_, constr) =>
constr.inst != NoType && constr.inst.isGround
constr.instValid && constr.inst.isGround
case TypeRef(pre, sym, args) =>
sym.isPackageClass || pre.isGround && (args forall (_.isGround))
case SingleType(pre, sym) =>
@ -1193,6 +1230,10 @@ trait Types {
if (isHigherKinded) parents.head.typeParams
else super.typeParams
//@M may result in an invalid type (references to higher-order args become dangling )
override def typeConstructor =
copyRefinedType(this, parents map (_.typeConstructor), decls)
private def dummyArgs = typeParams map (_.typeConstructor)
/* MO to AM: This is probably not correct
@ -1514,15 +1555,16 @@ A type's typeSymbol should never be inspected directly.
private def dummyArgs = typeParamsDirect map (_.typeConstructor) //@M must be .typeConstructor
// (!result.isEmpty) IFF isHigherKinded
override def typeParams: List[Symbol] = if (args.isEmpty) typeParamsDirect else List()
override def typeParams: List[Symbol] = if (isHigherKinded) typeParamsDirect else List()
//@M equivalent to:
// (!typeParams.isEmpty && args.isEmpty) && // because args.isEmpty is checked in typeParams
// !isRawType(this) // needed for subtyping
override def typeConstructor = rawTypeRef(pre, sym, List())
// (args.isEmpty && !typeParamsDirect.isEmpty) && !isRawType(this)
// check for isRawType: otherwise raw types are considered higher-kinded types during subtyping:
override def isHigherKinded
= !typeParams.isEmpty &&
// otherwise raw types are considered higher-kinded types during subtyping:
(phase.erasedTypes || !sym.hasFlag(JAVA))
= (args.isEmpty && !typeParamsDirect.isEmpty) && !isRaw(sym, args)
// (args.isEmpty && !typeParamsDirect.isEmpty) && (phase.erasedTypes || !sym.hasFlag(JAVA))
override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type =
if (isHigherKinded) {
@ -1872,7 +1914,9 @@ A type's typeSymbol should never be inspected directly.
def withTypeVars(op: Type => Boolean): Boolean = withTypeVars(op, AnyDepth)
def withTypeVars(op: Type => Boolean, depth: Int): Boolean = {
val tvars = quantified map (tparam => new TypeVar(tparam.tpe, new TypeConstraint))
val tvars = quantified map (tparam => TypeVar(tparam.tpe, new TypeConstraint)) // @M TODO
//@M should probably change to handle HK type infer properly:
// val tvars = quantified map (tparam => TypeVar(tparam))
val underlying1 = underlying.instantiateTypeParams(quantified, tvars)
op(underlying1) && {
solve(tvars, quantified, quantified map (x => 0), false, depth) &&
@ -1913,31 +1957,104 @@ A type's typeSymbol should never be inspected directly.
//private var tidCount = 0 //DEBUG
//@M
// a TypeVar used to be a case class with only an origin and a constr
// then, constr became mutable (to support UndoLog, I guess), but pattern-matching returned the original constr0 (a bug)
// now, pattern-matching returns the most recent constr
object TypeVar {
def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr))
def apply(origin: Type, constr: TypeConstraint) = new TypeVar(origin, constr)
def apply(tparam: Symbol) = new TypeVar(tparam.tpeHK, new TypeConstraint(List(),List()))
}
/** A class representing a type variable
* Not used after phase `typer'.
* Not used after phase `typer'.
* A higher-kinded type variable has type arguments (a list of Type's) and type paramers (list of Symbols)
* A TypeVar whose list of args is non-empty can only be instantiated by a higher-kinded type that can be applied to these args
* NOTE:
*/
case class TypeVar(origin: Type, constr0: TypeConstraint) extends Type {
class TypeVar(val origin: Type, val constr0: TypeConstraint) extends Type {
// var tid = { tidCount += 1; tidCount } //DEBUG
/** The constraint associated with the variable */
var constr = constr0
def instValid = constr.instValid
/** The variable's skolemizatuon level */
val level = skolemizationLevel
override def isHigherKinded = origin.isHigherKinded
def setInst(tp: Type) {
// assert(!(tp containsTp this), this)
constr.inst = tp
}
def tryInstantiate(tp: Type): Boolean =
if (constr.lobounds.forall(_ <:< tp) && constr.hibounds.forall(tp <:< _)) {
setInst(tp)
true
} else false
/** Can this variable be related in a constraint to type `tp'?
* This is not the case if `tp' contains type skolems whose
* skolemization level is higher than the level of this variable.
*/
def isRelatable(tp: Type): Boolean =
!tp.exists { t =>
t.typeSymbol match {
case ts: TypeSkolem => ts.level > level
case _ => false
}
}
/** Called from isSubtype0 when a TypeVar is involved in a subtyping check.
* if isLowerBound is true,
* registerBound returns whether this TypeVar could plausibly be a supertype of tp and,
* if so, tracks tp as a lower bound of this type variable
*
* if isLowerBound is false,
* registerBound returns whether this TypeVar could plausibly be a subtype of tp and,
* if so, tracks tp as a upper bound of this type variable
*/
def registerBound(tp: Type, isLowerBound: Boolean): Boolean = { //println("regBound: "+(safeToString, debugString(tp), isLowerBound)) //@MDEBUG
if(isLowerBound) assert(tp != this)
undoLog record this
def checkSubtype(tp1: Type, tp2: Type) =
if(isLowerBound) tp1 <:< tp2
else tp2 <:< tp1
def addBound(tp: Type) = {
if(isLowerBound) constr.lobounds = tp :: constr.lobounds
else constr.hibounds = tp :: constr.hibounds
// println("addedBound: "+(this, tp)) // @MDEBUG
}
if (constr.instValid) // type var is already set
checkSubtype(tp, constr.inst)
else isRelatable(tp) && {
addBound(tp)
true
}
}
def registerTypeEquality(tp: Type, typeVarLHS: Boolean): Boolean = { //println("regTypeEq: "+(safeToString, debugString(tp), typeVarLHS)) //@MDEBUG
def checkIsSameType(tp: Type) =
if(typeVarLHS) constr.inst =:= tp
else tp =:= constr.inst
if (constr.instValid) checkIsSameType(tp)
else isRelatable(tp) && {
undoLog record this
val newInst = wildcardToTypeVarMap(tp)
if (constr.lobounds.forall(_ <:< newInst) && constr.hibounds.forall(newInst <:< _)) {
setInst(tp)
true
} else false
}
}
override val isHigherKinded = false
override def normalize: Type =
if (constr.instValid) constr.inst
else super.normalize
override def typeSymbol = origin.typeSymbol
override def safeToString: String = {
@ -1950,6 +2067,8 @@ A type's typeSymbol should never be inspected directly.
override def isStable = origin.isStable
override def isVolatile = origin.isVolatile
override def kind = "TypeVar"
def cloneInternal = TypeVar(origin, constr cloneInternal)
}
/** A type carrying some annotations. Created by the typechecker
@ -2420,21 +2539,25 @@ A type's typeSymbol should never be inspected directly.
// Helper Classes ---------------------------------------------------------
/** A class expressing upper and lower bounds constraints
* for type variables, as well as their instantiations */
class TypeConstraint(lo: List[Type], hi: List[Type]) {
/** A class expressing upper and lower bounds constraints of type variables,
* as well as their instantiations.
*/
class TypeConstraint(lo0: List[Type], hi0: List[Type]) {
//var self: Type = _ //DEBUG
def this() = this(List(), List())
var lobounds: List[Type] = lo
var hibounds: List[Type] = hi
var inst: Type = NoType
var lobounds: List[Type] = lo0
var hibounds: List[Type] = hi0
def duplicate = {
val tc = new TypeConstraint(lo, hi)
var inst: Type = NoType // @M reduce visibility?
def instValid = (inst ne null) && (inst ne NoType)
def cloneInternal = {
val tc = new TypeConstraint(lobounds, hibounds)
tc.inst = inst
tc
}
override def toString =
(lobounds map (_.safeToString)).mkString("[ _>:(", ",", ") ") +
(hibounds map (_.safeToString)).mkString("| _<:(", ",", ") ] _= ") +
@ -2553,9 +2676,9 @@ A type's typeSymbol should never be inspected directly.
val args1 = args mapConserve (this)
if ((pre1 eq pre) && (args1 eq args)) tp
else AntiPolyType(pre1, args1)
case TypeVar(_, constr) =>
if (constr.inst != NoType) this(constr.inst)
else tp
case tv@TypeVar(_, constr) =>
if (constr.instValid) this(constr.inst)
else tv
case NotNullType(tp) =>
val tp1 = this(tp)
if (tp1 eq tp) tp
@ -3392,22 +3515,6 @@ A type's typeSymbol should never be inspected directly.
}
}
/** Can variable `tv' be related in a constraint to type `tp'?
* This is not the case if `tp' contains type skolems whose
* skolemization level is higher than the level of `tv'.
*/
private def isRelatable(tv: TypeVar, tp: Type): Boolean = {
var ok = true
for (t <- tp) {
t.typeSymbol match {
case ts: TypeSkolem => if (ts.level > tv.level) ok = false
case _ =>
}
}
if (ok) undoLog = (tv, tv.constr.duplicate) :: undoLog
ok
}
/** Is intersection of given types populated? That is,
* for all types tp1, tp2 in intersection
* for all common base classes bc of tp1 and tp2
@ -3482,18 +3589,6 @@ A type's typeSymbol should never be inspected directly.
}
}
/** Undo all changes to constraints to type variables upto `limit'
*/
private def undoTo(limit: UndoLog) {
while (undoLog ne limit)/* && !undoLog.isEmpty*/ { // @M added `&& !undoLog.isEmpty`
// Martin: I don't think the addition is necessary?
// @M TODO: I had an example, but seem to have misplaced it :-)
val (tv, constr) = undoLog.head
undoLog = undoLog.tail
tv.constr = constr
}
}
private var subsametypeRecursions: Int = 0
private def isUnifiable(pre1: Type, pre2: Type) =
@ -3506,26 +3601,24 @@ A type's typeSymbol should never be inspected directly.
/** Do `tp1' and `tp2' denote equivalent types?
*/
def isSameType(tp1: Type, tp2: Type): Boolean = try {
subsametypeRecursions += 1
val lastUndoLog = undoLog
val result = isSameType0(tp1, tp2)
sametypeCount += 1
if (!result) undoTo(lastUndoLog)
result
subsametypeRecursions += 1
undoLog undoUnless {
isSameType0(tp1, tp2)
}
} finally {
subsametypeRecursions -= 1
if (subsametypeRecursions == 0) undoLog = List()
if (subsametypeRecursions == 0) undoLog clear
}
def isDifferentType(tp1: Type, tp2: Type): Boolean = try {
subsametypeRecursions += 1
val lastUndoLog = undoLog
val result = isSameType0(tp1, tp2)
undoTo(lastUndoLog)
!result
undoLog undo { // undo type constraints that arise from operations in this block
!isSameType0(tp1, tp2)
}
} finally {
subsametypeRecursions -= 1
if (subsametypeRecursions == 0) undoLog = List()
if (subsametypeRecursions == 0) undoLog clear
}
def isDifferentTypeConstructor(tp1: Type, tp2: Type): Boolean = tp1 match {
@ -3627,12 +3720,10 @@ A type's typeSymbol should never be inspected directly.
bounds containsType tp2
case (_, BoundedWildcardType(bounds)) =>
bounds containsType tp1
case (tv1 @ TypeVar(_, constr1), _) =>
if (constr1.inst != NoType) constr1.inst =:= tp2
else isRelatable(tv1, tp2) && (tv1 tryInstantiate wildcardToTypeVarMap(tp2))
case (_, tv2 @ TypeVar(_, constr2)) =>
if (constr2.inst != NoType) tp1 =:= constr2.inst
else isRelatable(tv2, tp1) && (tv2 tryInstantiate wildcardToTypeVarMap(tp1))
case (tv @ TypeVar(_,_), tp) =>
tv.registerTypeEquality(tp, true)
case (tp, tv @ TypeVar(_,_)) =>
tv.registerTypeEquality(tp, false)
case (AnnotatedType(_,_,_), _) =>
annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations
case (_, AnnotatedType(_,_,_)) =>
@ -3672,8 +3763,8 @@ A type's typeSymbol should never be inspected directly.
def isSubType(tp1: Type, tp2: Type, depth: Int): Boolean = try {
subsametypeRecursions += 1
val lastUndoLog = undoLog
val result =
undoLog undoUnless { // if subtype test fails, it should not affect constraints on typevars
if (subsametypeRecursions >= LogPendingSubTypesThreshold) {
val p = new SubTypePair(tp1, tp2)
if (pendingSubTypes contains p)
@ -3688,11 +3779,10 @@ A type's typeSymbol should never be inspected directly.
} else {
isSubType0(tp1, tp2, depth)
}
if (!result) undoTo(lastUndoLog)
result
}
} finally {
subsametypeRecursions -= 1
if (subsametypeRecursions == 0) undoLog = List()
if (subsametypeRecursions == 0) undoLog clear
}
/** Does this type have a prefix that begins with a type variable,
@ -3703,8 +3793,8 @@ A type's typeSymbol should never be inspected directly.
def beginsWithTypeVarOrIsRefined(tp: Type): Boolean = tp match {
case SingleType(pre, sym) =>
!(sym hasFlag PACKAGE) && beginsWithTypeVarOrIsRefined(pre)
case TypeVar(_, constr) =>
constr.inst == NoType || beginsWithTypeVarOrIsRefined(constr.inst)
case tv@TypeVar(_, constr) =>
!tv.instValid || beginsWithTypeVarOrIsRefined(constr.inst)
case RefinedType(_, _) =>
true
case _ =>
@ -3834,8 +3924,7 @@ A type's typeSymbol should never be inspected directly.
case AnnotatedType(_, _, _) | BoundedWildcardType(_) =>
secondTry
case _ =>
if (constr2.inst != NoType) tp1 <:< constr2.inst
else isRelatable(tv2, tp1) && { assert(tp1 != tv2); constr2.lobounds = tp1 :: constr2.lobounds; true }
tv2.registerBound(tp1, true)
}
case _ =>
secondTry
@ -3851,9 +3940,8 @@ A type's typeSymbol should never be inspected directly.
tp1.withoutAnnotations <:< tp2.withoutAnnotations && annotationsConform(tp1, tp2)
case BoundedWildcardType(bounds) =>
tp1.bounds.lo <:< tp2
case tv1 @ TypeVar(_, constr1) =>
if (constr1.inst != NoType) constr1.inst <:< tp2
else isRelatable(tv1, tp2) && { constr1.hibounds = tp2 :: constr1.hibounds; true }
case tv @ TypeVar(_,_) =>
tv.registerBound(tp2, false)
case ExistentialType(_, _) =>
try {
skolemizationLevel += 1
@ -4041,7 +4129,7 @@ A type's typeSymbol should never be inspected directly.
(List.forall2(tparams1, tparams2)((p1, p2) =>
p2.info.substSym(tparams2, tpsFresh) <:< p1.info.substSym(tparams1, tpsFresh)) &&
res1.substSym(tparams1, tpsFresh) <:< res2.substSym(tparams2, tpsFresh))
//@M TODO: should also check that the kinds of the type parameters conform (ticket 2066)
//@M the forall in the previous test could be optimised to the following,
// but not worth the extra complexity since it only shaves 1s from quick.comp
// (List.forall2(tpsFresh/*optimisation*/, tparams2)((p1, p2) =>
@ -4061,12 +4149,10 @@ A type's typeSymbol should never be inspected directly.
bounds.lo <:< tp2
case (_, BoundedWildcardType(bounds)) =>
tp1 <:< bounds.hi
case (_, tv2 @ TypeVar(_, constr2)) =>
if (constr2.inst != NoType) tp1 <:< constr2.inst
else isRelatable(tv2, tp1) && { assert(tp1 != tv2); constr2.lobounds = tp1 :: constr2.lobounds; true }
case (tv1 @ TypeVar(_, constr1), _) =>
if (constr1.inst != NoType) constr1.inst <:< tp2
else isRelatable(tv1, tp2) && { constr1.hibounds = tp2 :: constr1.hibounds; true }
case (tp, tv @ TypeVar(_,_)) =>
tv.registerBound(tp, true)
case (tv @ TypeVar(_,_), tp) =>
tv.registerBound(tp, false)
case (_, _) if (tp1.isHigherKinded || tp2.isHigherKinded) =>
(tp1.typeSymbol == NothingClass
||
@ -4234,10 +4320,11 @@ A type's typeSymbol should never be inspected directly.
//Console.println("solveOne0 "+tvar+" "+config+" "+bound);//DEBUG
var cyclic = bound contains tparam
for ((tvar2, (tparam2, variance2)) <- config) {
// Console.println("solveOne0(tp,up,lo,hi,lo=tp,hi=tp)="+(tparam.tpe, up, tparam2.info.bounds.lo, tparam2.info.bounds.hi, (tparam2.info.bounds.lo =:= tparam.tpe), (tparam2.info.bounds.hi =:= tparam.tpe))) //DEBUG
if (tparam2 != tparam &&
((bound contains tparam2) ||
up && (tparam2.info.bounds.lo =:= tparam.tpe) || //@M TODO: might be affected by change to tpe in Symbol
!up && (tparam2.info.bounds.hi =:= tparam.tpe))) { //@M TODO: might be affected by change to tpe in Symbol
up && (tparam2.info.bounds.lo =:= tparam.tpe) || //@M TODO: should probably be .tpeHK
!up && (tparam2.info.bounds.hi =:= tparam.tpe))) { //@M TODO: should probably be .tpeHK
if (tvar2.constr.inst eq null) cyclic = true
solveOne(tvar2, tparam2, variance2)
}
@ -4249,7 +4336,7 @@ A type's typeSymbol should never be inspected directly.
bound.instantiateTypeParams(tparams, tvars) :: tvar.constr.hibounds
}
for (tparam2 <- tparams)
if (tparam2.info.bounds.lo =:= tparam.tpe) //@M TODO: might be affected by change to tpe in Symbol
if (tparam2.info.bounds.lo =:= tparam.tpe) //@M TODO: should probably be .tpeHK
tvar.constr.hibounds =
tparam2.tpe.instantiateTypeParams(tparams, tvars) :: tvar.constr.hibounds
} else {
@ -4258,21 +4345,27 @@ A type's typeSymbol should never be inspected directly.
bound.instantiateTypeParams(tparams, tvars) :: tvar.constr.lobounds
}
for (tparam2 <- tparams)
if (tparam2.info.bounds.hi =:= tparam.tpe) //@M TODO: might be affected by change to tpe in Symbol
if (tparam2.info.bounds.hi =:= tparam.tpe) //@M TODO: should probably be .tpeHK
tvar.constr.lobounds =
tparam2.tpe.instantiateTypeParams(tparams, tvars) :: tvar.constr.lobounds
}
}
tvar.constr.inst = NoType // necessary because hibounds/lobounds may contain tvar
// println("solveOne(useGlb, glb, lub): "+ (up, //@MDEBUG
// if (depth != AnyDepth) glb(tvar.constr.hibounds, depth) else glb(tvar.constr.hibounds),
// if (depth != AnyDepth) lub(tvar.constr.lobounds, depth) else lub(tvar.constr.lobounds)))
tvar setInst (
if (up) {
if (depth != AnyDepth) glb(tvar.constr.hibounds, depth) else glb(tvar.constr.hibounds)
} else {
if (depth != AnyDepth) lub(tvar.constr.lobounds, depth) else lub(tvar.constr.lobounds)
})
//Console.println("solving "+tvar+" "+up+" "+(if (up) (tvar.constr.hibounds) else tvar.constr.lobounds)+((if (up) (tvar.constr.hibounds) else tvar.constr.lobounds) map (_.widen))+" = "+tvar.constr.inst)//DEBUG"
// Console.println("solving "+tvar+" "+up+" "+(if (up) (tvar.constr.hibounds) else tvar.constr.lobounds)+((if (up) (tvar.constr.hibounds) else tvar.constr.lobounds) map (_.widen))+" = "+tvar.constr.inst)//@MDEBUG
}
}
}
for ((tvar, (tparam, variance)) <- config)
solveOne(tvar, tparam, variance)
@ -4395,7 +4488,7 @@ A type's typeSymbol should never be inspected directly.
case ExistentialType(_, res) =>
res
case TypeVar(_, constr) =>
if ((constr.inst ne null) && (constr.inst ne NoType)) constr.inst
if (constr.instValid) constr.inst
else throw new Error("trying to do lub/glb of typevar "+tp)
case t => t
}

View File

@ -615,7 +615,7 @@ abstract class ClassfileParser {
}
val newtparam = sym.newExistential(sym.pos, "?"+i) setInfo bounds
existentials += newtparam
xs += newtparam.tpe
xs += newtparam.tpe //@M should probably be .tpeHK
i += 1
case _ =>
xs += sig2type(tparams, skiptvs)

View File

@ -102,14 +102,11 @@ self: Analyzer =>
}
override def equals(other: Any) = other match {
case that: ImplicitInfo =>
if (this eq NoImplicitInfo) that eq this
else
case that: ImplicitInfo =>
this.name == that.name &&
this.pre =:= that.pre &&
this.sym == that.sym
case _ =>
false
case _ => false
}
override def hashCode =
@ -119,7 +116,12 @@ self: Analyzer =>
}
/** A sentinel indicating no implicit was found */
val NoImplicitInfo = new ImplicitInfo(null, NoType, NoSymbol)
val NoImplicitInfo = new ImplicitInfo(null, NoType, NoSymbol) {
// equals used to be implemented in ImplicitInfo with an `if(this eq NoImplicitInfo)`
// overriding the equals here seems cleaner and benchmarks show no difference in performance
override def equals(other: Any) = other match { case that: AnyRef => that eq this case _ => false }
override def hashCode = 1
}
/** A constructor for types ?{ name: tp }, used in infer view to member
* searches.
@ -510,6 +512,7 @@ self: Analyzer =>
if (containsError(info.tpe) ||
(isLocal && shadowed.contains(info.name)) ||
(isView && (info.sym == Predef_identity || info.sym == Predef_conforms)) //@M this condition prevents no-op conversions, which are a problem (besides efficiency),
// TODO: remove `info.sym == Predef_identity` once we have a new STARR that only has conforms as an implicit
// one example is removeNames in NamesDefaults, which relies on the type checker failing in case of ambiguity between an assignment/named arg
) SearchFailure
else typedImplicit(info)

View File

@ -26,7 +26,7 @@ trait Infer {
var normP = 0
var normO = 0
private final val inferInfo = false
private final val inferInfo = false //@MDEBUG
/* -- Type parameter inference utility functions --------------------------- */
@ -72,8 +72,7 @@ trait Infer {
* @param tparam ...
* @return ...
*/
def freshVar(tparam: Symbol): TypeVar =
new TypeVar(tparam.tpe, new TypeConstraint) //@M TODO: might be affected by change to tpe in Symbol
def freshVar(tparam: Symbol): TypeVar = TypeVar(tparam)
//todo: remove comments around following privates; right now they cause an IllegalAccess
// error when built with scalac
@ -442,7 +441,7 @@ trait Infer {
def isCompatible(tp: Type, pt: Type): Boolean = {
val tp1 = normalize(tp)
(tp1 <:< pt) || isCoercible(tp, pt)
(tp1 <:< pt) || isCoercible(tp1, pt) //@M: was isCoercible(tp, pt), but I assume this was a typo...
}
def isWeaklyCompatible(tp: Type, pt: Type): Boolean =
@ -630,6 +629,16 @@ trait Infer {
if (formals.length != argtpes.length) {
throw new NoInstance("parameter lists differ in length")
}
if (inferInfo) // @MDEBUG
println("methTypeArgs "+
" tparams = "+tparams+"\n"+
" formals = "+formals+"\n"+
" restpe = "+restpe+"\n"+
" restpe_inst = "+restpe.instantiateTypeParams(tparams, tvars)+"\n"+
" argtpes = "+argtpes+"\n"+
" pt = "+pt)
// check first whether type variables can be fully defined from
// expected result type.
if (!isConservativelyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
@ -646,7 +655,8 @@ trait Infer {
// Then define remaining type variables from argument types.
List.map2(argtpes, formals) {(argtpe, formal) =>
if (!isCompatible(argtpe.deconst.instantiateTypeParams(tparams, tvars),
//@M isCompatible has side-effect: isSubtype0 will register subtype checks in the tvar's bounds
if (!isCompatible(argtpe.deconst.instantiateTypeParams(tparams, tvars),
formal.instantiateTypeParams(tparams, tvars))) {
throw new DeferredNoInstance(() =>
"argument expression's type is not compatible with formal parameter type" +
@ -1035,7 +1045,7 @@ trait Infer {
// check that the type parameters <arg>hkargs</arg> to a higher-kinded type conform to the expected params <arg>hkparams</arg>
def checkKindBoundsHK(hkargs: List[Symbol], arg: Symbol, param: Symbol, paramowner: Symbol): (List[(Symbol, Symbol)], List[(Symbol, Symbol)], List[(Symbol, Symbol)]) = {
// NOTE: sometimes hkargs != arg.typeParams, the symbol and the type may have very different type parameters
// @M sometimes hkargs != arg.typeParams, the symbol and the type may have very different type parameters
val hkparams = param.typeParams
if(hkargs.length != hkparams.length) {
@ -1081,19 +1091,21 @@ trait Infer {
else "invariant";
def qualify(a0: Symbol, b0: Symbol): String = if (a0.toString != b0.toString) "" else {
assert(a0 ne b0)
assert(a0.owner ne b0.owner)
var a = a0; var b = b0
while (a.owner.name == b.owner.name) { a = a.owner; b = b.owner}
if (a.locationString ne "") " (" + a.locationString.trim + ")" else ""
if((a0 eq b0) || (a0.owner eq b0.owner)) ""
else {
var a = a0; var b = b0
while (a.owner.name == b.owner.name) { a = a.owner; b = b.owner}
if (a.locationString ne "") " (" + a.locationString.trim + ")" else ""
}
}
val errors = new ListBuffer[String]
(tparams zip targs).foreach{ case (tparam, targ) if (targ.isHigherKinded || !tparam.typeParams.isEmpty) => //println("check: "+(tparam, targ))
(tparams zip targs).foreach{ case (tparam, targ) if (targ.isHigherKinded || !tparam.typeParams.isEmpty) =>
// @M must use the typeParams of the type targ, not the typeParams of the symbol of targ!!
val tparamsHO = targ.typeParams
val (arityMismatches, varianceMismatches, stricterBounds) =
checkKindBoundsHK(targ.typeParams, targ.typeSymbolDirect, tparam, tparam.owner) // NOTE: *not* targ.typeSymbol, which normalizes
// NOTE 2: must use the typeParams of the type targ, not the typeParams of the symbol of targ!!
checkKindBoundsHK(tparamsHO, targ.typeSymbolDirect, tparam, tparam.owner) // NOTE: *not* targ.typeSymbol, which normalizes
if (!(arityMismatches.isEmpty && varianceMismatches.isEmpty && stricterBounds.isEmpty)){
errors += (targ+"'s type parameters do not match "+tparam+"'s expected parameters: "+
(for ((a, p) <- arityMismatches)
@ -1149,7 +1161,7 @@ trait Infer {
" pt = "+pt)
val targs = exprTypeArgs(tparams, tree.tpe, pt)
val uninstantiated = new ListBuffer[Symbol]
val detargs = if (keepNothings || (targs eq null)) targs
val detargs = if (keepNothings || (targs eq null)) targs //@M: adjustTypeArgs fails if targs==null, neg/t0226
else adjustTypeArgs(tparams, targs, WildcardType, uninstantiated)
val undetparams = uninstantiated.toList
val detparams = tparams filterNot (undetparams contains _)
@ -1300,12 +1312,7 @@ trait Infer {
}
def isInstantiatable(tvars: List[TypeVar]) = {
def cloneTypeVar(tv: TypeVar) = {
val tv1 = TypeVar(tv.origin, new TypeConstraint(tv.constr.lobounds, tv.constr.hibounds))
tv1.constr.inst = tv.constr.inst
tv1
}
val tvars1 = tvars map cloneTypeVar
val tvars1 = tvars map (_.cloneInternal)
// Note: right now it's not clear that solving is complete, or how it can be made complete!
// So we should come back to this and investigate.
solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (x => COVARIANT), false)

View File

@ -2253,7 +2253,7 @@ trait Typers { self: Analyzer =>
assert((mode & PATTERNmode) == 0); // this case cannot arise for patterns
val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt)
val strictTargs = List.map2(lenientTargs, tparams)((targ, tparam) =>
if (targ == WildcardType) tparam.tpe else targ)
if (targ == WildcardType) tparam.tpe else targ) //@M TODO: should probably be .tpeHK
def typedArgToPoly(arg: Tree, formal: Type): Tree = {
val lenientPt = formal.instantiateTypeParams(tparams, lenientTargs)
val arg1 = typedArg(arg, argMode(fun, mode), POLYmode, lenientPt)

View File

@ -87,7 +87,7 @@ object Predef extends LowPriorityImplicits {
// will soon stop being a view: subsumed by `conforms` (which is less likely to give rise to ambiguities)
// @see `conforms` for the implicit version
implicit def identity[A](x: A): A = x
def identity[A](x: A): A = x
def currentThread = java.lang.Thread.currentThread()
@ -308,8 +308,8 @@ object Predef extends LowPriorityImplicits {
// reusing `Function2` and `identity` leads to ambiguities (any2stringadd is inferred)
// to constrain any abstract type T that's in scope in a method's argument list (not just the method's own type parameters)
// simply add an implicit argument of type `T <:< U`, where U is the required upper bound (for lower-bounds, use: `U <: T`)
sealed abstract class <:<[-From, +To] //extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def convert(x: A) = x}
sealed abstract class <:<[-From, +To] extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x}
/** A type for which there is aways an implicit value.
* @see fallbackCanBuildFrom in Array.scala

View File

@ -3,10 +3,4 @@ viewtest.scala:43: error: type mismatch;
required: List[a(in method view3)]
case y1: List[a] => compareLists(x, y1)
^
viewtest.scala:104: error: ambiguous implicit values:
both method view4 in object O of type [a](x: a)a
and method identity in object Predef of type [A](x: A)A
match expected type (test.Str) => test.Ordered[test.Str]
t = t insert Str(s)
^
two errors found
one error found