ObjectType specialize(ObjectType other) {
   Preconditions.checkState(areRelatedClasses(this.nominalType, other.nominalType));
   if (this == TOP_OBJECT && other.objectKind.isUnrestricted()) {
     return other;
   }
   NominalType resultNomType = NominalType.pickSubclass(this.nominalType, other.nominalType);
   ObjectKind ok = ObjectKind.meet(this.objectKind, other.objectKind);
   if (resultNomType != null && resultNomType.isClassy()) {
     Preconditions.checkState(this.fn == null && other.fn == null);
     PersistentMap<String, Property> newProps =
         meetPropsHelper(true, resultNomType, this.props, other.props);
     if (newProps == BOTTOM_MAP) {
       return BOTTOM_OBJECT;
     }
     return new ObjectType(resultNomType, newProps, null, false, ok);
   }
   FunctionType thisFn = this.fn;
   boolean isLoose = this.isLoose;
   if (resultNomType != null && resultNomType.isFunction() && this.fn == null) {
     thisFn = other.fn;
     isLoose = other.fn.isLoose();
   }
   PersistentMap<String, Property> newProps =
       meetPropsHelper(true, resultNomType, this.props, other.props);
   if (newProps == BOTTOM_MAP) {
     return BOTTOM_OBJECT;
   }
   FunctionType newFn = thisFn == null ? null : thisFn.specialize(other.fn);
   if (!FunctionType.isInhabitable(newFn)) {
     return BOTTOM_OBJECT;
   }
   return new ObjectType(resultNomType, newProps, newFn, isLoose, ok);
 }