Skip to content

Commit 7c726ee

Browse files
committed
fix #1354: improve type test and typecast of union types
1 parent b6882d6 commit 7c726ee

File tree

3 files changed

+86
-9
lines changed

3 files changed

+86
-9
lines changed

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

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
package dotty.tools.dotc
22
package transform
33

4-
import TreeTransforms._
5-
import core.Denotations._
6-
import core.SymDenotations._
74
import core.Contexts._
85
import core.Symbols._
96
import core.Types._
107
import core.Constants._
118
import core.StdNames._
129
import core.TypeErasure.isUnboundedGeneric
13-
import typer.ErrorReporting._
1410
import ast.Trees._
1511
import Erasure.Boxing._
1612
import core.TypeErasure._
1713
import ValueClasses._
14+
import core.Decorators._
15+
import core.Flags._
1816

1917
/** This transform normalizes type tests and type casts,
2018
* also replacing type tests with singleton argument type with reference equality check
@@ -80,7 +78,7 @@ trait TypeTestsCasts {
8078
}
8179
}
8280

83-
def transformAsInstanceOf(argType: Type): Tree = {
81+
def transformAsInstanceOf(qual: Tree, argType: Type): Tree = {
8482
def argCls = argType.widen.classSymbol
8583
if (qual.tpe <:< argType)
8684
Typed(qual, tree.args.head)
@@ -92,14 +90,57 @@ trait TypeTestsCasts {
9290
unbox(qual.ensureConforms(defn.ObjectType), argType)
9391
else if (isDerivedValueClass(argCls)) {
9492
qual // adaptToType in Erasure will do the necessary type adaptation
95-
} else
93+
}
94+
else
9695
derivedTree(qual, defn.Any_asInstanceOf, argType)
9796
}
98-
def erasedArg = erasure(tree.args.head.tpe)
97+
98+
/** Transform isInstanceOf OrType
99+
*
100+
* expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
101+
*
102+
* The transform happens before erasure of `argType`, thus cannot be merged
103+
* with the transformIsInstanceOf, which depends on erased type of `argType`.
104+
*/
105+
def transformOrTypeTest(qual: Tree, argType: Type): Tree = argType match {
106+
case OrType(tp1, tp2) =>
107+
evalOnce(qual) { fun =>
108+
transformOrTypeTest(fun, tp1)
109+
.select(nme.OR)
110+
.appliedTo(transformOrTypeTest(fun, tp2))
111+
}
112+
case _ =>
113+
transformIsInstanceOf(qual, erasure(argType))
114+
}
115+
116+
/** Transform asInstanceOf OrType
117+
*
118+
* expr.asInstanceOf[A | B] ~~> try expr.asInstanceOf[A] catch {
119+
* case ex: Throwable => expr.asInstanceOf[B]
120+
* }
121+
*
122+
* The transform happens before erasure of `argType`, thus cannot be merged
123+
* with the transformAsInstanceOf, which depends on erased type of `argType`.
124+
*/
125+
def transformOrTypeCast(qual: Tree, argType: Type): Tree = argType match {
126+
case OrType(tp1, tp2) =>
127+
evalOnce(qual) { fun =>
128+
val exName = ctx.freshName("ex").toTermName
129+
val exSymbol =
130+
ctx.newSymbol(ctx.owner, exName, Synthetic | Case, defn.ThrowableType, coord = tree.pos)
131+
132+
Try(transformOrTypeCast(fun, tp1),
133+
List(CaseDef(BindTyped(exSymbol, exSymbol.info), EmptyTree, transformOrTypeCast(fun, tp2))),
134+
EmptyTree)
135+
}
136+
case _ =>
137+
transformAsInstanceOf(qual, erasure(argType))
138+
}
139+
99140
if (sym eq defn.Any_isInstanceOf)
100-
transformIsInstanceOf(qual, erasedArg)
141+
transformOrTypeTest(qual, tree.args.head.tpe)
101142
else if (sym eq defn.Any_asInstanceOf)
102-
transformAsInstanceOf(erasedArg)
143+
transformOrTypeCast(qual, tree.args.head.tpe)
103144
else tree
104145

105146
case _ =>

tests/run/i1354.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
0
2+
false
3+
false
4+
1
5+
true
6+
true

tests/run/i1354.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
object Test {
2+
def foo(a: Int | Double) = a match {
3+
case a: (Float | Boolean) => 1
4+
case _ => 0
5+
}
6+
7+
def typeTest(a: Int | Double) = a.isInstanceOf[Float | Boolean] // is it true or false?
8+
9+
def typeCast(a: Int | Double) = a.asInstanceOf[Float | Boolean] // is it true or false?
10+
11+
def main(args: Array[String]): Unit = {
12+
println(foo(4))
13+
14+
println(typeTest(4))
15+
16+
try { println(typeCast(5)) }
17+
catch {
18+
case ex: Throwable => println("false")
19+
}
20+
21+
Boolean.box(true) match {
22+
case a: (Float | Boolean) => println(1)
23+
case _ => println(0)
24+
}
25+
26+
println(Boolean.box(true).isInstanceOf[Float | Boolean])
27+
28+
println(Boolean.box(true).asInstanceOf[Float | Boolean])
29+
}
30+
}

0 commit comments

Comments
 (0)