/** * Return the greatest lower bound of two types. That is, return the largest type that is a * subtype of both inputs. If none exists return {@code thisType}. */ public JReferenceType strengthenType(JReferenceType thisType, JReferenceType thatType) { if (thisType == thatType) { return thisType; } if (thisType.isNullType() || thatType.isNullType()) { return JReferenceType.NULL_TYPE; } if (thisType.canBeNull() != thatType.canBeNull()) { // If either is non-nullable, the result should be non-nullable. return strengthenType(thisType.strengthenToNonNull(), thatType.strengthenToNonNull()); } if (typeOracle.castSucceedsTrivially(thisType, thatType)) { return thisType; } if (typeOracle.castSucceedsTrivially(thatType, thisType)) { return thatType; } // This types are incompatible; ideally this code should not be reached, but there are two // situations where this happens: // 1 - unrelated interfaces; // 2 - unsafe code. // The original type is preserved in this case. return thisType; }
/** * Return the least upper bound of two types. That is, the "smallest" type that is a supertype of * both types. In this lattice there the smallest element might no exist, there might be multiple * minimal elements neither of which is smaller than the others. E.g. * * <p>{@code I J | \ /| | \ / | | x | | / \ | | / \ | A B } * * <p>where I and J are interfaces, A and B are classes and both A and B implement I and J. In * this case both I and J are generalizing the types A and B. */ private JReferenceType generalizeTypes(JReferenceType thisType, JReferenceType thatType) { if (!thisType.canBeNull() && !thatType.canBeNull()) { // Nullability is an orthogonal property, so remove non_nullability and perform the // generalization on the nullable types, and if both were NOT nullable then strengthen the // result to NOT nullable. // // not_nullable(A) v not_nullable(B) = not_nullable(A v B) JReferenceType nulllableGeneralizer = generalizeTypes(thisType.weakenToNullable(), thatType.weakenToNullable()); return nulllableGeneralizer.strengthenToNonNull(); } thisType = thisType.weakenToNullable(); thatType = thatType.weakenToNullable(); // From here on nullability does not need to be considered. // Generalization for exact types is as follows. // exact(A) v null = exact(A) // A v null = A if (thatType.isNullType()) { return thisType; } // null v exact(A) = exact(A) // null v A = A if (thisType.isNullType()) { return thatType; } // exact(A) v exact(A) = exact(A) // A v A = A if (thisType == thatType) { return thisType; } // exact(A) v exact(B) = A v B // A v exact(B) = A v B // exact(A) v B = A v B // A v B = A v B return generalizeUnderlyingTypes(thisType.getUnderlyingType(), thatType.getUnderlyingType()); }