Skip to content

Commit daab14a

Browse files
committed
Merge isInstanceOfEvaluator with TypeTestCasts
As the saying goes: "let erasure handle it... " The reason for the change is that things were a mess before - isInstanceOfEvaluator did not handle &/| types. It would be hard to change that since it was looking at erased types. - isInstanceEvaluator, but not TypeTestCasts added null-checks - therefore, necessary null-checks were not emitted on some tests of the form `x.isInstanceOf[A & B]`. - the pattern matcher patched this up by inserting some null checks on its own, whereas it should just have let the translation of isInstanceOf do the job.
1 parent 1a959c3 commit daab14a

File tree

7 files changed

+103
-64
lines changed

7 files changed

+103
-64
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ class Compiler {
7070
new CrossCastAnd, // Normalize selections involving intersection types.
7171
new Splitter), // Expand selections involving union types into conditionals
7272
List(new VCInlineMethods, // Inlines calls to value class methods
73-
new IsInstanceOfEvaluator, // Issues warnings when unreachable statements are present in match/if expressions
7473
new SeqLiterals, // Express vararg arguments as arrays
7574
new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods
7675
new Getters, // Replace non-private vals and vars with getter defs (fields are added later)

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
601601
override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
602602
ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args)
603603
// Same remark as for Apply
604-
604+
605605
override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure =
606606
ta.assignType(untpd.cpy.Closure(tree)(env, meth, tpt), meth, tpt)
607607

@@ -770,6 +770,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
770770
else if (!ctx.erasedTypes) asInstance(tp)
771771
else Erasure.Boxing.adaptToType(tree, tp)
772772

773+
/** `tree ne null` (might need a cast to be type correct) */
774+
def testNotNull(implicit ctx: Context): Tree =
775+
tree.ensureConforms(defn.ObjectType)
776+
.select(defn.Object_ne).appliedTo(Literal(Constant(null)))
777+
773778
/** If inititializer tree is `_', the default value of its type,
774779
* otherwise the tree itself.
775780
*/

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ class Definitions {
251251
lazy val Any_getClass = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final)
252252
lazy val Any_isInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final)
253253
lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, TypeParamRef(_, 0), Final)
254+
lazy val Any_typeTest = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final)
255+
// generated by pattern matcher, eliminated by erasure
254256

255257
def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode,
256258
Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ object StdNames {
438438
val isDefined: N = "isDefined"
439439
val isEmpty: N = "isEmpty"
440440
val isInstanceOf_ : N = "isInstanceOf"
441+
val isInstanceOfPM: N = "$isInstanceOf$"
441442
val java: N = "java"
442443
val key: N = "key"
443444
val lang: N = "lang"

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@ object Erasure extends TypeTestsCasts{
172172
}
173173

174174
def constant(tree: Tree, const: Tree)(implicit ctx: Context) =
175-
if (isPureExpr(tree)) const else Block(tree :: Nil, const)
175+
(if (isPureExpr(tree)) const else Block(tree :: Nil, const))
176+
.withPos(tree.pos)
176177

177178
final def box(tree: Tree, target: => String = "")(implicit ctx: Context): Tree = ctx.traceIndented(i"boxing ${tree.showSummary}: ${tree.tpe} into $target") {
178179
tree.tpe.widen match {

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

Lines changed: 91 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package dotty.tools.dotc
22
package transform
33

4-
import core.Contexts._
5-
import core.Symbols._
6-
import core.Types._
7-
import core.Constants._
8-
import core.StdNames._
9-
import core.TypeErasure.isUnboundedGeneric
4+
import core._
5+
import Contexts._, Symbols._, Types._, Constants._, StdNames._, Decorators._
106
import ast.Trees._
117
import Erasure.Boxing._
12-
import core.TypeErasure._
8+
import TypeErasure._
139
import ValueClasses._
10+
import core.Flags._
11+
import util.Positions._
12+
1413

1514
/** This transform normalizes type tests and type casts,
1615
* also replacing type tests with singleton argument type with reference equality check
@@ -26,8 +25,6 @@ import ValueClasses._
2625
trait TypeTestsCasts {
2726
import ast.tpd._
2827

29-
// override def phaseName: String = "typeTestsCasts"
30-
3128
def interceptTypeApply(tree: TypeApply)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show}", show = true) {
3229
tree.fun match {
3330
case fun @ Select(qual, selector) =>
@@ -38,88 +35,122 @@ trait TypeTestsCasts {
3835
def derivedTree(qual1: Tree, sym: Symbol, tp: Type) =
3936
cpy.TypeApply(tree)(qual1.select(sym).withPos(qual.pos), List(TypeTree(tp)))
4037

41-
def qualCls = qual.tpe.widen.classSymbol
38+
def foundCls = qual.tpe.widen.classSymbol
39+
// println(i"ta $tree, found = $foundCls")
4240

43-
def transformIsInstanceOf(expr:Tree, argType: Type): Tree = {
44-
def argCls = argType.classSymbol
45-
if ((expr.tpe <:< argType) && isPureExpr(expr))
46-
Literal(Constant(true)) withPos tree.pos
47-
else if (argCls.isPrimitiveValueClass)
48-
if (qualCls.isPrimitiveValueClass) Literal(Constant(qualCls == argCls)) withPos tree.pos
49-
else transformIsInstanceOf(expr, defn.boxedType(argCls.typeRef))
50-
else argType.dealias match {
51-
case _: SingletonType =>
52-
val cmpOp = if (argType derivesFrom defn.AnyValClass) defn.Any_equals else defn.Object_eq
53-
expr.select(cmpOp).appliedTo(singleton(argType))
54-
case AndType(tp1, tp2) =>
55-
evalOnce(expr) { fun =>
56-
val erased1 = transformIsInstanceOf(fun, tp1)
57-
val erased2 = transformIsInstanceOf(fun, tp2)
58-
erased1 match {
59-
case Literal(Constant(true)) => erased2
60-
case _ =>
61-
erased2 match {
62-
case Literal(Constant(true)) => erased1
63-
case _ => erased1 and erased2
64-
}
65-
}
41+
def inMatch =
42+
fun.symbol == defn.Any_typeTest || // new scheme
43+
qual.symbol.is(Case) // old scheme
44+
45+
def transformIsInstanceOf(expr:Tree, testType: Type, flagUnrelated: Boolean): Tree = {
46+
def testCls = testType.classSymbol
47+
48+
def unreachable(why: => String) =
49+
if (flagUnrelated)
50+
if (inMatch) ctx.error(em"this case is unreachable since $why", expr.pos)
51+
else ctx.warning(em"this will always yield false since $why", expr.pos)
52+
53+
def checkSensical: Boolean = {
54+
val foundCls = erasure(qual.tpe.widen).typeSymbol
55+
val testCls = testType.typeSymbol
56+
!foundCls.isClass ||
57+
!testCls.isClass ||
58+
testCls.isPrimitiveValueClass ||
59+
ValueClasses.isDerivedValueClass(foundCls) ||
60+
ValueClasses.isDerivedValueClass(testCls) ||
61+
foundCls == defn.ObjectClass ||
62+
foundCls.derivesFrom(testCls) || {
63+
if (foundCls.is(Final)) {
64+
unreachable(i"$foundCls is not a subclass of $testCls")
65+
false
6666
}
67-
case defn.MultiArrayOf(elem, ndims) if isUnboundedGeneric(elem) =>
68-
def isArrayTest(arg: Tree) =
69-
ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal(Constant(ndims)))
70-
if (ndims == 1) isArrayTest(qual)
71-
else evalOnce(qual) { qual1 =>
72-
derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1)
67+
else if (!testCls.derivesFrom(foundCls) &&
68+
(testCls.is(Final) || !testCls.is(Trait) && !foundCls.is(Trait))) {
69+
unreachable(i"$foundCls and $testCls are unrelated")
70+
false
7371
}
72+
else true
73+
}
74+
}
75+
println(i"test ii $tree ${expr.tpe} $testType")
76+
77+
if (expr.tpe <:< testType)
78+
if (expr.tpe.isNotNull) {
79+
ctx.warning(
80+
em"this will always yield true, since `$foundCls` is a subclass of `$testCls`",
81+
expr.pos)
82+
constant(expr, Literal(Constant(true)))
83+
}
84+
else expr.testNotNull
85+
else if (!checkSensical)
86+
constant(expr, Literal(Constant(false)))
87+
else if (testCls.isPrimitiveValueClass)
88+
if (foundCls.isPrimitiveValueClass)
89+
constant(expr, Literal(Constant(foundCls == testCls)))
90+
else
91+
transformIsInstanceOf(expr, defn.boxedType(testCls.typeRef), flagUnrelated)
92+
else testType.dealias match {
93+
case _: SingletonType =>
94+
val cmpOp =
95+
if (testType derivesFrom defn.AnyValClass) defn.Any_equals
96+
else defn.Object_eq
97+
expr.select(cmpOp).appliedTo(singleton(testType))
7498
case _ =>
75-
derivedTree(expr, defn.Any_isInstanceOf, argType)
99+
derivedTree(expr, defn.Any_isInstanceOf, testType)
76100
}
77101
}
78102

79-
def transformAsInstanceOf(argType: Type): Tree = {
80-
def argCls = argType.widen.classSymbol
81-
if (qual.tpe <:< argType)
103+
def transformAsInstanceOf(testType: Type): Tree = {
104+
def testCls = testType.widen.classSymbol
105+
if (qual.tpe <:< testType)
82106
Typed(qual, tree.args.head)
83-
else if (qualCls.isPrimitiveValueClass) {
84-
if (argCls.isPrimitiveValueClass) primitiveConversion(qual, argCls)
85-
else derivedTree(box(qual), defn.Any_asInstanceOf, argType)
107+
else if (foundCls.isPrimitiveValueClass) {
108+
if (testCls.isPrimitiveValueClass) primitiveConversion(qual, testCls)
109+
else derivedTree(box(qual), defn.Any_asInstanceOf, testType)
86110
}
87-
else if (argCls.isPrimitiveValueClass)
88-
unbox(qual.ensureConforms(defn.ObjectType), argType)
89-
else if (isDerivedValueClass(argCls)) {
111+
else if (testCls.isPrimitiveValueClass)
112+
unbox(qual.ensureConforms(defn.ObjectType), testType)
113+
else if (isDerivedValueClass(testCls)) {
90114
qual // adaptToType in Erasure will do the necessary type adaptation
91115
}
92116
else
93-
derivedTree(qual, defn.Any_asInstanceOf, argType)
117+
derivedTree(qual, defn.Any_asInstanceOf, testType)
94118
}
95119

96120
/** Transform isInstanceOf OrType
97121
*
98122
* expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
99123
* expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
100124
*
101-
* The transform happens before erasure of `argType`, thus cannot be merged
102-
* with `transformIsInstanceOf`, which depends on erased type of `argType`.
125+
* The transform happens before erasure of `testType`, thus cannot be merged
126+
* with `transformIsInstanceOf`, which depends on erased type of `testType`.
103127
*/
104-
def transformTypeTest(qual: Tree, argType: Type): Tree = argType.dealias match {
128+
def transformTypeTest(qual: Tree, testType: Type, flagUnrelated: Boolean): Tree = testType.dealias match {
105129
case OrType(tp1, tp2) =>
106130
evalOnce(qual) { fun =>
107-
transformTypeTest(fun, tp1)
131+
transformTypeTest(fun, tp1, flagUnrelated = false)
108132
.select(defn.Boolean_||)
109-
.appliedTo(transformTypeTest(fun, tp2))
133+
.appliedTo(transformTypeTest(fun, tp2, flagUnrelated = false))
110134
}
111135
case AndType(tp1, tp2) =>
112136
evalOnce(qual) { fun =>
113-
transformTypeTest(fun, tp1)
137+
transformTypeTest(fun, tp1, flagUnrelated)
114138
.select(defn.Boolean_&&)
115-
.appliedTo(transformTypeTest(fun, tp2))
139+
.appliedTo(transformTypeTest(fun, tp2, flagUnrelated))
140+
}
141+
case defn.MultiArrayOf(elem, ndims) if isUnboundedGeneric(elem) =>
142+
def isArrayTest(arg: Tree) =
143+
ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal(Constant(ndims)))
144+
if (ndims == 1) isArrayTest(qual)
145+
else evalOnce(qual) { qual1 =>
146+
derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1)
116147
}
117148
case _ =>
118-
transformIsInstanceOf(qual, erasure(argType))
149+
transformIsInstanceOf(qual, erasure(testType), flagUnrelated)
119150
}
120151

121-
if (sym eq defn.Any_isInstanceOf)
122-
transformTypeTest(qual, tree.args.head.tpe)
152+
if ((sym eq defn.Any_isInstanceOf) || (sym eq defn.Any_typeTest))
153+
transformTypeTest(qual, tree.args.head.tpe, flagUnrelated = true)
123154
else if (sym eq defn.Any_asInstanceOf)
124155
transformAsInstanceOf(erasure(tree.args.head.tpe))
125156
else tree

tests/neg/tryPatternMatchError.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ object Test {
2525
case _: ExceptionTrait =>
2626
case _: NoSuchElementException if a <= 1 =>
2727
case _: NullPointerException | _:IOException =>
28-
case e: Int => // error
28+
case e: Int => // error: unrelated
2929
case EX =>
3030
case IAE(msg) =>
3131
case e: IllegalArgumentException =>

0 commit comments

Comments
 (0)