diff options
author | Martin Odersky <odersky@gmail.com> | 2014-05-07 19:06:17 +0200 |
---|---|---|
committer | Samuel Gruetter <samuel.gruetter@epfl.ch> | 2014-05-20 13:38:49 +0200 |
commit | fb3dba1bac13a755d2304928cbd49e7dde6f1bf9 (patch) | |
tree | a4e712103779decdebc439e33aa2c3f8f16890e8 /tests | |
parent | f7910005038c188e573e8d1a42ff3e31c69c90c1 (diff) | |
download | dotty-fb3dba1bac13a755d2304928cbd49e7dde6f1bf9.tar.gz dotty-fb3dba1bac13a755d2304928cbd49e7dde6f1bf9.tar.bz2 dotty-fb3dba1bac13a755d2304928cbd49e7dde6f1bf9.zip |
Avoid hoisting of local classes out of method bodies.
Doing so is unsound. We instead approximate local classes by their parents, but
only if the expected type is not fully defined.
This makes the test t2421_delitedsl1.scala in the commit pass. The oter test,
blockEscapesNeg.scala is modified to fail. Previously it failed outright but with the
new rules the nested class Bar is approximated to Object. That means that the block
containing `Foo.Bar` typechecks, but with type `Object` instead of the unreachable `Bar`.
Diffstat (limited to 'tests')
-rw-r--r-- | tests/neg/blockescapesNeg.scala | 5 | ||||
-rw-r--r-- | tests/new/t2421_delitedsl.scala | 39 |
2 files changed, 42 insertions, 2 deletions
diff --git a/tests/neg/blockescapesNeg.scala b/tests/neg/blockescapesNeg.scala index 028ef5162..7b448ad23 100644 --- a/tests/neg/blockescapesNeg.scala +++ b/tests/neg/blockescapesNeg.scala @@ -1,5 +1,6 @@ object blockescapesNeg { - def m0 = { object Foo { class Bar } ; new Foo.Bar } + def m0 = { object Foo { class Bar { val field = 2 }} ; new Foo.Bar } + m0.field class A[T] def m1 = { val x = 1; new A[x.type]} -}
\ No newline at end of file +} diff --git a/tests/new/t2421_delitedsl.scala b/tests/new/t2421_delitedsl.scala new file mode 100644 index 000000000..554702a03 --- /dev/null +++ b/tests/new/t2421_delitedsl.scala @@ -0,0 +1,39 @@ +trait DeliteDSL { + abstract class <~<[-From, +To] extends (From => To) + implicit def trivial[A]: A <~< A = new (A <~< A) {def apply(x: A) = x} + + trait Forcible[T] + object Forcible { + def factory[T](f: T => Forcible[T]) = new (T <~< Forcible[T]){def apply(x: T) = f(x)} + } + + case class DeliteInt(x: Int) extends Forcible[Int] + + implicit val forcibleInt: DeliteDSL.this.<~<[Int,DeliteDSL.this.Forcible[Int]] = + Forcible.factory((x: Int) => DeliteInt(x)) + + import scala.collection.Traversable + class DeliteCollection[T](val xs: Traversable[T]) { + // must use existential in bound of P, instead of T itself, because we cannot both have: + // Test.x below: DeliteCollection[T=Int] -> P=DeliteInt <: Forcible[T=Int], as T=Int <~< P=DeliteInt + // Test.xAlready below: DeliteCollection[T=DeliteInt] -> P=DeliteInt <: Forcible[T=DeliteInt], as T=DeliteInt <~< P=DeliteInt + // this would required DeliteInt <: Forcible[Int] with Forcible[DeliteInt] + + def headProxy[P <: Forcible[_]](implicit w: T <~< P): P = xs.head + } + // If T is already a proxy (it is forcible), the compiler should use + // forcibleIdentity to deduce that P=T. If T is Int, the compiler + // should use intToForcible to deduce that P=DeliteInt. + // + // Without this feature, the user must write 'xs.proxyOfFirst[DeliteInt]', + // with the feature they can write 'xs.proxyOfFirst', which is shorter and + // avoids exposing internal DELITE types to the world. + + object Test { + val x = new DeliteCollection(List(1,2,3)).headProxy + // inferred: val x: Forcible[Int] = new DeliteCollection[Int](List.apply[Int](1, 2, 3)).headProxy[Forcible[Int]](forcibleInt); + + val xAlready = new DeliteCollection(List(DeliteInt(1),DeliteInt(2),DeliteInt(3))).headProxy + // inferred: val xAlready: DeliteInt = new DeliteCollection[DeliteInt](List.apply[DeliteInt](DeliteInt(1), DeliteInt(2), DeliteInt(3))).headProxy[DeliteInt](trivial[DeliteInt]); + } +} |