diff options
author | Martin Odersky <odersky@gmail.com> | 2016-01-29 23:46:00 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-02-09 09:40:52 +0100 |
commit | 41f056750990a2e6391eec3436077715041d2b8a (patch) | |
tree | ede383d5ec756b7a391e747eac472a1dc8f77755 | |
parent | 633e2ebfd42af65f8324aec87a2444bb9cec5eff (diff) | |
download | dotty-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.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 17 | ||||
-rw-r--r-- | test/dotc/tests.scala | 2 | ||||
-rw-r--r-- | tests/neg/cycles.scala | 18 | ||||
-rw-r--r-- | tests/pos/i1047.scala | 31 |
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 }) +} |