@@ -31,7 +31,7 @@ predicate hasSubtype(RefType t, Type sub) {
31
31
arraySubtype ( t , sub ) and t != sub
32
32
or
33
33
// Type parameter containment for parameterized types.
34
- parContainmentSubtype ( t , sub ) and t != sub
34
+ parContainmentSubtype ( t , sub )
35
35
or
36
36
// Type variables are subtypes of their upper bounds.
37
37
typeVarSubtypeBound ( t , sub ) and t != sub
@@ -59,19 +59,23 @@ private predicate arraySubtype(Array sup, Array sub) {
59
59
* )
60
60
* ```
61
61
* For performance several transformations are made. First, the `forex` is
62
- * written as a loop where `typeArgumentsContain(_, pt, psub, n)` encode that
63
- * the `forex` holds for `i in [0..n]`. Second, the relation is split into two
64
- * cases depending on whether `pt.getNumberOfTypeArguments()` is 1 or 2+, as
65
- * this allows us to unroll the loop and collapse the first two iterations. The
66
- * base case for `typeArgumentsContain` is therefore `n=1` and this allows an
67
- * improved join order implemented by `contains01`.
62
+ * written as a loop where `typePrefixContains(ppt, ppsub)` encode that
63
+ * `ppt` and `ppsub` are prefixes of `pt` and `ptsub` and that
64
+ * the `forex` holds for `i in [0..n-1]` where `n` is the length of the prefixes.
65
+ * Second, the recursive case that determines containment of length `n+1`
66
+ * prefixes is split into three cases depending on whether there is
67
+ * non-reflexive type parameter containment:
68
+ * - only in the length `n` prefix,
69
+ * - only in the `n`th position,
70
+ * - both in the length `n` prefix and the `n`th position.
68
71
*/
69
72
70
73
private predicate parContainmentSubtype ( ParameterizedType pt , ParameterizedType psub ) {
71
- pt != psub and
72
- typeArgumentsContain ( _, pt , psub , pt .getNumberOfTypeArguments ( ) - 1 )
73
- or
74
- typeArgumentsContain0 ( _, pt , psub )
74
+ exists ( ParameterizedPrefix ppt , ParameterizedPrefix ppsub |
75
+ typePrefixContains ( ppt , ppsub ) and
76
+ ppt .equals ( pt ) and
77
+ ppsub .equals ( psub )
78
+ )
75
79
}
76
80
77
81
/**
@@ -94,100 +98,116 @@ private RefType parameterisationTypeArgumentVarianceCand(
94
98
varianceCandidate ( t )
95
99
}
96
100
97
- /**
98
- * Holds if every type argument of `s` (up to `n` with `n >= 1`) contains the
99
- * corresponding type argument of `t`. Both `s` and `t` are constrained to
100
- * being parameterizations of `g`.
101
- */
102
- pragma [ nomagic]
103
- private predicate typeArgumentsContain (
104
- GenericType g , ParameterizedType s , ParameterizedType t , int n
105
- ) {
106
- contains01 ( g , s , t ) and n = 1
101
+ private newtype TParameterizedPrefix =
102
+ TGenericType ( GenericType g ) or
103
+ TTypeParam ( ParameterizedPrefix pp , RefType t ) { prefixMatches ( pp , t , _, _) }
104
+
105
+ /** Holds if `pp` is a length `n` prefix of `pt`. */
106
+ private predicate prefixMatches ( ParameterizedPrefix pp , ParameterizedType pt , int n ) {
107
+ pp = TGenericType ( pt .getGenericType ( ) ) and n = 0
107
108
or
108
- contains ( g , s , t , n ) and
109
- typeArgumentsContain ( g , s , t , n - 1 )
109
+ exists ( ParameterizedPrefix pp0 , RefType t |
110
+ pp = TTypeParam ( pp0 , t ) and prefixMatches ( pp0 , t , pt , n - 1 )
111
+ )
110
112
}
111
113
112
- private predicate typeArgumentsContain0 (
113
- GenericType g , ParameterizedType sParm , ParameterizedType tParm
114
- ) {
115
- exists ( RefType s , RefType t |
116
- containsAux0 ( g , tParm , s , t ) and
117
- s = parameterisationTypeArgument ( g , sParm , 0 ) and
118
- s != t
119
- )
114
+ /**
115
+ * Holds if `pp` is a length `n` prefix of `pt` and `t` is the `n`th type
116
+ * argument of `pt`.
117
+ */
118
+ private predicate prefixMatches ( ParameterizedPrefix pp , RefType t , ParameterizedType pt , int n ) {
119
+ prefixMatches ( pp , pt , n ) and
120
+ t = pt .getTypeArgument ( n )
120
121
}
121
122
122
123
/**
123
- * Holds if the `n`-th type argument of `sParm` contain the `n`-th type
124
- * argument of `tParm` for both `n = 0` and `n = 1`, where both `sParm` and
125
- * `tParm` are parameterizations of the same generic type `g`.
126
- *
127
- * This is equivalent to
128
- * ```
129
- * contains(g, sParm, tParm, 0) and
130
- * contains(g, sParm, tParm, 1)
131
- * ```
132
- * except `contains` is restricted to only include `n >= 2`.
124
+ * A prefix of a `ParameterizedType`. This encodes the corresponding
125
+ * `GenericType` and the first `n` type arguments where `n` is the prefix
126
+ * length.
133
127
*/
134
- private predicate contains01 ( GenericType g , ParameterizedType sParm , ParameterizedType tParm ) {
135
- exists ( RefType s0 , RefType t0 , RefType s1 , RefType t1 |
136
- contains01Aux0 ( g , tParm , s0 , t0 , t1 ) and
137
- contains01Aux1 ( g , sParm , s0 , s1 , t1 )
138
- )
128
+ private class ParameterizedPrefix extends TParameterizedPrefix {
129
+ string toString ( ) { result = "ParameterizedPrefix" }
130
+
131
+ predicate equals ( ParameterizedType pt ) { prefixMatches ( this , pt , pt .getNumberOfTypeArguments ( ) ) }
132
+
133
+ /** Holds if this prefix has length `n`, applies to `g`, and equals `TTypeParam(pp, t)`. */
134
+ predicate split ( GenericType g , ParameterizedPrefix pp , RefType t , int n ) {
135
+ this = TTypeParam ( pp , t ) and
136
+ (
137
+ pp = TGenericType ( g ) and n = 0
138
+ or
139
+ pp .split ( g , _, _, n - 1 )
140
+ )
141
+ }
139
142
}
140
143
144
+ /**
145
+ * Holds if every type argument of `pps` contains the corresponding type
146
+ * argument of `ppt`. Both `pps` and `ppt` are constrained to be equal-length
147
+ * prefixes of parameterizations of the same `GenericType`.
148
+ */
141
149
pragma [ nomagic]
142
- private predicate contains01Aux0 (
143
- GenericType g , ParameterizedType tParm , RefType s0 , RefType t0 , RefType t1
144
- ) {
145
- typeArgumentContains ( g , s0 , t0 , 0 ) and
146
- t0 = parameterisationTypeArgument ( g , tParm , 0 ) and
147
- t1 = parameterisationTypeArgument ( g , tParm , 1 )
150
+ private predicate typePrefixContains ( ParameterizedPrefix pps , ParameterizedPrefix ppt ) {
151
+ // Let `pps = TTypeParam(pps0, s)` and `ppt = TTypeParam(ppt0, t)`.
152
+ // Case 1: pps0 = ppt0 and typeArgumentContains(_, s, t, _)
153
+ typePrefixContains_base ( pps , ppt )
154
+ or
155
+ // Case 2: typePrefixContains(pps0, ppt0) and s = t
156
+ typePrefixContains_ext_eq ( pps , ppt )
157
+ or
158
+ // Case 3: typePrefixContains(pps0, ppt0) and typeArgumentContains(_, s, t, _)
159
+ typePrefixContains_ext_neq ( pps , ppt )
148
160
}
149
161
150
- pragma [ nomagic]
151
- private predicate contains01Aux1 (
152
- GenericType g , ParameterizedType sParm , RefType s0 , RefType s1 , RefType t1
153
- ) {
154
- typeArgumentContains ( g , s1 , t1 , 1 ) and
155
- s0 = parameterisationTypeArgumentVarianceCand ( g , sParm , 0 ) and
156
- s1 = parameterisationTypeArgumentVarianceCand ( g , sParm , 1 )
162
+ private predicate typePrefixContains_base ( ParameterizedPrefix pps , ParameterizedPrefix ppt ) {
163
+ exists ( ParameterizedPrefix pp , RefType s |
164
+ pps = TTypeParam ( pp , s ) and
165
+ typePrefixContainsAux2 ( ppt , pp , s )
166
+ )
157
167
}
158
168
159
- pragma [ nomagic]
160
- private predicate containsAux0 ( GenericType g , ParameterizedType tParm , RefType s , RefType t ) {
161
- typeArgumentContains ( g , s , t , 0 ) and
162
- t = parameterisationTypeArgument ( g , tParm , 0 ) and
163
- g .getNumberOfTypeParameters ( ) = 1
169
+ private predicate typePrefixContains_ext_eq ( ParameterizedPrefix pps , ParameterizedPrefix ppt ) {
170
+ exists ( ParameterizedPrefix pps0 , ParameterizedPrefix ppt0 , RefType t |
171
+ typePrefixContains ( pragma [ only_bind_into ] ( pps0 ) , pragma [ only_bind_into ] ( ppt0 ) ) and
172
+ pps = TTypeParam ( pragma [ only_bind_into ] ( pps0 ) , t ) and
173
+ ppt = TTypeParam ( ppt0 , t )
174
+ )
164
175
}
165
176
166
- /**
167
- * Holds if the `n`-th type argument of `sParm` contain the `n`-th type
168
- * argument of `tParm`, where both `sParm` and `tParm` are parameterizations of
169
- * the same generic type `g`. The index `n` is restricted to `n >= 2`, the
170
- * cases `n < 2` are handled by `contains01`.
171
- *
172
- * See JLS 4.5.1, Type Arguments of Parameterized Types.
173
- */
174
- private predicate contains ( GenericType g , ParameterizedType sParm , ParameterizedType tParm , int n ) {
175
- exists ( RefType s , RefType t |
176
- containsAux ( g , tParm , n , s , t ) and
177
- s = parameterisationTypeArgumentVarianceCand ( g , sParm , n )
177
+ private predicate typePrefixContains_ext_neq ( ParameterizedPrefix pps , ParameterizedPrefix ppt ) {
178
+ exists ( ParameterizedPrefix ppt0 , RefType s |
179
+ typePrefixContainsAux1 ( pps , ppt0 , s ) and
180
+ typePrefixContainsAux2 ( ppt , ppt0 , s )
178
181
)
179
182
}
180
183
181
184
pragma [ nomagic]
182
- private predicate containsAux ( GenericType g , ParameterizedType tParm , int n , RefType s , RefType t ) {
183
- typeArgumentContains ( g , s , t , n ) and
184
- t = parameterisationTypeArgument ( g , tParm , n ) and
185
- n >= 2
185
+ private predicate typePrefixContainsAux1 (
186
+ ParameterizedPrefix pps , ParameterizedPrefix ppt0 , RefType s
187
+ ) {
188
+ exists ( ParameterizedPrefix pps0 |
189
+ typePrefixContains ( pps0 , ppt0 ) and
190
+ pps = TTypeParam ( pps0 , s ) and
191
+ s instanceof Wildcard // manual magic, implied by `typeArgumentContains(_, s, t, _)`
192
+ )
193
+ }
194
+
195
+ pragma [ nomagic]
196
+ private predicate typePrefixContainsAux2 (
197
+ ParameterizedPrefix ppt , ParameterizedPrefix ppt0 , RefType s
198
+ ) {
199
+ exists ( GenericType g , int n , RefType t |
200
+ // Implies `ppt = TTypeParam(ppt0, t)`
201
+ ppt .split ( g , ppt0 , t , n ) and
202
+ typeArgumentContains ( g , s , t , n )
203
+ )
186
204
}
187
205
188
206
/**
189
207
* Holds if the type argument `s` contains the type argument `t`, where both
190
208
* type arguments occur as index `n` in an instantiation of `g`.
209
+ *
210
+ * The case `s = t` is not included.
191
211
*/
192
212
pragma [ noinline]
193
213
private predicate typeArgumentContains ( GenericType g , RefType s , RefType t , int n ) {
@@ -205,26 +225,26 @@ private predicate typeArgumentContainsAux2(GenericType g, RefType s, RefType t,
205
225
* Holds if the type argument `s` contains the type argument `t`, where both
206
226
* type arguments occur as index `n` in some parameterized types.
207
227
*
228
+ * The case `s = t` is not included.
229
+ *
208
230
* See JLS 4.5.1, Type Arguments of Parameterized Types.
209
231
*/
210
232
private predicate typeArgumentContainsAux1 ( RefType s , RefType t , int n ) {
211
- exists ( int i |
212
- s = parameterisationTypeArgumentVarianceCand ( _, _, i ) and
213
- t = parameterisationTypeArgument ( _, _, n ) and
214
- i <= n and
215
- n <= i
216
- |
233
+ s = parameterisationTypeArgumentVarianceCand ( _, _, pragma [ only_bind_into ] ( n ) ) and
234
+ t = parameterisationTypeArgument ( _, _, pragma [ only_bind_into ] ( n ) ) and
235
+ s != t and
236
+ (
217
237
exists ( RefType tUpperBound | tUpperBound = t .( Wildcard ) .getUpperBound ( ) .getType ( ) |
218
238
// ? extends T <= ? extends S if T <: S
219
- hasSubtypeStar ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , tUpperBound )
239
+ hasSubtypeStar1 ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , tUpperBound )
220
240
or
221
241
// ? extends T <= ?
222
242
s .( Wildcard ) .isUnconstrained ( )
223
243
)
224
244
or
225
245
exists ( RefType tLowerBound | tLowerBound = t .( Wildcard ) .getLowerBound ( ) .getType ( ) |
226
246
// ? super T <= ? super S if s <: T
227
- hasSubtypeStar ( tLowerBound , s .( Wildcard ) .getLowerBound ( ) .getType ( ) )
247
+ hasSubtypeStar2 ( tLowerBound , s .( Wildcard ) .getLowerBound ( ) .getType ( ) )
228
248
or
229
249
// ? super T <= ?
230
250
s .( Wildcard ) .isUnconstrained ( )
@@ -233,14 +253,14 @@ private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
233
253
wildcardExtendsObject ( s )
234
254
)
235
255
or
236
- // T <= T
237
- s = t
238
- or
239
256
// T <= ? extends T
240
- hasSubtypeStar ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , t )
257
+ hasSubtypeStar1 ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , t )
241
258
or
242
259
// T <= ? super T
243
- hasSubtypeStar ( t , s .( Wildcard ) .getLowerBound ( ) .getType ( ) )
260
+ hasSubtypeStar2 ( t , s .( Wildcard ) .getLowerBound ( ) .getType ( ) )
261
+ // or
262
+ // T <= T
263
+ // but this case is handled directly in `typePrefixContains`
244
264
)
245
265
}
246
266
@@ -249,12 +269,38 @@ private predicate wildcardExtendsObject(Wildcard wc) {
249
269
wc .getUpperBound ( ) .getType ( ) instanceof TypeObject
250
270
}
251
271
252
- private predicate hasSubtypeStar ( RefType t , RefType sub ) {
253
- sub = t
272
+ // manual magic for `hasSubtypeStar1`
273
+ private predicate getAWildcardUpperBound ( RefType t ) {
274
+ t = any ( Wildcard w ) .getUpperBound ( ) .getType ( )
275
+ }
276
+
277
+ // manual magic for `hasSubtypeStar2`
278
+ private predicate getAWildcardLowerBound ( RefType t ) {
279
+ t = any ( Wildcard w ) .getLowerBound ( ) .getType ( )
280
+ }
281
+
282
+ /**
283
+ * Holds if `hasSubtype*(t, sub)`, but manual-magic'ed with `getAWildcardUpperBound(t)`.
284
+ */
285
+ pragma [ nomagic]
286
+ private predicate hasSubtypeStar1 ( RefType t , RefType sub ) {
287
+ sub = t and getAWildcardUpperBound ( t )
288
+ or
289
+ hasSubtype ( t , sub ) and getAWildcardUpperBound ( t )
290
+ or
291
+ exists ( RefType mid | hasSubtypeStar1 ( t , mid ) and hasSubtype ( mid , sub ) )
292
+ }
293
+
294
+ /**
295
+ * Holds if `hasSubtype*(t, sub)`, but manual-magic'ed with `getAWildcardLowerBound(sub)`.
296
+ */
297
+ pragma [ nomagic]
298
+ private predicate hasSubtypeStar2 ( RefType t , RefType sub ) {
299
+ sub = t and getAWildcardLowerBound ( sub )
254
300
or
255
- hasSubtype ( t , sub )
301
+ hasSubtype ( t , sub ) and getAWildcardLowerBound ( sub )
256
302
or
257
- exists ( RefType mid | hasSubtypeStar ( t , mid ) and hasSubtype ( mid , sub ) )
303
+ exists ( RefType mid | hasSubtype ( t , mid ) and hasSubtypeStar2 ( mid , sub ) )
258
304
}
259
305
260
306
/** Holds if type `t` declares member `m`. */
0 commit comments