@@ -230,15 +230,63 @@ private boolean isConcreteClass(DJClass c) {
230230 };
231231
232232
233- /** Determine if {@link #cast} would succeed given an expression of the given type */
234- public boolean isCastable (Type target , Type expT ) {
235- try {
236- Expression e = TypeUtil .makeEmptyExpression ();
237- NodeProperties .setType (e , expT );
238- cast (target , e );
239- return true ;
233+ /** Whether two types are known to be disjoint. (Standard version is implicitly defined in JLS 5.5.) */
234+ public boolean isDisjoint (final Type s , final Type t ) {
235+ // By default, returns null for arrays and classes
236+ abstract class Visitor extends TypeAbstractVisitor <Boolean > {
237+ private final Type _other ;
238+ public Visitor (Type other ) { _other = other ; }
239+ public abstract boolean recur (Type that );
240+ @ Override public Boolean forPrimitiveType (PrimitiveType that ) {
241+ return !isSubtype (that , _other ) && !isSubtype (_other , that );
242+ }
243+ @ Override public Boolean forNullType (NullType that ) {
244+ return !isSubtype (that , _other );
245+ }
246+ @ Override public Boolean forArrayType (ArrayType that ) { return null ; }
247+ @ Override public Boolean forClassType (ClassType that ) { return null ; }
248+ @ Override public Boolean forIntersectionType (IntersectionType that ) {
249+ for (Type elt : that .ofTypes ()) { if (recur (elt )) return true ; }
250+ return false ;
251+ }
252+ @ Override public Boolean forUnionType (UnionType that ) {
253+ for (Type elt : that .ofTypes ()) { if (!recur (elt )) return false ; }
254+ return true ;
255+ }
256+ @ Override public Boolean forVariableType (VariableType that ) {
257+ // TODO: to be correct, we would need to recur (checked by a RecursionStack)
258+ return false ;
259+ }
260+ @ Override public Boolean forTopType (TopType s ) { return false ; }
261+ @ Override public Boolean forBottomType (BottomType s ) { return true ; }
262+ }
263+
264+ Boolean sResult = s .apply (new Visitor (t ) {
265+ public boolean recur (Type that ) { return isDisjoint (that , t ); }
266+ });
267+ if (sResult != null ) { return sResult ; }
268+ else {
269+ return t .apply (new Visitor (s ) {
270+ public boolean recur (Type that ) { return isDisjoint (s , that ); }
271+ @ Override public Boolean forArrayType (ArrayType t ) {
272+ if (s instanceof ArrayType ) { return isDisjoint (((ArrayType ) s ).ofType (), t .ofType ()); }
273+ else { return !isSubtype (t , s ); }
274+ }
275+ @ Override public Boolean forClassType (ClassType t ) {
276+ if (s instanceof ArrayType ) { return !isSubtype (s , t ); }
277+ else {
278+ ClassType sAsClass = (ClassType ) s ;
279+ if (sAsClass .ofClass ().isFinal () || t .ofClass ().isFinal () ||
280+ (!sAsClass .ofClass ().isInterface () && !t .ofClass ().isInterface ())) {
281+ // either one of them is a final class or both are non-final classes
282+ if (!isSubtype (s , erase (t )) && !isSubtype (t , erase (s ))) { return true ; }
283+ }
284+ // TODO: The JLS also checks for disjoint type arguments (comparing *all* common superclasses)
285+ return false ;
286+ }
287+ }
288+ });
240289 }
241- catch (UnsupportedConversionException e ) { return false ; }
242290 }
243291
244292 /** Determine if {@link #assign} would succeed given a non-constant expression of the given type */
@@ -1149,8 +1197,7 @@ public Expression cast(final Type target, final Expression e) throws Unsupported
11491197 Expression result = makeReference (e );
11501198 Type source = NodeProperties .getType (result );
11511199 if (!isSubtype (source , target )) {
1152- if (validCheckedCast (target , source ) ||
1153- (!_opt .prohibitUncheckedCasts () && validUncheckedCast (target , source ))) {
1200+ if (!isDisjoint (source , target ) && (!_opt .prohibitUncheckedCasts () || validCheckedCast (target , source ))) {
11541201 NodeProperties .setCheckedType (result , erasedClass (target ));
11551202 }
11561203 else { throw new UnsupportedConversionException (); }
@@ -1165,34 +1212,27 @@ public Expression cast(final Type target, final Expression e) throws Unsupported
11651212 }
11661213
11671214 /**
1168- * Whether a reference down-cast is valid and guaranteed safe. See JLS 5.5. (This is not completely faithful
1169- * to the JLS, simply because the details in the JLS are very complex.)
1215+ * Whether a reference down-cast is valid and guaranteed safe. May assume that
1216+ * source is not a subtype of target and that the two are not disjoint. See JLS 5.5.
11701217 */
11711218 private boolean validCheckedCast (Type target , final Type source ) {
11721219 return target .apply (new TypeAbstractVisitor <Boolean >() {
1173- @ Override public Boolean defaultCase (Type target ) {
1174- return isReifiable (target ) && isSubtype (target , erase (source ));
1175- }
1176- @ Override public Boolean forClassType (ClassType target ) {
1177- return isReifiable (target ) && (target .getClass ().isInterface () || isSubtype (target , erase (source )));
1178- }
1220+ @ Override public Boolean defaultCase (Type target ) { return isReifiable (target ); }
11791221 @ Override public Boolean forParameterizedClassType (ParameterizedClassType target ) {
1180- if (isReifiable (target )) { return target .ofClass ().isInterface () || isSubtype (target , erase (source )); }
1181- else {
1182- if (isSubtype (target , source )) {
1183- // Verify that that, given a value of type source & erase(target), it must have type target.
1184- // Must show, where target=Target<T1..Tn>, forall X1..Xn, Target<X1..Xn> <: source implies Xi=Ti.
1185- ParameterizedClassType wildCapt = capture (parameterize (new RawClassType (target .ofClass ())));
1186- Iterable <VariableType > unboundArgs =
1187- IterUtil .filterInstances (IterUtil .relax (wildCapt .typeArguments ()), VariableType .class );
1188- Iterable <Type > boundArgs = inferTypeArguments (unboundArgs , EMPTY_TYPE_ITERABLE , wildCapt ,
1189- EMPTY_TYPE_ITERABLE , Option .some (source ));
1190- return boundArgs != null && IterUtil .and (boundArgs , target .typeArguments (), new Predicate2 <Type , Type >() {
1191- public boolean contains (Type inferred , Type orig ) { return isEqual (inferred , orig ); }
1192- });
1193- }
1194- else { return false ; }
1222+ if (isReifiable (target )) { return true ; }
1223+ else if (isSubtype (target , source )) {
1224+ // Verify that that, given a value of type source & erase(target), it must have type target.
1225+ // Must show, where target=Target<T1..Tn>, forall X1..Xn, Target<X1..Xn> <: source implies Xi=Ti.
1226+ ParameterizedClassType wildCapt = capture (parameterize (new RawClassType (target .ofClass ())));
1227+ Iterable <VariableType > unboundArgs =
1228+ IterUtil .filterInstances (IterUtil .relax (wildCapt .typeArguments ()), VariableType .class );
1229+ Iterable <Type > boundArgs = inferTypeArguments (unboundArgs , EMPTY_TYPE_ITERABLE , wildCapt ,
1230+ EMPTY_TYPE_ITERABLE , Option .some (source ));
1231+ return boundArgs != null && IterUtil .and (boundArgs , target .typeArguments (), new Predicate2 <Type , Type >() {
1232+ public boolean contains (Type inferred , Type orig ) { return isEqual (inferred , orig ); }
1233+ });
11951234 }
1235+ else { return false ; }
11961236 }
11971237 @ Override public Boolean forArrayType (ArrayType target ) {
11981238 if (isArray (source )) { return validCheckedCast (target .ofType (), arrayElementType (source )); }
@@ -1201,14 +1241,6 @@ private boolean validCheckedCast(Type target, final Type source) {
12011241 });
12021242 }
12031243
1204- /**
1205- * Whether a reference down-cast is valid as an unchecked cast. See JLS 5.5. (This is not completely faithful
1206- * to the JLS, simply because the details in the JLS are very complex.)
1207- */
1208- private boolean validUncheckedCast (Type target , Type source ) {
1209- return isSubtype (target , erase (source )) || isSubtype (source , erase (target ));
1210- }
1211-
12121244 /**
12131245 * Prepare the given expression for assignment, wrapping it in any necessary conversions.
12141246 *
0 commit comments