diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Types.scala | 12 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 79 | ||||
-rw-r--r-- | test/files/neg/tcpoly_override.check | 5 | ||||
-rw-r--r-- | test/files/neg/tcpoly_override.scala | 10 |
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 |