summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--test/files/neg/viewtest.check8
8 files changed, 256 insertions, 149 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
diff --git a/test/files/neg/viewtest.check b/test/files/neg/viewtest.check
index a93f803fe2..21ed93a01c 100644
--- a/test/files/neg/viewtest.check
+++ b/test/files/neg/viewtest.check
@@ -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