@@ -4,9 +4,10 @@ package transform
4
4
import util .Positions ._
5
5
import MegaPhase .MiniPhase
6
6
import core ._
7
- import Contexts .Context , Types ._ , Decorators ._ , Symbols ._ , typer ._ , ast ._
7
+ import Contexts .Context , Types ._ , Decorators ._ , Symbols ._ , typer ._ , ast ._ , NameKinds . _ , StdNames . _
8
8
import TypeUtils ._ , Flags ._
9
9
import config .Printers .{ transforms => debug }
10
+ import collection .mutable .Map
10
11
11
12
/** Check runtime realizability of type test, see the documentation for `Checkable`.
12
13
*/
@@ -16,9 +17,24 @@ class IsInstanceOfChecker extends MiniPhase {
16
17
17
18
val phaseName = " isInstanceOfChecker"
18
19
19
- override def transformTypeApply (tree : TypeApply )(implicit ctx : Context ): Tree = {
20
+ override def transformTemplate (tree : Template )(implicit ctx : Context ): Tree = {
21
+ // remap to cancel effect of GADT narrowing
22
+ val binders = Map .empty[Symbol , Symbol ]
23
+
24
+ new TreeTraverser () {
25
+ override def traverse (tree : Tree )(implicit ctx : Context ): Unit =
26
+ tree match {
27
+ case t : TypeApply => checkInstanceOf(t, binders)
28
+ case _ => traverseChildren(tree)
29
+ }
30
+ }.traverse(tree)
31
+
32
+ tree
33
+ }
34
+
35
+ def checkInstanceOf (tree : TypeApply , binders : Map [Symbol , Symbol ])(implicit ctx : Context ): Tree = {
20
36
def ensureCheckable (qual : Tree , pt : Tree ): Tree = {
21
- if (! Checkable .checkable(qual.tpe, pt.tpe))
37
+ if (! Checkable .checkable(qual.tpe, pt.tpe, binders, tree.pos ))
22
38
ctx.warning(
23
39
s " the type test for ${pt.show} cannot be checked at runtime " ,
24
40
tree.pos
@@ -43,12 +59,7 @@ object Checkable {
43
59
44
60
/** Whether `(x:X).isInstanceOf[P]` can be checked at runtime?
45
61
*
46
- * First do the following substitution:
47
- * (a) replace `T @unchecked` and pattern binder types (e.g., `_$1`) in P with WildcardType
48
- * (b) replace pattern binder types (e.g., `_$1`) in X:
49
- * - variance = 1 : hiBound
50
- * - variance = -1 : loBound
51
- * - variance = 0 : OrType(Any, Nothing) // TODO: use original type param bounds
62
+ * First cancel the effect of GADT bound narrowing with the map `binders`.
52
63
*
53
64
* Then check:
54
65
*
@@ -58,13 +69,14 @@ object Checkable {
58
69
* 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`.
59
70
* 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`:
60
71
* (a) replace `Ts` with fresh type variables `Xs`
61
- * (b) constrain `Xs` with `pre.F[Xs] <:< X` (may fail)
72
+ * (b) constrain `Xs` with `pre.F[Xs] <:< X`,
73
+ * if `X` cannot be uniquely determined, instantiate `X` with fresh type symbol.
62
74
* (c) instantiate Xs and check `pre.F[Xs] <:< P`
63
75
* 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
64
76
* 7. if `P` is a refinement type, FALSE
65
77
* 8. otherwise, TRUE
66
78
*/
67
- def checkable (X : Type , P : Type )(implicit ctx : Context ): Boolean = {
79
+ def checkable (X : Type , P : Type , binders : Map [ Symbol , Symbol ], pos : Position )(implicit ctx : Context ): Boolean = {
68
80
def isAbstract (P : Type ) = ! P .dealias.typeSymbol.isClass
69
81
70
82
def replaceP (implicit ctx : Context ) = new TypeMap {
@@ -88,6 +100,40 @@ object Checkable {
88
100
}
89
101
}
90
102
103
+ def replace (implicit ctx : Context ) = new TypeMap {
104
+ def mapTypeBoundSymbol (tp : Type , paramInfo : Type ): Type = tp match {
105
+ case tref : TypeRef
106
+ if ! tref.typeSymbol.isClass && tref.symbol.is(Case ) =>
107
+ if (binders.contains(tref.typeSymbol))
108
+ binders(tref.typeSymbol).typeRef
109
+ else if (tref.typeSymbol.name == tpnme.WILDCARD )
110
+ WildcardType
111
+ else {
112
+ val fresh = ctx.newSymbol(ctx.owner, WildcardParamName .fresh().toTypeName, Case , paramInfo.bounds, coord = pos)
113
+ binders(tref.typeSymbol) = fresh
114
+ WildcardType (paramInfo.bounds)
115
+ }
116
+ case _ =>
117
+ apply(tp)
118
+ }
119
+
120
+ def apply (tp : Type ) = tp match {
121
+ case tapp @ AppliedType (tycon, args) =>
122
+ val args2 = args.zip(tycon.typeParams).map { case (arg, paramInfo) => mapTypeBoundSymbol(arg, paramInfo.paramInfo) }
123
+ tycon.appliedTo(args2)
124
+ case tref : TypeRef if ! tref.typeSymbol.isClass && tref.symbol.is(Case ) =>
125
+ if (binders.contains(tref.typeSymbol))
126
+ binders(tref.typeSymbol).typeRef
127
+ else
128
+ // annotation for Array[_] could reach here
129
+ tref
130
+ case AnnotatedType (_, annot) if annot.symbol == defn.UncheckedAnnot =>
131
+ WildcardType
132
+ case _ =>
133
+ mapOver(tp)
134
+ }
135
+ }
136
+
91
137
def isClassDetermined (X : Type , P : AppliedType )(implicit ctx : Context ) = {
92
138
val AppliedType (tycon, _) = P
93
139
val typeLambda = tycon.ensureHK.asInstanceOf [TypeLambda ]
@@ -100,7 +146,17 @@ object Checkable {
100
146
101
147
P1 <:< X // may fail, ignore
102
148
103
- val res = isFullyDefined(P1 , ForceDegree .noBottom) && P1 <:< P
149
+ tvars.foreach { case tvar : TypeVar =>
150
+ val bounds = ctx.typerState.constraint.entry(tvar.origin)
151
+ if (bounds.loBound =:= bounds.hiBound)
152
+ tvar.instantiateWith(bounds.loBound)
153
+ else {
154
+ val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName .fresh().toTypeName, Case , tvar.origin.underlying, coord = pos)
155
+ tvar.instantiateWith(wildCard.typeRef)
156
+ }
157
+ }
158
+
159
+ val res = P1 <:< P
104
160
debug.println(" P1 : " + P1 )
105
161
debug.println(" P1 <:< P = " + res)
106
162
@@ -119,12 +175,20 @@ object Checkable {
119
175
case tpe : AppliedType => isClassDetermined(X , tpe)(ctx.fresh.setNewTyperState())
120
176
case AndType (tp1, tp2) => recur(X , tp1) && recur(X , tp2)
121
177
case OrType (tp1, tp2) => recur(X , tp1) && recur(X , tp2)
122
- case AnnotatedType (t, an) => recur(X , t)
178
+ case AnnotatedType (t, _) => recur(X , t)
123
179
case _ : RefinedType => false
124
180
case _ => true
125
181
})
126
182
127
- val res = recur(replaceX.apply(X .widen), replaceP.apply(P ))
183
+ debug.println(s " $X <: $P" )
184
+
185
+ val X1 = replace.apply(X .widen)
186
+ val P1 = replace.apply(P )
187
+
188
+ debug.println(s " $X ~~> $X1" )
189
+ debug.println(s " $P ~~> $P1" )
190
+
191
+ val res = recur(X1 , P1 )
128
192
129
193
debug.println(i " checking ${X .show} isInstanceOf ${P } = $res" )
130
194
0 commit comments