diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Symbols.scala | 12 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Types.scala | 313 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 13 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 49 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 | ||||
-rw-r--r-- | src/library/scala/Predef.scala | 6 | ||||
-rw-r--r-- | test/files/neg/viewtest.check | 8 |
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 |