aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-01-29 23:46:00 +0100
committerMartin Odersky <odersky@gmail.com>2016-02-09 09:40:52 +0100
commit41f056750990a2e6391eec3436077715041d2b8a (patch)
treeede383d5ec756b7a391e747eac472a1dc8f77755
parent633e2ebfd42af65f8324aec87a2444bb9cec5eff (diff)
downloaddotty-41f056750990a2e6391eec3436077715041d2b8a.tar.gz
dotty-41f056750990a2e6391eec3436077715041d2b8a.tar.bz2
dotty-41f056750990a2e6391eec3436077715041d2b8a.zip
Use isRealizable to identify stable prefixes
Replaces isVolatile, which is too weak (and more complicated). Backwards compatibility with Scala2 is ensured by dropping the requirement in Scala2 mode. Fixes #1047, which now compiles without inifinite recursion.
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala4
-rw-r--r--src/dotty/tools/dotc/core/Types.scala17
-rw-r--r--test/dotc/tests.scala2
-rw-r--r--tests/neg/cycles.scala18
-rw-r--r--tests/pos/i1047.scala31
5 files changed, 61 insertions, 11 deletions
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index ccbbbe1b9..9a432829b 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -522,7 +522,9 @@ object SymDenotations {
final def isStable(implicit ctx: Context) = {
val isUnstable =
(this is UnstableValue) ||
- is(Lazy) && ctx.isVolatile(info) && !hasAnnotation(defn.UncheckedStableAnnot)
+ is(Lazy, butNot = Module) &&
+ !(info.isRealizable || ctx.scala2Mode) &&
+ !hasAnnotation(defn.UncheckedStableAnnot)
(this is Stable) || isType || {
if (isUnstable) false
else { setFlag(Stable); true }
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 47a4f088f..1d4e80601 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -127,6 +127,23 @@ object Types {
false
}
+ /** Is this type realizable in all contexts? */
+ def isRealizable(implicit ctx: Context): Boolean = dealias match {
+ case tp: TermRef => tp.symbol.isStable
+ case tp: SingletonType => true
+ case tp =>
+ def isConcrete(tp: Type): Boolean = tp.dealias match {
+ case tp: TypeRef => tp.symbol.isClass
+ case tp: TypeProxy => isConcrete(tp.underlying)
+ case tp: AndOrType => isConcrete(tp.tp1) && isConcrete(tp.tp2)
+ case _ => false
+ }
+ isConcrete(tp) && tp.abstractTypeMembers.forall { m =>
+ val bounds = m.info.bounds
+ bounds.lo <:< bounds.hi
+ }
+ }
+
/** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`?
* Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types
*/
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 549dc582c..13010323e 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -146,7 +146,7 @@ class tests extends CompilerTest {
@Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1)
@Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1)
@Test def neg_clashes = compileFile(negDir, "clashes", xerrors = 2)
- @Test def neg_cycles = compileFile(negDir, "cycles", xerrors = 8)
+ @Test def neg_cycles = compileFile(negDir, "cycles", xerrors = 9)
@Test def neg_boundspropagation = compileFile(negDir, "boundspropagation", xerrors = 5)
@Test def neg_refinedSubtyping = compileFile(negDir, "refinedSubtyping", xerrors = 2)
@Test def neg_hklower = compileFile(negDir, "hklower", xerrors = 3)
diff --git a/tests/neg/cycles.scala b/tests/neg/cycles.scala
index a2b5e9691..77f902bf0 100644
--- a/tests/neg/cycles.scala
+++ b/tests/neg/cycles.scala
@@ -1,39 +1,39 @@
-class Foo[T <: U, U <: T] // error
+class Foo[T <: U, U <: T] // error: cycle
-class Bar[T >: T] // error
+class Bar[T >: T] // error: cycle
class A {
val x: T = ???
- type T <: x.type // error
+ type T <: x.type // error: cycle
}
class B {
- type T <: x.type // error
+ type T <: x.type // error: cycle
val x: T = ???
}
class C {
val x: D#T = ???
class D {
- type T <: x.type // error
+ type T <: x.type // error: cycle
val z: x.type = ???
}
}
class E {
class F {
- type T <: x.type
- val z: x.type = ??? // error
+ type T <: x.type // error: not stable
+ val z: x.type = ??? // error: not stable
}
lazy val x: F#T = ???
}
class T1 {
- type X = (U, U) // error
+ type X = (U, U) // error: cycle
type U = X & Int
}
class T2 {
- type X = (U, U) // error
+ type X = (U, U) // error: cycle
type U = X | Int
}
object T12 {
diff --git a/tests/pos/i1047.scala b/tests/pos/i1047.scala
new file mode 100644
index 000000000..0f9b54135
--- /dev/null
+++ b/tests/pos/i1047.scala
@@ -0,0 +1,31 @@
+package hello
+
+object world extends App {
+ println("hello dotty!")
+
+ trait AnimalPackage {
+ type Animal <: AnimalU
+ type AnimalU = { val age: Int }
+ def newAnimal(a: AnimalU): Animal
+ def newSubAnimal[T](a: AnimalU & T): Animal & T
+ }
+ val p: AnimalPackage = new AnimalPackage { p =>
+ type Animal = AnimalU
+ override def newAnimal(a: AnimalU): Animal = a
+ override def newSubAnimal[T](a: AnimalU & T): Animal & T = a
+ }
+ val lambda: p.Animal = p.newAnimal(new { val age = 1 })
+ trait CatPackage { pc =>
+ type Cat <: p.Animal & pc.CatDelta
+ type CatDelta = { val meow: Int }
+ type CatU = p.AnimalU & pc.CatDelta
+ def newCat(c: CatU): Cat
+ def newSubCat[T](c: CatU & T): Cat & T
+ }
+ val pc: CatPackage = new CatPackage { pc =>
+ type Cat = p.Animal & pc.CatDelta
+ def newCat(c: CatU): Cat = p.newSubAnimal[pc.CatDelta](c)
+ def newSubCat[T](c: CatU & T): Cat & T = p.newSubAnimal[pc.CatDelta & T](c)
+ }
+ val felix: pc.Cat = pc.newCat(new { val age = 1; val meow = 2 })
+}