1
1
package dotty .tools .dotc
2
2
package transform
3
3
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 ._
10
6
import ast .Trees ._
11
7
import Erasure .Boxing ._
12
- import core . TypeErasure ._
8
+ import TypeErasure ._
13
9
import ValueClasses ._
10
+ import core .Flags ._
11
+ import util .Positions ._
12
+
14
13
15
14
/** This transform normalizes type tests and type casts,
16
15
* also replacing type tests with singleton argument type with reference equality check
@@ -26,8 +25,6 @@ import ValueClasses._
26
25
trait TypeTestsCasts {
27
26
import ast .tpd ._
28
27
29
- // override def phaseName: String = "typeTestsCasts"
30
-
31
28
def interceptTypeApply (tree : TypeApply )(implicit ctx : Context ): Tree = ctx.traceIndented(s " transforming ${tree.show}" , show = true ) {
32
29
tree.fun match {
33
30
case fun @ Select (qual, selector) =>
@@ -38,88 +35,122 @@ trait TypeTestsCasts {
38
35
def derivedTree (qual1 : Tree , sym : Symbol , tp : Type ) =
39
36
cpy.TypeApply (tree)(qual1.select(sym).withPos(qual.pos), List (TypeTree (tp)))
40
37
41
- def qualCls = qual.tpe.widen.classSymbol
38
+ def foundCls = qual.tpe.widen.classSymbol
39
+ // println(i"ta $tree, found = $foundCls")
42
40
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
66
66
}
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
73
71
}
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))
74
98
case _ =>
75
- derivedTree(expr, defn.Any_isInstanceOf , argType )
99
+ derivedTree(expr, defn.Any_isInstanceOf , testType )
76
100
}
77
101
}
78
102
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 )
82
106
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 )
86
110
}
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 )) {
90
114
qual // adaptToType in Erasure will do the necessary type adaptation
91
115
}
92
116
else
93
- derivedTree(qual, defn.Any_asInstanceOf , argType )
117
+ derivedTree(qual, defn.Any_asInstanceOf , testType )
94
118
}
95
119
96
120
/** Transform isInstanceOf OrType
97
121
*
98
122
* expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
99
123
* expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
100
124
*
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 `.
103
127
*/
104
- def transformTypeTest (qual : Tree , argType : Type ): Tree = argType .dealias match {
128
+ def transformTypeTest (qual : Tree , testType : Type , flagUnrelated : Boolean ): Tree = testType .dealias match {
105
129
case OrType (tp1, tp2) =>
106
130
evalOnce(qual) { fun =>
107
- transformTypeTest(fun, tp1)
131
+ transformTypeTest(fun, tp1, flagUnrelated = false )
108
132
.select(defn.Boolean_|| )
109
- .appliedTo(transformTypeTest(fun, tp2))
133
+ .appliedTo(transformTypeTest(fun, tp2, flagUnrelated = false ))
110
134
}
111
135
case AndType (tp1, tp2) =>
112
136
evalOnce(qual) { fun =>
113
- transformTypeTest(fun, tp1)
137
+ transformTypeTest(fun, tp1, flagUnrelated )
114
138
.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)
116
147
}
117
148
case _ =>
118
- transformIsInstanceOf(qual, erasure(argType) )
149
+ transformIsInstanceOf(qual, erasure(testType), flagUnrelated )
119
150
}
120
151
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 )
123
154
else if (sym eq defn.Any_asInstanceOf )
124
155
transformAsInstanceOf(erasure(tree.args.head.tpe))
125
156
else tree
0 commit comments