summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2007-04-06 09:23:03 +0000
committerAdriaan Moors <adriaan.moors@epfl.ch>2007-04-06 09:23:03 +0000
commite1c732db445d1ab0172bc25073ce865acc7d280b (patch)
tree4e1445535167194e09815e95563e4d2a9ef927f3 /src/compiler/scala/tools/nsc/typechecker
parent289fd3d7307ca117a419e71e3a20b0b811a0d33f (diff)
downloadscala-e1c732db445d1ab0172bc25073ce865acc7d280b.tar.gz
scala-e1c732db445d1ab0172bc25073ce865acc7d280b.tar.bz2
scala-e1c732db445d1ab0172bc25073ce865acc7d280b.zip
merged in tcpoly branch (at r10641)
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala165
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala160
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala521
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala132
5 files changed, 616 insertions, 365 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index eece1dabd0..156700d590 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -381,7 +381,8 @@ trait Contexts requires Analyzer {
if (settings.debug.value) log("resetting " + sym + " to " + info);
sym.info match {
case TypeBounds(lo, hi) if (hi <:< lo && lo <:< hi) =>
- current = current.subst(List(sym), List(lo))
+ current = current.instantiateTypeParams(List(sym), List(lo))
+//@M TODO: when higher-kinded types are inferred, probably need a case PolyType(_, TypeBounds(...)) if ... =>
case _ =>
}
sym.setInfo(info)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 64e49e4b35..18b757d981 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -64,7 +64,7 @@ trait Infer requires Analyzer {
* @return ...
*/
def freshVar(tparam: Symbol): TypeVar =
- new TypeVar(tparam.tpe, new TypeConstraint)
+ new TypeVar(tparam.tpe, new TypeConstraint) //@M TODO: might be affected by change to tpe in Symbol
//todo: remove comments around following privates; right now they cause an IllegalAccess
// error when built with scalac
@@ -117,7 +117,7 @@ trait Infer requires Analyzer {
*/
private def isWithinBounds(pre: Type, owner: Symbol, tparams: List[Symbol], targs: List[Type]): boolean = {
val bounds = tparams map { tparam =>
- tparam.info.asSeenFrom(pre, owner).subst(tparams, targs).bounds
+ tparam.info.asSeenFrom(pre, owner).instantiateTypeParams(tparams, targs).bounds
}
!(List.map2(bounds, targs)((bound, targ) => bound containsType targ) contains false)
}
@@ -145,8 +145,8 @@ trait Infer requires Analyzer {
for (val (tvar2, (tparam2, variance2)) <- config) {
if (tparam2 != tparam &&
((bound contains tparam2) ||
- up && (tparam2.info.bounds.lo =:= tparam.tpe) ||
- !up && (tparam2.info.bounds.hi =:= tparam.tpe))) {
+ 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
if (tvar2.constr.inst eq null) cyclic = true
solveOne(tvar2, tparam2, variance2)
}
@@ -155,21 +155,21 @@ trait Infer requires Analyzer {
if (up) {
if (bound.symbol != AnyClass) {
tvar.constr.hibounds =
- bound.subst(tparams, tvars) :: tvar.constr.hibounds
+ bound.instantiateTypeParams(tparams, tvars) :: tvar.constr.hibounds
}
for (val tparam2 <- tparams)
- if (tparam2.info.bounds.lo =:= tparam.tpe)
+ if (tparam2.info.bounds.lo =:= tparam.tpe) //@M TODO: might be affected by change to tpe in Symbol
tvar.constr.hibounds =
- tparam2.tpe.subst(tparams, tvars) :: tvar.constr.hibounds
+ tparam2.tpe.instantiateTypeParams(tparams, tvars) :: tvar.constr.hibounds
} else {
if (bound.symbol != AllClass && bound.symbol != tparam) {
tvar.constr.lobounds =
- bound.subst(tparams, tvars) :: tvar.constr.lobounds
+ bound.instantiateTypeParams(tparams, tvars) :: tvar.constr.lobounds
}
for (val tparam2 <- tparams)
- if (tparam2.info.bounds.hi =:= tparam.tpe)
+ if (tparam2.info.bounds.hi =:= tparam.tpe) //@M TODO: might be affected by change to tpe in Symbol
tvar.constr.lobounds =
- tparam2.tpe.subst(tparams, tvars) :: tvar.constr.lobounds
+ tparam2.tpe.instantiateTypeParams(tparams, tvars) :: tvar.constr.lobounds
}
}
tvar.constr.inst = NoType // necessary because hibounds/lobounds may contain tvar
@@ -203,7 +203,7 @@ trait Infer requires Analyzer {
normalize(restpe)
case tp1 =>
if (util.Statistics.enabled) normO = normO + 1
- tp1
+ tp1 // @MAT aliases already handled by subtyping
}
private val stdErrorClass = RootClass.newErrorClass(nme.ERROR.toTypeName)
@@ -395,7 +395,7 @@ trait Infer requires Analyzer {
*/
private def exprTypeArgs(tparams: List[Symbol], restpe: Type, pt: Type): List[Type] = {
val tvars = tparams map freshVar
- if (isCompatible(restpe.subst(tparams, tvars), pt)) {
+ if (isCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
try {
solve(tvars, tparams, tparams map varianceInType(restpe), false)
} catch {
@@ -447,7 +447,7 @@ trait Infer requires Analyzer {
case ex: NoInstance => WildcardType
}
val tvars = tparams map freshVar
- if (isWeaklyCompatible(restpe.subst(tparams, tvars), pt))
+ if (isWeaklyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt))
List.map2(tparams, tvars) ((tparam, tvar) =>
instantiateToBound(tvar, varianceInTypes(formals)(tparam)))
else
@@ -483,7 +483,7 @@ trait Infer requires Analyzer {
}
// check first whether type variables can be fully defined from
// expected result type.
- if (!isWeaklyCompatible(restpe.subst(tparams, tvars), pt)) {
+ if (!isWeaklyCompatible(restpe.instantiateTypeParams(tparams, tvars), pt)) {
throw new DeferredNoInstance(() =>
"result type " + normalize(restpe) + " is incompatible with expected type " + pt)
}
@@ -492,13 +492,13 @@ trait Infer requires Analyzer {
// Then define remaining type variables from argument types.
List.map2(argtpes, formals) {(argtpe, formal) =>
- if (!isCompatible(argtpe.deconst.subst(tparams, tvars),
- formal.subst(tparams, tvars))) {
+ if (!isCompatible(argtpe.deconst.instantiateTypeParams(tparams, tvars),
+ formal.instantiateTypeParams(tparams, tvars))) {
if (settings.explaintypes.value)
- explainTypes(argtpe.deconst.subst(tparams, tvars), formal.subst(tparams, tvars))
+ explainTypes(argtpe.deconst.instantiateTypeParams(tparams, tvars), formal.instantiateTypeParams(tparams, tvars))
throw new DeferredNoInstance(() =>
"argument expression's type is not compatible with formal parameter type" +
- foundReqMsg(argtpe.deconst.subst(tparams, tvars), formal.subst(tparams, tvars)))
+ foundReqMsg(argtpe.deconst.instantiateTypeParams(tparams, tvars), formal.instantiateTypeParams(tparams, tvars)))
}
()
}
@@ -506,7 +506,7 @@ trait Infer requires Analyzer {
List.map2(tparams, targs) {(tparam, targ) =>
if (targ.symbol == AllClass && (varianceInType(restpe)(tparam) & COVARIANT) == 0) {
uninstantiated += tparam
- tparam.tpe
+ tparam.tpe //@M TODO: might be affected by change to tpe in Symbol
} else targ}
}
@@ -535,7 +535,7 @@ trait Infer requires Analyzer {
try {
val uninstantiated = new ListBuffer[Symbol]
val targs = methTypeArgs(undetparams, formals, restpe, argtpes, pt, uninstantiated)
- (exprTypeArgs(uninstantiated.toList, restpe.subst(undetparams, targs), pt) ne null) &&
+ (exprTypeArgs(uninstantiated.toList, restpe.instantiateTypeParams(undetparams, targs), pt) ne null) &&
isWithinBounds(NoPrefix, NoSymbol, undetparams, targs)
} catch {
case ex: NoInstance => false
@@ -595,8 +595,18 @@ trait Infer requires Analyzer {
/** error if arguments not within bounds. */
def checkBounds(pos: PositionType, pre: Type, owner: Symbol,
- tparams: List[Symbol], targs: List[Type], prefix: String) {
- if (!isWithinBounds(pre, owner, tparams, targs)) {
+ tparams: List[Symbol], targs: List[Type], prefix: String) = {
+ //@M validate variances & bounds of targs wrt variances & bounds of tparams
+ //@M TODO: better place to check this?
+ //@M TODO: errors for getters & setters are reported separately
+ val kindErrors = checkKindBounds(tparams, targs)
+
+ if(!kindErrors.isEmpty)
+ error(pos,
+ prefix + "the kinds of the type arguments " + targs.mkString("(", ",", ")") +
+ " do not conform to the expected kinds of the type parameters "+ tparams.mkString("(", ",", ")") + tparams.head.locationString+ "." +
+ kindErrors.toList.mkString("\n", ", ", ""))
+ else if (!isWithinBounds(pre, owner, tparams, targs)) {
if (!(targs exists (.isErroneous)) && !(tparams exists (.isErroneous))) {
error(pos,
prefix + "type arguments " + targs.mkString("[", ",", "]") +
@@ -604,7 +614,7 @@ trait Infer requires Analyzer {
(tparams map (.defString)).mkString("[", ",", "]"))
}
if (settings.explaintypes.value) {
- val bounds = tparams map (.info.subst(tparams, targs).bounds)
+ val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, targs).bounds)
List.map2(targs, bounds)((targ, bound) => explainTypes(bound.lo, targ))
List.map2(targs, bounds)((targ, bound) => explainTypes(targ, bound.hi))
()
@@ -612,6 +622,87 @@ trait Infer requires Analyzer {
}
}
+ /** Check whether <arg>sym1</arg>'s variance conforms to <arg>sym2</arg>'s variance
+ *
+ * If <arg>sym2</arg> is invariant, <arg>sym1</arg>'s variance is irrelevant. Otherwise they must be equal.
+ */
+ def variancesMatch(sym1: Symbol, sym2: Symbol): boolean = (sym2.variance==0 || sym1.variance==sym2.variance)
+
+ /** Check well-kindedness of type application (assumes arities are already checked) -- @M
+ *
+ * This check is also performed when abstract type members become concrete (aka a "type alias") -- then tparams.length==1
+ * (checked one type member at a time -- in that case, prefix is the name of the type alias)
+ *
+ * Type application is just like value application: it's "contravariant" in the sense that
+ * the type parameters of the supplied type arguments must conform to the type parameters of
+ * the required type parameters:
+ * - their bounds must be less strict
+ * - variances must match (here, variances are absolute, the variance of a type parameter does not influence the variance of its higher-order parameters)
+ * - @M TODO: are these conditions correct,sufficient&necessary?
+ *
+ * e.g. class Iterable[t, m[+x <: t]] --> the application Iterable[Int, List] is okay, since
+ * List's type parameter is also covariant and its bounds are weaker than <: Int
+ */
+ def checkKindBounds(tparams: List[Symbol], targs: List[Type]): List[String] = {
+ // 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], hkparams: List[Symbol]): (List[(Symbol, Symbol)], List[(Symbol, Symbol)]) = {
+ val _varianceMismatches = new ListBuffer[(Symbol, Symbol)]
+ val _stricterBounds = new ListBuffer[(Symbol, Symbol)]
+ def varianceMismatch(a: Symbol, p: Symbol): unit = _varianceMismatches += (a, p)
+ def stricterBound(a: Symbol, p: Symbol): unit = _stricterBounds += (a, p)
+ def varianceMismatches(as: Iterable[(Symbol, Symbol)]): unit = _varianceMismatches ++= as
+ def stricterBounds(as: Iterable[(Symbol, Symbol)]): unit = _stricterBounds ++= as
+
+ for(val (hkarg, hkparam) <- hkargs zip hkparams) {
+ if(hkparam.typeParams.isEmpty) { // base-case: kind *
+ if(!variancesMatch(hkarg, hkparam))
+ varianceMismatch(hkarg, hkparam)
+
+ // instantiateTypeParams(tparams, targs) --> higher-order bounds may contain references to type arguments
+ // substSym(hkparams, hkargs) --> these types are going to be compared as types of kind *
+ // --> their arguments use different symbols, but are conceptually the same
+ // (could also replace the types by polytypes, but can't just strip the symbols, as ordering is lost then)
+ if(!(hkparam.info.instantiateTypeParams(tparams, targs).bounds.substSym(hkparams, hkargs) <:< hkarg.info.bounds))
+ stricterBound(hkarg, hkparam)
+ } else {
+ val (vm, sb) = checkKindBoundsHK(hkarg.typeParams, hkparam.typeParams)
+ varianceMismatches(vm)
+ stricterBounds(sb)
+ }
+ }
+
+ (_varianceMismatches.toList, _stricterBounds.toList)
+ }
+
+ // @M TODO this method is duplicated all over the place (varianceString)
+ def varStr(s: Symbol): String =
+ if (s.isCovariant) "covariant"
+ else if (s.isContravariant) "contravariant"
+ else "invariant";
+
+ def qualify(a0: Symbol, b0: Symbol): String = if(a0.toString != b0.toString) "" else {
+ assert((a0 ne b0) && (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 ""
+ }
+
+ val errors = new ListBuffer[String]
+ (tparams zip targs).foreach{ case (tparam, targ) if(targ.isHigherKinded) =>
+ val (varianceMismatches, stricterBounds) = checkKindBoundsHK(targ.typeParams, tparam.typeParams)
+
+ if(!(varianceMismatches.isEmpty && stricterBounds.isEmpty)){
+ errors += (targ+"'s type parameters do not match "+tparam+"'s expected parameters: "+
+ (for(val (a,p) <- varianceMismatches) yield a+qualify(a,p)+ " is "+varStr(a)+", but "+
+ p+qualify(p,a)+" is declared "+varStr(p)).toList.mkString("", ", ", "") +
+ (for(val (a,p) <- stricterBounds) yield a+qualify(a,p)+"'s bounds "+a.info+" are stricter than "+
+ p+qualify(p,a)+"'s declared bounds "+p.info).toList.mkString("", ", ", ""))
+ }
+ case _ =>
+ }
+
+ errors.toList
+ }
+
/** Substitite free type variables `undetparams' of polymorphic argument
* expression `tree', given two prototypes `strictPt', and `lenientPt'.
* `strictPt' is the first attempt prototype where type parameters
@@ -717,7 +808,7 @@ trait Infer requires Analyzer {
}
/** Type with all top-level occurrences of abstract types replaced by their bounds */
- def widen(tp: Type): Type = tp match {
+ def widen(tp: Type): Type = tp.normalize match {
case TypeRef(pre, sym, _) if sym.isAbstractType =>
widen(tp.bounds.hi)
case rtp @ RefinedType(parents, decls) =>
@@ -752,28 +843,28 @@ trait Infer requires Analyzer {
}
def instError = {
if (settings.debug.value) Console.println("ici " + tree + " " + undetparams + " " + pt)
- if (settings.explaintypes.value) explainTypes(restpe.subst(undetparams, tvars), pt)
+ if (settings.explaintypes.value) explainTypes(restpe.instantiateTypeParams(undetparams, tvars), pt)
errorTree(tree, "constructor cannot be instantiated to expected type" +
foundReqMsg(restpe, pt))
}
- if (restpe.subst(undetparams, tvars) <:< pt) {
+ if (restpe.instantiateTypeParams(undetparams, tvars) <:< pt) {
computeArgs
} else if (isFullyDefined(pt)) {
if (settings.debug.value) log("infer constr " + tree + ":" + restpe + ", pt = " + pt)
var ptparams = freeTypeParamsOfTerms.collect(pt)
if (settings.debug.value) log("free type params = " + ptparams)
- val ptWithWildcards = pt.subst(ptparams, ptparams map (ptparam => WildcardType))
+ val ptWithWildcards = pt.instantiateTypeParams(ptparams, ptparams map (ptparam => WildcardType))
tvars = undetparams map freshVar
- if (restpe.subst(undetparams, tvars) <:< ptWithWildcards) {
+ if (restpe.instantiateTypeParams(undetparams, tvars) <:< ptWithWildcards) {
computeArgs
restpe = skipImplicit(tree.tpe.resultType)
if (settings.debug.value) log("new tree = " + tree + ":" + restpe)
val ptvars = ptparams map freshVar
- val pt1 = pt.subst(ptparams, ptvars)
+ val pt1 = pt.instantiateTypeParams(ptparams, ptvars)
if (isPopulated(restpe, pt1)) {
ptvars foreach instantiateTypeVar
} else { if (settings.debug.value) Console.println("no instance: "); instError }
- } else { if (settings.debug.value) Console.println("not a subtype " + restpe.subst(undetparams, tvars) + " of " + ptWithWildcards); instError }
+ } else { if (settings.debug.value) Console.println("not a subtype " + restpe.instantiateTypeParams(undetparams, tvars) + " of " + ptWithWildcards); instError }
} else { if (settings.debug.value) Console.println("not fuly defined: " + pt); instError }
}
@@ -869,14 +960,14 @@ trait Infer requires Analyzer {
val tpparams = freeTypeParamsOfTerms.collect(pattp)
if (settings.debug.value) log("free type params (1) = " + tpparams)
var tvars = tpparams map freshVar
- var tp = pattp.subst(tpparams, tvars)
+ var tp = pattp.instantiateTypeParams(tpparams, tvars)
if (!(tp <:< pt)) {
tvars = tpparams map freshVar
- tp = pattp.subst(tpparams, tvars)
+ tp = pattp.instantiateTypeParams(tpparams, tvars)
val ptparams = freeTypeParamsOfTerms.collect(pt)
if (settings.debug.value) log("free type params (2) = " + ptparams)
val ptvars = ptparams map freshVar
- val pt1 = pt.subst(ptparams, ptvars)
+ val pt1 = pt.instantiateTypeParams(ptparams, ptvars)
if (!isPopulated(tp, pt1)) {
error(pos, "pattern type is incompatibe with expected type"+foundReqMsg(pattp, pt))
return pattp
@@ -893,7 +984,7 @@ trait Infer requires Analyzer {
val ptparams = freeTypeParamsOfTerms.collect(pt)
if (settings.debug.value) log("free type params (2) = " + ptparams)
val ptvars = ptparams map freshVar
- val pt1 = pt.subst(ptparams, ptvars)
+ val pt1 = pt.instantiateTypeParams(ptparams, ptvars)
if (pat.tpe <:< pt1)
ptvars foreach instantiateTypeVar
else
@@ -912,7 +1003,7 @@ trait Infer requires Analyzer {
protected def includeCondition(sym: Symbol): boolean
override def traverse(tp: Type): TypeTraverser = {
- tp match {
+ tp.normalize match {
case TypeRef(_, sym, _) =>
if (includeCondition(sym) && !result.contains(sym)) result = sym :: result
case _ =>
@@ -934,7 +1025,7 @@ trait Infer requires Analyzer {
}
object approximateAbstracts extends TypeMap {
- def apply(tp: Type): Type = tp match {
+ def apply(tp: Type): Type = tp.normalize match {
case TypeRef(pre, sym, _) if sym.isAbstractType => WildcardType
case _ => mapOver(tp)
}
@@ -1110,7 +1201,7 @@ trait Infer requires Analyzer {
if (sym.hasFlag(OVERLOADED)) {
val tparams = new AsSeenFromMap(pre, sym.alternatives.head.owner).mapOver(
sym.alternatives.head.typeParams)
- val bounds = tparams map (.tpe)
+ val bounds = tparams map (.tpe) //@M TODO: might be affected by change to tpe in Symbol
val tpe =
PolyType(tparams,
OverloadedType(AntiPolyType(pre, bounds), sym.alternatives))
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index c41a30de7e..be5d2600d9 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -46,6 +46,15 @@ trait Namers requires Analyzer {
protected final def doEnterValueParams = !inIDE;
protected def inIDE = false;
+ //@M a worklist of AbsTypeDef trees we must process when the next finishWith is called
+ // example: trait FOO3[m[A <: B], B]
+ // if we go depth-first, B will not be in scope yet when we create the context in which A <: B will be typed
+ // instead, an AbsTypeDef registers its type parameters in lateParams, which will be processed when all
+ // type parameters in the enclosing scope have been entered (i.e., when m as well as B have been entered by
+ // the finishWith in the case for ClassDef in enterSym, that finishWith will then handle m's type param, A,
+ // which has been registered in lateParams by the finishWithLate in the case for AbsTypeDef)
+ private val lateParams = new scala.collection.mutable.ListBuffer[(Namer, Symbol, Tree, List[AbsTypeDef])]
+
class Namer(val context: Context) {
val typer = newTyper(context)
@@ -113,14 +122,14 @@ trait Namers requires Analyzer {
def enterInScope(sym: Symbol): Symbol = {
// allow for overloaded methods
if (!(sym.isSourceMethod && sym.owner.isClass && !sym.owner.isPackageClass)) {
- val prev = context.scope.lookupEntry(sym.name);
- if ((prev ne null) && prev.owner == context.scope &&
+ val prev = context.scope.lookupEntry(sym.name);
+ if ((prev ne null) && prev.owner == context.scope &&
(!prev.sym.isSourceMethod ||
nme.isSetterName(sym.name) ||
sym.owner.isPackageClass)) {
- doubleDefError(sym.pos, prev.sym)
+ doubleDefError(sym.pos, prev.sym)
sym setInfo ErrorType
- } else context.scope enter sym
+ } else context.scope enter sym
} else context.scope enter sym
sym
}
@@ -152,9 +161,9 @@ trait Namers requires Analyzer {
c = enterInScope(context.owner.newClass(pos, name)).setFlag(flags | inConstructorFlag)
}
if (c.owner.isPackageClass) {
- val file = context.unit.source.getFile()
- val clazz = c.asInstanceOf[ClassSymbol]
- if (settings.debug.value && (clazz.sourceFile ne null) && !clazz.sourceFile.equals(file)) {
+ val file = context.unit.source.getFile()
+ val clazz = c.asInstanceOf[ClassSymbol]
+ if (settings.debug.value && (clazz.sourceFile ne null) && !clazz.sourceFile.equals(file)) {
Console.err.println("SOURCE MISMATCH: " + clazz.sourceFile + " vs. " + file + " SYM=" + c);
}
clazz.sourceFile = file
@@ -211,12 +220,14 @@ trait Namers requires Analyzer {
val tskolems = tparams map (.newTypeSkolem)
val ltp = new LazyType {
override def complete(sym: Symbol): unit =
- sym setInfo sym.deSkolemize.info.substSym(tparams, tskolems)
+ sym setInfo sym.deSkolemize.info.substSym(tparams, tskolems) //@M the info of a skolem is the skolemized info of the actual type parameter of the skolem
}
tskolems foreach (.setInfo(ltp))
tskolems
}
+ //@M? Replace type parameters with their TypeSkolems, which can later be deskolemized to the original type param
+ // (a skolem is a representation of a bound variable when viewed outside its scope?)
def skolemize(tparams: List[AbsTypeDef]): unit = {
val tskolems = newTypeSkolems(tparams map (.symbol))
for (val (tparam, tskolem) <- tparams zip tskolems) tparam.symbol = tskolem
@@ -228,40 +239,63 @@ trait Namers requires Analyzer {
def deSkolemize: TypeMap = new DeSkolemizeMap(applicableTypeParams(context.owner))
- def enterSym(tree: Tree): Context = {
- def finishWith(tparams: List[AbsTypeDef]): unit = {
- val sym = tree.symbol
- if (settings.debug.value) log("entered " + sym + " in " + context.owner + ", scope-id = " + context.scope.hashCode());
- var ltype: LazyType = namerOf(sym).typeCompleter(tree)
- if (!tparams.isEmpty) {
- new Namer(context.makeNewScope(tree, sym)).enterSyms(tparams)
- ltype = new LazyPolyType(tparams map (.symbol), ltype)
- if (sym.isTerm) skolemize(tparams)
+ private def doLateParams = if(!lateParams.isEmpty) {
+ val todo = lateParams.toList
+ lateParams.clear
+ for(val rec <- todo) {
+ rec._1.finishWith0(rec._2, rec._3, rec._4)
}
- sym.setInfo(ltype)
}
- def finish = finishWith(List())
+ private def finishWith0(sym: Symbol, tree: Tree, tparams: List[AbsTypeDef]): unit = {
+ if (settings.debug.value) log("entered " + sym + " in " + context.owner + ", scope-id = " + context.scope.hashCode());
+
+ var ltype: LazyType = namerOf(sym).typeCompleter(tree)
+
+ if (!tparams.isEmpty) {
+ new Namer(context.makeNewScope(tree, sym)).enterSyms(tparams)
+
+ ltype = new LazyPolyType(tparams map (.symbol), ltype)
+
+ if (sym.isTerm) // skolemize the type parameters of a method
+ skolemize(tparams)
+ }
+
+ sym.setInfo(ltype)
+
+ doLateParams
+ }
+
+ def enterSym(tree: Tree): Context = {
+ def finishWith(tparams: List[AbsTypeDef]): unit = finishWith0(tree.symbol, tree, tparams)
+ def finish = finishWith(List())
+ def finishWithLate(tparams: List[AbsTypeDef]): unit = {
+ if(tparams.isEmpty) finishWith(tparams)
+ else {
+ lateParams += (this, tree.symbol, tree, tparams) // this and tree.symbol must be remembered too, these will change by the time the worklist is performed (new nested namer, skolemization for tree's symbol)
+ }
+ }
if (tree.symbol == NoSymbol) {
- val owner = context.owner
- tree match {
- case PackageDef(name, stats) =>
- tree.symbol = enterPackageSymbol(tree.pos, name)
- val namer = new Namer(
- context.make(tree, tree.symbol.moduleClass, tree.symbol.info.decls))
- namer.enterSyms(stats)
- case ClassDef(mods, name, tparams, _, impl) =>
- if ((mods.flags & CASE) != 0) { // enter case factory method.
- tree.symbol = enterCaseFactorySymbol(
- tree.pos, mods.flags & AccessFlags | METHOD | CASE, name.toTermName)
+ val owner = context.owner
+ tree match {
+ case PackageDef(name, stats) =>
+ tree.symbol = enterPackageSymbol(tree.pos, name)
+ val namer = new Namer(
+ context.make(tree, tree.symbol.moduleClass, tree.symbol.info.decls))
+ namer.enterSyms(stats)
+ case ClassDef(mods, name, tparams, _, impl) =>
+ if ((mods.flags & CASE) != 0) { // enter case factory method.
+ tree.symbol = enterCaseFactorySymbol(
+ tree.pos, mods.flags & AccessFlags | METHOD | CASE, name.toTermName)
tree.symbol.setInfo(namerOf(tree.symbol).caseFactoryCompleter(tree))
setPrivateWithin(tree, tree.symbol, mods)
}
tree.symbol = enterClassSymbol(tree.pos, mods.flags, name)
setPrivateWithin(tree, tree.symbol, mods)
finishWith(tparams)
+ assert(lateParams.isEmpty)
case ModuleDef(mods, name, _) =>
tree.symbol = enterModuleSymbol(tree.pos, mods.flags | MODULE | FINAL, name)
setPrivateWithin(tree, tree.symbol, mods)
@@ -278,16 +312,16 @@ trait Namers requires Analyzer {
enterInScope(getter)
if ((mods.flags & MUTABLE) != 0) {
val setter = owner.newMethod(tree.pos, nme.getterToSetter(name))
- .setFlag(accflags & ~STABLE & ~CASEACCESSOR)
+ .setFlag(accflags & ~STABLE & ~CASEACCESSOR)
setter.setInfo(namerOf(setter).setterTypeCompleter(tree))
setPrivateWithin(tree, setter, mods)
enterInScope(setter)
}
tree.symbol =
- if ((mods.flags & DEFERRED) == 0) {
+ if ((mods.flags & DEFERRED) == 0) {
val value =
- enterInScope(owner.newValue(tree.pos, nme.getterToLocal(name)))
- .setFlag(mods.flags & FieldFlags | PRIVATE | LOCAL)
+ enterInScope(owner.newValue(tree.pos, nme.getterToLocal(name)))
+ .setFlag(mods.flags & FieldFlags | PRIVATE | LOCAL)
value.setInfo(namerOf(value).typeCompleter(tree))
value
} else getter;
@@ -301,16 +335,18 @@ trait Namers requires Analyzer {
.setFlag(mods.flags | owner.getFlag(ConstrFlags))
setPrivateWithin(tree, tree.symbol, mods)
finishWith(tparams)
+ assert(lateParams.isEmpty)
case DefDef(mods, name, tparams, _, _, _) =>
tree.symbol = enterInScope(owner.newMethod(tree.pos, name))
.setFlag(mods.flags)
setPrivateWithin(tree, tree.symbol, mods)
finishWith(tparams)
- case AbsTypeDef(mods, name, _, _) =>
+ assert(lateParams.isEmpty)
+ case AbsTypeDef(mods, name, tparams, _, _) =>
tree.symbol = enterInScope(owner.newAbstractType(tree.pos, name))
.setFlag(mods.flags)
setPrivateWithin(tree, tree.symbol, mods)
- finish
+ finishWithLate(tparams) // @M! enter this AbsTypeDef's type params in scope after all the siblings of this abstypedef have been entered (e.g., in [A[x <: B], B], A and B are entered first, then x is entered)
case AliasTypeDef(mods, name, tparams, _) =>
tree.symbol = enterInScope(owner.newAliasType(tree.pos, name))
.setFlag(mods.flags)
@@ -331,7 +367,7 @@ trait Namers requires Analyzer {
// --- Lazy Type Assignment --------------------------------------------------
def typeCompleter(tree: Tree) = new TypeCompleter(tree) {
- override def complete(sym: Symbol): unit = {
+ override def complete(sym: Symbol): unit = { //@M? toString (on sym/tree/...? don't know exactly) will force a lazy type and execute this method (quantum debugging!)
if (settings.debug.value) log("defining " + sym);
val tp = typeSig(tree)
sym.setInfo(tp)
@@ -428,7 +464,10 @@ trait Namers requires Analyzer {
}
val parents = typer.parentTypes(templ) map checkParent
val decls = newDecls(templ, clazz)
- new Namer(context.make(templ, clazz, decls)).enterSyms(templ.body)
+ val namer = new Namer(context.make(templ, clazz, decls))
+ namer.enterSyms(templ.body)
+ namer.doLateParams // @M set info on higher-kinded type members -- @M TODO cleanup & document
+
ClassInfoType(parents, decls, clazz)
}
@@ -564,8 +603,8 @@ trait Namers requires Analyzer {
for (val r <- required(tp)) {
if (!isContainedIn(r, p) || (r =:= p)) {
context.error(sym.pos, "implicit " + sym + " is not contractive," +
- "\n because the implicit parameter type " + r +
- "\n is not strictly contained in the signature " + p);
+ "\n because the implicit parameter type " + r +
+ "\n is not strictly contained in the signature " + p);
result = ErrorType;
}
}
@@ -573,6 +612,19 @@ trait Namers requires Analyzer {
result
}
+ //@M! an abstract type definition (abstract type member/type parameter) may take type parameters, which are in scope in its bounds
+ private def abstractTypeSig(tree: Tree, tpsym: Symbol, tparams: List[AbsTypeDef], lo: Tree, hi: Tree) = {
+ val tparamSyms = typer.reenterTypeParams(tparams) //@M make tparams available in scope (just for this abstypedef)
+
+ var lt = typer.typedType(lo).tpe
+ if (lt.isError) lt = AllClass.tpe
+
+ var ht = typer.typedType(hi).tpe
+ if (ht.isError) ht = AnyClass.tpe
+
+ makePolyType(tparamSyms, mkTypeBounds(lt, ht)) //@M
+ }
+
private def aliasTypeSig(tpsym: Symbol, tparams: List[AbsTypeDef], rhs: Tree): Type =
makePolyType(typer.reenterTypeParams(tparams), typer.typedType(rhs).tpe);
@@ -611,8 +663,8 @@ trait Namers requires Analyzer {
case vdef @ ValDef(mods, _, tpt, rhs) =>
if (tpt.isEmpty) {
if (rhs.isEmpty) {
- context.error(tpt.pos, "missing parameter type");
- ErrorType
+ context.error(tpt.pos, "missing parameter type");
+ ErrorType
} else {
tpt.tpe = deconstIfNotFinal(sym,
typer.valDefRhsTyper(vdef).computeType(rhs, WildcardType))
@@ -629,12 +681,8 @@ trait Namers requires Analyzer {
case tree @ AliasTypeDef(_, _, tparams, rhs) =>
new Namer(makeNewScope(context, tree, sym)).aliasTypeSig(sym, tparams, rhs)
- case AbsTypeDef(_, _, lo, hi) =>
- var lt = typer.typedType(lo).tpe
- if (lt.isError) lt = AllClass.tpe
- var ht = typer.typedType(hi).tpe
- if (ht.isError) ht = AnyClass.tpe
- mkTypeBounds(lt, ht)
+ case AbsTypeDef(_, _, tparams, lo, hi) =>
+ new Namer(makeNewScope(context, tree, sym)).abstractTypeSig(tree, sym, tparams, lo, hi) //@M!
case Import(expr, selectors) =>
val expr1 = typer.typedQualifier(expr)
@@ -661,17 +709,17 @@ trait Namers requires Analyzer {
}
def checkSelectors(selectors: List[(Name, Name)]): unit = selectors match {
case (from, to) :: rest =>
- if (from != nme.WILDCARD && base != ErrorType) {
- if (base.member(from) == NoSymbol && base.member(from.toTypeName) == NoSymbol)
- context.error(tree.pos, from.decode + " is not a member of " + expr);
+ if (from != nme.WILDCARD && base != ErrorType) {
+ if (base.member(from) == NoSymbol && base.member(from.toTypeName) == NoSymbol)
+ context.error(tree.pos, from.decode + " is not a member of " + expr);
if (checkNotRedundant(tree.pos, from, to))
checkNotRedundant(tree.pos, from.toTypeName, to.toTypeName)
}
- if (from != nme.WILDCARD && (rest.exists (sel => sel._1 == from)))
- context.error(tree.pos, from.decode + " is renamed twice");
- if ((to ne null) && to != nme.WILDCARD && (rest exists (sel => sel._2 == to)))
- context.error(tree.pos, to.decode + " appears twice as a target of a renaming");
- checkSelectors(rest)
+ if (from != nme.WILDCARD && (rest.exists (sel => sel._1 == from)))
+ context.error(tree.pos, from.decode + " is renamed twice");
+ if ((to ne null) && to != nme.WILDCARD && (rest exists (sel => sel._2 == to)))
+ context.error(tree.pos, to.decode + " appears twice as a target of a renaming");
+ checkSelectors(rest)
case Nil =>
}
checkSelectors(selectors)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 00e015956c..39ff15d671 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -72,11 +72,13 @@ abstract class RefChecks extends InfoTransform {
* 1.2. O must not be final.
* 1.3. O is deferred, or M has `override' modifier.
* 1.4. If O is an immutable value, then so is M.
- * 1.5. Neither M nor O are a parameterized type alias
+ * // @M: LIFTED 1.5. Neither M nor O are a parameterized type alias
* 1.6. If O is a type alias, then M is an alias of O.
* 1.7. If O is an abstract type then
- * either M is an abstract type, and M's bounds are sharper than O's bounds.
- * or M is an unparameterized type alias or class which conforms to O's bounds.
+ * 1.7.1 either M is an abstract type, and M's bounds are sharper than O's bounds.
+ * or M is a type alias or class which conforms to O's bounds.
+ * 1.7.2 higher-order type arguments must respect bounds on higher-order type parameters -- @M
+ * (explicit bounds and those implied by variance annotations) -- @see checkKindBounds
* 1.8. If O and M are values, then
* 1.8.1 M's type is a subtype of O's type, or
* 1.8.2 M is of type []S, O is of type ()T and S <: T, or
@@ -100,7 +102,7 @@ abstract class RefChecks extends InfoTransform {
else "")))
}
- def overridesType(tp1: Type, tp2: Type): boolean = (tp1, tp2) match {
+ def overridesType(tp1: Type, tp2: Type): boolean = (tp1.normalize, tp2.normalize) match {
case (MethodType(List(), rtp1), PolyType(List(), rtp2)) =>
rtp1 <:< rtp2
case (PolyType(List(), rtp1), MethodType(List(), rtp2)) =>
@@ -115,52 +117,52 @@ abstract class RefChecks extends InfoTransform {
* <code>member</code> are met.
*/
def checkOverride(clazz: Symbol, member: Symbol, other: Symbol): unit = {
- val pos = if (member.owner == clazz) member.pos else clazz.pos
+ val pos = if (member.owner == clazz) member.pos else clazz.pos
- def overrideError(msg: String): unit =
- if (other.tpe != ErrorType && member.tpe != ErrorType)
- unit.error(pos, "error overriding " + infoString(other) +
- ";\n " + infoString(member) + " " + msg);
+ def overrideError(msg: String): unit =
+ if (other.tpe != ErrorType && member.tpe != ErrorType)
+ unit.error(pos, "error overriding " + infoString(other) +
+ ";\n " + infoString(member) + " " + msg);
- def overrideTypeError(): unit = {
- if (other.tpe != ErrorType && member.tpe != ErrorType) {
- overrideError("has incompatible type "+analyzer.underlying(member).tpe);
- explainTypes(member.tpe, other.tpe);
- }
- }
+ def overrideTypeError(): unit = {
+ if (other.tpe != ErrorType && member.tpe != ErrorType) {
+ overrideError("has incompatible type "+analyzer.underlying(member).tpe.normalize);
+ explainTypes(member.tpe, other.tpe);
+ }
+ }
def overrideAccessError(): unit = {
val pwString = if (other.privateWithin == NoSymbol) ""
else other.privateWithin.name.toString
val otherAccess = flagsToString(other getFlag (PRIVATE | PROTECTED), pwString)
- overrideError("has weaker access privileges; it should be "+
+ overrideError("has weaker access privileges; it should be "+
(if (otherAccess == "") "public" else "at least "+otherAccess))
}
- //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG
-
- // return if we already checked this combination elsewhere
- if (member.owner != clazz) {
- if ((member.owner isSubClass other.owner) &&
- ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED))) {
- //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG
- return;
- }
- if (clazz.info.parents exists (parent =>
- (parent.symbol isSubClass other.owner) && (parent.symbol isSubClass member.owner) &&
- ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED)))) {
- //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG
- return;
- }
- if (clazz.info.parents forall (parent =>
- (parent.symbol isSubClass other.owner) == (parent.symbol isSubClass member.owner))) {
- //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG
- return;
- }
- }
-
- if (member hasFlag PRIVATE) { // (1.1)
- overrideError("has weaker access privileges; it should not be private")
+ //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG
+
+ // return if we already checked this combination elsewhere
+ if (member.owner != clazz) {
+ if ((member.owner isSubClass other.owner) &&
+ ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED))) {
+ //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG
+ return;
+ }
+ if (clazz.info.parents exists (parent =>
+ (parent.symbol isSubClass other.owner) && (parent.symbol isSubClass member.owner) &&
+ ((member hasFlag DEFERRED) || !(other hasFlag DEFERRED)))) {
+ //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG
+ return;
+ }
+ if (clazz.info.parents forall (parent =>
+ (parent.symbol isSubClass other.owner) == (parent.symbol isSubClass member.owner))) {
+ //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG
+ return;
+ }
+ }
+
+ if (member hasFlag PRIVATE) { // (1.1)
+ overrideError("has weaker access privileges; it should not be private")
}
val mb = member.accessBoundary(member.owner)
val ob = other.accessBoundary(member.owner)
@@ -170,81 +172,106 @@ abstract class RefChecks extends InfoTransform {
(other hasFlag PROTECTED) && !(member hasFlag PROTECTED))) {
overrideAccessError()
} else if (other hasFlag FINAL) { // (1.2)
- overrideError("cannot override final member");
- } else if (!(other hasFlag DEFERRED) && !(member hasFlag (OVERRIDE | ABSOVERRIDE))) { // (1.3)
- overrideError("needs `override' modifier");
- } else if ((other hasFlag ABSOVERRIDE) && other.isIncompleteIn(clazz) && !(member hasFlag ABSOVERRIDE)) {
- overrideError("needs `abstract override' modifiers")
+ overrideError("cannot override final member");
+ } else if (!(other hasFlag DEFERRED) && !(member hasFlag (OVERRIDE | ABSOVERRIDE))) { // (1.3)
+ overrideError("needs `override' modifier");
+ } else if ((other hasFlag ABSOVERRIDE) && other.isIncompleteIn(clazz) && !(member hasFlag ABSOVERRIDE)) {
+ overrideError("needs `abstract override' modifiers")
} else if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) &&
(other hasFlag ACCESSOR) && other.accessed.isVariable) {
overrideError("cannot override a mutable variable")
- } else if (other.isStable && !member.isStable) { // (1.4)
- overrideError("needs to be an immutable value")
+ } else if (other.isStable && !member.isStable) { // (1.4)
+ overrideError("needs to be an immutable value")
} else if (other.isStable && !(other hasFlag DEFERRED) && other.owner.isTrait && (member hasFlag OVERRIDE)) {
overrideError("cannot override a value or variable definition in a trait " +
"\n (this is an implementation restriction)")
- } else {
- if (other.isAliasType) {
- if (!member.typeParams.isEmpty) // (1.5)
- overrideError("may not be parameterized");
- if (!other.typeParams.isEmpty) // (1.5)
- overrideError("may not override parameterized type");
- if (!(self.memberType(member) =:= self.memberType(other))) // (1.6)
- overrideTypeError();
- } else if (other.isAbstractType) {
- if (!member.typeParams.isEmpty) // (1.7)
- overrideError("may not be parameterized");
- if (!(self.memberInfo(other).bounds containsType self.memberType(member))) { // (1.7) {
- overrideTypeError(); // todo: do an explaintypes with bounds here
+ } else {
+ if (other.isAliasType) {
+ //if (!member.typeParams.isEmpty) // (1.5) @MAT
+ // overrideError("may not be parameterized");
+ //if (!other.typeParams.isEmpty) // (1.5) @MAT
+ // overrideError("may not override parameterized type");
+ // @M: substSym
+ if (!(self.memberType(member).substSym(member.typeParams, other.typeParams) =:= self.memberType(other))) // (1.6)
+ overrideTypeError();
+ } else if (other.isAbstractType) {
+ //if (!member.typeParams.isEmpty) // (1.7) @MAT
+ // overrideError("may not be parameterized");
+ var memberTp = self.memberType(member)
+
+ if (!(self.memberInfo(other).bounds containsType memberTp)) { // (1.7.1) {
+ overrideTypeError(); // todo: do an explaintypes with bounds here
}
- } else if (other.isTerm) {
- if (!overridesType(self.memberInfo(member), self.memberInfo(other))) { // 8
- overrideTypeError();
+
+ // check overriding (abstract type --> abstract type or abstract type --> concrete type member (a type alias))
+ // making an abstract type member concrete is like passing a type argument
+ val kindErrors = typer.infer.checkKindBounds(List(other), List(memberTp)) // (1.7.2)
+
+ if(!kindErrors.isEmpty)
+ unit.error(member.pos,
+ "The kind of "+member.keyString+" "+member.varianceString + member.nameString+
+ " does not conform to the expected kind of " + other.defString + other.locationString + "." +
+ kindErrors.toList.mkString("\n", ", ", ""))
+
+ // check a type alias's RHS corresponds to its declaration
+ // this overlaps somewhat with validateVariance
+ if(member.isAliasType) {
+ val kindErrors = typer.infer.checkKindBounds(List(member), List(memberTp.normalize))
+
+ if(!kindErrors.isEmpty)
+ unit.error(member.pos,
+ "The kind of the right-hand side "+memberTp.normalize+" of "+member.keyString+" "+
+ member.varianceString + member.nameString+ " does not conform to its expected kind."+
+ kindErrors.toList.mkString("\n", ", ", ""))
+ }
+ } else if (other.isTerm) {
+ if (!overridesType(self.memberInfo(member), self.memberInfo(other))) { // 8
+ overrideTypeError();
}
- }
- }
+ }
+ }
}
val opc = new overridingPairs.Cursor(clazz)
while (opc.hasNext) {
- //Console.println("overrides " + opc.overriding/* + ":" + opc.overriding.tpe*/ + opc.overriding.locationString + " " + opc.overridden/* + ":" + opc.overridden.tpe*/ + opc.overridden.locationString + opc.overridden.hasFlag(DEFERRED));//DEBUG
- if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden);
+ //Console.println("overrides " + opc.overriding/* + ":" + opc.overriding.tpe*/ + opc.overriding.locationString + " " + opc.overridden/* + ":" + opc.overridden.tpe*/ + opc.overridden.locationString + opc.overridden.hasFlag(DEFERRED));//DEBUG
+ if (!opc.overridden.isClass) checkOverride(clazz, opc.overriding, opc.overridden);
- opc.next
+ opc.next
}
// 2. Check that only abstract classes have deferred members
if (clazz.isClass && !clazz.isTrait) {
- def abstractClassError(mustBeMixin: boolean, msg: String): unit = {
- unit.error(clazz.pos,
- (if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible"
- else if (mustBeMixin) clazz.toString() + " needs to be a mixin"
- else clazz.toString() + " needs to be abstract") + ", since " + msg);
- clazz.setFlag(ABSTRACT)
- }
- for (val member <- clazz.tpe.nonPrivateMembers)
- if ((member hasFlag DEFERRED) && !(clazz hasFlag ABSTRACT)) {
- abstractClassError(
+ def abstractClassError(mustBeMixin: boolean, msg: String): unit = {
+ unit.error(clazz.pos,
+ (if (clazz.isAnonymousClass || clazz.isModuleClass) "object creation impossible"
+ else if (mustBeMixin) clazz.toString() + " needs to be a mixin"
+ else clazz.toString() + " needs to be abstract") + ", since " + msg);
+ clazz.setFlag(ABSTRACT)
+ }
+ for (val member <- clazz.tpe.nonPrivateMembers)
+ if ((member hasFlag DEFERRED) && !(clazz hasFlag ABSTRACT)) {
+ abstractClassError(
false, infoString(member) + " is not defined" + analyzer.varNotice(member))
- } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) {
- val other = member.superSymbol(clazz);
- abstractClassError(true,
- infoString(member) + " is marked `abstract' and `override'" +
- (if (other != NoSymbol)
- " and overrides incomplete superclass member " + infoString(other)
- else ""))
- }
+ } else if ((member hasFlag ABSOVERRIDE) && member.isIncompleteIn(clazz)) {
+ val other = member.superSymbol(clazz);
+ abstractClassError(true,
+ infoString(member) + " is marked `abstract' and `override'" +
+ (if (other != NoSymbol)
+ " and overrides incomplete superclass member " + infoString(other)
+ else ""))
+ }
}
// 3. Check that every defined member with an `override' modifier overrides some other member.
for (val member <- clazz.info.decls.toList)
- if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) &&
- (clazz.info.baseClasses.tail forall {
+ if ((member hasFlag (OVERRIDE | ABSOVERRIDE)) &&
+ (clazz.info.baseClasses.tail forall {
bc => member.matchingSymbol(bc, clazz.thisType) == NoSymbol
})) {
// for (val bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG
- unit.error(member.pos, member.toString() + " overrides nothing");
- member resetFlag OVERRIDE
- }
+ unit.error(member.pos, member.toString() + " overrides nothing");
+ member resetFlag OVERRIDE
+ }
}
// Basetype Checking --------------------------------------------------------
@@ -270,37 +297,37 @@ abstract class RefChecks extends InfoTransform {
var seenCaseClass = if (clazz hasFlag CASE) clazz else NoSymbol
def validateTypes(tps: List[Type], includeSuper: boolean): unit = {
- if (!tps.isEmpty) {
- for (val tp <- tps.tail.reverse) validateType(tp, false);
- if (includeSuper) validateType(tps.head, true);
- }
+ if (!tps.isEmpty) {
+ for (val tp <- tps.tail.reverse) validateType(tp, false);
+ if (includeSuper) validateType(tps.head, true);
+ }
}
def validateType(tp: Type, includeSuper: boolean): unit = {
- val baseClass = tp.symbol
- if (baseClass.isClass) {
- val index = clazz.info.closurePos(baseClass)
- if (index >= 0) {
- if ((seenTypes(index) ne null) && !(seenTypes(index) <:< tp))
- unit.error(clazz.pos, "illegal inheritance;\n " + clazz +
- " inherits different type instances of " + baseClass +
- ":\n" + tp + " and " + seenTypes(index));
- seenTypes(index) = tp;
- // check that case classes do not inherit from case classes
- if (baseClass hasFlag CASE) {
- if (seenCaseClass != NoSymbol && seenCaseClass != baseClass)
- unit.error(clazz.pos, "implementation restriction: case " +
- seenCaseClass + " and case " + baseClass +
+ val baseClass = tp.symbol
+ if (baseClass.isClass) {
+ val index = clazz.info.closurePos(baseClass)
+ if (index >= 0) {
+ if ((seenTypes(index) ne null) && !(seenTypes(index) <:< tp))
+ unit.error(clazz.pos, "illegal inheritance;\n " + clazz +
+ " inherits different type instances of " + baseClass +
+ ":\n" + tp + " and " + seenTypes(index));
+ seenTypes(index) = tp;
+ // check that case classes do not inherit from case classes
+ if (baseClass hasFlag CASE) {
+ if (seenCaseClass != NoSymbol && seenCaseClass != baseClass)
+ unit.error(clazz.pos, "implementation restriction: case " +
+ seenCaseClass + " and case " + baseClass +
" cannot be combined in one object");
- seenCaseClass = baseClass
- }
- // check that inner classes do not inherit from Annotation
+ seenCaseClass = baseClass
+ }
+ // check that inner classes do not inherit from Annotation
if (baseClass == ClassfileAnnotationClass)
if (!clazz.owner.isPackageClass)
unit.error(clazz.pos, "inner classes cannot be classfile annotations")
- }
- validateTypes(tp.parents, includeSuper)
- }
+ }
+ validateTypes(tp.parents, includeSuper)
+ }
}
validateTypes(clazz.info.parents, true)
@@ -322,68 +349,70 @@ abstract class RefChecks extends InfoTransform {
private def validateVariance(base: Symbol, all: Type, variance: int): unit = {
def varianceString(variance: int): String =
- if (variance == 1) "covariant"
- else if (variance == -1) "contravariant"
- else "invariant";
+ if (variance == 1) "covariant"
+ else if (variance == -1) "contravariant"
+ else "invariant";
def relativeVariance(tvar: Symbol): int = {
- val clazz = tvar.owner
- var sym = base
- var state = CoVariance
- while (sym != clazz && state != AnyVariance) {
- //Console.println("flip: " + sym + " " + sym.isParameter());//DEBUG
- if ((sym hasFlag PARAM) && !sym.owner.isConstructor) state = -state;
- else if (!sym.owner.isClass || sym.isPrivateLocal) state = AnyVariance;
- else if (sym.isAliasType) state = NoVariance;
- sym = sym.owner
- }
- state
+ val clazz = tvar.owner
+ var sym = base
+ var state = CoVariance
+ while (sym != clazz && state != AnyVariance) {
+ //Console.println("flip: " + sym + " " + sym.isParameter());//DEBUG
+ if ((sym hasFlag PARAM) && !sym.owner.isConstructor) state = -state;
+ else if (!sym.owner.isClass || sym.isPrivateLocal) state = AnyVariance;
+ else if (sym.isAliasType) state = NoVariance;
+ sym = sym.owner
+ }
+ state
}
def validateVariance(tp: Type, variance: int): unit = tp match {
- case ErrorType => ;
- case WildcardType => ;
- case NoType => ;
- case NoPrefix => ;
- case ThisType(_) => ;
- case ConstantType(_) => ;
- case SingleType(pre, sym) =>
- validateVariance(pre, variance)
- case TypeRef(pre, sym, args) =>
- if (sym.variance != NoVariance) {
- val v = relativeVariance(sym);
- if (v != AnyVariance && sym.variance != v * variance) {
- //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG
- unit.error(base.pos,
- varianceString(sym.variance) + " " + sym +
- " occurs in " + varianceString(v * variance) +
- " position in type " + all + " of " + base);
- }
- }
- validateVariance(pre, variance)
- validateVarianceArgs(args, variance, sym.typeParams)
- case ClassInfoType(parents, decls, symbol) =>
- validateVariances(parents, variance)
- case RefinedType(parents, decls) =>
- validateVariances(parents, variance)
- case TypeBounds(lo, hi) =>
- validateVariance(lo, -variance)
- validateVariance(hi, variance)
- case MethodType(formals, result) =>
- validateVariance(result, variance)
- case PolyType(tparams, result) =>
- validateVariance(result, variance)
+ case ErrorType => ;
+ case WildcardType => ;
+ case NoType => ;
+ case NoPrefix => ;
+ case ThisType(_) => ;
+ case ConstantType(_) => ;
+ case SingleType(pre, sym) =>
+ validateVariance(pre, variance)
+ case TypeRef(pre, sym, args) =>
+ if (sym.variance != NoVariance) {
+ val v = relativeVariance(sym);
+ if (v != AnyVariance && sym.variance != v * variance) {
+ //Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG
+ unit.error(base.pos,
+ varianceString(sym.variance) + " " + sym +
+ " occurs in " + varianceString(v * variance) +
+ " position in type " + all + " of " + base);
+ }
+ }
+ validateVariance(pre, variance)
+ validateVarianceArgs(args, variance, sym.typeParams) //@M for higher-kinded typeref, args.isEmpty
+ // However, these args respect variances by construction anyway
+ // -- the interesting case is in type application, see checkKindBounds in Infer
+ case ClassInfoType(parents, decls, symbol) =>
+ validateVariances(parents, variance)
+ case RefinedType(parents, decls) =>
+ validateVariances(parents, variance)
+ case TypeBounds(lo, hi) =>
+ validateVariance(lo, -variance)
+ validateVariance(hi, variance)
+ case MethodType(formals, result) =>
+ validateVariance(result, variance)
+ case PolyType(tparams, result) =>
+ validateVariance(result, variance)
case AnnotatedType(attribs, tp) =>
validateVariance(tp, variance)
}
def validateVariances(tps: List[Type], variance: int): unit =
- tps foreach (tp => validateVariance(tp, variance))
+ tps foreach (tp => validateVariance(tp, variance))
def validateVarianceArgs(tps: List[Type], variance: int, tparams: List[Symbol]): unit =
- (tps zip tparams) foreach {
- case (tp, tparam) => validateVariance(tp, variance * tparam.variance)
- }
+ (tps zip tparams) foreach {
+ case (tp, tparam) => validateVariance(tp, variance * tparam.variance)
+ }
validateVariance(all, variance)
}
@@ -409,35 +438,38 @@ abstract class RefChecks extends InfoTransform {
private def enterSyms(stats: List[Tree]): unit = {
var index = -1
for (val stat <- stats) {
- index = index + 1;
- stat match {
+ index = index + 1;
+ stat match {
case ClassDef(_, _, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) =>
assert(stat.symbol != NoSymbol, stat);//debug
if (stat.symbol.isLocal) {
- currentLevel.scope.enter(newScopeEntry(stat.symbol, currentLevel.scope));
- symIndex(stat.symbol) = index;
+ currentLevel.scope.enter(newScopeEntry(stat.symbol, currentLevel.scope));
+ symIndex(stat.symbol) = index;
}
case _ =>
- }
+ }
}
}
private def enterReference(pos: int, sym: Symbol): unit =
if (sym.isLocal) {
- val e = currentLevel.scope.lookupEntry(sym.name)
- if ((e ne null) && sym == e.sym) {
+ val e = currentLevel.scope.lookupEntry(sym.name)
+ if ((e ne null) && sym == e.sym) {
var l = currentLevel
while (l.scope != e.owner) l = l.outer;
- val symindex = symIndex(sym)
- if (l.maxindex < symindex) {
- l.refpos = pos
- l.refsym = sym
- l.maxindex = symindex
- }
- }
+ val symindex = symIndex(sym)
+ if (l.maxindex < symindex) {
+ l.refpos = pos
+ l.refsym = sym
+ l.maxindex = symindex
+ }
+ }
}
// Comparison checking -------------------------------------------------------
+ object normalizeAll extends TypeMap {
+ def apply(tp: Type) = mapOver(tp).normalize
+ }
def checkSensible(pos: int, fn: Tree, args: List[Tree]) = fn match {
case Select(qual, name) if (args.length == 1) =>
@@ -461,8 +493,8 @@ abstract class RefChecks extends InfoTransform {
unit.warning(pos, "comparing "+what+" using `"+name.decode+"' will always yield "+
(alwaysEqual == (name == nme.EQ || name == nme.LE || name == nme.GE)))
def nonSensible(pre: String, alwaysEqual: boolean) =
- nonSensibleWarning(pre+"values of types "+qual.tpe.widen+" and "+args.head.tpe.widen,
- alwaysEqual)
+ nonSensibleWarning(pre+"values of types "+normalizeAll(qual.tpe.widen)+" and "+normalizeAll(args.head.tpe.widen),
+ alwaysEqual) // @MAT normalize for consistency in error message, otherwise part is normalized due to use of `symbol', but the rest isn't
def hasObjectEquals = receiver.info.member(nme.equals_) == Object_equals
if (formal == UnitClass && actual == UnitClass)
nonSensible("", true)
@@ -511,13 +543,13 @@ abstract class RefChecks extends InfoTransform {
def transformStat(tree: Tree, index: int): List[Tree] = tree match {
case ModuleDef(mods, name, impl) =>
- val sym = tree.symbol
- val cdef = ClassDef(mods | MODULE, name, List(), emptyValDef, impl)
- .setPos(tree.pos)
+ val sym = tree.symbol
+ val cdef = ClassDef(mods | MODULE, name, List(), emptyValDef, impl)
+ .setPos(tree.pos)
.setSymbol(sym.moduleClass)
.setType(NoType);
- if (sym.isStatic) List(transform(cdef))
- else {
+ if (sym.isStatic) List(transform(cdef))
+ else {
val vdef =
localTyper.typed {
atPos(tree.pos) {
@@ -526,8 +558,8 @@ abstract class RefChecks extends InfoTransform {
}
val ddef =
- atPhase(phase.next) {
- localTyper.typed {
+ atPhase(phase.next) {
+ localTyper.typed {
if (sym.owner.isTrait) gen.mkModuleAccessDcl(sym)
else gen.mkModuleAccessDef(sym, vdef.symbol)
}
@@ -535,7 +567,7 @@ abstract class RefChecks extends InfoTransform {
if (sym.owner.isTrait) transformTrees(List(cdef, ddef))
else transformTrees(List(cdef, vdef, ddef))
- }
+ }
case ClassDef(_, _, _, _, _) if isConcreteLocalCaseFactory(tree.symbol) =>
val clazz = tree.symbol
@@ -564,27 +596,27 @@ abstract class RefChecks extends InfoTransform {
}
case ValDef(_, _, _, _) =>
- val tree1 = transform(tree); // important to do before forward reference check
- if (tree.symbol.isLocal && index <= currentLevel.maxindex) {
- if (settings.debug.value) Console.println(currentLevel.refsym);
- unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol);
- }
- List(tree1)
+ val tree1 = transform(tree); // important to do before forward reference check
+ if (tree.symbol.isLocal && index <= currentLevel.maxindex) {
+ if (settings.debug.value) Console.println(currentLevel.refsym);
+ unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol);
+ }
+ List(tree1)
case Import(_, _) =>
- List()
+ List()
case _ =>
- List(transform(tree))
+ List(transform(tree))
}
override def transform(tree: Tree): Tree = try {
/* Check whether argument types conform to bounds of type parameters */
def checkBounds(pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type]): unit = try {
- typer.infer.checkBounds(tree.pos, pre, owner, tparams, argtps, "");
+ typer.infer.checkBounds(tree.pos, pre, owner, tparams, argtps, "");
} catch {
- case ex: TypeError => unit.error(tree.pos, ex.getMessage());
+ case ex: TypeError => unit.error(tree.pos, ex.getMessage());
}
def isIrrefutable(pat: Tree, seltpe: Type): boolean = {
@@ -631,40 +663,41 @@ abstract class RefChecks extends InfoTransform {
val sym = tree.symbol
var result = tree
tree match {
- case ClassDef(mods, name, tparams, _, impl) =>
- validateVariance(sym, sym.info, CoVariance)
- validateVariance(sym, sym.typeOfThis, CoVariance)
+ case ClassDef(mods, name, tparams, _, impl) =>
+ validateVariance(sym, sym.info, CoVariance)
+ validateVariance(sym, sym.typeOfThis, CoVariance)
- case DefDef(_, _, _, _, _, _) =>
- validateVariance(sym, sym.tpe, CoVariance)
+ case DefDef(_, _, _, _, _, _) =>
+ validateVariance(sym, sym.tpe, CoVariance) //@M TODO: might be affected by change in tpe --> can't use tree.tpe though
checkDeprecatedOvers()
- case ValDef(_, _, _, _) =>
- validateVariance(sym, sym.tpe, if (sym.isVariable) NoVariance else CoVariance)
+ case ValDef(_, _, _, _) =>
+ validateVariance(sym, sym.tpe, if (sym.isVariable) NoVariance else CoVariance) //@M TODO: might be affected by change in tpe --> can't use tree.tpe though
checkDeprecatedOvers()
- case AbsTypeDef(_, _, _, _) =>
- validateVariance(sym, sym.info, CoVariance)
+ case AbsTypeDef(_, _, _, _, _) =>
+ validateVariance(sym, sym.info, CoVariance)
- case AliasTypeDef(_, _, _, _) =>
- validateVariance(sym, sym.info, CoVariance)
+ case AliasTypeDef(_, _, _, _) =>
+ validateVariance(sym, sym.info, CoVariance)
- case Template(_, _) =>
- localTyper = localTyper.atOwner(tree, currentOwner)
- validateBaseTypes(currentOwner)
- checkAllOverrides(currentOwner)
+ case Template(_, _) =>
+ localTyper = localTyper.atOwner(tree, currentOwner)
+ validateBaseTypes(currentOwner)
+ checkAllOverrides(currentOwner)
- case TypeTree() =>
- new TypeTraverser {
- def traverse(tp: Type): TypeTraverser = tp match {
- case TypeRef(pre, sym, args) => checkBounds(pre, sym.owner, sym.typeParams, args); this
- case _ => this
- }
- } traverse tree.tpe
+ case TypeTree() =>
+ new TypeTraverser {
+ def traverse(tp: Type): TypeTraverser = tp match {
+ case tr@TypeRef(pre, sym, _) if tr.isHigherKinded => this //@M a higher-kinded typeref doesn't have any args to check
+ case TypeRef(pre, sym, args) => checkBounds(pre, sym.owner, sym.typeParams, args); this
+ case _ => this
+ }
+ } traverse tree.tpe
- case TypeApply(fn, args) =>
- checkBounds(NoPrefix, NoSymbol, fn.tpe.typeParams, args map (.tpe))
- if (sym.isSourceMethod && sym.hasFlag(CASE)) result = toConstructor(tree.pos, tree.tpe)
+ case TypeApply(fn, args) =>
+ checkBounds(NoPrefix, NoSymbol, fn.tpe.typeParams, args map (.tpe))
+ if (sym.isSourceMethod && sym.hasFlag(CASE)) result = toConstructor(tree.pos, tree.tpe)
case Apply(
Select(qual, nme.filter),
@@ -686,37 +719,37 @@ abstract class RefChecks extends InfoTransform {
case _ =>
}
- case New(tpt) =>
- enterReference(tree.pos, tpt.tpe.symbol)
-
- case Ident(name) =>
- if (sym.isSourceMethod && sym.hasFlag(CASE))
- result = toConstructor(tree.pos, tree.tpe)
- else if (name != nme.WILDCARD && name != nme.WILDCARD_STAR.toTypeName) {
- assert(sym != NoSymbol, tree)//debug
- enterReference(tree.pos, sym)
- }
-
- case Select(qual, name) =>
- if (sym.isSourceMethod && sym.hasFlag(CASE))
- result = toConstructor(tree.pos, tree.tpe)
- else qual match {
- case Super(qualifier, mix) =>
+ case New(tpt) =>
+ enterReference(tree.pos, tpt.tpe.symbol)
+
+ case Ident(name) =>
+ if (sym.isSourceMethod && sym.hasFlag(CASE))
+ result = toConstructor(tree.pos, tree.tpe)
+ else if (name != nme.WILDCARD && name != nme.WILDCARD_STAR.toTypeName) {
+ assert(sym != NoSymbol, tree)//debug
+ enterReference(tree.pos, sym)
+ }
+
+ case Select(qual, name) =>
+ if (sym.isSourceMethod && sym.hasFlag(CASE))
+ result = toConstructor(tree.pos, tree.tpe)
+ else qual match {
+ case Super(qualifier, mix) =>
val base = qual.symbol;
//Console.println("super: " + tree + " in " + base);//DEBUG
assert(!(base.isTrait && sym.isTerm && mix == nme.EMPTY.toTypeName)) // term should have been eliminated by super accessors
case _ =>
}
- case _ =>
+ case _ =>
}
result = super.transform(result)
localTyper = savedLocalTyper
result
} catch {
case ex: TypeError =>
- if (settings.debug.value) ex.printStackTrace();
- unit.error(tree.pos, ex.getMessage())
- tree
+ if (settings.debug.value) ex.printStackTrace();
+ unit.error(tree.pos, ex.getMessage())
+ tree
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index b54d378eb7..329eb344c6 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -239,8 +239,9 @@ trait Typers requires Analyzer {
tp match {
case TypeRef(pre, sym, args) =>
(checkNotLocked(sym)) && (
- !sym.isAliasType && !sym.isAbstractType ||
- checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym)
+ !sym.isTypeMember ||
+ checkNonCyclic(pos, appliedType(pre.memberInfo(sym), args), sym) // @M! info for a type ref to a type parameter now returns a polytype
+ // @M was: checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym)
)
case SingleType(pre, sym) =>
checkNotLocked(sym)
@@ -524,7 +525,8 @@ trait Typers requires Analyzer {
* unapply or unapplySeq method.
*
* (6) Convert all other types to TypeTree nodes.
- * (7) When in TYPEmode nut not FUNmode, check that types are fully parameterized
+ * (7) When in TYPEmode but not FUNmode, check that types are fully parameterized
+ * (7.1) @M! when in POLYmode | TAPPmode, check that types have the expected kind
* (8) When in both EXPRmode and FUNmode, add apply method calls to values of object type.
* (9) If there are undetermined type variables and not POLYmode, infer expression instance
* Then, if tree's type is not a subtype of expected type, try the following adaptations:
@@ -584,8 +586,16 @@ trait Typers requires Analyzer {
if (tree.isType) {
if ((mode & FUNmode) != 0) {
tree
- } else if (tree.hasSymbol && !tree.symbol.typeParams.isEmpty) { // (7)
+ } else if (tree.hasSymbol && !tree.symbol.typeParams.isEmpty && (mode & (POLYmode | TAPPmode)) == 0) { // (7)
+ // @M higher-kinded types are allowed in (POLYmode | TAPPmode) -- see (7.1)
errorTree(tree, tree.symbol+" takes type parameters")
+ tree setType tree.tpe
+ } else if (tree.hasSymbol && ((mode & (POLYmode | TAPPmode)) == (POLYmode | TAPPmode)) && // (7.1) @M: check kind-arity (full checks are done in checkKindBounds in Infer)
+ tree.symbol.typeParams.length != pt.typeParams.length &&
+ !(tree.symbol==AnyClass || tree.symbol==AllClass || pt == WildcardType )) { //@M Any and Nothing are kind-polymorphic -- WildcardType is only used when typing type arguments to an overloaded method, before the overload is resolved
+ errorTree(tree, tree.symbol+" takes "+reporter.countElementsAsString(tree.symbol.typeParams.length, "type parameter")+
+ ", expected: "+reporter.countAsString(pt.typeParams.length))
+ tree setType tree.tpe
} else tree match { // (6)
case TypeTree() => tree
case _ => TypeTree(tree.tpe) setOriginal(tree)
@@ -648,7 +658,7 @@ trait Typers requires Analyzer {
if (tree1.tpe <:< pt) adapt(tree1, mode, pt)
else {
if ((mode & (EXPRmode | FUNmode)) == EXPRmode) {
- pt match {
+ pt.normalize match {
case TypeRef(_, sym, _) =>
// note: was if (pt.symbol == UnitClass) but this leads to a potentially
// infinite expansion if pt is constant type ()
@@ -824,7 +834,7 @@ trait Typers requires Analyzer {
else
psym addChild context.owner
}
- if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes) {
+ if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes) { //@M .typeOfThis seems necessary
//Console.println(context.owner);//DEBUG
//Console.println(context.owner.unsafeTypeParams);//DEBUG
//Console.println(List.fromArray(context.owner.info.closure));//DEBUG
@@ -848,7 +858,7 @@ trait Typers requires Analyzer {
if (classinfo.expansiveRefs(tparam) contains tparam) {
error(tparam.pos, "class graph is not finitary because type parameter "+tparam.name+" is expansively recursive")
val newinfo = ClassInfoType(
- classinfo.parents map (.subst(List(tparam), List(AnyRefClass.tpe))),
+ classinfo.parents map (.instantiateTypeParams(List(tparam), List(AnyRefClass.tpe))),
classinfo.decls,
clazz)
clazz.setInfo {
@@ -1137,13 +1147,15 @@ trait Typers requires Analyzer {
}
def typedAbsTypeDef(tdef: AbsTypeDef): AbsTypeDef = {
+ reenterTypeParams(tdef.tparams) // @M!
+ val tparams1 = List.mapConserve(tdef.tparams)(typedAbsTypeDef) // @M!
val lo1 = checkNoEscaping.privates(tdef.symbol, typedType(tdef.lo))
val hi1 = checkNoEscaping.privates(tdef.symbol, typedType(tdef.hi))
checkNonCyclic(tdef.symbol)
if (!(lo1.tpe <:< hi1.tpe))
error(tdef.pos,
"lower bound "+lo1.tpe+" does not conform to upper bound "+hi1.tpe)
- copy.AbsTypeDef(tdef, tdef.mods, tdef.name, lo1, hi1) setType NoType
+ copy.AbsTypeDef(tdef, tdef.mods, tdef.name, tparams1, lo1, hi1) setType NoType
}
def typedAliasTypeDef(tdef: AliasTypeDef): AliasTypeDef = {
@@ -1231,7 +1243,7 @@ trait Typers requires Analyzer {
body1 =
typed {
atPos(body1.pos) {
- TypeApply(Select(body1, Any_asInstanceOf), List(TypeTree(pt)))
+ TypeApply(Select(body1, Any_asInstanceOf), List(TypeTree(pt))) // @M no need for pt.normalize here, is done in erasure
}
}
}
@@ -1503,12 +1515,12 @@ trait Typers requires Analyzer {
val strictTargs = List.map2(lenientTargs, tparams)((targ, tparam) =>
if (targ == WildcardType) tparam.tpe else targ)
def typedArgToPoly(arg: Tree, formal: Type): Tree = {
- val lenientPt = formal.subst(tparams, lenientTargs)
+ val lenientPt = formal.instantiateTypeParams(tparams, lenientTargs)
val arg1 = typedArg(arg, mode, POLYmode, lenientPt)
val argtparams = context.undetparams
context.undetparams = List()
if (!argtparams.isEmpty) {
- val strictPt = formal.subst(tparams, strictTargs)
+ val strictPt = formal.instantiateTypeParams(tparams, strictTargs)
inferArgumentInstance(arg1, argtparams, strictPt, lenientPt)
}
arg1
@@ -1651,10 +1663,24 @@ trait Typers requires Analyzer {
//Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")")
def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) pt else lub(tps)
+ //@M! get the type of the qualifier in a Select tree, otherwise: NoType
+ def prefixType(fun: Tree): Type = fun match {
+ case Select(qualifier, _) => qualifier.tpe
+// case Ident(name) => ??
+ case _ => NoType
+ }
+
def typedTypeApply(fun: Tree, args: List[Tree]): Tree = fun.tpe match {
case OverloadedType(pre, alts) =>
inferPolyAlternatives(fun, args map (.tpe))
- typedTypeApply(fun, args)
+ val tparams = fun.symbol.typeParams
+ assert(args.length == tparams.length) //@M: in case TypeApply we can't check the kind-arities
+ // of the type arguments as we don't know which alternative to choose... here we do
+ val args1 = map2Conserve(args, tparams) {
+ //@M! the polytype denotes the expected kind
+ (arg, tparam) => typedHigherKindedType(arg, makePolyType(tparam.typeParams, AnyClass.tpe))
+ }
+ typedTypeApply(fun, args1)
case PolyType(tparams, restpe) if (tparams.length != 0) =>
if (tparams.length == args.length) {
val targs = args map (.tpe)
@@ -1663,8 +1689,14 @@ trait Typers requires Analyzer {
if (!targs.head.symbol.isClass || targs.head.symbol.isRefinementClass)
error(args.head.pos, "class type required");
Literal(Constant(targs.head)) setPos tree.pos setType ClassClass.tpe
- } else
- copy.TypeApply(tree, fun, args) setType restpe.subst(tparams, targs)
+ } else {
+ val resultpe0 = restpe.instantiateTypeParams(tparams, targs)
+ //@M TODO -- probably ok
+ //@M example why asSeenFrom is necessary: class Foo[a] { def foo[m[x]]: m[a] } (new Foo[Int]).foo[List] : List[Int]
+ //@M however, asSeenFrom widens a singleton type, thus cannot use it for those types
+ val resultpe = if(resultpe0.isInstanceOf[SingletonType]) resultpe0 else resultpe0.asSeenFrom(prefixType(fun), fun.symbol.owner)
+ copy.TypeApply(tree, fun, args) setType resultpe
+ }
} else {
errorTree(tree, "wrong number of type parameters for "+treeSymTypeMsg(fun))
}
@@ -1867,7 +1899,7 @@ trait Typers requires Analyzer {
var qual: Tree = EmptyTree // the qualififier tree if transformed tree is a select
// if we are in a constructor of a pattern, ignore all methods
// which are not case factories (note if we don't do that
- // case x :: xs in class List would return the :: method.
+ // case x :: xs in class List would return the :: method).
def qualifies(sym: Symbol): boolean =
sym.exists &&
((mode & PATTERNmode | FUNmode) != (PATTERNmode | FUNmode) ||
@@ -1982,6 +2014,9 @@ trait Typers requires Analyzer {
}
}
+ // @M: copied from Namers
+ def makePolyType(tparams: List[Symbol], tpe: Type) = if (tparams.isEmpty) tpe else PolyType(tparams, tpe)
+
// begin typed1
val sym: Symbol = tree.symbol
if (sym ne null) sym.initialize
@@ -2004,7 +2039,7 @@ trait Typers requires Analyzer {
case ddef @ DefDef(_, _, _, _, _, _) =>
newTyper(makeNewScope(context, tree, sym)).typedDefDef(ddef)
- case tdef @ AbsTypeDef(_, _, _, _) =>
+ case tdef @ AbsTypeDef(_, _, _, _, _) =>
newTyper(makeNewScope(context, tree, sym)).typedAbsTypeDef(tdef)
case tdef @ AliasTypeDef(_, _, _, _) =>
@@ -2263,9 +2298,39 @@ trait Typers requires Analyzer {
copy.Typed(tree, expr1, tpt1) setType owntype
case TypeApply(fun, args) =>
- val args1 = List.mapConserve(args)(typedType)
- // do args first in order to maintain conext.undetparams on the function side.
- typedTypeApply(typed(fun, funMode(mode) | TAPPmode, WildcardType), args1)
+ // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
+ //@M! we must type fun in order to type the args, as that requires the kinds of fun's type parameters.
+ // However, args should apparently be done first, to save context.undetparams. Unfortunately, the args
+ // *really* have to be typed *after* fun. We escape from this classic Catch-22 by simply saving&restoring undetparams.
+
+ // @M TODO: the compiler still bootstraps&all tests pass when this is commented out..
+ //val undets = context.undetparams
+
+ // @M: fun is typed in TAPPmode because it is being applied to its actual type parameters
+ val fun1 = typed(fun, funMode(mode) | TAPPmode, WildcardType)
+ val tparams = fun1.symbol.typeParams
+
+ //@M TODO: val undets_fun = context.undetparams ?
+ // "do args first" (by restoring the context.undetparams) in order to maintain context.undetparams on the function side.
+
+ // @M TODO: the compiler still bootstraps when this is commented out.. TODO: run tests
+ //context.undetparams = undets
+
+ // @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds?
+ val args1 = if(args.length == tparams.length) map2Conserve(args, tparams) {
+ //@M! the polytype denotes the expected kind
+ (arg, tparam) => typedHigherKindedType(arg, makePolyType(tparam.typeParams, AnyClass.tpe))
+ } else {
+ assert(fun1.symbol.info.isInstanceOf[OverloadedType])
+ // @M this branch is hit for an overloaded polymorphic type.
+ // Until the right alternative is known, be very liberal,
+ // typedTypeApply will find the right alternative and then do the same check as
+ // in the then-branch above. (see pos/tcpoly_overloaded.scala)
+ List.mapConserve(args)(typedHigherKindedType)
+ }
+
+ //@M TODO: context.undetparams = undets_fun ?
+ typedTypeApply(fun1, args1)
case Apply(Block(stats, expr), args) =>
typed1(atPos(tree.pos)(Block(stats, Apply(expr, args))), mode, pt)
@@ -2408,13 +2473,18 @@ trait Typers requires Analyzer {
case AppliedTypeTree(tpt, args) =>
val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType)
val tparams = tpt1.symbol.typeParams
- val args1 = List.mapConserve(args)(typedType)
+
if (tpt1.tpe.isError) {
setError(tree)
- } else if (tparams.length == args1.length) {
+ } else if (tparams.length == args.length) {
+ // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
+ val args1 = map2Conserve(args, tparams) {
+ (arg, tparam) => typedHigherKindedType(arg, makePolyType(tparam.typeParams, AnyClass.tpe)) //@M! the polytype denotes the expected kind
+ }
val argtypes = args1 map (.tpe)
- val owntype = if (tpt1.symbol.isClass) appliedType(tpt1.tpe, argtypes)
- else tpt1.tpe.subst(tparams, argtypes)
+ val owntype = if (tpt1.symbol.isClass || tpt1.symbol.isTypeMember) // @M! added the latter condition
+ appliedType(tpt1.tpe, argtypes)
+ else tpt1.tpe.instantiateTypeParams(tparams, argtypes)
List.map2(args, tparams) { (arg, tparam) => arg match {
// note: can't use args1 in selector, because Bind's got replaced
case Bind(_, _) =>
@@ -2476,8 +2546,8 @@ trait Typers requires Analyzer {
reportTypeError(tree.pos, ex)
setError(tree)
case ex: Throwable =>
- if (settings.debug.value)
- Console.println("exception when typing "+tree+", pt = "+pt)
+// if (settings.debug.value) // @M causes cyclic reference error
+// Console.println("exception when typing "+tree+", pt = "+pt)
if ((context ne null) && (context.unit ne null) &&
(context.unit.source ne null) && (tree ne null))
logError("AT: " + context.unit.source.dbg(tree.pos), ex);
@@ -2530,12 +2600,20 @@ trait Typers requires Analyzer {
def typedType(tree: Tree): Tree =
withNoGlobalVariance{ typed(tree, TYPEmode, WildcardType) }
+ /** Types a higher-kinded type tree -- pt denotes the expected kind*/
+ def typedHigherKindedType(tree: Tree, pt: Type): Tree =
+ if(pt.typeParams.isEmpty) typedType(tree) // kind is known and it's *
+ else withNoGlobalVariance{ typed(tree, POLYmode | TAPPmode, pt) }
+
+ def typedHigherKindedType(tree: Tree): Tree =
+ withNoGlobalVariance{ typed(tree, POLYmode | TAPPmode, WildcardType) }
+
/** Types a type constructor tree used in a new or supertype */
def typedTypeConstructor(tree: Tree): Tree = {
val result = withNoGlobalVariance{ typed(tree, TYPEmode | FUNmode, WildcardType) }
if (!phase.erasedTypes && result.tpe.isInstanceOf[TypeRef] && !result.tpe.prefix.isStable)
error(tree.pos, result.tpe.prefix+" is not a legal prefix for a constructor")
- result
+ result setType(result.tpe.normalize) // @MAT remove aliases when instantiating
}
def computeType(tree: Tree, pt: Type): Type = {
@@ -2558,7 +2636,7 @@ trait Typers requires Analyzer {
/* -- Views --------------------------------------------------------------- */
private def tparamsToWildcards(tp: Type, tparams: List[Symbol]) =
- tp.subst(tparams, tparams map (t => WildcardType))
+ tp.instantiateTypeParams(tparams, tparams map (t => WildcardType))
private def depoly(tp: Type): Type = tp match {
case PolyType(tparams, restpe) => tparamsToWildcards(restpe, tparams)