Skip to content

Disallow Phantom types in casts #2525

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class Compiler {
List(new FirstTransform, // Some transformations to put trees into a canonical form
new CheckReentrant), // Internal use only: Check that compiled program has no data races involving global vars
List(new CheckStatic, // Check restrictions that apply to @static members
new CheckPhantomCast, // Checks that no Phantom types in are in casts
new ElimRepeated, // Rewrite vararg parameters and arguments
new RefChecks, // Various checks mostly related to abstract members and overriding
new NormalizeFlags, // Rewrite some definition flags
Expand Down
50 changes: 50 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/CheckPhantomCast.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dotty.tools.dotc
package transform

import core._
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
import Types._
import Contexts.Context
import Symbols._
import Decorators._
import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.ast.tpd


/** A no-op transform to ensure that the compiled sources have no Phantom types in casts */
class CheckPhantomCast extends MiniPhaseTransform { thisTransformer =>

override def phaseName = "checkPhantomCast"

override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = {
tree match {
case TypeApply(fun, targs) if fun.symbol eq defn.Any_asInstanceOf => assert(!containsPhantom(targs.head.tpe))
case Bind(_, Typed(_, tpt)) => assert(!containsPhantom(tpt.tpe))
case _ =>
}
}

override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
if (tree.fun.symbol eq defn.Any_asInstanceOf)
checkNoPhantoms(tree.args.head)
tree
}

override def transformBind(tree: tpd.Bind)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
tree.body match {
case Typed(_, tpt) => checkNoPhantoms(tpt)
case _ =>
}
tree
}

private def checkNoPhantoms(tpTree: tpd.Tree)(implicit ctx: Context): Unit = {
if (containsPhantom(tpTree.tpe))
ctx.error("Cannot cast type containing a phantom type", tpTree.pos)
}

private def containsPhantom(tp: Type)(implicit ctx: Context): Boolean = new TypeAccumulator[Boolean] {
override def apply(x: Boolean, tp: Type): Boolean = x || tp.isPhantom || foldOver(false, tp)
}.apply(x = false, tp)

}
47 changes: 47 additions & 0 deletions tests/neg/phantom-class-type-members.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Boo._

object Test {
def main(args: Array[String]): Unit = {
val a = new Bar()
foo(a.asInstanceOf[Foo{type T = BooNothing}].y) // error

a match {
case a: Foo{type T = BooNothing} => a.y // error
}

val b = new Baz
b.asInstanceOf[Foo{type T = BooAny}].z(any) // error

b match {
case b: Foo{type T = BooAny} => a.z(any) // error
}
}

def foo(x: BooNothing) = println("foo")

}

abstract class Foo {
type T <: BooAny
def y: T
def z(z: T): Unit
}

class Bar extends Foo {
type T = BooAny
def y: T = any
def z(z: T) = ()
}

class Baz extends Foo {
type T = BooNothing
def y: T = nothing
def z(z: T) = ()
}

object Boo extends Phantom {
type BooAny = this.Any
type BooNothing = this.Nothing
def any: BooAny = assume
def nothing: BooNothing = assume
}
34 changes: 34 additions & 0 deletions tests/neg/phantom-class-type-parameters.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Boo._

object Test {
def main(args: Array[String]): Unit = {
val a = new Foo[BooAny](any)
foo(a.asInstanceOf[Foo[BooNothing]].x) // error
foo(a.asInstanceOf[Foo[BooNothing]].y) // error

a match {
case a: Foo[BooNothing] => a.x // error
}

val b = new Foo[BooNothing](a.asInstanceOf[Foo[BooNothing]].x) // error
b.asInstanceOf[Foo[BooAny]].z(any) // error

b match {
case b: Foo[BooAny] => b.z(any) // error
}
}

def foo(x: BooNothing) = println("foo")

}

class Foo[T <: BooAny](val x: T) {
def y: T = x
def z(z: T) = ()
}

object Boo extends Phantom {
type BooAny = this.Any
type BooNothing = this.Nothing
def any: BooAny = assume
}