Project: Scala
Revision: 16024
Author: odersky
Date: 04 Sep 2008 12:33:51
Diff at Trac: https://lampsvn.epfl.ch/trac/scala/changeset/16024
Changes:now checking for volatile types. parameterless type classes are now deprecated.
Files:modified: /scala/trunk/test/files/neg/bug412.check (
try)
modified: /scala/trunk/test/files/neg/bug692.scala (
try)
modified: /scala/trunk/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala (
try)
modified: /scala/trunk/src/compiler/scala/tools/nsc/symtab/Types.scala (
try)
modified: /scala/trunk/test/files/neg/bug414.check (
try)
added: /scala/trunk/test/files/pos/jesper.scala (
try)
modified: /scala/trunk/build.xml (
try)
modified: /scala/trunk/src/compiler/scala/tools/nsc/CompileClient.scala (
try)
modified: /scala/trunk/src/compiler/scala/tools/nsc/symtab/Symbols.scala (
try)
added: /scala/trunk/test/files/neg/null-unsoundness.check (
try)
modified: /scala/trunk/test/files/neg/bug961.check (
try)
modified: /scala/trunk/test/files/neg/bug692.check (
try)
modified: /scala/trunk/src/compiler/scala/tools/nsc/typechecker/Typers.scala (
try)
added: /scala/trunk/test/files/pos/typesafecons.scala (
try)
added: /scala/trunk/test/files/neg/null-unsoundness.scala (
try)
Diff:
| ... | ...@@ -1,6 +1,5 @@ |
| 1 | | bug412.scala:9: error: type mismatch; |
| 2 | | found : Null(null) |
| 3 | | required: A.this.CX with A.this.C2 |
| 4 | | val c: CX with C2 = null; |
| 5 | | ^ |
| 1 | bug412.scala:11: error: stable identifier required, but A.this.c found. |
| 2 | Note that value c is not stable because its type, A.this.CX with A.this.C2, is volatile. |
| 3 | def castA(x: c.T): T2 = x; |
| 4 | ^ |
| 6 | 5 | one error found |
| ... | ...@@ -3,11 +3,11 @@ |
| 3 | 3 | trait Type[T0] extends Type0[T]; |
| 4 | 4 | trait ClassType0[+C <: AnyRef] extends Type0[C]; |
| 5 | 5 | abstract class RefType[C <: AnyRef] extends Type[C]; |
| 6 | | case class ObjectType extends RefType[AnyRef]; |
| 6 | case class ObjectType() extends RefType[AnyRef]; |
| 7 | 7 | abstract class ClassType[C <: Z, Z <: AnyRef](zuper : RefType[Z]) extends RefType[C]; |
| 8 | 8 | |
| 9 | 9 | |
| 10 | | case class FooType extends ClassType[Foo,AnyRef](ObjectType()); |
| 10 | case class FooType() extends ClassType[Foo,AnyRef](ObjectType()); |
| 11 | 11 | implicit def typeOfFoo = FooType(); |
| 12 | 12 | |
| 13 | 13 | case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType); |
| ... | ...@@ -1703,6 +1703,9 @@ |
| 1703 | 1703 | val vds = new ListBuffer[List[ValDef]] |
| 1704 | 1704 | val pos = inCurrentPos |
| 1705 | 1705 | newLineOptWhenFollowedBy(LPAREN) |
| 1706 | if (ofCaseClass && inToken != LPAREN) |
| 1707 | deprecationWarning(in.currentPos, "case classes without a parameter list have been deprecated;\n"+ |
| 1708 | "use either case objects or case classes with `()' as parameter list.") |
| 1706 | 1709 | while (implicitmod == 0 && inToken == LPAREN) { |
| 1707 | 1710 | inNextToken |
| 1708 | 1711 | vds += paramClause() |
| ... | ...@@ -143,6 +143,7 @@ |
| 143 | 143 | override def isError = underlying.isError |
| 144 | 144 | override def isErroneous = underlying.isErroneous |
| 145 | 145 | override def isStable: Boolean = underlying.isStable |
| 146 | override def isVolatile = underlying.isVolatile |
| 146 | 147 | override def finalResultType = underlying.finalResultType |
| 147 | 148 | override def paramSectionCount = underlying.paramSectionCount |
| 148 | 149 | override def paramTypes = underlying.paramTypes |
| ... | ...@@ -210,6 +211,14 @@ |
| 210 | 211 | /** Does this type denote a stable reference (i.e. singleton type)? */ |
| 211 | 212 | def isStable: Boolean = false |
| 212 | 213 | |
| 214 | /** Is this type dangerous (i.e. it might contain conflicting |
| 215 | * type information when empty, so that it can be constructed |
| 216 | * so that type unsoundness results.) A dangerous type has an underlying |
| 217 | * type of the form T_1 with T_n { decls }, where one of the |
| 218 | * T_i (i > 1) is an abstract type. |
| 219 | */ |
| 220 | def isVolatile: Boolean = false |
| 221 | |
| 213 | 222 | /** Is this type guaranteed not to have `null' as a value? */ |
| 214 | 223 | def isNotNull: Boolean = false |
| 215 | 224 | |
| ... | ...@@ -840,7 +849,8 @@ |
| 840 | 849 | abstract class SingletonType extends SubType with SimpleTypeProxy { |
| 841 | 850 | def supertype = underlying |
| 842 | 851 | override def isTrivial = false |
| 843 | | override def isStable = true |
| 852 | override def isStable = !underlying.isVolatile |
| 853 | override def isVolatile = underlying.isVolatile |
| 844 | 854 | override def widen: Type = underlying.widen |
| 845 | 855 | override def baseTypeSeq: BaseTypeSeq = { |
| 846 | 856 | if (util.Statistics.enabled) singletonBaseTypeSeqCount += 1 |
| ... | ...@@ -934,6 +944,7 @@ |
| 934 | 944 | override def isStable = true |
| 935 | 945 | override def safeToString = "<param "+level+"."+paramId+">" |
| 936 | 946 | override def kind = "DeBruijnIndex" |
| 947 | // todo: this should be a subtype, which forwards to underlying |
| 937 | 948 | } |
| 938 | 949 | |
| 939 | 950 | /** A class for singleton types of the form <prefix>.<sym.name>.type. |
| ... | ...@@ -1148,6 +1159,11 @@ |
| 1148 | 1159 | decls)) |
| 1149 | 1160 | else super.normalize |
| 1150 | 1161 | |
| 1162 | override def isVolatile = |
| 1163 | !parents.isEmpty && |
| 1164 | (!parents.tail.isEmpty || !decls.isEmpty) && |
| 1165 | (parents exists (_.typeSymbol.isAbstractType)) |
| 1166 | |
| 1151 | 1167 | override def kind = "RefinedType" |
| 1152 | 1168 | } |
| 1153 | 1169 | |
| ... | ...@@ -1337,6 +1353,10 @@ |
| 1337 | 1353 | sym.isAbstractType && (sym.info.bounds.hi.typeSymbol isSubClass SingletonClass) |
| 1338 | 1354 | } |
| 1339 | 1355 | |
| 1356 | override def isVolatile: Boolean = { |
| 1357 | sym.isAbstractType && transform(sym.info.bounds.hi).isVolatile || |
| 1358 | sym.isAliasType && sym.info.normalize.isVolatile |
| 1359 | } |
| 1340 | 1360 | override val isTrivial: Boolean = |
| 1341 | 1361 | pre.isTrivial && !sym.isTypeParameter && args.forall(_.isTrivial) |
| 1342 | 1362 | |
| ... | ...@@ -1621,6 +1641,7 @@ |
| 1621 | 1641 | override def baseClasses: List[Symbol] = resultType.baseClasses |
| 1622 | 1642 | override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) |
| 1623 | 1643 | override def narrow: Type = resultType.narrow |
| 1644 | override def isVolatile = resultType.isVolatile |
| 1624 | 1645 | |
| 1625 | 1646 | override def deconst = |
| 1626 | 1647 | if (inIDE) PolyType(typeParams, resultType.deconst) |
| ... | ...@@ -1798,6 +1819,7 @@ |
| 1798 | 1819 | else constr.inst.toString |
| 1799 | 1820 | } |
| 1800 | 1821 | override def isStable = origin.isStable |
| 1822 | override def isVolatile = origin.isVolatile |
| 1801 | 1823 | override def kind = "TypeVar" |
| 1802 | 1824 | } |
| 1803 | 1825 | |
| ... | ...@@ -3627,8 +3649,10 @@ |
| 3627 | 3649 | true |
| 3628 | 3650 | case (_, RefinedType(parents2, ref2)) => |
| 3629 | 3651 | (parents2 forall (tp2 => tp1 <:< tp2)) && |
| 3630 | | (ref2.toList forall tp1.specializes) && |
| 3652 | (ref2.toList forall tp1.specializes) /* && |
| 3653 | removed, replaced by stricter condition on stable values. |
| 3631 | 3654 | (tp1.typeSymbol != NullClass || !parents2.exists(_.typeSymbol.isAbstractType)) |
| 3655 | */ |
| 3632 | 3656 | case (ExistentialType(_, _), _) => |
| 3633 | 3657 | try { |
| 3634 | 3658 | skolemizationLevel += 1 |
| ... | ...@@ -1,3 +1,7 @@ |
| 1 | bug414.scala:1: warning: case classes without a parameter list have been deprecated; |
| 2 | use either case objects or case classes with `()' as parameter list. |
| 3 | case class Empty[a] extends IntMap[a]; |
| 4 | ^ |
| 1 | 5 | bug414.scala:5: error: pattern type is incompatible with expected type; |
| 2 | 6 | found : object Empty |
| 3 | 7 | required: IntMap[a] |
| ... | ...@@ -8,4 +12,5 @@ |
| 8 | 12 | required: a |
| 9 | 13 | case _ => |
| 10 | 14 | ^ |
| 15 | one warning found |
| 11 | 16 | two errors found |
| ... | ...@@ -0,0 +1,30 @@ |
| 1 | object Pair { |
| 2 | sealed trait Pair { |
| 3 | type First |
| 4 | type Second <: Pair |
| 5 | } |
| 6 | |
| 7 | case class End extends Pair { |
| 8 | type First = Nothing |
| 9 | type Second = End |
| 10 | |
| 11 | def ::[T](v : T) : Cons[T, End] = Cons(v, this) |
| 12 | } |
| 13 | |
| 14 | case object End extends End |
| 15 | |
| 16 | final case class Cons[T1, T2 <: Pair](_1 : T1, _2 : T2) extends Pair { |
| 17 | type First = T1 |
| 18 | type Second = T2 |
| 19 | |
| 20 | def ::[T](v : T) : Cons[T, Cons[T1, T2]] = Cons(v, this) |
| 21 | def find[T](implicit finder : Cons[T1, T2] => T) = finder(this) |
| 22 | } |
| 23 | |
| 24 | implicit def findFirst[T1, T2 <: Pair] : Cons[T1, T2] => T1 = (p : Cons[T1, T2]) => p._1 |
| 25 | implicit def findSecond[T, T1, T2 <: Pair](implicit finder : T2 => T) : Cons[T1, T2] => T = (p : Cons[T1, T2]) => finder(p._2) |
| 26 | |
| 27 | val p : Cons[Int, Cons[Boolean, End]] = 10 :: false :: End |
| 28 | // val x : Boolean = p.find[Boolean](findSecond(findFirst)) |
| 29 | val x2 : Boolean = p.find[Boolean] // Doesn't compile |
| 30 | } |
| ... | ...@@ -147,10 +147,10 @@ |
| 147 | 147 | <condition property="os.win"> |
| 148 | 148 | <os family="windows"/> |
| 149 | 149 | </condition> |
| 150 | | <!-- Finding out SVN revision --> |
| 150 | <!-- Finding out SVN revision |
| 151 | 151 | <exec executable="svn" outputproperty="svn.out"> |
| 152 | 152 | <arg line=" info ${basedir}"/> |
| 153 | | </exec> |
| 153 | </exec> --> |
| 154 | 154 | <propertyregex |
| 155 | 155 | property="svn.number" input="${svn.out}" select="\1" |
| 156 | 156 | regexp="Revision: ([0-9]+)" |
| ... | ...@@ -93,26 +93,30 @@ |
| 93 | 93 | } |
| 94 | 94 | val socket = if (serverAdr == "") compileSocket.getOrCreateSocket(vmArgs, !shutdown) |
| 95 | 95 | else compileSocket.getSocket(serverAdr) |
| 96 | | if (shutdown && (socket==null)) { |
| 97 | | Console.println("[No compilation server running.]") |
| 98 | | return 0 |
| 99 | | } |
| 100 | | val out = new PrintWriter(socket.getOutputStream(), true) |
| 101 | | val in = new BufferedReader(new InputStreamReader(socket.getInputStream())) |
| 102 | | out.println(compileSocket.getPassword(socket.getPort())) |
| 103 | | out.println(args.mkString("", "\0", "")) |
| 104 | 96 | var sawerror = false |
| 105 | | var fromServer = in.readLine() |
| 106 | | while (fromServer ne null) { |
| 107 | | if (compileSocket.errorPattern.matcher(fromServer).matches) |
| 97 | if (socket == 0) { |
| 98 | if (shutdown) { |
| 99 | Console.println("[No compilation server running.]") |
| 100 | } else { |
| 101 | Console.println("Compilation failed.") |
| 108 | 102 | sawerror = true |
| 109 | | Console.println(fromServer) |
| 110 | | fromServer = in.readLine() |
| 103 | } |
| 104 | } else { |
| 105 | val out = new PrintWriter(socket.getOutputStream(), true) |
| 106 | val in = new BufferedReader(new InputStreamReader(socket.getInputStream())) |
| 107 | out.println(compileSocket.getPassword(socket.getPort())) |
| 108 | out.println(args.mkString("", "\0", "")) |
| 109 | var fromServer = in.readLine() |
| 110 | while (fromServer ne null) { |
| 111 | if (compileSocket.errorPattern.matcher(fromServer).matches) |
| 112 | sawerror = true |
| 113 | Console.println(fromServer) |
| 114 | fromServer = in.readLine() |
| 115 | } |
| 116 | in.close() |
| 117 | out.close() |
| 118 | socket.close() |
| 111 | 119 | } |
| 112 | | in.close() |
| 113 | | out.close() |
| 114 | | socket.close() |
| 115 | | |
| 116 | 120 | if (sawerror) 1 else 0 |
| 117 | 121 | } |
| 118 | 122 | |
| ... | ...@@ -268,7 +268,7 @@ |
| 268 | 268 | |
| 269 | 269 | /** Does this symbol denote a stable value? */ |
| 270 | 270 | final def isStable = |
| 271 | | isTerm && !hasFlag(MUTABLE) && (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)) |
| 271 | isTerm && !hasFlag(MUTABLE) && (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)) && !tpe.isVolatile |
| 272 | 272 | |
| 273 | 273 | def isDeferred = |
| 274 | 274 | hasFlag(DEFERRED) && !isClass |
| ... | ...@@ -0,0 +1,5 @@ |
| 1 | null-unsoundness.scala:8: error: stable identifier required, but A.this.x found. |
| 2 | Note that value x is not stable because its type, A.this.D with A.this.A, is volatile. |
| 3 | var y: x.T = new C("abc") |
| 4 | ^ |
| 5 | one error found |
| ... | ...@@ -1,4 +1,9 @@ |
| 1 | bug961.scala:4: warning: case classes without a parameter list have been deprecated; |
| 2 | use either case objects or case classes with `()' as parameter list. |
| 3 | private case class B_inner extends A
|
| 4 | ^ |
| 1 | 5 | bug961.scala:11: error: Temp.this.B of type object Temp.B does not take parameters |
| 2 | | B() match { |
| 6 | B() match {
|
| 3 | 7 | ^ |
| 8 | one warning found |
| 4 | 9 | one error found |
| ... | ...@@ -1,19 +1,19 @@ |
| 1 | 1 | bug692.scala:3: error: not found: type T |
| 2 | | trait Type[T0] extends Type0[T]; |
| 2 | trait Type[T0] extends Type0[T];
|
| 3 | 3 | ^ |
| 4 | 4 | bug692.scala:10: error: class Foo takes type parameters |
| 5 | | case class FooType extends ClassType[Foo,AnyRef](ObjectType()); |
| 6 | | ^ |
| 5 | case class FooType() extends ClassType[Foo,AnyRef](ObjectType());
|
| 6 | ^ |
| 7 | 7 | bug692.scala:13: error: class Foo takes type parameters |
| 8 | | case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType); |
| 8 | case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType);
|
| 9 | 9 | ^ |
| 10 | 10 | bug692.scala:13: error: class Foo takes type parameters |
| 11 | | case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType); |
| 11 | case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType);
|
| 12 | 12 | ^ |
| 13 | 13 | bug692.scala:19: error: class Foo takes type parameters |
| 14 | | class Bar[A <: Foo](implicit tpeA : Type[A]) extends Foo; |
| 14 | class Bar[A <: Foo](implicit tpeA : Type[A]) extends Foo;
|
| 15 | 15 | ^ |
| 16 | 16 | bug692.scala:14: error: class Foo takes type parameters |
| 17 | | implicit def typeOfBar[T4 <: Foo](implicit elem : RefType[T4]) : RefType[Bar[T4]] = |
| 17 | implicit def typeOfBar[T4 <: Foo](implicit elem : RefType[T4]) : RefType[Bar[T4]] =
|
| 18 | 18 | ^ |
| 19 | 19 | 6 errors found |
| ... | ...@@ -253,7 +253,32 @@ |
| 253 | 253 | */ |
| 254 | 254 | def checkStable(tree: Tree): Tree = |
| 255 | 255 | if (treeInfo.isPureExpr(tree)) tree |
| 256 | | else errorTree(tree, "stable identifier required, but " + tree + " found.") |
| 256 | else errorTree( |
| 257 | tree, |
| 258 | "stable identifier required, but "+tree+" found."+ |
| 259 | (if (isStableExceptVolatile(tree)) { |
| 260 | val tpe = tree.symbol.tpe match { |
| 261 | case PolyType(_, rtpe) => rtpe |
| 262 | case t => t |
| 263 | } |
| 264 | "\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile." |
| 265 | } else "")) |
| 266 | |
| 267 | /** Would tree be a stable (i.e. a pure expression) if the type |
| 268 | * of its symbol was not volatile? |
| 269 | */ |
| 270 | private def isStableExceptVolatile(tree: Tree) = { |
| 271 | tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile && |
| 272 | { val savedTpe = tree.symbol.info |
| 273 | val savedSTABLE = tree.symbol getFlag STABLE |
| 274 | tree.symbol setInfo AnyRefClass.tpe |
| 275 | tree.symbol setFlag STABLE |
| 276 | val result = treeInfo.isPureExpr(tree) |
| 277 | tree.symbol setInfo savedTpe |
| 278 | tree.symbol setFlag savedSTABLE |
| 279 | result |
| 280 | } |
| 281 | } |
| 257 | 282 | |
| 258 | 283 | /** Check that `tpt' refers to a non-refinement class type */ |
| 259 | 284 | def checkClassType(tpt: Tree, existentialOK: Boolean) { |
| ... | ...@@ -671,7 +696,9 @@ |
| 671 | 696 | (pt <:< functionType(mt.paramTypes map (t => WildcardType), WildcardType)))*/ { // (4.2) |
| 672 | 697 | if (settings.debug.value) log("eta-expanding "+tree+":"+tree.tpe+" to "+pt) |
| 673 | 698 | checkParamsConvertible(tree.pos, tree.tpe) |
| 674 | | typed(etaExpand(context.unit, tree), mode, pt) |
| 699 | val tree1 = etaExpand(context.unit, tree) |
| 700 | // println("eta "+tree+" ---> "+tree1+":"+tree1.tpe) |
| 701 | typed(tree1, mode, pt) |
| 675 | 702 | } else if (!meth.isConstructor && mt.paramTypes.isEmpty) { // (4.3) |
| 676 | 703 | adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt) |
| 677 | 704 | } else if (context.implicitsEnabled) { |
| ... | ...@@ -2430,17 +2457,18 @@ |
| 2430 | 2457 | .setOriginal(tpt1) /* .setPos(tpt1.pos) */ |
| 2431 | 2458 | .setType(appliedType(tpt1.tpe, context.undetparams map (_.tpe))) |
| 2432 | 2459 | } |
| 2460 | /** If current tree <tree> appears in <val x(: T)? = <tree>> |
| 2461 | * return `tp with x.type' else return `tp'. |
| 2462 | */ |
| 2433 | 2463 | def narrowRhs(tp: Type) = { |
| 2434 | 2464 | var sym = context.tree.symbol |
| 2435 | 2465 | if (sym != null && sym != NoSymbol && sym.owner.isClass && sym.getter(sym.owner) != NoSymbol) |
| 2436 | 2466 | sym = sym.getter(sym.owner) |
| 2437 | 2467 | context.tree match { |
| 2438 | | case ValDef(_, _, _, Apply(Select(`tree`, _), _)) if (sym.isStable) => |
| 2439 | | // println("narrowing...") |
| 2468 | case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !(mods hasFlag MUTABLE) => |
| 2440 | 2469 | val pre = if (sym.owner.isClass) sym.owner.thisType else NoPrefix |
| 2441 | 2470 | intersectionType(List(tp, singleType(pre, sym))) |
| 2442 | 2471 | case _ => |
| 2443 | | // println("no narrow: "+sym+" "+sym.isStable+" "+context.tree+"//"+tree) |
| 2444 | 2472 | tp |
| 2445 | 2473 | } |
| 2446 | 2474 | } |
| ... | ...@@ -0,0 +1,30 @@ |
| 1 | object Pair { |
| 2 | sealed trait Pair { |
| 3 | type First |
| 4 | type Second <: Pair |
| 5 | } |
| 6 | |
| 7 | case class End extends Pair { |
| 8 | type First = Nothing |
| 9 | type Second = End |
| 10 | |
| 11 | def ::[T](v : T) : Cons[T, End] = Cons(v, this) |
| 12 | } |
| 13 | |
| 14 | case object End extends End |
| 15 | |
| 16 | final case class Cons[T1, T2 <: Pair](_1 : T1, _2 : T2) extends Pair { |
| 17 | type First = T1 |
| 18 | type Second = T2 |
| 19 | |
| 20 | def ::[T](v : T) : Cons[T, Cons[T1, T2]] = Cons(v, this) |
| 21 | def find[T](implicit finder : Cons[T1, T2] => T) = finder(this) |
| 22 | } |
| 23 | |
| 24 | implicit def findFirst[T1, T2 <: Pair] : Cons[T1, T2] => T1 = (p : Cons[T1, T2]) => p._1 |
| 25 | implicit def findSecond[T, T1, T2 <: Pair](implicit finder : T2 => T) : Cons[T1, T2] => T = (p : Cons[T1, T2]) => finder(p._2) |
| 26 | |
| 27 | val p : Cons[Int, Cons[Boolean, End]] = 10 :: false :: End |
| 28 | // val x : Boolean = p.find[Boolean](findSecond(findFirst)) |
| 29 | val x2 : Boolean = p.find[Boolean] // Doesn't compile |
| 30 | } |
| ... | ...@@ -0,0 +1,15 @@ |
| 1 | class B |
| 2 | class C(x: String) extends B |
| 3 | |
| 4 | class A { |
| 5 | type A >: Null |
| 6 | class D { type T >: C <: B } |
| 7 | val x: D with A = null |
| 8 | var y: x.T = new C("abc") |
| 9 | } |
| 10 | object Test extends A with Application { |
| 11 | class C { type T = Int; val x = 1 } |
| 12 | type A = C |
| 13 | y = 42 |
| 14 | } |
| 15 | |
To list