summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala12
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala79
-rw-r--r--test/files/neg/tcpoly_override.check5
-rw-r--r--test/files/neg/tcpoly_override.scala10
4 files changed, 75 insertions, 31 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 2fb4ecbdc4..4093144d12 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -103,7 +103,9 @@ trait Types {
override def isErroneous = underlying.isErroneous
override def isStable: Boolean = underlying.isStable
override def termSymbol = underlying.termSymbol
+ override def termSymbolDirect = underlying.termSymbolDirect
override def typeSymbol = underlying.typeSymbol
+ override def typeSymbolDirect = underlying.typeSymbolDirect
@deprecated override def symbol = underlying.symbol
override def widen = underlying.widen
override def typeOfThis = underlying.typeOfThis
@@ -186,6 +188,14 @@ trait Types {
*/
def typeSymbol: Symbol = NoSymbol
+ /** The term symbol *directly* associated with the type
+ */
+ def termSymbolDirect: Symbol = termSymbol
+
+ /** The type symbol *directly* associated with the type
+ */
+ def typeSymbolDirect: Symbol = typeSymbol
+
/** The base type underlying a type proxy,
* identity on all other types */
def underlying: Type = this
@@ -1250,6 +1260,8 @@ trait Types {
override def typeSymbol = if (sym.isAliasType) normalize.typeSymbol else sym
override def termSymbol = if (sym.isAliasType) normalize.termSymbol else super.termSymbol
+ override def typeSymbolDirect = sym
+ override def termSymbolDirect = super.termSymbol
@deprecated override def symbol = if (sym.isAliasType) normalize.symbol else sym
/* @MAT
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index a125348f12..248e7a94d2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -636,33 +636,44 @@ trait Infer {
def transform(tp: Type, clazz: Symbol): Type = tp.asSeenFrom(pre, clazz) // instantiate type params that come from outside the abstract type we're currently checking
// 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], paramowner: 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 ((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 (!(transform(hkparam.info.instantiateTypeParams(tparams, targs).bounds.substSym(hkparams, hkargs), paramowner) <:< transform(hkarg.info.bounds, owner)))
- stricterBound(hkarg, hkparam)
- } else {
- val (vm, sb) = checkKindBoundsHK(hkarg.typeParams, hkparam.typeParams, paramowner)
- varianceMismatches(vm)
- stricterBounds(sb)
+ 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
+ val hkparams = param.typeParams
+
+ if(hkargs.length != hkparams.length) {
+ if(arg == AnyClass || arg == AllClass) (Nil, Nil, Nil) // Any and Nothing are kind-overloaded
+ else (List((arg, param)), Nil, Nil)
+ } else {
+ val _arityMismatches = new ListBuffer[(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 arityMismatches(as: Iterable[(Symbol, Symbol)]): unit = _arityMismatches ++= as
+ def varianceMismatches(as: Iterable[(Symbol, Symbol)]): unit = _varianceMismatches ++= as
+ def stricterBounds(as: Iterable[(Symbol, Symbol)]): unit = _stricterBounds ++= as
+
+ for ((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 (!(transform(hkparam.info.instantiateTypeParams(tparams, targs).bounds.substSym(hkparams, hkargs), paramowner) <:< transform(hkarg.info.bounds, owner)))
+ stricterBound(hkarg, hkparam)
+ } else {
+ val (am, vm, sb) = checkKindBoundsHK(hkarg.typeParams, hkarg, hkparam, paramowner)
+ arityMismatches(am)
+ varianceMismatches(vm)
+ stricterBounds(sb)
+ }
}
- }
- (_varianceMismatches.toList, _stricterBounds.toList)
+ (_arityMismatches.toList, _varianceMismatches.toList, _stricterBounds.toList)
+ }
}
// @M TODO this method is duplicated all over the place (varianceString)
@@ -679,19 +690,25 @@ trait Infer {
}
val errors = new ListBuffer[String]
- (tparams zip targs).foreach{ case (tparam, targ) if(targ.isHigherKinded) =>
- val (varianceMismatches, stricterBounds) = checkKindBoundsHK(targ.typeParams, tparam.typeParams, tparam.owner)
+ (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!!
- if (!(varianceMismatches.isEmpty && stricterBounds.isEmpty)){
+ if (!(arityMismatches.isEmpty && varianceMismatches.isEmpty && stricterBounds.isEmpty)){
errors += (targ+"'s type parameters do not match "+tparam+"'s expected parameters: "+
+ (for ((a, p) <- arityMismatches)
+ yield a+qualify(a,p)+ " has "+reporter.countElementsAsString(a.typeParams.length, "type parameter")+", but "+
+ p+qualify(p,a)+" has "+reporter.countAsString(p.typeParams.length)).toList.mkString(", ") +
(for ((a, p) <- varianceMismatches)
yield a+qualify(a,p)+ " is "+varStr(a)+", but "+
- p+qualify(p,a)+" is declared "+varStr(p)).toList.mkString("", ", ", "") +
+ p+qualify(p,a)+" is declared "+varStr(p)).toList.mkString(", ") +
(for ((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("", ", ", ""))
+ p+qualify(p,a)+"'s declared bounds "+p.info).toList.mkString(", "))
}
- case _ =>
+ // case (tparam, targ) => println("no check: "+(tparam, targ, tparam.typeParams.isEmpty))
+ case _ =>
}
errors.toList
diff --git a/test/files/neg/tcpoly_override.check b/test/files/neg/tcpoly_override.check
new file mode 100644
index 0000000000..95529329e8
--- /dev/null
+++ b/test/files/neg/tcpoly_override.check
@@ -0,0 +1,5 @@
+tcpoly_override.scala:9: error: The kind of type T does not conform to the expected kind of type T[_] in trait A.
+C.this.T's type parameters do not match type T's expected parameters: type T (in class C) has no type parameters, but type T (in trait A) has one
+ type T = B // This compiles well (@M: ... but it shouldn't)
+ ^
+one error found
diff --git a/test/files/neg/tcpoly_override.scala b/test/files/neg/tcpoly_override.scala
new file mode 100644
index 0000000000..f2c3b04413
--- /dev/null
+++ b/test/files/neg/tcpoly_override.scala
@@ -0,0 +1,10 @@
+// bug1231: reported by Vladimir Reshetnikov on 19 July 2007
+trait A {
+ type T[_]
+}
+
+trait B // First-order type
+
+class C extends A {
+ type T = B // This compiles well (@M: ... but it shouldn't)
+} \ No newline at end of file