JSType getGreatestSubtypeHelper(JSType that) { if (that.isRecordType()) { RecordType thatRecord = that.toMaybeRecordType(); RecordTypeBuilder builder = new RecordTypeBuilder(registry); builder.setSynthesized(true); // The greatest subtype consists of those *unique* properties of both // record types. If any property conflicts, then the NO_TYPE type // is returned. for (String property : getOwnPropertyNames()) { if (thatRecord.hasProperty(property) && !thatRecord.getPropertyType(property).isInvariant(getPropertyType(property))) { return registry.getNativeObjectType(JSTypeNative.NO_TYPE); } builder.addProperty(property, getPropertyType(property), getPropertyNode(property)); } for (String property : thatRecord.getOwnPropertyNames()) { if (!hasProperty(property)) { builder.addProperty( property, thatRecord.getPropertyType(property), thatRecord.getPropertyNode(property)); } } return builder.build(); } JSType greatestSubtype = registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE); JSType thatRestrictedToObj = registry.getNativeType(JSTypeNative.OBJECT_TYPE).getGreatestSubtype(that); if (!thatRestrictedToObj.isEmptyType()) { // In this branch, the other type is some object type. We find // the greatest subtype with the following algorithm: // 1) For each property "x" of this record type, take the union // of all classes with a property "x" with a compatible property type. // and which are a subtype of {@code that}. // 2) Take the intersection of all of these unions. for (String propName : getOwnPropertyNames()) { JSType propType = getPropertyType(propName); UnionTypeBuilder builder = new UnionTypeBuilder(registry); for (ObjectType alt : registry.getEachReferenceTypeWithProperty(propName)) { JSType altPropType = alt.getPropertyType(propName); if (altPropType != null && !alt.isEquivalentTo(this) && alt.isSubtype(that) && propType.isInvariant(altPropType)) { builder.addAlternate(alt); } } greatestSubtype = greatestSubtype.getLeastSupertype(builder.build()); } } return greatestSubtype; }
/** Try to get the sup/inf of two functions by looking at the piecewise components. */ private FunctionType tryMergeFunctionPiecewise(FunctionType other, boolean leastSuper) { Node newParamsNode = null; if (call.hasEqualParameters(other.call, EquivalenceMethod.IDENTITY)) { newParamsNode = call.parameters; } else { // If the parameters are not equal, don't try to merge them. // Someday, we should try to merge the individual params. return null; } JSType newReturnType = leastSuper ? call.returnType.getLeastSupertype(other.call.returnType) : call.returnType.getGreatestSubtype(other.call.returnType); JSType newTypeOfThis = null; if (isEquivalent(typeOfThis, other.typeOfThis)) { newTypeOfThis = typeOfThis; } else { JSType maybeNewTypeOfThis = leastSuper ? typeOfThis.getLeastSupertype(other.typeOfThis) : typeOfThis.getGreatestSubtype(other.typeOfThis); newTypeOfThis = maybeNewTypeOfThis; } boolean newReturnTypeInferred = call.returnTypeInferred || other.call.returnTypeInferred; return new FunctionType( registry, null, null, new ArrowType(registry, newParamsNode, newReturnType, newReturnTypeInferred), newTypeOfThis, null, false, false); }
@Override public JSType findPropertyType(String propertyName) { JSType propertyType = null; for (JSType alternate : getAlternates()) { // Filter out the null/undefined type. if (alternate.isNullType() || alternate.isVoidType()) { continue; } JSType altPropertyType = alternate.findPropertyType(propertyName); if (altPropertyType == null) { continue; } if (propertyType == null) { propertyType = altPropertyType; } else { propertyType = propertyType.getLeastSupertype(altPropertyType); } } return propertyType; }