Skip to content

Commit 4bdcaa8

Browse files
committed
add more tests
1 parent d0b6b20 commit 4bdcaa8

File tree

5 files changed

+64
-16
lines changed

5 files changed

+64
-16
lines changed

compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package transform
44
import util.Positions._
55
import MegaPhase.MiniPhase
66
import core._
7-
import Contexts.Context, Types._, Decorators._, Symbols._, typer._, ast._
7+
import Contexts.Context, Types._, Decorators._, Symbols._, typer._, ast._, NameKinds._
88
import TypeUtils._, Flags._
99
import config.Printers.{ transforms => debug }
1010

@@ -18,7 +18,7 @@ class IsInstanceOfChecker extends MiniPhase {
1818

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

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

101102
P1 <:< X // may fail, ignore
102103

103-
val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P
104+
tvars.foreach { case tvar: TypeVar =>
105+
val bounds = ctx.typerState.constraint.entry(tvar.origin)
106+
if (bounds.loBound =:= bounds.hiBound)
107+
tvar.instantiateWith(bounds.loBound)
108+
else {
109+
val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName.fresh().toTypeName, Case, tvar.origin.underlying, coord = pos)
110+
tvar.instantiateWith(wildCard.typeRef)
111+
}
112+
}
113+
114+
val res = P1 <:< P
104115
debug.println("P1 : " + P1)
105116
debug.println("P1 <:< P = " + res)
106117

@@ -119,7 +130,7 @@ object Checkable {
119130
case tpe: AppliedType => isClassDetermined(X, tpe)(ctx.fresh.setNewTyperState())
120131
case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
121132
case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
122-
case AnnotatedType(t, an) => recur(X, t)
133+
case AnnotatedType(t, _) => recur(X, t)
123134
case _: RefinedType => false
124135
case _ => true
125136
})

compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
159159
* ```
160160
*
161161
* If `C` is a value class the initial `eq` test is omitted.
162+
*
163+
* `@unchecked` is needed for parametric case classes.
164+
*
162165
*/
163166
def equalsBody(that: Tree)(implicit ctx: Context): Tree = {
164167
val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0
@@ -253,6 +256,8 @@ class SyntheticMethods(thisPhase: DenotTransformer) {
253256
* ```
254257
* def canEqual(that: Any) = that.isInstanceOf[C @unchecked]
255258
* ```
259+
*
260+
* `@unchecked` is needed for parametric case classes.
256261
*/
257262
def canEqualBody(that: Tree): Tree = that.isInstance(AnnotatedType(clazzType, Annotation(defn.UncheckedAnnot)))
258263

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class Test {
2+
trait A[+T]
3+
class B[T] extends A[T]
4+
class C[T] extends B[Any] with A[T]
5+
6+
def foo[T](c: C[T]): Unit = c match {
7+
case _: B[T] => // error
8+
}
9+
10+
def bar[T](b: B[T]): Unit = b match {
11+
case _: A[T] =>
12+
}
13+
14+
def quux[T](a: A[T]): Unit = a match {
15+
case _: B[T] => // error
16+
}
17+
18+
quux(new C[Int])
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
object Test {
2+
trait Foo
3+
case class One[+T](fst: T)
4+
5+
def bad[T <: Foo](e: One[T])(x: T) = e match {
6+
case foo: One[a] =>
7+
x.isInstanceOf[a] // error
8+
val y: Any = ???
9+
y.isInstanceOf[a] // error
10+
}
11+
}
12+
13+
object Test2 {
14+
case class One[T](fst: T)
15+
16+
def bad[T](e: One[T])(x: T) = e match {
17+
case foo: One[a] =>
18+
x.isInstanceOf[a] // error
19+
val y: Any = ???
20+
y.isInstanceOf[a] // error
21+
}
22+
}
Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
1-
import scala.util.control.NonFatal
21
object p {
32

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

9-
type Try[T] = Result[T, Throwable]
10-
object Try {
11-
def apply[T](x: => T): Try[T] =
12-
try Result.OK(x)
13-
catch {
14-
case NonFatal(ex) => Result.Err(ex)
15-
}
16-
}
17-
189
def foo(x: Any): Boolean =
1910
x.isInstanceOf[List[String]] // error
2011
}

0 commit comments

Comments
 (0)