summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2009-10-21 18:33:45 +0000
committerAdriaan Moors <adriaan.moors@epfl.ch>2009-10-21 18:33:45 +0000
commit247895b5e023bf6038988f7da485495012e66b9b (patch)
tree53a2658a5aaeabfd389cbf8ccccada67db0e78da /src
parent2ae67db5557f512e2c8e1e4f5fd8205b1e0f324c (diff)
downloadscala-247895b5e023bf6038988f7da485495012e66b9b.tar.gz
scala-247895b5e023bf6038988f7da485495012e66b9b.tar.bz2
scala-247895b5e023bf6038988f7da485495012e66b9b.zip
refactoring of TypeVar to set the stage for tcp...
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
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala12
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala313
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala13
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala49
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala2
-rw-r--r--src/library/scala/Predef.scala6
7 files changed, 255 insertions, 142 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index b61dc02c8c..7602696488 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -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.
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 9bd9c3ccfb..89cbb9c7b5 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -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()
+
+ override def typeConstructor = rawTypeRef(pre, sym, List())
- //@M equivalent to:
- // (!typeParams.isEmpty && args.isEmpty) && // because args.isEmpty is checked in typeParams
- // !isRawType(this) // needed for subtyping
+ // (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)
+ /** 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
- } else false
+ }
+ }
+
+ 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,17 +2539,21 @@ 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
+
+ var inst: Type = NoType // @M reduce visibility?
- def duplicate = {
- val tc = new TypeConstraint(lo, hi)
+ def instValid = (inst ne null) && (inst ne NoType)
+
+ def cloneInternal = {
+ val tc = new TypeConstraint(lobounds, hibounds)
tc.inst = inst
tc
}
@@ -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
}
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 2612e62f3e..6178cf60a3 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -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)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index a83ce8e53b..76e4818a72 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -103,13 +103,10 @@ self: Analyzer =>
override def equals(other: Any) = other match {
case that: ImplicitInfo =>
- if (this eq NoImplicitInfo) that eq this
- else
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)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 4a9399bf99..637e0e3659 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -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,6 +655,7 @@ trait Infer {
// Then define remaining type variables from argument types.
List.map2(argtpes, formals) {(argtpe, formal) =>
+ //@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(() =>
@@ -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))
- 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!!
+ (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(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)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index f6ceb7024a..7307a0d379 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -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)
diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala
index 708fe6db61..12cf8615d3 100644
--- a/src/library/scala/Predef.scala
+++ b/src/library/scala/Predef.scala
@@ -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