From c35d8d29435ff96e8e03d2f46a3765547de222b1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 May 2017 14:23:58 +0200 Subject: [PATCH 1/3] Disallow phantom type parameters in classes --- .../dotty/tools/dotc/typer/TypeAssigner.scala | 8 ++++-- tests/neg/phantom-class-type-parameters.scala | 26 +++++++++++++++++++ tests/{run => neg}/phantom-decls-1.scala | 2 +- tests/{run => neg}/phantom-decls-5.scala | 2 +- tests/run/phantom-decls-1.check | 4 --- tests/run/phantom-decls-5.check | 2 -- 6 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 tests/neg/phantom-class-type-parameters.scala rename tests/{run => neg}/phantom-decls-1.scala (94%) rename tests/{run => neg}/phantom-decls-5.scala (92%) delete mode 100644 tests/run/phantom-decls-1.check delete mode 100644 tests/run/phantom-decls-5.check diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 93bd2528c380..219f6d02e9d7 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -506,8 +506,12 @@ trait TypeAssigner { def assignType(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = tree.withType(symbolicIfNeeded(sym).orElse(sym.termRefWithSig)) - def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = - tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) + def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = { + val tdef = tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) + if (tdef.symbol.owner.isPrimaryConstructor && tdef.tpe.typeSymbol.is(Param) && tdef.tpe.isPhantom) + ctx.error("Classes cannot have phantom type parameters", tdef.pos) + tdef + } private def symbolicIfNeeded(sym: Symbol)(implicit ctx: Context) = { val owner = sym.owner diff --git a/tests/neg/phantom-class-type-parameters.scala b/tests/neg/phantom-class-type-parameters.scala new file mode 100644 index 000000000000..d466a8c18ec4 --- /dev/null +++ b/tests/neg/phantom-class-type-parameters.scala @@ -0,0 +1,26 @@ +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + val a = new Foo[BooAny](any) + foo(a.asInstanceOf[Foo[BooNothing]].x) // should not be possible + foo(a.asInstanceOf[Foo[BooNothing]].y) // should not be possible + + val b = new Foo[BooNothing](a.asInstanceOf[Foo[BooNothing]].x) + b.asInstanceOf[Foo[BooAny]].z(any) // should not be possible + } + + def foo(x: BooNothing) = println("foo") + +} + +class Foo[T <: BooAny](val x: T) { // error + def y: T = x + def z(z: T) = () +} + +object Boo extends Phantom { + type BooAny = this.Any + type BooNothing = this.Nothing + def any: BooAny = assume +} diff --git a/tests/run/phantom-decls-1.scala b/tests/neg/phantom-decls-1.scala similarity index 94% rename from tests/run/phantom-decls-1.scala rename to tests/neg/phantom-decls-1.scala index c2781abf1696..8ccfc31a232d 100644 --- a/tests/run/phantom-decls-1.scala +++ b/tests/neg/phantom-decls-1.scala @@ -13,7 +13,7 @@ object Test { def fun(top: BooAny): Unit = () - class Boo1[P <: BooAny] { + class Boo1[P <: BooAny] { // error println("Boo1") def polyfun1(p1: P): Unit = { println("Boo1.polyfun1") diff --git a/tests/run/phantom-decls-5.scala b/tests/neg/phantom-decls-5.scala similarity index 92% rename from tests/run/phantom-decls-5.scala rename to tests/neg/phantom-decls-5.scala index f5bb1a8ef711..3cc7876412a7 100644 --- a/tests/run/phantom-decls-5.scala +++ b/tests/neg/phantom-decls-5.scala @@ -11,7 +11,7 @@ object Test { new Boo5[Pinky](boo[Pinky]) } - class Boo5[P <: Blinky](p5: P) { + class Boo5[P <: Blinky](p5: P) { // error println("Boo5") } } diff --git a/tests/run/phantom-decls-1.check b/tests/run/phantom-decls-1.check deleted file mode 100644 index b50ae590ec5c..000000000000 --- a/tests/run/phantom-decls-1.check +++ /dev/null @@ -1,4 +0,0 @@ -Boo1 -Boo1.polyfun1 -Boo1 -Boo1.polyfun1 diff --git a/tests/run/phantom-decls-5.check b/tests/run/phantom-decls-5.check deleted file mode 100644 index 2a678b77b83a..000000000000 --- a/tests/run/phantom-decls-5.check +++ /dev/null @@ -1,2 +0,0 @@ -Boo5 -Boo5 From 3e28967f9f0348db5c8605e8ea863a0e9c901702 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 May 2017 15:03:52 +0200 Subject: [PATCH 2/3] Disallow phantom type members in classes --- .../dotty/tools/dotc/typer/TypeAssigner.scala | 6 ++- tests/neg/phantom-bottom.scala | 2 +- tests/neg/phantom-class-type-members.scala | 37 +++++++++++++++++++ tests/neg/phantom-classOf-2.scala | 6 +-- tests/neg/phantom-expr.scala | 5 +-- tests/run/phantom-decls-2.scala | 3 +- tests/run/phantom-decls-3.scala | 14 ++----- 7 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 tests/neg/phantom-class-type-members.scala diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 219f6d02e9d7..fbfaacf82b08 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -508,8 +508,12 @@ trait TypeAssigner { def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = { val tdef = tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) - if (tdef.symbol.owner.isPrimaryConstructor && tdef.tpe.typeSymbol.is(Param) && tdef.tpe.isPhantom) + val isParam = tdef.tpe.typeSymbol.is(Param) + val isPhantom = tdef.tpe.isPhantom + if (isParam && isPhantom && tdef.symbol.owner.isPrimaryConstructor) ctx.error("Classes cannot have phantom type parameters", tdef.pos) + else if (!isParam && isPhantom && !tdef.symbol.owner.isStaticOwner) + ctx.error("Non static classes cannot have phantom type members", tdef.pos) tdef } diff --git a/tests/neg/phantom-bottom.scala b/tests/neg/phantom-bottom.scala index 7f6f2c2ff333..ef224d0b8c4c 100644 --- a/tests/neg/phantom-bottom.scala +++ b/tests/neg/phantom-bottom.scala @@ -10,7 +10,7 @@ class BooFunDef1 { } class Foo { - type Y <: Boo.BooAny + type Y <: Boo.BooAny // error } object Boo extends Phantom { diff --git a/tests/neg/phantom-class-type-members.scala b/tests/neg/phantom-class-type-members.scala new file mode 100644 index 000000000000..2ddcc2e0a927 --- /dev/null +++ b/tests/neg/phantom-class-type-members.scala @@ -0,0 +1,37 @@ +import Boo._ + +object Test { + def main(args: Array[String]): Unit = { + val a = new Bar() + foo(a.asInstanceOf[Foo{type T = BooNothing}].y) // error + + val b = new Baz + b.asInstanceOf[Foo{type T = BooAny}].z(any) // error + } + + def foo(x: BooNothing) = println("foo") + +} + +abstract class Foo { + type T <: BooAny // error + def y: T + def z(z: T) = () +} + +class Bar { + type T = BooAny // error + def y: T = any + def z(z: T) = () +} + +class Baz { + type T = BooNothing // error + def z(z: T) = () +} + +object Boo extends Phantom { + type BooAny = this.Any + type BooNothing = this.Nothing + def any: BooAny = assume +} diff --git a/tests/neg/phantom-classOf-2.scala b/tests/neg/phantom-classOf-2.scala index 1d3b0e3752f3..45d2ed26b0c3 100644 --- a/tests/neg/phantom-classOf-2.scala +++ b/tests/neg/phantom-classOf-2.scala @@ -1,10 +1,8 @@ class phantomClassOf { - type Blinky <: Boo.BooAny - - classOf[Blinky] // error + classOf[Boo.Blinky] // error } object Boo extends Phantom { - type BooAny = this.Any + type Blinky = this.Any } diff --git a/tests/neg/phantom-expr.scala b/tests/neg/phantom-expr.scala index eff415121dae..c8d2cc2822d0 100644 --- a/tests/neg/phantom-expr.scala +++ b/tests/neg/phantom-expr.scala @@ -3,9 +3,6 @@ class Foo { import Boo._ import Boo1._ - type Blinky <: BooAny - type Inky <: BooAny - val blinky = Boo.boo[Blinky] val inky = Boo.boo[Inky] @@ -40,6 +37,8 @@ class Foo { object Boo extends Phantom { type BooAny = this.Any + type Blinky <: BooAny + type Inky <: BooAny def boo[B <: BooAny]: B = assume } diff --git a/tests/run/phantom-decls-2.scala b/tests/run/phantom-decls-2.scala index 4a4f594fe5f3..bf3f71b36856 100644 --- a/tests/run/phantom-decls-2.scala +++ b/tests/run/phantom-decls-2.scala @@ -12,9 +12,10 @@ object Test { new Boo2().polyfun1(boo[Pinky]) } + type Boo3 = BooAny + class Boo2 { println("Boo2") - type Boo3 = BooAny def polyfun1(p2: Boo3): Unit = { println("Boo2.polyfun1") } diff --git a/tests/run/phantom-decls-3.scala b/tests/run/phantom-decls-3.scala index fe18c8651f04..6a87be58f4e1 100644 --- a/tests/run/phantom-decls-3.scala +++ b/tests/run/phantom-decls-3.scala @@ -7,20 +7,14 @@ object Test { import Boo._ def main(args: Array[String]): Unit = { - - new Boo3(){ - type Boo1 = BooAny - }.polyfun1(boo[Pinky]) - - new Boo3(){ - type Boo1 = Blinky - }.polyfun1(boo[Blinky]) - + new Boo3(){ }.polyfun1(boo[Pinky]) + new Boo3(){ }.polyfun1(boo[Blinky]) } + type Boo1 = BooAny + trait Boo3 { println("Boo3") - type Boo1 <: BooAny def polyfun1(p3: Boo1): Unit = { println("Boo3.polyfun1") } From cf24ca00c31cfc3295635b911aea3119eba19b8a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 18 May 2017 14:41:33 +0200 Subject: [PATCH 3/3] Move TypeDef checking to Typer --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 10 ++++++++++ .../src/dotty/tools/dotc/typer/TypeAssigner.scala | 12 ++---------- compiler/src/dotty/tools/dotc/typer/Typer.scala | 4 +++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 527b0bd1f33e..9c7d68b8a62c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -473,6 +473,16 @@ object Checking { stats.foreach(checkValueClassMember) } } + + /** Verify type definitions meet the requirements */ + def checkTypeDef(tdef: tpd.TypeDef)(implicit ctx: Context): Unit = { + val isParam = tdef.tpe.typeSymbol.is(Param) + val isPhantom = tdef.tpe.isPhantom + if (isParam && isPhantom && tdef.symbol.owner.isPrimaryConstructor) + ctx.error("Classes cannot have phantom type parameters", tdef.pos) + else if (!isParam && isPhantom && !tdef.symbol.owner.isStaticOwner) + ctx.error("Non static classes cannot have phantom type members", tdef.pos) + } } trait Checking { diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index fbfaacf82b08..93bd2528c380 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -506,16 +506,8 @@ trait TypeAssigner { def assignType(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = tree.withType(symbolicIfNeeded(sym).orElse(sym.termRefWithSig)) - def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = { - val tdef = tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) - val isParam = tdef.tpe.typeSymbol.is(Param) - val isPhantom = tdef.tpe.isPhantom - if (isParam && isPhantom && tdef.symbol.owner.isPrimaryConstructor) - ctx.error("Classes cannot have phantom type parameters", tdef.pos) - else if (!isParam && isPhantom && !tdef.symbol.owner.isStaticOwner) - ctx.error("Non static classes cannot have phantom type members", tdef.pos) - tdef - } + def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = + tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) private def symbolicIfNeeded(sym: Symbol)(implicit ctx: Context) = { val owner = sym.owner diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 69140fcd0204..1787d876330b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1261,7 +1261,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case rhs => typedType(rhs) } - assignType(cpy.TypeDef(tdef)(name, rhs1), sym) + val tdef1 = assignType(cpy.TypeDef(tdef)(name, rhs1), sym) + Checking.checkTypeDef(tdef1) + tdef1 } def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") {