Skip to content

Commit 48258c5

Browse files
Merge pull request #2525 from dotty-staging/disallow-casts-on-phantoms
Disallow Phantom types in casts
2 parents c101ce9 + 4b5bd1f commit 48258c5

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class Compiler {
4949
List(new FirstTransform, // Some transformations to put trees into a canonical form
5050
new CheckReentrant), // Internal use only: Check that compiled program has no data races involving global vars
5151
List(new CheckStatic, // Check restrictions that apply to @static members
52+
new CheckPhantomCast, // Checks that no Phantom types in are in casts
5253
new ElimRepeated, // Rewrite vararg parameters and arguments
5354
new RefChecks, // Various checks mostly related to abstract members and overriding
5455
new NormalizeFlags, // Rewrite some definition flags
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
6+
import Types._
7+
import Contexts.Context
8+
import Symbols._
9+
import Decorators._
10+
import dotty.tools.dotc.ast.Trees._
11+
import dotty.tools.dotc.ast.tpd
12+
13+
14+
/** A no-op transform to ensure that the compiled sources have no Phantom types in casts */
15+
class CheckPhantomCast extends MiniPhaseTransform { thisTransformer =>
16+
17+
override def phaseName = "checkPhantomCast"
18+
19+
override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = {
20+
tree match {
21+
case TypeApply(fun, targs) if fun.symbol eq defn.Any_asInstanceOf => assert(!containsPhantom(targs.head.tpe))
22+
case Bind(_, Typed(_, tpt)) => assert(!containsPhantom(tpt.tpe))
23+
case _ =>
24+
}
25+
}
26+
27+
override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
28+
if (tree.fun.symbol eq defn.Any_asInstanceOf)
29+
checkNoPhantoms(tree.args.head)
30+
tree
31+
}
32+
33+
override def transformBind(tree: tpd.Bind)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
34+
tree.body match {
35+
case Typed(_, tpt) => checkNoPhantoms(tpt)
36+
case _ =>
37+
}
38+
tree
39+
}
40+
41+
private def checkNoPhantoms(tpTree: tpd.Tree)(implicit ctx: Context): Unit = {
42+
if (containsPhantom(tpTree.tpe))
43+
ctx.error("Cannot cast type containing a phantom type", tpTree.pos)
44+
}
45+
46+
private def containsPhantom(tp: Type)(implicit ctx: Context): Boolean = new TypeAccumulator[Boolean] {
47+
override def apply(x: Boolean, tp: Type): Boolean = x || tp.isPhantom || foldOver(false, tp)
48+
}.apply(x = false, tp)
49+
50+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import Boo._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val a = new Bar()
6+
foo(a.asInstanceOf[Foo{type T = BooNothing}].y) // error
7+
8+
a match {
9+
case a: Foo{type T = BooNothing} => a.y // error
10+
}
11+
12+
val b = new Baz
13+
b.asInstanceOf[Foo{type T = BooAny}].z(any) // error
14+
15+
b match {
16+
case b: Foo{type T = BooAny} => a.z(any) // error
17+
}
18+
}
19+
20+
def foo(x: BooNothing) = println("foo")
21+
22+
}
23+
24+
abstract class Foo {
25+
type T <: BooAny
26+
def y: T
27+
def z(z: T): Unit
28+
}
29+
30+
class Bar extends Foo {
31+
type T = BooAny
32+
def y: T = any
33+
def z(z: T) = ()
34+
}
35+
36+
class Baz extends Foo {
37+
type T = BooNothing
38+
def y: T = nothing
39+
def z(z: T) = ()
40+
}
41+
42+
object Boo extends Phantom {
43+
type BooAny = this.Any
44+
type BooNothing = this.Nothing
45+
def any: BooAny = assume
46+
def nothing: BooNothing = assume
47+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Boo._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val a = new Foo[BooAny](any)
6+
foo(a.asInstanceOf[Foo[BooNothing]].x) // error
7+
foo(a.asInstanceOf[Foo[BooNothing]].y) // error
8+
9+
a match {
10+
case a: Foo[BooNothing] => a.x // error
11+
}
12+
13+
val b = new Foo[BooNothing](a.asInstanceOf[Foo[BooNothing]].x) // error
14+
b.asInstanceOf[Foo[BooAny]].z(any) // error
15+
16+
b match {
17+
case b: Foo[BooAny] => b.z(any) // error
18+
}
19+
}
20+
21+
def foo(x: BooNothing) = println("foo")
22+
23+
}
24+
25+
class Foo[T <: BooAny](val x: T) {
26+
def y: T = x
27+
def z(z: T) = ()
28+
}
29+
30+
object Boo extends Phantom {
31+
type BooAny = this.Any
32+
type BooNothing = this.Nothing
33+
def any: BooAny = assume
34+
}

0 commit comments

Comments
 (0)