Skip to content

Commit f463cb0

Browse files
committed
Add restrictions on phantom classes.
1 parent 59bbd4b commit f463cb0

File tree

6 files changed

+135
-26
lines changed

6 files changed

+135
-26
lines changed

src/dotty/tools/dotc/transform/PhantomDeclErasure.scala

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import core.Types._
99
import core.Decorators._
1010
import dotty.tools.dotc.ast.{Trees, tpd}
1111
import ast.Trees._
12-
12+
import dotty.tools.dotc.core.StdNames._
1313
import dotty.tools.dotc.core.Flags
1414
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform}
1515

@@ -57,13 +57,31 @@ class PhantomDeclErasure extends MiniPhaseTransform with InfoTransformer {
5757
case ValDef(_, tpt, _) =>
5858
!tpt.tpe.derivesFrom(defn.PhantomAnyClass)
5959

60-
case tree @ DefDef(_, _, _, tpt, _) =>
60+
case tree @ DefDef(name, _, _, tpt, _) =>
61+
val flags = tree.symbol.flags
6162
val isPhantom = tpt.tpe.derivesFrom(defn.PhantomAnyClass)
62-
if (isPhantom && tree.symbol.flags.is(Flags.Accessor)) {
63-
if (tree.symbol.flags.is(Flags.Mutable))
64-
ctx.error("Can not define var with phantom type.", tree.pos)
65-
else if (tree.symbol.flags.is(Flags.Lazy))
66-
ctx.error("Can not define lazy var with phantom type.", tree.pos)
63+
if (isPhantom) {
64+
if (flags.is(Flags.Mutable))
65+
ctx.error("Can not define 'var' with phantom type.", tree.pos)
66+
else if (flags.is(Flags.Lazy))
67+
ctx.error("Can not define 'lazy val' with phantom type.", tree.pos)
68+
} else if (name != nme.CONSTRUCTOR) {
69+
tree.symbol.owner match {
70+
case cls: ClassSymbol if cls.classSymbol.derivesFrom(defn.PhantomAnyClass) =>
71+
val defKey =
72+
if (!flags.is(Flags.Accessor)) "def"
73+
else if (flags.is(Flags.Mutable)) "var"
74+
else if (flags.is(Flags.Lazy)) "lazy val"
75+
else "val"
76+
val flags2 = tree.symbol.owner.flags
77+
val classKey =
78+
if (flags2.is(Flags.Trait)) "trait"
79+
else if (flags2.is(Flags.Abstract)) "abstract class"
80+
else "class"
81+
ctx.error(s"Can not define '$defKey' with non phantom type in a phantom '$classKey'.", tree.pos)
82+
83+
case _ =>
84+
}
6785
}
6886
!isPhantom
6987

src/dotty/tools/dotc/transform/PhantomRefErasure.scala

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@ import core.Types._
99
import core.Decorators._
1010
import dotty.tools.dotc.ast.{Trees, tpd}
1111
import ast.Trees._
12-
12+
import dotty.tools.dotc.core.StdNames._
1313
import dotty.tools.dotc.core.Flags
1414
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform}
15+
import dotty.tools.dotc.util.Positions.Position
1516

1617
import scala.annotation.tailrec
1718

1819
class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
19-
thisTransformer =>
20-
21-
import dotty.tools.dotc.ast.tpd._
20+
import tpd._
2221

2322
override def phaseName: String = "phantomRefErasure"
2423

@@ -28,7 +27,7 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
2827

2928
/** Check what the phase achieves, to be called at any point after it is finished.
3029
*/
31-
override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = {
30+
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
3231
def assertNotPhantom(tpe: Type): Unit =
3332
assert(!tpe.derivesFrom(defn.PhantomAnyClass), "All phantom type values should be erased in " + tree)
3433

@@ -42,17 +41,32 @@ class PhantomRefErasure extends MiniPhaseTransform with InfoTransformer {
4241

4342
// Transform trees
4443

45-
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = {
46-
trees.foreach {
47-
case tree @ Apply(fun, _) if tree.tpe.derivesFrom(defn.PhantomAnyClass) =>
48-
ctx.error(s"Functions returning a phantom type can not be in statement position.", fun.pos)
49-
case tree @ TypeApply(fun, _) if tree.tpe.derivesFrom(defn.PhantomAnyClass) =>
50-
ctx.error(s"Functions returning a phantom type can not be in statement position.", fun.pos)
51-
case tree: Select if tree.tpe.derivesFrom(defn.PhantomAnyClass) =>
52-
ctx.error(s"Fields containing a phantom type can not be accessed in statement position.", tree.pos)
44+
override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = {
45+
ctx.owner match {
46+
case cls: ClassSymbol if cls.classDenot.classSymbol.derivesFrom(defn.PhantomAnyClass) =>
47+
trees.foreach {
48+
case tree: TypeDef =>
49+
case tree: DefDef =>
50+
case tree =>
51+
ctx.error(s"Phantom classes can not have expressions in statement position.", tree.pos)
52+
}
5353
case _ =>
54+
trees.foreach {
55+
case tree@Apply(fun, _) =>
56+
fun match {
57+
case Select(_: This, name) if name == nme.CONSTRUCTOR =>
58+
case _ if tree.tpe.derivesFrom(defn.PhantomAnyClass) =>
59+
ctx.error(s"Functions returning a phantom type can not be in statement position.", fun.pos)
60+
case _ =>
61+
}
62+
case tree@TypeApply(fun, _) if tree.tpe.derivesFrom(defn.PhantomAnyClass) =>
63+
ctx.error(s"Functions returning a phantom type can not be in statement position.", fun.pos)
64+
case tree: Select if tree.tpe.derivesFrom(defn.PhantomAnyClass) =>
65+
ctx.error(s"Fields containing a phantom type can not be accessed in statement position.", tree.pos)
66+
case _ =>
67+
}
5468
}
55-
super.transformStats(trees)
69+
trees
5670
}
5771

5872
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {

tests/neg/phantomClass2.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import dotty.phantom.PhantomAny
2+
3+
class Blinky extends PhantomAny {
4+
new Blinky // error
5+
println() // error
6+
System.currentTimeMillis() // error
7+
}

tests/neg/phantomClass3.scala

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import dotty.phantom.PhantomAny
2+
3+
class Blinky extends PhantomAny {
4+
5+
def this(p: PhantomAny) = {
6+
this()
7+
}
8+
9+
def this(n: Int) = { // FIXME should fail
10+
this()
11+
}
12+
13+
def boo1() = new Blinky
14+
val boo2 = new Blinky
15+
16+
var boo3 = new Blinky // error
17+
lazy val boo4 = new Blinky // error
18+
19+
def foo1() = 42 // error
20+
val foo2 = 42 // error
21+
var foo3 = 42 // error
22+
lazy val foo4 = 42 // error
23+
24+
}
25+
26+
abstract class Inky extends PhantomAny {
27+
28+
def this(p: PhantomAny) = {
29+
this()
30+
}
31+
32+
def this(n: Int) = { // FIXME should fail
33+
this()
34+
}
35+
36+
def boo1() = new Blinky
37+
val boo2 = new Blinky
38+
39+
var boo3 = new Blinky // error
40+
lazy val boo4 = new Blinky // error
41+
42+
def foo1() = 42 // error
43+
val foo2 = 42 // error
44+
var foo3 = 42 // error
45+
lazy val foo4 = 42 // error
46+
47+
}
48+
49+
trait Pinky extends PhantomAny {
50+
51+
def this(p: PhantomAny) = {
52+
this()
53+
}
54+
55+
def this(n: Int) = { // FIXME should fail
56+
this()
57+
}
58+
59+
def boo1() = new Blinky
60+
val boo2 = new Blinky
61+
62+
var boo3 = new Blinky // error
63+
lazy val boo4 = new Blinky // error
64+
65+
def foo1() = 42 // error
66+
val foo2 = 42 // error
67+
var foo3 = 42 // error
68+
lazy val foo4 = 42 // error
69+
70+
}

tests/pos/phantomEq.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
/* This is a example of how to implement Eq using erasable phantom types.
33
*
44
* Run this test with
5-
* `run tests/pos/phantomEq.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomErasure,erasure`
6-
* to see the the diffs after PhantomErasure and Erasure.
5+
* `run tests/pos/phantomEq.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomRefErasure,phantomDeclErasure,erasure`
6+
* to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure.
77
*
88
*/
99
object PhantomEq {
@@ -20,7 +20,7 @@ object PhantomEq {
2020
(1: Number) === 1.toByte
2121
}
2222

23-
object PhantomEqUtil extends PhantomEqUtil {
23+
object PhantomEqUtil {
2424
class PhantomEq[-L, -R] extends dotty.phantom.PhantomAny
2525
type PhantomEqEq[T] = PhantomEq[T, T]
2626

tests/run/phantom.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Run this test with
2-
* `run tests/run/phantom.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomErasure,erasure`
3-
* to see the the diffs after PhantomErasure and Erasure.
2+
* `run tests/run/phantom.scala -Xprint-diff-del -Xprint:arrayConstructors,phantomRefErasure,phantomDeclErasure,erasure`
3+
* to see the the diffs after PhantomRefErasure, PhantomDeclErasure and Erasure.
44
*
55
*/
66
object Test {

0 commit comments

Comments
 (0)