diff options
author | Martin Odersky <odersky@gmail.com> | 2008-09-04 16:33:51 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2008-09-04 16:33:51 +0000 |
commit | 3e0cd7e7488477e60eb8d12ffeea1b3dc02433a5 (patch) | |
tree | 39e044e9c789277e68c89a72533088dc4190e8d7 | |
parent | 743edeefd44939ada6e81c5936cc04f7c6601931 (diff) | |
download | scala-3e0cd7e7488477e60eb8d12ffeea1b3dc02433a5.tar.gz scala-3e0cd7e7488477e60eb8d12ffeea1b3dc02433a5.tar.bz2 scala-3e0cd7e7488477e60eb8d12ffeea1b3dc02433a5.zip |
now checking for volatile types.
-rw-r--r-- | build.xml | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/CompileClient.scala | 38 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Symbols.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Types.scala | 28 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 38 | ||||
-rw-r--r-- | test/files/neg/bug412.check | 9 | ||||
-rw-r--r-- | test/files/neg/bug414.check | 5 | ||||
-rw-r--r-- | test/files/neg/bug692.check | 14 | ||||
-rw-r--r-- | test/files/neg/bug692.scala | 4 | ||||
-rw-r--r-- | test/files/neg/bug961.check | 7 | ||||
-rw-r--r-- | test/files/neg/null-unsoundness.check | 5 | ||||
-rw-r--r-- | test/files/neg/null-unsoundness.scala | 15 | ||||
-rw-r--r-- | test/files/pos/jesper.scala | 30 | ||||
-rw-r--r-- | test/files/pos/typesafecons.scala | 30 |
15 files changed, 190 insertions, 42 deletions
@@ -147,10 +147,10 @@ INITIALISATION <condition property="os.win"> <os family="windows"/> </condition> - <!-- Finding out SVN revision --> + <!-- Finding out SVN revision <exec executable="svn" outputproperty="svn.out"> <arg line=" info ${basedir}"/> - </exec> + </exec> --> <propertyregex property="svn.number" input="${svn.out}" select="\1" regexp="Revision: ([0-9]+)" diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala index 6bac62aefd..55a561b8cf 100644 --- a/src/compiler/scala/tools/nsc/CompileClient.scala +++ b/src/compiler/scala/tools/nsc/CompileClient.scala @@ -93,26 +93,30 @@ class StandardCompileClient { } val socket = if (serverAdr == "") compileSocket.getOrCreateSocket(vmArgs, !shutdown) else compileSocket.getSocket(serverAdr) - if (shutdown && (socket==null)) { - Console.println("[No compilation server running.]") - return 0 - } - val out = new PrintWriter(socket.getOutputStream(), true) - val in = new BufferedReader(new InputStreamReader(socket.getInputStream())) - out.println(compileSocket.getPassword(socket.getPort())) - out.println(args.mkString("", "\0", "")) var sawerror = false - var fromServer = in.readLine() - while (fromServer ne null) { - if (compileSocket.errorPattern.matcher(fromServer).matches) + if (socket == 0) { + if (shutdown) { + Console.println("[No compilation server running.]") + } else { + Console.println("Compilation failed.") sawerror = true - Console.println(fromServer) - fromServer = in.readLine() + } + } else { + val out = new PrintWriter(socket.getOutputStream(), true) + val in = new BufferedReader(new InputStreamReader(socket.getInputStream())) + out.println(compileSocket.getPassword(socket.getPort())) + out.println(args.mkString("", "\0", "")) + var fromServer = in.readLine() + while (fromServer ne null) { + if (compileSocket.errorPattern.matcher(fromServer).matches) + sawerror = true + Console.println(fromServer) + fromServer = in.readLine() + } + in.close() + out.close() + socket.close() } - in.close() - out.close() - socket.close() - if (sawerror) 1 else 0 } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ed68e42341..5f2b5c76c8 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1703,6 +1703,9 @@ trait Parsers extends NewScanners with MarkupParsers { val vds = new ListBuffer[List[ValDef]] val pos = inCurrentPos newLineOptWhenFollowedBy(LPAREN) + if (ofCaseClass && inToken != LPAREN) + deprecationWarning(in.currentPos, "case classes without a parameter list have been deprecated;\n"+ + "use either case objects or case classes with `()' as parameter list.") while (implicitmod == 0 && inToken == LPAREN) { inNextToken vds += paramClause() diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index 59854137ed..3365555ed6 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -268,7 +268,7 @@ trait Symbols { /** Does this symbol denote a stable value? */ final def isStable = - isTerm && !hasFlag(MUTABLE) && (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)) + isTerm && !hasFlag(MUTABLE) && (!hasFlag(METHOD | BYNAMEPARAM) || hasFlag(STABLE)) && !tpe.isVolatile def isDeferred = hasFlag(DEFERRED) && !isClass diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 1b93f17cd0..f50f82d5b1 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -143,6 +143,7 @@ trait Types { override def isError = underlying.isError override def isErroneous = underlying.isErroneous override def isStable: Boolean = underlying.isStable + override def isVolatile = underlying.isVolatile override def finalResultType = underlying.finalResultType override def paramSectionCount = underlying.paramSectionCount override def paramTypes = underlying.paramTypes @@ -210,6 +211,14 @@ trait Types { /** Does this type denote a stable reference (i.e. singleton type)? */ def isStable: Boolean = false + /** Is this type dangerous (i.e. it might contain conflicting + * type information when empty, so that it can be constructed + * so that type unsoundness results.) A dangerous type has an underlying + * type of the form T_1 with T_n { decls }, where one of the + * T_i (i > 1) is an abstract type. + */ + def isVolatile: Boolean = false + /** Is this type guaranteed not to have `null' as a value? */ def isNotNull: Boolean = false @@ -840,7 +849,8 @@ trait Types { abstract class SingletonType extends SubType with SimpleTypeProxy { def supertype = underlying override def isTrivial = false - override def isStable = true + override def isStable = !underlying.isVolatile + override def isVolatile = underlying.isVolatile override def widen: Type = underlying.widen override def baseTypeSeq: BaseTypeSeq = { if (util.Statistics.enabled) singletonBaseTypeSeqCount += 1 @@ -934,6 +944,7 @@ trait Types { override def isStable = true override def safeToString = "<param "+level+"."+paramId+">" override def kind = "DeBruijnIndex" + // todo: this should be a subtype, which forwards to underlying } /** A class for singleton types of the form <prefix>.<sym.name>.type. @@ -1148,6 +1159,11 @@ trait Types { decls)) else super.normalize + override def isVolatile = + !parents.isEmpty && + (!parents.tail.isEmpty || !decls.isEmpty) && + (parents exists (_.typeSymbol.isAbstractType)) + override def kind = "RefinedType" } @@ -1337,6 +1353,10 @@ trait Types { sym.isAbstractType && (sym.info.bounds.hi.typeSymbol isSubClass SingletonClass) } + override def isVolatile: Boolean = { + sym.isAbstractType && transform(sym.info.bounds.hi).isVolatile || + sym.isAliasType && sym.info.normalize.isVolatile + } override val isTrivial: Boolean = pre.isTrivial && !sym.isTypeParameter && args.forall(_.isTrivial) @@ -1621,6 +1641,7 @@ A type's typeSymbol should never be inspected directly. override def baseClasses: List[Symbol] = resultType.baseClasses override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) override def narrow: Type = resultType.narrow + override def isVolatile = resultType.isVolatile override def deconst = if (inIDE) PolyType(typeParams, resultType.deconst) @@ -1798,6 +1819,7 @@ A type's typeSymbol should never be inspected directly. else constr.inst.toString } override def isStable = origin.isStable + override def isVolatile = origin.isVolatile override def kind = "TypeVar" } @@ -3627,8 +3649,10 @@ A type's typeSymbol should never be inspected directly. true case (_, RefinedType(parents2, ref2)) => (parents2 forall (tp2 => tp1 <:< tp2)) && - (ref2.toList forall tp1.specializes) && + (ref2.toList forall tp1.specializes) /* && + removed, replaced by stricter condition on stable values. (tp1.typeSymbol != NullClass || !parents2.exists(_.typeSymbol.isAbstractType)) +*/ case (ExistentialType(_, _), _) => try { skolemizationLevel += 1 diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 529ec0444c..406e056128 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -253,7 +253,32 @@ trait Typers { self: Analyzer => */ def checkStable(tree: Tree): Tree = if (treeInfo.isPureExpr(tree)) tree - else errorTree(tree, "stable identifier required, but " + tree + " found.") + else errorTree( + tree, + "stable identifier required, but "+tree+" found."+ + (if (isStableExceptVolatile(tree)) { + val tpe = tree.symbol.tpe match { + case PolyType(_, rtpe) => rtpe + case t => t + } + "\n Note that "+tree.symbol+" is not stable because its type, "+tree.tpe+", is volatile." + } else "")) + + /** Would tree be a stable (i.e. a pure expression) if the type + * of its symbol was not volatile? + */ + private def isStableExceptVolatile(tree: Tree) = { + tree.hasSymbol && tree.symbol != NoSymbol && tree.tpe.isVolatile && + { val savedTpe = tree.symbol.info + val savedSTABLE = tree.symbol getFlag STABLE + tree.symbol setInfo AnyRefClass.tpe + tree.symbol setFlag STABLE + val result = treeInfo.isPureExpr(tree) + tree.symbol setInfo savedTpe + tree.symbol setFlag savedSTABLE + result + } + } /** Check that `tpt' refers to a non-refinement class type */ def checkClassType(tpt: Tree, existentialOK: Boolean) { @@ -671,7 +696,9 @@ trait Typers { self: Analyzer => (pt <:< functionType(mt.paramTypes map (t => WildcardType), WildcardType)))*/ { // (4.2) if (settings.debug.value) log("eta-expanding "+tree+":"+tree.tpe+" to "+pt) checkParamsConvertible(tree.pos, tree.tpe) - typed(etaExpand(context.unit, tree), mode, pt) + val tree1 = etaExpand(context.unit, tree) +// println("eta "+tree+" ---> "+tree1+":"+tree1.tpe) + typed(tree1, mode, pt) } else if (!meth.isConstructor && mt.paramTypes.isEmpty) { // (4.3) adapt(typed(Apply(tree, List()) setPos tree.pos), mode, pt) } else if (context.implicitsEnabled) { @@ -2430,17 +2457,18 @@ trait Typers { self: Analyzer => .setOriginal(tpt1) /* .setPos(tpt1.pos) */ .setType(appliedType(tpt1.tpe, context.undetparams map (_.tpe))) } + /** 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 && sym.owner.isClass && sym.getter(sym.owner) != NoSymbol) sym = sym.getter(sym.owner) context.tree match { - case ValDef(_, _, _, Apply(Select(`tree`, _), _)) if (sym.isStable) => -// println("narrowing...") + 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 _ => -// println("no narrow: "+sym+" "+sym.isStable+" "+context.tree+"//"+tree) tp } } diff --git a/test/files/neg/bug412.check b/test/files/neg/bug412.check index e275c5d271..f25ad6fa7e 100644 --- a/test/files/neg/bug412.check +++ b/test/files/neg/bug412.check @@ -1,6 +1,5 @@ -bug412.scala:9: error: type mismatch; - found : Null(null) - required: A.this.CX with A.this.C2 - val c: CX with C2 = null; - ^ +bug412.scala:11: error: stable identifier required, but A.this.c found. + Note that value c is not stable because its type, A.this.CX with A.this.C2, is volatile. + def castA(x: c.T): T2 = x; + ^ one error found diff --git a/test/files/neg/bug414.check b/test/files/neg/bug414.check index ec23e26337..c0f039ad26 100644 --- a/test/files/neg/bug414.check +++ b/test/files/neg/bug414.check @@ -1,3 +1,7 @@ +bug414.scala:1: warning: case classes without a parameter list have been deprecated; +use either case objects or case classes with `()' as parameter list. +case class Empty[a] extends IntMap[a]; + ^ bug414.scala:5: error: pattern type is incompatible with expected type; found : object Empty required: IntMap[a] @@ -8,4 +12,5 @@ bug414.scala:7: error: type mismatch; required: a case _ => ^ +one warning found two errors found diff --git a/test/files/neg/bug692.check b/test/files/neg/bug692.check index 308048b80a..099a261f42 100644 --- a/test/files/neg/bug692.check +++ b/test/files/neg/bug692.check @@ -1,19 +1,19 @@ bug692.scala:3: error: not found: type T - trait Type[T0] extends Type0[T]; + trait Type[T0] extends Type0[T];
^ bug692.scala:10: error: class Foo takes type parameters - case class FooType extends ClassType[Foo,AnyRef](ObjectType()); - ^ + case class FooType() extends ClassType[Foo,AnyRef](ObjectType());
+ ^ bug692.scala:13: error: class Foo takes type parameters - case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType); + case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType);
^ bug692.scala:13: error: class Foo takes type parameters - case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType); + case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType);
^ bug692.scala:19: error: class Foo takes type parameters - class Bar[A <: Foo](implicit tpeA : Type[A]) extends Foo; + class Bar[A <: Foo](implicit tpeA : Type[A]) extends Foo;
^ bug692.scala:14: error: class Foo takes type parameters - implicit def typeOfBar[T4 <: Foo](implicit elem : RefType[T4]) : RefType[Bar[T4]] = + implicit def typeOfBar[T4 <: Foo](implicit elem : RefType[T4]) : RefType[Bar[T4]] =
^ 6 errors found diff --git a/test/files/neg/bug692.scala b/test/files/neg/bug692.scala index 184a14b4b6..24e1d2fea3 100644 --- a/test/files/neg/bug692.scala +++ b/test/files/neg/bug692.scala @@ -3,11 +3,11 @@ abstract class test3 { trait Type[T0] extends Type0[T]; trait ClassType0[+C <: AnyRef] extends Type0[C]; abstract class RefType[C <: AnyRef] extends Type[C]; - case class ObjectType extends RefType[AnyRef]; + case class ObjectType() extends RefType[AnyRef]; abstract class ClassType[C <: Z, Z <: AnyRef](zuper : RefType[Z]) extends RefType[C]; - case class FooType extends ClassType[Foo,AnyRef](ObjectType()); + case class FooType() extends ClassType[Foo,AnyRef](ObjectType()); implicit def typeOfFoo = FooType(); case class BarType[T3 <: Foo](tpeT : RefType[T3]) extends ClassType[Bar[T3],Foo](FooType); diff --git a/test/files/neg/bug961.check b/test/files/neg/bug961.check index 794acf5519..33bb0559b1 100644 --- a/test/files/neg/bug961.check +++ b/test/files/neg/bug961.check @@ -1,4 +1,9 @@ +bug961.scala:4: warning: case classes without a parameter list have been deprecated; +use either case objects or case classes with `()' as parameter list. + private case class B_inner extends A
+ ^ bug961.scala:11: error: Temp.this.B of type object Temp.B does not take parameters - B() match { + B() match {
^ +one warning found one error found diff --git a/test/files/neg/null-unsoundness.check b/test/files/neg/null-unsoundness.check new file mode 100644 index 0000000000..5f28e76d06 --- /dev/null +++ b/test/files/neg/null-unsoundness.check @@ -0,0 +1,5 @@ +null-unsoundness.scala:8: error: stable identifier required, but A.this.x found. + Note that value x is not stable because its type, A.this.D with A.this.A, is volatile. + var y: x.T = new C("abc") + ^ +one error found diff --git a/test/files/neg/null-unsoundness.scala b/test/files/neg/null-unsoundness.scala new file mode 100644 index 0000000000..d30ff613b1 --- /dev/null +++ b/test/files/neg/null-unsoundness.scala @@ -0,0 +1,15 @@ +class B +class C(x: String) extends B + +class A { + type A >: Null + class D { type T >: C <: B } + val x: D with A = null + var y: x.T = new C("abc") +} +object Test extends A with Application { + class C { type T = Int; val x = 1 } + type A = C + y = 42 +} + diff --git a/test/files/pos/jesper.scala b/test/files/pos/jesper.scala new file mode 100644 index 0000000000..b2a027b0f2 --- /dev/null +++ b/test/files/pos/jesper.scala @@ -0,0 +1,30 @@ +object Pair { + sealed trait Pair { + type First + type Second <: Pair + } + + case class End extends Pair { + type First = Nothing + type Second = End + + def ::[T](v : T) : Cons[T, End] = Cons(v, this) + } + + case object End extends End + + final case class Cons[T1, T2 <: Pair](_1 : T1, _2 : T2) extends Pair { + type First = T1 + type Second = T2 + + def ::[T](v : T) : Cons[T, Cons[T1, T2]] = Cons(v, this) + def find[T](implicit finder : Cons[T1, T2] => T) = finder(this) + } + + implicit def findFirst[T1, T2 <: Pair] : Cons[T1, T2] => T1 = (p : Cons[T1, T2]) => p._1 + implicit def findSecond[T, T1, T2 <: Pair](implicit finder : T2 => T) : Cons[T1, T2] => T = (p : Cons[T1, T2]) => finder(p._2) + + val p : Cons[Int, Cons[Boolean, End]] = 10 :: false :: End +// val x : Boolean = p.find[Boolean](findSecond(findFirst)) + val x2 : Boolean = p.find[Boolean] // Doesn't compile +} diff --git a/test/files/pos/typesafecons.scala b/test/files/pos/typesafecons.scala new file mode 100644 index 0000000000..b2a027b0f2 --- /dev/null +++ b/test/files/pos/typesafecons.scala @@ -0,0 +1,30 @@ +object Pair { + sealed trait Pair { + type First + type Second <: Pair + } + + case class End extends Pair { + type First = Nothing + type Second = End + + def ::[T](v : T) : Cons[T, End] = Cons(v, this) + } + + case object End extends End + + final case class Cons[T1, T2 <: Pair](_1 : T1, _2 : T2) extends Pair { + type First = T1 + type Second = T2 + + def ::[T](v : T) : Cons[T, Cons[T1, T2]] = Cons(v, this) + def find[T](implicit finder : Cons[T1, T2] => T) = finder(this) + } + + implicit def findFirst[T1, T2 <: Pair] : Cons[T1, T2] => T1 = (p : Cons[T1, T2]) => p._1 + implicit def findSecond[T, T1, T2 <: Pair](implicit finder : T2 => T) : Cons[T1, T2] => T = (p : Cons[T1, T2]) => finder(p._2) + + val p : Cons[Int, Cons[Boolean, End]] = 10 :: false :: End +// val x : Boolean = p.find[Boolean](findSecond(findFirst)) + val x2 : Boolean = p.find[Boolean] // Doesn't compile +} |