diff options
author | Antonio Cunei <antonio.cunei@epfl.ch> | 2010-09-14 14:33:23 +0000 |
---|---|---|
committer | Antonio Cunei <antonio.cunei@epfl.ch> | 2010-09-14 14:33:23 +0000 |
commit | 62fce2ceab1bc85319afbe17d4717bf03e563b45 (patch) | |
tree | b00937e0a5b32ba20d813507561a870c4a1aac93 | |
parent | 3ef8c7575deb5081bf623dde9d29c44648ce59af (diff) | |
download | scala-62fce2ceab1bc85319afbe17d4717bf03e563b45.tar.gz scala-62fce2ceab1bc85319afbe17d4717bf03e563b45.tar.bz2 scala-62fce2ceab1bc85319afbe17d4717bf03e563b45.zip |
Merged revisions 22978-22979 via svnmerge from
https://lampsvn.epfl.ch/svn-repos/scala/scala/trunk
........
r22978 | moors | 2010-09-14 14:15:35 +0200 (Tue, 14 Sep 2010) | 3 lines
closes #3692: make instantiateTypeVar more careful so it does not
change T's info to >: T <: T.
review by odersky
........
r22979 | moors | 2010-09-14 14:37:50 +0200 (Tue, 14 Sep 2010) | 10 lines
closes #3612. relaxed self-type conformance check slightly by
comparing the self types instead of the self-type symbols, which are
trivially different when you introduce a self variable without giving it
a type
given the definitions below (for full context, see test file), before,
O0 would work but O would not, now both are accepted:
{{{
object O0 extends C {}
object O extends C { self => }
}}}
review by odersky
........
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Infer.scala | 5 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 60 | ||||
-rw-r--r-- | test/files/neg/t3692.check | 14 | ||||
-rw-r--r-- | test/files/neg/t3692.scala | 17 | ||||
-rw-r--r-- | test/files/pos/t3612.scala | 6 |
5 files changed, 71 insertions, 31 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index a02f384a8d..86faddab37 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1250,6 +1250,8 @@ trait Infer { solve(tvars1, tvars1 map (_.origin.typeSymbol), tvars1 map (x => COVARIANT), false) } + // this is quite nasty: it destructively changes the info of the syms of e.g., method type params (see #3692, where the type param T's bounds were set to >: T <: T, so that parts looped) + // the changes are rolled back by restoreTypeBounds, but might be unintentially observed in the mean time def instantiateTypeVar(tvar: TypeVar) { val tparam = tvar.origin.typeSymbol if (false && @@ -1263,7 +1265,8 @@ trait Infer { } else { val (lo, hi) = instBounds(tvar) if (lo <:< hi) { - if (!((lo <:< tparam.info.bounds.lo) && (tparam.info.bounds.hi <:< hi))) { + if (!((lo <:< tparam.info.bounds.lo) && (tparam.info.bounds.hi <:< hi)) // bounds were improved + && tparam != lo.typeSymbolDirect && tparam != hi.typeSymbolDirect) { // don't create illegal cycles context.nextEnclosing(_.tree.isInstanceOf[CaseDef]).pushTypeBounds(tparam) tparam setInfo TypeBounds(lo, hi) if (settings.debug.value) log("new bounds of " + tparam + " = " + tparam.info) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 8ce8151aee..cbb872c3ed 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3188,44 +3188,44 @@ trait Typers { self: Analyzer => } def typedNew(tpt: Tree) = { - var tpt1 = typedTypeConstructor(tpt) - checkClassType(tpt1, false, true) - if (tpt1.hasSymbol && !tpt1.symbol.typeParams.isEmpty) { - context.undetparams = cloneSymbols(tpt1.symbol.typeParams) - tpt1 = TypeTree() - .setOriginal(tpt1) - .setType(appliedType(tpt1.tpe, context.undetparams map (_.tpe))) + val tpt1 = { + val tpt0 = typedTypeConstructor(tpt) + checkClassType(tpt0, false, true) + if (tpt0.hasSymbol && !tpt0.symbol.typeParams.isEmpty) { + context.undetparams = cloneSymbols(tpt0.symbol.typeParams) + TypeTree().setOriginal(tpt0) + .setType(appliedType(tpt0.tpe, context.undetparams map (_.tpe))) + } else tpt0 } /** If current tree <tree> appears in <val x(: T)? = <tree>> * return `tp with x.type' else return `tp'. */ - def narrowRhs(tp: Type) = { - var sym = context.tree.symbol - if (sym != null && sym != NoSymbol) - if (sym.owner.isClass) { - if (sym.getter(sym.owner) != NoSymbol) sym = sym.getter(sym.owner) - } else if (sym hasFlag LAZY) { - if (sym.lazyAccessor != NoSymbol) sym = sym.lazyAccessor - } + def narrowRhs(tp: Type) = { val sym = context.tree.symbol context.tree match { - case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !(mods hasFlag MUTABLE) => - val pre = if (sym.owner.isClass) sym.owner.thisType else NoPrefix - intersectionType(List(tp, singleType(pre, sym))) - case _ => - tp - } - } - if (tpt1.tpe.typeSymbol.isAbstractType || (tpt1.tpe.typeSymbol hasFlag ABSTRACT)) - error(tree.pos, tpt1.tpe.typeSymbol + " is abstract; cannot be instantiated") - else if (tpt1.tpe.typeSymbol.initialize.thisSym != tpt1.tpe.typeSymbol && - !(narrowRhs(tpt1.tpe) <:< tpt1.tpe.typeOfThis) && - !phase.erasedTypes) { - error(tree.pos, tpt1.tpe.typeSymbol + + case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !(mods hasFlag MUTABLE) && sym != null && sym != NoSymbol => + val sym1 = if (sym.owner.isClass && sym.getter(sym.owner) != NoSymbol) sym.getter(sym.owner) + else if ((sym hasFlag LAZY) && sym.lazyAccessor != NoSymbol) sym.lazyAccessor + else sym + val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix + intersectionType(List(tp, singleType(pre, sym1))) + case _ => tp + }} + + val tp = tpt1.tpe + val sym = tp.typeSymbol + if (sym.isAbstractType || (sym hasFlag ABSTRACT)) + error(tree.pos, sym + " is abstract; cannot be instantiated") + else if (!( tp == sym.initialize.thisSym.tpe // when there's no explicit self type -- with (#3612) or without self variable + // sym.thisSym.tpe == tp.typeOfThis (except for objects) + || narrowRhs(tp) <:< tp.typeOfThis + || phase.erasedTypes + )) { + error(tree.pos, sym + " cannot be instantiated because it does not conform to its self-type "+ - tpt1.tpe.typeOfThis) + tp.typeOfThis) } - treeCopy.New(tree, tpt1).setType(tpt1.tpe) + treeCopy.New(tree, tpt1).setType(tp) } def typedEta(expr1: Tree): Tree = expr1.tpe match { diff --git a/test/files/neg/t3692.check b/test/files/neg/t3692.check new file mode 100644 index 0000000000..ce89a6563d --- /dev/null +++ b/test/files/neg/t3692.check @@ -0,0 +1,14 @@ +t3692.scala:11: warning: type Integer in package scala is deprecated: use <code>java.lang.Integer</code> instead + case m0: Map[Int, Int] => new java.util.HashMap[Integer, Integer] + ^ +t3692.scala:12: warning: type Integer in package scala is deprecated: use <code>java.lang.Integer</code> instead + case m1: Map[Int, V] => new java.util.HashMap[Integer, V] + ^ +t3692.scala:13: warning: type Integer in package scala is deprecated: use <code>java.lang.Integer</code> instead + case m2: Map[T, Int] => new java.util.HashMap[T, Integer] + ^ +t3692.scala:13: error: unreachable code + case m2: Map[T, Int] => new java.util.HashMap[T, Integer] + ^ +three warnings found +one error found diff --git a/test/files/neg/t3692.scala b/test/files/neg/t3692.scala new file mode 100644 index 0000000000..78b0e4b843 --- /dev/null +++ b/test/files/neg/t3692.scala @@ -0,0 +1,17 @@ +object ManifestTester { + def main(args: Array[String]) = { + val map = Map("John" -> 1, "Josh" -> 2) + new ManifestTester().toJavaMap(map) + } +} + +class ManifestTester { + private final def toJavaMap[T, V](map: Map[T, V])(implicit m1: Manifest[T], m2: Manifest[V]): java.util.Map[_, _] = { + map match { + case m0: Map[Int, Int] => new java.util.HashMap[Integer, Integer] + case m1: Map[Int, V] => new java.util.HashMap[Integer, V] + case m2: Map[T, Int] => new java.util.HashMap[T, Integer] + case _ => new java.util.HashMap[T, V] + } + } +}
\ No newline at end of file diff --git a/test/files/pos/t3612.scala b/test/files/pos/t3612.scala new file mode 100644 index 0000000000..d3bcc373e3 --- /dev/null +++ b/test/files/pos/t3612.scala @@ -0,0 +1,6 @@ +trait C + +class Outer { + object O0 extends C {} + object O extends C { self => } +}
\ No newline at end of file |