Skip to content

Commit

Permalink
add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
liufengyun committed Mar 7, 2018
1 parent d0b6b20 commit 4bdcaa8
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 16 deletions.
23 changes: 17 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package transform
import util.Positions._
import MegaPhase.MiniPhase
import core._
import Contexts.Context, Types._, Decorators._, Symbols._, typer._, ast._
import Contexts.Context, Types._, Decorators._, Symbols._, typer._, ast._, NameKinds._
import TypeUtils._, Flags._
import config.Printers.{ transforms => debug }

Expand All @@ -18,7 +18,7 @@ class IsInstanceOfChecker extends MiniPhase {

override def transformTypeApply(tree: TypeApply)(implicit ctx: Context): Tree = {
def ensureCheckable(qual: Tree, pt: Tree): Tree = {
if (!Checkable.checkable(qual.tpe, pt.tpe))
if (!Checkable.checkable(qual.tpe, pt.tpe, tree.pos))
ctx.warning(
s"the type test for ${pt.show} cannot be checked at runtime",
tree.pos
Expand Down Expand Up @@ -58,13 +58,14 @@ object Checkable {
* 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`.
* 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`:
* (a) replace `Ts` with fresh type variables `Xs`
* (b) constrain `Xs` with `pre.F[Xs] <:< X` (may fail)
* (b) constrain `Xs` with `pre.F[Xs] <:< X`,
* if `X` cannot be uniquely determined, instantiate `X` with fresh type symbol.
* (c) instantiate Xs and check `pre.F[Xs] <:< P`
* 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
* 7. if `P` is a refinement type, FALSE
* 8. otherwise, TRUE
*/
def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = {
def checkable(X: Type, P: Type, pos: Position)(implicit ctx: Context): Boolean = {
def isAbstract(P: Type) = !P.dealias.typeSymbol.isClass

def replaceP(implicit ctx: Context) = new TypeMap {
Expand Down Expand Up @@ -100,7 +101,17 @@ object Checkable {

P1 <:< X // may fail, ignore

val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P
tvars.foreach { case tvar: TypeVar =>
val bounds = ctx.typerState.constraint.entry(tvar.origin)
if (bounds.loBound =:= bounds.hiBound)
tvar.instantiateWith(bounds.loBound)
else {
val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName.fresh().toTypeName, Case, tvar.origin.underlying, coord = pos)
tvar.instantiateWith(wildCard.typeRef)
}
}

val res = P1 <:< P
debug.println("P1 : " + P1)
debug.println("P1 <:< P = " + res)

Expand All @@ -119,7 +130,7 @@ object Checkable {
case tpe: AppliedType => isClassDetermined(X, tpe)(ctx.fresh.setNewTyperState())
case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
case AnnotatedType(t, an) => recur(X, t)
case AnnotatedType(t, _) => recur(X, t)
case _: RefinedType => false
case _ => true
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
* ```
*
* If `C` is a value class the initial `eq` test is omitted.
*
* `@unchecked` is needed for parametric case classes.
*
*/
def equalsBody(that: Tree)(implicit ctx: Context): Tree = {
val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0
Expand Down Expand Up @@ -253,6 +256,8 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
* ```
* def canEqual(that: Any) = that.isInstanceOf[C @unchecked]
* ```
*
* `@unchecked` is needed for parametric case classes.
*/
def canEqualBody(that: Tree): Tree = that.isInstance(AnnotatedType(clazzType, Annotation(defn.UncheckedAnnot)))

Expand Down
19 changes: 19 additions & 0 deletions tests/neg-custom-args/isInstanceOf/3324g.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Test {
trait A[+T]
class B[T] extends A[T]
class C[T] extends B[Any] with A[T]

def foo[T](c: C[T]): Unit = c match {
case _: B[T] => // error
}

def bar[T](b: B[T]): Unit = b match {
case _: A[T] =>
}

def quux[T](a: A[T]): Unit = a match {
case _: B[T] => // error
}

quux(new C[Int])
}
22 changes: 22 additions & 0 deletions tests/neg-custom-args/isInstanceOf/4075.scala.ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
object Test {
trait Foo
case class One[+T](fst: T)

def bad[T <: Foo](e: One[T])(x: T) = e match {
case foo: One[a] =>
x.isInstanceOf[a] // error
val y: Any = ???
y.isInstanceOf[a] // error
}
}

object Test2 {
case class One[T](fst: T)

def bad[T](e: One[T])(x: T) = e match {
case foo: One[a] =>
x.isInstanceOf[a] // error
val y: Any = ???
y.isInstanceOf[a] // error
}
}
11 changes: 1 addition & 10 deletions tests/neg-custom-args/isInstanceOf/Result.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import scala.util.control.NonFatal
object p {

// test parametric case classes, which synthesis `canEqual` and `equals`
enum Result[+T, +E] {
case OK [T](x: T) extends Result[T, Nothing]
case Err[E](e: E) extends Result[Nothing, E]
}

type Try[T] = Result[T, Throwable]
object Try {
def apply[T](x: => T): Try[T] =
try Result.OK(x)
catch {
case NonFatal(ex) => Result.Err(ex)
}
}

def foo(x: Any): Boolean =
x.isInstanceOf[List[String]] // error
}

0 comments on commit 4bdcaa8

Please sign in to comment.