@@ -31,7 +31,7 @@ predicate hasSubtype(RefType t, Type sub) {
3131 arraySubtype ( t , sub ) and t != sub
3232 or
3333 // Type parameter containment for parameterized types.
34- parContainmentSubtype ( t , sub ) and t != sub
34+ parContainmentSubtype ( t , sub )
3535 or
3636 // Type variables are subtypes of their upper bounds.
3737 typeVarSubtypeBound ( t , sub ) and t != sub
@@ -59,19 +59,23 @@ private predicate arraySubtype(Array sup, Array sub) {
5959 * )
6060 * ```
6161 * 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.
6871 */
6972
7073private 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+ )
7579}
7680
7781/**
@@ -94,100 +98,116 @@ private RefType parameterisationTypeArgumentVarianceCand(
9498 varianceCandidate ( t )
9599}
96100
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
107108 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+ )
110112}
111113
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 )
120121}
121122
122123/**
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.
133127 */
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+ }
139142}
140143
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+ */
141149pragma [ 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 )
148160}
149161
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+ )
157167}
158168
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+ )
164175}
165176
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 )
178181 )
179182}
180183
181184pragma [ 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+ )
186204}
187205
188206/**
189207 * Holds if the type argument `s` contains the type argument `t`, where both
190208 * type arguments occur as index `n` in an instantiation of `g`.
209+ *
210+ * The case `s = t` is not included.
191211 */
192212pragma [ noinline]
193213private predicate typeArgumentContains ( GenericType g , RefType s , RefType t , int n ) {
@@ -205,26 +225,26 @@ private predicate typeArgumentContainsAux2(GenericType g, RefType s, RefType t,
205225 * Holds if the type argument `s` contains the type argument `t`, where both
206226 * type arguments occur as index `n` in some parameterized types.
207227 *
228+ * The case `s = t` is not included.
229+ *
208230 * See JLS 4.5.1, Type Arguments of Parameterized Types.
209231 */
210232private 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+ (
217237 exists ( RefType tUpperBound | tUpperBound = t .( Wildcard ) .getUpperBound ( ) .getType ( ) |
218238 // ? extends T <= ? extends S if T <: S
219- hasSubtypeStar ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , tUpperBound )
239+ hasSubtypeStar1 ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , tUpperBound )
220240 or
221241 // ? extends T <= ?
222242 s .( Wildcard ) .isUnconstrained ( )
223243 )
224244 or
225245 exists ( RefType tLowerBound | tLowerBound = t .( Wildcard ) .getLowerBound ( ) .getType ( ) |
226246 // ? super T <= ? super S if s <: T
227- hasSubtypeStar ( tLowerBound , s .( Wildcard ) .getLowerBound ( ) .getType ( ) )
247+ hasSubtypeStar2 ( tLowerBound , s .( Wildcard ) .getLowerBound ( ) .getType ( ) )
228248 or
229249 // ? super T <= ?
230250 s .( Wildcard ) .isUnconstrained ( )
@@ -233,14 +253,14 @@ private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
233253 wildcardExtendsObject ( s )
234254 )
235255 or
236- // T <= T
237- s = t
238- or
239256 // T <= ? extends T
240- hasSubtypeStar ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , t )
257+ hasSubtypeStar1 ( s .( Wildcard ) .getUpperBound ( ) .getType ( ) , t )
241258 or
242259 // 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`
244264 )
245265}
246266
@@ -249,12 +269,38 @@ private predicate wildcardExtendsObject(Wildcard wc) {
249269 wc .getUpperBound ( ) .getType ( ) instanceof TypeObject
250270}
251271
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 )
254300 or
255- hasSubtype ( t , sub )
301+ hasSubtype ( t , sub ) and getAWildcardLowerBound ( sub )
256302 or
257- exists ( RefType mid | hasSubtypeStar ( t , mid ) and hasSubtype ( mid , sub ) )
303+ exists ( RefType mid | hasSubtype ( t , mid ) and hasSubtypeStar2 ( mid , sub ) )
258304}
259305
260306/** Holds if type `t` declares member `m`. */
0 commit comments